撰文:Chakra Research

概述

与以太坊等图灵完备的区块链相比,比特币上的脚本被认为存在很大限制,只能进行基本操作,甚至不支持乘除法。更加重要的是,区块链自身的数据也几乎无法被脚本读取,导致灵活性严重不足,可编程性低。因此,人们长期以来都在试图寻找让比特币脚本(Script)获得内省(Introspection)的方法。

内省指的是允许比特币脚本检查和约束交易数据的能力。这使得脚本可以根据交易的具体细节来控制资金的使用,从而实现更复杂的功能。当前比特币的操作码(Opcode)大多只具备两种功能,将用户提供的数据推送到堆栈上,或者操作堆栈上已有的数据。内省操作码则能够将当前交易的数据(例如时间戳、金额、交易 ID 等)推送到堆栈上,对 UTXO 的支出方式进行更加细粒度的控制。

当前比特币脚本中只有三个主要的操作码支持内省: CHECKLOCKTIMEVERIFY、CHECKSEQUENCEVERIFY、CHECKSIG;CHECKSIG 还有 CHECKSIGVERIFY、CHECKSIGADD、CHECKMULTISIG 和 CHECKMULTISIGVERIFY 等变体。

契约(Covenant)简单来讲,是对代币如何转移的限制,用户通过契约能够指定 UTXO 的分发方式。许多契约都是通过内省操作码实现的,现在 Bitcoin Optech 中内省已经被归类至契约条目下讨论。

比特币现有两个契约,CSV (CheckSequenceVerify) 和 CLTV (CheckLockTimeVerify),都为时间契约,这是许多扩容方案运行的基础,例如闪电网络。从中我们也可以看出,比特币的扩容方案,极大程度上依赖内省与契约。

如何对代币的转移添加限制条件?在加密世界中,我们最常用的方式是承诺(Commitment),往往通过哈希(Hash)来实现。而为了证明我们满足转移的要求,又需要通过签名机制来验证。因此,契约中存在许多对哈希与签名的调整。

下面我们将描述被广泛讨论的契约操作码提案。

CTV (CheckTemplateVerify) BIP-119

CTV (CheckTemplateVerify) 是一项被社区热议的比特币升级,包含在 BIP-119 中。CTV 使输出脚本能够指定支出交易中使用这笔资金的模板,包括交易的 nVersion、 nLockTime、 scriptSig hash、 input count、 sequences hash、 output count、 outputs hash、 input index 等字段,这些模板限制是通过一个哈希承诺来实现的,未来花费时脚本将检查支出交易中指定字段的哈希值与输入脚本中的哈希值匹配,这些模板实际限定了该 UTXO 未来支出交易的时间、方式、金额等细节。

值得注意的是,输入的 TXID 从哈希承诺中被排除了。这种排除是必需的,无论是在 Legacy 或者 Segwit 交易中,在默认的 SIGHASH_ALL 签名类型中,TXID 都要依赖 scriptPubKey 的值生成。因此,包含 TXID 将导致哈希承诺的循环,无法成功构建。

CTV 实现内省的方式是通过新的操作码直接获取交易的指定信息进行哈希,并与堆栈中的承诺进行对比。这种内省方式对链上空间的消耗较少,但是缺乏一定的灵活性。

闪电网络等比特币二层方案的基础是预签名交易。预签名通常指的是提前生成并签名交易,但在一定条件满足之前不将其广播到网络。从本质上讲,CTV 实现了一个更加严格的预签名功能,将预签名的承诺直接发布在了链上,只能按照预先模板进行交易。

CTV 最初被提出是为了缓解比特币的拥堵,也可以称之为拥塞控制。在比特币较为拥堵时,可以通过 CTV 通过单笔交易承诺多个未来交易,而无需在拥堵时广播多个交易,待区块拥堵缓解后,再完成实际交易。拥塞控制在交易所遭到挤兑时可能起到很大的帮助。此外,模板还可用于保险库(Vault)的实现,防范黑客攻击。由于资金的去向被确定,黑客无法将使用 CTV 脚本的 UTXO 发送到自己的地址。

