一台刚上线的全节点,地址库里几乎是空的,它要在几小时内攒出成千上万个候选邻居,靠的不是逐个碰运气,而是一次干脆的开口:把你知道的地址都给我。这个请求在协议里叫 getaddr,而回答它的规矩,比索取它多得多。本文以 Bitcoin Core 31.0 源码为准,把 getaddr 的三道纪律逐一拆开。
第一道:只回答进门的客人
代码里 GETADDR 的处理开头就是一段防指纹说明。历史上发生过这样的攻击:有人往你的地址库里塞满精心标记过的假地址,然后发一条 getaddr 把你存的东西原样要回去——哪些地址被你接受过,一目了然,我的节点是谁、连了谁,全暴露在这份回礼里。这就是地址指纹攻击。
Bitcoin Core 的解法写得非常直接:不对称。只有入站连接(别人主动来连我)发来的 getaddr 才值得回答;我自己拨出去的出站连接,即使开口要,也一律忽略,日志里记一句「Ignoring getaddr from outbound connection」了事。理由同样写在注释里:处在 NAT 后面、只能发起出站连接的节点,靠「不回话」这一条就足以让盖章攻击落空。

第二道:一条连接只答一次
回答入站客人时还有第二道闸:每条连接一个连接周期内只答一次 getaddr。源码用 m_getaddr_recvd 标志位记账,第二次再收到就直接忽略,注释点名了防的对象——addr stamping,即反复用「再给我一份」来给 INV 宣告阶段可预测的批量地址打上可追踪的时间戳。一次给足,之后不再配合你的节奏。
第三道:一次最多一千条
回答的份量也有硬上限。MAX_ADDR_TO_SEND 定义为 1000:单次 getaddr 回复,节点最多从地址库里挑一千条发出去。挑选还带着第二个系数——MAX_PCT_ADDR_TO_SEND 为 23,意思是回复不超过地址库总量的百分之二十三,两头都防止小本经营被一次掏空、也防止大户用一条请求榨干你的带宽。拥有 addr 特权的对端走另一条不受限路径。
还有一处容易忽略的镜像关系:入站 addr 消息的令牌桶容量恰好也定义为一千条(MAX_ADDR_PROCESSING_TOKEN_BUCKET 取自 MAX_ADDR_TO_SEND),平时每秒只放行十分之一条,唯独处理 getaddr 回礼时豁免性注满。也就是说,源码在设计上就让「一次性讨回一整份地址簿」走得动,同时又让日常零散的 addr 洪水被慢慢消化。令牌桶的细节见 每秒只放行十分之一条。
谁在什么时候开口要
请求侧同样有固定剧本:与一个支持地址中继的出站对端完成握手后,节点会向「第一个」这样的对端发一条 getaddr(每个连接一次),专门跳过 block-relay-only 连接——那类纯区块通道刻意不参与地址交换,避免把网络拓扑画出来。讨回来的地址进入地址库后,会按各自的规则被改写年龄、分桶保管,相关机制可读 全新节点第一批邻居从哪来 与 peers.dat 与比特币节点的熟人账本。
实操含义
对普通全节点用户,getaddr 不需要任何配置:它发生在每次新出站连接的开头,是地址库冷启动与日常换血的主管道。值得知道的两个可观测点:一是 getpeerinfo 里没有 getaddr 的直接字段,但每条新出站连接建立后的头几秒,往往是 addr 消息最密集的时刻;二是若你的节点长期只出不进(地址库数周不涨),排查方向应是对端是否回 addr、而非 getaddr 是否发出——请求本身是握手后无条件动作。对跑私有网络或防火墙后节点的运维者,还应理解「回礼只给入站客人」的另一面:你的节点从别人那里讨清单不受影响,但你要为别人作答时,答的永远是别人进门之后。而对做链上隐私研究的人,「出站不回 getaddr」这条纪律本身就是比特币 P2P 层最便宜的拓扑防泄漏设计之一:它让「谁认识我」这个问题,对出站-only 的节点天然保密。
三句话可以带走:想从别人手里要整份地址簿,可以,但只有你进门作客时才要得到;要到了就一次,没有追加;每次最多一千条,多了自己慢慢攒。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。