eosio.cdt:EOS智能合约工具集
目前EOS已经迎来了1.5.x时代,很多内容都有了较大的改变。其中智能合约的工作流程发生了改变,EOSIO为智能合约提供了独立且功能完整的工具集eosio.cdt。该工具集基于WASM平台,可解耦于eos系统,更便携地完成智能合约的开发编译。此外eosio.cdt在底层优化上已经超脱于eos系统,使用了更新的对LLVM有了更佳地支持,未来在性能方面会有较大提升。本章讲重点介绍eosio.cdt工具集,在该工具集的基础上,分析研究eos现有的官方智能合约例子。
关键字:eos,eosio.cdt,hello,bbt脚本,李嘉图合约,合约更新,合约调试
hello
首先,在本机安装eosio.cdt。官方推荐的安装方式有很多,笔者推荐采用源码安装的方式。
$ git clone --recursive https://github.com/eosio/eosio.cdt
$ cd eosio.cdt
$ ./build.sh
$ sudo ./install.sh
编译
hello合约的源文件内容:
#include "hello.hpp"
using namespace eosio;
ACTION hello::hi( name user ) {
print_f( "Hello % from hello", user );
}
EOSIO_DISPATCH( hello, (hi) )
下面开始编译example/hello合约。执行eosio.cdt刚刚安装的命令eosio-cpp编译合约,传入hello.cpp文件。执行命令:
eosio-cpp -abigen hello.cpp -o hello.wasm
将会生成两个文件:
- abi文件,是合约的描述文件,是可读的json结构,其中包含了合约的备注、版本、结构、接口或动作以及状态库的table,这些内容是由编译工具eosio-cpp通过合约源文件生成的。
- wasm文件,内容不可读,用于将合约部署运行在wasm平台上。
部署
合约成功编译以后,可以在链上部署运行。使用前文介绍的bbt脚本快速构建一个多节点EOS网络环境,然后执行:
cleos set contract useraaaaaaaa examples/hello/ hello.wasm hello.abi
这时会报错:
Error 3080001: Account using more than allotted RAM usage
说明useraaaaaaaa账户的内存容量不够,需要再购买一些。执行命令购买内存:
cleos system buyram useraaaaaaaa useraaaaaaaa "10 SYS"
然后再次执行前面的合约部署命令,内存充足以后,hello合约被成功部署。
执行
下面尝试执行hello合约。使用命令:
cleos push action useraaaaaaaa hi '["evsward"]' -p useraaaaaaaa
同步跟踪EOS节点的日志输出,会发现有hello合约的信息打印出来。
debug 2018-12-20T12:13:20.233 thread-0 apply_context.cpp:28 print_debug ]
[(useraaaaaaaa,hi)->useraaaaaaaa]: CONSOLE OUTPUT BEGIN =====================
Hello evsward from hello
[(useraaaaaaaa,hi)->useraaaaaaaa]: CONSOLE OUTPUT END =====================
要注意开启节点的合约调试选项:--contracts-console
合约编写
下面来研究如何利用eosio.cdt完成合约的编写。首先尝试修改hello.cpp。前面的测试中,执行hello合约时是不限制输入参数的内容的,可以增加检验将输入参数改为有效用户。
#include "hello.hpp"
using namespace eosio;
ACTION hello::hi( name user ) {
require_auth(user);
print_f( "Hello % from lwb", user );
}
EOSIO_DISPATCH( hello, (hi) )
加入了一行代码require_auth(user);用来校验用户的权限,如果输入参数不是有效用户名或者传入的权限与输入用户不一致则会报错,下面来演示如何更新hello合约以及执行最新代码。仍然是编译、部署、执行三步:
- eosio-cpp -abigen hello.cpp -o hello.wasm
- set contract useraaaaaaaa ../hello/ hello.wasm hello.abi
- push action useraaaaaaaa hi '["evsward"]' -p useraaaaaaaa
此时第三步的运行命令将会执行失败,因为不存在名为"evsward"的有效账户,因此修改命令:
push action useraaaaaaaa hi '["useraaaaaaaa"]' -p useraaaaaaaa
即可执行成功,这行命令末尾的-p参数不可省略,因为hello合约修改后增加了对账户权限的校验。目前这个参数是hello合约的部署者,如果传入其他有效账户并附属其权限,也是可以执行成功的。
push action useraaaaaaaa hi '["useraaaaaaab"]' -p useraaaaaaab
传入的参数改为了b结尾的账户,同时附属了其权限,该action也会成功执行。输出日志为:
Hello useraaaaaaab from lwb
李嘉图合约
通过以上对权限hello的测试,似乎能够察觉出某种深意,即有效账户useraaaaaaaa的动作是由自身签名(通过-p参数)的,useraaaaaaab账户的hi动作也是其自身签名。这种该执行其合法绑定者来关联该合约的每一个action的合约,被成为李嘉图等价的合约,简称李嘉图合约。
合约更新的结论
上面更新一个合约时,是使用相同的部署者重新部署修改后的合约。在EOS中,合约一旦被部署者部署,该合约的所有动作均通过部署者来发起。此外,一个部署者可以多次部署同一个合约,同时也可以部署不同的多个合约,以最后一次部署为准。这部分可以通过部署者code来检查:
cleos get code useraaaaaaaa
输出该账户的code,code就是对应部署的合约的hash。
code hash: ddf06bb75dadfb0b598df0047f5e469891dafce125e0224f869dc8d8e2f5d770
当部署者部署新的合约时,该code会被更新。
合约调试
由于智能合约的运行平台的特殊性,目前暂无法去到WASM平台通过断点的方式调试合约的字节码,因此官方给出的调试方法是通过日志,正如上面所展示的内容那样,官方称之为Caveman debugging,也自嘲了智能合约的原始调试手段。