1.6.4 单位及全局变量

1.6.4.1 以太坊代币单位(Ether Units)

以太坊的代币被称为Ether一般翻译为以太币,它的单位有:wei,finney,szabo和ether。不同的单位之间可以进行换算。不含任何后缀的默认单位是wei。各单位间的换算关系如下:
1) 1 ether == 10^3 finney == 1000 finney
2) 1 ether == 10^6 szabo
3) 1 ether == 10^18 wei
以太币的单位其实是一些密码学家的名字,以太坊创始人为了纪念一些在数字货币领域做出重大贡献的人物用他们的名字作为以太币的单位。他们分别是:
wei:Wei Dai 戴伟,密码学家 ,发明了 B-money。
finney:Hal Finney 哈尔芬尼, 密码学家,为工作量证明机制(POW)作出了巨大贡献。
Szabo:Nick Szabo 尼克萨博, 密码学家,智能合约概念的提出者。

1.6.4.2 时间单位(Time Units)

Solidity中时间单位有: seconds, minutes, hours, days, weeks, years。它们之间可进行换算,规则如下:
1) 1 == 1 seconds (默认是seconds为单位)
2) 1 minutes == 60 seconds
3) 1 hours == 60 minutes
4) 1 days == 24 hours
5) 1 weeks == 7 days
6) 1 years == 365 days
使用这些单位进行日期计算时需要特别小心,因为不是每年(实际上years后缀已经过时)都是365天,不是每天都有24小时,另外还有闰秒。由于无法预测闰秒,必须由外部的预言机(oracle)来更新从而得到一个精确的日历库。
这些后缀不能作用于变量。如果想标注函数的输入参数为不同的单位,可以使用下面的方式:
function f(uint start, uint daysAfter) public {
if (now >= start + daysAfter * 1 days) {
// ...
}
}

1.6.4.3 特殊变量和函数

Solidity中有一些特殊的变量及函数,它们存在于全局命名空间内,主要用于提供区块链的信息等,它们主要分为以下几类:
1) 区块和交易的属性
2) ABI编码函数
3) 错误处理
4) 数学及加密函数
5) 地址相关
6) 合约相关
下面我们分别进行介绍。

1.6.4.3.1 区块和交易的属性(Block and Transaction Properties)

区块和交易的属性用来提供区块链当前的信息。
1) block.blockhash(uint blockNumber) returns (bytes32):返回给定区块的哈希值,只支持查询最近256个区块的哈希值,且不包含当前区块,在0.4.22版本中被blockhash(uint blockNumber)替代。
2) block.coinbase (address): 当前区块矿工的地址。
3) block.difficulty (uint):当前区块的出块难度。
4) block.gaslimit (uint):当前块的gaslimit。
5) block.number (uint):当前区块的区块号。
6) block.timestamp (uint): 当前区块的Unix时间戳(从1970/1/1 00:00:00 UTC开始所经过的秒数)
7) gasleft() (uint256): 剩余gas。
8) msg.data (bytes): 完整的调用数据(calldata)。
9) msg.gas (uint): 当前的gas余额,在版本0.4.21中被gasleft()替代。
10) msg.sender (address): 当前消息的发送者。
11) msg.sig (bytes4):调用数据(calldata)的前四个字节(例如函数标识符)。
12) msg.value (uint): 消息所附带的以太币,单位为wei。
13) now (uint): 当前区块的时间戳(block.timestamp的别名)
14) tx.gasprice (uint) : 本次交易的gas价格。
15) tx.origin (address): 本次交易的发送者(完整调用链)
注意:msg的所有成员的值,如msg.sender和msg.value等会因为每一次外部函数调用,或库函数调用而发生变化。
不要通过 block.timestamp, now 和 block.blockhash来产生一个随机数(除非万不得已),因为这几个值在一定程度上会被矿工影响。比如在一个博彩合约里,不诚实的矿工可能会重试选择一个对自己有利的哈希值。
当前区块的时间戳(timestamp)必须大于上一个区块的时间戳,它介于区块链上前一个区块的时间戳和后一个区块的时间戳之间。
为了迁就可扩展性,用户只能查最近256个块的哈希值,若查询超出此范围的区块,则哈希值将返回0。

1.6.4.3.2 ABI编码函数

