作者:Christian Lewe

来源:https://btctranscripts.com/advancing-bitcoin/2023/simplicity-going-beyond-miniscript/

本文为 Christian Lewe 在 Advancing Bitcoin 2023 大会上的演讲,由参与 BTC transcript Review 项目的 aphilg 将录音转写成文字稿。

欢迎各位。我是 Christian Lewe。我在 Blockstream 做研究,主要工作是 Simplicity 和 Miniscript。刚刚各位听了一场关于 Miniscript 的演讲,所以各位都知道 Miniscript 好在哪里了。那么现在我只需要作一个非常短的介绍就可以了。

我们已经看到了,Bitcoin Script 是复杂的。前一位演讲者已经解释得很好了。我只有一张幻灯片,这是一种极为常见得智能合约,它用在闪电网络中。尽管它无处不在,也并不很难理解,脚本需要做的事情也并不是很复杂,但它看起来还是那么复杂,而且有很多可能搬起石头砸自己的脚的地方。事实证明,这样的智能合约也可以用这个非常短的 Miniscript 代码来表达。它们是完全等价的。

Miniscript vs. Bitcoin Script

Miniscript 是一种带有结构的 Bitcoin Script,而且,重点是,不管是人,还是机器,都能更容易地分析它。它有一个树形结构,你可以看到一个 or 和一个 and,还可以看出哪里将使用公钥、哈希原像。人类阅读起来容易得多,机器也更容易分析它。我要非常简略地讲一下它的好处。所以,结构的好处就在于,我们可以确保我们的脚本是正确的:我们希望设置的花费路径就在其中、不想要的路径就不在其中,这样黑客就无法偷盗我们的钱。这个脚本是不可熔铸的(non-malleable),所以没有人可以拿到我的交易之后以奇怪的办法调试我的见证数据,然后让我追加手续费什么的。这样以来,脚本也就变成了可以组合的东西,就像我们再上一个演讲里听到的,从两个不同的地方得到两个脚本之后,我们可以把它们组合成一个更大的脚本;脚本的来源并不需要彼此知晓,我可以直接分析最终的脚本。我也可以尝试为这个最终的脚本找出一种见证数据,这对互通性(interoperability)是好事。我也可以静态地分析脚本,看看我需要支付多少手续费、我是否会触及共识规则上的限制(以至于无法花费我的资金)。而且,最终来说,所有的脚本都成了机器可读的东西,所以我可以使用第三方的工具来分析它们,也可以在工具中将它们默克尔化,我还可以将它们用在零知识证明以及所有这些东西中,它是充分面向未来的。

Simplicity

那我要讲什么呢?我要聊聊 Simplicity,这是一种新的区块链语言,而且它具备我刚刚提到的一切好处。已经摆在台面上的事情是,它具备任意计算功能(我后面会解释这意味着什么),以及,它是可以形式化验证的(比 Miniscript 更强),这都是令人兴奋的东西。我们深入讲一下。

首先,我说的 “任意计算”,听起来有点模糊,它到底是什么意思呢?一种理解的角度是,我可以计算任何有限函数(finitary function),也就是以 n 个比特为输入,输出 m 个比特的函数。也许你大概知道这是什么意思,但也许你还是觉得太过理论化了,还是不明所以。那么,另一种理解角度是,(用它写出的)每一种函数最终都会停止,也就是使用 Simplicity 我无法写出一个可以运行无限循环的图灵机,我无法写出任何无限的循环。我们都不想要这样的东西,对吧?这会是一个负面特性(anti-feature)。我只希望拥有有限函数。但是,这又意味着什么呢?我还是说不好。那么第三个角度,每一种你真正想要的函数。虽然这个角度不能帮助你理解它,但至少这揭示了我们设计 Simplicity 语言的时候的目标。我们希望实现每一种你可能想在区块链上实现的函数,但是又不能用来 DDoS(分布式拒绝式服务)攻击区块链,所以不能拥有无限循环。那么剩下的东西就都可以用 Simplicity 来实现了。你可能想到了一些什么,但我会用三个准确的案例,来揭示 Simplicity 可以做到、而当前我们做不到的事情。

