EOS 系統賬號的建立
當EOS啟動時,程式會去呼叫create_native_account 建立eosio等賬號,但是建立一般賬號時,卻不是呼叫create_native_account,那此時cleos create account是如何建立賬號的呢, 其實它也是呼叫eosio智慧合約的newaccount,但eosio預設的智慧合約是由程式碼提前約定好的,而不是eosio.system智慧合約。
eosio的預設合約是由eos_contract_abi.cpp
和eos_contract_abi.cpp
這兩個檔案約定好的。
eos_contract_abi.cpp 是構建智慧合約的abi資訊的。
// TODO add ricardian contracts
eos_abi.actions.push_back( action_def{name("newaccount"), "newaccount",""} );
eos_abi.actions.push_back( action_def{name("setcode"), "setcode",""} );
eos_abi.actions.push_back( action_def{name("setabi"), "setabi",""} );
eos_abi.actions.push_back( action_def{name("updateauth"), "updateauth",""} );
eos_abi.actions.push_back( action_def{name("deleteauth"), "deleteauth",""} );
eos_abi.actions.push_back( action_def{name("linkauth"), "linkauth",""} );
eos_abi.actions.push_back( action_def{name("unlinkauth"), "unlinkauth",""} );
eos_abi.actions.push_back( action_def{name("canceldelay"), "canceldelay",""} );
eos_abi.actions.push_back( action_def{name("onerror"), "onerror",""} );
eos_abi.actions.push_back( action_def{name("onblock"), "onblock",""} );
複製程式碼
eos_contract.cpp 是實現了abi定義的action方法。
/**
* This method is called assuming precondition_system_newaccount succeeds a
*/
void apply_eosio_newaccount(apply_context& context) {
auto create = context.act.data_as<newaccount>();
try {
context.require_authorization(create.creator);
// context.require_write_lock( config::eosio_auth_scope );
auto& authorization = context.control.get_mutable_authorization_manager();
//判斷公鑰是否合法。
EOS_ASSERT( validate(create.owner), action_validate_exception, "Invalid owner authority");
EOS_ASSERT( validate(create.active), action_validate_exception, "Invalid active authority");
auto& db = context.db;
auto name_str = name(create.name).to_string();
//判斷account name的合法性
EOS_ASSERT( !create.name.empty(), action_validate_exception, "account name cannot be empty" );
EOS_ASSERT( name_str.size() <= 12, action_validate_exception, "account names can only be 12 chars long" );
// Check if the creator is privileged
//只有eosio才能建立eosio.為字首的賬號。
const auto &creator = db.get<account_object, by_name>(create.creator);
if( !creator.privileged ) {
EOS_ASSERT( name_str.find( "eosio." ) != 0, action_validate_exception,
"only privileged accounts can have names that start with 'eosio.'" );
}
//判斷使用者名稱是否存在。
auto existing_account = db.find<account_object, by_name>(create.name);
EOS_ASSERT(existing_account == nullptr, account_name_exists_exception,
"Cannot create account named ${name}, as that name is already taken",
("name", create.name));
const auto& new_account = db.create<account_object>([&](auto& a) {
a.name = create.name;
a.creation_date = context.control.pending_block_time();
});
db.create<account_sequence_object>([&](auto& a) {
a.name = create.name;
});
for( const auto& auth : { create.owner, create.active } ){
validate_authority_precondition( context, auth );
}
const auto& owner_permission = authorization.create_permission( create.name, config::owner_name, 0,
std::move(create.owner) );
const auto& active_permission = authorization.create_permission( create.name, config::active_name, owner_permission.id,
std::move(create.active) );
context.control.get_mutable_resource_limits_manager().initialize_account(create.name);
int64_t ram_delta = config::overhead_per_account_ram_bytes;
ram_delta += 2*config::billable_size_v<permission_object>;
ram_delta += owner_permission.auth.get_billable_size();
ram_delta += active_permission.auth.get_billable_size();
context.trx_context.add_ram_usage(create.name, ram_delta);
} FC_CAPTURE_AND_RETHROW( (create) ) }
複製程式碼
本章通過newaccount來做示例。
我們再來找找EOS 是在哪裡給eosio設定了預設合約。直接全域性搜尋eosio_contract.
可以發現在建立eosio賬號的時候,便給他設定了abi資訊,但是action的wasm是如何匯入的呢。
找找eosio_contract.cpp的方法在哪裡引用到,會在controller.cpp找到,因為他是通過巨集去連線字元,所以全域性搜尋字元無法找到。
#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 );
void set_apply_handler( account_name receiver, account_name contract, action_name action, apply_handler v ) {
apply_handlers[receiver][make_pair(contract,action)] = v;
}
const apply_handler* controller::find_apply_handler( account_name receiver, account_name scope, action_name act ) const
{
auto native_handler_scope = my->apply_handlers.find( receiver );
if( native_handler_scope != my->apply_handlers.end() ) {
auto handler = native_handler_scope->second.find( make_pair( scope, act ) );
if( handler != native_handler_scope->second.end() )
return &handler->second;
}
return nullptr;
}
複製程式碼
按照這樣的順序可以得知,eosio的預設合約是通過find_apply_handler來呼叫的。找找哪裡呼叫了find_apply_handler。
action_trace apply_context::exec_one()
{
auto start = fc::time_point::now();
const auto& cfg = control.get_global_properties().configuration;
try {
const auto& a = control.get_account( receiver );
privileged = a.privileged;
//尋找eosio預設智慧合約方法控制程式碼
auto native = control.find_apply_handler( receiver, act.account, act.name );
if( native ) {
if( trx_context.can_subjectively_fail && control.is_producing_block()) {
control.check_contract_list( receiver );
control.check_action_list( act.account, act.name );
}
(*native)( *this );
}
if( a.code.size() > 0
&& !(act.account == config::system_account_name && act.name == N( setcode ) &&
receiver == config::system_account_name)) {
if( trx_context.can_subjectively_fail && control.is_producing_block()) {
control.check_contract_list( receiver );
control.check_action_list( act.account, act.name );
}
try {
control.get_wasm_interface().apply( a.code_version, a.code, *this );
} catch( const wasm_exit& ) {}
}
} FC_RETHROW_EXCEPTIONS(warn, "pending console output: ${console}", ("console", _pending_console_output.str()))
action_receipt r;
r.receiver = receiver;
r.act_digest = digest_type::hash(act);
r.global_sequence = next_global_sequence();
r.recv_sequence = next_recv_sequence( receiver );
const auto& account_sequence = db.get<account_sequence_object, by_name>(act.account);
r.code_sequence = account_sequence.code_sequence;
r.abi_sequence = account_sequence.abi_sequence;
for( const auto& auth : act.authorization ) {
r.auth_sequence[auth.actor] = next_auth_sequence( auth.actor );
}
action_trace t(r);
t.trx_id = trx_context.id;
t.act = act;
t.console = _pending_console_output.str();
trx_context.executed.emplace_back( move(r) );
if ( control.contracts_console() ) {
print_debug(receiver, t);
}
reset_console();
t.elapsed = fc::time_point::now() - start;
return t;
}
複製程式碼
EOS 在push action的時候會先去找找find_apply_handler有沒有該action,有的話通過它找到方法控制程式碼來執行。沒有的話直接通過wasm去執行action。
eosio賬號的預設合約的呼叫過程已經解析完畢了。
結論
eosio 的初始合約是通過硬編碼部署的,實際上也不是通過虛擬機器執行的。從程式碼上也可以看出,即使部署了 eosio.system
智慧合約,eosio 的預設合約還是會執行。