当然,对于保管链的证明是没有用的,除非它也可以传递给主链以保证资金的安全。链上接受证明的机制是Plasma安全模型的核心,被称为“退出博弈”。
当用户想把他们的钱从Plasma链上转移出去时,这会打开一个争议期。在争议期结束时,如果没有未解决的争议,款项将从主链上的Plasma合同发送给退出者。在争议期间,用户可以提交“挑战”,声称退出的资金不属于退出的人。上面描述的证据保证对这些挑战的“反应”总是可以计算的。
退出游戏的目标是确保资金的安全,即使是在一个最大的对手的情况下。特别是,我们必须缓解三种主要攻击:
-
数据预扣:操作员可以向智能合约公开根哈希,但不能告诉任何人关于块的内容。
-
伪造/无效的交易:操作员可以在一个块中包括一个交易,该块的发送者不是监管链中的前一个接收者。
-
审查:在某人存入资金后,操作员可以拒绝发布任何汇款交易。
在所有这些情况下,退出游戏的挑战/响应协议确保这些行为不允许盗窃,最多1个挑战后1个响应。
跟踪存款与退出
存款映射
每次存放一组新硬币时,合同都会更新一个映射,每个映射都包含一个存款结构。从合同中:
请注意,此结构既不包含存款的untypedEnd或tokenType。那是因为合同使用这些值作为映射映射中的键。例如,访问给定存款的存款人的访问如下所示:someDepositer:address = self.deposits [tokenType] [untypedEnd] .depositer
这一选择节省了一点gas,也使一些代码更干净,因为我们不需要存储任何类型的存款ID来引用存款。
现有的范围映射
除了在每次有存款时添加self.deposit s条目之外,合同还需要以某种方式跟踪历史出口,以防止同一范围内的多个出口。这有点棘手,因为出口不会像存款那样按顺序出现,而且搜索出口列表会很昂贵。
我们的合约实现了一个常量大小的解决方案,它存储一个可存在范围的列表,并在出现新的出口时更新该列表。从智能合约:
同样,我们使用带有tokenType和untypedEnd键的双嵌套映射,以便我们可以调用self.exitable [tokenType] [untpyedEnd] .untypedStart来访问范围的开头。请注意,对于所有未设置的映射键,Vyper返回0,因此我们需要一个isSet bool,以便用户不会通过传递未设置的exitableRange来“欺骗”合同。
合约的self.exitable范围基于通过名为removeFromExitable的帮助函数成功调用finalizeExit而被拆分和删除。请注意,先前退出的范围的退出甚至不需要受到挑战; 他们永远不会通过在finalizeExit中调用的checkRangeExitable测试。
退出游戏与Plasma cash的关系
从本质上讲,我们规范中的退出游戏与原始的Plasma Cash设计非常相似。退出是通过调用该函数启动的
beginExit(tokenType: uint256, blockNumber: uint256, untypedStart: uint256, untypedEnd: uint256) -> uint256:
为了对退出提出异议,所有挑战都指定了一个特定的coinID,并对该特定硬币进行了Plasm cash挑战游戏。只需要证明一枚硬币无效即可取消整个退出。
退出和两种类型的应对挑战都有一个exitid和challengeid,通过递增的challengenonce和exitnonce按顺序分配。
Blocknumber事务指定
在最初的Plasma cash规范中,exiter需要指定退出的交易及其以前的“父”交易,以防止“in-flight”攻击,其中操作员延迟包括有效的交易,并在两个交易之间插入一个无效的交易。
这给基于范围的方案带来了一个问题,因为一个事务可能有多个父级。如果Alice向Carol发送(0,50),并且Bob向Carol发送(50,100),Carol现在可以向Dave发送(0,100)。但是,如果Dave想要退出,则两者(0父母是父母。[50]和(50,100)。
虽然指定多个父级是绝对可行的,但是这个规范将非常昂贵,而且实现起来似乎更复杂。因此,我们选择了一个更简单的替代方案,其中每个事务都指定其发送者希望它进入的“块”,如果包含在不同的块中,则会失效。这解决了”in-flight”的攻击,意味着合同不需要事务的父级。
每个币交易有效性
我们的退出游戏的一个非必然属性值得预先注意的是,某些交易可能对其范围内的一些硬币“有效”,但对其他硬币无效。
例如,假设Alice向Bob发送(0,100),Bob又向Carol发送(50,100).Carol不需要验证Alice是完整的合法所有者(0,100)。仅限Carol需要保证爱丽丝拥有(50,100) - 托管链的一部分适用于她的收据。虽然如果爱丽丝不拥有(0,50),交易可能在某种意义上是“无效的”,智能合约对于硬币出口的争议而言,并不关心这一点(50,100)。只要收到的硬币的所有权得到核实,其余的交易都无关紧要。
这是一个非常重要的要求,以保持轻客户端证据的大小。如果Carol必须检查完整(0,100),她可能还必须检查(0,10000)的重叠父项,然后检查其所有父项,依此类推。如果交易相互依赖,这种“级联”效应可能会大大增加证据的大小。
请注意,此属性也适用于描述要交换的多个范围的原子多重性。如果Alice为Bob的1 DAI交易1 ETH,则Alice有责任在签名之前检查Bob是否拥有1 Dai。然而,之后,如果Bob然后将1 ETH发送给Carol,Carol无需验证Bob拥有1 DAI,只有Alice拥有她发送给Bob的1 ETH。Alice冒了风险,所以Carol不必这样做。
从智能合约的角度来看,这个属性是在出口处始终为特定coinID提交的挑战的直接结果。
合同如何处理交易检查
请注意,要在退出游戏中使用,Transactions必须通过上面的证明部分中描述的TransactionProof检查(有效签名,分支边界等)。此检查在函数的合同级别执行
这里一个重要的注意事项是transferIndex参数。请记住,事务可能包含多个传输,并且必须在树中包含一次以进行每次传输。但是,由于挑战涉及特定的coinID,因此只有一次转移是相关的。因此,挑战者和响应者给出了transferIndex - 无论哪个转移与有争议的硬币有关。该检查解码并检查TransactionProof中的所有TransferProofs,然后检查每个函数的包含:
一旦验证了所有的transferProof,transferIndexth传输的争议相关值将返回到exit game函数:即sender,recipient,typedStart,typedEnd和plasmaBlockNumber。
在这种情况下,我们可以为退出指定完整的挑战/响应游戏集。
立即取消退出的挑战
有两种挑战会立即取消退出:一种是已用过硬币退出,另一种是存款发生前退出。
花费硬币挑战
这个挑战用来证明交易的存在者已经将硬币发送给了其他人。
它使用checkTransactionProof和gettypedTransfer,然后检查以下内容:
-
受挑战的coinID位于指定的退出内。
-
受挑战的coinID位于transaction.transfers的transferIndexth元素的typedStart和typedEnd中。
-
挑战的plasmaBlockNumber大于退出的plasmaBlockNumber。
-
transfer.sender是exiter。
原子交换的引入确实意味着一件事:用过的硬币挑战期必须严格地小于其他因素,因为边缘情况下操作员在两方或多方之间保留原子交换。在这种情况下,这些当事人必须退出他们预先交换的硬币,迫使操作员进行旧硬币挑战并揭示是否包括交换。但是,如果我们允许操作员在最后一刻做到这一点,那将成为一种竞争条件,在这种情况下,各方没有时间使用显示来取消其他出口。因此,超时比常规挑战窗口更短(1/2),消除了“最后一分钟响应”攻击。
存款挑战之前
这个挑战用于证明退出来自早于的plasmaBlockNumber,而不是实际存入的硬币。
合同查找self.deposits[self.exit s[exitid].tokentype][depositiontypedend].precingPlasmalbLockNumber并检查它是否晚于出口的块号。如果是这样,它就会取消。
在乐观的情况下,我们的合同允许在不进行任何包含检查的情况下退出。为了实现这一点,任何退出都可以直接通过
退出方必须直接对其退出的交易或存款作出响应。
第二种情况是,如果操作员在存款后审查了所有交易,用户就可以把钱取出来。
如果出现以下情况,两种响应都将取消挑战:
-
存款或交易确实在出口的Plasma区块编号处。
-
存款人或收款人确实是存在者。
-
退出的起点和终点都在存款或转账的起点和终点之内。
无效的历史挑战
对于普通的Plasma cash 和这个规范来说,最复杂的挑战响应游戏是历史失效的案例。协议的这一部分缓解了操作员包含伪造的“无效”事务(发送者不是前一个接收者)的攻击。这个解决方案被称为无效的历史挑战:因为合法拥有者还没有花掉他们的硬币,他们证明了这一点并提出挑战:“哦,是的,那枚硬币是你的吗?嗯,那是我以前的,你不能证明我曾经花过它。”
无效的历史挑战和响应都可以是存款或交易。
挑战性
根据当前的合法所有者,有两种方法可以挑战:
这两个都叫A
应对无效的历史挑战
挑战者确实花了他们的硬币,监管链确实是有效的。我们必须允许这种回应, 有两种:
第一种方法是通过显示挑战者支出的交易进行响应:
然后,智能合约执行以下检查:
-
transactionEncoding中的transferIndexth Transfer包含挑战的coinID。
-
transferIndexth transfer.sender确实是无效历史挑战的索赔人。
-
事务的Plasma块号位于无效的历史记录质询和退出之间。
另一种反应是证明挑战是在硬币实际存放-之前发生的,使挑战无效。这类似于Challengebeforedeposit for exits本身。
在这种情况下,由于质询无效,因此没有对作为质询收件人的发件人进行检查。因此,合同必须简单地检查:
-
存款涵盖受质疑的coinID。
-
存储Plasma块数位于挑战和退出之间。
-
如果是这样,退出将被取消。
这就结束了完整的退出游戏规范。有了这些构建块,即使是在最大程度上恶意的Plasma链的情况下,资金也可以保持安全。
本文转载公众号:区块链研究实验室
海纳学院的内容将围绕:区块链技术,产品社群,经济模型等全方位的知识体系输出,为大家带来不一样的社群学习体验。欢迎联系作者微信加入社群:csschan1120