计算模式

首先是计算模式(computation model)。因为 Simplicity 听起来很复杂,但实际上并不。它简单得就想自己的名字一样。所以,在花费一个脚本(我们称之为 “程序”)的时候,我们有见证数据(witness),其中是签名和哈希原像什么的。然后是交易,它包含了时间序列,以及所有的交易数据。然后,Simplicity 程序会告诉你,你的花费成不成功。这就是 Simplicity 做的事情。它没有状态,没有这些那些东西。而且程序最终总是会终止的。没有昂贵的内省(introspection),所以我们不需要昂贵的索引结构以及所有这类东西。所以,Simplicity 执行起来相当快,即使它功能非常强大。

Universal Sighashes

另一个我们可以实现的东西是 “universal sighashes(通用的签名哈希模式)”,自身也是非常酷的一个东西。很久以前我们为之撰写了一篇博客,所以你可能知道 Sig Hash 模式。Sig Hash 模式就是说,在比特币中,签名都涵盖交易的特定部分,并承诺这些交易。这就意味着,如果这些部分遭到改动,这个签名就失效了(无法被改动后的交易所用),因为签名中包含了承诺。但反过来,如果改动的是签名没有涵盖的部分,那就不会让签名失效,改动后的交易也依然是有效的。这对于闪电通道等方案来说是非常重要的,因为没有承诺的部分可以熔铸。而在 Bitcoin Script 中, 我们可以使用 6 种签名哈希模式。但是 6 种其实并不多,在 Simplicity 中,我们可以做得更多、更好。

其中一个可以做到的事情是,我们可以写一个 Simplicity 程序来影响哈希值。比如:我们可以设置一个条件,要求签名所涵盖的时间锁大于某一个阈值,这是你用现在的 Bitcoin Script 做不到的。我可以签名一个比例,表示我不在乎具体的数值;假定这是在 Liquid 侧链上,那么,我可以表示我不在乎某一种资产我有多少、要花多少,我只在乎它跟另一种资产的交换比例,也就是它的价格。所以,在我表示愿意付出一定数量的资产 A 之后,你可以熔铸我的交易,你可以改变我支付的数量以及我可以得到的资产 B 的数量,因为我只限定价格,不限定数量。我还可以签名一种限制条款(covenant),这也很酷。我们可以签名要求这个限制条款得到执行。许多东西都依赖于委托,从而,我可以在签名时插入我想要的 Sig Hash 模式;你也可以限制哪些部分是可以插入的、哪些部分是不允许插入的;我们还可以在签名哈希模式中表示我完全不签名,只是把权限交给另一方。我可以控制某一方来承接这种权限,使得他们可以花费我的钱,这样一来,我就把我的公钥的签名所用到的一种签名哈希模式,转变成了一个来自其它公钥的签名,这是非常酷的。而且它比以往任何的哈希函数都要强大得多,因为我们可以在花费的时候再计算哈希值。

零知识证据验证器

还有一个东西是零知识证明(ZKP)。最近非常火,但 ZKP 的问题在于,我们基本上需要一次软分叉。使用 Simplicity,因为我们可以计算任何函数,这其中就包含了ZKP 验证器,所以我们可以在 Simplicity 中构造它,而不需要软分叉,现在就可以开始尝试。而上一个演讲中,我们提到了可以加强隐私性的 “机密交易(confidential transactions)”,这用到了一种叫做 “bulletproofs” 的东西来隐藏数额,这就是使用了 bulletproofs 和零知识证明的 “零知识” 特性。如果我们可以实现一种 SNARKs,那么我们就能实现 rollup(一种侧链),这用到了零知识证明的简洁性(succintness)。这意味着我们可以压缩计算,让证据变得非常短小(对数级别的长度),并让验证时间非常短(也是对数级别),这对可扩展性很有好处。我们可以在 Simplicity 中实现所有这些东西,而且现在就可以开始尝试。

定制化的密码学

