[EOS源码分析] EOS保留权限eosio.code深度解读
inline action简单来说就是action调用另外一个action, 具体来说就是一个智能合约的代码调用另外一个智能合约的函数。
eoiso.code这一特殊权限是dawn4.0后新增的内部特殊权限,用来加强inline action的安全性。比如alice调用智能合约contract1.test,一开始alice看过contract1.test的逻辑,发现它只是一个打印函数,并不会调用其他合约。所以alice以自己active的权限alice@active去执行contract1.test。但是contract1的拥有者某一天可能偷偷更改了test的实现,在test函数中调用eosio.token的transfer函数以alice@active权限就可以取走alice的EOS. 为了解决权限乱用问题,EOS新增了eosio.code这个特殊权限。采用eosio.code后,contract1.test要以alice@active去调用eosio.token,必须得到alice的授权,即必须在alice@active里添加contrac1@eosio.code授权
$cleos set account permission alice active '{"threshold": 1,"keys": [{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":1}],"accounts": [{"permission":{"actor":"contract1","permission":"eosio.code"},"weight":1}]}' owner -p alice@owner
即用户调用push action -p permission授权的权限只作用域该action,要用到其他action必须再授权eosio.code
Inline action权限分析
我们以【inline action开发实践】博文中的实例为例,hello.code智能合约调用hello.target合约
class hello : public eosio::contract {
public:
using contract::contract;
/// @abi action
void hi( account_name from, account_name to) {
require_auth(from);
print( "Hello, from:", name{from}, ", to:", name{to});
action(
//这里{to, active}必须授权给{_self, eosio.code}
permission_level{to, N(active)},
//调用 hello.target合约 的'callme' action
N(hello.target), N(callme),
std::make_tuple(to)
).send();
}
};
通过下面的命令执行hello.code智能合约 $cleos push action hello.code hi '["args.user","args.user1"]' -p args.user
调用时序图如下
时序图中有2次调用check_authorization
- 3中的check_authorization是检测交易的签名是否满足action 'hello.code@hi'调用的权限声明args.user@active,这个检测机制已经在【EOS权限机制】一文已经详细分析过了
- 11中的check_authorization是检测hello.code@eosio.code是否满足action "hello.target@callme"的权限声明to@active(这里的to='args.user1'),也就是args.user1@active。所以为了让这个inline action调用成功,必须添加如下授权
$cleos set account permission args.user1 active '{"threshold": 1,"keys": [],"accounts": [{"permission":{"actor":"hello.code","permission":"eosio.code"},"weight":1}]}' owner -p args.user1@owner
这里有个疑问,为啥是检测(hello.code, eosio.code)授权是否满足权限声明呢?我们仔细看下上面的hi代码
void hi( account_name from, account_name to) {
require_auth(from);
print( "Hello, from:", name{from}, ", to:", name{to});
action(
//这里{to, active}必须授权给{_self, eosio.code}
permission_level{to, N(active)},
//调用 hello.target合约 的'callme' action
N(hello.target), N(callme),
std::make_tuple(to)
).send();
}
当hello.code智能合约代码通过action.send调用其他智能合约时,hi代码是拿不到任何私钥的,也就没法为声明的权限签名,即没法证明该智能合约具备action声明的权限to@active。因此,只有系统代码做担保了,因而系统提出了一个虚拟权限eosio.code。然后系统直接告诉系统检验逻辑(authorization_manager) ,‘action(hello.target)’已经具备hello.code@eosio.code权限。然后authorization_manager只需检验to@active是否授权给hello.code@eosio.code即可。通过这种虚拟的权限证明解决了合约调用合约的权限检测问题
上面红色的部分就是系统代为担保的权限证明。对于用户直接提交的action,这个权限证明是从transaction的签名里恢复出来的