区块链技术全面剖析:Solidity合约编写

一、特别点

1.版本声明,pragma solidity ^0.4.0;版本要高于0.4才可以编译

2. 函数,分为internalexternal函数,可以直接调用f(),或者使用this.f()。但两者有一个区别。前者是通过internal的方式在调用,而后者是通过external的方式在调用。请注意,这里关于this的使用与大多数语言相背。出参在returns关键字后定义,参数名字可以省略。其它合约的函数必须通过外部的方式调用。对于一个外部调用,所有函数的参数必须要拷贝到内存中。当调用其它合约的函数时,可以通过选项.value()和.gas()来分别指定要发送的ether量(以wei为单位)和gas值。函数的参数可以通过指定名字的方式以任意的顺序来调用,使用方式是{}包含。

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

4. 数据 array,对storage的数组来说,元素类型可以是任意的,类型可以是数组,映射类型,数据结构等。但对于memory的数组来说。如果函数是对外可见的,那么函数参数不能是映射类型的数组,只能是支持ABI的类型。如一个类型为uint的数组长度为5的变长数组,可以声明为uint[][5] x ,注意多维数组的长度声明是反的,如果当前在外部函数中,则不能使用多维数组。可使用new关键字创建一个memory的数组,例子:uint[] memory a = new uint[](7);storage的变长数组可以通过给.length赋值调整数组长度。memory的变长数组不支持。

5.映射 mappings,被视作为一个哈希表,其中所有可能的键已被虚拟化的创建,被映射到一个默认值(二进制表示的零)。并没有长度,并未提供迭代输出的方法。

6. 字节数组,固定长度bytes1~bytes32,支持序号的访问,length属性;不定长字节数组bytes用来存储任意长度的字节数据,string用来存储任意长度的UTF-8编码的字符串数据。string类似bytes,但不提供长度和按序号的访问方式。

7. 十六进制字面量,hex”001122FF”、货币字面量,wei,finney,szabo或ether、时间字面量,seconds,minutes,hours,days,weeks,years日期计算并不精确

8. 控制结构,不支持switch和goto,没有非Boolean类型的自动转换

9. throw,可以使用throw来手动抛出一个异常。抛出异常的效果是当前的执行被终止且被撤销(值的改变和帐户余额的变化都会被回退),没有捕捉异常的功能。如果发生运行时异常,或assert失败时,将执行无效操作(指令0xfe)

10. 合约,构造器函数是可选的,仅能有一个构造器不支持重载。函数和状态变量提供了四种可见性。分别是external,public,internal,private。自动为所有的public的状态变量创建访问函数

11. 修饰符 modifer,是一种合约属性(比如用于在函数执行前检查某种前置条件),可被继承,同时还可被派生的合约重写(override)。特殊”_”表示使用修改符的函数体的替换位置。如果同一个函数有多个修改器,他们之间以空格隔开,修饰器会依次检查执行。在修改器中和函数体内的显式的return语句,仅仅跳出当前的修改器和函数体。返回的变量会被赋值,但整个执行逻辑会在前一个修改器后面定义的”_”后继续执行。

二、关键点

1. 整形 int8 ~ int256 不支持八进制表示,没有浮点型,字面量不保证小数计算精度。注意类型推断编译器是根据第一次变量赋值类型进行推断,所以代码for (var i = 0; i < 2000; i++) {}将是一个无限循环,因为一个uint8的i的将小于2000

2. 地址 address 大小20个字节,160位,所有的合约都会继承地址对象,字面量可以通过balance属性获得一个地址的余额,send方法向某个地址发送货币(货币单位是wei)使用这个方法要检查成功与否(gas不够会执行失败),call方法函数支持传入任意类型的任意参数,并将参数打包成32字节,相互拼接后向合约发送这段数据。支持ABI协议定义的函数选择器。如果第一个参数恰好4个字节,在这种情况下,会被认为根据ABI协议定义的函数器指定的函数签名。所以如果你只是想发送消息体,需要避免第一个参数是4个字节。

3. 数据的存储位置,可选为memorystorage

4. 特殊变量与函数,为了可扩展性的原因,你只能查最近256个块,所有其它的将返回0

  • block.blockhash(uint blockNumber) returns (bytes32),给定区块号的哈希值,只支持最近256个区块,且不包含当前区块。
  • block.coinbase (address) 当前块矿工的地址。
  • block.difficulty (uint)当前块的难度。
  • block.gaslimit (uint)当前块的gaslimit。
  • block.number (uint)当前区块的块号。
  • block.timestamp (uint)当前块的时间戳。
  • msg.data (bytes)完整的调用数据(calldata)。
  • msg.gas (uint)当前还剩的gas。
  • msg.sender (address)当前调用发起人的地址。
  • msg.sig (bytes4)调用数据的前四个字节(函数标识符)。
  • msg.value (uint)这个消息所附带的货币量,单位为wei。
  • now (uint)当前块的时间戳,等同于block.timestamp
  • tx.gasprice (uint) 交易的gas价格。
  • tx.origin (address)交易的发送者(完整的调用链)

5. 数学与加密函数,参数是“紧密打包(tightly packed)”的,意思是说参数不会补位,就直接连接在一起的。如果需要补位,需要明确的类型转换。在私链上运行sha256,ripemd160或ecrecover等可能会出现Out-Of-Gas报错。因为它们实现了一种预编译的机制,但合约要在收到第一个消息后才会存在。在你真正使用它们前,先发送1 wei到这些合约上来完成初始化。主链及测试链没有这个问题。

  • keccak256(…) returns (bytes32) 使用以太坊的(Keccak-256)计算HASH值。紧密打包。
  • sha3(…) returns (bytes32)  等同上面
  • sha256(…) returns (bytes32) 使用SHA-256计算HASH值。紧密打包。
  • ripemd160(…) returns (bytes20)  使用RIPEMD-160计算HASH值。紧密打包。
  • ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)  通过签名信息恢复非对称加密算法公匙地址。如果出错会返回0
  • revert() 取消执行,并回撤状态变化。

event 创建EVM log

 

实例:

https://github.com/OpenZeppelin/zeppelin-solidity
https://github.com/slockit/smart-contract

本文是全系列中第6 / 7篇:区块链技术

提交看法

抢沙发

还没有评论,你可以来抢沙发