资讯专栏INFORMATION COLUMN

EOS 源码解析 区块回滚对交易的影响

diabloneo / 1780人阅读

摘要:在主网上玩耍的小伙伴们肯定遇到过区块回滚导致自己的交易没有上链。这种情况让有些人误以为区块回滚会丢弃交易。其实区块回滚并不是导致交易没上链的主要原因,主要原因是交易过期了才导致交易被丢弃。源码解析我们来看看区块生产时是如何丢弃过期交易的。

    在主网上玩耍的小伙伴们肯定遇到过区块回滚导致自己的交易没有上链。这种情况让有些人误以为区块回滚会丢弃交易。 其实区块回滚并不是导致交易没上链的主要原因, 主要原因是交易过期了才导致交易被丢弃。

流程描述:
 每个交易是会被广播到全网每个节点上面的( ps: 当然传播过程中过期的话,当我没说哈 ),假如出块节点 A 打包了 trx a, 但此时出块节点 B 没接受到 A 的打包块,他也开始打包了,那么他也包含了该 trx a 并会将他打包( ps: 当然也有例外情况,那就是 出块节点 B 接收到 trx a 时,他就过期了,所以还没打包就丢弃他,或者还没传递到出块节点 B, 但会抵达下个节点 C D E, 但情况相似,就不另外说明了 )。但如果 a 的过期时间设置过短,导致出块节点 B 打包时发现他过期了,就会丢弃他。 这便是交易没上链的原因。


源码解析:
 我们来看看区块生产时是如何丢弃过期交易的。区块生产的流程 区块的分叉处理可以看下我之前的文章。这里生产区块以及回滚的细节就不赘述了。

区块打包时,会将 pending block 里已经执行成功了的 trx 另外存起来, 并初始化 pending block。 等到打包的时候会再去执行一次这些 trx , 咦,为什么要重新执行一遍浪费资源,因为这些 trx 都是在区块打包之前执行的,鬼知道过期了没有 =,=。

以下是交易被残忍丢弃的过程:

// controller.cpp
// 将 pending block 的 trx 保存到 unapplied_transaction
void abort_block() {
if( pending ) {

  if ( read_mode == db_read_mode::SPECULATIVE ) {
     for( const auto& t : pending->_pending_block_state->trxs )
        unapplied_transactions[t->signed_id] = t;
  }
  pending.reset();

}
}

