EOS解读--插件通信

2019-01-18  本文已影响0人  脆果咒

整体插件

image.png

文件目录说明如下:

1、contracts目录

这个目录包含了EOS基础合约和示例合约,以及这些合约使用的库:eosiolib库、libc++库、musl库;

其中eosiolib库是EOS官方编写的库,libc++库、musl库是外部库。

2、plugins目录

EOS采用插件化设计,这个目录包含了EOS项目所用到的插件。

3、libraries目录

这个目录包含了具体实现代码需要引用的基础类库:

4、programs目录

这个目录包含EOS最终构建的应用程序(eos/build/programs目录下)的源码:

5、governance目录

这个目录包含的不是源代码,而是EOS社区治理文件,觉得比较重要,提到这里:

6、externals目录

这个目录包含的是外部项目:

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类,它们的继承关系如下:

image

plugin的生命周期

每个插件都会经历如下生命周期:

注册(register_plugin)

插件如何通信

一、执行命令行创建账号

     1、创建钱包,导入秘钥

     2、创建账号

cleos create account eosio test EOS5VZk1B9HqPNypN9XczvrtT6AkaDgZafyAcD6xovh63uC133stj

二、代码流程

堆栈图:

image.png

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) ) }

上一篇下一篇

猜你喜欢

热点阅读