CTV 能为二层网络带来极大的改善。例如,对于闪电网络中 Timeout Trees 与通道工厂 的实现,通过 CTV,单个 UTXO 能够展开成一颗 CTV 树,同时打开多个状态通道,而在链上仅有一笔交易与一个确认。此外,CTV 也为 Ark 协议中的原子交易 ATLC 提供了支持。

APO (SIGHASH_ANYPREVOUT) BIP-118

BIP-118 为 tapscript 提出了一种新型的签名哈希标志,以便于编写更加灵活的支出逻辑,称为 SIGHASH_ANYPREVOUT。APO 与 CTV 在许多方面都比较相似,面对 scriptPubKeys 和 TXID 之间的循环问题,APO 的解决方案是将输入的相关信息排除在外,仅对输出进行签名,使得交易能够动态绑定到不同的 UTXO。

从逻辑上来划分,验签操作 OP_CHECKSIG(及其同类操作码)有三个功能:

  1. 组装花费交易的各个部分

  2. 对其进行哈希处理。

  3. 验证哈希是否已由给定密钥签名。

而签名的具体内容,具备很大的调整空间,组装哪些交易字段进行签字,是由 SIGHASH 标志决定的。根据 BIP 342 签名操作码的定义,SIGHASH 标志分为 SIGHASH_ALL、SIGHASH_NONE、SIGHASH_SINGLE、SIGHASH_ANYONECANPAY 等,其中 SIGHASH_ANYONECANPAY 是控制输入的,其余都是控制输出的。

SIGHASH_ALL 是默认的 SIGHASH 标志,对所有输出进行签名;SIGHASH_NONE 不签署任何输出;SIGHASH_SINGLE 对指定的一个输出进行签名。SIGHASH_ANYONECANPAY 可以和前三个 SIGHASH 标志共同设置,如果设置了 SIGHASH_ANYONECANPAY,则仅对指定的输入签名,否则需要对所有的输入签名。

显然,这些 SIGHASH 标志都无法消除输入的影响,哪怕是 SIGHASH_ANYONECANPAY,都需要对一个输入完成承诺。

因此,BIP 118 提出 SIGHASH_ANYPREVOUT。APO 签名无需承诺被花费的输入 UTXO(称为 PREVOUT),而只需对输出签字,为比特币的控制提供了更高的灵活性。通过预先构建交易并构建对应的单次使用签名与公钥,发送到该公钥地址的资产必须通过预先构建的交易进行花费,从而实现契约。APO 的灵活性还可以用于交易修复,如果一笔交易因费用低在链上卡住,可以轻松创建另一笔交易提高费用,而无需新的签名。此外,对于多重签名钱包而言,签名不依赖花费的输入使得操作更加简便。

由于消除了 scriptPubKeys 和输入 TXID 之间的循环。APO 可以通过在见证(Witness)中添加输出数据来实现内省 ,当然这仍然需要额外的见证数据空间消耗。

对于闪电网络与保险库等链下协议,APO 减少了需要保存的中间状态,大大降低了存储需求与复杂性。APO 最直接的用例是 Eltoo,简化了通道工厂,构建了轻量化且便宜的瞭望塔,且允许单方面退出,而不遗留错误状态,从各方面提升了闪电网络的性能。APO 也可以用于模拟 CTV 的功能,不过需要个人存储签名及预签名交易,成本更高,效率不如 CTV。

APO 受到的质疑主要集中在其需要全新的密钥版本,而无法通过单纯向后兼容实现。此外,新的签名哈希类型可能会带来潜在的双花风险。经过社区的广泛讨论后,APO 在原先签名基础上要求加入普通签名,安全问题得到缓解,也因此获得 BIP-118 编号。

