节点切换到一个新的链顶时,会主动把这个消息告诉所有对端。但如果是深度重组,代码给自己设了一条硬规矩:最多只报八个区块。这条限制写在常量 MAX_BLOCKS_TO_ANNOUNCE 里,值就是 8。
通告发生在什么时候
链顶变化由一个专门的回调处理。这个回调第一件事就是判断节点是否还在初始同步阶段——如果是,直接返回,不向任何对端中继新区块。原因不难理解:一个还在补历史的节点,自己看到的”新块”对全网来说可能是几天前的旧块,广播出去只会让对端做无用功。
过了这道闸,代码从新链顶开始沿着父指针往回走,一路收集”这次变化里新出现在最佳链上的区块哈希”,每走一步就判一次:收够了八个就停。注释把这件事讲得很干脆——巨大重组时限制造告数量,这种情况下依靠对端自己的同步机制。
也就是说,被截断的那部分区块,节点不是不认,而是不主动说。对端如果自己不知道自己错过了什么,它该发 getheaders 就发 getheaders,该要区块头清单就要清单,同步不依赖这一声吆喝。
收到通告的对端有不同待遇
收集到哈希列表后,节点给每个对端的待中继队列里逐个塞进去,插入顺序是反向的——从分叉之后最早的块开始,最新块排在最后。这样对端按队列顺序读到时,看到的是一条从旧到新的时间线,符合链的自然顺序。
对端拿到通告的形式分两种。如果它之前表达过希望直接接收区块头公告(也就是我们熟悉的 sendheaders 那套协商),节点就发 headers 消息,把完整的区块头直接推过去;否则退回最古老的存货公告,用 inv 告诉对端”我有这几个块,你要不要”。两种形式的语义相同,差别在于对端要不要多花一次往返去取正文。
为什么 8 不是 2016 或 6
这个数字本质上是”一次通告能装多少信息”与”对端处理成本”的折中,而且它只在异常场景才有意义。比特币里正常的重组深度通常是个位数,超过八个块的重组在历史上很少见,属于链分裂、长时间孤立挖矿或运维误操作那一类局面。
在常见的浅重组里,八个哈希的通告是够用的:对端能立刻知道自己从哪个高度开始错了,进而有针对性地发请求。真到了需要截断的深重组,问题已经不在”通知不够及时”,而在”两边对链的历史理解差别太大”,这时候让对端走自己的头同步流程反而更稳。
值得顺带对个账的是,代码里还有一组数字管着别的维度:区块下载窗口是一次最多往前提一千多个高度;对修剪模式下愿意服务的历史块数下限是 288 个;紧凑区块的 getblocktxn 应答窗口是 10 个块深。它们和这个 8 完全不是一回事,看日志时不要混。
运维上会感知到什么
第一,深重组时你的节点可能”没喊出全部新块”。监控面板上看到的对端同步滞后,未必是你的节点坏了,它只是按规则闭了嘴。对端会靠 getheaders 补齐,只是慢一个来回。
第二,深重组期间区块中继会明显变慢。八个块只是通告上限,实际影响更大的是初始同步判定:如果重组把你打回了同步判定的门槛以内,那段时间所有新区块都不会被中继,节点只低头补自己的链。想验证这一点,看 getblockchaininfo 里 initialblockdownload 的取值最直接。
第三,浅重组下的通告是完整的。如果你只是丢了最近一两三个块,节点会把这些块正常公告出去,不受这个上限影响。
第四,不要把通告数量当作”节点是否健康”的指标。它是纯带宽与礼貌性的限流,不改变节点对链的判断,也不会因为截断而漏掉任何一笔交易的最终状态。
风险提示:本文依据比特币核心 v31.0 源码撰写,常量取值与中继策略可能随版本调整;本文描述节点行为,不构成任何投资建议。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。