而且,总的来说,我们还可以支持定制化的密码学,所以,只要你想,你可以实现曲线配对(Pairing),你可以实现另一条曲线,然后实现 BLS 签名。你也可以实现 SHA3,只要你想。我们已经实现了 SHA2,这是可以做到的,所以你也可以实现 SHA3,还可以做基于身份的加密。这些大家都不是很了解,但非常强大,而且可能性几乎是无限的。这一切都跟尝试新事物有关,而且不需要软分叉。这些密码学可能是昂贵的,但你依然可以尝试在自己的机器上实现它们。而且,如果社区感兴趣,他们可以加快这个进程,后面我会解释这意味着什么。

常见的误解

表达能力:Simplicity vs. Bitcoin Script

接下来我准备谈谈一些常见的误解。第一个事情是,你可能会认为,Bitcoin Script 的表达能力已经足够强了。因为有人尝试说服我,Bitcoin Script 可以做到的事情比人们以为的要多得多,但是那些使用脚本和比特串的方法非常不实用。所以,我非常信服你可以(用 Bitcoin Script)计算出 Simplicity 可以计算的一切东西。但问题在于,这样的构造,就像我说的,会是非常不实用的。这是一个大问题,因为 Bitcoin Script 并无意于支持任意计算,但 Simplicity 有这个设计目标。所以 Simplicity 优雅得多,也短小得多。它是为任意计算而设计的,而 Bitcoin Script 不是。所以,说 Bitcoin Script 表达能力很强在技术上是对的,但在实际场景中却是错的。

结构化:Simplicity vs. Miniscript

然后是结构化。因为 Simplicity 是一种新语言,但它实际上跟 Miniscript 非常相似。Simplicity 也有树形结构,而且也使用所谓的 “拼接符(combinators)”,就像是你拥有一些拼图块,然后你以递归的方式将它们组合成一棵树。你可以看到,这些拼接符可以接收不同数量的参数,有一些不接收参数,有一些只接受一个参数,还有一些接收两个参数。你只需将它们拼在一起,就像 Scratch 编程语言一样,完成之后就是你的程序了。所有这些拼接符都有自己的含义,但我这里就不展开了。你会觉得它很像 BDK playground 那个 Miniscript 调试器,这并不是巧合,就像我说的,它跟 Miniscript 很像。两者都有树形结构。唯一的区别可能是,Miniscript 的拼图块拥有更抽象的含义。Simplicity 更像一种汇编语言,所以更底层,它的拼图块会做非常具体的事情。而屏幕上这些拼图块(Miniscript 拼图块)会做更抽象的事情。

Simplicity 代码示例

我准备非常快地展示一些 Simplicity 程序。这样你回到家的时候就可以说自己今天看过 Simplicity 了。我不会讲细节,因为时间也不够。但是,这是一个公钥锁,所以你可以看到公钥,还有 Sig Hash。所形成的一个签名,会放在见证数据里面,然后还有 Schnorr 签名验证,都以树结构的形式表达。这是一个哈希锁,所以有一个硬编码的镜像(image),也就是你的目标。还有见证数据,包含原像和一些哈希运算,最后是等式检查。你还可以实现一种时间锁,你要设置一个阈值,然后是锁定时间,是从交易中读取出来的,然后你检查是否小于等于。所以,Simplicity 程序都是树形的,你可以表示所有函数,也可以确定或者说找出每一个函数;这个部分,我们称为 “core Simplicity”。core Simplicity 指的是我们可以使用拼接符表示的所有程序,而且我们证明了这是完备的,所以我们真的可以地表达出所有这些我们声称自己可以表达的函数。但一个明显的缺点是,其中一些程序的体积很大,尤其是比如 Schnorr 签名验证这样的东西。我们使用 Simplicity 编写过,非常大。所以我们需要一些捷径,这些捷径我们称为 “jets”。也就是说,因为一些程序可以计算我们认为非常有用的东西,我关心,社区也觉得每个人都关心,所以我们就把它变成一个 jet。我把它变成一个 jet,以便加速计算;我会编写 C 语言代码来加速。所以,jets 是 core Simplicity 的子集。这也意味着,我们马上拥有了设计新的操作码的空间。所以,我们已经知道了,Simplicity 可以计算所有我们可能希望部署在比特币上的东西,那么,这也意味着它能实现所有可能发明出来的操作码;这也意味着,如果我们在比特币上合并了 Simplicity,或者我们从 Simplicity 的角度来考虑问题,那么一个新的操作码就是一个新的 jet,而且它必定落在 core Simplicity 范围内。这样,我们就有了一种结构化的方式来讨论新的操作码,而且我们也可以验证这个操作码做到了我们想让它做的事。我会简单谈谈这一点。

