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

BIP-381 非 Segwit 输出脚本描述符

定义 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 里的组合写法就少了根基。

七、延伸

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