bips/bip-0342.mediawiki,位于 ajtowns/bips 的 bip-anyprevout

  • ajtowns
  • 发布于 2025-03-25 16:22
  • 阅读 15

该文档详细描述了 Taproot 脚本验证的语义,包括对签名操作码的修改、多重签名策略的实现、以及资源限制的变化等。Tapscript通过软分叉进行升级,并引入了OP_SUCCESS操作码以实现更简洁的新操作码引入方式,旨在改进比特币脚本系统的灵活性和效率。

跳到内容

ajtowns/ bips 公开

forked from bitcoin/bips

折叠文件树

文件

bip-anyprevout

搜索此仓库

/

bip-0342.mediawiki

复制路径

BlameMore 文件操作

BlameMore 文件操作

最近提交

luke-jrluke-jr

合并 pull request bitcoin#1104 来自 ajtowns/202103-bip341-speedy-tri…

打开提交详情

Apr 25, 2021

40b10c8 · Apr 25, 2021

历史

历史

打开提交详情

查看此文件的提交历史。

146 行 (113 loc) · 23.3 KB

/

bip-0342.mediawiki

顶部

文件元数据和控制

  • 预览
  • 代码
  • Blame

146 行 (113 loc) · 23.3 KB

Raw

复制原始文件

下载原始文件

大纲

编辑和原始操作

  BIP: 342
  Layer: Consensus (soft fork)
  Title: Validation of Taproot Scripts
  Author: Pieter Wuille <pieter.wuille@gmail.com>
          Jonas Nick <jonasd.nick@gmail.com>
          Anthony Towns <aj@erisian.com.au>
  Comments-Summary: No comments yet.
  Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0342
  Status: Draft
  Type: Standards Track
  Created: 2020-01-19
  License: BSD-3-Clause
  Requires: 340, 341
  Post-History: 2019-05-06: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2019-May/016914.html [bitcoin-dev] Taproot proposal
## 目录<br>固定链接:目录<br>- 简介 <br> - 摘要<br> - 版权<br> - 动机<br>- 设计<br>- 规范 <br> - 脚本执行<br> - 签名操作码规则<br> - 签名验证<br> - 资源限制<br>- 原理<br>- 部署<br>- 示例<br>- 致谢

简介

固定链接:简介

摘要

固定链接:摘要

本文档规定了在 BIP341 下的初始脚本系统的语义。

版权

固定链接:版权

本文档采用 3-clause BSD 许可。

动机

固定链接:动机

BIP341 仅对脚本结构提出了改进,但其某些目标与脚本语言本身中某些操作码的语义不兼容。 虽然可以在单独的可选改进中处理这些问题,但除非与 BIP341 本身同时解决,否则无法保证其影响。

具体来说,目标是使 Schnorr 签名批量验证签名哈希 改进也可用于使用脚本系统的花费。

设计

固定链接:设计

为了实现这些目标,签名操作码 OP_CHECKSIGOP_CHECKSIGVERIFY 被修改为验证 BIP340 中指定的 Schnorr 签名,并使用基于 BIP341 中通用消息计算的签名消息算法。 tapscript 签名消息还简化了 OP_CODESEPARATOR 处理并使其更有效。

效率低下的 OP_CHECKMULTISIGOP_CHECKMULTISIGVERIFY 操作码被禁用。 相反,引入了一个新的操作码 OP_CHECKSIGADD,以允许以可批量验证的方式创建相同的多重签名策略。 Tapscript 使用了一种新的、更简单的签名操作码限制,修复了与交易权重复杂的交互。 此外,通过要求 MINIMALIF,消除了潜在的可延展性向量。

Tapscript 可以通过定义未知的密钥类型通过软分叉进行升级,例如添加新的 hash_types 或签名算法。 此外,新的 tapscript OP_SUCCESS 操作码允许比通过 OP_NOP 更干净地引入新的操作码。

规范

固定链接:规范