共识限制

另一个误解是,由于共识规则上的限制,Simplicity 不可能是万能的。当然,即使我们可以实现所有的函数,可以计算所有的函数,实际上,也会有一些函数计算起来需要更长的时间,而我们不想 DDoS 攻击区块链,所以就会有一些共识上的限制,但这对 Bitcoin Script 来说也是如此。而且解决方案我也已经提到了,对于一切我们关心的东西,我们希望运行得快一点得东西,我们就创建一个 jet。所以这并不是一个问题。

形式化验证

形式化验证(formal verification),我知道,这又是一个很罕见的词。我会简单讲讲。这张图也许会有帮助。图上的左边是 Simplicity 的技术栈,右边是 Miniscript 的技术栈。Simplicity 程序有一些形式化的语义,所以我们真的知道它在数学上意味着什么;然后,Simplicity 程序会在 Simplicity 运行时环境里执行。Simplicity 运行时环境也有一个形式化的表达,所以我们也知道它是什么。我们还有一些 jet,它是用 C 语言写的,但实际上,我们用的是 “可验证的 C 语言”,它是 C 语言的一个子集,而且它由于数学上的语义。而且,所有这些都执行在可验证的 C 语言编译器上,它被证明了可以正确执行,所以我们让这些二进制文件得到了一些保证,这很重要,不是吗?我们不止在论文中证明了数学理论,而且在产生的二进制文件中这些理论也是成立的。这比 Miniscript 要可靠得多,Miniscript 自身也有形式化的语义,这很好,但我们很艰难才能得到,而在它之下,就远远不是那么形式化了。在它之下是 Bitcoin Script,这个可以说在一定程度上是形式化的,它拥有一些 C 语言代码。它基于 C++ 标准,这个标准是有定义的,但它从未意图用于形式化分析,而且它有一些非常恶搞的语义,可能会给你惊吓。这真的非常难办,你很难验证使用 Bitcoin Script 写出来的东西。

Simplicity 带来了什么

再来谈谈 Simplicity 可以做哪些现在的 Miniscript 做不到的事。在 Simplicity 中我们可以证明更多东西。我画了一些图来展示这一点。

正确的子程序(Correct Subroutines)

其中一个应用是子程序。我已经提过,我们可以编写出所有这些有趣的数学小工具,例如 SHA3、零知识证据验证器,这都是些复杂的程序。那么,我如何知道程序是正确的呢?而且,更大的问题是,当这些程序变得非常大的时候,我们会将它们嵌套起来,就是在一个程序里面嵌入另一个程序。我需要保持一种总体认知。所以,我需要知道这些数学小工具会产生正确的输出,那我怎么做到呢?我要写一个形式化的规范,这可以是用另一种语言表述的精确规范,而且这个规范有一些形式化的表现,就像这个螺旋形一样。这是一个数学对象,此外我拥有一些 Simplicity 程序。可能是一个 SHA3 程序吧,或者是我写的别的什么东西,而且,因为这是 Simplicity,我们有一种办法可以将它放进数学领域,这就是另一个螺旋形。那么,我可以做的事情是,我可以使用一种证明助手(proof assistant),证明这两个螺旋形是一样的。也许乍看起来并不相同,但经过一些数学分析之后,可以证明它们是一样的,然后我就可以确信,这个程序实现了这个规范 —— 我验证了这个程序。这是很棒的,而且当它跟更多的钱产生关联的时候,我们应该做更多这样的工作。

明确指定且验证过的操作码

