上篇文章中型宙,我們講解了deferred action
和inline action
,并且舉了兩個例子伦吠。然而妆兑,那兩個例子中,我們從合約發(fā)出的action毛仪,都是發(fā)給自己的搁嗓。那么可以發(fā)給其他的合約嗎?這就引出今天的知識點了:神秘的eosio.code
箱靴。
eosio.code
在狼人殺游戲里
有用到腺逛,并且備受爭議;本文將詳細介紹一下eosio.code
衡怀。
在開菜
之前棍矛,我們先做好準備工作:
更新一下MakeContract
MakeContract
是我們之前寫的一個編譯腳本安疗。為了便于后面的使用,更改了一下這個腳本:
#/bin/bash
cd $1
filename=`basename $1`
eosiocpp -o $filename.wast $filename.cpp
eosiocpp -g $filename.abi $filename.cpp
相比之前的腳本茄靠,此腳本可以編譯子文件夾下的合約了茂契。
編寫兩個合約send.code
和recv.code
我們先在~/eos-workspace
里面建立相關(guān)的智能合約文件夾:
mkdir testAB
cd testAB
mkdir sender
mkdir receiver
在testAB/sender
文件夾里建立一個sender.cpp
蝶桶,并寫入如下合約內(nèi)容:
#include <eosiolib/eosio.hpp>
#include <eosiolib/transaction.hpp>
#include <eosiolib/asset.hpp>
using namespace eosio;
class sender : public eosio::contract {
public:
using contract::contract;
void send( account_name user ) {
require_auth( user );
print( "before send inline code sender Say, ", name{user});
action(
permission_level{user, N(active)},
N(recv.code), N(receive),
user).send();
}
};
EOSIO_ABI( sender, (send) )
可以看到慨绳,這個合約中有個inline action
,它在原action的處理器里真竖,向recv.code
發(fā)了個receive
action脐雪。
然后在testAB/receiver
文件夾里建立一個receiver.cpp
,并寫入如下合約內(nèi)容:
#include <eosiolib/eosio.hpp>
#include <eosiolib/transaction.hpp>
using namespace eosio;
class receiver1 : public eosio::contract {
public:
using contract::contract;
void receive( account_name user ) {
require_auth( user );
print( "receiver Say, ", name{user});
}
};
EOSIO_ABI( receiver1, (receive) )
這個合約更為簡單恢共,僅僅打印一個文字战秋,代表收到了receive
action。
你可能注意到了讨韭,這個類名是receiver1
而不是receiver
脂信,是為了避免與EOSIO_ABI
中的receiver名字沖突,從而引起編譯錯誤透硝。
編譯和部署合約
以前都介紹過狰闪,這里僅列出命令,不再做解釋了:
./MakeContract testAB/receiver
./MakeContract testAB/sender
cleos create account user send.code \
EOS83sN8bfKGk3jTBezN41UN7LfXSVFa1w3YQcGApE67J26t3HLcr \
EOS83sN8bfKGk3jTBezN41UN7LfXSVFa1w3YQcGApE67J26t3HLcr
cleos create account user recv.code \
EOS83sN8bfKGk3jTBezN41UN7LfXSVFa1w3YQcGApE67J26t3HLcr \
EOS83sN8bfKGk3jTBezN41UN7LfXSVFa1w3YQcGApE67J26t3HLcr
cleos wallet unlock
cleos set contract recv.code ./testAB/receiver -p recv.code@active
cleos set contract send.code ./testAB/sender -p send.code@active
向send.code
合約發(fā)送send
action
~ cleos push action send.code send '["user"]' -p user@active
Error 3090003: Provided keys, permissions, and delays do not satisfy declared authorizations
Ensure that you have the related private keys inside your wallet and your wallet is unlocked.
再看一下nodeos的output濒生,有如下內(nèi)容:
pending console output: before send inline code sender Say, user
{"console":"before send inline code sender Say, user"}
send
action handler 的 log已經(jīng)在在pending console
里了埋泵,說明send.code
合約的send
action handler已經(jīng)成功觸發(fā)了。后面的錯誤是inline action
失敗的信息罪治。
為什么會失敗呢丽声?
我們想想看,如果一個用戶向某個合約里發(fā)送了action消息觉义,這個合約就能夠使用該用戶的權(quán)限發(fā)送任何的inline action
是不是很可怕雁社?比如把該用戶的所有的EOS
都轉(zhuǎn)走,那用戶豈不是很慘晒骇?
在一開始霉撵,EOS的確存在這個漏洞,后來為了限制合約濫用用戶的授權(quán)厉碟,就不再允許inline action
使用原有action的權(quán)限了喊巍。
那沒有用戶授權(quán),智能合約如何互相發(fā)inline action
消息呢箍鼓?
BM想了一個辦法崭参,設(shè)計了一個eosio.code
許可,這個許可是虛擬的款咖。我們對比下這個方式和之前方式的區(qū)別:
以前的方式是:用戶對交易(或者稱為事務(wù))簽名授權(quán)后何暮,發(fā)送給合約奄喂,合約收到action之后,以
當前合約被授權(quán)的許可
執(zhí)行新的inline action
海洼。我們知道用戶通常使用active
許可簽名的跨新,并且此權(quán)限可以做很多事情,比如把資產(chǎn)轉(zhuǎn)移等等坏逢。
現(xiàn)在的方式是:用戶把交易(或者稱為事務(wù))簽名域帐,發(fā)送給合約,合約收到action之后是整,以當前合約賬號的eosio.code
許可肖揣,執(zhí)行inline action
。
解決方案
根據(jù)我們之前學的賬號和權(quán)限相關(guān)的知識浮入,如果要讓send.code
的eosio.code
能夠代表user執(zhí)行inline action
龙优,那么需要四步:
- 給
send.code
設(shè)置一個eosio.code
許可- 把
user
的某個許可(可以是active,不過我們這次不用active了事秀,新建一個sendp
許可吧)授權(quán)給send.code
的eosio.code
許可- 把user的這個
sendp
許可和recv.code
的receive
關(guān)聯(lián)起來- 因為新建了一個
sendp
許可彤断,所以為了能向send.code
發(fā)送send
action,我們還需要把它和send.code
的send
action關(guān)聯(lián)起來易迹。
我們先給send.code
加上一個eosio.code
許可:
~ cleos set account permission send.code eosio.code EOS83sN8bfKGk3jTBezN41UN7LfXSVFa1w3YQcGApE67J26t3HLcr -p send.code@active
Error 3050000: Action validate exceptio
失敗了宰衙,nodeos的output有詳情:
Permission names that start with 'eosio.' are reserved
說明eosio.
是保留給系統(tǒng)用的,我們沒法給合約賬號設(shè)置eosio.code
許可了赴蝇。這也說明了菩浙,我們無需給合約設(shè)置eosio.code
許可,系統(tǒng)會幫我們處理好相關(guān)的問題句伶。
那我們進入第二步吧劲蜻, 給user
新建一個許可sendp
,并把它授權(quán)給send.code
的eosio.code
許可:
~ cleos set account permission user sendp '{"threshold": 1,"keys": [{"key":"EOS83sN8bfKGk3jTBezN41UN7LfXSVFa1w3YQcGApE67J26t3HLcr", "weight":1}],"accounts": [{"permission":{"actor":"send.code","permission":"eosio.code"},"weight":1}]}' owner -p user@owner
executed transaction: aa60976084adbada1d89b35a023a88cc2d8535a284d1fca04084a952168fbfd6 184 bytes 1076 us
# eosio <= eosio::updateauth {"account":"user","permission":"sendp","parent":"owner","auth":{"threshold":1,"keys":[{"key":"EOS83s...
很成功考余。
進入第三步先嬉,把user的sendp
許可和recv.code
的receive
關(guān)聯(lián)起來:
~ cleos set action permission user recv.code receive sendp -p user@active
executed transaction: f85293c5ef00f1ca56b8ae75f02b74f94f45e22d8dbf31ffddc4dc235e60c492 128 bytes 4338 us
# eosio <= eosio::linkauth {"account":"user","code":"recv.code","type":"receive","requirement":"sendp"}
第四步, 把sendp
和send.code
的send action
關(guān)聯(lián)起來:
~ cleos set action permission user send.code send sendp -p user@active
executed transaction: d4670605d0a64648158cc96ade52b86fc6818a5fa0d62c393acccaca6106a254 128 bytes 943 us
# eosio <= eosio::linkauth {"account":"user","code":"send.code","type":"send","requirement":"sendp"}
再修改一下send.code
的合約代碼楚堤,主要是發(fā)送action這里疫蔓,把原來的active
許可,修改為sendp
許可:
action(
permission_level{user, N(sendp))},
N(recv.code), N(receive),
user).send();
最后身冬,我們使用sendp
許可向send.code
發(fā)送send
action:
~ cleos push action send.code send '["user"]' -p user@sendp
executed transaction: 7c5262c16ca32fd809258794e259846f012ae6ba57b97b6fda0c3c7a13849ca3 104 bytes 2534 us
# send.code <= send.code::send {"user":"user"}
>> before send inline code sender Say, user
# recv.code <= recv.code::receive {"user":"user"}
>> receiver Say, user
哈哈衅胀,成功了。
注意事項
- 我們在給
user
設(shè)置sendp
許可的時候酥筝,使用的是這樣的命令:
~ cleos set account permission user sendp '{"threshold": 1,"keys": [{"key":"EOS83sN8bfKGk3jTBezN41UN7LfXSVFa1w3YQcGApE67J26t3HLcr", "weight":1}],"accounts": [{"permission":{"actor":"send.code","permission":"eosio.code"},"weight":1}]}' owner -p user@owner
我們除了授權(quán)給send.code
的eosio.code
許可外滚躯,還授權(quán)給了一個key,其實這個key是什么無關(guān)緊要,只要你的錢包里有這個key就可以了掸掏。問題是茁影,可以把這個key去掉嗎?可以只授權(quán)給send.code
的eosio.code
許可嗎丧凤?
答案是不可以募闲。如果只授權(quán)給send.code
的eosio.code
許可,那么觸發(fā)send.code
的send
action的交易愿待,就找不到合適的key簽名浩螺,因為用戶是沒有eosio.code
許可所對應(yīng)的key的。
- 真的會比沒有
eosio.code
許可權(quán)限之前的情況要好嗎呼盆?
在之前沒有eosio.code
許可權(quán)限以前年扩,系統(tǒng)允許inline action
以原有的action的權(quán)限運行,存在重大隱患访圃,因為合約可以很容易的轉(zhuǎn)移到用戶的資產(chǎn)。
在引入eosio.code
之后相嵌,原來的方式行不通了腿时。合約要想代表用戶,必須要用戶自己同意添加eosio.code
授權(quán)饭宾。這中間多了個用戶授權(quán)的過程批糟。如果用戶信任該合約,那么可以授權(quán)看铆;如果不信任徽鼎,則不予授權(quán),相對來說的確安全了一些弹惦。
等一下否淤,這真的安全嗎?
這真的安全嗎棠隐?狼人殺也用了eosio.code
石抡,不是說有漏洞嗎?我們下次詳解助泽。
簡介:不羈啰扛,一名程序員;專研EOS技術(shù)嗡贺,玩轉(zhuǎn)EOS智能合約開發(fā)隐解。
微信公眾號:know_it_well
知識星球地址:https://t.zsxq.com/QvbuzFM