协议演进 / BIP/比特币改进提案 / BIP-348
BIP-348
提出 OP_CHECKSIGFROMSTACK,让 Tapscript 对栈上给出的公钥、消息与签名做 Schnorr 验签,而不再局限于当前交易的签名哈希。本 BIP 目前处于草案状态。
一、档案卡
| 编号 | BIP-348 |
|---|---|
| 标题 | CHECKSIGFROMSTACK |
| 作者 | Brandon Black <[email protected]> Jeremy Rubin <[email protected]> |
| 指定日期 | 2024-11-26 |
| 层级 | Consensus (soft fork)(共识(软分叉)) |
| 类型 | Specification(规范类) |
| 状态 | Draft(草案) |
| 阅读版 | https://bips.dev/348 |
| 官方原文 | https://github.com/bitcoin/bips/blob/master/bip-0348.md |
二、放在哪一族
Tapscript 验签能力扩展族。比特币脚本原有的 OP_CHECKSIG 只能验证“对当前这笔花费交易的签名哈希”的签名;BIP-348 把验签的对象换成栈上任意给出的数据,是 Tapscript 走向更通用合约的一块拼图。
它和邻居是配套关系:
BIP-340:Schnorr 签名方案,本 BIP 的验签完全按它执行
BIP-342:Tapscript 规则;本 BIP 在其叶子版本 0xc0 语境下替换 OP_SUCCESS 占位操作码,验签计费也用它的 sigops 预算
BIP-119:OP_CHECKTEMPLATEVERIFY。与 CSFS 组合可实现闪电网络的 LN Symmetry 通道(见下节)
BIP-346:OP_TXHASH。TXHASH 算出交易哈希、CSFS 对它验签,两者相加即得到完全通用的签名哈希构造
BIP-349:OP_INTERNALKEY。同作者同批提案;CSFS 的设计讨论里明确说,APO 式的内部公钥用法不塞进本 BIP,而由 BIP-349 单独承担
历史上 CHECKSIGFROMSTACK 先在 Elements 侧链实现,本 BIP 的参考实现即参照它、并以 Bitcoin Core 的拉取请求形式给出(原文 Implementation 一节链接 PR #29270)。
三、它要解决什么
OP_CHECKSIG 的验签对象被共识写死:只能是当前交易的签名哈希。这意味着脚本无法要求“某人对某段任意数据签过名”——比如对一个模板哈希签名、对一条委托声明签名。想绕开只能靠各种技巧,复杂且受限。
OP_CHECKSIGFROMSTACK(常缩写为 CSFS)把三件东西都交给脚本从栈上提供:公钥、消息、签名。原文给的主要用途有三类:
-
LN Symmetry(闪电网络对称通道):与 BIP-119 的 CTV 组合,
OP_CHECKTEMPLATEVERIFY <pubkey> OP_CHECKSIGFROMSTACK加上含 CTV 哈希与其签名的花费栈,在逻辑上等价于 BIP-118 的<bip118_pubkey> OP_CHECKSIG配 SIGHASH_ALL|SIGHASH_ANYPREVOUTANYSCRIPT 签名,只多花 8 vBytes。原文也对比了其他实现路线(APO 原案、CTV+CSFS+IKEY 等组合)。 -
委托(delegation):脚本先直接验签,或改走“被委托人签名”分支——原文给出的脚本形如
<pubkey> SWAP IF 2 PICK SWAP CSFS VERIFY ENDIF CHECKSIG,持有人可以把花费权临时让给另一把公钥,而不必转移币。 -
带时间锁的高级委托:与 OP_CAT(BIP-347)或拟议的 OP_PAIRCOMMIT 组合,可以做到“某个时间点之后可委托给不同公钥、各自对应不同时间”的安排。
四、规则本身
4.1 激活方式与栈语义
在验证叶子版本为 0xc0 的 Taproot 脚本花费时(定义见 BIP-342),以软分叉方式把 OP_SUCCESS204(0xcc)重定义为 OP_CHECKSIGFROMSTACK。执行时从栈顶向下依次读:公钥(栈顶)、消息(次顶)、签名(第三个),三者一并弹出,然后:
- 栈上不足 3 个元素:脚本立即失败;
- 公钥长度为 0:脚本立即失败;
- 公钥为 32 字节:按 BIP-340 的公钥对待;签名非空时按 BIP-340 对该消息验签,验签失败立即终止脚本并判失败;
- 公钥长度非 0 且非 32 字节:视为未知公钥类型,不做验签、按验签成功处理——这是照 BIP-342 对未知公钥类型的既有办法预留前向兼容。
4.2 结果压栈
脚本未失败的前提下:
- 签名为空向量:压入一个空向量(相当于 0),执行继续;
- 签名非空:该操作计入 BIP-342 的 sigops 预算,并压入 1 字节的 0x01(相当于真)。
注意它没有单独的 VERIFY 版本:要“验不过就失败”,脚本自己在后面接 OP_VERIFY 或条件判断。原文解释,加单一操作码是为了保持实现与设计简单;早期草案曾有以 NOP 升级的 verify 变体,留待将来若把这功能带到传统脚本时再议。
4.3 几个明确的设计取舍
- 消息不再哈希:验签前不对消息做预哈希,因为 BIP-340 的验签本身兼容任意长度消息、不要求输入先安全哈希;
- 不照顾多签语义:不对 OP_CHECKMULTISIG / OP_CHECKSIGADD 做任何让步;在 Tapscript 里想拼出“累加”效果,每个公钥多花 1 vByte 即可(借助辅助栈与 OP_ADD 组合,原文给了写法);
- 不负责拆分签名:把签名的 R、S 分开摆在栈上这类事,留给 BIP-347(OP_CAT)等其他升级;
- 资源计费:与其它验签操作码同等对待,计入 sigops 与预算。
五、谁必须动
普通持币人:无直接变化。草案未激活,现有钱包与地址不受影响。
钱包与协议开发者:是直接使用者,尤其是闪电网络与委托类协议。写脚本时要分清:CSFS 验的是栈上消息,与当前交易没有自动绑定关系——想约束交易,必须自己把交易相关哈希(如 CTV 哈希或 BIP-346 的 TXHASH 结果)作为消息放上栈,漏了这一步,签名可被重放到别的交易上。
全节点与矿工:若将来部署,这是一次共识软分叉:节点按新语义执行 OP_SUCCESS204 并对验签计 sigops。不升级的旧节点会把含该操作码的脚本按 OP_SUCCESS 直接放行,这正是软分叉的兼容方式;也因此原文提醒,依赖 OP_SUCCESS204 旧行为的花费会被新规则判为无效。
共识层:改动仅限叶子版本 0xc0 的 Tapscript 语境,其它脚本语境不受影响。
六、现在算不算数,以及一个常见误读
状态为 Draft(草案),原文的部署一节直接写 TBD(待定)。它既未定稿,更未激活:主网节点今天遇到 OP_SUCCESS204 只会按占位语义直接通过,不会做任何 CSFS 验签。用它写的脚本现在放到主网上,等于把验签条件凭空抹掉——这是草案阶段最实际的风险提示。
常见误读:
“OP_CHECKSIGFROMSTACK 就是 OP_CHECKSIG 的加强版,验的还是这笔交易。”
不对。OP_CHECKSIG 验的签名对象由共识固定为当前交易的签名哈希;CSFS 验的是栈上任意消息,脚本给什么它验什么,与当前交易毫无自动关联。它之所以能用于 LN Symmetry、委托与契约,正是因为这种“任意消息”;而用它约束交易时,必须由协议设计者亲手把交易哈希送上栈,绑定关系是设计出来的,不是操作码白送的。
七、延伸
- 阅读版:https://bips.dev/348
- 官方原文:https://github.com/bitcoin/bips/blob/master/bip-0348.md
- 相关:BIP-340 Schnorr、BIP-342 Tapscript、BIP-119 OP_CHECKTEMPLATEVERIFY、BIP-346 OP_TXHASH、BIP-349 OP_INTERNALKEY
- 仓库总目录:https://github.com/bitcoin/bips