// producer_plugin.cpp
producer_plugin_impl::start_block_result producer_plugin_impl::start_block(bool &last_block) {
// ...

try {

  // ...

  // 将 pending block 里面的 trx 保存了 unapplied_transaction
  chain.abort_block();
  // 初始化 pending block
  chain.start_block(block_time, blocks_to_confirm);

} FC_LOG_AND_DROP();

const auto& pbs = chain.pending_block_state();
if (pbs) {

  // ...


  // _persistent_transactions 是指通过该节点的 http 端口 push_transaction 推送过来的 trx
  // remove all persisted transactions that have now expired
  auto& persisted_by_id = _persistent_transactions.get();
  auto& persisted_by_expiry = _persistent_transactions.get();
  if (!persisted_by_expiry.empty()) {
     int num_expired_persistent = 0;
     int orig_count = _persistent_transactions.size();

     // 丢弃过期交易
     while(!persisted_by_expiry.empty() && persisted_by_expiry.begin()->expiry <= pbs->header.timestamp.to_time_point()) {
        auto const& txid = persisted_by_expiry.begin()->trx_id;
        if (_pending_block_mode == pending_block_mode::producing) {
           fc_dlog(_trx_trace_log, "[TRX_TRACE] Block ${block_num} for producer ${prod} is EXPIRING PERSISTED tx: ${txid}",
                   ("block_num", chain.head_block_num() + 1)
                   ("prod", chain.pending_block_state()->header.producer)
                   ("txid", txid));
        } else {
           fc_dlog(_trx_trace_log, "[TRX_TRACE] Speculative execution is EXPIRING PERSISTED tx: ${txid}",
                   ("txid", txid));
        }

        persisted_by_expiry.erase(persisted_by_expiry.begin());
        num_expired_persistent++;
     }

     fc_dlog(_log, "Processed ${n} persisted transactions, Expired ${expired}",
            ("n", orig_count)
            ("expired", num_expired_persistent));
  }

  try {
     size_t orig_pending_txn_size = _pending_incoming_transactions.size();

     // Processing unapplied transactions...
     // 当节点不是 出块节点并且 也没有通过该节点推送的 trx, 则 unapplied_transaction 无意义
     // 因为你不需要打包区块, 也没有 trx 需要广播出去。
     if (_producers.empty() && persisted_by_id.empty()) {
        // if this node can never produce and has no persisted transactions,
        // there is no need for unapplied transactions they can be dropped
        chain.drop_all_unapplied_transactions();
     } else {
        std::vector apply_trxs;
        { // derive appliable transactions from unapplied_transactions and drop droppable transactions
           auto unapplied_trxs = chain.get_unapplied_transactions();
           apply_trxs.reserve(unapplied_trxs.size());

           auto calculate_transaction_category = [&](const transaction_metadata_ptr& trx) {
              if (trx->packed_trx.expiration() < pbs->header.timestamp.to_time_point()) {
                 return tx_category::EXPIRED;
              } else if (persisted_by_id.find(trx->id) != persisted_by_id.end()) {
                 return tx_category::PERSISTED;
              } else {
                 return tx_category::UNEXPIRED_UNPERSISTED;
              }
           };


           // 将没过期的放进 apply_trxs, 过期的丢弃掉。
           for (auto& trx: unapplied_trxs) {
              auto category = calculate_transaction_category(trx);
              if (category == tx_category::EXPIRED || (category == tx_category::UNEXPIRED_UNPERSISTED && _producers.empty())) {
                 if (!_producers.empty()) {
                    fc_dlog(_trx_trace_log, "[TRX_TRACE] Node with producers configured is dropping an EXPIRED transaction that was PREVIOUSLY ACCEPTED : ${txid}",
                           ("txid", trx->id));
                 }
                 chain.drop_unapplied_transaction(trx);
              } else if (category == tx_category::PERSISTED || (category == tx_category::UNEXPIRED_UNPERSISTED && _pending_block_mode == pending_block_mode::producing)) {
                 apply_trxs.emplace_back(std::move(trx));
              }
           }
        }

        if (!apply_trxs.empty()) {
           // 执行 trx, 成功的 emplace_back 进 pending block
        }
     }

     // ...
     // 执行 deffered transaction
     // 和执行 初始化 pending block 时推送进来的 transcation ( 因为初始化时,pending block 不能存 trx, 所以先另外存起来)

}

return start_block_result::failed;
}

总结:

回滚并不会丢弃 trx, 只会导致 trx 延后打包,以致于 trx 可能过期被丢弃。
设置过期时间时,时间跨度应该足够 2 个 BP 出完块,这样即使 B 没接收到 A 的区块,但 trx 不会因为过期而被 B 丢弃,当然还要大致估算你的 trx 广播到出块节点的时间。

有任何疑问或者想交流的朋友可以加 EOS LIVE 小助手,备注 eos开发者拉您进 EOS LIVE DAPP 开发者社区微信群哦。
图片描述

原文链接:https://eos.live/detail/18931

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

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

相关文章

  • EOS 源码解析 股权证明交易(TaPos) 作用

    摘要:在每个交易中,我们都会看到和这个参数有什么作用呢。先讲下它的大致作用,再来对代码进行分析。这是白皮书对这个参数的作用描述他是用来防止有不包含区块引用的交易被重放到某个分叉上,这样能避免不是该分叉的区块被添加到该分叉。 在每个 trx 交易中,我们都会看到 ref_block_num 和 ref_block_prefix, 这2个参数有什么作用呢。 先讲下它的大致作用,再来对代码进行分析...

    neuSnail 评论0 收藏0
  • EOS源码解析 使用多线程从签名生成对应公钥

    摘要:区块多线程签名改动同步区块时进行多线程签名,过程中依然是单线程签名。代码解析块签名因为不适用多线程签名,所以依旧沿用之前的签名代码,而同步则使用了新的部分。当大家比较关注的使用并没有得到改善,因为多线程签名无法应该在生产区块上。 昨天早上,EOS 1.5.0 release 版本发布了。这次比较大改动点是在多线程签名上面。它将同步区块时的 block 签名验证和 trx 签名验证都使用...

    mist14 评论0 收藏0
  • FIBOS 周报

    摘要:稳定币的上线年月日,发布了稳定币,并且成功通过了社区多签。年月日,的稳定币正式上线。年月日,六大个税抵扣社会保险费由税务部门统一征收等一批新规正式实施。本次的攻击为针对项目方的重放攻击。 FIBOS 稳定币的上线 2018年12月21日,FIBOS 发布了稳定币—— FOD,并且成功通过了社区多签。 2018年12月28日, FIBOS 的稳定币 FOD 正式上线。 早在2018年9月...

    explorer_ddf 评论0 收藏0
  • FIBOS 周报

    摘要:稳定币的上线年月日,发布了稳定币,并且成功通过了社区多签。年月日,的稳定币正式上线。年月日,六大个税抵扣社会保险费由税务部门统一征收等一批新规正式实施。本次的攻击为针对项目方的重放攻击。 FIBOS 稳定币的上线 2018年12月21日,FIBOS 发布了稳定币—— FOD,并且成功通过了社区多签。 2018年12月28日, FIBOS 的稳定币 FOD 正式上线。 早在2018年9月...

    dabai 评论0 收藏0
  • BBFT共识算法深度解析丨Bystack是如何实现单条侧链 20000+TPS

    摘要:第一类模式是在公链项目中运用的最广泛应用的共识算法,比特币长达年的运行已充分证明的安全性与稳定性。此时当前区块已是合法区块但是未获得最终确认,类似于比特币未获得个块确认存在回滚的可能性。 showImg(https://segmentfault.com/img/bVbtamO?w=1000&h=600); 共识算法是分布式系统保证节点数据状态一致性的方法,在区块链的共识算法分POW(工...

    novo 评论0 收藏0

发表评论

0条评论

diabloneo

|高级讲师

TA的文章

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