
9 月 11 日比特币在高度 966,500 附近又发生了一次单块重组。SpiderPool 和 AntPool 几乎同时挖出有效块两个块都指向同一个父块。Galaxy Research 的节点先看到了 SpiderPool 的版本随后 AntPool 的块到达节点最终切到了 AntPool 那条链。SpiderPool 的块被孤立大约 3.1389 BTC 的区块奖励没了。这不是传统意义上的 bug。它是分布式共识在真实网络延迟下必然会出现的现象。但“必然”不等于“最优”。每一次单块重组都意味着算力浪费、矿池收入损失以及商户和交易所在一小段时间里无法确定自己看到的确认到底算不算数。真正的问题不是“会不会再发生”。它一定还会发生。真正的问题是代码在重组发生时到底做了什么以及有没有空间在不碰共识规则的前提下做得更好。事情是怎么一步步发生的整个过程拆开看其实很直白。SpiderPool 和 AntPool 在高度 966,500 同时找到有效块两个块都建立在高度 966,499 之上。两个块都通过验证都有合法的工作量证明。两个矿池各自开始广播自己的块。SpiderPool 发给它的对等节点AntPool 也发给它的对等节点。两个块在 P2P 网络里传播的速度不一样取决于对等节点拓扑、地理分布和网络状况。Galaxy Research 的节点先收到 SpiderPool 的块。它验证、连接、更新链尖。此时这个节点认为高度 966,500 是 SpiderPool 的块连在这个节点上的商户和交易所会看到一个新的确认。过了一会儿AntPool 的块也到了同一个节点。节点现在有两个高度相同、都有效的块都是链尖候选。节点按比特币的分叉选择规则处理累计工作量更大的那条链赢。AntPool 的块包含 4,359 笔交易网络也开始从它继续扩展所以这条分支累计了更多工作。节点执行重组断开 SpiderPool 的块把它放进孤块池连接 AntPool 的块。SpiderPool 块里那些不在 AntPool 块里的交易回到内存池。SpiderPool 的 3.1389 BTC 区块奖励丢失。从 SpiderPool 的角度看他们没做错任何事只是输掉了传播竞赛。一旦两个块被挖出来后面的过程基本是确定的。唯一的变量是传播时序。代码路径重组在哪里触发在哪里解决比特币核心的区块处理逻辑主要在validation.cpp。关键函数是ConnectBlock()它把一个块的效果应用到 UTXO 集并验证块内所有交易。当两个块在同一高度竞争时节点会调用ActivateBestChain()判断新块所在的链是否比当前链尖有更多工作。核心逻辑可以简化成这样// validation.cpp 简化boolCChainState::ActivateBestChain(BlockValidationStatestate,...){// ...while(pindexMostWork-nChainWorkpindexBest-nChainWork){// 新链有更多工作执行重组if(!Reorganize(pindexMostWork,state)){returnfalse;}}}真正切换链的地方在Reorganize()。它会断开旧链上的块再连接新链上的块。对于单块重组就是断开 SpiderPool 的块连接 AntPool 的块。// validation.cpp 简化boolCChainState::Reorganize(constCBlockIndex*pindexNew,...){// 找分叉点CBlockIndex*pforkpindexNew;CBlockIndex*plongerchainActive.Tip();while(pfork!plonger){// 一直往回走直到找到共同祖先}// 断开旧链上的块for(CBlockIndex*pindex:vDisconnect){if(!DisconnectBlock(pindex,state,...)){returnfalse;}}// 连接新链上的块for(CBlockIndex*pindex:vConnect){if(!ConnectBlock(block,state,pindex,...)){returnfalse;}}}这段代码本身没问题。它忠实实现了共识规则。问题不在这里。等Reorganize()被调用时竞赛其实已经输了。真正的问题传播不对称和孤块处理更深层的问题是比特币的 P2P 层不会根据矿池身份、权益或者任何“重要性”来给区块排序。一个块就是一个块。它通过 TCP 和对等连接在网络里传播能多快就多快。两个块竞争时先到达更多算力那边的块赢。这不是代码 bug而是去中心化系统没有中心协调时自然涌现出来的性质。但有两个具体方向可以在不碰共识的前提下降低孤块率。改动一在net_processing.cpp里更聪明地处理孤块现在比特币核心对孤块的处理基本是丢弃。块里的交易回到内存池但块头和元数据丢了。如果被孤立的块所在链后来又被矿池扩展节点必须重新下载、重新验证一遍。更好的做法是把孤块保留在一个单独索引里给它一个短窗口比如 100 个块或者 1 小时。把里面的交易标记为“可能因重组回来”。如果竞争链后来从孤块继续扩展节点可以立刻重连不需要完整重验证。一个最小实现大概是这样// net_processing.cpp 增加structOrphanBlockIndex{CBlock block;CBlockIndex*pindex;int64_tnTimeReceived;};std::mapuint256,OrphanBlockIndexm_orphan_blocksGUARDED_BY(cs_main);voidPeerManagerImpl::BlockConnected(conststd::shared_ptrconstCBlockpblock,constCBlockIndex*pindexConnected,conststd::vectorCTransactionRefvtxConflicted){// 当有块被连接时检查是否有孤块从它扩展。// 如果有就保留一段时间方便以后重连。for(autoitm_orphan_blocks.begin();it!m_orphan_blocks.end();){if(it-second.block.hashPrevBlockpindexConnected-GetBlockHash()){// 这个孤块从刚连接的块扩展而来。// 留在索引里等待可能的重连。it-second.nTimeReceivedGetTime();it;}elseif(GetTime()-it-second.nTimeReceived3600){// 超过一小时的孤块过期清理itm_orphan_blocks.erase(it);}else{it;}}}效果单块重组发生后如果孤块那条链后来获得更多工作节点可以直接从本地索引重连孤块不用再向对等节点重新请求。这样能降低重组延迟也能减少链切换的带宽消耗。它不改共识也不会给任何矿池特殊优势。改动二通过矿池级区块中继协调减少传播不对称现在的CConnman和对等节点选择逻辑对所有出站节点一视同仁。一个节点连了十个随机对等节点可能因为地理距离近先收到 SpiderPool 的块。它没有机制说“我想更快收到 AntPool 中继网络的块”也没有机制说“我想优先处理从当前链尖扩展的块”。一个有针对性的改进是允许节点维护一小批“优先中继”对等节点根据历史区块传播表现来选。当新块头通过cmpctblock或headers公告时节点可以向历史上延迟更低、有效块率更高的对等节点请求完整块。// net_processing.cpp 对等节点评分增加structPeerBlockRelayStats{doubledAvgLatency;// 滚动平均区块投递延迟intnValidBlocks;// 投递并通过验证的块数intnInvalidBlocks;// 投递但验证失败的块数};// 在 ProcessMessage 里处理新区块公告时if(msg_typeNetMsgType::CMPCTBLOCK){autostatsm_peer_relay_stats[pfrom-GetId()];stats.dAvgLatency(stats.dAvgLatency*0.9)(latency*0.1);// 优先向延迟更低、有效率更高的对等节点请求完整块}效果节点能更快收敛到主链。两个块竞争时节点更可能先收到最终赢的那个块减少跟着错误链尖走的窗口。这能直接降低接收节点发生重组的概率。为什么这不只是丢一个块单块重组不算罕见通常几周一次。但频率在增加四周三次就值得注意了。原因不是网络故障而是矿池更多、地理分布更散、算力增长下出块更快。每一次单块重组都有三个成本。输的矿池损失收入。SpiderPool 在 9 月 11 日丢了 3.1389 BTC。对中型矿池来说这不是小数。商户和交易所的确认变得不确定。重组窗口里孤块里看起来已经确认的交易不再确认。接受支付的商户可能面临回滚。少见但真实。全网带宽和 CPU 浪费。每个跟错链的节点都要断开块、重连新块、重新验证交易。几万个节点加起来成本不小。上面这些改动不会消灭重组只会降低影响。这才是现实目标。比特币共识层故意抗变P2P 层不是。下一轮改进应该聚焦在这里。普通用户该明白什么单块重组不是黑客攻击也不是安全漏洞。它是网络在解决“哪个有效块先来”的临时分歧设计上就是这样。交易有 6 个确认就安全。多数场景 3 个确认够用。高价值交易等 6 个确认。这不是新建议9 月 11 日重组只是提醒大家为什么会有这条建议。真正的问题不是重组发生而是代码没有从重组里学习。每个孤块都被当成孤立事件。通过孤块处理和 peer 中继评分的小改动可以降低孤块率同时不碰保护比特币安全的共识规则。比特币共识层像坦克慢、难改、不在乎你的感受。P2P 层像跑车快、可调下一波改进会从这里来。9 月 11 日这次重组是一个信号跑车该保养了。