当前位置:首页 > 区块链新闻 > 正文

Pieter Wuille:Miniscript将改善比特币脚本

来源: 互联网时间:2019-02-11 11:22:54

北京时间2月1日,比特币“代码老司机”、Blockstream公司联合创始人Pieter Wuille在斯坦福区块链会议上发表了主题为《Miniscript:比特币脚本的实用可组合性》的演讲,他提到说:

“比特币脚本对简单的策略非常有用,但它也有相比当前更多的可能用途。这在一定程度上是因为很难让事物很好地相互作用。这种交互可能随时间的推移,会让诸如闪电网络客户端和多重签名客户端这类东西,在不需要阅读所有协议工作的情况下进行琐碎的交互。”
Pieter Wuille

(图片来自:Blockstream‏)

以下是他的演讲内容译文及ppt:

我是Pieter Wuille,目前在Blockstream工作,我会做各种各样的事。今天我要讲的主题是Ministcript,这是我和Blockstream的一些同事,特别是Andrew Poelstra在共同努力的一项研究。

首先,这是关于什么?

在我开始之前,我想告诉你它不是什么。我最近发布了一个名为minicketch的软件库,但它和此是完全无关的。我希望大家并不期望我会谈minisketch,它也不是关于未来任何软分叉或研究比特币共识层的改进。这一切都是关于今天可用的和准备好的东西,但它是非常实用的。

p4

概述

我将讨论这个问题,并向您展示这是一个被忽视的合约和脚本问题的领域。然后,我将设计一种策略语言来描述比特币脚本中的内容,将其映射到比特币脚本,并描述如何使用它,然后以一些未来的工作结束这次演讲。

比特币脚本介绍

在很高的 level上,比特币脚本是一种类似于基于堆栈的第四类语言,它没有循环,其设计并非图灵完备,它是非类型化的。比特币有未花费交易输出,它是一个UTXO模型。每一个币都与脚本关联。该脚本定义了可以使用该输出的条件,并且它们总是整体使用的。实际上,花费比特币输出的能力,就是找到一个输入使得这个脚本语言中的特定脚本的计算结果为真。就是这样,当你可以这样做的时候,你可以花费输出。

p5

不幸的是,这种语言很难理解和使用,尽管自10年前比特币诞生以来就一直存在。真的没有那么多有趣的脚本被使用。我会给你一些理由来解释为什么会是这样。

在考虑脚本时,我们需要一些正确的东西,这是因为size efficient,它直接转化为链上的低成本,而且我们也希望避免延展性问题。尽管不久前比特币已升级到隔离见证(Segwit)的版本,这消除了延展性的大部分问题,但仍然有一些原因可以让你仍想要避免延展性(即无法解决所有问题)。

示例策略

一个简单的例子是特定的公钥必须签名。这是大多数比特币地址的编码方式,但这并不是唯一的可能性。也有类似于2-of-3阈值的多重签名托管,第三方托管代理机构会表示,只要有其中2个密钥同意签名,资金就可以得到释放。发送方将资金发送到2-of-3阈值策略或脚本,然后托管方可同意这两个策略中的其中一个。第三方托管方本身不能卷款跑路。

p6

还有其他可能的条件,比如2-of-3,一段时间后,脚本中内置了一个超时,其他构造中使用的一个常见脚本是HTLCs,其中你有两个密钥,并且显示了允许花费的哈希原像,或者在一段时间后,只需要一些密钥就可以花费了。

实际上,这似乎是比特币脚本可做到的。它是对密钥、时间和哈希的布尔运算。对于实际能做一点,这种语言似乎有点过于复杂。

策略的可组合性

我想在这次演讲中,特别关注的内容是可组合性。假设你有一个公司希望将资金存储在一个3-of-4 的多重签名构造中,在这个结构中,公司的一些员工或董事需为支出而签名。但是其中一个参与者已经有了一个奇特的硬件设置结构,里面内置了一个时间锁,可能还有一个带有冷存储密钥的多重签名。为什么这个设置不能作为一个整体,成为这4个成员的参与者之一?这似乎是你想做的很自然的事情吧。如果我有信心保护自己的资金,那为什么我要被迫坚持一些预先定义的策略呢?

