协议演进 / BIP/比特币改进提案 / BIP-61
BIP-61
为比特币 P2P 网络新增 reject 消息:当一笔交易、一个区块或一次 version 握手被拒绝时,节点把原因用标准化代码回告发送方,便于不同实现之间排查互操作问题。本 BIP 目前处于已部署状态。
一、档案卡
| 编号 | BIP-61 |
|---|---|
| 标题 | Reject P2P message |
| 作者 | Gavin Andresen <[email protected]> |
| 指定日期 | 2014-06-18 |
| 层级 | Peer Services(对等服务) |
| 类型 | Specification(规范类) |
| 状态 | Deployed(已部署) |
| 阅读版 | https://bips.dev/61 |
| 官方原文 | https://github.com/bitcoin/bips/blob/master/bip-0061.mediawiki |
二、放在哪一族
P2P 网络协议的消息族,与 BIP-60 同属对等服务层、围绕同一时期的协议版本(70,002)展开:一份管 version 消息的格式,一份新增一种消息类型。
它处在交易与区块中继流程之中:inv 公告、getdata 索取、tx/block 发送是既有流程,BIP-61 只是在“发送方送来的东西被拒收”时,补上一条回告通道。此前,被拒绝是沉默的——发送方不知道对方拒收了,更不知道为什么。
三、它要解决什么
原文的动机有两条:
- 互操作:让对等方得到反馈——它的区块或交易为什么被拒、它为什么因不遵守协议而被封禁——有助于不同实现之间彼此兼容、排查分歧;
- 给 SPV 客户端一个提示:当 SPV(简易支付验证)客户端的交易因优先级或手续费不足被拒时,能得到“出问题了”的线索,而不是无声无息地发不出去。
核心问题是:P2P 网络此前没有标准的“我拒绝,理由如下”的表达方式,发送方只能靠超时与猜测。
四、规则本身
4.1 reject 消息的基本形态
新增一种消息类型 “reject”,在收到 “version”“tx” 或 “block” 消息后直接回送给该对等方。例如一笔中继交易被拒的流程是:
--> inv
<-- getdata
--> tx
<-- reject
P2P 协议版本 70,002 及以后的所有实现都应支持 reject 消息。
每条 reject 消息以公共字段开头:
response-to-msg(变长,var_str):触发这条 reject 的消息类型;reject-code(1 字节,uint8_t):0x01 至 0x4f 的拒绝代码;reason(变长,var_string):供调试用的人类可读说明。
原文特别强调:reason 字符串只用于调试,不同实现可以用不同措辞;不应展示给用户,也不应拿来做诊断互操作问题以外的任何用途。真正可依赖的是代码,不是文案。
代码按区间分类:0x01–0x0f 为协议语法错误,0x10–0x1f 为协议语义错误,0x40–0x4f 为服务器策略规则。所有消息类型共用的代码只有一个:0x01(消息无法解码)。
4.2 针对 version 的拒绝代码
在初始连接阶段、回应 “version” 消息时产生:
- 0x11:客户端版本已过时、不受支持;
- 0x12:收到了重复的 version 消息。
4.3 针对 tx 的拒绝:负载与代码
交易拒绝消息在公共字段后追加 32 字节的交易 id 哈希。代码包括:
- 0x10:交易因某种原因无效(签名无效、输出大于输入等);
- 0x12:某个输入已被花费;
- 0x40:因“非标准”(类型或版本不为服务器所知)而未被挖出/中继;
- 0x41:一个或多个输出金额低于 dust(尘埃)阈值;
- 0x42:交易的手续费/优先级不足以被中继或挖出。
4.4 针对 block 的拒绝:负载与代码
区块拒绝消息在公共字段后追加 32 字节的区块头哈希。代码包括:
- 0x10:区块因某种原因无效(工作量证明无效、签名无效等);
- 0x11:区块的版本已不再受支持;
- 0x43:与内建的检查点(checkpoint)不一致。
原文还定了一条克制原则:仅仅“不在服务器眼中的当前最优链上”、但本身有效的区块,不应触发 reject 消息。
4.5 兼容性与实现警告
reject 消息向后兼容:不认识它的旧对等方会直接忽略它。
原文的实现注意事项值得单列:实现者必须考虑攻击者发来伪造 reject 的情形——对有效交易/区块发 reject,或随机乱发 reject——并防范拒绝服务攻击。例如,把每条收到的 reject 都通知用户,会让攻击者轻易实施“烦扰用户”攻击;哪怕只是把每条 reject 都写进调试日志,也可能让实现遭受“塞满用户磁盘”的攻击。
五、谁必须动
普通持币人:无直接变化。你的节点与对等方之间多了一种回告,你不需要做任何设置。
钱包开发者(尤其 SPV 钱包):可以监听 reject 来得知自家交易被拒及大致原因类别;但必须按原文警告处理——不能把对等方单方面发来的 reject 当成最终裁决,更不能逐条弹给用户。
全节点与矿工:按规范,在拒收 version/tx/block 时可以回送 reject;协议版本 70,002 及以后的实现应支持该消息。收到 reject 时则要防伪造与刷屏。
共识层:本 BIP 不修改共识规则。交易或区块是否有效仍由原有规则决定;reject 只是把“我这边的判断与理由”传回去,不改变判断本身。
六、现在算不算数,以及一个常见误读
状态为 Deployed(已部署):它定义的 reject 消息及其代码表是 P2P 协议的一部分。需要注意其定位始终是“调试与互操作反馈”,不是裁决机制。
常见误读:
“收到 reject,就证明我的交易/区块确实无效。”
不对。reject 只是发送方对等节点的一面之词:它可能判断错了,甚至可能是攻击者伪造的——原文专门用一节提醒实现者防范伪造 reject 与借 reject 发起的拒绝服务。交易的最终命运看它能否被诚实节点接受并最终上链,而不是看某一个对等方回了什么。
七、延伸
- 阅读版:https://bips.dev/61
- 官方原文:https://github.com/bitcoin/bips/blob/master/bip-0061.mediawiki
- 相关:BIP-60 定长 version 消息
- 仓库总目录:https://github.com/bitcoin/bips
