隔着网络问一句“这笔钱还在吗”:BIP-64 的 getutxo 查询提案 图 1
隔着网络问一句“这笔钱还在吗”:BIP-64 的 getutxo 查询提案 · 图 1

一个轻客户端的经典难题

比特币全节点自己维护一整套未花费输出集合,也就是 UTXO 集合,双击与否、余额多少都是本地算出来的。轻节点不行:它只同步区块头,账本细节要靠邻居节点施舍。于是出现一个很具体的问题——我刚刚在广播里看到一笔新交易质押到了某个合约式的应用里,我怎么知道支撑它的那笔输出还在不在?翻遍整条链太贵,干等确认又太慢。

2014 年 6 月,迈克·赫恩在比特币改进提案仓库挂出了 BIP-64,标题就叫 getutxo message。它给 P2P 协议加了一问一答两条消息,让轻客户端可以拿着输出点直接问邻居:这几个 outpoint,现在哪些还活着。注意 outpoint 这个标识本身就是引用式的:交易哈希加上输出序号,这正是全节点做双击检查时用的那把钥匙。

一问一答长什么样

提问方发 getutxos 消息,字段只有两个:一个布尔值表示要不要顺带检查内存池,以及一串按交易消息同款方式序列化的输出点列表。回答方回 utxos 消息,内容比问的多一层语境:计算结果时的链高度、当时的链顶哈希、一张命中位图,以及每个命中输出的完整明细——包含它的交易版本、所在区块高度(如果还在内存池里则标记为特殊值),外加输出本身。

这张位图设计得很省:查询十个输出,未命中的不占明细空间,只烧一个比特。而链顶哈希是给结果盖章——客户端如果已经通过区块头同步掌握了高度到哈希的对应表,就能拿它核对回答语境,再要么索回那个区块直接检索,证明输出曾经真实存在过。提案也坦承当时的上限:UTXO 集合本身没有共识层承诺,默克尔包含证明要等协议将来给集合加上根哈希之后才谈得上。

它能干嘛,以及它救不了什么

提案原文举了两类场景。一类是保证合约式的押金监控:应用看到一笔新押金出现时,快速查一下支撑交易有没有已被双击撤销,不必先下载整条链。另一类是轻钱包的浮动费用计算——但那需要协议其他部分配套改动,BIP-64 自己也承认这点。

关键的限制写在提案尾部:这个查询的信任模型和布隆过滤收款一样——对端节点大可以用『查不到』来撒谎,声称你要的输出已经花掉了。协议没有给 P2P 层的这类回答签名,多问几个节点做交叉验证只是部分缓解,中间人仍然可能整体投毒。换言之,getutxos 提供的是效率,不是信任。

提案关闭了,思路活下来了

BIP-64 的正式状态是 Closed:作为 P2P 消息从未部署,作者挂出的实现演示也没进主干。但同一套查询语义在比特币核心的 REST 接口里活得好好的——给节点开 rest 选项后,访问 getutxos 路径加上以斜杠分隔的输出点列表,就能拿回同类结果;想连内存池一起查,加一段 checkmempool。当前源码里这条路径一次最多接受十五个输出点,超了就整单拒绝。它走的是 HTTP 而不是 P2P 洪泛,恰好绕开了提案当年最被诟病的攻击面:把可批量拉取的状态查询直接暴露给任意对等连接,等于给流量放大器开了门。

一条判断线

把这件事放回轻钱包的信息版图里看:布隆过滤解决的是『在区块流里捞出和我有关的交易』,问题在隐私和放大;getutxo 类查询解决的是『对单个输出做点查』,问题在可信。两者的共同软肋都是回答方可信度,所以成熟钱包的常规姿势是组合拳:区块头自己验、数据找多个来源问、涉及金额的结论最终以自己盯紧的节点或可验证包含证明为准。查 UTXO 这件事本身没有捷径——信得少,才查得稳。

风险提示:本文介绍协议机制与历史提案,不构成对任何资产或实现的安全背书;轻客户端结论依赖节点回答,重要操作请以全节点或可验证数据交叉核对,内容不构成投资建议。