通读oracle官方文档,通读solidity官方文档

通读solidity官方文档,参考资料

14e7fd615bbd

智能合约

Solidity是一种面向合同编写的高级语言,用于实现智能合同。它受到c++、Python和JavaScript的影响,被设计为在Ethereum虚拟机(EVM)上运行。

Solidity是静态类型的,支持继承、库和复杂的用户定义类型。

正如您将看到的,可以为投票、众筹、盲目拍卖、数字钱包等创建智能合约。

一、合约的结构

合约在Solidity中,和面向对象语言中的Class类似。每个合约中可以包含状态变量,函数,函数修饰符,事件,结构类型以及枚举类型。此外合约可以继承另外一个合约。

状态变量 State variable

函数 Function

函数修饰符 Modifier

事件 Event

结构类型 Struct Types

枚举类型 enum Types

状态变量 State variable

状态变量是永久存储在合约中的值。

pragma solidity ^0.4.0;

contract SimpleStorage {

uint storedData; // State variable

// ...

}

函数 Function

函数是合约中可执行的代码单元。

pragma solidity ^0.4.0;

contract SimpleAuction {

function bid() public payable { // 函数 Function

// ...

}

}

函数修饰符 Modifier

函数修饰符可用于以声明的方式修改函数的语义(请参阅合约部分中的函数修饰符)。

pragma solidity ^0.4.22;

contract Purchase {

address public seller;

modifier onlySeller() { // 修饰符 Modifier

require(

msg.sender == seller,

"Only seller can call this."

);

_;

}

function abort() public onlySeller { // 使用修饰符

// ...

}

}

事件 Event

事件是在EVM日志功能中方便使用的接口。

pragma solidity ^0.4.21;

contract SimpleAuction {

event HighestBidIncreased(address bidder, uint amount); // Event 事件

function bid() public payable {

// ...

emit HighestBidIncreased(msg.sender, msg.value); // Triggering event 触发事件

}

}

结构类型 Struct Types

struct 中可以自定义组合多个变量类型(请参阅类型部分的Struts)

pragma solidity ^0.4.0;

contract Ballot {

struct Voter { // 结构体 Struct

uint weight;

bool voted;

address delegate;

uint vote;

}

}

枚举类型 Enum Types

枚举可以使用有限的“常量值”集创建自定义类型(请参阅类型部分的Enum)。

pragma solidity ^0.4.0;

contract Purchase {

enum State { Created, Locked, Inactive } // Enum

}

二、类型 Types

值类型

布尔(Booleans)

整型(Integer)

地址(Address)

定长字节数组(fixed byte arrays)

有理数和整型(Rational and Integer Literals,String literals)

枚举类型(Enums)

函数(Function Types)

引用类型

数据位置(Data location)

每个复杂类型,例如数组和结构,都有一个附加的注解,即“数据位置”,关于它是存储在memory还是存储在storage。根据上下文的不同,总是存在一个默认值,但是可以通过类型判断。函数参数(包括返回参数)的默认值是存储在memory,本地变量的默认值是存储在storage。

还有第三个数据位置calldata,它是一个不可修改的、非持久性的区域,函数参数在其中存储。外部函数的函数参数(而不是返回参数)被强制使用calldata,用法更像 memory。

pragma solidity ^0.4.0;

contract C {

uint[] x; // x 的数据位置是指向storage

// memoryArray 的数据位置是内存,

function f(uint[] memoryArray) public {

x = memoryArray; // 把数组中所有数据放在storage中

var y = x; // 分配一个指针,y的数据位置是storage

y[7]; // 返回第8个元素

y.length = 2; // 通过y的指向,修改x的值

delete x; // 清空x的数组,同时也修改了y

// 接下来这些代码不会生效,它需要创建一个副本 /

// 重命名storage中的数组,但是storage是静态分配的。

// y = memoryArray;

// 这也不起作用,因为它将“重置”指针,但是它不能指向任何合理的位置。

// delete y;

g(x); // 调用g,传递一个对x的引用值

h(x); // 调用h并在内存中创建一个独立的临时副本

}

function g(uint[] storage storageArray) internal {}

function h(uint[] memoryArray) public {}

}

总结:

强制指定数据存放位置:

-> 外部函数的参数(不是返回值):calldata

-> 状态变量:storage

