协议演进 / BIP/比特币改进提案 / BIP-370
BIP-370
PSBT 的第 2 版:把交易信息从一笔固定的未签名交易里拆回各输入输出自己的字段,使 PSBT 创建之后仍能增删输入与输出。本 BIP 目前处于已部署状态。
一、档案卡
| 编号 | BIP-370 |
|---|---|
| 标题 | PSBT Version 2 |
| 作者 | Ava Chow <[email protected]> |
| 指定日期 | 2021-01-14 |
| 层级 | Applications(应用) |
| 类型 | Specification(规范类) |
| 状态 | Deployed(已部署) |
| 阅读版 | https://bips.dev/370 |
| 官方原文 | https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki |
二、放在哪一族
PSBT(部分签名比特币交易)家族的版本升级。它定义的对象由 BIP-174 首创:一种在多个钱包、设备与人之间传递“还没签完的交易”的标准格式,让创建、补充信息、签名、合并、广播可以由不同角色分工完成——硬件钱包离线签名、多签各方接力签名,都靠它。
BIP-174:PSBT Version 0,本 BIP 要修订的对象
BIP-370:PSBT Version 2,只新增字段与字段的必备/禁用规则,其余沿用 BIP-174 的通用格式与角色框架
后续多个 BIP 都是往 PSBT 里加字段,例如 BIP-371 的 Taproot 字段、BIP-373 的 MuSig2 字段;BIP-353 也定义过一个按输出的 PSBT 字段(DNSSEC 证明)。这些扩展字段在 v0 与 v2 中的可用版本,分别由各自文档标注。
一个命名注脚:为什么没有 Version 1?原文脚注解释,BIP-174 的 v0 在口语里常被叫作“version 1”,本提案设计阶段又一直被叫作 version 2,为免混乱,干脆跳过 1、直接定为 2。
三、它要解决什么
BIP-174 的 v0 有一个结构性限制:全局区里放着一笔完整的未签名交易(PSBT_GLOBAL_UNSIGNED_TX),交易长什么样在创建 PSBT 的那一刻就焊死了。此后任何角色都不能再往里加输入或输出,也不能删——想凑整、想合并付款、想多方各出一个输入搞协作交易(例如 CoinJoin 式的多方共同构造),v0 都接不住。
还有一个附带的不便:某个输入或输出的信息被劈成两半,一半在它自己的 input-map / output-map 里,另一半(前序交易、金额、脚本等)埋在那笔全局未签名交易里,读取方要到两处拼凑。
BIP-370 的解法很直接:把全局未签名交易整个去掉,交易版本、锁定时间、每个输入花的是哪笔前序输出、每个输出的金额与脚本,全部拆成独立字段,分别放回全局区与各自的输入/输出区。信息归位之后,增删输入输出才成为可能。
四、规则本身
4.1 总原则
PSBTv2 只规定新字段以及字段的包含/排除要求,通用序列化格式与 BIP-174 相同。
PSBT_GLOBAL_UNSIGNED_TX在 v2 中必须排除(不得出现);PSBT_GLOBAL_VERSION在 v2 中必须包含,且值必须为 2;- 靠版本号区分,v2 与 v0 有意不兼容(见 4.5)。
4.2 全局字段
- 交易版本
PSBT_GLOBAL_TX_VERSION = 0x02(v2 必备):32 位小端整数,表示所构造交易自身的版本号。注意这与 PSBT 的版本号是两回事。 - 回退锁定时间
PSBT_GLOBAL_FALLBACK_LOCKTIME = 0x03(v2 可选):没有任何输入指定必需锁定时间时,用它作交易的 nLockTime;缺省视为 0。 - 输入计数
PSBT_GLOBAL_INPUT_COUNT = 0x04(v2 必备):compact size 整数,记录当前输入个数,增删输入时必须同步增减。 - 输出计数
PSBT_GLOBAL_OUTPUT_COUNT = 0x05(v2 必备):同理,记录输出个数。 - 交易可修改标志
PSBT_GLOBAL_TX_MODIFIABLE = 0x06(v2 可选):8 位位域。第 0 位是“输入可修改”,第 1 位是“输出可修改”,第 2 位是“存在 SIGHASH_SINGLE 签名”——置位表示交易里已有 SIGHASH_SINGLE 签名,其输入输出配对必须保全,后续增删要先逐个输入核查。
4.3 按输入字段
- 前序交易 ID
PSBT_IN_PREVIOUS_TXID = 0x0e(v2 必备):32 字节,标准字节序(非显示序),指明本输入花的是哪笔交易的输出; - 被花输出索引
PSBT_IN_OUTPUT_INDEX = 0x0f(v2 必备):32 位小端整数,与上条共同定位被花的 UTXO; - 序列号
PSBT_IN_SEQUENCE = 0x10(v2 可选):本输入的 sequence,省略时视为最终序列号0xffffffff; - 必需的时间型锁定时间
PSBT_IN_REQUIRED_TIME_LOCKTIME = 0x11(v2 可选):不小于 500,000,000 的 Unix 时间戳,表示本输入要求交易锁定时间至少为该值; - 必需的高度型锁定时间
PSBT_IN_REQUIRED_HEIGHT_LOCKTIME = 0x12(v2 可选):大于 0 且小于 500,000,000 的区块高度,同理。
4.4 按输出字段
- 输出金额
PSBT_OUT_AMOUNT = 0x03(v2 必备):64 位小端整数,单位为聪; - 输出脚本
PSBT_OUT_SCRIPT = 0x04(v2 必备):该输出的 scriptPubKey 原始字节。
4.5 锁定时间怎么定、唯一标识怎么算
交易的 nLockTime 由全局回退值与各输入的必需锁定时间共同决定:没有任何输入提要求时,用 PSBT_GLOBAL_FALLBACK_LOCKTIME(缺省 0);有输入提要求时,只能选全体提要求的输入都支持的那一类(时间型或高度型),取该类中的最大值;若两类都有可能(有输入同时给了两类),选高度型——原文给的理由是签名要承诺锁定时间,签名方必须能唯一确定用哪个,而比特币的时间单位本质上是区块高度。
PSBTv2 的唯一标识:用其中的字段拼出一笔未签名交易并计算其 txid。其中 sequence 一律置 0(因为 Updater 与 Combiner 可以改 sequence),锁定时间按上段规则计算。
4.6 角色变化
v2 在 BIP-174 的角色链(Creator、Updater、Signer、Combiner、Input Finalizer、Transaction Extractor)中插入了一个新角色,并改了几个角色的义务:
- Creator(创建者):在 v2 中只做初始化——建一个 0 输入、0 输出的空 PSBT,版本置 2,建议同时设好回退锁定时间。若之后要由别人加输入输出,必须写明
PSBT_GLOBAL_TX_MODIFIABLE的可修改位,且交易版本至少为 2(原文理由:交易版本参与 OP_CHECKSEQUENCEVERIFY 等特性的验证规则,要求不低于 2 比逐项协商更简单)。若创建者自己兼任构造者且不再增删,该标志可省略。 - Constructor(构造者,v2 新增):负责真正添加输入与输出。添加前必须检查可修改标志:输入可修改位为真才能加输入,输出同理。加输入必须同时写入前序 txid 与输出索引并递增输入计数,加输出必须写入金额与脚本并递增输出计数。输入带必需锁定时间时要写入相应字段,并遍历既有输入检查锁定时间类型是否兼容;若新输入会改变锁定时间而 PSBT 里已有签名,则不得加入。SIGHASH_SINGLE 标志为真时,还须先满足带该类签名的输入与对应输出的配对数量要求。构造者也可以把可修改位清零或删掉整个标志字段,宣告“到此为止、不许再加”。
- Updater(更新者):在 v2 中可以设置 sequence。
- Signer(签名者):签名后必须同步更新可修改标志以反映真实状态:签了不带 SIGHASH_ANYONECANPAY 的签名,输入可修改位置假;签了不带 SIGHASH_NONE 的签名,输出可修改位置假;签了 SIGHASH_SINGLE 的签名,把相应标志位置真。
- Input Finalizer(输入定稿者):按 BIP-174 清理已定稿输入的字段时,必须保留前序 txid、输出索引、sequence 与两个必需锁定时间字段——交易提取靠它们拼交易,缺了前两者的输入无效。
- Transaction Extractor(交易提取者):改用 v2 字段拼出最终交易,锁定时间按 4.5 的规则定;全部输入完成时,应能产出一笔可广播的完整序列化交易。
4.7 与 v0 的兼容
两者共用 BIP-174 的通用格式,v0 的解析器只需增加对新字段的支持即可反序列化 v2。但因版本号不同,v2 与 v0 有意互不兼容——正是为了能名正言顺地去掉全局未签名交易。需要转换时,可以从 v2 字段拼出未签名交易,再据此生成一个 v0 的 PSBT。原文附了大量测试向量,多数是“v0 里混入 v2 字段”或“v2 缺必备字段”一类应判定为无效的样例。
五、谁必须动
普通持币人:无直接感知。PSBT 是钱包与签名设备之间的幕后格式,最终广播出去的仍是普通比特币交易,链上看不出这笔交易曾用 v0 还是 v2 传递。
钱包与签名设备开发者:是主要的动工方——要实现 v2 的字段拆分、计数维护、可修改标志的检查与更新、新角色 Constructor 的流程,以及锁定时间裁决规则。只支持 v0 的软件无法处理 v2 的 PSBT。
多签与协作交易参与方:间接受益方。输入输出可在创建后增删,多方接力构造同一笔交易(各加各的输入、签名方逐个加入)才有了标准格式支撑;同时每位后加入者都必须遵守可修改标志与 SIGHASH_SINGLE 配对约束。
全节点与矿工、共识层:完全不涉及。PSBT 是链下的签名协调格式,不改交易格式,不改共识规则。
六、现在算不算数,以及一个常见误读
状态为 Deployed(已部署):表示该标准已经定稿并进入实际部署使用,是 BIP 状态体系里“已被采用”的档位。它不是共识规则,不存在“激活高度”一说;具体哪些钱包与硬件设备支持 v2,原文没有给出清单,本页不臆测。工程上要注意的是混用问题:v2 与 v0 有意不兼容,传递前要确认链条上每个工具都认 v2,否则应在交界处按原文方法转换回 v0。
常见误读:“PSBT Version 2 是比特币交易的第 2 版,升级了链上交易格式。”不对。PSBT 是交易在签完名之前、在各参与方之间流转的链下文件格式;BIP-370 改的只是这个文件的内部字段组织——把交易信息从一笔焊死的未签名交易里拆开,好让输入输出能后加。最终提取、广播的仍是一笔完全普通的比特币交易,节点与矿工对此毫无感知。
七、延伸
- 阅读版:https://bips.dev/370
- 官方原文:https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki
- 相关:BIP-174 PSBT Version 0、BIP-371 PSBT 的 Taproot 字段
- 仓库总目录:https://github.com/bitcoin/bips
