协议演进 / BIP/比特币改进提案 / BIP-449
BIP-449
为 tapscript 新增 OP_TWEAKADD:在脚本内把一个 x-only 公钥按标量微调为新公钥 P + t·G,不必泄露私钥。本 BIP 目前处于草案状态。
一、档案卡
| 编号 | BIP-449 |
|---|---|
| 标题 | OP_TWEAKADD - x-only key tweak addition |
| 作者 | Jeremy Rubin <[email protected]> |
| 指定日期 | 2026-03-05 |
| 层级 | Consensus (soft fork)(共识(软分叉)) |
| 类型 | Specification(规范类) |
| 状态 | Draft(草案) |
| 阅读版 | https://bips.dev/449 |
| 官方原文 | https://github.com/bitcoin/bips/blob/master/bip-0449.md |
二、放在哪一族
Taproot / tapscript 扩展族,也是近年来一批“给脚本补上基础运算原语”的软分叉提案之一。
比特币在推导 Taproot 输出密钥时已经在用 x-only 密钥微调(tweak):内部密钥加上一个标量对应的点,得到实际的输出密钥,规则见 BIP-340 与 BIP-341。但这套运算只发生在脚本之外;tapscript 内部没有对应的操作码,脚本算不出微调后的密钥。
BIP-449 补的正是这一块。它单独一个操作码并不能完成复杂合约,原文的示例构造大多要与同期提案组合使用:
BIP-348:OP_CHECKSIGFROMSTACK,对栈上数据验签
BIP-349:OP_INTERNALKEY,把当前 Taproot 内部密钥推上栈
BIP-442:OP_PAIRCOMMIT,专为成对承诺设计;原文指出,若它可用,默克尔化承诺有更简单便宜的做法,BIP-449 的拼法只是“单独部署时也能做”的备选
三、它要解决什么
没有脚本内的密钥微调原语,凡是依赖“密钥之间关系”的协议只能绕路:公开哈希原像、预先承诺多个完整公钥,或者在每一步追加签名检查。成本更高,表达也更笨拙。
原文列出的具体构造包括:
- 微调揭示(tweak-reveal)脚本:脚本承诺一个基础密钥,见证提供标量,现场导出花费密钥;
- 签名顺序证明:一位签名者的授权以另一位的完整签名为微调量,从而证明“我是在看到他的签名之后才签的”,后签者可以据此把前签者的部分授权细化为一笔完整交易;
- 委托(delegation):一方用“以对方公钥哈希为微调量导出的密钥”签名,授权对方在宽松条件下代行;
- 目标微调与密钥揭示合约:要求花费者公开某个标量 t,证明 t·G + k1·G = k2·G 一类等式,本质上是证明掌握某个离散对数;
- 默克尔化承诺:把默克尔根哈希当作微调标量,用一个导出密钥承诺一整棵数据结构,揭示时再验证。
共同的模式是:脚本里固定基础密钥,标量由见证提供。BIP-449 只暴露椭圆曲线加法这一个原语,哈希、Taproot 专用微调的构造等高层逻辑留给脚本组合或其他操作码。
四、规则本身
4.1 适用范围与操作码编号
- 只在 tapscript 中有效(见证版本 1、叶子版本 0xc0);在旧式脚本或 SegWit v0 脚本中,该操作码被禁用,执行即失败。
- 操作码写作 OP_TWEAKADD,编号 0xBE(或待定);原文要求取一个未使用的 OP_SUCCESSx,最好是将来也不太可能恢复原义的位置。这也是它能以软分叉方式部署的原因:未升级节点把 OP_SUCCESSx 一律视为成功。
4.2 栈语义
... [tweak32] [pubkey32] OP_TWEAKADD -> ... [pubkey32_out]
输入在栈顶的是 32 字节 x-only 公钥(大端 x 坐标),其下是 32 字节大端无符号整数标量 t。输出是 Q = P + t·G 的 x 坐标,其中 P 是把输入 x 坐标按 BIP-340 偶数 Y 规则提升(lift)得到的曲线点,G 是 secp256k1 生成元。
执行步骤与失败条件(n 为曲线阶):
- 栈元素不足两个,失败;
- 弹出 pubkey32 与 tweak32,任一长度不是 32 字节,失败;
- 把 tweak32 解析为整数 t,若 t ≥ n,失败——不做模 n 约减;
- 按偶数 Y 提升 pubkey32,若该 x 坐标在曲线上无对应点,失败;
- 计算 Q = P + t·G,若 Q 为无穷远点,失败;
- 把 x(Q) 以 32 字节大端形式压栈。
资源计价:一次定基标量乘加一次点加,预算扣减 50,与 OP_CHECKSIG 同档。
4.3 为什么拒绝 t ≥ n,而不是模约减
若允许模约减,两个不同的 32 字节编码可以表示同一个有效微调。当微调量来自见证数据时(例如对数据做 OP_SHA256 后微调),这会让不同见证产生同一导出密钥,却有不同的交易权重与手续费特性,构成不必要的可塑性来源。拒绝非规范标量,等于规定“导出密钥相同”不能反推出“微调编码相同”这类含混关系。由哈希导出标量的应用本来就要处理哈希值偶然不是合法标量的极小概率情形,原文认为这不构成负担。
4.4 只压栈、不验签;只收原始标量
OP_TWEAKADD 只把导出密钥压回栈上,不直接验签。导出结果可以喂给已有的或拟议的验签操作码、做相等比较,或进入其他脚本逻辑,用途不被锁死在一条验证路径上。
它接收的也是原始 32 字节标量,不在内部做哈希、不计算 Taproot 专用微调。需要哈希微调的构造自己先执行 OP_SHA256;要完全在链上算出 BIP-341 兼容的输出密钥,还需要能向脚本暴露 tapleaf 哈希或 taptree 承诺的通用操作码,不在本提案范围内。
操作数顺序(公钥在栈顶)也是刻意的:常见模式是脚本压入固定密钥、见证给出标量,这样调用 OP_TWEAKADD 前不必先 OP_SWAP。
4.5 两个容易踩的设计注记
标量调整:x-only 编码表示的是点对 {P, −P},规范提升取偶数 Y 的那个。因此链下协议若把某个密钥说成 s·G,共识层面实际的点是 adj(s)·G:s·G 的 Y 为偶数时 adj(s) = s,为奇数时 adj(s) = n − s。跨不同微调路径要求代数关系一致时,协议应在链下先把标量规范化为 adj(s),这用普通库函数即可,不需要改共识。
代数上限:它只做“已知点 + t·G”,不是通用椭圆曲线运算。每次输出再被用作输入时都会重新按偶数 Y 提升,符号信息丢失,所以反复调用也不能拼出任意点的标量乘或任意点加;像 Schnorr 关系 s·G = R + e·P 这样的检查,单靠 OP_TWEAKADD 做不出来。
五、谁必须动
普通持币人:现在什么都不用做。提案仍是草案,部署方式原文标注为待定(TBD);将来即使激活,也只有用到相关合约脚本的人才受影响。
钱包与协议开发者:在支持的前提下,可按 4.2 的栈语义构造微调揭示、委托、签名顺序等脚本;设计时必须处理偶数 Y 的标量调整(见 4.5),并自行保证标量 t 的推导方式安全。
全节点与矿工:这是一次软分叉。升级节点按新规则执行 OP_TWEAKADD;未升级节点仍把该位置当作 OP_SUCCESSx 直接成功——旧节点眼中“无条件成功”的脚本,在新规则下反而可能失败,收紧方向与软分叉的兼容方式一致。
共识层:改动仅限 tapscript。旧式脚本与 SegWit v0 脚本中该操作码保持禁用。
六、现在算不算数,以及一个常见误读
状态为 Draft(草案),部署一节原文只写 TBD,没有激活参数、没有时间表。它目前只是一份带测试向量的规范草案,不能当作主网可用的脚本能力来设计产品;原文也没有给出任何已部署或已采用的记录。
常见误读:
“OP_TWEAKADD 就是把 Taproot 微调完整搬进脚本,有了它什么椭圆曲线运算都能在脚本里做。”
不对。它只做一件事:x(lift_x(X) + t·G),输入是原始标量,不做哈希、不算 BIP-341 的标签哈希微调,更不提供任意点的标量乘或点加。原文专设一节说明其代数上限:连反复调用都拼不出通用的 OP_ECMUL_XONLY。它的定位是“关系到已知基础密钥或生成元导出微调”的窄原语,不是通用密码学计算器。
七、延伸
- 阅读版:https://bips.dev/449
- 官方原文:https://github.com/bitcoin/bips/blob/master/bip-0449.md
- 相关:BIP-340 Schnorr / x-only 密钥、BIP-341 Taproot、BIP-348 OP_CHECKSIGFROMSTACK、BIP-349 OP_INTERNALKEY、BIP-442 OP_PAIRCOMMIT
- 仓库总目录:https://github.com/bitcoin/bips