默认数据存放的位置:

-> 函数参数(含返回值):memory

-> 所有其他本地的变量:storage

数组 Arrays

数组在编译的时候可以是固定大小,也可以是动态的。对于storage 中的数组,元素类型可以是任何类型(例如:其他arrays、mapping或structs)。对于memory数组,它不能是mapping 。并且如果是一个公开可见函数的参数,必须是ABI类型。

举例:定义长度为5的uint动态数组是uint[][5](注意,与其他一些语言相比,符号是颠倒的),要访问第三个动态数组中的第二个uint,可以使用x[2][1](索引是基于零的,访问工作与声明的方式相反,即x[2]从右边的类型中删除一个级别)。

字节和字符串类型的变量是特殊的数组。字节类似于byte[],但它在calldata中被紧密地打包。字符串等于bytes,但不允许长度或索引访问(目前为止)。

注意:

所以bytes 比byte[]更可取,因为它开销更小。

由于bytes与string,可以自由转换,你可以将字符串s通过bytes(s)转为一个bytes。但需要注意的是通过这种方式访问到的是UTF-8编码的码流,并不是独立的一个个字符。比如中文编码是多字节,变长的,所以你访问到的很有可能只是其中的一个代码点。

结构体 Structs

Solidity 提供一种新的方式定义结构体,如下面代码:

pragma solidity ^0.4.11;

contract CrowdFunding {

// 定义一种新的类型有两个属性字段

struct Funder {

address addr;

uint amount;

}

struct Campaign {

address beneficiary;

uint fundingGoal;

uint numFunders;

uint amount;

mapping (uint => Funder) funders;

}

uint numCampaigns;

mapping (uint => Campaign) campaigns;

function newCampaign(address beneficiary, uint goal) public returns (uint campaignID) {

campaignID = numCampaigns++; // campaignID 返回一个变量

// 创建新的结构体并保存在 storage 中。这里我们不用赋值 mapping 类型

campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);

}

function contribute(uint campaignID) public payable {

Campaign storage c = campaigns[campaignID];

// 创建一个新的临时memory结构,初始化给定值。

// 并将其复制到 storage 中。

// 注意,您还可以使用Funder(msg.sender, msg.value) 来初始化。

c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value});

c.amount += msg.value;

}

function checkGoalReached(uint campaignID) public returns (bool reached) {

Campaign storage c = campaigns[campaignID];

if (c.amount < c.fundingGoal)

return false;

uint amount = c.amount;

c.amount = 0;

c.beneficiary.transfer(amount);

return true;

}

}

上面的代码向我们展示的一个简化版的众筹项目,其实包含了一些 struct的使用。

结构体可以包含 mapping 和 arrays ,但是不能包含 自己类型的结构体,因为结构体的大小是有限的。

需要注意的是在函数中,将一个 struct赋值给一个局部变量(默认是 storage类型),实际是拷贝的引用,所以修改局部变量值时,会影响到原变量。

当然,你也可以直接通过访问成员修改值,而不用一定赋值给一个局部变量,如 campaigns[comapingnId].amount = 0

映射 Mappings

Mapping 类型被声明为Mapping(_KeyType => _ValueType)。这里的_KeyType几乎可以是任何类型,除了Mapping、动态大小的数组 Arrays、合约 contract、枚举 enum 和结构 struct。_ValueType实际上可以是任何类型,包括Mapping。

Mapping可以被视为hash表,它实际上是初始化的,以便每个可能的键都存在,并映射到一个值,该值的字节表示都是0:类型的默认值。但是,相似之处就在这里结束了:key数据实际上并没有存储在Mapping中,只有它的keccak256 hash值用于查找值。

因此,Mapping没有长度或键或值“set”的概念。

映射只允许状态变量(或作为内部函数中的存储引用类型)。

可以将Mapping标记为public,并合约创建一个getter。_KeyType将成为getter的必需参数,它将返回_ValueType。

_ValueType也可以是Mapping。getter将递归地为每个_KeyType提供一个参数。

pragma solidity ^0.4.0;

contract MappingExample {

mapping(address => uint) public balances;

function update(uint newBalance) public {

balances[msg.sender] = newBalance;

}

}

contract MappingUser {

function f() public returns (uint) {

MappingExample m = new MappingExample();

m.update(100);

return m.balances(this);

}

}