Solidity 提供了以下函数,用来直接得到ABI编码信息,这些函数有:
1)abi.encode(...) returns (bytes):计算参数的ABI编码。
2)abi.encodePacked(...) returns (bytes):计算参数的打包编码。
3)abi. encodeWithSelector(bytes4 selector, ...) returns (bytes): 计算函数中从第二个开始的参数的ABI编码并插入4字节的选择器selector。
4)abi.encodeWithSignature(string signature, ...) returns (bytes): 该函数等价于
abi.encodeWithSelector(bytes4(keccak256(signature), ...)
ABI编码函数可以在不调用函数的情况下,计算ABI编码值。
另外,keccak256(abi.encodePacked(a, b))是个更加显式计算keccak256(a, b)的方法,keccak256(a, b)将被淘汰。

1.6.4.3.3 错误处理

1) assert(bool condition):用于判断内部错误,条件不满足时交易无效。
2) require(bool condition):用于判断输入或外部执行错误,条件不满足时回退。
3) require(bool condition, string message):同上,多了给出错误信息。
4) revert(): 终止执行并还原改变的状态
5) revert(string reason): 同上,多了提供错误信息。

1.6.4.3.4 数学及加密功能

1)addmod(uint x, uint y, uint k) returns (uint):计算(x + y) % k,加法支持任意精度,且不会在2**256处溢出,从0.5.0版本开始assert(k != 0)。
2) mulmod(uint x, uint y, uint k) returns (uint):计算 (x * y) % k, 乘法支持任意精度,且不会在2**256处溢出, 从0.5.0版本开始assert(k != 0)。
3) keccak256(…) returns (bytes32):计算打包参数的(Keccak-256)哈希值。
4) sha256(…) returns (bytes32):计算打包参数的SHA-256哈希值。
5) sha3(…) returns (bytes32):keccak256的别名。
6) ripemd160(…) returns (bytes20): 计算打包参数的RIPEMD-160哈希值。
7) ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address):通过椭圆曲线签名来恢复与公钥关联的地址,或在错误时返回零。
打包(tightly packed)的意思是参数不会补位,参数之间前后直接连接在一起,下面几个例子调用效果相同。
keccak256("ab", "c")
keccak256("abc")
keccak256(0x616263)
keccak256(6382179)
keccak256(97, 98, 99)
如果需要填充补位,可以使用显式类型转换:keccak256(“\x00\x12”) 与keccak256(uint16(0x12))相同。
注意,对常量值,Solidity会用存储它所需的最少字节数来打包,例如keccak256(0) == keccak256(uint8(0))和keccak256(0x12345678) == keccak256(uint32(0x12345678))。
在私有区块链上运行sha256,ripemd160或ecrecover可能会出现Out-Of-Gas错误。因为私链上合约是预编译的,合约要在收到第一个消息后才被真正激活(虽然合约的代码已经写好了)。向一个没有被激活的合约发送消息会导致Out-Of-Gas错误。要避免这个问题可以在用户真正使用每个合约之前先发送1 wei的以太币到这些合约。在主链和测试链上不会有这个问题。

1.6.4.3.5 地址相关

1) <address>.balance (uint256):address的余额,以wei为单位。
2) <address>.transfer(uint256 amount):发送amount所定义金额的以太币到某个地址,以wei为单位。失败时抛出异常。
3) <address>.send(uint256 amount) returns (bool):发送amount所定义金额的以太币到某个地址,以wei为单位, 失败时返回false。
4) <address>.call(…) returns (bool):发起底层调用call。失败时返回false。
5) <address>.callcode(…) returns (bool):发起底层调用callcode,失败时返回false。
6) <address>.delegatecall(…) returns (bool):发起底层调用delegatecall,失败时返回false
注意:调用send()有风险,如果调用堆栈的深度达到1024或gas耗光,交易会失败。因此,为了保证安全,必须检查send的返回值。建议使用transfer函数。

1.6.4.3.6 合约相关

1)this(当前合约的类型):表示当前合约,可以显式的转换为address。
2) selfdestruct(address recipient):销毁当前合约,并将其余额发送到给定的地址。
3) suicide(address recipient):selfdestruct的别名
另外,合约里的所有函数均可支持调用,包括当前函数本身。