solidity的基本语法及使用参见官方文档
https://solidity.readthedocs.io/en/v0.4.25/
这里总结一些特别之处:
1. ABI编码
例:函数 functionName(uint p1, address p2, bytes p3)
则 函数签名 functionName(uint256,address,bytes)
函数选择器 bytes4(keccak256("functionName(uint256,address,bytes)"))
【注意】1. uint类的写法改为uint256
2. 没有空格,选择器取前4个字节
调用合约函数及参数的序列化算法
交易的data字段的前4个字节来做匹配,即函数选择
函数选择器4个字节后紧挨着的是入参,每个入参一个word(32字节),但并不全是入参的编码,还可能是入参位置的编码(动态变量),即先把确定性变量和动态变量位置进行编码,再后面是动态变量的实际值的编码
2. 全局函数
blockhash(uint) 取最新的256个区块号对应的hash,但不包括当前区块的
block.coinbase/block.difficulty/block.gaslimit/block.number/block.timestamp/now 都是取当前区块的相关信息
gasleft() 交易带的gas值减去交易执行到现在的gas
msg.data 完整的calldata,ABI编码 字节数组
msg.sender/msg.sig(calldata前4个字节 函数选择器)/msg.value
tx.gasprice/tx.origin
3. 以太坊中的“消息”
message: gas value to sender data code
消息调用就是找到to指向的账户地址的code(合约账户创建时最后的结果),然后用data的ABI编码匹配到相应的函数,然后输入入参,执行相应的函数
即基于账户的存储运行相应的代码
EVM代码中,如果有create/call/callcode/delegatecall/staticcall中的一个,就会产生child message,进而形成新的EVM Computation
执行中,memory:一维数组,前4个words固定,每个EVM Computation独立使用
栈:初始的EVM Computation及后续的child message触发的EVM Computation同用的。
4. 全局函数 ABI编码
abi.encode() 对指定的参数进行ABI编码
abi.encodePacked() ABI编码并紧打包
abi.encodeWithSelector(bytes4,...) 从第2个参数开始进行ABI编码,并将第一个参数放在编码结果的前面作为前缀
abi.encodeWithSignature(string signature,...) 即abi.encodeWithSelector(bytes4(keccak256(signature), ...)
5. 全局函数 异常处理
assert(bool) 非预期错误,不返还gas,惩罚性质,很少使用
require(bool) 有条件检查,返还gas,原则上对所有输入数据的检查,或者计算结果的状态检查都用require,可以附加消息作为第2个参数
revert() 无条件直接返回异常,返还gas,可以附加消息作为第2个参数
6. 全局函数 算术函数
addmod(x,y,z) ==》 (x + y) % z
mulmod(x,y,z) ==》 (x * y) % z
超过2的256次方不会被舍弃
7. 全局函数 密码学函数
keccak256/sha3/sha256/ripemid160
ecrecover(hash, v, r, s) 由签名恢复公钥
8. 全局函数 地址相关
.balance .transfer 2300gas限制,失败时抛出异常 .send 2300gas限制 ,失败不抛出异常,只返回false .call 传入ABI编码,对其调用,附加所有gas(可以.gas调整),失败返回false .callcode 等价于call,但是保持执行上下文,即将目标地址相应的合约函数代码拷贝到当前合约中执行 .delegatecall. 等价于callcode, 但是会保持msg.sender msg.value9. 全局修饰器
pure: function 不访问,不修改状态变量
view: function 可以访问,不修改状态变量
payable: function 函数调用可以附加以太币
constanct: state variable 状态变量除初始化外不允许赋值,也就是不占用存储空间,最终就是固定的合约代码
anongmous: event (收据 日志) 指明时间的签名不作为topic
indexed: event parameter 指明事件参数需要作为topic
10. event
event 事件是收据中的一个条目,合约执行的日志,
一个事件可以有5个topic和额外的若干个数据
topic 0固定是事件签名的keccak256 (不取4字节了), 除非事件被声明为anongmous
所有的topic都会被加入到交易和区块的bloomfilter 可以通过jsonrpc/web3的filter功能进行快速过滤匹配
《注》所有日志数据都是公开可见的,不只是indexed的
11.合约函数的可见性
private internal public external
合约调用private/internal函数,本质上是代码跳转(标签跳转),不会产生calldata和returndata,内存是共用的,msg也保持不变,返回值直接在栈或者内存中传递
调用public函数,如果直接用函数名调用,等价于internal,用this.调用,等价于external函数,产生child message,会有calldata和returndata,所以public函数,如果本合约有内部调用时,会有两套处理代码
external函数,只能被外界调用,必定会产生calldata和returndata
12. 状态变量的可见性
private internal public
13. 引用类型的数据位置
可用位置 局部变量默认
array storage/memory storage
struct storage/memory storage
mapping storage
14. fallback函数
fallback不处理输入,不返回返回值
通过transfer/send函数对合约地址转账,必须触发fallback函数,所以需要明确写明payable的fallback函数