p7

我在这里试图实现的目标是采用任何策略,并且在该策略内,可以用另一个策略替换任何密钥。这就是可组合性。这本身就是一个有趣的目标。实际上,最困难的部分是如何让为不同策略设计的软件和硬件实际交互,并实现这一可组合性目标?

我想指出一种名为Ivy.的工具。我想其作者今天早些时候在这里演讲过。像ivy这样的工具确实已帮助您构建了更复杂的脚本,但我认为它们并不能真正为可组合性问题提供解决方案。当我们谈论这个的时候,你可能会认为设计一个新的脚本是很好的,只有当有人在比特币的基础上设计一个新的协议,或者一个新的应用程序时,这种情况很少发生。但是如果我们想要可组合性,那么情况就不再是这样了,也许这是你需要在连续的基础上做的事情。

设计策略语言

我将首先设计一种策略语言来描述我在这里谈论的事情。在比特币中,原语是你可以用一个公钥消费,我将把它写成pk(KEY)。然后是multi(k,key1,key2,…)。然后有时间(T)或哈希(H),其中需显示哈希原像。

p8

我将使用阈值构造,其中参数不再是密钥,而是子表达式。我这里有三个限定。其中之一是并列版本(表达式1,表达式2),它是2-of-2的,然后则是or的两个版本。原因是第二个(非对称or)是不对称的,或者其目的是表示第一个子表达式比第二个子表达式更容易被表达。我将更多地讨论概率模型,以及如何在特定情况下进行实际优化以最小化成本。

我想指出的是,我在这里没有谈论验证内容的地方。此语言中没有签名。原像从未出现,它不是真正能计算出任何东西的东西。它实际上是设计条件的组合语言,仅此而已。这让我们可选择最有效的方式来表达在以后的阶段需要如何花费。

策略语言示例

下面是一些我们可以用它l做什么的例子。这是一个带有pk(key)的例子。我不会把所有的东西都用十六进制写出来。但在实际语言中,每次看到幻灯片上的字母时,都必须提供一个key。另一个例子是托管:multi(2,A,B,C)。另一个例子是一个可写为aor(and(pk(A),time(T)),pk(B))的保管库。HTLC可以写成aor(and(pk(A),time(T)),and(pk(B),hash(H)))

2-of-3和3-of-4多签可写成thres(3,pk(A),pk(B),pk(C),multi(2,D,E,F))

p9

我认为这涵盖了大多数可能的表达。但哈希冲突,恕我无法用这种语言表示。

输出描述符?

如果你完全熟悉最近Bitcoin Core有关输出描述符(output descriptors)进行的研究,你可能会看到一些相似性。这是合理的,因为我计划将其作为Bitcoin Core描述符语言的扩展,用于表达关于如何使用特定输出的所有知识。

映射到比特币脚本

那么我们怎么把它映射到比特币脚本呢?基本构造相当简单。你将每个子表达式视为期望其输入的操作码脚本序列的一部分,因此它的签名或堆栈顶部期望的任何内容,并将其替换为堆栈顶部的0或1。多重签名变为2 A B C 3 CHECKMULTISIG.而pk(A)变成一个CHECKSIG,(X,Y) 变成X TAS Y FAS BOOLOR,thres(2,X,Y,Z)是X TAS Y FAS ADDTAS Z……

10

11

在执行期间,你可以访问两个堆栈,但你唯一能做操作是将某个堆栈移动到另一个堆栈。所以除了堆栈,你还有一个备用的堆栈。

优化工作

如果我想要一个2-of-3的多签地址及密钥,但又需要第四把密钥key D来一直签名呢?按照我以前的天真执行方案,这将变成一个相当长的脚本。如果你已经研究了脚本的语义,那么这真的是不必要的长。我们改进这一点的方法是认识到,我们可以使用不同的“调用规则”来转换这些子表达式。它以什么方式期望堆栈上的输入,以及如何返回?

12

