资讯专栏INFORMATION COLUMN

EOS源码解析 使用多线程从签名生成对应公钥

mist14 / 1712人阅读

摘要:区块多线程签名改动同步区块时进行多线程签名,过程中依然是单线程签名。代码解析块签名因为不适用多线程签名,所以依旧沿用之前的签名代码,而同步则使用了新的部分。当大家比较关注的使用并没有得到改善,因为多线程签名无法应该在生产区块上。

昨天早上,EOS 1.5.0 release 版本发布了。这次比较大改动点是在多线程签名上面。它将同步区块时的 block 签名验证和 trx 签名验证都使用多线程签名验证,来节省同步所需要的时间, 但是生产区块所需要的成本是不变的,但为什么生产区块成本不变呢。接下来介绍一下具体的改动。

 区块多线程签名改动:同步区块时进行多线程签名, replay 过程中依然是单线程签名。因为区块同步时需要回滚 pending block 的 trx 操作, 这块时间刚好可以用来并行处理签名, 但 replay 的时候没有这一步,即使用多线程签名也无法节省时间,反而会让主线程阻塞等待异步结果返回。

 trx 多线程签名改动:同步区块以及 replay 过程都会进行多线程签名, 因为有多个 trx 要执行,所以执行 trx 的时间可以供其他 trx 的签名并行进行。 但生产区块的时候无法使用,因为执行 BP 接受到一个 广播的 trx 就立马去执行了,执行完之后才回去接受下一个广播 trx, 所以无法使用多线程签名。


代码解析: 块签名:

因为 replay 不适用多线程签名, 所以 replay 依旧沿用之前的签名代码, 而同步则使用了新的部分。

// producer_plugin.cpp 接受到广播块
void on_incoming_block(const signed_block_ptr& block) {
   // ...

   // start processing of block
   // 调用一个线程去对块进行签名验证
   auto bsf = chain.create_block_state_future( block );

   // abort the pending block
   // 回滚掉 pending block 的执行 trx, 这段时间刚好可以用来并发执行区块签名验证
   chain.abort_block();

   // ...
}

// controller.cpp
std::future create_block_state_future( const signed_block_ptr& b ) {

   //验证区块是否存在。
   EOS_ASSERT( b, block_validate_exception, "null block" );

   auto id = b->id();

   // no reason for a block_state if fork_db already knows about block
   auto existing = fork_db.get_block( id );
   EOS_ASSERT( !existing, fork_database_exception, "we already know about this block: ${id}", ("id", id) );

   auto prev = fork_db.get_block( b->previous );
   EOS_ASSERT( prev, unlinkable_block_exception, "unlinkable block ${id}", ("id", id)("previous", b->previous) );

   // 进行多线程签名
   return async_thread_pool( [b, prev]() {
      const bool skip_validate_signee = false;
      return std::make_shared( *prev, move( b ), skip_validate_signee );
   } );
}

void push_block( std::future& block_state_future ) {
   controller::block_status s = controller::block_status::complete;
   EOS_ASSERT(!pending, block_validate_exception, "it is not valid to push a block when there is a pending block");

   auto reset_prod_light_validation = fc::make_scoped_exit([old_value=trusted_producer_light_validation, this]() {
      trusted_producer_light_validation = old_value;
   });
   try {
      // 获取验证结果, 当区块验证失败时会抛出异常,中止 push block
      block_state_ptr new_header_state = block_state_future.get();
      auto& b = new_header_state->block;
      emit( self.pre_accepted_block, b );

      fork_db.add( new_header_state, false );

      if (conf.trusted_producers.count(b->producer)) {
         trusted_producer_light_validation = true;
      };
      emit( self.accepted_block_header, new_header_state );

      if ( read_mode != db_read_mode::IRREVERSIBLE ) {
         maybe_switch_forks( s );
      }

   } FC_LOG_AND_RETHROW( )
}
交易签名

从改动得知,apply_block 的时候才会启动交易的多线程验证签名,而 bcast_transaction 则不会,因为并没有多余的动作可以与验证签名并行。

void apply_block( const signed_block_ptr& b, controller::block_status s ) { try {
   try {
      EOS_ASSERT( b->block_extensions.size() == 0, block_validate_exception, "no supported extensions" );
      auto producer_block_id = b->id();
      start_block( b->timestamp, b->confirmed, s , producer_block_id);

      // 按顺序启动每个 trx 的多线程验证签名,生产对应公钥
      std::vector packed_transactions;
      packed_transactions.reserve( b->transactions.size() );
      for( const auto& receipt : b->transactions ) {
         if( receipt.trx.contains()) {
            auto& pt = receipt.trx.get();
            auto mtrx = std::make_shared( pt );
            if( !self.skip_auth_check() ) {
               std::weak_ptr mtrx_wp = mtrx;
               mtrx->signing_keys_future = async_thread_pool( [chain_id = this->chain_id, mtrx_wp]() {
                  auto mtrx = mtrx_wp.lock();
                  return mtrx ?
                         std::make_pair( chain_id, mtrx->trx.get_signature_keys( chain_id ) ) :
                         std::make_pair( chain_id, decltype( mtrx->trx.get_signature_keys( chain_id ) ){} );
               } );
            }
            packed_transactions.emplace_back( std::move( mtrx ) );
         }
      }

      // 执行 trx
      // ...

      commit_block(false);
      return;
   } catch ( const fc::exception& e ) {
      edump((e.to_detail_string()));
      abort_block();
      throw;
   }
} FC_CAPTURE_AND_RETHROW() } /// apply_block

