协议演进 / BIP/比特币改进提案 / BIP-381
BIP-381
定义 pk()、pkh() 与 sh() 三种输出脚本描述符表达式,分别用一个密钥或一段脚本生成 P2PK、P2PKH 与 P2SH 输出脚本。本 BIP 目前处于已部署状态。
一、档案卡
| 编号 | BIP-381 |
|---|---|
| 标题 | Non-Segwit Output Script Descriptors |
| 作者 | Pieter Wuille <[email protected]>, Ava Chow <[email protected]> |
| 指定日期 | 2021-06-27 |
| 层级 | Applications(应用) |
| 类型 | Specification(规范类) |
| 状态 | Deployed(已部署) |
| 依赖 | BIP-380 |
| 阅读版 | https://bips.dev/381 |
| 官方原文 | https://github.com/bitcoin/bips/blob/master/bip-0381.mediawiki |
二、放在哪一族
输出脚本描述符族中负责“非 Segwit”老格式的一篇。通用语法、校验和与密钥表达式都在 BIP-380,本 BIP 在档案上依赖它,只新增三个脚本表达式。
同族分工是:
BIP-381(本篇):pk()、pkh()、sh(),对应 SegWit 激活之前的三种主要标准输出格式
BIP-382:wpkh()、wsh(),对应 SegWit 的两种格式
BIP-383:multi()、sortedmulti(),多签脚本,常被 sh() 或 wsh() 包在里面
BIP-384:combo(),一把钥匙一次生成多种格式,其 P2PK、P2PKH 两项正是复用本篇的 pk()、pkh()
这三个表达式也是嵌套结构的零件:sh() 可以包住其他表达式,pkh() 还能被放进 BIP-382 的 wsh() 里。
三、它要解决什么
在 SegWit 激活之前,比特币主要有三种标准输出脚本格式:P2PK、P2PKH 与 P2SH。钱包要备份、导出或恢复这几类输出,需要一种统一的写法,把“用哪把钥匙、套哪种格式”一次写清——这正是描述符要做的事,本篇负责其中最老的三种格式。
原文的动机只有一句话:这些表达式让上述格式能以描述符的形式被指定。背后的通用问题——只存私钥不足以恢复钱包、派生路径缺失导致跨钱包不兼容——已在 BIP-380 里说明,本篇不再重复,而是直接给出三个表达式与脚本模板的对应关系。
把老格式纳入描述符还有一层意义:它们并没有消失。P2PKH 仍是常见地址类型,P2SH 仍是包住 SegWit 与多签脚本的外壳(见 sh(wpkh(...))、sh(wsh(...)) 等组合)。描述符体系要完整,就必须能描述这一层。
四、规则本身
三个新脚本表达式:pk()、pkh()、sh()。参数与密钥表达式的通用写法见 BIP-380;上层表达式可对可放入的公钥类型另加限制,由上层那篇 BIP 说明。
4.1 pk()
pk(KEY) 可用在描述符的任何上下文与任何层级。它取一个密钥表达式,生成 P2PK 输出脚本:
<KEY> OP_CHECKSIG
即把公钥本身写进脚本,花费时只需该钥匙的一个签名。这是最早的输出形式,今天已很少直接用作收款地址,但仍是其他结构里的基本零件。
4.2 pkh()
pkh(KEY) 可作顶层表达式,也可放在 sh() 或 wsh() 里面。它取一个密钥表达式,生成 P2PKH 输出脚本:
OP_DUP OP_HASH160 <KEY_hash160> OP_EQUALVERIFY OP_CHECKSIG
脚本里只放公钥的 HASH160 哈希,不放公钥本身;花费时出示公钥与签名,节点先验公钥哈希对得上、再验签名。这就是以 1 开头的传统地址背后的脚本。
4.3 sh()
sh(SCRIPT) 只能作顶层表达式。它取一个脚本表达式,生成 P2SH 输出脚本:
OP_HASH160 <SCRIPT_hash160> OP_EQUAL
同时它还产生一个 redeemScript——就是参数 SCRIPT 生成的那段脚本本身。收款方地址里只放这段脚本的哈希;花费时必须出示 redeemScript 并满足它。把真实花费条件藏进哈希,是 P2SH(见 BIP-16)的基本手法,多签、嵌套 SegWit 都靠它落地。
三者的参数类型不能混:pk()、pkh() 只收密钥表达式,sh() 只收脚本表达式;sh() 也不能再套 sh(),原文把这些列为无效描述符。
五、谁必须动
普通持币人:无直接变化。仍在用传统地址或 P2SH 地址的钱包,可以用这三个表达式把收款脚本写成可备份、可迁移的描述符。
钱包开发者:实现描述符时需支持这三个表达式及其嵌套位置限制,能由密钥表达式派生出子钥匙、再按模板生成脚本;使用 sh() 时必须同时保存 redeemScript,否则日后无法花费。
全节点与矿工:无须改动。生成的都是早已存在的标准脚本,共识规则不变。
六、现在算不算数,以及一个常见误读
状态为 Deployed(已部署)。原文记载:pk()、pkh()、sh() 描述符自 Bitcoin Core 0.17 起已有实现。它们采用 BIP-380 的格式与通用操作;作为全新描述符,它们与此前任何实现并不兼容,但生成的脚本都是标准脚本,既有软件对这些脚本本身并不陌生。
常见误读:
“非 Segwit 描述符讲的是已被淘汰的格式,可以跳过不看。”
不对。原文把这三种格式列为 SegWit 之前的主要标准输出格式,而描述符体系要用同一套语言覆盖钱包实际会遇到的全部输出:P2PKH 仍在使用,sh() 更是嵌套 SegWit 与多签的必经外壳。跳过本篇,BIP-382、BIP-383 里的组合写法就少了根基。
七、延伸
- 阅读版:https://bips.dev/381
- 官方原文:https://github.com/bitcoin/bips/blob/master/bip-0381.mediawiki
- 相关:BIP-380 描述符通用操作、BIP-382 Segwit 描述符、BIP-383 多签描述符、BIP-384
combo()描述符、BIP-16 P2SH - 仓库总目录:https://github.com/bitcoin/bips