另一个事物是操作码。Jonas Nick 曾经在 TABConf 上讲过这一点,关于可证明无硬伤的 BIP,那是非常酷的(中文译本)。如果你阅读 BIP,你会发现,一份 BIP 常常会指定一种新的操作码,在满足某些条件的时候中,这个操作码会失败,反之,则不做任何事;等等。这就是一种非形式化的规范。Taproot BIP 包含一些伪代码,可能是一些 Python 代码,这是有帮助的。但它依然是一种非形式化的规范。我么可以做得更好。我们可以编写一些形式化得东西,比如一份精确的规范(我听说 “减半聚合(half aggregation,Schnorr 签名的一种非交互式聚合方法)” 的 BIP 就是用精确规范写的,非常棒)。这就意味着,这份规范具有一些数学上的形式化结构,是我们可以抽取出来的,这样我们就能清晰地交流我们的真实意思。这里面将没有歧义,因为数学不允许,这正是我们这样做的理由。而且我们还可以对这份规范运行一些单元测试,并验证它。这就意味着 —— 在 Simplicity 的案例中,一个操作码就是一个 jet,它是 C 语言的代码,是我们用可验证的 C 语言编写的,那么,我们也有办法,抽取出其中的数学对象 —— 这些代码所计算的函数。再一次,我们可以证明,这个 jet 实现了这份规范 —— 通过证明这两者的表述是相同的:规范和实现具有相同的数学描述。这会让我们的操作码更少错误。理论上可以做到没有 bug,但这显然是很难得。规范也有局限性,但是,即使只是这样,即使我们不贯彻整个流程,只是这样撰写规范,那也是一个巨大的进步。Simplicity 就带来了着一些,而且也让我们可以验证一个操作码,只要我们想验证的话。你还可以做更多,但我在这里就只讲这些例子了。

脚本验证

一定程度上说,Bitcoin Script 已经是可验证的了。我们可以作手工审计(manual audits)。我们可以一行一行看代码,检查这些代码做了什么,以及我希望它们做什么。我们也可以跟踪合约的状态,而且在 Miniscript 的演讲中,我们也这样做了,为了发现 Script 哪里出错了。但这有点乏味,而且也没有人想要这样做。有了 Simplicity,我们可以尝试让它变得更易于验证,而且我们也尝试开发,给出一些模块、一些准备好了的程序(它们已经有许多的定理和保证了),这样你就可以组合它们,用这种拼图模式写出更大的正确程序。而且你可以相对容易地证明它们是正确的程序。验证工具可以在这里派上用场。Simplicity 有意成为可以验证的语言,而 Bitcoin Script 虽然是可以验证的,但并不是有意设计成可以验证的。

从 Miniscript 到 Simplicity

我们要超越 Miniscript,因此,让我们来聊聊如何将 Miniscript 代码映射成 Simplicity 代码,因为它们有相似的结构,所以这很容易办到。

递归转化

它们都具有树形结构,所以我们可以作递归转化(recursive conversion)。我们可以逐个节点地转化它们,这样一个签名会转化成一个签名,一个原像会转化成一个原像,等等,这样一个一个映射过去,从上到下。最终,一棵 Miniscript 树可以递归地转化成一棵 Simplicity 树。而且我们永远可以检查 Simplicity 程序,并确定它来自于这个 Miniscript 程序。所以说,它不像 Miniscript 编译器那么复杂,它要优雅得多。据我所知,这是用 “驱逐(ejection)” 来实现的。我们正在开发一种工具,让你也可以这么做,我们称之为 “Miniscript-Simplicity 桥”:你传入一段 Miniscript 十六进制码,一段 Bitcoin Script 十六进制码,它就会给你等价的 Simplicity 程序。我们也在开发别的工具,帮助你可视化这个程序,它的树形结构,等等。所以,这都是让人激动的东西,敬请期待接下来的新博客。

还有一方面是语义上的。上文是关于语法上的,我们也可以聊聊语义上的。

语义表述

