【译】From Smart Contracts to Courts with not so Smart Judges

以太坊通常被描述为自我实施智能合约的平台。 虽然这确实是正确的,但本文认为,特别是当涉及更复杂的系统时,它更像是一个拥有聪明的律师的法院和一个不那么聪明的法官,或者更正式的法官,他是一个计算资源有限的法官。 稍后我们将看到如何利用这种视图来编写非常有效的智能合约系统,以便能够以几乎免费的方式实现交叉链代币转移或计算工作证明等计算。

法庭类比

首先,您可能知道以太坊上的智能合约本身无法从外部世界检索信息。 它只能要求外部参与者代表其提供信息。 即便如此,它要么必须信任外部参与者,要么验证信息本身的完整性。 在法庭上,法官通常会向专家询问他们的意见(他们通常信任的人)或证人的证词,这些证词通常经过交叉核对验证。

我猜很明显,由于气体限制,以太坊中的判断者的计算资源受到限制,与来自外部世界的律师的计算能力相比,这种限制相当低。 然而,以这种方式受到限制的法官仍然可以决定非常复杂的法律案件:她的权力来自于她可以扮演辩护人对抗检察官这一事实。

复杂性理论

这个确切的类比在Feige,Shamir和Tennenholtz, Noisy Oracle Problem的一篇文章中正式化。 他们主要结果的一个非常简化的版本如下:假设我们有一个合同(判断)谁可以使用N个步骤来执行计算(可能分布在多个事务中)。 有几个外部演员(律师)可以帮助法官,其中至少有一个是诚实的(即至少有一个演员遵循既定的协议,其他人可能是恶意的并发送任意信息),但法官不知道谁诚实的演员是。 这样的合同可以在没有外界帮助的情况下执行可以使用N个存储器单元和任意数量的步骤执行的任何计算。 (正式版本声明多项式时间验证器可以接受此模型中的所有PSPACE)

这可能听起来有点笨拙,但他们的证明实际上非常有启发性,并且使用PSPACE的类比是可以通过“游戏”解决的一类问题。 举个例子,让我告诉你一个以太坊合约如何下棋几乎没有汽油费用(专家可以原谅我使用NEXPTIME完成的国际象棋,但我们将在这里使用经典的8x8变体,所以它实际上是在PSPACE ... ):在这种情况下下棋意味着一些外部演员提出国际象棋位置,合同必须确定该位置是否是白色的获胜位置,即白色总是获胜,假设白色和黑色是无限聪明的。 这假设诚实的脱链演员有足够的计算能力来完美地下棋,但是......所以任务不是与外部演员下棋,而是确定给定位置是否是白棋的获胜位置并且询问外部演员(除了其中一个可能误导,给出错误的答案)以寻求帮助。 我希望你同意在没有外界帮助的情况下这样做非常复杂。 为简单起见,我们只看一下我们有两个外部演员A和B的情况。这是合同的作用:

  1. 询问A和B这是否是白色的获胜位置。 如果双方都同意,这就是答案(至少有一个是诚实的)。
  2. 如果他们不同意,请问那个回答“是”的人(我们将从现在开始称这个演员为W,另一个为B)以获得白色的胜利。
  3. 如果移动无效(例如因为无法移动),则黑棋获胜
  4. 否则,将移动应用到董事会并要求B获得黑色的胜利行动(因为B声称黑色可以获胜)
  5. 如果移动无效(例如因为无法移动),则白棋获胜
  6. 否则,将移动应用到棋盘上,向A请求获胜白棋,继续3。

合同并不需要对国际象棋战略有任何线索。 它只需要能够验证单个移动是否有效。 因此合同的成本大致为N*(V+U) ,其中N是移动的数量(实际上是ply),V是验证移动的成本,U是更新电路板的成本。

这个结果实际上可以改进为N*U + V ,因为我们不需要验证每一个动作。 我们可以更新电路板(假设移动是通过坐标给出的),当我们要求下一步时,我们也会询问前一步是否无效。 如果回答为“是”,我们检查移动。 根据移动是否有效,其中一名球员被骗,我们知道谁获胜。

家庭作业:改进合同,以便我们只需存储移动序列并仅为移动的一小部分更新电路板,并仅针对单个移动执行移动验证,即将成本带到N*M + tiny(N)*U + V ,其中M是存储移动的成本,而tiny是一个适当的函数,它返回N的“微小部分”。

