交易所开发系统如何重新编译并部署合约(国王小组)
动作的授权
EOSIO 区块链使用非对称密码学来验证推送交易的账户是否已使用匹配的私钥签署了交易。使用账户权限表来检查账户是否具有执行操作所需的权限。使用授权是保护智能合约的第一步。
本文档提供了四种方式在合约代码中进行授权检查。
1、has_auth(name n) 函数
验证指定账户是否与调用动作的账户相符,返回bool值。
以hi动作为例,如果我们希望动作只能向调用账户进行问好,而不相符的账户名进行消息提示,可以将动作部分的代码做如下调整:
[[eosio::action]] void hi( eosio::name user ) {
if(has_auth( user )){
print("Hello, ", eosio::name{user} );
}else{
print("This is not ",eosio::name{user} );
}
}
重新编译并部署合约:
eosio-cpp -abigen -o hello.wasm hello.cpp
cleos set contract hello /home/xxx/biosboot/genesis/hello -p hello@active
使用alice账户对bob账户和alice账户分别调用hi动作。结果分别如下:
cleos push action hello hi '["bob"]' -p alice@active
executed transaction: 20a297df95fe19840893305a8f18e6380ee3482a3773885224f6381487559105 104 bytes 601 us
hello <= hello::hi {"user":"bob"}
This is not bob
cleos push action hello hi '["alice"]' -p alice@active
executed transaction: 3f12347d3458204be643a2c4cef5ef3542994ed3bb320466aac423e1a983e8d7 104 bytes 204 us
hello <= hello::hi {"user":"alice"}
Hello, alice
2、require_auth(name n) 函数
验证指定账户是否与调用动作的账户相符,不相符则直接报错失败。
与has_auth函数不同,此函数会在验证失败时直接停止运行。同样以hi动作为例,如果我们希望动作只能向调用账户进行问好,而对不相符的账户直接显示失败,可以将动作部分的代码做如下调整:
[[eosio::action]] void hi( eosio::name user ) {
require_auth( user );
print("Hello, ", eosio::name{user} );
}
重新编译并部署合约:
eosio-cpp -abigen -o hello.wasm hello.cpp
cleos set contract hello /home/xxx/biosboot/genesis/hello -p hello@active
使用alice账户对bob账户和alice账户分别调用hi动作。结果分别如下:
cleos push action hello hi '["bob"]' -p alice@active
Error 3090004: Missing required authority
Ensure that you have the related authority inside your transaction!;
If you are currently using 'cleos push action' command, try to add the relevant authority using -p option.
Error Details:
missing authority of bob
pending console output:
cleos push action hello hi '["alice"]' -p alice@active
executed transaction: 495b0e8d6fa3610f5b91ffd28eb4013d1a53a1e1e13046b6a3069566bfeaf65f 104 bytes 168 us
hello <= hello::hi {"user":"alice"}
Hello, alice
3、require_auth2(capi_name name, capi_name permission) 函数
验证指定账户和权限是否与调用动作的账户和权限相符,不相符则直接报错失败。
此函数比之前增加了对账户权限的限制。同样以hi动作为例,如果我们希望动作只能由账户的active权限进行调用,而对不相符的账户直接显示失败,可以将动作部分的代码做如下调整:
[[eosio::action]] void hi( eosio::name user ) {
require_auth2(user.value, "active"_n.value);
print("Hello, ", eosio::name{user} );
}
重新编译并部署合约:
eosio-cpp -abigen -o hello.wasm hello.cpp
cleos set contract hello /home/xxx/biosboot/genesis/hello -p hello@active
使用alice账户的family和active权限分别对alice账户调用hi动作。结果分别如下:
cleos push action hello hi '["alice"]' -p alice@family
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.
Error Details:
transaction declares authority '{"actor":"alice","permission":"family"}', but does not have signatures for it.
cleos push action hello hi '["alice"]' -p alice@active
executed transaction: 055228767c6f23c283 910ebc348d8a76d444fa1165454c9886dd58b940023ef5 104 bytes 395 us
hello <= hello::hi {"user":"alice"}
Hello, alice
4、check(bool pred, ...) 函数
断言,如果pred为假,则使用提供的消息进行反馈。例如:
eosio::check(a == b, "a does not equal b");
因此,之前我们实现的在参数与账户不相符时打印错误信息的代码可以优化为:
[[eosio::action]] void hi( eosio::name user ) {
eosio::check(has_auth(user), "User is not authorized to perform this action.");
print("Hello, ", eosio::name{user} );
}
重新编译并部署合约:
eosio-cpp -abigen -o hello.wasm hello.cpp
cleos set contract hello /home/xxx/biosboot/genesis/hello -p hello@active
使用alice账户对bob账户和alice账户分别调用hi动作。结果分别如下:
cleos push action hello hi '["bob"]' -p alice@active
Error 3050003: eosio_assert_message assertion failure
Error Details:
assertion failure with message: User is not authorized to perform this action.
pending console output:
cleos push action hello hi '["alice"]' -p alice@active
executed transaction: 86887c76f45d9c0f9abc017f2cfc49132bd5d0c1d6bbd7f41aeb7bc1675ab42c 104 bytes 172 us
hello <= hello::hi {"user":"alice"}
Hello, alice
关于上链
在之后的代码实践中,开发人员可以通过以上这四种授权检查代码的搭配来实现合约中动作的授权管理。
对于触发Error导致方法运行中断的情况,交易记录不会上链。只有当交易ID产生,动作正常运行完成,此次记录才会记录在链上。
对于上文的举例来说,第一种代码的错误案例完成了上链。因为交易正常结束,并打印出了错误信息。而后面三种分别触发了Error 3090003、Error 3090004、Error 3050003,导致动作中断,数据不会上链。
五、常见问题
(一)部署合约时遇到错误
<3>error 2022-08-08T08:44:27.913 cleos main.cpp:4371 operator() ] Failed with error: unspecified (0)
Unable to resolve path './hello'
遇到Unable to resolve path错误,可以将合约地址改为绝对路径,避免因相对路径产生的报错。
(二)require_auth2编译错误
/home/xxx/biosboot/genesis/hello/hello.cpp:6:10: error: use of undeclared identifier 'require_auth2'; did you mean 'eosio::internal_use_do_not_use::require_auth2'?
require_auth2(user.value, "active"_n.value);
^~~~~
eosio::internal_use_do_not_use::require_auth2
常规情况中可依据报错提示信息将require_auth2补充为eosio::internal_use_do_not_use::require_auth2解决。但本例中该前缀已经表明这是一个不推荐使用的方法。
故推荐的解决方法为引入action头文件。