account 情報の保存場所を調べてみました で newaccount
の例をあげましたが、実際 cleos create account
を実行する時に、どうやって newaccount
関数まで辿ったのかは把握出来なかったので、もう少し調べてみました。
処理ハンドラー登録
事前にシステム側で用意している処理をハンドラーに登録しています。
// libraries/chain/controller.cpp
// 下記のように、`eosio`の`newaccount`アクションの呼び出しを、`apply_eosio_newaccount`の `apply_handler` に指すように設定しています
#define SET_APP_HANDLER( receiver, contract, action) \
set_apply_handler( #receiver, #contract, #action, &BOOST_PP_CAT(apply_, BOOST_PP_CAT(contract, BOOST_PP_CAT(_,action) ) ) )
SET_APP_HANDLER( eosio, eosio, newaccount );
SET_APP_HANDLER( eosio, eosio, setcode );
SET_APP_HANDLER( eosio, eosio, setabi );
SET_APP_HANDLER( eosio, eosio, updateauth );
SET_APP_HANDLER( eosio, eosio, deleteauth );
SET_APP_HANDLER( eosio, eosio, linkauth );
SET_APP_HANDLER( eosio, eosio, unlinkauth );
/*
SET_APP_HANDLER( eosio, eosio, postrecovery );
SET_APP_HANDLER( eosio, eosio, passrecovery );
SET_APP_HANDLER( eosio, eosio, vetorecovery );
*/
SET_APP_HANDLER( eosio, eosio, canceldelay );
実行時の切り分け
アクションが実行される時は、まず find_apply_handler
で検索し、ネイティブ処理ハンドラーが登録されている場合は、それを実行してから、アカウントにコントラクトがデプロイされていればそのコントラクトのアクションも実行するようになっています。
// libraries/chain/apply_context.cpp の apply_context::exec_one
...
auto native = control.find_apply_handler( receiver, act.account, act.name );
if( native ) {
...
(*native)( *this );
}
if( a.code.size() > 0 ... {
...
try {
control.get_wasm_interface().apply( a.code_version, a.code, *this );
} catch( const wasm_exit& ) {}
}
cleos の動き
cleos create account
とcleos system newaccount
によってパラメータが異なる部分がありますが、結果的にはeosio
に対してアクションが送信されることが同じです。
// programs/cleos/main.cpp
create_account_subcommand(CLI::App* actionRoot, bool s) : simple(s) {
auto createAccount = actionRoot->add_subcommand(
(simple ? "account" : "newaccount"),
(simple ? localized("Create a new account on the blockchain (assumes system contract does not restrict RAM usage)")
: localized("Create a new account on the blockchain with initial resources") )
);
createAccount->add_option("creator", creator, localized("The name of the account creating the new account"))->required();
createAccount->add_option("name", account_name, localized("The name of the new account"))->required();
createAccount->add_option("OwnerKey", owner_key_str, localized("The owner public key for the new account"))->required();
createAccount->add_option("ActiveKey", active_key_str, localized("The active public key for the new account"));
...
createAccount->set_callback([this] {
...
auto create = create_newaccount(creator, account_name, owner_key, active_key);
if (!simple) {
...
if ( net.get_amount() != 0 || cpu.get_amount() != 0 ) {
action delegate = create_delegate( creator, account_name, net, cpu, transfer);
send_actions( { create, buyram, delegate } );
} else {
send_actions( { create, buyram } );
}
} else {
send_actions( { create } );
}
});
}
まとめ
-
cleos create account
を実行すると、アクションが送信され、libraries/chain/eosio_contract.cpp
のapply_eosio_newaccount
関数が実行されてから、eosio.system.cpp
のnative::newaccount
アクションが実行される流れになっています - ネイティブ実装とコントラクトの実装両方あるので、どう切り分けるべきかの設計は難しいところです