以下规则仅在验证满足以下所有条件的交易输入时适用:

  • 该交易输入是隔离见证花费(即,scriptPubKey 包含 BIP141 中定义的见证程序)。
  • 它是 BIP341 中定义的 taproot 花费(即,见证版本为 1,见证程序为 32 字节,并且未进行 P2SH 包装)。
  • 它是 BIP341 中定义的 脚本路径花费(即,在从见证堆栈中删除可选的 annex 之后,保留两个或多个堆栈元素)。
  • 叶子版本为 0xc0(即,在删除可选的 annex 之后,最后一个见证元素的第一个字节是 0xc00xc1),将其标记为 tapscript 花费

对此类输入的验证必须等同于按指定顺序执行以下步骤。

  1. 如果输入由于 BIP141 或 BIP341 而无效,则失败。
  2. BIP341 中定义的脚本(即,在删除可选 annex 之后,倒数第二个见证堆栈元素)称为 tapscript,并被解码为一个一个的操作码:
    1. 如果遇到任何编号为 80, 98, 126-129, 131-134, 137-138, 141-142, 149-153, 187-254 的操作码,则验证成功(以下任何规则均不适用)。 即使 tapscript 中后面的字节无法以其他方式解码,也是如此。 这些操作码被重命名为 OP_SUCCESS80、...、OP_SUCCESS254,统称为 OP_SUCCESSx[ 1]。
    2. 如果任何 push 操作码解码失败,因为它会超出 tapscript 的末尾,则失败。
    3. 如果 BIP341 中定义的 初始堆栈(即,在删除可选的 annex 和之后的两个最后一个堆栈元素之后的见证堆栈)违反任何资源限制(堆栈大小以及堆栈中元素的大小;请参阅下面的“资源限制”),则失败。 请注意,可以使用 OP_SUCCESSx 绕过此检查。
    4. 根据以下部分中的规则执行 tapscript,并将初始堆栈作为输入。
  3. 如果由于任何原因执行失败,则失败。
  4. 如果执行结果在堆栈上只留下一个元素,且该元素使用 CastToBool() 计算结果为 true,则失败。
  5. 如果此步骤在没有遇到失败的情况下到达,则验证成功。

脚本执行

固定链接:脚本执行

tapscript 的执行规则基于 BIP141 中 P2WSH 的执行规则,包括 BIP65BIP112 中定义的 OP_CHECKLOCKTIMEVERIFYOP_CHECKSEQUENCEVERIFY 操作码,但有以下修改:

  • 禁用的脚本操作码 以下脚本操作码在 tapscript 中被禁用:OP_CHECKMULTISIGOP_CHECKMULTISIGVERIFY[ 2]。 禁用的操作码的行为方式与 OP_RETURN 相同,即在执行时立即失败并终止脚本,并且在脚本的未执行分支中找到时会被忽略。
  • 共识强制执行的 MINIMALIF MINIMALIF 规则,它只是 P2WSH 中的一个标准规则,在 tapscript 中是共识强制执行的。 这意味着 OP_IFOP_NOTIF 操作码的输入参数必须正好是 0(空向量)或正好是 1(值为 1 的单字节向量)[ 3]。
  • OP_SUCCESSx 操作码 如上所述,某些操作码被重命名为 OP_SUCCESSx,并使脚本无条件有效。
  • 签名操作码OP_CHECKSIGOP_CHECKSIGVERIFY 被修改为对 Schnorr 公钥和签名(参见 BIP340)而不是 ECDSA 进行操作,并添加了一个新的操作码 OP_CHECKSIGADD
    • 操作码 186 ( 0xba) 被命名为 OP_CHECKSIGADD。[ 4][ 5]

签名操作码规则

固定链接:签名操作码规则