最初的一个,从堆栈顶部获取其输入,并改为0或1,我称之为“E”调用约定。我介绍的另一个是“W”,它代表覆盖(wrapped)。这是一个转换为脚本的版本,在该版本中,你希望输入到堆栈下的一个。你期待着一些东西,而你留下的那堆东西却没有动过。对于我们所拥有的几乎所有的结构,覆盖版本与其他版本相同,但它有两个.... 它实现了这个目标,但是对于一个带有单个key的checksig而言,,这太过分了,我们不需要移动到alt堆栈,我们可以交换两个输入,因为我们知道你只需消耗一个输入。

对于策略语言中的每一个构造,我们现在都有两个版本:E版本和W版本,以及示例调用覆盖版本的阈值参数。

我们可以做的另一个改进,是认识到顶部的整个脚本必须总是成功的。这是根据定义的,因为它只在你真正满足它时运行。当你有一些事情总是成功的时候,我们可以找到更有效的代码版本。

“C”调用约定总是中止或成功。例如,如果你有一个位于策略顶部的AND,那么你可以将第一个参数编译为V版本,而将第二个版本编译为再次位于顶部的版本。这再次允许你进行更多优化。结果是明显短了一些。

编译or() aor()的不同方式

记住,我告诉过你,策略语言没有描述验证内容的实际编码。你把什么放在堆栈上,我们之后再讲。编写or()语句的方法很多,我之前向你展示的是一个并行程序,或者在其中运行这两个程序,然后进行布尔组合。但这有一个缺点,那就是你必须为两者提供输入,即使知道只有两者中的一个会成功。

13

有一种替代方法叫级联或(cascade OR)。你运行A,如果它返回为true,则返回1,否则运行B。这意味着如果A要成功,则不再需要为B提供输入。

还有一种转换,你可以引入一个额外的论点(一个额外的验证内容),它会告诉你A或B是否会成功,然后你就只能提供一个。

你可以进一步使用条件转换,如果你说整个表达式会成功。如果它不会成功,那么我无论如何也不会给你输入。

我试图避免延展性。但问题是,如果你看一下转换或者,有两种方法可以让它不成功。我可以告诉你,A是一个不会成功的输入,然后给你一个不满足A的输入,或者我可以告诉你B,然后给你一个不满足B的输入。这会导致延展性,因为假设很容易找到不满足某些条件的输入。为了解决这个问题,我们需要引入另一个调用约定,它将1放在堆栈上,但在任何情况下都不能产生0。

还有一个步骤是,如果在两个分支中都有一个 if-then-else和一个checksig操作符,该怎么办?如果我们能把CHECKSIG移走就好了。为了解决这个问题,还有另一个调用条件,在这个条件下,我们不立即计算CHECKSIG,而是在堆栈上留下一个公钥,我们希望用它来进行签名。通过这种约定,我们可在这里和那里优化一些额外的操作码。

这些事情之所以重要,是因为不同的脚本,在满意和不满意感有多大以及脚本有多大之间有不同的权衡。有时,你可以找到需较少输入的较长脚本,等等。根据执行某个操作的概率或成功的概率,你可能需要选择不同的脚本。

Miniscript

如果我们看看所有这些事情,我们最终会为我们提出的所有策略,得出6个调用约定。我们提出了58种语义和调用约定的组合,包括14种表示OR的方法。我们修剪了许多。而结果,就是我们称之为Miniscript的脚本子集。

14

它似乎是合理有效的。特别是,我们发现使用它的脚本比我们在Blockstream Liquid中使用的手写脚本要好一些。我不知道我们是否已采用了它。

当然,它有一些限制。由于我们的可组合性构造,某些操作码实际上不可用。特别是DEPTH,它告诉你堆栈有多大。很明显,当你编写东西时,它的语义变化太大了。有可能存在使用DEPTH的更有效脚本,但我们并没有找到它。此外,我们没有有效的方法来进行CSE操作(常见子表达式消除)。

我们还发现了一种有效的算法,它可以在给定策略的情况下找到最有效的脚本。它是嵌套OR数量的指数。实际上,这是非常有用的。

Demo

http://bitcoin.sipa.be/miniscript/miniscript.html

我们也有一个停滞的实现。在编写这个编译程序时,我和Andrew Poelstra之间进行了一场比赛。我们试图竞争谁可以找到更有效的一个。最后,我们发现,我们的结构有重大缺陷,并最终解决了它,那是好的。

