节点可以说“不”:BIP-111 给布隆过滤挂上的服务位 图 1
节点可以说“不”:BIP-111 给布隆过滤挂上的服务位 · 图 1

一个默认人人都得提供的服务

BIP-37 在 2012 年给比特币 P2P 协议加了连接级布隆过滤:轻钱包把自己关心的脚本片段编成过滤器发给节点,节点替它筛区块和交易,只推送命中的部分。这套机制有个隐含前提——凡是给别人供数据的节点都支持过滤,当年连服务位都没为它单独定义一个。

到 2015 年,这个前提被两盆冷水浇透。隐私方面,学术界论证布隆过滤器在多方协作观察下基本保不住命中之外的信息,过滤器本身的比特模式还会泄露钱包结构。工程方面,精心构造的过滤器能让节点在每笔交易上做大量无谓比对,构成拒绝服务攻击面。既然继续无条件提供这项服务弊大于利,就必须允许节点说不——但在那之前,节点根本没有正当途表达这个不字。

一个比特加一次版本升位

BIP-111 的正文因此极短:在服务位字段定义第三比特为 NODE_BLOOM,支持布隆过滤的节点置位,不支持的留空。同时参考实现把协议版本从七万零二升到七万零一十一。这里有个兼容细节:版本号介于两者之间的老节点其实也支持过滤,只是没带比特,客户端不能拿版本号当充分依据,行为准则应看比特本身。

规范还补了一条硬边界:不支持过滤的节点若收到 filterload、filteradd 或 filterclear 消息,立即断开该对端。这句把说不的执行成本降到最低——与其维护一堆半残会话,不如直说这里没有你要的服务。另外比特设计允许 NODE_BLOOM 与 NODE_NETWORK 独立声明,法律上可以只供过滤不供全历史,为后来的混合部署留了余地。

落地:从可选到默认关

提案状态为已部署,落地节奏体现在客户端里。比特币核心 v0.13 引入节点端参数开关,默认仍开启,管理员可整体关闭;关掉了,内存池监听命令也一并失效——两件事同源于同一套过滤器逻辑。此后多年,社区对布隆过滤的态度持续走低,GCS 紧凑过滤器配合 BIP-157 给出了隐私和带宽都更好的替代路线,新代码基本不再给 BIP-37 添砖。服务位机制的遗产则活了下来:今天节点用不同比特各自声明全历史、过滤、新式过滤器等能力,连接前互相点名,对不上就不浪费带宽,这套协商思路正是从 BIP-111 这类小提案长出来的。

一个直觉账本

给节点算一笔账:开布隆过滤后,每笔进来的交易都要和你的过滤器比一次,过滤器越大比对越贵;而关掉它,轻钱包就得下载全量区块自己筛,带宽全部转嫁给全网的下载竞争。同一个资源池,两边互相伸手。服务位之所以是必要的妥协,是因为它让每个节点按自己的带宽、机器和威胁模型选一个位置,而不是让协议替所有人拍板。一个常被忽略的事实是:默认开着过滤的节点与默认关着的节点在同一张网络里运行了很多年,连接协商机制保证了互不干扰——协议允许分歧共存,正是靠这些比特位维持的。

快速问答

问:NODE_BLOOM 不置位就等于节点是归档或者受限模式吗? 答:不是,服务位各比特彼此独立。布隆过滤声明与供块历史的能力声明是两条正交信息。

问:现代钱包还依赖这个比特吗? 答:主流新实现转向 BIP-157 紧凑过滤器或服务器端索引方案,对 BIP-37 的依赖逐年减少,但老钱包与老服务器组合仍在网络里共存。

一条判断线

评估一个 P2P 扩展值不值得长期维护,可以量两本账:它省下的资源归谁,它引入的攻击面由谁付。布隆过滤把下载成本转嫁给了轻客户端的便利,又把比对成本和隐私风险摊给所有节点。BIP-111 没有评判这项交换的胜负,只是终于让节点有权按自己的账本表态——服务位的意义从来不是功能,而是选择权。

风险提示:本文讨论协议历史与节点运维机制,不构成投资建议;自行运行节点或调整过滤参数前,请阅读对应客户端文档。