EOS智能合约
##康烁简介:
康烁,柏链道捷(PDJ Education)CTO、副总裁、清华阿尔山区块链研究中心高级工程师。曾受邀参加2015LLVM开发者大会,展示了自研的Android模拟器项目。主持开发的开源项目SkyEye在国内外有众多用户,是知名的开源项目之一。主持开发的开源项目Android_S2E项目被华为公司内部使用,目前主要从事区块链底层系统开发工作。
以下为康烁在EOS入门及最新技术解读专场沙龙的精彩分享。
我分享的角度可能更多是从一个EOS智能合约的入门者或者开发者的角度去分析我对EOS的初步认识。
在这之前我编译过EOS,然后开发了一些智能合约,包括之前把以太坊的一些智能合约做了移植。对于这里面,我有自己的一些看法。
我主要是做系统软件出身,之前在操作系统、虚拟机、编译器、安全等领域都有一些项目实战,我自己现在主持和维护了两个开源项目:
1、SkyEye。这是一个比较知名的开源虚拟机项目,如果大家在网上用Google和百度搜索这个项目,可以看到用户还是比较多的,包括国内或者国外。
2、Android s2e。这个项目主要做的是符号执行,它可以检测安全漏洞。
大概在去年的时候,以太坊的智能合约被爆出大概有几千个漏洞。其实就是用符号执行的方法,把智能合约的好多漏洞都扫描出来了。而我做的这个项目是可以对Android的整个操作系统做符号分析,并且这个项目在华为内部得到使用。
所以,我对操作系统或者说编译器方面是蛮有经验的。我想先从系统软件从业者的角度,分享一些对EOS的看法。
EOS和以太坊很像,EOS很明确的说明它就是一个区块链的操作系统,BM在博客中也是说过的。
可以这样比喻,EOS就相当于内置激励系统的Windows/Linux/MacOS,这是它的一个定位。包括以太坊也有自己的定位,Vitalik在2014年第一次说以太坊想法的时候,他就很明确的说了,以太坊就是一个全球计算机,这些都是创始人本人提出的一个想法。
##EOS环境搭建和启动节点
下面从EOS入门的环境搭建、编译运行一个智能合约、发送一些Aigsen,给大家做一些展示,希望能让非技术人员也有一些收获。
首先下载EOS环境搭建和启动节点。这一步其实还是比较简单的,但是对于非技术人员,最大的障碍就是,现在用EOS,不管是在MacOS还是Linux,基本都是在命令行下操作,这一点和以太坊还是不太一样。因为以太坊有一个浏览器插件,可以用一些图形化的IDE去操作,这样对非技术人员或者前端人员,可能他不太熟悉这个命令行命令的人都是比较友好的。
EOS环境的搭建和节点启动分为以下两步:
第一步:用Git克隆,下载EOS的源代码
使用命令:
```
git clone https://github.com/EOSIO/eos --recursive
```
下载源代码,时间会比较长,大概1-3小时,另外,下载编译对计算机内存要求最少要有8GB。
下载编译之后,里面有三个应用程序,这三个应用程序也都是命令行的:
1、Cleos;
2、Nodeos;
3、Keosd。
这三个应用程序之间有什么关系呢?我们现在使用的前端,怎么使用EOS呢?
-Cleos,它是一个命令行程序。在前端使用EOS是通过Cleos输入命令,给EOS下达指令。
-Nodeos,其实它就是挖矿客户端。在启动Nodeos之后,它自然就会产生区块。
-Keosd,它是在后端启动。它的目的主要是管理钱包,可以创建私钥。
为什么分成Nodeos和Keosd?
我理解的是,Keosd可以认为是个轻客户端,因为有时候只需要进行钱包操作,不需要挖矿,就可以启动Keosd。
##第二步:节点启动
在第一步下载编译都做完之后,就可以启动一个节点开始愉快的挖矿了!下面是启动节点的命令:
```
cd build/programs/nodeos
./nodeos -e -p eosio --plugin eosio::wallet_api_plugin --plugineosio::chain_api_plugin --plugin eosio::account_history_api_plugin
```
整个EOS是插件式结构,需要有一个主程序,其它像钱包管理、区块链等都是一个插件。
输入上边命令后启动了一个节点,同时启动了一些插件,会打印一些基本信息,以及挖矿的信息。
##EOS创建和管理钱包
节点启动后我们要做的第一件事儿是什么呢?就是我们首先要有账号,但是有账号的前提是什么呢?倒不是先创建账号,而是先要有自己的一组私钥,有了私钥才能有账号,有账号的前提还是要有钱包。所以,最开始我们是创建钱包,钱包是用来管理我们的密钥的。
###第一步:创建钱包
使用命令:
```
cleos wallet create
```
就可以创建一个钱包。像以太坊、比特币这些已经比较成熟了,使用一些客户端可以去创建钱包。
如果你想用不同的参数,可以在命令后边加上-N,-N指定你的钱包名称是另外一个钱包名称,可以创建不同的钱包,然后每个钱包都可以管理一组密钥。
###第二步:创建一组秘钥
创建好钱包,就可以创建一组密钥,创建命令为:
```
cleos create key
```
###第三步:秘钥导入钱包
在第二步创建的密钥,只是生成一组公钥、私钥,还需要把公钥、私钥导入钱包,才能让钱包把公钥和秘钥管理起来。
导入命令为:
```
cleos wallet import
```
可以在命令后面指定钱包,这样就可以把第二步创建的一组公钥、私钥直接导入指定的钱包。
###第四步:查看钱包中的秘钥
使用以下命令就可以查看是不是成功的把秘钥导入钱包了:
```
cleos wallet keys
```
我觉得不太懂技术的非技术人员,可能以后也要懂技术,因为你最后玩比特币,也会了解什么是私钥、什么是公钥、什么是256位的二进制账号。大家刚开始可能学起来比较痛苦,可能也要逐渐有个习惯的过程。
这样就都做完了,我创建了一组密钥,密钥包括公钥和私钥,然后我把这组公钥又放到刚才我创建的钱包里面了。
创建好钱包、密钥之后,接下来我们可以做什么事儿呢?
##EOS创建和管理账号
创建好钱包、密钥之后,接下来你就可以创建账号了,账号是什么?账号保存在区块链上的人类可读的标志符。
创建账号的命令:
```
$ cleos createaccount eosio ${new_account} ${owner_key} ${active_key}
```
其中eosio是超级用户,需要靠超级用户来创建其它的新用户,eosio后面就是你的新用户的用户名。
除了新的账号之外,命令后面还有两个key:
1、Owner key
2、Active key
Owner key是什么意思呢?Owner key表示分配给新账号的一个Owner认证的公钥。Active key是分配给新账号一个Active认证的一个公钥。
至于这两个认证,我后面会给详细介绍,这是两个主要的权限。我创建一个账号,如果这个账号要有Owner的权限和Active的权限,就必须要用这两个key才能实现。
我们来总结一下刚才的操作,我们刚才操作是调用Cleoscreate account创建了一个账号,这个账号的命名规则遵守下边两个规则:
1、小于13个字符;
2、仅包含这些字符:.12345abcdefghijklmnopqrstuvwxyz
另外,刚才给大家说到Owner key和Active key的概念。Owner key的概念就是你账号的所有控制权限,你只要有了Owner key,你可以对这个账号的任何东西做任何的事儿,这是它的所有控制权。
而Active key只掌握了你的账号资金的访问权限,也就是你如果有了Active这个权限的话,你可以对这个账号的资金进行转移,但是你不能转移这个账号的所有权,或者不能做超过这个Active权限其它的权利。
如果简单的理解,Owner key就是对这个账号的最高权限,Active只是用来转移资金而已。这也是与以太坊智能合约开发的一个区别,以太坊账号的权限其实没有这么细分,它就只有一个账号,我只要有这个账号的公钥和私钥,我就可以做任何事情。
##EOS的权限和许可
对于EOS的权限和许可,分得非常细。
「Permission」我没有翻译成权限,我这里觉得它更像一种叫做「许可」,它是一个操作允许的权限,可以建立很多permission。比如允许你转账,允许你发微博或者允许你做其他事。它是通过permission来控制你的操作,可能不同的用户有不同的permission。
Threshold是某个许可需要的阈值。比如你要做一件事儿,它可能有个阈值,这个阈值是一个量化,你必须要达到这个阈值才可以进行相应的操作。
Weight表示权重。账号必须对应某个许可拥有的权重,就是你的权重要大于你的阈值,然后这件事儿你就可以做。
在permission许可里面有两种许可:
-owner许可;
-active许可。
这两个叫native(原生)的permission,另外还有public的permission。
下面通过两个例子来解释他们之间的关系与区别:
第一个例子, owner许可的阈值是1,它里面有一个账号,这个账号是用它的公钥来代表的,然后这个账号的权重是1,这样你的权重是大于等于阈值的,所以可以用owner的账号和权限。
active许可里面有一个账号,它的权重也是等于这个阈值,所以可以用active的账号和权限。
这两个账号,一个是有owner权限,一个是有active权限,因为它们的权重和阈值是大于等于这个值的,对应的账号与权限都是可以使用的。
第二个例子,除了owner的permission之外,你还可以有一个publish permission。它们之间有什么区别呢?
当你创建任何一个账号,这个账号都有owner的许可和active的许可。其他的许可就是你可以自己定义,来让不同的用户,拥有不同的许可组合,这样就可以很灵活的去管理不同的账号。
所以这一点,EOS考虑的比较比较细致一点,但是以太坊或者比特币没有做到这么细致,以太坊或者比特币拥有一个账号就可以做任何事情。
这里面再细一点来讲,这里面的owner的阈值是R,bob账号的权重只有1,stracy账号的权重也只有1,如果它俩单独的去获得owner许可相关的操作,它们是获得不了的。但是,如果bob和stracy加起来大于等于2,就是这两个账号联合起来大于等于2的,这两个账号就可以做这个操作的,其实这有点像投票。
我觉得这个权限或许可的限制,和我们传统的操作系统是不太一样的。我们传统的操作系统好像没有这种联合签名可以达到两个权重来执行owner的相关操作。
active这也是一样的,如果bob账号是1,stracy账号是1,如果active需要的阈值是1的话, bob账号和stracy账号是单独都可以做active许可相关的操作,就是转移资金。
另外,publish就是一个定制化许可。我们每个用户也可以定义自己某些允许的一些操作。publish这里面的阈值是2,bob账号是2,stracy账号是2,这两个账号也可以单独做publish许可相关的操作。而publish的权限可能是比如发布一些消息、发布一些微博的操作。
通过以上两个例子,给大家展示了EOS对权限设置的设计有多细致。
##EOS智能合约
EOS的智能合约里面有一个action(动作)和transaction(交易)的概念。
对于我们以太坊开发者来说,基本上只有transaction的概念。如果我只要执行一种操作,而且是只读操作,就不需要签名。如果需要划资金,有一些写的操作,那就需要用户用公钥对这个操作去签名,然后pos的一个transaction,这是以太坊的概念。
对于EOS,它多了一个action的概念,action其实它也是对一个智能合约中的某个函数的调用。transaction是由一个或者多个action组合而成的关系,就是在一个transaction里,可以包含多个action,这样你可以在一个transaction里签一次名,就可以调多个函数,做一组操作。
###部署智能合约
部署智能合约的示例代码如下:
```
$ cleos set contract eosio build/contracts/eosio.bios -p eosio
```
其中,eosio是要部署的账号,就是你用哪个账号去部署智能合约;
build/contracts/eosio.bios表示的是路径;
eos.bios是生成一个智能合约的目录。
运行Token合约
####第一步,Token智能合约部署,代码如下:
```
cleos set contracteosio.token build/contracts/eosio.token -p eosio.token
```
####第二步,调用create函数,代码如下:
```
$ cleos push actioneosio.token create ‘[ “eosio”, “1000000000.0000 EOS”, 0, 0, 0]’ -p eosio.token
```
调用create函数,设定到币的最大数目,开始创建我的token。
怎么调用一个action呢?我们都是通过cloes命令行调用action,不过后面跟的参数不一样。push action,这两个都是固定的,后面是合约的名称。
eosio是账号名,后面数字表示设定token总量是10亿,币的名称是EOS币,后面有3个0,代表的是三个参数:can freeze、can recall、can whitelist。这3个参数设置为0,一共传了5个参数,通过这样的方法,去调用整个的合约create的函数。
####第三步,转移100个pdj币到user用户,代码如下:
```
$cleos push actioneosio.token issue '[ "user", "100.0000 EOS","memo" ]' -p eosio
```
Issue这个操作是用来发币,它后面跟了3个参数:
一个是user,表示这个币发给谁;
数字表示要给这个用户转多少钱,我这里转了100多个user的token;
最后一个参数是是一个备注,相当于转账留言。
####第四步、从user用户转移25个PDJ币给tester用户,代码如下:
```
$cleos push actioneosio.token transfer '[ "user", "tester", "25.0000EOS", "m" ]' -p user
```
这是一个转账合约的运行过程,先部署合约,然后调用create,可以转账,既可以转到user,也可以从一个user用户转到test用户,整个过程在user wiki文档上也写得比较清楚的。
####调试智能合约
现在user官方网站推荐的一个调试方法就是print,把信息打印出来。这个必须要我们搭建本地节点,因为如果没有本地节点,相当于你print打印在别人的节点上,你根本看不到这个打印信息是什么,所以说你必须要搭建一个本地节点。搭建本地节点后,你运行智能合约,就会看到print出来的输出结果。
####EOS智能合约的RPC接口
其实智能合约整个只完成了DApp最核心的一部分,就是基本上和资金有关系的一些关键操作,其实大部分的接口、界面,还得我们用JavaScript、HTML去做。
那我们这些DApp其它写界面的操作,怎么去调用智能合约呢?都是通过user智能合约RPC接口调用,智能合约的RPC接口文档链接是:https://eosio.github.io/eos/group__eosiorpc.htm。
RPC的接口我们除了用C++或者用编程的方法去调用一些接口,我们可以用curl这种最简单的方法去调用这个接口。curl它相当于模拟了一个浏览器的操作,我可以向我的运行节点的RPC端口发消息。
这里面我可以给大家展示,我列了3个。
1.get_info:获得节点信息。通过调用这个接口,它会返回我运行节点,比如说server version,就是我运行节点这个节点的版本号;head blocknum,是我当前挖到哪个块了。
用法:
```
curl http://127.0.0.1:8888/v1/chain/get_info
```
2. get_block:获得一个块的信息。调用该接口,指定块号(blocknum),就可以获得指定块的详细信息。
用法:
```
$ curl http://127.0.0.1:8888/v1/chain/get_block -X POST -d'{"block_num_or_id":5}'
```
3.get_account:获得某个账号的信息。调用这个接口,可以获得当前我的一个账号信息
用法:
```
$ curl http://127.0.0.1:8888/v1/chain/get_account -X POST -d'{"account_name":"inita"}'
```
EOS智能合约编程示例:HelloPDJ
下面有一个编程示例,给大家展示一下我怎么样写智能合约的。它这个智能合约可以用C语言(一种计算机程序语言)来写,也可以用C++(一种计算机程序语言)来写,这里面我就用C++来写。示例代码如下:
```
// hello.cpp源代码
#include
#include
using namespace eosio;
class hello : public eosio::contract {
public:
using contract::contract;
/// @abi action
void hi( account_name user ){
print( “Hello, ”, name{user} );
}
};
EOSIO_ABI( hello, (hi) )
```
如果我自己写了一个智能合约,怎么去编译和部署呢?编译的步骤大概是这样的:
####第一步,编译hello文件
```
$ eosiocpp -o hello.wast hello.cpp
$ eosiocpp -g hello.abi hello.cpp
```
####第二步,创建账号
```
$cleos create account eosio hello.code EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 ...
```
####第三步,部署合约
```
cleos set contract hello.code../hello -p hello.code
```
####第四步,调用合约
```
cleos push action hello.codehi '["user"]' -p user
```
###关于开发的一些感受
最后,我想谈谈我个人开发EOS智能合约的一些感受,在开发EOS智能合约整个使用和编译比较流畅,基本没有碰到问题,感觉系统还是比较稳定可靠的。但是,编写复杂一点的智能合约感觉无从下手,文档还是偏少,有些无关大雅的小错误。此外,我认为使用C++写智能合约门槛有点高,不知道未来的走势如何?