EOS解读--插件通信

整体插件

image.png

文件目录说明如下:

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目录

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

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)

  • 配置(set_program_options)

  • 初始化(plugin_initialize)

  • 启动(plugin_startup)

  • 关闭(plugin_shutdown)

插件如何通信

一、执行命令行创建账号

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354

推荐阅读更多精彩内容