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

BIP-385 raw() 与 addr() 输出脚本描述符

把任意原始脚本和任意已有地址各自包成一个描述符,让描述符语言能覆盖现存的每一种输出。本 BIP 目前处于已部署状态。

一、档案卡

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

二、放在哪一族

输出脚本描述符(Output Script Descriptors)族的“兜底”一篇。这一族由 BIP-380 定通用语法与操作,其余各篇各管一类脚本表达式:

BIP-381:非 SegWit 描述符,如 pk()、pkh()

BIP-382:SegWit 描述符,如 wpkh()

BIP-383:多签描述符 multi()、sortedmulti()

BIP-384:combo() 描述符

BIP-386:Taproot 的 tr() 描述符

前几篇覆盖的都是结构规整、钱包日常生成的脚本。BIP-385 补上最后的缺口:碰到任何不在上述模板里的脚本、或者手边只有一个地址时,也能写成描述符,交给同一套描述符工具处理。

三、它要解决什么

描述符的目标,是用一段文本完整说明“这个钱包要看住哪些输出脚本、地址从哪里派生”。但现实中的脚本并不都长在模板里:

原文的动机一句话:为了让描述符与今天正在使用的脚本最大限度兼容,需要能把任意输出脚本、或一个地址,直接包进描述符。没有这一层兜底,描述符语言就总有一块覆盖不到的角落,钱包在导入、导出、只读观察等环节遇到这类输出就只能另走一套逻辑。

四、规则本身

本篇定义两个新的脚本表达式,都只能用作顶层描述符,不能嵌套进 sh()、wsh() 等其他表达式里。

4.1 raw()

写法为 raw(HEX),参数是一串十六进制字符串,表示一段比特币脚本(script)。这个描述符产生的输出脚本,就是 HEX 所表示的那段脚本本身,不做任何加工、包装或哈希。

例如测试向量中 raw(deadbeef) 产生的脚本就是 deadbeef;一长串十六进制的 P2WSH 风格脚本也可以原样放进去。

参数必须是合法十六进制:raw(asdf) 是无效描述符。

4.2 addr()

写法为 addr(ADDR),参数是一个比特币地址。描述符产生的输出脚本,就是该地址对应的输出脚本——即把地址解码还原成的那段 scriptPubKey。

例如测试向量中给出一个 P2SH 地址,其产生的脚本是对应的 OP_HASH160 <20 字节哈希> OP_EQUAL 形式。地址本身无效(如 addr(asdf))则描述符无效。

4.3 为什么只能在顶层

raw() 已经是一段完整脚本,再把它嵌进 sh() 或 wsh() 里哈希一层,语义上既多余又容易误导;addr() 展开后同样是完整输出脚本。原文的无效用例明确列出:sh(raw(...))、wsh(raw(...))、sh(addr(...))、wsh(addr(...)) 全部无效。实现遇到这种嵌套应直接拒绝。

4.4 向后兼容与实现

两者都沿用 BIP-380 的格式与通用操作。作为全新的描述符,它们与此前任何实现都不兼容——旧软件不认识是预期行为。addr() 复用已有地址格式,因此相对容易实现。原文注明:raw() 与 addr() 描述符自 Bitcoin Core 0.17 起已实现。

五、谁必须动

普通持币人:无直接变化。日常收款地址由钱包按常规模板生成,一般用不到手写 raw() 或 addr()。

钱包开发者:解析器需支持这两个顶层表达式:raw() 做十六进制校验后原样输出脚本,addr() 先校验并解码地址再输出其脚本;同时拒绝一切嵌套用法。在做钱包导入、只读观察、任意脚本登记时,这是补全覆盖面的工具。

全节点与矿工:无须改动。描述符是钱包层面的表示法,不改变任何脚本的共识有效性;raw() 包住的脚本上链后是否合法,仍按原有共识规则判断。

共识层:本 BIP 不修改共识规则。

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

状态为 Deployed,原文称已在 Bitcoin Core 0.17 实现。它算不算数,不看网络投票,而看钱包与工具是否按同一语法解析——描述符是应用层的互操作约定,Deployed 表示这套写法已有实现并稳定下来,可供其他钱包对齐。

需要强调的是,addr() 与 raw() 只负责“表示”,不负责“能花”。把一个地址包成描述符,不等于钱包就掌握了花这笔钱所需的私钥或赎回脚本。

常见误读:

“有了 addr() 描述符,就等于导入了这个地址的钱包。”

不对。它只把地址对应的输出脚本登记为描述符的产出,钱包最多能据此监视该地址的收支(只读观察)。要花费其中的资金,仍需相应的私钥或脚本知识,那些都不在地址本身、也不在本 BIP 里。

七、延伸

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