OP_VAULT BIP-345

BIP-345 提议新增 OP_VAULT 和 OP_VAULT_RECOVER 两个操作码,与 CTV 结合,实现一种专用的契约,允许用户对指定的代币的花费强制一个延迟期,在延迟期中,可以通过恢复路径将之前的花费「撤销」。

用户可以通过创建一个特定的 Taproot 地址来构造一个保险库,MAST 中需要包含至少两个脚本,一个 OP_VAULT 的脚本以促进预期的提款过程,另一个 OP_VAULT_RECOVER 脚本确保在提款完成前的任何时候都可以恢复硬币。

OP_VAULT 如何实现可中断的时间锁定提款?简单而言,OP_VAULT 操作码完成这样一件事:将所花费的 OP_VAULT 脚本替换为指定的脚本,实际上完成了 MAST 的单个叶子节点更新,其余叶子节点不变。与 TLUV 的设计类似,只不过 OP_VAULT 不支持内部密钥的更新。

在脚本更新的过程中引入模板,就能够实现限制支付的效果。其中,时间锁参数由 OP_VAULT 指定,CTV 操作码带来的模板则限制了通过该脚本路径花费的输出集合。

BIP-345 是为保险库而生的,借助 OP_VAULT 和 OP_VAULT_RECOVER,用户可以拥有一个安全的托管方式,将一个高度安全的密钥(纸钱包、分布式多签)作为恢复路径,其余的日常支付配置一定的支出延迟。用户的设备持续监控保险库的的支出情况,如果发生意外的转移,用户能够进行恢复。

BIP-345 实现保险库的过程中需要考虑费用问题,尤其是恢复交易,可行的解决方案包括 CPFP、临时锚点以及 SIGHASH_GROUP 等新的签名哈希标志。

TLUV (TapleafUpdateVerify)

TLUV 方案围绕着 Taproot 构建,旨在解决共享 UTXO 的高效退出问题。其指导思想是,当一个 Taproot 输出被花费时,我们可以通过根据 TLUV 脚本所描述的更新步骤,利用 Taproot 地址的内部结构与密码学变换,对内部密钥与 MAST 进行部分更新,进而实现契约功能。

TLUV 方案的想法就是通过一个新的操作码 TAPLEAF_UPDATE_VERIFY,可以通过执行以下一项或多项操作,来创建一个基于当前消费输入的新 Taproot 地址:

  • 更新内部公钥

  • 裁剪默克尔路径

  • 移除当前正在执行的叶子节点

  • 在默克尔路径的末端添加一个新叶子节点

具体而言,TLUV 接收三个输入:

  • 一个指定如何更新内部公钥

  • 一个为默克尔路径指定一个新叶子节点

  • 一个指定是否移除当前叶子节点和 / 或移除多少默克尔路径叶子节点

TLUV 操作码会计算出更新后的 scriptPubKey,并验证与当前输入对应的输出是否消费到该 scriptPubKey。

TLUV 的启发场景就是联合资金池(CoinPool)。今天可以仅使用预签名的交易创建联合池,但如果希望实现无许可的退出,则需要创建指数级增长的签名数量,而 TLUV 能够实现无需任何预签名的无许可退出。举例而言,一群伙伴利用 Taproot 构建了一个共享的 UTXO,汇集了彼此的资金。他们之间可以使用 Taproot 密钥在内部移动资金,也可以共同签名对外发起支付。个人可以随时退出该共享资金池,删除掉自己的支付路径,其余人仍然可以通过原先的路径完成支付,同时个人的退出不会暴露内部其他人的额外信息。相比于非池化的交易,这种方式更加高效与隐私。

TLUV 操作码通过对原 MAST 的更新实现了部分花费限制,然而没有实现输出金额的内省。因此还需要包括一个新的操作码 IN_OUT_AMOUNT,它将两条数据推送到堆栈:此输入的 UTXO 的金额和对应输出的金额,然后期望使用 TLUV 的人使用数学运算符来验证资金是否被适当地保留在更新的 scriptPubKey 中。