在旁注中, Babai,Fortnow和Lund表明,律师正在合作但不能相互沟通的模型和法官被允许掷骰子(两个变化都很重要)捕获了一个据称更大的类NEXPTIME,非确定性指数时间。

将加密经济学添加到游戏中

从前一节要记住的一点是,假设交易没有受到审查,合同将始终找出诚实的人和不诚实的演员是谁。 这导致了一个有趣的观察,我们现在有一个相当便宜的交互协议来解决难题,但我们可以添加一个加密经济机制,确保几乎不必执行该协议:该机制允许任何人提交结果与保证金一起计算。 任何人都可以挑战结果,但也必须提供押金。 如果存在至少一个挑战者,则执行交互协议(或其多证明者变体)。 假设在提议者和挑战者中至少有一个诚实的行为者,那些不诚实的行为者将被揭露,而诚实的行为者将获得存款(减去一定百分比,这将阻止一个不诚实的提议者自我挑战)作为奖励。 因此,最终的结果是,只要至少有一个诚实的人正在观察谁没有受到审查,恶意行为者就没有办法成功,甚至尝试对恶意行为者来说也是代价高昂的。

想要使用计算结果的应用程序可以将存款作为计算可信度的指标:如果解决方案提议者存在大量存款而且在一定时间内没有质询,则结果可能是正确的。 一旦出现挑战,应用程序应该等待协议得到解决。 我们甚至可以创建计算结果保险,承诺检查离线计算并退还用户,以防无效结果未及早提出质疑。

二元搜索的力量

在接下来的两节中,我将给出两个具体的例子。 一个是交互式地验证外部区块链中数据的存在,第二个是关于验证一般(确定性)计算。 在它们两者中,我们经常会遇到这样的情况:提议者有一个很长的值列表(由于它的长度而不能直接用于合同),它以正确的值开头但以不正确的值结束(因为提议者想欺骗)。 合同可以很容易地计算第i个(i + 1)st值,但检查完整列表会太昂贵。 挑战者知道正确的列表,并可以要求提议者从该列表中提供多个值。 由于第一个值是正确的而且最后一个是不正确的,所以在这个列表中必须至少有一个点i,其中第i个值是正确的并且(i + 1)st值是不正确的,并且挑战者的任务是找到这个位置(让我们称这一点为“转换点”),因为那时合同可以检查它。

让我们假设列表的长度为1.000.000,因此我们的搜索范围为1到1.000.000。 挑战者询问位置500.000的价值。 如果正确,则至少有一个转换点在500.000和1.000.000之间。 如果不正确,则存在介于1和500.000之间的转换点。 在这两种情况下,搜索范围的长度减少了一半。 我们现在重复这个过程,直到我们达到大小为2的搜索范围,这必须是转换点。 基数2的对数可用于计算“迭代二分”所采用的步数。 在1.000.000的情况下,这些是log1.000.000≈20步。

廉价的跨链转移

作为第一个真实世界的例子,我想展示如何设计一个极其便宜的交叉链状态或支付验证。 由于区块链不是确定性的,但可以分叉,这有点复杂,但总体思路是一样的。

提议者提交她想要在目标合同中可用的数据(例如比特币或狗狗币交易,另一个以太坊链中的状态值,或者Merkle-DAG中的任何东西,其根哈希包含在区块链的块头中并且是公知的(这是非常重要的))以及块号,该块头的散列和存款。

请注意,我们只提交一个块号和哈希值。 在BTCRelay的第一个版本中,目前需要提交所有比特币块头,并验证所有比特币的工作证明。 该协议仅在发生攻击时才需要该信息。

如果一切正常,即外部验证器检查块编号的散列是否与规范链匹配(并且可选地具有一些确认)并查看该块中包含的事务/数据,则提议者可以请求返回存款和交叉 - 链转移完成。 这就是非攻击案件中的全部内容。 这应该每次转移耗费约20万个气体。

如果出现问题,即我们要么有恶意提议者/提交者或恶意挑战者,那么挑战者现在有两种可能:

  1. 声明块哈希无效(因为它不存在或者是废弃的fork的一部分)或
  2. 声明Merkle-hashed数据无效(但是块散列和数字有效)

注意,区块链是由两个“臂”组成的Merkle-DAG:一个形成块头链,一个形成状态或事务的Merkle-DAG。 一旦我们接受根(当前块头哈希)有效,双臂中的验证就是简单的Merkle-DAG证明。