注意:Mapping 是不可迭代的,但是可以在其之上实现数据结构。例如,请参见可迭代映射。

左值的相关运算符 Operators Involving LValues

左值,是指位于表达式左边的变量,可以是与操作符直接结合的形成的,如自增,自减;也可以是赋值,位运算。

可以支持操作符有:-=,+=,*=,%=,|=,&=,^=,++,--。

特殊的运算符delete

delete运算符,用于将某个变量重置为初始值。对于整数,运算符的效果等同于a = 0。而对于定长数组,则是把数组中的每个元素置为初始值,变长数组则是将长度置为0。对于结构体,也是类似,是将所有的成员均重置为初始值。

delete对于mapping几乎无影响,因为键可能是任意的,且往往不可知。所以如果你删除一个结构体,它会递归删除所有非mapping的成员。当然,你是可以单独删除 mapping里的某个键,以及这个 mapping的某个值。

需要强调的是delete a的行为更像赋值,为a赋予一个新对象。我们来看看下文的示例:

pragma solidity ^0.4.0;

contract DeleteExample {

uint data;

uint[] dataArray;

function f() public {

uint x = data;

delete x; // 把x设置为0,但是不影响x的结构

delete data; // sets data to 0, does not affect x which still holds a copy

uint[] storage y = dataArray;

delete dataArray; //删除data,同样也不会影响x,因为是值传递,它存的是一份原值的拷贝。

// y is affected which is an alias to the storage object

// On the other hand: "delete y" is not valid, as assignments to local variables

// referencing storage objects can only be made from existing storage objects.

}

}

基本类型间的转换

隐式转换

如果一个操作符被应用到不同的类型,编译器会试图隐式地将一个操作数转换为另一个操作数(同样适用于赋值)。一般来说,如果值类型之间的隐式转换在语义上有意义并且没有信息丢失,那么它是可能的:uint8可以转换为uint16,而int128可以转换为int256,但是int8不能转换为uint256(因为uint256不能保存例如-1)。此外,无符号整数可以转换为大小相同或更大的字节,但反之亦然。可以转换为uint160的任何类型也可以转换为address。

显式转换

如果编译器不允许隐式转换,但是您知道自己在做什么,那么显式类型转换有时是可能的。请注意,这可能会给您一些意料之外的行为,所以一定要测试以确保结果是您想要的!在下面的例子中,你将一个负的int8转换成一个uint:

int8 y = -3;

uint x = uint(y);

在这个代码段的末尾,x将有值0xfffff..fd(64个十六进制字符),是-3在两个二进制的256位的补码表示法中。

如果一个类型被显式地转换为较小的类型,高阶位将被切断:

uint32 a = 0x12345678;

uint16 b = uint16(a); // b will be 0x5678 now

-类型推断(Type Deduction)

为了方便,并不总是需要明确指定一个变量的类型,编译器会通过第一个向这个对象赋予的值的类型来进行推断

uint24 x = 0x123;

var y = x;

函数的参数,包括返回参数,不可以使用var这种不指定类型的方式。

需要特别注意的是,由于类型推断是根据第一个变量进行的赋值。

所以代码for (var i = 0; i < 2000; i++) {}将是一个无限循环,因为一个uint8的i的将小于2000。

pragma solidity ^0.4.4;

contract Test{

function a() returns (uint){

uint count = 0;

for (var i = 0; i < 2000; i++) {

count++;

if(count >= 2100){

break;

}

}

return count;

}

}

单位和全局可用的变量Units and Globally Available Variables

以太坊单位 Ether Units

一个数字可以在以太坊上以wei、finney、szabo或 ether 后缀的单位进行转换,其中假定没有后缀的以太货币数为 wei ,例如,2 ether = 2000 finney的值为true。

时间单位 Time Units

在以秒为基本单位的时间单位和以秒为基本单位的单位之间,可以用诸如秒、分、小时、日、周、年等后缀来表示时间单位之间的转换。

1 = = 1秒

1分钟= 60秒

1小时= 60分钟

1天= 24小时

1周= 7天

1年= 365天

如果你用这些单位进行日历计算,要小心,因为不是每一年都等于365天,甚至不是每一天都因为闰秒而有24小时。由于无法预测闰秒,因此必须由外部oracle更新确切的日历库。