以下规则适用于 OP_CHECKSIGOP_CHECKSIGVERIFYOP_CHECKSIGADD

  • 对于 OP_CHECKSIGVERIFYOP_CHECKSIG,公钥(顶部元素)和签名(顶部第二个元素)从堆栈中弹出。
    • 如果堆栈上的元素少于 2 个,则脚本必须失败并立即终止。
  • 对于 OP_CHECKSIGADD,公钥(顶部元素)、CScriptNum n(顶部第二个元素)和签名(顶部第三个元素)从堆栈中弹出。
    • 如果堆栈上的元素少于 3 个,则脚本必须失败并立即终止。
    • 如果 n 大于 4 个字节,则脚本必须失败并立即终止。
  • 如果公钥大小为零,则脚本必须失败并立即终止。
  • 如果公钥大小为 32 字节,则认为它是 BIP340 中描述的公钥:
    • 如果签名不是空向量,则针对公钥验证签名(请参阅下一小节)。 在这种情况下,验证失败会立即终止脚本执行并失败。
  • 如果公钥大小不为零且不为 32 字节,则该公钥是未知的公钥类型[ 6] 并且不应用实际的签名验证。 在签名操作码的脚本执行期间,它们的行为与已知的公钥类型完全相同,只是签名验证被认为是成功的。
  • 如果脚本在此步骤之前没有失败并终止,无论公钥类型如何:
    • 如果签名是空向量:
      • 对于 OP_CHECKSIGVERIFY,脚本必须失败并立即终止。
      • 对于 OP_CHECKSIG,一个空向量被推送到堆栈上,并且执行继续执行下一个操作码。
      • 对于 OP_CHECKSIGADD,一个值为 nCScriptNum 被推送到堆栈上,并且执行继续执行下一个操作码。
    • 如果签名不是空向量,则该操作码计入 sigops 预算(请参阅下文)。
      • 对于 OP_CHECKSIGVERIFY,执行继续进行,而不会对堆栈进行任何进一步的更改。
      • 对于 OP_CHECKSIG,一个 1 字节的值 0x01 被推送到堆栈上。
      • 对于 OP_CHECKSIGADD,一个值为 n + 1CScriptNum 被推送到堆栈上。

签名验证

固定链接:签名验证

要使用公钥 p 验证签名 sig

  • 计算 tapscript 消息扩展 ext,它由以下内容的串联组成:
    • tapleaf_hash (32):BIP341 中定义的 tapleaf 哈希
    • key_version (1):表示 tapscript 签名操作码执行中公钥当前版本的常量值 0x00
    • codesep_pos (4):在当前执行的签名操作码之前最后执行的 OP_CODESEPARATOR 的操作码位置,值为小端字节序(或者如果未执行,则为 0xffffffff)。 脚本中的第一个操作码的位置为 0。 多字节 push 操作码计为一个操作码,无论正在推送的数据的大小如何。 已解析但未执行的分支中的操作码也计入此值。
  • 如果 sig 的长度为 64 字节,则返回 Verify(p, hashTapSighash(0x00 || SigMsg(0x00, 1) || ext), sig),其中 VerifyBIP340 中定义。
  • 如果 sig 的长度为 65 字节,则返回 sig[64] ≠ 0x00 和 Verify(p, hashTapSighash(0x00 || SigMsg(sig[64], 1) || ext), sig[0:64]).
  • 否则,失败。

总之,签名验证的语义与 BIP340 相同,除了以下几点:

  1. 签名消息包括 tapscript 特定的数据 key_version。[ 7]
  2. 签名消息通过 tapleaf_hash(包括叶子版本和脚本)而不是 scriptCode 来提交已执行的脚本。 这意味着此提交不受 OP_CODESEPARATOR 的影响。
  3. 签名消息包括最后执行的 OP_CODESEPARATOR 的操作码位置。[ 8]

资源限制

固定链接:资源限制

