cleos create account を実行する時の流れを調べてみました

[account 情報の保存場所を調べてみました](https://qiita.com/blueplanet/items/23db9f493c6718aad3d4)　で `newaccount` の例をあげましたが、実際 `cleos create account` を実行する時に、どうやって `newaccount` 関数まで辿ったのかは把握出来なかったので、もう少し調べてみました。

## 処理ハンドラー登録
事前にシステム側で用意している処理をハンドラーに登録しています。

```c++
// 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`で検索し、ネイティブ処理ハンドラーが登録されている場合は、それを実行してから、アカウントにコントラクトがデプロイされていればそのコントラクトのアクションも実行するようになっています。

```c++
// 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` に対してアクションが送信されることが同じです。

```c++
// 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` アクションが実行される流れになっています
- ネイティブ実装とコントラクトの実装両方あるので、どう切り分けるべきかの設計は難しいところです