// trx 执行时获取签名返回的公钥
const flat_set& recover_keys( const chain_id_type& chain_id ) {
   // Unlikely for more than one chain_id to be used in one nodeos instance
   if( !signing_keys || signing_keys->first != chain_id ) {
      if( signing_keys_future.valid() ) {
         // 获取公钥,如果未签名完则阻塞等待签名完毕
         signing_keys = signing_keys_future.get();
         if( signing_keys->first == chain_id ) {
            return signing_keys->second;
         }
      }
      // 当没开启多线程签名时, 直接验证生成对应公钥
      signing_keys = std::make_pair( chain_id, trx.get_signature_keys( chain_id ));
   }
   return signing_keys->second;
}
总结
    从这次的改动可以看出主要优化的地方是节点同步区块的速度, 因为开启了多线程签名,所以在 block 验证以及 apply_block 时节省了一定 CPU 时间, 可供其他地方使用。 例如 EOS 现在是当线程的,所以当你进行 RPC 访问的时候,如果涉及到数据提取,主线程的同步时会暂停的,等待你的操作结束, 这样就会影响节点的同步,所以 get_table_rows API 才会限制 10 ms。 现在同步所需时间减少,降低了节点既要同步数据也要提供 RPC API 的压力。

当大家比较关注的 CPU 使用并没有得到改善, 因为多线程签名无法应该在生产区块上。所以在生产区块时, trx 执行所需要的 CPU 时间并不会减少,也就是 CPU 资源的使用并没有得到改善。

EOS 开发的小伙伴有技术问题可以进群讨论哟

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/24469.html

相关文章

  • EOS入门指南——PART3 如何创建账户

    摘要:最后一步付款和比特币以及以太坊不一样的是,在创建账户是有成本的,这也就是为什么我们需要一个账户才能创建账户的原因找个人来买单。 之前我们学习了如何编译EOS程序,以及如何连接到EOS主网,接下来我们要谈一谈大家最关心的,如何创建自己的EOS账户。 摘要 这篇我们会学习如何创建钱包、秘钥对、主网账户,向大家介绍一些实用工具。最重要的是,我们会学习到在EOS里,公钥和账户到底有什么区别。 ...

    oliverhuang 评论0 收藏0
  • 【刘杰良】使用RPC接口新建EOS账户 - 实战

    摘要:适用于最新的前言最近在研究的,但是由于官方文档的不够详尽,新建账号这一个操作就折腾了一个多星期。皇天不负有心人,终于调通了新建账号,代币转账也轻松解决。 适用于最新的 EOS Dawn 4.0/4.1 前言 最近在研究 EOS 的 RPC API,但是由于官方API文档的不够详尽,新建账号(new account)这一个操作就折腾了一个多星期。皇天不负有心人,终于调通了新建账号,代币转...

    Little_XM 评论0 收藏0
  • EOS】名词解释

    摘要:在对一个交易进行签名时,与之间会发生交互。钱包通过将锁定的键值本地化存储的方式,实现以安全的方式活动签名。表示已解锁,创建一个钱包默认是解锁状态钱包必须是状态。自定义命名权限这些权限可用于进一步扩展帐户管理。 account 介绍 帐户是授权的集合,存储在区块链上,用于标识发送方/接收方。它具有灵活的授权结构,允许根据权限的配置方式由个人或一组个人拥有。向区块链发送或接收有效交易需要一...

    stackvoid 评论0 收藏0
  • 【许晓笛】EOS 上线前,先搞懂这两个基本概念

    摘要:的跟其他区块链项目是类似的,都是一个基本功能本地储存密钥,仅此而已。公网上线后,一定要将存有密钥的加密,并且将文件单独备份好。字面意思是账户,但我觉得有个概念更适合法人。代币就是由持有的。对于权限,则需要列表里至少两个账户的授权才能行使。 如果你曾经尝试在本地运行 EOS 测试节点,会发现编译、运行并不是特别复杂,但官方教程里两个概念很容易把人搞晕: Account(账户) 和 Wal...

    alogy 评论0 收藏0
  • 【刘文彬】【源码解读】EOS测试插件:txn_test_gen_plugin.cpp

    摘要:,调用函数,重置标志位为,计时器关闭,打印关闭提示日志。设定计时器的异步定时任务,任务体直接调用函数,对函数的返回值进行处理,如果有报错信息一般是服务中止则调用函数关闭插件。 原文链接:醒者呆的博客园,https://www.cnblogs.com/Evswa... 本文内容本属于《【精解】EOS TPS 多维实测》的内容,但由于在编写时篇幅过长,所以我决定将这一部分单独成文撰写,以便...

    dinfer 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<