对于输出金额的内省又增加了一个复杂性,因为比特币的金额用聪来表示需要多达 51 位,但脚本只允许进行 32 位的数学运算,需要通过重新定义操作码行为来升级脚本中的运算符,或者使用 SIGHASH_GROUP 来替换 IN_OUT_AMOUNT。

TLUV 有望为去中心化的 Layer 2 的资金池提供解决方案,当然,在 Taproot 公钥的 tweak 方面,可靠性还有待确认。

MATT

MATT (Merkleize All The Things) 试图实现三个目标:Merkle 化状态、Merkle 化脚本与 Merkle 化执行,并进而实现通用的智能合约。

Merkle 化状态:构建一个 Merkle Trie,其中每个叶子节点都是状态的哈希值,Merkle Root 就代表了整个合约的状态。

Merkle 化脚本:即 Tapscript 构成的 MAST,每个叶子节点都是一个可能的状态转换路径。

Merkle 化执行:通过加密承诺与欺诈挑战机制实现 Merkle 化执行,对于任意计算函数,参与方可以链下计算后发布承诺,f(x)=y,其他参与者发现计算结果错误 f(x)=z,则可以进行挑战,通过二分法进行仲裁,与 Optimistic Rollup 原理相同。

Merkle 化执行 欺诈挑战

为了实现 MATT,比特币编程脚本需要具备以下功能性:

  1. 强制一个输出拥有特定的脚本(以及它们的金额)

  2. 将一段数据附加到一个输出

  3. 读取当前输入(或其他输入)的数据

第二点非常重要,动态的数据意味着状态可以通过花费者提供的输入数据来计算,因为这提供了状态机的模拟,并且能够决定下一个状态与附加的数据。MATT 方案通过提出 OP_CHECKCONTRACTVERIFY(OP_CCV)操作码实现,是对原先提出的 OP_CHECKOUTPUTCONTRACTVERIFY 与 OP_CHECKINPUTCONTRACTVERIFY 的操作码合并,通过一个额外的 flags 参数来指定操作的对象。

对输出金额的控制:最直接的方式是通过直接的内省,然而输出金额是一个 64 位数,需要 64 位的运算,这在比特币脚本的实现有很高的复杂性。CCV 采用延迟检查方式,和 OP_VAULT 类似,所有向同一输出具有 CCV 的输入,其输入金额被求和,作为该输出金额的下限。延迟是因为该检查是在交易过程中而不是在输入的脚本评估期间进行的。

鉴于欺诈证明的通用性,MATT 契约的某些变体应该能实现所有类型的智能合约或二层构建,尽管额外的要求(例如资本锁定和挑战期延迟)需要准确评估;需要进一步研究来评估哪些应用是可接受的交易。例如,用加密承诺与欺诈挑战机制模拟 OP_ZK_VERIFY 函数,实现比特币上的去信任 Rollup。

实际上,事情已经在发生了。Johan Torås Halseth 利用 MATT 软分叉提案中的 OP_CHECKCONTRACTVERIFY 操作码实现了 elftrace,能够将所有支持 RISC-V 编译的程序在比特币链上完成验证,允许合约协议中的一方在通过合约领取资金,实现了比特币原生验证的桥接。

CSFS (OP_CHECKSIGFROMSTACK)

从 APO 操作码的介绍中,我们已经了解到,OP_CHECKSIG(及其相关操作)负责组装交易、哈希处理、验证签名。但是其所验证的消息,是由使用该操作码的交易序列化得到的,不允许指定其他消息。简单来说,OP_CHECKSIG(及其相关操作)起到的是通过签名机制,验证作为交易输入的 UTXO 是否被授权给签名的持有者花费,从而保护比特币的安全性。

