Study & contribute to bitcoin and lightning open source
Interactive AI chat to learn about bitcoin technology and its history
Technical bitcoin search engine
Daily summary of key bitcoin tech development discussions and updates
Engaging bitcoin dev intro for coders using technical texts and code challenges
Review technical bitcoin transcripts and earn sats
根据 “苏格拉底村” 规则,(除了演讲者之外)所有与会者都使用了假名;音频也不会公开,以保护提问者的匿名。
Shaun Apps(SA):我先做个开场吧。Andrew 和 Jeremy 都会来。然后我们就以问答的形式开展,最后再回答观众的提问。这就是本场演讲的形式。
SA:言归正传,限制条款(covenant)。这个术语有宗教和法律方面的历史语境。大多数人理解的可能是物权法范畴内的概念,比如使用限制条款来约束土地的用途。但是,你们有多少人知道它在比特币领域内的意思?好的,看来很多人都听过。所以,基本上,限制条款给予了比特币脚本内省的能力,例如你可以通过预先定义的约束来限制一个输出的花费方式。我不太清楚限制条款的概念可以追溯到多久远以前。但是这一篇影响力很大的 BitcoinTalk 论坛帖子,作者是 Greg Maxwell,就介绍了限制条款的概念,并提出了一些假想的案例。举个例子,一种 Smashcoin 限制条款,在花费由此种条款锁定的资金时,必须提供攻击另一种密码货币的证据;甚至是一种 adultcoin 限制条款,必须提供某人的生日才能花费,它会限制接收者的年龄必须大于 18 岁。当然,这些都只是假设的、博君一笑的例子,这篇帖子也只是一个玩笑。但是,今天我们能够看到,限制条款有一些非常激动人心的用途。有请 Andrew Poelstra 和 Jeremy Rubin。Andrew 准备从 CHECKSIGFROMSTACK(也叫 “CSFS”)的视角出发,理解限制条款。Jeremy 的演讲则讨论 CHECKTEMPLATEVERIFY(也叫 “CTV”)。其实,有不止一种办法可以在比特币中实现限制条款。不同的想法、不同的提议,都有自己的取舍也应用场景。所以,我们来好好探究一番。Andrew,你想先来吗?
Andrew Poelstra(AP):当然。如 Shaun 所说,某种程度上我代表着限制条款的 CHECKSIGFROMSTACK 流派。这种流派似乎代表着一种更普遍的视角。所以,为了帮助大家建立限制条款的概念 —— 我认为,这个概念确实肇始于那个 BitcoinTalk 帖子 …… 我不知道有什么讨论比这个更早 ……
Jeremy Rubin(JR):那也是我所知最早的。
AP:有两种类型的限制条款,我把它们叫做 “递归型限制条款” 和 “非递归型限制条款”。我想这两个术语可能也源自那篇文章。“限制条款” 是且只是一种限制资金可以花到哪里去的办法。使用比特币脚本,你可以决定在什么条件下一笔资金可以移动,然而,一旦条件满足,资金就可以去任何地方。而限制条款则更进一步,允许你限制它的去向,实现上面说的种种傻事。
非递归的限制条款和递归的限制条款有一个区别。在非递归的限制条款中,你可以限制一笔资金的去向。你可以把它引到另一个限制条款中,从而进一步约束其去向,等等。但是,你必须规划出这笔资金的全部未来,你可以让它经历很多个限制条款,但你必须在一开始就把所有的限制条款写出来。递归型限制条款则不同,你可以让资金回到一个本质上与其来源相同的限制条款中;这就创造了一种有限的状态机,资金可以在不同的状态中移动。要么资金可以只待在一个地方,只是改变一些常数;要么它们可以移动到不同的状态,甚至它们可以返回原来的状态。凭借它,你可以创建一种让资金无法逃离的限制条款。甚至我的用词,“无法逃离”,也反映出其内在的危险性,同时也反映了许多年来人们对启用递归限制条款的恐惧。
AP:这些年出现了许多的限制条款提议。其中一个是 Jeremy Rubin 的 OP_CTV,曾用名 “OP_SECURETHEBAG”。在它的整个生命中,它有过多个名称,也经历了几次迭代。OP_CTV 被有意设计成规避递归型限制条款,以避免所有这些看似是玩笑、某个层面上又不是玩笑的东西。不过,另一种非常著名的限制条款提议,是非常通用的,它基于两种新的操作码,CHECKSIGFROMSTACK 和 CAT。 还有一种更为高效的变种,它增设的操作码可以直接检查正在花费这笔资金的交易。也就是说你可以直接把花费资金的交易本身拉取到堆栈中,然后执行你的脚本、直接地约束它们。一定程度上,你是在直接检查花费资金的交易、为之施加任意的约束。尤其是,如果你限制了交易的输出,那你就实现了一种递归型限制条款,可以永久困住这笔资金。这两种方法之间存在取舍。如果你尝试做一些事情来避免递归型限制条款 ……
比特币的限制条款有一段挣扎的历史,令人惊讶的是比特币中实际上不存在任何限制条款。脚本的本性意味着它必须检查交易。而比特币脚本的 CHECKSIG(检查签名)操作码让你可以检查交易的一个签名。脚本必须能够获得签名数据,这样 CHECKSIG 操作码才能访问签名。但从数学上来说,数字签名工作的方式内在地跟交易数据本身有关。使用一些聪明的数学技巧,你可以迫使一笔签名 “往回走”、获得交易的一个哈希值并推入堆栈,然后开始分析它。事实上,你可以使用比特币的 CHECKSIG 操作来表示 “这笔资金只有在你提供的签名全都是 0 的时候才能花费”;这是一个有效的签名,其中有一些元数据,但本质上就是特定位置有一些 1 和 2 的全部为 0 的数据;脚本中不包含公钥。也就是说,正常情况下,你的脚本的意思是:“我需要这个公钥对花费它的交易的一个签名”;而在这种情况下,你是在说 “我需要拥有某一个公钥,其对花费这笔资金的交易的签名拥有这样的形式”。这样编写脚本之后,为了满足它的条件,你就必须提供一个能够让这个签名有效的公钥。而这个公钥,事实证明,在代数上是一笔交易的哈希值的奇怪混合。因此,你可以在脚本中约束花费这笔资金的交易的哈希值是什么样的,也就是你得到了一种限制条款。这种脚本无法工作的理由是,比特币中的所有 sighash 标签,除了其中一种,全部都覆盖了用来签名的公钥,所以你尝试这样做会遇到循环。但是,如果我们拥有 SIGHASH_ANYPREVOUT,一种热门的提议,那么你就可以像上面说的,靠 CHECKSIG 获得一种限制条款。
这个故事的重点不是提出一种严肃的限制条款实现,而是证明要避免实现递归型限制条款是非常困难的。所以我的立场是 —— 这不是一场辩论哈,但我的立场是,我们应该拥有一种非常通用、专门设计的方法来实现递归型限制条款,而不是尝试窄化一些东西。因为首先,如果你实现了一种窄化的限制条款提议,你就损失了一些灵活性。然后,该要经历的共识变更流程,你也一样要经历,但你只得到了更少的收益。不过,也有可能你最后会得到递归型限制条款。我打赌你会,我认为这是非常难以避免的。这就是我的部分。
JR:我认为,可能除了结论,AP 讲的大部分东西我都同意。完美的电视节目少不了戏剧,但现实生活没有那么多戏剧。我认为,Andrew 和我可能并不是比特币社区的绝对中心,但我们都认为限制条款对比特币是好事。我的观点之所以不同、我之所以开发 CHECKTEMPLATEVERIFY,都是出于工程师视角的实用主义(后面我会花点时间解释一下 CTV 的行为)。得到这种完美的元件 —— 给我们安全又有表达力的递归型限制条款 —— 是非常困难的。社区里也有人反对这种东西。所以,在 CHECKTEMPLATEVERIFY 上,我尝试的事情是不断寻找更狭窄的协议设计,让最反对限制条款的人也能接受。在一定程度上,这就是 CHECKTEMPLATEVERIFY 的目标。我们可以用来解决一些重要问题的最小方案是什么样的?我认为这就是人们会混淆的东西,我这样做不是因为我认为我们只应该实现这样的最小方案,而是因为我要的东西是比特币渐进式升级的一部分。与其一次搞得天翻地覆,也许我们应该用简单的元件引入可以实现许多有趣事物的能力。我们可以依据最小方案,先培育跟限制条款开发有关的工具链和基础设施。随着时间推移,当我们知晓了人们希望用限制条款来实现的更多应用之后,可以再实现一些充分通用的、对广泛的其他用途都有用的东西,而不至于引入漏洞,也不会为了设计对开发者基础设施、工具链、用户接口的限制条款框架性而一次性消化大量工程任务。
所以,尤其是 CHECKTEMPLATEVERIFY,它实际上非常接近于 Andrew 所谓的 “你可以使用公钥来做的复杂的事”。实际上它还要更简单一些,它做的事情仅仅让你限定你要用哪一笔交易来花费这笔资金。比如,我准备把钱打到这个地址,然后让这个地址可以给我的 100 个朋友支付,这就是一个你可以用 CHECKTEMPLATEVERIFY 来实现的东西。你在这个地址中承诺的,就是你接下来准备使用的交易。那么,什么使用你会想要我说的这种功能呢?这就是 CHECKTEMPLATEVERIFY 的主要用场之一,拥堵控制。
在开发过程中,我思考这些问题的方式是,我希望找出具体的一个用途,是 CHECKTEMPLATEVERIFY 绝对可以克服的,同时为人们需要这种(或这一小组)功能给出可靠的论述,然后设计出可以安全地满足这种需求的东西。然后再回到原点,看看还有没有别的东西可以做。但我希望确定的是 CHECKTEMPLATEVERIFY 作为一种解决方案,在产品市场上可以满足最终用户的需要。我给出的这个例子,是非常巧妙的,它将分割交易的任务从支付端转移到了接收端(from a spending side to expanding side)。举个例子,如果现在交易池很慢、网络手续费很贵,如果你想获得区块确认,你可以让你的交易只有一个输出,但你的 100 个朋友都可以完全验证,他们将随着限制条款的拆解,在未来收到支付。这对(比如说)交易所来说是非常棒的,而且这种思路可以通过利用 CHECKTEMPLATEVERIFY 可以实现的其它东西、扩展成复杂得多的形式,比如免交互地创建闪电通道。我认为,总的来说,当你开始深入思考你可以用这种非常简单的限制条款来做什么,甚至有一种我为 Sapio 编写的编译器。它可以制作基于 CHECKTEMPLATEVERIFY 的智能合约,可以用来制作所有基于限制条款的奇妙事物。这个编译器自身是通用的,对未来可能定义的限制条款也兼容。如果有人走过来说 “我发明了一种新的限制条款”,我们就有机会开发可以在未来、我们扩展出更多类型的限制条款时使用的工具链。
这就是我的观点。从现在来看,我不知道我们要多久才能实现充分通用的限制条款,Andrew 可能会说你只需要 CHECKSIGFROMSTACK,但这些限制条款的体积都非常非常大,在链上展开它们是非常昂贵的。CHECKTEMPLATEVERIFY,因为其简洁性,以及你要知道自己想把资金发送到哪里的提前编译的概念,其链上足迹是非常少的。就链上效率而言,稍微复杂一点点的限制条款都很难打败基于 CHECKTEMPLATEVERIFY 的限制条款。就像有一条河需要限制条款造的桥才能渡过,而 CHECKTEMPLATEVERIFY 做的事情就是砍倒一棵树,我把它扔到河上并表示 “现在你可以走过去了”。我感觉许多开发者会说 “不,我们想造一座华丽的桥,花点时间吧”。而我表示,“靠这个我们今天就可以过河了,也许日后我们可以用它造出更高效的东西,甚至于在未来我们可以双管齐下,等等。”
SA:提醒一下,在我们提问的时候,观众也可以自由回答这些问题。那么,你们能不能多谈谈,在设计限制条款的时候,哪些因素会导致搬起石头砸自己的脚?哪些部分需要最仔细的考虑、哪些地方可能会出错?
JR:我认为,基本上,只要你搞错一笔交易的任何一个细节,你就完蛋了。比如,在 CHECKTEMPLATEVERIFY 中,有一种情形是你发送了错误数量的资金到一个限制条款中(其实所有类型的限制条款在这种情况下都会出错);你制作了一个限制条款,预期它会使用 1 BTC,但你只发送了 1 BTC减 1 聪 到这个限制条款中,那么整个限制条款就没法按你预期的方式执行了。这个领域对任何开发限制条款的人都是一个大挑战。另一个大问题是 —— 我自己已经有某种程度的解决方案了,但在社区中还不是那么知名 —— 支付手续费。假定你有一个限制条款,你限制了资金只能以某一种规定得很细致的方式花费,那么你怎么支付手续费呢?如果你必须支付比自己预期更多的手续费,那么你可能会过量消耗限制条款的资金,导致它无法正确执行。所以,支付手续费也是一个非常棘手的问题。我的通用解决方案是,在这里讲可能有点离题哈,我们应该建立一种手续费支付方法,可以完全独立于交易的执行。不论你使用的是限制条款还是别的智能合约代码(比如闪电通道),直接执行你想要的交易图,另外考虑手续费支付。
AP:关于手续费问题,我绝对我们不必在细节上过度纠缠。但我认为,应该有一种应用 “子为父偿(CPFP)” 的办法,跟当前建构交易的方法没有很大差别。
但是,回到最初的问题,交易的哪一部分最危险、哪些部分相对没那么危险?不论你限制你的资金花费到哪个输出,那个输出就是最危险的部分。就像 Jeremy 说的,你可以让你的输出变成没法执行的东西。 输出的具体数值,在你建构限制条款所用的交易时可能是无法预计的。如果你的限制条款要求你构建一个太大、违反共识限制的脚本签名,这个输出也将变得无法花费。或者,你的执行步骤可能超出脚本签名的余量(balance)。也许你的限制条款 会要求你把两个数相加得出一个大于 32 位的数,等等。在一些奇怪的状态下,你的限制条款可能会让你受到惊吓:这些钱变成无法花费的了。即使你可以看到结局,限制条款也会强迫你往那个方向走。输出本身就是最危险的东西。
限制条款还可以约束交易的其余部分。比如你可以约束时间锁必须是某一个区高度。就算你搞砸了,你也必须等待网络达到这个区块高度。我猜,如果你搞砸了,你可能会让资金锁定几年乃至几十年 —— 可能没有什么安慰作用,但至少从原理上说它是可以恢复的。或者,如果你要求交易的版本号是一些奇怪的、非标准的数值,虽然这样的交易是可以被区块链接受的,但你就必须直接找到矿工,因为这样的交易是无法通过网络来传播的(尽管可以得到区块链确认)。诸如此类的东西还有你的输入的 sequence 字段,这个字段会约束交易的 “手续费替换行为”;等等。但实际上这些都是输出。当我们讨论限制条款时,我们真正在讨论的是限制输出,虽然在我看来,这是一种更通用的限制交易的形式的做法。最终来说,无论你是要做出酷炫的应用,还是危险的影响,都必须限制输出。
JR:我补充一点我的解释:输出也包括了的交易的输入。如果你的一笔交易有两个输入,你可以让两者都约束被创建的一个输出,这会得到一些让人惊喜的结果。所以,你可以设置两个输入,两个输入都表示需要创建一个价值 1 BTC 的输出,然后一个输出就能同时满足这两个输入。因此你在两个不同的限制条款中都丢失了一半的价值。所以,两个限制条款之间可能存在某些令人惊喜的互动,这也是为什么至少在目前,我对 CHECKTEMPLATEVERIFY 的做法是尽可能缩减它的范围,这样我们就能做出一些我们知道它一定程度上可以工作的东西。更令人意外的是,随着时间的推移、我们得到全部的复杂性之后,会更难得出正确的东西。
SA:Jeremy,你提到限制条款的用途之一是拥堵控制。你们能不能从它们会如何影响 Layer 2 解决方案(比如闪电网络)的角度讲讲这些提议?我们能从这些提议中得到什么?它们能不能优化现有的东西、如何优化?
JR:Layer 2 加上限制条款会非常精彩。当前,Layer2,额,好吧,Layer 2,管它呢。现在开发 Layer 2 会遇到很多问题。不要误会,我认为 Layer 2 是许许多多问题的良方,包括闪电网络,侧链,等等。我希望将比特币和区块链想象成 CPU,而 Layer 2 就是操作系统。那么,如果你给基础层增加了新功能,你的操作系统自然能利用这些新的工具、取得很棒的成果。
我喜欢的一个跟闪电网络有关例子是创建通道。当前这有点困难,因为双方都必须在线。如果你想用存放在 Coinbase 交易所中的钱开启一条通道,那么你必须先从 Coinbase 交易所取出资金,然后跟某人开设一条通道。如果我们能做到直接说 “Coinbase,请为我开设一条通道”、然后就离线、等待 Coinbase 给你发送一条通道呢?这里面无需对 Coinbase 的信任假设。这会给吸引流动性带来非常大的影响。
还有一种情况,是拥堵控制跟 Layer 2 有密切关系的地方。假设你想一次性创建 10000 条通道。现在,你必须消耗大量的区块空间才能做到。如果有 CHECKTEMPLATEVERIFY 或其它限制条款,你可以推迟产生链上负载,在你想要关闭通道的时候才懒洋洋地释放。如果你开启了一条通道,而且无意关闭它,你可能在一年以后才实际关闭它。这对于吸引流量有很大的影响。
还有关于退出的。在某一些侧链中,你可能要同时给大量用户支付,我知道 Stacks 在共识协议中安排了一些措施,以应对要给一定数量的用户支付、又无法让交易得到确认的情形。但结果是产生了一种共识错误。有了 CHECKTEMPLATEVERIFY,你的 Layer-2 侧链就可以无视交易池的天气。你总是可以为你的单输出交易支付很高的手续费,然后让人们慢慢取走它们。
限制条款也对 Layer 2 和个人的保管问题又很大帮助,我们可以分别讲讲。Blcokstream,是一家出色的公司了,在他们的 Liquid 联盟侧链中留下了 bug:在你将资金存入他们的 Liquid 托管服务之后,资金必须每隔几周就移动一次,以刷新时间锁。如果你有限制条款,就不需要这样移动资金了。现在,因为他们有这样的限制,Liquid 网络中需要运行一项服务,保证资金定期移动了、时间锁刷新了、错误的情形不会出现。有一次这项服务没有正确运行,Andrew 也许可以给出详细的解释,但可能这也离题了。然后时间锁的保证就被打破了。限制条款可以解决这个问题:你可以更多表达关于必须按构造发生的行动的逻辑,而不是自动化保证行动发生。
还有不同类型的存款。你可以设置让资金自动返回给存款人,而不是出错、交给另一个签名人。所以限制条款有许许多多可以帮助 Layer 2 的地方,要是能够部署,我们都会觉得很激动。
AP:这是一个非常棒的问题。说回那个 bug,问题在于资金必须每 2 周移动一次,而我们设置定时器的方式是错的。在比特币网络上你没法即时响应。创建交易之后你也没法期待它们会在下一个区块立即确认。计时器到期之后你还必须等待生成一个新区块。后来我们改变了逻辑,将移动资金的时间提早了一些。我记得我们好像提早了 300 个区块,差不多吧。但这是一个很棒的例子,说明了限制条款在哪些方面有帮助,因为它也有点特别:a)不断清扫资金有点烦人,也很昂贵;b)当你在处理签名和时间锁时,希望矿工能及时接受你的交易,是非常令人挫败的体验。你没办法保证它会工作,而且它导致了这样令人尴尬的 bug,然后就被 Telegram 上的人注意到了。
在 Jeremy 前面说的东西 —— 理论上你可以让资金原地不动,等待 Coinbase 为你开启一条支付通道 —— 的基础上,你可以将它通用化。在许多情况下,你都不必在乎是不是 Coinbase,你可以说,“任何人,只要有足够多的通道容量、可以连接到我的支付目标,都可以拿走这些钱并开启一条支付通道”。你可以将大量操作都外包出去,而在常规情况下你必须跟通过身份验证的对等节点在线交互。你可以形式化表达你对时间段的需求、把一些代码丢在区块链上,然后等待一些人捡起它。
感谢你给出了这么漂亮的回答。我本来以为限制条款跟 Layer 2 没什么关系,主要跟 “保险柜(vault)” 和保管方案有关。但你讲出了一些我通常不会想到的,很棒的应用。
SA:你们想回答观众的问题吗?
Q:我想听听你们对保险柜应用的看法。
AP:我的想法吗?坦率地说,我认为保险柜对比特币的大规模采用是必要的。那么,保险柜是什么呢,它们是一种限制条款构造,你可以约束资金的流向,使它们除了去往某个特定的目的地,哪儿也去不了。资金就呆在这种限制条款脚本中,如果你想移动它们,你必须创建一笔定义了目的地的交易,然后将资金移动到一个暂存区域,然后在三天(或者限制条款逻辑规定的别的条件满足)后,资金就可以移动到目的地。在三天的等待期中,任何拥有特定私钥的人都可以重设定时器,或者可以改变目的地,而且改变目的地时必定重设定时器。这背后的想法是,如果你的私钥脱离了你的控制,盗走你的私钥的人无法拿走你的比特币。他们可以尝试并移动资金到这个为期三天的定时器中。你可以不断重设这个定时器,然后你们两个会有一场手续费竞争。如果攻击者的目标是阻止你拿回资金,那么他们可以做到,只是要不间断地在线、花钱来维持攻击。但无法拿走资金,应该就能阻遏这种昂贵的攻击,私钥也就没那么敏感了。
关于保险柜,虽然你控制着你的私钥,你可能还想要限制资金取走的速度。也许这些私钥受到外部的法律的限制,也许因为输出中的钱属于公司,你的董事不允许你一次性花太多的钱。又或者,你安排了一些严密保管的冷私钥。热私钥可能被盗,因此你希望这些热私钥只能一点一点地拿走资金。你可以使用相同的金库构造,指定资金只能在一段时间后移动并且只能移动特定的数量。
在当前的比特币中,如果你授权的资金的移动,那么移动任意数量到任意目的地都可以,它是完全二元化的,要么什么也拿不走,要么全部都可以拿走,对于尝试保管比特币的个人用户,这真的很吓人。如果有人要从事道富银行(State Street)这样的业务,或者保管大量的比特币,那真的很吓人。
我认为保险柜是比特币被广泛采用的必要条件,希望我们能做到这一点。
JR:我也喜欢保险柜,CHECKTEMPLATEVERIFY 可以做出稍有区别的变种,但也是很有意义的。我认为我制作的保险柜有 一种最重要的属性,就是它有故障应对机制,它可以说是一种 “不冷不热” 的钱包。热钱包的意思是你可以随时随地花费,而冷钱包要设计得难以动用。比如说,你去了三个不同的沙漠,然后把刻有你的种子词的金属板埋在沙子底下,需要获得其中两个才能恢复你的多签名钱包。或者你可以埋在混凝土底下,对抗核辐射什么的,这个我们改天再说。完成了这些困难的操作之后,有一天,你说,“我要把钱取出来!我要把钱从这个 2-of-3 多签名钱包取出来”,这你是做不到的。需要安排一种紧急取款流程。你可以模拟你的备份流程,因为你每次访问它都会泄露熵。每一次你动用紧急取款,一些人就能意识到“Andrew 又在整理他的资金了”。另一方面,做起来越困难,你就越是不想经常做。沙漠可能会换成你的书桌的三个抽屉,降级了,但这样你才愿意为之付出一次性的成本。你可能会想:“随便吧,等我去一个新城市时,我再带上 GPS,把种子词埋在某个地方”。而有了这种不冷不热的钱包,你可以把你的资金设置成年金。假设里面有 10 BTC,在上一次取款的一个月后,你可以取出 1 BTC。这样就可以从保险柜中取出流动资金。但是,如果你被攻击了,你也可以将它们紧急发送到深冷存储中。它所做的是将你对备份的两种担心 —— “我希望我的资金是安全的” 和 “可以花费但我也想要在紧急的时候恢复它们” —— 分离开来。很好地分切两者之后,我认为,会有多得多的用户接受这种更安全的冷存储备份。
SA:一部分围绕限制条款的讨论是重新引入一个被中本聪在 2010 年禁用的操作码,叫做 “OP_CAT”。你们能不能讲讲这会对限制条款的提议有什么样的影响?我们从种可以得到什么?它在限制条款中有什么作用?
AP:OP_CAT 是我最喜欢的操作码。CAT 是 “拼接(concatenate)” 的缩写。OP_CAT 的功能就是把你的脚本解释器环境堆栈中的两个对象拼接在一起。现在你有了一个元素,是两个对象前后相接而形成的。在比特币中,执行就是验证,所以你也可以反过来运行。如果你有一个很大的数据,那么你可以要求用户在花费时提供两半数据,然后你再 CAT 它们,检查是否相等。你总是可以这样逆转每一个操作。
就 CAT 自身,你可以用来打碎所签名。从代数上来说,不论 ECDSA 签名还是 Schnorr 签名,都有两个不同的部分,一个是数字的部分,我们通常称为 s
,另一部分是一个公钥,我们通常称为 R
。 如果没有 OP_CAT,如果你尝试实现一些有趣的限制条款技巧,比如通过发现代数上的后门来把限制条款藏进协议中,那是很难做到的,因为你无法分割签名,因此无法将 s
跟 R
分割开来、专门限制某一样。 s
和 R
是完全不同的两种东西,而且 R
更难限制。
在 Taproot 中,我们已经有 Schnorr 签名了;事实证明,使用 OP_CAT 和 Schnorr 签名验证操作码, 你就可以做出一种形式的非递归型限制条款,你可以确确实实得到一条交易的哈希值。你得到的甚至不是什么有趣的、扭曲了的交易哈希值,而就是堆栈中的交易数据的 SHA2 哈希值。我不想涉及太多代数,但这真的非常酷,所以我要说清楚一些。Schnorr 签名的生成方式是: s
等于 k
加一个哈希值乘以 x
,这里的 k
是一次性私钥,而 x
是你的真实私钥。使用限制条款和 CAT,你可以约束 k
等于 1
,而且 x
也等于 1
,这样一来,等式就化约成了 s = e + 1
。在比特币中,你没法真的对大数字作几何运算,但使用 CAT,你可以把大数字打碎成小数字。你要做的事情就是取得签名、约束它,使你的 s
值必须等于你的交易的哈希值加 1
,你可以使用 CAT 拉取 s
值的最低几个字节,然后使用普通的操作码从中减去 1,然后使用 CAT 把结果再放回去。这样,你就得到了所有交易数据的 SHA2 值。然后,你要求用户将显式的交易数据放到堆栈中,然后使用 SHA2 操作符来压缩它、检查它跟你从签名中抽取的部分是否相等。现在,你再使用 OP_CAT 来打散刚刚被你哈希的交易的组成部分,然后你就能约束它们了。这就是限制条款,你所需的只是 CAT。
JR:不知道这样说能不能让你们好受一点 —— 我只听懂了一半。你得读这篇博客才能真的理解。我会这样讲解 CAT:它有点像一个骨瘦如柴、只有 80 磅的孩子,因为每次都能投中三分球而被首选为篮球运动员。你肯定会问:“你这运动天赋是怎么来的” CAT 也可以让比特币神奇地变成量子安全的,因为你可以加入 Lamport 签名。你知道这个吗?
AP:我忘了这回事。
JR:有很多这样没有关联的事。如果我们有 CAT,我们也可以得到一个 CHECKSIGFROMSATCK 的变种。很多很多。我认为这也是 Andrew 签名的观点的一个很好的例子,也许我们会在未来意外地得到递归型限制条款,因为 CAT 这样不起眼的东西也能给比特币带来可怕的复杂性。力量都来自最让人意外的地方。
Q:你怎么使用 CAT 来分割数据呢?
JR:你把它转变成一种验证操作码,验证你传入见证数据的两个东西正是某个东西的碎片。
Q:可以处理签名数据这么大的东西?
JR:是的,在理论上是可以做到的。而且有一些代码技巧可以使之缩小。
Q:为什么当初中本聪要禁用 CAT?
AP:最初的 CAT 实现有一个内存膨胀问题,它并没有约束输出的大小。你可以把 0 字节或者别的什么东西推进去,然后使用 OP_DUP 来复制它,再使用 CAT 把它们拼再一起。只要重复使用 DUP 和 CAT,很快数据量就能长到天上去。这就是它被禁用的原因。如果当时只破坏了 OP_CAT,而且只有这个问题的话,我想我们可能已经正确地修复了它。只需要一次软分叉,表示从此开始不允许 CAT 太大的数据,就没问题了。但是,这是一个更大的、审计所有操作码的项目的一部分。有许多的操作码都被禁用了,而且各自都有一些表明需要重新评估的小问题。中本聪当时决定单方面全部禁用它们。我认为那时候还没有人真正思考过软分叉和硬分叉的区别,以及加入东西和移除东西在难度上的不对称。当时使用的语言是我们可以现在禁用它们,未来再重新启用。但实际上它们直接就被移除了。代码库里只剩下了名字。这就是 CAT 被禁用的原因,它会导致内存膨胀。
Q:你能直观地告诉我计算这些程序的代码运行在哪儿吗?
JR:这取决于你使用的限制条款提议。对于通用的限制条款,代码基本上都运行在脚本内部。然后这个脚本路径的创造者也要遵循一些逻辑,比如如何创建满足脚本的数据、如何生成满足这些要求的见证数据和交易。这些代码可能比放在脚本中的验证代码更为复杂。尤其是 CHECKTEMPLATEVERIFY,它是一个极端例子,脚本里面只有交易的哈希值。所有的逻辑 —— 怎么处理交易、怎么广播交易,以及其它你可能会做的事 —— 都要作为一个单独的东西,由你自己维护。
Q:非常简单,在你可以看到一个多签名脚本的地方,常常会有一种等价的脚本,它有各种更高级的约束。
Q:但是,如何触发一笔新交易呢?
JR:推动它前进的所有东西都必须是用户创建的。没有自我执行这回事。比特币中没有代码表示 “根据共识,这笔交易必须在某个时间触发另一笔交易”。
Q:我猜递归型限制条款会更加复杂。问题是,相同的脚本,跟什么一起转移呢?
AP:没错,确实如此,必须由用户来采取行动、移动资金,但基本上,他们可以做的唯一操作就是移动资金到原始脚本的另一份拷贝中。所以限制条款会限制的你的行动,但必须有人才能发起积极操作。
Q:可以有多种脚本能满足同一个限制条款吗?
AP:简单的回答是,有。限制条款不必非得限制得很死。你可以表示一笔资金可以移动到任意数量的具体脚本中。你可以表示资金可以转移到任意脚本中,只要那个脚本以某些具体的条件开头,后面是什么条件则全无限制。都取决于具体的限制条款构造,但理论上,你在这里有完全的计算自由。
Q:在应用层,我知道关于 “coin pool” 这种概念的研究。假设我有一笔资金,我可以把它扔进一个跟其他 100 个人形成的免信任托管合约种。 随着人们花费资金,这个 UTXO 会逐块逐块地变化。也许有人会运行潜水艇互换服务器,在闪电网络上把钱接回来。有点像 Green Wallet 正在使用的架构,不带限制条款。缺点之一是其交易的图谱可能会剧烈膨胀?我想听听你对这方面的进展的看法。
JR:当然,coin pool 是可以实现的。最近出现了一些别的限制条款操作码。一种叫做 “TLUV(TapleafUpdateVerify)”。我认为 coin pool 是一个非常棒的机会。一定程度上它需要一个声誉层,我认为闪电网络也有相同的忧虑。你肯定不想进入一个人们的手机经常离线的 coin pool 中。你需要确信无论什么时候你要求一笔交易,而且你的交易满足更高阶的业务逻辑 “这个人正在请求发送资金,这是 TA 的余额的一部分”,你都可以默认签名交易。你希望得到参与者的活性保证。但这样会让交易图保持较小规模,因为人们需要共享 UTXO。不同的提议有不同的取舍。我认为,人们应该忽略掉交易的数量,只从字节的数量来考虑,因为有时候人们会认为 “这样做只需使用更少的交易”,我认为这就是人们的 TLUV 的感觉,类似于 “这种提议只有 N 笔交易需要存储,而 CTV 将需要 1.2N 笔,所以 CTV 更糟一些”。但如果你计算字节数,CTV 的体积小 6 倍。所以,对于某些事情,如果你统计交易的数量 vs 字节数,小体积交易 vs 大体积但数量更少的交易,你会感到惊讶。
Q:活性需求需要 coin pool 的每个人都在线吗?还是说可以实现一种阈值,只需一些人在线?
JR:看你想要什么样的保证。想象你有一个 coin pool,每个人在其中都有一个死党,然后你说,“我相信我的死党和其他人会保持诚实。”然后你就可以做一个一半人参与的门限机制【注:例如, AND(OR(Key(A),Key(B)), OR(Key(C),Key(D)), OR(Key(E), Key(F))
,任意 N/2 的组合】。这样你可以做一些更复杂的事情了,但他们也就能偷盗你的资金了。在 CTV 的世界里,我思考它的方式是,只要你遇到了一个故障,你就希望这个池子分成两半。所以,当你的池子遇到了一个活性故障,你就分割它;然后,如果又有一个人离线,那么一半的池子会恢复在线,但另一半就必须再次分割。你可以自己算一下,使用两笔交易,你就让 75% 的池子恢复了在线。发起另一笔交易你就可以让人们很快回到线上。唯一一个不走运的人是那个在交易树上跟离线者靠得特别近的人,TA 必须发起 O(log(N)) 笔交易才能退出,但这也不坏。因为 O(log(N)) 非常小。
Q:交代一下背景,我问这个问题的原因是,现在我们都看到了,闪电网络已经大大扩展了,许多用户共用一个节点,但许多用户在链上都没有公钥。多名用户共用一条通道,这并不理想,而且没有人会感到高兴。但 Green Wallet 做的一个有趣的事情是,他们让多个用户都在链上拥有公钥,但依然使用同一个节点,这个节点只做从链上到链下的潜水艇互换。拓展这个模型、使之能容纳许多用户,而且不要求【语音不可分辨】在池子内部实现分布式,是非常有意思的概念。
JR:在闪电网络语境下考虑这个问题时,我想补充的另一件事情是,这是 CTV 对 TLUV 的一个优势:无需加入 ANYPREVOUT,一个基于 CTV 的 coin pool 就可以拥有嵌套的通道。所有这些东西,只有在你想要重新平衡流动性,或者发起对外支付时,才需要做一次更新。不然的会,你可以在你的交易树的叶子节点中实现所有东西,就像闪电支付一样。我认为,这是一个非常强大的元件组合,你可以跟许多人创建通道,而且只需要偶尔更新。
SA:最后一个问题。
Q:这是给 Andrew 的问题。你觉得 CTV 的主要缺点是它让我们必须审核过于狭窄的东西,还是说你真的看到了一些有问题的故障模式?
AP:不是这个原因,主要就是因为在我看来它实在太过有限了。有一种稍微接近的观点是,如果我们在未来发现了一种很酷的应用,需要更通用的限制条款来实现,但因为我们 90% 的功能都已经在 CTV 里实现了,那么就会更难通过(通用的限制条款),因为这需要大家往回走并再来一遍。同样地,也许 CTV 会跟未来某一种如无 CTV 就会得到推行的提议不兼容。或者,可能存在一些效率因素或者某种交互,是复杂得难以分析的。实际上,如果我们要给比特币增加新东西,我会倾向于得到尽可能通用的东西。我猜我们的分歧在于什么东西才是我们可以得到的最通用的东西?我认为 Jeremy 已经表示了怀疑:即使我提出了正确的东西,也无法让我的提议全部加进比特币。
JR:仅仅从社区的观点看,有一些人,比如 Shinobi 就公开表示,他不会接受任何比 CTV 更复杂的东西。我不同意这种立场,也不认为这是好的,但他有他的理由,而且社区是一位严厉的女主人。我不知道我们能否让每个人都参与进来,但我想尝试一下,甚至连 CTV,都很难让人们 “受到触动”。有一个 utxos.org/signals 网站,是那些充分审核过 CTV 并认为他们可以推动社区走向合并、释放软件然后激活的人留名的地方。看起来不错,但也不是一蹴而就的事,需要持续多年的努力,让每个人都感觉它已经得到充分审核了,而且它已经获得了足够多的应用场景。
SA:好的,我们到这里就结束了。感谢各位!
Community-maintained archive to unlocking knowledge from technical bitcoin transcripts