本文件提议与 Taproot 升级一起添加到 Elements 网络的新操作码新的 Tapscript `OP SUCCESS` 操作码允许比通过 `OP NOP` 更简洁地引入新操作码在本文档中,我们提议修改以下 `OP SUCCESS`

该文档提出了在 Elements 网络中 Taproot 升级时添加的新操作码,包括用于流式哈希的流式操作码、用于交易内省的交易内省代码、有符号 64 位算术操作码、转换操作码和新的加密操作符,旨在增强智能合约的功能和效率,同时考虑了资源限制和安全性。

本文档提议将新的操作码添加到 elements 网络以及 taproot 升级中。新的 tapscript OP_SUCCESS 操作码允许比通过 OP_NOP 更干净地引入新的操作码。在本文档中,我们提议修改以下 OP_SUCCESS 以具有额外的语义。我们按顺序使用操作码 OP_SUCCESS196197...,以避免与 bitcoin 可能使用的 OP_SUCESSSx 发生冲突(假设 bitcoin 根据可用性按顺序使用这些操作码)。本文档的初始版本具有额外的操作码(OP_FOR,多字节操作码),但为了简化应用程序的复杂性,已更新为当前版本。

资源限制

Taproot 中的变更(包括标准性策略变更)

Taproot 已经极大地增加了 segwitv0 的资源限制,因此无需额外更改任何内容。特别是,根据 BIP 342

  • 脚本大小限制:10000 字节的最大脚本大小限制不适用。它们的大小仅受区块权重限制的隐式约束。
  • 非 push 操作码限制:每个脚本 201 个的最大非 push 操作码限制不适用。
  • Sigops 限制:tapscript 中的 sigops 不计入全区块 80000(加权)的限制。相反,每个脚本都有一个 sigops 预算。预算等于 50 + 交易输入见证的总序列化大小(以字节为单位)(包括 CompactSize 前缀)。执行带有非空签名的签名操作码(OP_CHECKSIGOP_CHECKSIGVERIFYOP_CHECKSIGADD)会将预算减少 50。如果这使预算低于零,则脚本会立即失败。
  • 堆栈 + 替代堆栈元素计数限制:每次执行操作码后,堆栈和替代堆栈中总共 1000 个元素的现有限制仍然存在。它也扩展到适用于初始堆栈的大小。
  • 堆栈元素大小限制:在堆栈机操作期间,每个堆栈元素的最大 520 字节的现有限制仍然存在。还有一个额外的策略规则将初始 push 大小限制为 80 字节。

    用于附加功能的新操作码:

  1. 用于流式哈希的流式操作码:由于 MAX_SCRIPT_ELEMENT_SIZE(520 字节) 的限制,我们无法对超过 520 字节的消息执行诸如 OP_SHA256 之类的哈希函数。这允许对超过 520 字节的数据进行哈希,同时仍然保持现有的针对资源耗尽攻击的安全性。Russell O'Connor 对此的提议可以在此处的描述中找到。

    1. OP_SUCCESS196 定义为 OP_SHA256INITIALIZE,它弹出一个字节串,并通过将字节串添加到初始 SHA256 上下文中来 push SHA256 上下文。
    2. OP_SUCCESS197 定义为 OP_SHA256UPDATE,它首先弹出一个字节串,然后弹出另一个 SHA256 上下文,并通过将字节串添加到正在哈希的数据流来 push 更新后的上下文。
    3. OP_SUCCESS198 定义为 OP_SHA256FINALIZE,它首先弹出一个字节串,然后弹出另一个 SHA256 上下文,最后在添加字节串并完成填充后 push SHA256 哈希值。
  2. 交易自省代码:通过使用 OP_CHECKSIGFROMSTACKVERIFY,已经可以在 elements 脚本中进行交易自省,但是,当前的解决方案在诸如 covenants 之类的应用程序中非常昂贵。因此,我们没有通过支持自省来添加任何新功能,只是使其更易于使用。与 covenants 一样,警告仍然相同,如果用户从交易中未签名的部分检查数据,则脚本可能会导致意外行为。 对于从 sighash 中未提交的数据部分进行检查的操作码,自省是安全的,因为对见证数据的任何更改都会导致 wtxid 发生更改,并且它将再次重新验证交易。对于 pegin 输入,资产/价值/脚本信息将来自父链。

    • 交易输入自省操作码:
      1. OP_SUCCESS199 定义为 OP_INSPECTINPUTOUTPOINT:弹出一个 CScriptNum 输入索引 idx,并将 outpoint 作为元组 push。首先 push prev_outtxid(32),然后 push vout 的 4 字节,然后 push outpoint_flag(1),如 Elements 的修改版 BIP-341 SigMsg 中所定义。
      2. OP_SUCCESS200 定义为 OP_INSPECTINPUTASSET:弹出一个 CScriptNum 输入索引 idx,并将 nAsset 作为两个元素 push 到堆栈上。首先 push assetID(32),然后 push 前缀(1)。
      3. OP_SUCCESS201 定义为 OP_INSPECTINPUTVALUE:弹出一个 CScriptNum 输入索引 idx,并将 nValue 作为元组 push,值(8 字节 LE, 32) 后跟前缀(1)。
      4. OP_SUCCESS202 定义为 OP_INSPECTINPUTSCRIPTPUBKEY:弹出一个 CScriptNum 输入索引 idx,并根据 scriptPubkey 的类型 push 以下内容:
        • 如果 scriptPubKey 不是原生的隔离见证程序,则在堆栈顶部 push scriptPubKey 的单个 sha256 哈希值。接下来,push 一个 CScriptNum(-1) 以指示非原生的隔离见证 scriptPubKey。
        • 如果 scriptPubKey 是原生的隔离见证程序,则 push 见证程序(2-40) 后跟隔离见证版本(0-1)。
      5. OP_SUCCESS203 定义为 OP_INSPECTINPUTSEQUENCE:弹出一个 CScriptNum 输入索引 idx,并将 nSequence(4) 作为小端数字 push。
      6. OP_SUCCESS204 定义为 OP_INSPECTINPUTISSUANCE:弹出一个 CScriptNum 输入索引 idx,如果资产有发行,则 push 资产发行信息,否则 push 一个空向量。资产发行信息按如下方式 push
        • nInflationKeys 作为元组 push,值(8 字节 LE, 32) 后跟 push 前缀(1)。如果 nInflationKeys 为 null,则 push 一个 8 字节 LE 的 0,后跟一个对于显式前缀(1)的 push。
        • nAmount 作为元组 push,值(8 字节 LE, 32) 后跟 push 前缀(1)。如果 nAmount 为 null,则 push 一个 8 字节 LE 的 0,后跟一个对于显式前缀(1)的 push。
        • Push 32 字节的 assetEntropy
        • Push 32 字节的 assetBlindingNonce
    • OP_SUCCESS205 定义为 OP_PUSHCURRENTINPUTINDEX,它将当前输入索引作为 CScriptNum push。这可以与输入自省操作码结合使用,以检查当前输入。
    • 输出自省操作码:
      1. OP_SUCCESS206 定义为 OP_INSPECTOUTPUTASSET:弹出一个 CScriptNum 输入索引 idx,并将 nAsset 作为元组 push,首先 push assetID(32),然后 push 前缀(1)。
      2. OP_SUCCESS207 定义为 OP_INSPECTOUTPUTVALUE:弹出一个 CScriptNum 输入索引 idx,并将 nValue 作为元组 push,值(8 字节 LE, 32) 后跟前缀。
      3. OP_SUCCESS208 定义为 OP_INSPECTOUTPUTNONCE:弹出一个 CScriptNum 输入索引 idx,并将 nNonce(33) push 到堆栈上。如果 nonce 为 null,则将一个空向量 push 到堆栈上。
      4. OP_SUCCESS209 定义为 OP_INSPECTOUTPUTSCRIPTPUBKEY:弹出一个 CScriptNum 输入索引 idx,并将 scriptPubkey push 到堆栈上。
        • 如果 scriptPubKey 不是原生的隔离见证程序,则在堆栈顶部 push scriptPubKey 的单个 sha256 哈希值。接下来,push 一个 CScriptNum(-1) 以指示非原生的隔离见证 scriptPubKey。
        • 如果 scriptPubKey 是原生的隔离见证程序,则 push 见证程序(2-40),后跟隔离见证版本(0-1)。
    • 交易自省操作码:
      1. OP_SUCCESS210 定义为 OP_INSPECTVERSION:将 nVersion(4) 作为小端 push。
      2. OP_SUCCESS211 定义为 OP_INSPECTLOCKTIME:将 nLockTime(4) 作为小端 push。
      3. OP_SUCCESS212 定义为 OP_INSPECTNUMINPUTS:将输入的数量作为 CScriptNum push
      4. OP_SUCCESS213 定义为 OP_INSPECTNUMOUTPUTS:将输出的数量作为 CScriptNum push
      5. OP_SUCCESS214 定义为 OP_TXWEIGHT:将交易权重 (8) 作为小端 push
  3. 有符号 64 位算术操作码: 当前对 CScriptNum 的操作限制为 4 字节,并且由于最小化规则而难以组合。使用 8 字节有符号操作的固定宽度小端操作有助于对编码为 8 字节小端的金额进行计算。

    • 在处理溢出时,我们显式地将成功位作为堆栈顶部的 CScriptNum 返回,结果是从顶部开始的第二个元素。如果操作溢出,则首先将操作数 push 到堆栈上,然后 push 成功位。[a_second a_top] 溢出,操作后的堆栈状态为 [a_second a_top 0],如果操作未溢出,则堆栈状态为 [res 1]。
    • 如果他们使用 OP_IF\OP_ELSE 编写脚本来处理溢出,或者如果他们期望操作永远不会失败,则这使用户可以灵活地处理。 在定义可能失败的操作码时,我们只定义成功路径,并假设如上所述的溢出行为。
      1. OP_SUCCESS215 定义为 OP_ADD64:弹出第一个数字(8 字节 LE)作为 b,然后弹出另一个 a(8 字节 LE)。将 a + b push 到堆栈上。如果没有溢出,则 push 1 CScriptNum。 溢出行为如上所述。
      2. OP_SUCCESS216 定义为 OP_SUB64:弹出第一个数字(8 字节 LE)作为 b,然后弹出另一个 a(8 字节 LE)。将 a - b push 到堆栈上。如果没有溢出,则 push 1 CScriptNum。 溢出行为如上所述。
      3. OP_SUCCESS217 定义为 OP_MUL64:弹出第一个数字(8 字节 LE)作为 b,然后弹出另一个 a(8 字节 LE)。将 a*b push 到堆栈上。如果没有溢出,则 push 1 CScriptNum。 溢出行为如上所述。
      4. OP_SUCCESS218 定义为 OP_DIV64:弹出第一个数字(8 字节 LE)作为 b,然后弹出另一个 a(8 字节 LE)。首先将余数 a%b(必须为非负数且小于 |b|)push 到堆栈上,然后将商 (a//b) push 到堆栈上。如果 b==0a = -2<sup>63</sup> && b = -1,则视为溢出,如上所述。如果没有溢出,则 push 1 CScriptNum
      5. OP_SUCCESS219 定义为 OP_NEG64:弹出第一个数字(8 字节 LE)作为 a,并将 -a push 到堆栈顶部。如果数字为 -2<sup>63</sup>(int64_min),则视为溢出,否则 push CScriptNum 1 以指示没有溢出。
      6. OP_SUCCESS220 定义为 OP_LESSTHAN64(不会失败!):弹出第一个数字(8 字节 LE)作为 b,然后弹出另一个 a(8 字节 LE)。Push a < b
      7. OP_SUCCESS221 定义为 OP_LESSTHANOREQUAL64(不会失败!):弹出第一个数字(8 字节 LE)作为 b,然后弹出另一个 a(8 字节 LE)。Push a <= b
      8. OP_SUCCESS222 定义为 OP_GREATERTHAN64(不会失败!):弹出第一个数字(8 字节 LE)作为 b,然后弹出另一个 a(8 字节 LE)。Push a > b
      9. OP_SUCCESS223 定义为 OP_GREATERTHANOREQUAL64(不会失败!):弹出第一个数字(8 字节 LE)作为 b,然后弹出另一个 a(8 字节 LE)。Push a >= b
      10. 通过 OP_ANDOP_OROP_INVERTOP_XOR 已经支持二进制运算
  4. 转换操作码:CScriptNum 转换为 8 字节 LE4 字节 LE 的方法。

    1. OP_SUCCESS224 定义为 OP_SCIPTNUMTOLE64:将堆栈弹出为最小的 CSciptNum,push 与该数字对应的 8 字节有符号 LE。
    2. OP_SUCCESS225 定义为 OP_LE64TOSCIPTNUM:将堆栈弹出为 8 字节有符号 LE。转换为 CScriptNum 并 push 它,失败则中止。
    3. OP_SUCCESS226 定义为 OP_LE32TOLE64:将堆栈弹出为 4 字节无符号 LE。Push 相应的 8 字节有符号 LE 数字。不会失败,对于版本、locktime、序列、输入数量、输出数量、权重等操作很有用。
  5. 加密: 为了允许在 elements 上进行更复杂的操作,我们引入了以下新的加密操作符。每个操作码在 sigops 预算中计为 50。

    1. OP_SUCCESS227 定义为 OP_ECMULSCALARVERIFY,它从堆栈中弹出三个元素,如下所述:1) 一个 32 字节的大端、无符号标量 k。2) 压缩的 EC 点 P,以及 3) 压缩的 EC 点 Q。如果 PQ 无效或 k 不是 32 字节并且超出 secp256k1 曲线阶数,则中止。如果 Q != k*P,则中止。
    2. OP_SUCCESS228 定义为 OP_TWEAKVERIFY,具有以下语义:弹出以下三个元素:1) 32 字节的仅 X 内部密钥 P,2) 一个 32 字节的大端、无符号标量 k,以及 3) 33 字节的压缩点 Q。如果 PQ 无效或 k 不是 32 字节并且超出 secp256k1 曲线阶数,则中止。如果 Q != P + k*G,其中 G 是 secp256k1 的生成器,则中止。
  6. 对现有操作码的更改:

    • 添加 OP_CHECKSIGFROMSTACKOP_CHECKSIGFROMSTACKVERIFY 以在见证程序为 v1 时遵循 bip340 的语义。更详细地说,操作码弹出三个元素堆栈 1) 32 字节的 pk 仅 X 公钥 2) 可变长度消息 msg 和 3) 64 字节的 Schnorr 签名 sig。令 res = BIP340_verify(pk, msg, sig),其中 BIP340_verify 被定义为 elements 在这里。如果操作码是 OP_CHECKSIGFROMSTACKVERIFY,如果验证失败,则中止。
    • 如果操作码是 OP_CHECKSIGFROMSTACK,如果提供了一个空签名,则 push 0,如果验证成功,则 push 1。如果提供了未能通过验证的非空签名,则中止。OP_CHECKSIGFROMSTACKOP_CHECKSIGFROMSTACKVERIFY 在 sigops 预算中都计为 50。
    • 如果公钥为空,则中止,但允许非空非 32 字节密钥成功,以便将来扩展标记密钥。

使用 Taproot 操作码的一般提示、建议和怪癖

  • 为了检查当前输入,OP_PUSHCURRENTINPUTINDEX 可以与 OP_INSPECTINPUTXX 结合使用,以获取有关堆栈上所花费输入的信息
  • 输入 nNonce 字段未一致地存储在 elements UTXO 数据库中。因此,它未包含在 sighash 或 wtxid 中,因此无法进行自省。
  • 转换操作码可用于将 ScriptNums/LE32 数字转换为 LE64 以进行操作。
  • 原文链接: github.com/ElementsProje...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

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