CSFS,顾名思义,从堆栈(Stack)中检查签名。CSFS 操作码从堆栈接收三个参数:一个签名、一个消息以及一个公钥,并验证签名的有效性,这意味着人们能够通过见证数据向堆栈传递任意消息,并且通过 CSFS 进行验证,这使得比特币上的一些创新成为可能。

CSFS 灵活的特点使得其能够实现 支付签名、权限委托、预言机合约、双花保护债券等多种机制,更重要的是能够实现交易的内省。使用 CSFS 进行交易内省的原理非常简单,如果通过见证向堆栈上推送 OP_CHECKSIG 所使用的交易内容,对该内容使用同一个公钥和签名对用 CSFS 和 OP_CHECKSIG 都进行一次验签,如果都顺利通过,那么传递给 CSFS 的任意消息的内容与隐式用于 OP_CHECKSIG 的序列化花费交易(及其他数据)相同,我们在堆栈上就获得了经过验证的交易数据,可以运用其他操作码对花费交易施加限制。

CSFS 通常与 OP_CAT 共同出现,因为通过 OP_CAT 能够连接交易的不同字段以完成序列化,能够更精确地选取需要内省的交易字段。没有 OP_CAT,脚本无法从可单独检查的数据重新计算哈希,所以它真正能做的就是检查哈希是否与特定值相等,这意味着币只能通过单一特定交易来花费。

CSFS 能实现 CLTV、CSV、CTV、APO 等操作码,是一个通用的内省操作码,因此也能帮助比特币 Layer 2 的扩容方案。不足之处在于需要在堆栈上添加一份完整的签名交易副本,这可能会显著增加想要用 CSFS 进行内省的交易的大小。相比之下,单一目的内省操作码如 CLTV 和 CSV 使用的开销最小,但添加每个新的特殊内省操作码都需要共识变更。

TXHASH (OP_TXHASH)

OP_TXHASH 是一个非常直接的内省操作码,让操作者选择某个字段的哈希推送至堆栈。具体而言,OP_TXHASH 将从堆栈中弹出一个 txhash 标志,并根据该标志计算一个(标记的)txhash,然后将结果哈希推送到堆栈上。

由于 TXHASH 与 CTV 的相似性,社区内产生了大量关于二者的探讨。

TXHASH 可以被视为 CTV 的通用升级,提供了一种更高级的交易模板,允许用户明确支出交易的一部分,这解决了关于交易费用的很多问题。对比其他的契约操作码,TXHASH 无需在见证中提供所需数据的副本,进一步降低了对存储的需要;与 CTV 不同,TXHASH 不是 NOP 兼容的,只能在 tapscript 中实现;TXHASH 与 CSFS 的组合可以被当作 CTV 与 APO 的替代方案。

从构建契约的方式来看,TXHASH 更容易实现「加法契约」,将你希望固定的交易数据的所有部分推到堆栈上,将它们全部一起哈希,并验证结果哈希与固定值匹配;CTV 更容易实现「减法契约」,将你希望保持自由的交易数据的所有部分推到堆栈上。然后使用滚动 OP_SHA256 从固定的中间状态开始,该中间状态承诺了交易哈希数据的前缀。自由部分被哈希到该中间状态中。

TXHASH 的规范中定义的 TxFieldSelector 字段有望拓展到其他操作码中,例如 OP_TX。

TXHASH 相关的 BIP 当前在 Github 上处于 Draft 状态,未确定编号。

OP_CAT

OP_CAT 是一个颇具神秘色彩的操作码,曾被中本聪因安全问题废弃,又在近期引起大量比特币核心开发者讨论,甚至引发了互联网上的 Meme 文化,最终 OP_CAT 获批 BIP-347,被多人称为是近期最有可能通过的 BIP 提案。

实际上,OP_CAT 的行为非常简单,将堆栈上的两个元素拼接为一个。如何实现契约功能?