(2)因此,让我们先考虑第二种情况,因为它更简单:因为我们希望尽可能高效,所以我们不要求提议者提供完整的Merkle-DAG证明。 相反,我们只是请求从根到数据的DAG路径(即一系列子索引)。

如果路径太长或索引无效,则挑战者会询问提议者超出范围的点的父值和子值,并且提议者无法提供散列到父级的有效数据。 否则,我们的情况是根哈希是正确的,但某些点的哈希是不同的。 使用二进制搜索,我们在路径中找到一个点,我们在一个不正确的哈希上方有一个正确的哈希值。 提议者将无法提供散列到正确散列的子值,因此合同可以检测到欺诈。

(1)现在让我们考虑提议者使用无效块或作为废弃叉子一部分的块的情况。 让我们假设我们有一种机制将其他区块链的区块编号与以太坊区块链上的时间相关联,因此合同有一种方法可以告诉区块编号无效,因为它必须在将来存在。 提议者现在必须提供所有块头(比特币只有80个字节,如果它们太大,仅从哈希开始)直到合同已经知道的某个检查点(或者挑战者以块的形式请求它们)。 挑战者必须做同样的事情,并希望提供一个具有更高的区块数/总难度的区块。 现在两人都可以交叉检查他们的积木。 如果有人发现错误,他们可以将块号提交给合同,该合同可以检查或让其由另一个交互阶段验证。

一般计算的特定交互式证明

假设我们有一个尊重局部性的计算模型,即它只能在一个步骤中对内存进行局部修改。 图灵机尊重局部性,但随机访问机器(通常的计算机)如果只在每一步中修改内存中的恒定点数,也可以。 此外,假设我们有一个H位输出的安全散列函数。 如果在这样的机器上进行计算需要t步并且使用最多s个字节的内存/状态,那么我们可以在以太网中执行关于log(t)+ 2 * log的此计算的交互式验证(在提议器/挑战者模型中) (log(s))+ 2轮,其中每轮中的消息不长于max(log(t),H + k + log(s)),其中k是“程序计数器”的大小,寄存器,磁头位置或类似的内部状态。 除了将消息存储在存储器中之外,合同还需要执行机器的最多一步或散列函数的一次评估。

证明:

该想法是计算(至少在请求时)每个步骤中计算所使用的所有存储器的Merkle树。 合同很容易验证单步对存储器的影响,并且由于只能访问存储器中的恒定点数,因此可以使用Merkle-proofs验证存储器的一致性。

在不失一般性的情况下,我们假设每一步都只访问内存中的一个点。 协议从提交者提交输入和输出开始。 挑战者现在可以针对各种时间步骤i请求存储器的Merkle树根,内部状态/程序计数器以及访问存储器的位置。 挑战者使用它来执行二进制搜索,该步骤导致步骤i,其中返回的信息是正确的但在步骤i + 1中是不正确的。这需要最多log(t)轮和大小log(t)的消息。 H + k + log(s)。

挑战者现在请求访问(在步骤之前和之后)的内存中的值以及沿着根路径的所有兄弟(即Merkle证明)。 请注意,兄弟姐妹在步骤之前和之后是相同的,只有数据本身发生了变化。 使用此信息,合同可以检查步骤是否正确执行并且根哈希是否正确更新。 如果合同验证Merkle证明有效,则输入内存数据必须正确(因为散列函数是安全的,并且提议者和挑战者都具有相同的预根哈希)。 如果步骤执行被验证正确,则它们的输出存储器数据相等。 由于Merkle树的兄弟姐妹是相同的,找到不同的后根哈希的唯一方法是计算或Merkle证明有错误。

注意,前一段中描述的步骤进行了一轮并且消息大小为(H + 1)log(s)。 所以我们总共有log(t)+ 1轮和消息大小max(log(t),k +(H + 2)log(s))。 此外,合同需要计算散列函数2 * log(s)次。 如果s很大或散列函数很复杂,我们可以稍微减小消息的大小,并且只需要以更多交互为代价来达到散列函数的单个应用程序。 我们的想法是在Merkle证明上执行二进制搜索,如下所示:

我们不要求提议者发送完整的Merkle证明,而只发送内存中的pre-post值和post值。 合同可以检查停止的执行,所以让我们假设转换是正确的(包括步骤i + 1中的内部post状态和内存访问索引)。 剩下的案例是:

  1. 提议者提供了错误的预先数据
  2. 前后数据是正确的,但后记忆的Merkle根是错误的