特殊的变量和函数 Special Variables and Functions

全局命名空间中总是存在一些特殊的变量和函数,它们主要用于提供关于区块链的信息,或者是通用的实用工具函数。

块和交易属性 Block and Transaction Properties

block.blockhash(uint blockNumber) returns (bytes32): hash of the given block - only works for 256 most recent, excluding current, blocks - deprecated in version 0.4.22 and replaced by blockhash(uint blockNumber).

block.coinbase (address): current block miner’s address

block.difficulty (uint): current block difficulty

block.gaslimit (uint): current block gaslimit

block.number (uint): current block number

block.timestamp (uint): current block timestamp as seconds since unix epoch

gasleft() returns (uint256): remaining gas

msg.data (bytes): complete calldata

msg.gas (uint): remaining gas - deprecated in version 0.4.21 and to be replaced by gasleft()

msg.sender (address): sender of the message (current call)

msg.sig (bytes4): first four bytes of the calldata (i.e. function identifier)

msg.value (uint): number of wei sent with the message

now (uint): current block timestamp (alias for block.timestamp)

tx.gasprice (uint): gas price of the transaction

tx.origin (address): sender of the transaction (full call chain)

请注意

msg 的所成员变量,包括msg.sender和msg.value可以随着每次外部函数调用而改变。这包括对库函数的调用。

请注意

不要依赖 block.timestamp,now 和 blockhash 作为机性的来源,除非你知道自己在做什么。

时间戳和块散列在某种程度上都可以受到挖掘程序的影响。例如,采矿社区中的不良行为者可以在选定的散列上运行赌场支付函数,如果他们没有收到任何钱,就重新尝试不同的散列。

当前块时间戳必须严格大于上一个块的时间戳,但唯一的保证是它将介于标准链中两个连续块的时间戳之间。

请注意

由于可伸缩性的原因,块散列不能用于所有块。您只能访问最近的256块的散列,所有其他值都为零。

ABI 转码的函数 ABI Encoding Functions

abi.encode(...) returns (bytes): ABI-encodes the given arguments

abi.encodePacked(...) returns (bytes): Performes packed encoding of the given arguments

abi.encodeWithSelector(bytes4 selector, ...) returns (bytes): ABI-encodes the given arguments

starting from the second and prepends the given four-byte selector

abi.encodeWithSignature(string signature, ...) returns (bytes): Equivalent to

abi.encodeWithSelector(bytes4(keccak256(signature), ...)

异常处理 Error Handling

assert(bool condition):

如果条件不满足,则使交易无效——用于内部错误。

require(bool condition):

如果条件不满足,则返回—用于输入或外部组件中的错误。

require(bool condition, string message):

如果条件不满足,则返回—用于输入或外部组件中的错误。还提供一个错误消息。

revert():

中止执行并恢复状态更改

revert(string reason):

中止执行并恢复状态更改,提供一个解释字符串

数学和加密功能 Mathematical and Cryptographic Functions

以下函数可以根据方法和返回值的字面意思理解,因为这里翻译很怪 ,或者参考资料

addmod(uint x, uint y, uint k) returns (uint):

mulmod(uint x, uint y, uint k) returns (uint):

keccak256(...) returns (bytes32):

sha256(...) returns (bytes32):

sha3(...) returns (bytes32):

ripemd160(...) returns (bytes20):

ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address):

keccak256("ab", "c")

keccak256("abc")

keccak256(0x616263)

keccak256(6382179)

keccak256(97, 98, 99)

账户相关 Address Related

.balance (uint256): 返回Wei单位的余额
.transfer(uint256 amount): 给该Address发送 Wei单位的amount,默认消耗2300gas ,不可调整
.send(uint256 amount) returns (bool):

给该Address发送 Wei单位的amount, 失败的时候返回false,默认消耗2300gas ,不可调整

.call(...) returns (bool): 失败的时候返回false,转发所有gas,可调整
.callcode(...) returns (bool):失败的时候返回false,转发所有gas,可调整
.delegatecall(...) returns (bool):失败的时候返回false,转发所有gas,可调整

合同相关 Contract Related

this (current contract’s type):

指当前合同

selfdestruct(address recipient):

销毁当前合同,将其资金发送到指定地址

suicide(address recipient):

已弃用的别名 ,请使用selfdestruct

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值