协议演进 / BIP/比特币改进提案 / BIP-382

BIP-382 Segwit 输出脚本描述符

定义 wpkh() 与 wsh() 两种输出脚本描述符表达式,分别用一个密钥或一段脚本生成 P2WPKH 与 P2WSH 输出脚本。本 BIP 目前处于已部署状态。

一、档案卡

档案卡
编号BIP-382
标题Segwit Output Script Descriptors
作者Pieter Wuille <[email protected]>, Ava Chow <[email protected]>
指定日期2021-06-27
层级Applications(应用)
类型Specification(规范类)
状态Deployed(已部署)
依赖BIP-380
阅读版https://bips.dev/382
官方原文https://github.com/bitcoin/bips/blob/master/bip-0382.mediawiki

二、放在哪一族

输出脚本描述符族中负责 Segwit 格式的一篇。通用语法、校验和与密钥表达式都在 BIP-380,本 BIP 在档案上依赖它,只新增两个脚本表达式。

同族分工是:

BIP-381:pk()、pkh()、sh(),对应非 Segwit 格式

BIP-382(本篇):wpkh()、wsh(),对应 SegWit 新增的两种标准输出格式

BIP-383:multi()、sortedmulti() 多签表达式,其脚本常被放进本篇的 wsh() 里

BIP-379:Miniscript,其片段预期用在 wsh() 描述符中——本篇是 Miniscript 落到 P2WSH 的入口

BIP-384:combo(),其 P2WPKH 与 P2SH-P2WPKH 两项正是复用本篇的 wpkh() 与 sh(wpkh()) 组合

三、它要解决什么

隔离见证(SegWit,见 BIP-141)新增了两种主要标准输出格式:P2WPKH 与 P2WSH。钱包要备份、导出、恢复这两类输出,同样需要把“用哪把钥匙、套哪种格式”写进描述符。

原文的动机只有一句话:这些表达式让上述格式能以描述符的形式被指定。通用背景——只存私钥不足以恢复 SegWit 钱包、须把脚本类型与派生路径写显式——见 BIP-380。事实上 BIP-380 特意点明:备份不足的问题正是 SegWit 引入新输出类型后变得突出的;本篇补的就是 SegWit 这一半的描述能力。

四、规则本身

两个新脚本表达式:wpkh()、wsh()。参数与密钥表达式的通用写法见 BIP-380。

4.1 wpkh()

wpkh(KEY) 可作顶层表达式,也可放在 sh() 里面(即 P2SH 嵌套的 P2WPKH,地址以 3 开头的那一类)。它取一个密钥表达式,生成 P2WPKH 输出脚本:

OP_0 <KEY_hash160>

即版本号 OP_0 加公钥的 HASH160 哈希,花费规则与 P2PKH 类似,但签名等见证数据放在 witness 中。一条硬限制:wpkh() 里只能放压缩公钥的钥匙——钥匙本身是压缩公钥、或派生后得到压缩公钥;放入非压缩公钥(例如未压缩 WIF 私钥或以 04 开头的公钥)的描述符无效。

4.2 wsh()

wsh(SCRIPT) 可作顶层表达式,也可放在 sh() 里面。它取一个脚本表达式,生成 P2WSH 输出脚本:

OP_0 <SCRIPT_sha256>

即版本号 OP_0 加内层脚本的 SHA256 哈希。同时它还产生一个 witnessScript——就是参数 SCRIPT 生成的那段脚本本身;花费时必须出示它并满足它,作用相当于 P2SH 的 redeemScript 在 SegWit 下的对应物。

限制有两条:其一,wsh() 内部任何位置出现的密钥表达式,都只能产生压缩公钥;其二,嵌套有禁区——wpkh() 不能放进 wsh(),wsh() 不能再套 wsh(),wsh() 的参数必须是脚本表达式而不能直接是钥匙。原文把这些组合逐一列为无效描述符。

与 BIP-381 对照着看,分工很清楚:sh() 包的是 HASH160、产物在 scriptSig 里交 redeemScript;wsh() 包的是 SHA256、产物在 witness 里交 witnessScript。

五、谁必须动

普通持币人:无直接变化。使用原生 SegWit 地址的钱包,可以用 wpkh()、wsh() 把收款脚本写成可备份、可迁移的描述符。

钱包开发者:实现描述符时需支持这两个表达式,严格执行压缩公钥限制与嵌套禁区;使用 wsh() 时必须同时保存 witnessScript,否则日后无法花费;生成多签或 Miniscript 输出时,内层脚本经 wsh() 包好再交给上层。

全节点与矿工:无须改动。生成的 P2WPKH、P2WSH 都是 SegWit 既有的标准脚本,共识规则不变。

六、现在算不算数,以及一个常见误读

状态为 Deployed(已部署)。原文记载:wpkh()、wsh() 描述符自 Bitcoin Core 0.17 起已有实现。它们采用 BIP-380 的格式与通用操作;作为全新描述符,它们与此前任何实现并不兼容,但生成的脚本都是标准脚本,既有软件对这些脚本本身并不陌生。

常见误读:

“wpkh() 和 pkh() 差不多,钥匙随便放哪种格式都行。”

不对。原文为 SegWit 表达式单列了压缩公钥限制:wpkh() 中只能用压缩公钥,wsh() 内部所有钥匙也只能产生压缩公钥,放入非压缩公钥的描述符直接判为无效。这不是写法偏好,而是规范的一部分,实现若不校验,就会生成与别的钱包对不上的结果。

七、延伸

本页内容整理自 bitcoin/bips 仓库与 bips.dev,事实已与仓库核对;解读仅供学习参考。