eos源码赏析(十一):EOS之从“狼人游戏”看智能合约调用及权限分配(上)
火了没几天的“狼人游戏”因某些原因遭到口诛笔伐而下线,后期参与的玩家可谓损失惨重,而这一切的操盘仿佛都指向一个并不算匿名的匿名团队,毕竟在区块链上任何动作的执行都是可追溯的。该游戏上线不久即被指出存在数据溢出风险,同时该游戏的合约可以获取到参与用户的active权限,作为一个新生事物,eos的智能合约是可以更新的,可以想象当一个智能合约的开发者拥有了用户的active权限和一颗作恶的心,后果将是不堪设想的。
今天我们就从开发者的角度来看看在eos中智能合约的调用过程中都发生了什么,当然提到智能合约调用就不得不说权限分配,本文将分为上下两篇,下篇会对eos账户权限作出一些说明。在上篇中主要包括:
-
eosio智能合约通信方式
-
eosio.token智能合约源码分析
-
eosio智能合约调用实例
eosio智能合约通信方式
关于eosio智能合约的通信方式,官方给出了具体的解释,具体请参看:
https://developers.eos.io/eosio-cpp/docs/communication-model
关于智能合约通信的部分简单的翻译如下:
eosio智能合约之间可以相互通信,比如可以让另一个智能合约执行与当前交易相关的某些操作,或者触发接下来即将发生的一些交易。eosio支持两种基本通信类型,即内联和延迟,在当前action执行的操作是内联操作的示例,而触发将来action是延迟操作的示例。
内联通信采用调用其他action的形式,这些action需要作为调用操作的一部分来执行。在确保当前action可以执行的情况下,内联操作使用和当前action相同的域和权限进行操作,这又可以被称之为嵌套action,如果action的任何部分失败了,那么内联操作将与action的其余部分一起展开。无论成功与否,调用内联操作都不会在action范围之外生成任何通知。
延时通信在概念上采用发送到对等action的动作通知的形式,根据生产者的判断,延时的操作最多可以安排在稍后的时间运行,但无法保证延时操作一定被执行。
action表示单个操作,而transaction则是一个或者多个action的集合,智能合约和账户以action的形式进行通信,如果要将action以一个整体运行,则可以单独发送action,当然也可以以组合的形式发送action。action的类型是base32编码之后的64位整数,每个transaction完成之后将生成一个交易回执,接收到transaction的hash值并不意味着transaction被确认,只是说明节点接收到这个transaction且没有错误,同样,这也意味着其他生产者可能会接收到这个transaction。通过交易确认,我们在transaction的历史记录中会看到包含着这个transaction的区块信息。
智能合约提供操作处理程序来执行所请求的action,每次action执行的时候通过apply在智能合约中实现应用操作。从eosio全局来看,每个节点都会获得每个智能合约中每个action的副本并运行。因此对智能合约来说能够确定他们是谁,他们在什么环境下运行是比较重要的。receiver是当前正在处理操作的账户,code是授权智能合约的账户,action是正在运行的操作的id。
综上所述,action是包含在transaction中的,如果一个transaction执行失败了,就必须回滚transaction中所有的action,比较重要的是当前transaction中的交易数据,还包含有transaction的相关头信息,transaction中所有原始action的有序容器存储,transaction中所有已释放的容器存储,由代码定义的可修复的数据集,以及blob向量的完整索引。
eosio.token智能合约源码分析
有很多朋友加了我好友之后,最经常问的问题就是可以教我怎么在eos上发币么。作为开发人员,如果一心只想着发币我想这种心态还是不成熟的。况且,如果真的对这些真的感兴趣,按照官方的指引一步步来就可以完成整个发币的过程,只不过在主网上要耗费一定量的ram罢了。接下来我们就简单的了解下“发币”的合约即eosio.token,其内容在
https://developers.eos.io/eosio-cpp/docs/token-tutorial中可以看到,相信部署过该合约的朋友都已经了解了以下操作:
-
cleos push action eosio.token create '[ "eosio", "1000000000.0000 SYS"]' -p [email protected]
-
cleos push action eosio.token issue '[ "user", "100.0000 SYS", "memo" ]' -p [email protected]
-
cleos push action eosio.token transfer '[ "user", "tester", "25.0000 SYS", "m" ]' -p [email protected]
在创建eosio.token这个账户之后部署智能合约eosio.token,接下来的create、issue、transfer分别完成创建你自己的币、向用户user空投(转账)100你的已发行的币、用户user向用户tester转账25已发行的币,请注意这三步操作均使用了当前操作账户的active权限。其余部分不再做过多介绍,来看源码中这三个函数的实现:
图1 token::create的实现
我们可以看到,在创建该货币的时候进行了一系列的校验,此处require_auth是用来校验当前action执行的账户是否有权限,如果没有权限则当前action将会终止,事务会回滚。在issue和transfer中也进行了相应的校验,整体来看逻辑并不复杂,关于stats的定义,涉及到multi_index我们会在接下来介绍multi_index的时候进行分析,最后在statstable中存储记录了币的发行者、币的名字、发币的数量。
图2 token::issue的实现
issue负责向用户空投(转)你发的币,前面判断略过不提,在最后以内联的方式使用SEND_LINLINE_ACTION调用了一次本合约中的transfer方法,可以看到参数的对应关系。如前文中提到的,内联操作使用和当前action相同的域和权限进行操作,即active权限。在群中有大佬(Jimmy Guo)提出,可以尽量少用issue向用户空投,而是先创建一个空投账户,通过空投账户使用transfer向用户转币,这样可以节省不少的cpu。
图3 token::transfer的实现
transfer的逻辑也相对简单,有兴趣的朋友可以自己调试下。在图3中圈注的地方,是将转账账户和收款账户加入到待通知列表,以确认两者均可以收到相应的通知。在require_recipient的注释中我们可以看到,是为了减少c接口的调用次数而将需要通知的账户添加至账户列表。
图4 require_recipient的相关解释
智能合约之间的通信
了解了智能合约之间的通信方式即内联通信和延迟通信,让我们来了解下两个智能合约之间的相互调用。仍旧以被口诛笔伐的狼人游戏为例,我们新建两个智能合约,合约名分别为wolf和human,要知道,在狼的字典里,每一个字里行间都写着吃人二字。
智能合约wolf的内容如下:
图5 智能合约wolf的内容
智能合约human的内容如下:
图6 智能合约human的内容
接下来就是我们熟悉的生成wast文件、生成abi文件、创建账户、部署合约、更新狼账户的权限,具体操作步骤如下:
1、eosiocpp -o wolf.wast wolf.cpp
2、eosiocpp -g wolf.abi wolf.cpp
3、eosiocpp -o human.wast human.cpp
4、eosiocpp -g human.abi human.cpp
5、cleos create account eosio wolf.code EOS4uN9j2wpMNNzF74sHVmt1ZS3tdfAZygwWJPrPNjt5uRLWyDbAe EOS4uN9j2wpMNNzF74sHVmt1ZS3tdfAZygwWJPrPNjt5uRLWyDbAe
6、cleos create account eosio human EOS4uN9j2wpMNNzF74sHVmt1ZS3tdfAZygwWJPrPNjt5uRLWyDbAe EOS4uN9j2wpMNNzF74sHVmt1ZS3tdfAZygwWJPrPNjt5uRLWyDbAe
7、cleos set contract human /home/chen/eos/contracts/human -p [email protected]
8、cleos set contract wolf.code /home/chen/eos/contracts/wolf -p [email protected]
9、cleos set account permission wolf.code active '{"threshold": 1,"keys": [{"key": "EOS4uN9j2wpMNNzF74sHVmt1ZS3tdfAZygwWJPrPNjt5uRLWyDbAe","weight": 1}],"accounts": [{"permission":{"actor":"wolf.code","permission":"eosio.code"},"weight":1}]}' owner -p wolf.code
然后我们执行,杀 这个动作,人就被狼杀了。
cleos push action wolf.code kill '["wolf.code","这才是狼人游戏"]' -p [email protected]
图7 狼开始杀人
本文从内联通信开始,简单的介绍了eosio.token合约的部署,及发币、空投、转账等操作,其中涉及到内联通信,及权限校验的相关内容。最后通过一个简单的狼人游戏介绍了两个智能合约之间是如何调用和通信的,关于权限的校验,我们会在下一篇文章中作出详细的说明,敬请期待。
有些游戏,我们可以参与,但是有的游戏从本质上就不适合我们参与,本来你想去做一头狼去杀人,没想到却成了狼王的果腹品。
如果你觉得我的文章对你有一定的帮助,请点击文章末尾的喜欢该作者。
如果你对eos开发感兴趣,欢迎关注本公众号,一起学习eos开发。
微信公众号
有任何疑问或者指教请添加本人个人微信,当然有对eos开发感兴趣或者金庸粉的也可以添加一起交流,备注eos开发或金庸。
个人微信号