实际使用

到目前为止,我只讨论了如何构造一个由某些策略组成的脚本。但我最初的目标更雄心勃勃:我们如何才能为不同的策略,设计不同的工具来进行交互?为了解释这一点,我将从Andrew Chow的bip174部分签名比特币交易开始。它是一种带有元数据的密钥格式,其中包含了你需要了解如何签署交易的所有信息,如果需要,它将告诉你reedemScript(赎回脚本)。你可以对密钥派生自的bip32 hd路径进行编码,以便硬件钱包可以实时获取它们,等等。

15

16

有6个不同的角色与PSBT实现相关。有一个创建交易模板的创建者,一个填充信息的更新程序,可访问私钥并可生成签名的签名者,一个组合它们的合并者(combiner),一个生成完整交易的终结器(finalizer)和提取器(extractor)。在实践中,其中许多角色同时发生。

在典型的PSBT工作流中,只有更新程序和终结器需要了解有关输入脚本的任何信息。这有点令人惊讶,但它确实有意义,因为签署合约时,你通常关心钱的去向,而不是钱是否已是你的了。最坏的情况是,它不是你的,你的签名无效。

一个显而易见的思考方式是,好吧,我们有一个很好的注释策略表。所以我们可以把它存储在PSBT的策略记录中,然后其他人就可以实际签署了。实际上,这似乎不是必需的,因为不仅我们的策略语言是可组合的,而且脚本的子集也是可组合的。这意味着你实际上可以有一个非常简单的解析器,你可以在给定的脚本中编写它,而它将再次告诉你策略。这意味着我们甚至不需要向PSBT添加任何元数据来实现与它的集成。这是一种人为的限制,我们可以非常多地为此提出一个扩展,但总的来说,我们似乎不会通过使用一个不可解析或不易解析的脚本,来获得那么多的好处。

交互

我们如何想象交互工作?可以很容易地将脚本解析回带注释的策略,从中可以确定哪些密钥子集可以签名。你可了解策略的作用,并且可验证一个策略与另一个策略的组合,并且组合总是合理的。你可以通过签名,组装完全签名的交易。

17

这是从一个关于比特币库签名者逻辑的讨论中得出的。我们想知道如何进行签名,有人非常反对基于模板的签名,你说,这些是我们支持的几种类型的脚本,只有这些是受支持的脚本。所以我们用这种迂回的方式来设计这个东西,最后我们决定,描述脚本结构的语言可以是脚本本身,只要它在这个我们称为Miniscript的子集当中。

结论

最后我想说的是,比特币脚本对简单的策略非常有用,但它也有相比当前更多的可能用途。这在一定程度上是因为很难让事物很好地相互作用。这种交互可能随着时间的推移,会让诸如闪电网络客户端和多重签名客户端这类东西,在不需要阅读所有协议工作的情况下进行琐碎的交互。关于这些内容的细节,我不是很在行。

18

我们可以定义脚本的一个子集,它可以被通用地签名,可以很容易地构造,也可以很容易地分析,并且只要我们没有通用的子表达式,它对于很多用例而言都是相当有效的。

未来的工作

将来,我们需要对编译器和解析器的实现进行开源。现在,演示中的javascript编译器不是您想要看到的。此外,我们希望在通用钱包软件中集成通用签名者,以便我们可立即支持PSBT或其他。还有一些进一步的优化,可以用来改进这一点。我们使用了一种相对详尽的方法来查找所有的结构,但是在脚本中,可能有很多我们不使用的东西。

19

我们可以利用这里学到的经验来改进…只有当一个OP_ROLL_BACKWARD操作朝另一个方向滚动时,可以预先计算一个将被多次使用的值,然后重新插入堆栈。将来我们还会对比特币脚本进行改进,比如TapRoot和Mast,它们都可直接重用策略语言。注释会有所不同,但这也会使升级更容易。你现在可编写策略,而在将来的脚本版本中,它将以不同的方式进行翻译。

谢谢大家。

免责声明:

1.本文内容综合整理自互联网,观点仅代表作者本人,不代表本站立场。

2.资讯内容不构成投资建议,投资者应独立决策并自行承担风险。

你可能感兴趣

    error