比特币脚本的历史上有一桩悬案:2010 年 3 月,开发者发现早期协议里某些操作码可能被滥用做拒绝服务攻击,于是通过一次紧急软分叉批量禁用了 OP_SUB、OP_MUL、OP_SPLIT 等十几个操作码——那次修复对应的安全隐患后来被编号为 CVE-2010-5137。从此「被禁操作码」成了比特币脚本的一块封存区:不是被证明永远危险,而是再没人敢确认放开它们会不会重演当年的计算量失控。BIP-441 是社区里第一个把「解封」写成完整可执行文本的提案:Rusty Russell 与 Julian Moik 起草,2026 年 3 月 25 日立项,仍是草案,标题起得很直白——恢复被禁脚本(Tapleaf 0xC2)。
它的手法不是逐条平反,而是新开一种 Tapscript 叶子版本。一个 Taproot 输出内部的脚本树,每片叶子都可以有自己的版本字节;BIP-441 认领 0xC2 这个版本,声明:叶子版本是 0xC2 的脚本,按「恢复后的规则」执行——被禁操作码重新可用,其余规则照 BIP-342(现行 Tapscript)执行。这种「加新版本」的软分叉套路,与当年塞格维特、Taproot 用版本号扩展脚本语义是同一家族:旧规则下这些输出本来就没法花(未知叶子版本一律无效),激活后它们变成可花的,老节点看到也不会破坏共识安全。
真正的功夫在护栏上。当年禁码的根因是没有一个机制约束「脚本最坏情况要算多久」,BIP-441 的答案是整体接上 BIP-440 的可变操作码预算(Varops Budget):每个操作码按其最坏情况消耗计费,整笔交易有一个总预算,超了就拒绝——预算模型下,计算量与交易体积、数据规模挂钩,而不是像 2010 年那样全靠拍脑袋禁用。在此之上,BIP-441 给自己划了四条内存线:单个栈对象从 520 字节放宽到 4,000,000 字节——选这个数字是因为「把整笔交易压进栈里再处理」是可预见的主流用法,4MB 正是区块大小量级;栈与备用栈的元素总字节数封顶 8,000,000,给「操作至少复制一份」的运算留双倍空间;栈元素数量上限从 1,000 提到 32,768——提案算了笔账:最小输入 41 字节约能塞两万四千个,最小输出 9 字节约能塞十一万个,三万二的槽位对「把全部输入输出逐一上栈验证」的场景足够;数值不再限 32 位宽,且一律按无符号数处理。作者的意图写得很明白:早期限制保住了网络,却也「拿走了用户精确控制支出条件的能力」,可编程货币的理想被冻在 2010 年的恐惧里。
值得注意的还有它把每个操作码逐字节定义到可以机械推算 varops 成本的写法——文档自己承认这种细节「对脚本使用者没必要,只给实现者看」。这是新一代脚本提案的共同气质:不争论要不要功能,先把成本模型算到字节级,让矿工可以用统一口径评估验证负担。当然,草案与现实之间还隔着标准:BIP-440 本身还在推进,钱包编译器要会生成 0xC2 叶子,节点要默认执行它,激活信号要走完整流程。在被禁操作码封存十六年之后,这份提案至少第一次让「解封」从口号变成了一个带价格表的工程方案。
顺手把 0xC2 的账算一遍给矿工看:一笔交易若把整块 4MB 的数据全部堆进栈里,再用恢复的乘法类操作码逐字节处理,最坏情况的 CPU 量由 varops 预算封顶——这正是提案强调逐字节定义操作码的原因:每种操作的预算价签都能从描述机械推出,不同实现之间的最坏情况解释不会跑偏。对比 2010 年那种「不确定就全禁」的处置,这套方法论本身就值一次升级。
快速问答
问:为什么不直接修改现有 Tapscript 规则而要新叶子版本? 答:改现行规则会立刻改变已有输出的花费语义,所有节点必须同步升级;新叶子版本只影响主动选择 0xC2 的新输出,旧输出纹丝不动,兼容代价最小。
问:520 字节栈对象上限本来防什么? 答:防止单个巨大元素让验证内存与复制成本失控。提到 400 万字节之所以被认为可控,全靠 varops 预算把「大对象上的每次操作」都标了价。
风险提示:脚本规则变更属于共识层事项,落地时间与参数可能随审查调整,本文以立项草案文本为准,不预设激活结果。

发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。