我们有一段 Miniscript 代码,它有一些含义,也就是它的语义。我们也有一个 Simplicity 程序,它也有一些含义。它的意思就是它实现出来的花费条件,这些条件决定我们什么时候可以花这笔钱、什么时候不能花,等等。而且我提到过,Miniscript 要弱于 Simplicity,以及,我们可以将 Miniscript 代码转化成 Simplicity 代码,就如这个箭头所示。所以,我们也知道,Miniscript 的语义比 Simplicity 的语义要弱。Simplicity 的语义更丰富,也更完整。所以,我们可以通过一个我们称为 “lifting” 的程序,将这些语义嵌入那些语义中,从而形成一个更完整的图像。我们可以做的一个很酷的事情是,我们可以检查一个 Miniscript 程序和一个 Simplicity 程序是等价的,即使它们使用了不同的语言、语法也截然不同,我们也可以知道,它们实现了相同的 checksig 或者别的东西,相同的公钥锁或者相同的时间锁,相同的更复杂的东西。这都很棒。我们也在开发这样的东西。

集成到比特币

我要简单讲讲如何将 Simplicity 集成到比特币中。这会是一个很长的过程。当然,可能是五年,甚至十年。

Taproot

但是,总的来说,我们会将它集成进 Taproot。也就是说,它会成为一个新的 Taproot 叶子版本,你也知道,Taproot 是带有版本号的,它特意为将来可能的升级做了延展性。也许 Simplicity 将获得一个 tapleaf 版本号。(在这张图中)我给 Simplicity 随便选择了一个版本号;tap 树是用于脚本路径花费的;这棵 tap 树有许多叶子,大部分叶子都使用了旧式的,标准的 Taproot 叶子,其中有一些 Bitcoin Script,也可以说实际上就是 Miniscript 代码。但也有一个新叶子,使用了 c2 的版本号,这就意味着它是一个 Simplicity 叶子,也就意味着这些二进制码,这些字节实际上是一个 Simplicity 程序。所以,当我们花费它的时候,我们将使用 Simplicity 解序列器来解序列化这些二进制码,然后用一个 Simplicity 运行时环境来运行它。但它们会一起存在。Simplicity 会跟 Bitcoin Script 和 Miniscript 一起存在于 tap 树上。

Liquid Network

这是我们现在的目标,我们正在开发 Liquid。所以 Elements 库有 Simplicity 的分支。我们正在跟 Elements 团队一起开发,将 Simplicity 集成到 Liquid 中,虽然我们知道自己在做什么,虽然我们花了很多时间,来确保我们证明了许多我们声称自己知道的东西。理论归理论,实践归实践,我们必须检验它们,在侧链上测试这些特性。获得反馈,学习教训。现在我们正在开发这些 jet。看看这些 jet 要花多长时间,并分配一些权重。这就是 Simplicity 的下一个重大步骤。

结论

在结尾部分,我来讲讲结构化的程序。前一个演讲提到了结构化的程序,我也提一句。结构化的程序就是好的程序。Simplicity 在很多方面都继承了 Miniscript,比如设计目标,以及采取的方法:树形结构。Simplicity 给了我们结构化所有的好处,但又向前迈出了一步:我们有更多的功能。相比 Miniscript,我们可以计算多得多的函数。我们也拥有可验证性,所以我们可以证明,我们可以做到任何我们可能想做的事情,而且我们可以证明我们可能想证明的任何东西。希望这会带来大规模的采用,但用不用取决于你。Simplicity 毫无疑问令人激动,是一个非常激动人心的提议,我希望更多人对此感兴趣,也希望更多人能理解它。下一步是 Liquid 的集成。

我讨论了一种新的区块链编程语言,也许在你听来,这就像是潘多拉的盒子,它会摧毁比特币。我希望我能说服你,正好相反。我希望我可以说服你,虽然它很强大,但不论如何,我们需要这种力量。比特币需要与时俱进,虽然这会是一个缓慢而且非常谨慎的过程,但与此同时,我们可以验证我们的程序是正确的,而且我们还有共识上的限制,所以不会产生任何 DDoS。所以它不会破坏比特币,只会帮助比特币。谢谢。

(完)