EOS插件间通信一窥

本文以在私有节点创建账号为例,简单介绍eos插件间通信机制。

前提:

        1、安装并启动本地节点(参考上一篇博文)

        2、熟悉命令行操作(参考官方文档)

        3、最好会用Clion调试代码(参考https://blog.csdn.net/zhuxiangzhidi/article/details/82597672

一、执行命令行创建账号

         1、创建钱包,导入秘钥(参见上篇博文)

         2、创建账号

cleos create account eosio test EOS5VZk1B9HqPNypN9XczvrtT6AkaDgZafyAcD6xovh63uC133stj

二、代码流程

          先上堆栈截图:

       

 

       1、cleos解析命令行,组织并发送http协议(参见https://blog.csdn.net/weichanghu_/article/details/81414529)。

此处简单介绍一下代码流程:

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

三、结语

       鉴于笔者书写能力和代码能力有限,文章有多处代码解释不甚详细,若大家有更好的见解还望多多交流

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值