实际上,拼接两个元素的功能对应了一个强大的密码学数据结构 Merkle Trie。Merkle Trie 的构建过程,只需要拼接与哈希两种操作,而比特币脚本中,哈希函数是可用的。因此,有了 OP_CAT 之后,我们在理论上就能够在比特币脚本验证 Merkle Proof,这是区块链中最常用的轻量化验证方式。

正如之前所提到的,CSFS 借助 OP_CAT,能够实现通用的契约方案。实际上,无需 CSFS,利用 Schnorr 签名的结构,OP_CAT 自身就能实现对交易的内省。

在 Schnorr 签名中,需要签名的消息,是由以下字段构成的:

这些字段包含了交易的主要元素,通过将其放置在 scriptPubKey 或者见证中,利用 OP_CAT 与 OP_SHA256,我们可以构建一个 Schnorr 签名,并利用 OP_CHECKSIG 检验。如果检验通过,则堆栈中保留了经过验证的交易数据,实现了交易的内省,能够提取并「检查」交易的各个部分,例如其输入、输出、目标地址或所涉及的比特币金额。

具体的密码学原理,可以参照 Andrew Poelstra 所发表的 CAT and Schnorr Tricks 文章。

总结而言,OP_CAT 的灵活性使得其几乎可以模拟任何契约操作码,并且大量契约操作码要依赖于 OP_CAT 的功能,这使得其在合并列表上的位置显著提前。从理论上讲,仅依靠 OP_CAT 与现有的比特币操作码,我们有望构建一个信任最小化的 BTC ZK Rollup,Starknet、Chakra 以及其他生态伙伴都在积极推进这件事的发生。

Conclusion

随着我们探索扩展比特币和增强其可编程性的多种策略,显而易见的是,前进的道路涉及本地改进、链下计算和复杂的脚本功能的结合。

没有灵活的基础层,就无法构建更灵活的第二层。

链下计算扩容是未来,但比特币上的可编程性必须有所突破,才能更好地支持扩容,成为真正的世界货币。

但是比特币的计算与以太坊的计算实际有着本质区别,比特币只支持「验证」作为一种计算形式,不能进行通用计算,而以太坊本质是计算性的,验证是计算的副产品。从一点就可以看出这种差异:以太坊会为执行失败的交易收取以 Gas 为形式的手续费,而比特币不会。

契约实现了一种基于验证而不是计算的智能合约形式。关于契约,除了极少数中本聪原教旨主义者,似乎所有人都认为契约是改进比特币的一个好选择。然而,社区对于该通过哪个方案实现契约始终争论不休。

APO、OP_VAULT、TLUV 更偏向直接的应用,选择它们能够以更便宜、高效的方式实现特定应用。闪电网络的爱好者会偏爱 APO,因为能够实现 LN-Symmetry;想要实现保险库,那么最好使用 OP_VAULT;构建 CoinPool,用 TLUV 更加具备隐私和效率。OP_CAT、TXHASH 更加通用,存在安全漏洞的可能性也更小,通过与其他操作码的组合能够实现更多的用例,当然也许需要以脚本的复杂性作为代价。CTV 与 CSFS 对区块链的处理过程做了调整,CTV 实现了延迟输出,CSFS 实现了延迟签名。MATT 相对特立独行,利用乐观执行与欺诈证明的思想,借助 Merkle Trie 的结构实现了通用的智能合约,不过仍然需要新操作码带来内省功能。

我们看到,比特币社区已经在激烈地讨论通过软分叉让比特币获得契约的可能性。Starknet 正式宣布进入比特币生态,计划在 OP_CAT 合并后六个月内实现比特币网络上的结算。Chakra 将持续关注比特币生态的最新动向,推动 OP_CAT 软分叉的合并,并利用内省与契约带来的可编程性构建一个更加安全、高效的比特币结算层。

感谢 Jeffrey、Ben、Mutourend、Lynndell 对本文的审阅与建议