在第一种情况下,挑战者在从包含存储器数据的Merkle树叶到根的路径上执行交互式二进制搜索,并找到具有正确父但错误子的位置。 这最多需要log(log(s))轮次和大小log(log(s))的消息。 H位。 最后,由于散列函数是安全的,因此提议者不能为散列到父节点的错误子节点提供兄弟节点。 这可以通过合同检查哈希函数的单个评估。

在第二种情况下,我们处于倒置状态:根是错误的,但叶子是正确的。 挑战者再次在最多log(log(s(n))轮中执行交互式二进制搜索,其中消息大小为log(log(s))resp。 H位并在树中找到父P错误但子C正确的位置。 挑战者向提议者询问兄弟S,使得(C,S)散列到P,合同可以检查。 由于我们知道只有存储器中的给定位置可以随着步骤的执行而改变,所以S也必须出现在步骤之前的存储器的Merkle树中的相同位置。 此外,提议者为S提供的值不能正确,因为那时,(C,S)不会哈希到P(我们知道P是错误的,但C和S是正确的)。 因此,我们将其减少到提议者在pre-Merkle-tree中提供了错误节点但是正确的根哈希的情况。 如第一种情况所示,这最多需要log(log(s))轮次和大小log(log(s))的消息。 H位验证。

总的来说,我们最多有log(t)+ 1 + 2 * log(log(s))+ 1轮,消息大小最多为max(log(t),H + k + log(s))。

家庭作业:将此证明转换为可用于EVM或TinyRAM(以及C)程序的工作合同,并将其集成到Piper Merriam的以太坊计算市场中

感谢Vitalik建议使用Merkle-hash内存以允许任意的步进内存大小! 顺便说一句,这很可能不是一个新结果。

在实践中

这些对数很好,但这在实践中意味着什么? 让我们假设我们在使用5 GB RAM的4 GHz计算机上进行5秒的计算。 简化实际时钟速率与人工架构上的步骤之间的关系,我们大致有t =20000000000≈243和s =5000000000≈232。 交互式验证这样的计算应该采用43 + 2 + 2 * 5 = 55轮,即2 * 55 = 110块并使用大约128字节的消息(主要取决于k,即架构)。 如果我们不以交互方式验证Merkle证明,我们得到44轮(88块)和大小为1200字节的消息(只有最后一条消息那么大)。

如果你说110块(在以太坊上大约30分钟,3比特币上的确认)听起来很多,不要忘记我们在这里谈论的内容:4 GHz机器上实际使用5 GB RAM的5秒钟。 如果您经常运行具有如此强大功能的程序,它们会搜索满足特定条件的特定输入值(优化例程,密码破解程序,工作解算器证明......)。 由于我们只想验证计算,因此不需要以这种方式执行搜索,我们可以从头开始提供解决方案,只检查条件。

好吧,对于每个计算步骤计算和更新Merkle树应该是非常昂贵的,但是这个例子应该只显示这个协议在链上的扩展程度。 此外,大多数计算,特别是在函数式语言中,可以细分为我们称之为昂贵函数的级别,这些函数使用大量内存但输出的数量很少。 我们可以将此功能视为主协议中的一个步骤,并在该功能中检测到错误时启动新的交互协议。 最后,正如已经说过的那样:在大多数情况下,我们只是验证输出并且从不挑战它(只有那时我们才需要计算Merkle树),因为提议者几乎肯定会失去他们的存款。

打开问题

在本文的几个地方,我们假设我们只有两个外部参与者,其中至少有一个是诚实的。 我们可以通过要求提议者和挑战者的存款来接近这个假设。 一个问题是其中一个可能只是拒绝继续协议,所以我们需要超时。 另一方面,如果我们增加超时,恶意行为者可能会使区块链与不相关的交易饱和,希望答案不会及时成为一个块。 合同是否有可能检测到这种情况并延长超时? 此外,诚实的提议者可能会被阻止离开网络。 正因为如此(并且因为比恶意演员更诚实),我们可能允许任何人在存款后进入(双方)。 同样,如果我们允许这样做,恶意行为者可以介入“诚实”的一方,只是假装诚实。 这听起来有点复杂,但我相信它最终会成功。

https://blog.ethereum.org/2016/02/17/smart-contracts-courts-not-smart-judges/

猜你喜欢

转载自blog.csdn.net/omnispace/article/details/81283556