除了更改许多操作码的语义之外,资源限制也进行了一些更改:

  • 脚本大小限制 10000 字节的最大脚本大小不适用。 它们的大小仅受到区块权重限制的隐式限制。[ 9]
  • 非 push 操作码限制 每个脚本 201 个的最大非 push 操作码限制不适用。[ 10]
  • Sigops 限制 tapscript 中的 sigops 不计入 80000(加权)的区块范围限制。 相反,每个脚本都有一个 sigops 预算。 预算等于 50 + 交易输入见证的总序列化大小(以字节为单位)(包括 CompactSize 前缀)。 使用非空签名执行签名操作码(OP_CHECKSIGOP_CHECKSIGVERIFYOP_CHECKSIGADD)会将预算减少 50。如果这使预算低于零,则脚本会立即失败。 具有未知公钥类型和非空签名的签名操作码也会被计算在内。[ 11][ 12][ 13]。
  • 堆栈 + 替代堆栈元素计数限制 每次执行操作码后,堆栈和替代堆栈中总共 1000 个元素的现有限制仍然存在。 它也扩展到适用于初始堆栈的大小。
  • 堆栈元素大小限制 每个堆栈元素的最大 520 字节的现有限制仍然存在,无论是在初始堆栈中还是在 push 操作码中。

原理

