信标链的验证为什么不全网广播?64 条子网与订阅轮换机制 图 1
信标链的验证为什么不全网广播?64 条子网与订阅轮换机制 · 图 1

以太坊信标链每秒都在产生成千上万条验证消息。如果它们全都广播给全网所有节点,网络会在几秒内被自己淹死。协议给出的答案是把验证流量切进 64 条话题通道,让每条通道只服务一小撮委员会,并且每隔一段时间换一批通道来听。这套机制写在共识规范的 P2P 接口文档里,叫验证子网(attestation subnet)。它决定了你的节点连多少个 peers、为什么日志里写着”subscribed to 2 persistent subnets”、以及为什么验证偶尔会在网络里”迟到”。

为什么不能一把广播

一条验证消息很小,但数量极大:每个槽位有若干委员会,每个委员会上百人,每人都要发一条未聚合的验证。如果全部走全局话题,每个节点要为每条验证做一次签名验证和转发,带宽成本与节点数量成正比。协议的拆法很朴素:既然一条验证只需要被”关心这个委员会的人”看到,那就按委员会把流量分到不同话题上,各自组网。规范把话题总数固定为 64,并规定未聚合的验证只发往它所属委员会对应的那一条子网;聚合后的结果则走全局话题,由聚合者代为扩散。

委员会怎么映射到子网

机制示意(图片由 Agnes 生成,非产品界面或链上数据图)

槽位到子网不是固定表,而是随每槽委员会数量轮转。派生逻辑先算出这条委员会在本纪元里的偏移量——由纪元在槽位上的位置、槽位编号与每槽委员会数量共同决定,再加上委员会在本槽内的编号——然后把这个偏移量对 64 取模,得到子网编号。这意味着同一个物理子网在一个纪元里会被不同委员会轮流使用,一条子网在纪元开头装着某组委员会、结尾可能装着完全不同的委员会。规范也解释了为什么用取模而不是别的映射:在没有更完整网络测试之前,把子网数与每槽最大委员会数取齐最省心。由于编号完全由槽位与委员会索引推出,节点不必下载状态就能算出某条验证该去哪条子网,这也是接收方能够直接拒收”走错子网”消息的前提。

节点该听几条、听多久

每个信标节点被要求长期订阅 2 条子网,这两条由节点自身的标识符映射得到,因此分布在节点之间是近似均匀的。订阅的持续时长定为 256 个纪元,差不多 27 小时——到点换一批,避免同一批节点长期负责同一批委员会而形成可预测的攻击面。除长期订阅之外,节点还需要临时订阅:想可靠收到全部验证,得按当前每槽委员会数量轮转地把每个子网都听一遍。这就是很多实现提供”全部子网模式”的原因,也是为什么普通节点在验证聚合上不如专职节点积极。节点还会把”我长期在听哪几条子网”以位图形式登记进自己的节点记录,供别人在需要某条子网数据时按图寻找对等节点;如果这张位图全为零,这个字段可以省略。

失败模式与排障

子网机制把”收不到验证”变成了几种不同现象。第一种是订阅缺失:你的节点没在听那条子网,验证从你身边经过而你不知道,表现为验证包含延迟偏大但节点一切正常。第二种是 gossip 校验拒绝:验证被发到错误的子网,接收方按规范直接拒收,日志里会出现与子网相关的拒绝原因——这时问题通常在发送方。第三种是时钟偏差:一条来自略微未来的槽位的验证会被暂时搁置或丢弃,因为消息的槽位范围校验容许的时钟差很小。第四种是 peers 太少:子网内需要足够的注意力记录才能组网,长期只连到寥寥几个 peers 会让你的验证出不了本地。

与聚合的分工

值得强调分工的两端:子网负责未聚合验证的局部扩散,全局话题负责聚合结果的广域扩散。聚合者从自己订阅的子网收集验证、压缩成一个聚合并附上自己的证明,再发到全局话题。这个结构意味着聚合质量取决于聚合者愿意订阅多少子网——如果聚合者只听长期订阅的两条,本槽其余子网的验证就只能靠提议者自己去收集。这也是为什么”提议者收不全验证”在某些时段会发生,而它与任何单个节点的健康状况无关。

边界

子网数量、每节点订阅数、订阅时长、组网的稳定邻居数量等参数都在规范常量表里,可能随升级调整,运维时应以所用客户端当前版本的文档与共识规范为准。相关参数改动的方向始终是降低带宽需求,同时不牺牲验证可达性。本文只解释机制与排障思路,不构成任何投资建议。