整体插件
文件目录说明如下:
1、contracts目录
这个目录包含了EOS基础合约和示例合约,以及这些合约使用的库:eosiolib库、libc++库、musl库;
其中eosiolib库是EOS官方编写的库,libc++库、musl库是外部库。
2、plugins目录
EOS采用插件化设计,这个目录包含了EOS项目所用到的插件。
3、libraries目录
这个目录包含了具体实现代码需要引用的基础类库:
abi-generator:生成ABI的类库
appbase:application基础类库
chain、chainbase:区块链基础类库
fc:通用函数库
wasm-jit:WebAssembly JIT编译库
4、programs目录
这个目录包含EOS最终构建的应用程序(eos/build/programs目录下)的源码:
cleos:命令行客户端
nodeos:服务端守护进程
keosd:钱包守护进程
eosio-abigen:ABI生成器
eosio-launcher:多节点启动器
snapshot:配置创世区块和创始相关工具的Web程序
5、governance目录
这个目录包含的不是源代码,而是EOS社区治理文件,觉得比较重要,提到这里:
bp_agreement.md:区块生产者需要遵守的协议
constitution.md:EOS宪法
6、externals目录
这个目录包含的是外部项目:
binaryen:WebAssembly编译器和工具链基础库
magic_get:C++编译期反射库
EOS采用插件化设计,插件是一种高度解耦的设计模式,它把不同的程序功能模块独立开来,需要时再进行自由组合;
插件的原理是:
所有插件继承自同一个基类,这个基类会定义一系列生命周期函数,插件子类需要实现这些函数;
一般会有插件管理器来管理插件,比如注册到主程序中,或从主程序中注销;
主程序也会定义一系列生命周期函数,这些函数内部一般是对注册了的插件进行遍历,调用它们公共接口的函数;
这样主程序和插件就绑定到了一起,主程序一个生命周期函数的调用,会让注册了的插件的对应生命周期函数都得到调用;
不需要使用插件时可以注销,这样就实现了即插即用的灵活设计。
3、所有插件(plugin)都派生自同一个基类:appbase::abstract_plugin,它的位置在:eos/libraries/appbase/include/appbase/plugin.hpp;
4、appbase::abstract_plugin是一个抽象类,有一个继承它的实现类:appbase::plugin,位于eos/libraries/appbase/include/appbase/application.hpp文件中;
5、其他所有插件都会继承appbase::plugin类,它们的继承关系如下:
plugin的生命周期
每个插件都会经历如下生命周期:
注册(register_plugin)
配置(set_program_options)
初始化(plugin_initialize)
启动(plugin_startup)
关闭(plugin_shutdown)
插件如何通信
一、执行命令行创建账号
1、创建钱包,导入秘钥
2、创建账号
cleos create account eosio test EOS5VZk1B9HqPNypN9XczvrtT6AkaDgZafyAcD6xovh63uC133stj
二、代码流程
堆栈图:
1、cleos解析命令行,组织并发送http协议(参见https://blog.csdn.net/weichanghu_/article/details/81414529)。
cleos create account eosio test EOS5VZk1B9HqPNypN9XczvrtT6AkaDgZafyAcD6xovh63uC133stj
此处简单介绍一下代码流程:
main → send_actions → push_actions → push_transaction → call → do_http_call → (httpc.cpp)do_connect → do_txrx
使用Clion调试时会发现cleos最终发送的是push_transaction的POST请求。
2、nodeos加载http_plugin插件并注册处理函数:
main → app().startup() → plugin->startup() → (chain_api_plugin.cpp) plugin_startup → (http_plugin.hpp)
add_api → add_handler → url_handlers.insert()
其中add_api对push_transaction消息的处理函数是read_write::push_transaction,代码如下:
CHAIN_RW_CALL_ASYNC(push_transaction, chain_apis::read_write::push_transaction_results, 202)
3、http_plugin插件监听端口,接收消息并调用处理函数
(http_plugin.cpp)plugin_startup → create_server_for_endpoint → handle_http_request → handle_http_request →
handler_itr->second()
所以,nodeos收到push_transaction请求后就调用了read_write::push_transaction,该函数代码如下:
void read_write::push_transaction(const read_write::push_transaction_params& params, next_function<read_write::push_transaction_results> next) {
try {
auto pretty_input = std::make_shared<packed_transaction>();
auto resolver = make_resolver(this, abi_serializer_max_time);
try {
abi_serializer::from_variant(params, *pretty_input, resolver, abi_serializer_max_time);
} EOS_RETHROW_EXCEPTIONS(chain::packed_transaction_type_exception, "Invalid packed transaction")
app().get_method<incoming::methods::transaction_async>()(pretty_input, true, [this, next](const fc::static_variant<fc::exception_ptr, transaction_trace_ptr>& result) -> void{
if (result.contains<fc::exception_ptr>()) {
next(result.get<fc::exception_ptr>());
} else {
auto trx_trace_ptr = result.get<transaction_trace_ptr>();
try {
fc::variant pretty_output;
pretty_output = db.to_variant_with_abi(*trx_trace_ptr, abi_serializer_max_time);
chain::transaction_id_type id = trx_trace_ptr->id;
next(read_write::push_transaction_results{id, pretty_output});
} CATCH_AND_CALL(next);
}
});
} catch ( boost::interprocess::bad_alloc& ) {
raise(SIGUSR1);
} CATCH_AND_CALL(next);
}
函数先将交易数据封装成packed_transaction对象,然后通过信号槽的异步方法app().get_method<incoming::methods::transaction_async>()将该对象传给了producer_plugin,该插件在初始化的时候会注册处理函数on_incoming_transaction_async()接收交易数据,
my->_incoming_transaction_async_provider = app().get_method<incoming::methods::transaction_async>().register_provider([this](const packed_transaction_ptr& trx, bool persist_until_expired, next_function<transaction_trace_ptr> next) -> void {
return my->on_incoming_transaction_async(trx, persist_until_expired, next );
});
并在这一步将交易数据传给了controller.cpp里的push_transaction函数,
auto trace = chain.push_transaction(std::make_shared<transaction_metadata>(*trx), deadline);
4、controller.cpp里的push_transaction函数进一步处理该请求,接收的transaction_metadata_ptr就是交易数据:
transaction_trace_ptr push_transaction( const transaction_metadata_ptr& trx,
fc::time_point deadline,
uint32_t billed_cpu_time_us,
bool explicit_billed_cpu_time = false )
{
EOS_ASSERT(deadline != fc::time_point(), transaction_exception, "deadline cannot be uninitialized");
transaction_trace_ptr trace;
try {
transaction_context trx_context(self, trx->trx, trx->id);
。。。。。。
trx_context.exec();
。。。。。。
emit(self.applied_transaction, trace);
。。。。。。
emit( self.accepted_transaction, trx );
emit( self.applied_transaction, trace );
return trace;
} FC_CAPTURE_AND_RETHROW((trace))
} /// push_transaction
该函数进入trx_context.exec(),并由此函数内的dispatch_action()函数将action分发下去进入acontext.exec()函数,该函数执行 trace = exec_one(),代码如下:
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;
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 );
}
。。。。。。
}
。。。。。。。
}
最终在该函数内查找对应的处理函数find_apply_handler(),
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;
}
apply_handlers定义和上面提到的url_handlers类似,也是个map:
map< account_name, map<handler_key, apply_handler> > apply_handlers;
该map在初始化的时候也插入了一堆处理函数,并且按接受者、合约、动作分类,如下所示:
#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;
}
最终调用的处理函数就是eosio智能合约里面的名叫newaccount的action,也就是apply_eosio_newaccount函数:
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();
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
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) ) }