固定链接:原理

  1. ^ OP_SUCCESSx OP_SUCCESSx 是一种升级脚本系统的机制。 在软分叉定义其含义之前使用 OP_SUCCESSx 是不安全的,并且会导致资金损失。 在脚本中包含 OP_SUCCESSx 将无条件地传递它。 它优先于任何脚本执行规则,以避免在指定各种边缘情况时遇到的困难,例如:输入堆栈大于 1000 个元素的脚本中的 OP_SUCCESSx,在太多签名操作码之后的 OP_SUCCESSx,甚至是没有缺少 OP_ENDIF 的条件语句的脚本。 脚本中任何位置存在的 OP_SUCCESSx 都会保证所有这些情况都能通过。 OP_SUCCESSx 类似于早期比特币版本(v0.1 到 v0.3.5,包括 v0.3.5)中的 OP_RETURN。 原始的 OP_RETURN 立即终止脚本执行,并根据终止时顶部堆栈元素返回 pass 或 fail。 这是原始比特币协议中的一个主要设计缺陷,因为它允许通过将 OP_RETURN 放入 scriptSig 中进行无条件的第三方盗窃。 这在当前提案中不是一个问题,因为第三方不可能将 OP_SUCCESSx 注入到验证过程中,因为 OP_SUCCESSx 是脚本的一部分(因此由 taproot 输出提交),这意味着币所有者的同意。 OP_SUCCESSx 可用于各种升级可能性:

    • 可以通过软分叉将 OP_SUCCESSx 转换为功能性操作码。 与只能对堆栈进行只读访问的 OP_NOPx 派生的操作码不同,OP_SUCCESSx 也可以写入堆栈。 对包含 OP_SUCCESSx 的脚本的任何规则更改都可能只会将有效的脚本变为无效的脚本,并且这始终可以通过软分叉实现。
    • 由于 OP_SUCCESSx 优先于初始堆栈和 push 操作码的大小检查,因此需要大于 520 字节的堆栈元素的 OP_SUCCESSx 派生操作码可能会在软分叉中提升限制。
    • OP_SUCCESSx 还可以重新定义现有操作码的行为,以便它们可以与新操作码一起使用。 例如,如果 OP_SUCCESSx 派生的操作码使用 64 位整数,它也可能允许 同一脚本 中的现有算术操作码执行相同的操作。
    • 鉴于 OP_SUCCESSx 甚至会导致可能无法解析的脚本通过,因此它可以用于引入多字节操作码,甚至是全新的脚本语言,并在前面加上特定的 OP_SUCCESSx 操作码。
  2. ^ 为什么禁用 OP_CHECKMULTISIGOP_CHECKMULTISIGVERIFY,而不是将它们转换为 OP_SUCCESSx? 这是一种预防措施,以确保意外地在 Tapscript 中继续使用 OP_CHECKMULTISIG 的人立即注意到问题。 这也避免了脚本反汇编程序需要成为上下文相关的复杂性。
  3. ^ 为什么要使 MINIMALIF 成为共识? 这使得编写从堆栈中获取分支信息的非延展性脚本变得非常容易。
  4. ^ OP_CHECKSIGADD 添加此操作码是为了补偿与批量验证不兼容的 OP_CHECKMULTISIG 类操作码的丢失。 OP_CHECKSIGADD 在功能上等同于 OP_ROT OP_SWAP OP_CHECKSIG OP_ADD,但仅占用 1 个字节。 OP_ADD 的所有与 CScriptNum 相关的行为也适用于 OP_CHECKSIGADD
  5. ^ CHECKMULTISIG 的替代方案 有多种使用 Taproot 和 Tapscript 实现 k-of-n 阈值策略的方法:
    • 使用单个基于OP_CHECKSIGADD的脚本 一个带有见证 0 &lt;signature_1> ... &lt;signature_m>CHECKMULTISIG 脚本 m &lt;pubkey_1> ... &lt;pubkey_n> n CHECKMULTISIG 可以被重写为带有见证 &lt;w_n> ... &lt;w_1> 的脚本 &lt;pubkey_1> CHECKSIG &lt;pubkey_2> CHECKSIGADD ... &lt;pubkey_n> CHECKSIGADD m NUMEQUAL。每个见证元素 w_i 都是对应于 pubkey_i 的签名或者一个空向量。一个类似的 CHECKMULTISIGVERIFY 脚本可以通过将 NUMEQUAL 替换为 NUMEQUALVERIFY 来翻译为 BIP342。这种方法与现有的基于 OP_CHECKMULTISIG 的脚本具有非常相似的特性。
    • 对于每种组合使用一个 k-of- k 脚本 一个 k-of- n 策略可以通过将脚本拆分为 Merkle 树的几个叶子来实现,每个叶子使用 &lt;pubkey_1> CHECKSIGVERIFY ... &lt;pubkey_(n-1)> CHECKSIGVERIFY &lt;pubkey_n> CHECKSIG 实现一个 k-of- k 策略。出于隐私原因,这可能比前一种方法更可取,因为它只暴露了参与的公钥,但只有在 k 值较小的情况下才更具成本效益(对于任何 n 都是 1-of- n,对于 n ≥ 6 都是 2-of- n,对于 n ≥ 9 都是 3-of- n,以此类推)。此外,这里的签名提交到使用的分支,这意味着签名者需要知道哪些其他签名者将参与,或为每个树叶生成签名。
    • 对于每种组合使用一个聚合公钥 不是构建一棵树,其中每个叶子由 k 个公钥组成,而是可以构建一棵树,其中每个叶子包含使用 MuSig 的这些 k 个密钥的单个聚合。这种方法效率更高,但确实需要一个 3 轮交互式签名协议来共同生成(单个)签名。
    • 原生 Schnorr 阈值签名 多重签名策略也可以使用可验证秘密共享的 阈值签名 来实现。这导致输出和输入与单密钥支付无法区分,但代价是在确定发送地址之前需要一个交互式协议(以及相关的备份程序)。
  6. ^ 未知的公钥类型 允许通过软分叉添加新的签名验证规则。软分叉可以添加实际的签名验证,该验证要么通过,要么使脚本失败并立即终止。这样,可以添加新的 SIGHASH 模式,以及 NOINPUT 标记的公钥 和一个公钥常量,该常量被 taproot 内部密钥替换以进行签名验证。
  7. ^ 为什么签名消息要提交到key_version 这是为了定义未知公钥类型的未来扩展,以确保签名不能从一种密钥类型移动到另一种密钥类型。
  8. ^ 为什么签名消息包含上次执行的 OP_CODESEPARATOR 的位置? 这允许继续使用 OP_CODESEPARATOR 来签署脚本的已执行路径。由于 codeseparator_position 是哈希的最后一个输入,因此可以有效地缓存 SHA256 中间状态,以用于单个脚本中的多个 OP_CODESEPARATOR。相比之下,BIP143 对 OP_CODESEPARATOR 的处理是仅从上次执行的 OP_CODESEPARATOR 开始提交到已执行的脚本,这需要不必要的脚本重新哈希。应该注意的是,通过在两个代码分支之间共享第一个公钥来节省脚本中第二个公钥推送的唯一已知 OP_CODESEPARATOR 用例,很可能可以通过将每个分支移动到单独的 taproot 叶子中来更便宜地表达。
  9. ^ 为什么不再需要限制脚本大小? 由于签名哈希中没有直接包含 scriptCode(仅通过可预先计算的 tapleaf 哈希间接包含),因此签名检查花费的 CPU 时间不再与正在执行的脚本的大小成正比。
  10. ^ 为什么不再需要限制操作码的数量? 操作码限制仅在可以防止数据结构在执行期间无限增长的范围内有所帮助(既因为内存使用,又因为时间可能与这些结构的大小成比例增长)。堆栈和备用堆栈的大小已经独立地受到限制。通过对 OP_IFOP_NOTIFOP_ELSEOP_ENDIF 使用 O(1) 逻辑,如 此处 建议和 此处 实现,也可以避免唯一的其他实例。
  11. ^ tapscript sigop 限制 签名操作码限制可防止由于签名操作过多而导致验证速度缓慢的脚本。在 tapscript 中,签名操作码的数量不计入 BIP141 或旧版 sigop 限制。旧的 sigop 限制使区块构建中的交易选择变得不必要地困难,因为它是在权重之外的第二个约束。相反,tapscript 签名操作码的数量受到见证权重的限制。此外,该限制适用于交易输入,并且仅计算实际执行的签名操作码。Tapscript 执行允许每 50 个见证权重单位一个签名操作码,外加一个免费签名操作码。
  12. ^ sigop 限制的参数选择 常规见证不受该限制的影响,因为它们的权重由公钥和(SIGHASH_ALL)签名对组成,每个签名对具有 33 + 65 个权重单位(包括 1 个权重单位的 CompactSize 标签)。如果在脚本中重复使用公钥,情况也是如此,因为签名的权重本身就是 65 或 66 个权重单位。但是,该限制通过要求额外的权重来增加具有重复签名(和公钥)的异常脚本的费用。每个 sigop 因子 50 的权重对应于 BIP141 区块限制的比率:4 mega 权重单位除以 80,000 个 sigop。限制允许的“免费”签名操作码的存在是为了考虑交易输入中非见证部分的权重。
  13. ^ 为什么只有签名操作码计入预算,而不是例如哈希操作码或其他昂贵的操作? 事实证明,对于由最大密度的签名检查操作码组成的脚本的验证,每个见证字节的 CPU 成本(考虑到 50 WU/sigop 限制)已经非常接近于与其他操作码(包括哈希操作码)(考虑到 520 字节的堆栈元素限制)和 OP_ROLL(考虑到 1000 个堆栈元素限制)打包的脚本的成本。也就是说,该结构非常灵活,并且允许添加新的签名操作码,例如 CHECKSIGFROMSTACK,以通过软分叉计入限制。即使将来引入了改变正常脚本成本的新操作码,也没有必要用毫无意义的数据填充见证。相反,可以使用 taproot 附件向见证添加权重,而无需增加实际的见证大小。

部署

Permalink: 部署

此提案的部署方式与 Taproot ( BIP341) 相同。

例子

Permalink: 例子

Taproot ( BIP341) 测试向量还包含 Tapscript 执行的示例。

致谢

Permalink: 致谢

本文档是多次讨论的结果,并包含许多人的贡献。作者要感谢所有提供宝贵反馈和评论的人,包括 结构化评论 的参与者。

  • 原文链接: github.com/ajtowns/bips/...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
ajtowns
ajtowns
江湖只有他的大名,没有他的介绍。