Skip to content

Latest commit

 

History

History
622 lines (372 loc) · 68.7 KB

1_洞悉Arbitrum.md

File metadata and controls

622 lines (372 loc) · 68.7 KB

1_洞悉Arbitrum

本章是对Arbitrum的设计及其原理的深入解析。本章节并不是API文档也不是代码教程,如有需要请去其余章节查看。

为什么使用Arbitrum?

Arbitrum是以太坊的L2扩容方案,它拥有独特的特性集:

  • 无需信任的安全性:安全扎根于以太坊,任何人都可以确保正确的L2结果
  • 以太坊兼容性:所有EVM标准的合约和转账都可以在Arbitrum上执行
  • 可扩展性:将以太坊的计算和存储转移至链下,吞吐量更高
  • 最低成本:为最小化以太坊L1燃气成本而生,降低每笔交易的成本

其余的L2方案也提供了其中一部分特性,但据我们所了解的,并没有一种方案能在相同成本下提供同样的特性集。

高屋建瓴

在最基础层,Arbitrum链工作方式如图:

用户与合约将信息发送至收件箱。链将各条信息逐一读取并依次执行,将链的状态更新并产生一些输出。

若想要Arbitrum链为你执行一笔交易,你需要把该笔交易提交到收件箱中。随后Arbitrum链会发现这笔交易,然后执行,并产生输出:一笔交易的记录,以及交易所中任何的提款等内容。

交易执行是确定的——这意味着链的状态仅取决于收件箱。因此,交易的结果在你将交易提交到收件箱的那一刻就已经是确定的了。任何一个Arbitrum节点都能告知你结果。(如果你喜欢也可以自己运行一个节点。)

本文档中的所有的技术细节都与这个模型有关。从此模型谈起直到Arbitrum的完整论述,我们需要回答如下问题:

  • 谁负责追踪收件箱、链状态、输出?
  • Arbitrum如何确保链状态和输出是正确的?
  • 以太坊用户、合约如何与Arbitrum互动?
  • Arbitrum是如何支持兼容以太坊的合约与转账的?
  • 以太币和代币是如何转入转出Arbitrum链的?在Arbitrum链上是如何对其进行管理的?
  • 如何运行我自己的Arbitrum节点或成为一个验证者?

乐观式Rollup

Arbitrum是一种乐观式Rollup。我们来解读一下这个词汇。

/Rollup/ Arbitrum是一种rollup,这意味着对Arbitrum链的输入也即收件箱中收到的信息,全部都以calldata的形式被记录在以太坊上。每个人都能查看收件箱的完整历史,而链状态又完全取决于收件箱历史。因此,仅凭借公开信息就可以决定当前正确的链状态的信息。如有必要,还可重建整个链状态。

任何人都可以成为Arbitrum协议的完整参与者,运行自己的Arbitrum节点或者成为一名验证者。链的历史或状态是完全透明的。

/乐观式/ Arbitrum是乐观的,通过验证者发布rollup区块以及任何人都可以对其进行挑战的方式,Arbitrum将链状态不断向前推进。如果挑战期(大约为一周)结束后仍没有对rollup区块提出挑战,Arbitrum就会将该rollup区块认定为正确的。如果在挑战期内有人提出挑战,Arbitrum会使用一个高效率的争议解决协议(下述)来判定谁是作恶者。作恶者的质押资金将被没收,其中一部分将奖励给诚信者(另一部分被销毁,以保证即使有共谋发生作恶者仍会被惩罚)。

交互式证明

在乐观式rollup中,最重要的设计是如何解决争议。假设Alice断言链上会产生某个特定的结果,而Bob不认同。那么仲裁协议应该接受谁的提议呢?

基本上,有两种方案:交互式证明、再执行交易。Arbitrum使用交互式证明,我们认为这种方式是更有效率更灵活的。大部分Arbitrum的设计都遵循此观点。

交互式证明

在交互式证明的范式中,Alice和Bob会在一个往返驳诘式的协议中进行交互,以最大限度减少L1上仲裁人锁需要的工作量。仲裁人为L1上的合约。

Arbitrum的解决方案基于争议的分解。如果Alice的断言包含的行为有N步,Alice会将其分解为两个N/2长度的断言,Bob则需要选择N/2步长度的断言来挑战。该过程不断继续,每次将争议范围减半,直至找到它们所争议的那一步行为。L1仲裁在范围缩窄到单步时才会介入,去检查该行为到底做了什么以及Alice的断言是否正确。

交互式证明的核心思想是,若Alice和Bob产生争议,应该尽可能将大部分工作量置于链下解决,而不是把所有工作量都放到L1的仲裁合约上。

再执行交易

交互式证明的替代方案是让每一个rollup区块中在每一条交易执行后都更新一个断言状态哈希。在有争议的情况下,L1裁判会对整个交易进行审查,来确定是否满足Alice的断言。

为什么交互式证明更好

基于下列理由,我们坚定认为交互式证明是更好的选择。

*在乐观式情况下更加有效率:*由于交互式证明能够解决多于一笔的争议,它能够让rollup区块只包含一个所有行为执行完毕后的末状态哈希。相反,再执行则需要在rollup区块中为每一笔交易都附上一个状态声明。在一个rollup区块中有成千上百笔交易,所以在L1燃气经济性上有显著不同——而L1燃气则是成本的主要来源。

*在悲观式情况下更加有效率:*在有争议情况下,交互式证明需要L1仲裁合约只需要检查Alice和Bob的切分步骤行为是正确的,例如Alice确实将自己的断言二等分了。(在接触到最终步骤之前,仲裁并不需要判定Alice的断言是否正确,Bob在链下做了这个工作。)只有一步交易需要再执行。相反,再执行交易范式则需要L1仲裁去评估整个的交易历史。

*每笔交易更高的gas limit:*交互式证明可以摆脱以太坊每笔交易的比较紧张的gas limit;一笔需要花费大量gas的交易在以太坊上无法实现,但在Arbitrum上却是有可能的。显然,gas limit不可能是无限的,但还是可以比以太坊上大很多。考虑到以太坊的话,在Arbitrum上高gas交易的尾翼缺点是,可能需要稍多一点的交互式证明步骤(而且还是在存在欺诈的情况下)。而对于再执行交易范式,其gas limit必须低于以太坊,因为那样才有可能对在单笔以太坊交易中对其包含的所有交易的执行进行检查(比直接执行交易更贵)。

*合约大小不受限制:*交互式证明不需要为每一个L2合约部署一个对应的以太坊合约,所以也不需要遵循以太坊所规定的合约大小限制。只要有Arbitrum的可仲裁机制,在L2 上部署一个合约就与进行其他计算是一样的。相反,再执行交易范式需要币以太坊更小的合约大小限制,因为他们需要检验该合约,而能够检验该合约的代码则必须能装进以太坊合约中。

*更多实现上的灵活性:*交互式证明提供了实现上更多的灵活性。例如,增加EVM中没有的指令。而这仅需要通过在以太坊上验证一个单步证明即可。相反,再执行交易范式则要受限于EVM。

交互式证明驱动着Arbitrum的设计

Arbitrum的许多设计都是由交互式证明所带来的可能所驱动的。如果你看到一些Arbitrum的特性并疑惑为什么会这样,那么请记住两个非常好的问题:『这个特性是怎样支持交互式证明的?』和『它是怎样利用交互式证明的?』你在Arbitrum中大部分的问题都与交互式证明相关。

Arbitrum架构

下图展示了Arbitrum的基础架构。

左侧是用户以及用户选择的连接到区块链的服务提供者。右侧是构建于以太坊之上的Arbitrum系统。

我们先讲一讲右侧的Arbitrum栈是如何工作的,然后再来讲一讲当左边用户连接到它时发生了什么。

最下面是我们的老朋友以太坊。Arbitrum构建于以太坊之上并继承了其安全性。

在以太坊之上是EthBridge,它由管理Arbitrum链的一系列以太坊上的合约组成。EthBridge仲裁Arbitrum rollup协议,以保证L2运行的正确性。(更多关于rollup协议请见下方的Rollup协议分区。)EthBridge还维护着链的收件箱和发件箱,让用户与合约能够将交易信息提交给L2并观察这些交易的输出。用户,L1合约,Arbitrum节点,调用EthBridge的一系列合约来与Arbitrum链交互。

EthBridge上方的水平线是AVM,EthBridge就是通过这里与上层交流的。AVM可以执行程序来读取输入并产生输出。这里是Arbitrum最重要的接口,因为它区分开了L1和L2——具体分为抽象为收件箱/执行/发件箱/L1与使用该抽象模型的L2.

再上一层是ArbOS,由Offchain Labs开发的软件,负责维护记录,交易管理,以及对智能合约监管。之所以叫ArbOS是因为它就像电脑或手机的操作系统(轻量级的)一样,它先启动然后再管理链上的其他代码。重要的一点是,ArbOS运行于L2上而非以太坊上,所以非常好地利用了扩容和L2低成本的运算。

ArbOS之上的水平层叫做EVM兼容层,因为ArbOS为智能合约提供了兼容以太坊虚拟机的执行环境。也就是说,你可以向ArbOS发送合约的EVM代码,像在以太坊上部署合约一样,ArbOS会加载合约并令其可用。ArbOS在兼容性的细节方面处理的非常好,所以智能合约开发者可以像在以太坊上那样编写代码(更常见的情况是,直接将现有的以太坊合约移植过来)。

在整个栈智之上,图表的右上部分,是由开发者部署到Arbitrum上的EVM合约。

图表右边是整个Arbitrum链的功能。现在我们再来看一下左侧,左侧更多与用户有关。

左下方是标准的以太坊节点,用户借此与以太坊链交互。上面的是Arbitrum节点。顾名思义,是用于用户与Arbitrum交互的。Arbitrum节点支持与以太坊节点相同的API,所以与现行的以太坊工具都能完美工作——你可以将你的以太坊钱包或工具直接指向Arbitrum节点,双方即可通信了。正如在以太坊上一样,任何人都可以运行Arbitrum节点,但仍会有许多人选择依赖其他人建立的节点。

在线上方还是下方?

我们常说,在Arbitrum架构中分割L1和L2的是位于AVM的这条线。这种分层有利于界定某些行为发生的地点。 线下方,是用来确保AVM以及链的执行的正确性的。而线上方则假设AVM会正确运行,专注于与运行在L2上的软件的互动。

例如,Arbitrum验证者在下方工作,因为他们参与由线下方EthBridge管理的rollup协议,来确保AVM的运转是正常的。

另一边,Arbitrum全节点工作在线上方,它们每个节点在本地都有一份AVM状态的副本,并假设线下方的工作机制能够保证每个人本地的运算最终都相同。它们并不监视下方是如何工作的。

大部分用户在大部分场景下,只关心线上方的事。与Arbitrum互动与其他链互动是相同的,不需要考虑线下方如何确保链工作正常。

EthBridge

EthBridge是管理Arbitrum链的一组合约。EthBridge会记录收件箱的内容,链状态的哈希,以及输出信息。EthBridge是Arbitrum链上发生了什么的终极权威消息源。

EthBridge是Arbitrum安全的基石。它运行于以太坊上,所以是公开透明且无需信任的。

收件箱合约则管理着收件箱。收件箱记录了每条消息的哈希。调用一个send*函数会向Arbitrum的收件箱发送一条信息。

收件箱合约确保进入的信息是准确无误的:信息需要正确记录发送人,以太坊区块编号,时间戳。

理所当然地,还有一个发件箱合约,管理着链的输出。例如,在Arbitrum上发生的需要(最终会)返回在以太坊上的事(比如提现)。当一个rollup区块确认后,该区块的输出就放入了收件箱内。输出最终如何反应在以太坊上请见 桥接

Rollup合约及其伙伴管理着整个rollup合约。它们共同追踪Arbitrum链的状态:提出的,接受的,被拒绝的rollup区块以及在哪个rollup结点上谁进行了质押。Challenge挑战合约及其伙伴则负责解决验证者之间的哪个rollup区块是正确的的争端。Rollup,Challenge和它们的朋友们会在Rollup协议章节中详细介绍。

Arbitrum Rollup协议

在深入理解rollup协议之前,有两件事需要明确。 首先,如果你是Arbitrum的用户或开发者,你不需要理解rollup协议。除非你认为有必要,否则你都不需要对其进行思考。这就像火车乘客与火车引擎一样:乘客知道引擎的存在,只需要引擎正常工作即可,但并不需要花费时间监控它或学习其内部结构。

我们欢迎大家来学习,观察,甚至参与到rollup协议中,不过对大部分人来说这并非必要的。如果你只是一位传统的火车乘客,可以直接跳到验证者章节。如果不是则请继续阅读。

其次,需要支出,rollup协议并不决定交易的结果,它只对结果进行确认。结果是由收件箱中信息的顺序确定的。所以,一旦消息进入了收件箱,其结果就是确定的——Arbitrum节点会将待完成的交易提交。就Arbitrum用户而言Rollup协议的作用是用来确认这些结果。这也是为什么Arbitrum用户可以不关心rollup协议。

那么你可能会奇怪,如果每个人都知道交易的结果,为什么还要rollup协议确认呢?两个原因。首先,有人可能会撒谎、作弊,所以需要一个权威的、无需信任的方式来确认谁是恶意的。第二,以太坊并不知道这些结果。而L2扩容的核心要义就是以太坊不需要承担交易的所有工作量。Arbitrum交易速度非常快,以太坊是没有能力去监控每一笔Arbitrum交易的。但一旦结果确认了,以太坊就知晓了,结果就是可信赖的。

有了这些铺垫,我们就可以深入rollup协议的细节进了。

参与rollup协议的人成为验证者,任何人都可以成为验证者。有些验证者则会选择成为质押者——将以太质押进来,如果作弊将被罚没。这些工作都是免许可的,任何人都能参与。

Rollup协议的核心安全属性是/AnyTrust Guarantee/(『一诚则成』原理):只要有一个诚实的验证者,那么整个链的正确运行就会有绝对的保证。这意味着Arbitrum链的运行是与以太坊一样免信任的。不论有多少恶意敌手试图阻止你,你凭一己之力就可以强制所有的交易被正确处理。

Rollup链

Rollup协议记录了一条rollup区块的链条,它们与以太坊区块并不是同一个概念。你可以认为rollup链是一条概念上的单独的链,是由Arbitrum rollup协议管理并监控的。

验证者可以提出rollup区块。新的rollup区块一开始是待决状态。最终每个rollup区块都会被解决,要么被确认,要么被拒绝。已确认的区块构成了整条链的历史。

每个rollup区块包含:

  • rollup区块编号
  • 父rollup区块编号:本rollup区块之前一个(被宣称为)正确的rollup区块的编号
  • 本链历史上所发生的运算量(以ArbGas计量)
  • 本链历史上所接收的收件箱信息数量
  • 对本链历史输出的哈希
  • 链状态的哈希

除了rollup区块编号,上述内容中的其余内容均是区块的提出者声明的。Arbitrum在最开始并不知道这些内容是否是正确的。如果所有的内容都正确,rollup协议最终应确认该区块。如果有任意内容是错误的,该区块最终会被拒绝。

每一个区块都会隐形地声明它的父区块是正确的。这也连锁式地标明了:一个区块会隐形地宣称整条链是正确的:一系列的祖先区块都是正确的,直至整条链的创始区块。

同样,每个区块都隐形地声明,其兄弟区块(有着同一个父区块的其他区块)都是错误的(如果存在兄区块的话)。若两个区块是兄弟关系,兄区块是正确的,那么弟区块一定是错误的,即使弟区块中的所有内容是真实有效的。

每个区块都会被分配一个截止时间,在该时间内其他验证者才能对其进行响应。如果你是一名验证者,并认同某一个rollup区块是正确的,那么你什么也不用做。如果你不认同该区块,你可以发布另一个有不同结果的区块,你可能会被该区块的质押者挑战。(更多请见下方『挑战』章节。)

正常情况下,rollup链看起来是这样的: 左侧都是已确认区块,代表了该链的早期历史。这些区块都被EthBridge接受并记录下来。94号区块是『最新确认区块』。在右侧,有一系列新的刚被提出的rollup区块。EthBridge尚不能确认或拒绝,因为其截止时间还没有到。待决区块中最老的95号区块,被称为『首个待决区块』。

注意,一个待决区块是可以连接在另一个待决区块之后的。这使得验证者能够不断地提出新的区块而不用等待EthBridge的最终确认。正常乐观情况下,所有的待决区块都是有效的,最终都会被接受。

下面的例子展现了有恶意验证者存在的情况下链的状态。这是一个人工策划过的场景,用来说明协议可能碰到的各种情况。我们将各种情况都融汇到了一个场景中去。

看起来有些复杂,我们梳理下。

  • 区块100已被确认。
  • 区块101宣称自己是区块100的正确子区块,但101被拒绝了(因为打了叉)。
  • 区块102最终被确认为区块100的正确子区块。
  • 区块103也确认了,现在是最新确认区块。
  • 区块104是103的子区块,105是104的子区块。由于104是错误的,105自然也会被拒绝,因为它的父区块就是错的。
  • 区块106待决。它宣称自己是区块103的子区块,但协议尚未决定是确认还是拒绝它。这是首个待决区块。
  • 区块107和108延续自区块106,它们也是待决状态。如果106被拒绝,它们也会被拒绝。
  • 区块109不认同区块106,因为它们的父区块相同。它们中至少会有一个被拒绝,但协议尚未解决。
  • 区块110跟随109。待决状态。如果109被拒绝,110也会。
  • 区块111跟随104。111最终肯定会被拒绝因为其父区块已经被拒绝,但目前111还是待决状态,因为协议是按顺序处理区块的,所以要先处理106到110。在110解决后,111会被立即拒绝。

再次提醒:这种情况在实践中是非常不可能发生的。本图中,至少有四方质押在了错误的结点上,尘埃落定后至少会有四方失去质押物。协议是有能力正确处理这些情况的,但这确实是边缘场景。这个例子仅用来说明原理上可能会出现的各种情况,以及协议会如何处理。

质押

在任何时间,都会有一部分验证者成为质押者,而另一部分则不会。质押者通过EthBridge充值资金,如果输掉挑战则会被没收。目前所有链接受以太币为质押物。

单笔质押可覆盖一系列rollup区块。每个质押者都质押在最新确认区块上;如果你质押了一个区块,你还可以继续向其子区块质押。所以你是可以质押在一串连续正确的区块上的。

要创建新的rollup区块,你必须成为质押者,并且已经在你所创建的区块的父区块上质押了资金。创建新区块的质押需求确保了如果作恶则惩罚机制能够执行。

EthBridge记录了当前所需要的质押数量。正常情况下会与基础质押数量相等,基础质押数量是Arbitrum链的一个参数。但如果链后来行动迟缓,质押数量会增加,下面为详述。

质押规则:

  • 如果你没有质押,可以质押在最新已确认rollup区块上。质押数量由EthBridge的当前最小质押额确定。
  • 如果你已经质押在了一个rollup区块上,你还可以将质押物移动到任意的子区块上。(EthBridge会追踪你所质押的区块的最大高度,并允许你为任意子区块质押,同时更新最大高度至该子区块。)这个过程不需要额外增加质押。只有一个特殊情况需要额外增加质押物,即在你质押的区块下创建新的rollup区块。
  • 如果你仅仅质押在了最新确认区块(可能是比较老的区块)上,你或任何人都可以请求将你的质押物返还。质押物返还给你后,你将不再质押者。
  • 如果你输掉了挑战,你的质押物将从所有的区块上移除,没收为罚金。

请注意,一旦你质押了一个rollup区块,你对这个区块就做出了承诺,是无法撤销质押的。最终会发生两件事:该区块确认了,或者你的质押被没收了。只有在区块被确认后你才能拿回你的质押物。

设置当前最小质押额 之前没有讨论当前最小质押额是如何设定的。通常,它等于基础质押额,基础质押额是Arbitrum链的一个参数。不过,如果链确认区块的速度缓慢,质押数量会暂时增加。具体来说,最小质押额 = 基础质押额 × 一个因子,该因子与自首个待决区块的截止时间以来所流逝的时间呈指数增长。这样可以确保,如果有作恶者通过错误质押来降低链的运行速度(尽管最终会被没收),它们的降速攻击也需要付出指数增长的成本。随着区块确认继续向前,质押需求最终会回到正常。

确认或拒绝rollup区块的规则

解决rollup区块的规则非常简单。

首个待决区块满足下列情况会被确认:

  • 该区块的父区块是最新确认区块,且
  • 该区块的截止时间已经过了,且
  • 至少有一个质押者,且
  • 所有质押者均质押在该区块上

首个待决区块满足下列情况会被拒绝:

  • 该区块的父区块已被拒绝,或
  • 下列三点皆为真:该区块的截止时间已过;至少有一名质押者;没有质押者质押在该区块上。

这一系列规则的结果是,一旦首个待决区块的截止时间已过(假设当前有至少一名质押者质押在了除最新确认区块以外的其他地方),该区块仍是待决状态只有一种可能:至少有一名质押者质押在其上,至少有一名质押者质押在其兄弟区块上。如果发生了这种情况,双方互不认同,就要进行挑战了。

挑战

假设rollup链的状态如下:

区块93和95是兄弟区块(父区块皆为92)。Alice质押于93上而Bob质押于95上。 目前我们可知Alice和Bob对区块93的正确性有争议,Alice承诺93是正确的而Bob承诺93是错的。(Bob质押了95,而95之前的最新确认区块是92,这暗示了93是错的。)

只要双方质押了兄弟区块,且分割双方没有在既有挑战中,任何人都可以对他们进行挑战。Rollup协议会记录下本次挑战并充当仲裁,最终会宣布胜者并没收失败者的质押资金。失败者不再是质押者。

挑战是一场Alice和Bob交替出手的博弈,以一个以太坊作合约作为仲裁。Alice,作为辩护者,最先行动。

博弈分为两个阶段:分割,之后是单步证明。分割会缩窄二人的争议范围,直至有争议的操作只有一条。随后单步证明会决定谁的主张是对的。

我们会分两次讲述这部分的协议。首先,会给出一个精简版,易于理解但效率不够高。然后还会给出一个真实实现版本。

分割协议:精简版

Alice为自己的主张辩护,她的主张是:从父区块的状态开始,虚拟机的状态可以前进至她所主张的区块A上的状态。本质上,她是在宣称,虚拟机可以执行N条指令,消耗M条收件箱中的信息并将哈希从H'转换为H。

Alice的第一个动作需要她把她的断言从开始(0条指令已执行)到结束(N条指令已执行)以中间点切分。协议要求Alice将其主张对半切分,发布中间点在执行了N/2步指令后的的状态。

当Alice已经有效地将她的断言二等分变为两个N/2步的断言后,Bob需要在这两个片段中选择一段并声明它是错的。

在此,我们又回到了之前的状态:Alice主张一个断言,Bob不同意。不过现在我们已经把断言长度从N缩短到了N/2。我们可以再重复之前的动作,Alice二分,Bob选择不同意的那一半,缩短尺度到N/4。我们可以继续该动作,经过对数轮的博弈,Alice和Bob的争议点就缩减为了单步操作。自此,分割协议就终止了,Alice必须给EthBridge生成一个单步证明供其检测。

为什么分割能够正确分辨作弊者

在我们谈论更复杂的真实挑战协议之前,要停下来思考一下,为什么精简版的协议也是正确的。这里的正确意味着两点:

  1. 如果Alice的初始断言是正确的,Alice总会赢得挑战。
  2. 如果Alice的初始断言是错误的,Bob总会赢得挑战。

欲证明1:Alice的初始断言是正确的,则她可以提供一个正确的中间点断言,两侧的半长断言也是正确的。所以,不论Bob反对哪一侧,Alice还是会为正确的主张辩护。在协议的任何阶段,Alice都在为正确的主张辩护。最终的单步辩护也是正确的,Alice会赢得挑战。

欲证明2:Alice的初始断言是错误的,只可能是因为她所宣称的终点在经过N步后是错误的。Alice现在要提供中间点的断言,该断言要么正确要么错误。如果错误,Bob可以挑战左半边断言,肯定是错的。如果Alice的中间点是正确的,那另一半断言肯定是错的,Bob就可以挑战右半边的断言。所以,不论Alice怎样做,Bob都可以找到不正确的半边断言。而在接下来的每一轮中如法炮制,最终,Alice无法提供正确的单步证明,Bob将赢得挑战。

(如果你是一个非常在意数学形式的人,显然这些结论是可以由基于N的归纳法得出的。)

真实的分割协议

真是的结构协议在理念上与上面的精简版是近似的,不过有了一些对效率和边界情况的提升。下面是二者的区别。

*K式分割:*将断言分割为N/K长度的K段,而非N/2长度的N段。所以需要发布K-1个均匀分布的中间点断言。该方法使降低了互动轮数,所需轮数为原轮数除以log(K)/log(2)倍。

以分割响应分割:在精简版中每一轮都有两步,Alice进行分割,Bob选择一个片段来挑战。在真实实现中,Bob在挑战一个分段时,需要发布他自己的针对该分段的最终态(和Alice的必须是不同的)以及Bob版本的对该分段的进行的再分割。Alice则进行回应,找出一个分段,为该分段提供一个最终态,并再进行分割。这样一来,由于每一次行动(而不是每两次行动)就分割了K次,使博弈轮数又额外除以了2倍。

*以ArbGas而非步数进行分割:*我们用ArbGas消耗量作为分割依据,而非指令的数量。这使得分割协议能精确地确定验证者需要花多久来检验断言的正确性(因为验证者的检验时间与ArbGas消耗成正比),由此即能让协议为rollup结点设定更加准确的截止时间。不过,缺点是,不同的指令需要不同量的ArbGas,我们就不能再假设某一段可以在N/K步的边界就能恰好执行完。真实的协议允许断言在第一个指令边界或在其目标终点后结束;而对正确性争辩也会考虑有人会在界定分段的正确终点上说谎。

*空收件箱场景:*真实的AVM不可能总能一直执行N个ArbGas而从不会卡住。AVM可能会宕机,也可能是因为收件箱已经空了也没有新信息进入。所以必须允许Bob,对Alice的N单位的运算断言,回应以:该N步是不可能的。因此真实协议会允许进行这种『在当前条件下不可能执行所述数量的操作』的回应(对初始断言除外)。

*时间限制:*博弈双方都有时间限制。博弈中一方所有行动的加总不能超过其时间限制,否则会输掉博弈。时间限制大约为一周。

需要澄清的是,上述变化并不会对挑战协议的正确性判定有任何影响。但它们确实在效率和边界情况处理上对协议提升很大。

效率

挑战协议设计的目的是让仲裁EthBridge只承担最小工作量。当Alice行动时,EthBridge只需要记录Alice的用时以及她的行动中是否包含了所要求的99个中间点。EthBridge在任何层面上都不关心这些断言是否正确;它只需要知道Alice的行动是符合要求的。

EthBridge仅在单步证明时需要依情断案,它需要核查Alice提供的证明是否真的能够反映VM在执行该步运算之前的状态到之后的状态。我们会在下方的AVM章节中对单步证明进行详述。

验证者

有些Arbitrum节点会选择成为/验证者/。验证者会关注rollup协议的运行并参与到协议中来安全地推动链状态不断向前。

并非所有的节点都会选择成为验证者。因为rollup协议并不决定链的行为,只是对其正确行为进行确认,而正确行为是完全由收件箱信息决定的。节点可以忽略rollup协议,自己就可以轻易计算出正确的行为。关于全节点请见全节点

成为验证者是免许可的,任何人都可以。Offchain Labs提供了开源的验证者软件,及预编译的Docker image。

每个验证者都可以选择自己的策略,不过我们希望验证者能够遵循下列三种常见策略。

  • 主动验证者:这种策略的验证者会通过发布新的rollup区块来推动链状态前进。由于创建rollup区块需要质押,主动验证者永远在进行质押。一条链其实只需要一个诚实的主动验证者;多余皆为浪费。对于Arbitrum的官方旗舰链,Offchain Labs官方会成为一个主动验证者。
  • 防御型验证者:这种验证者会监控rollup协议的运行。如果只有正确的区块发布,它们什么也不做。一旦有错误的区块发布,这种验证者就会进行干预,要么是发布正确的rollup区块,要么是质押在别人发布的正确区块上。该策略在一切顺利时不需要质押,但如果有人作恶则进行质押以捍卫正确结果。
  • 守望者:这种验证者从不质押。它们只单纯监视rollup协议,如果有错误的区块发布,就发布警报(不论什么形式的警报)使其他人可以介入。这种策略假设其他愿意质押的验证者同样愿意为获得作恶者的质押物而进行干预。在虚假区块过期之前都有机会补救。(实践中有大约几天的时间窗口。)

正常情况下,防御型验证者和守望者只需要观察而不需要做其他任何事。准备捣乱的作恶者并不知道有多少双眼睛在暗中观察。也许有一些防御型验证者会一开始就亮明身份,但其他人可能并不会这么做。所以,潜在的防御者一直都是作恶者的心头大患。

那么谁可以成为验证者呢?任何人都行,但大部分用户并不需要关心这些。实践中,我们预期大家会基于下列理由成为验证者:

  • 被链的创始方或其他人雇佣的验证者。在Arbitrum官方旗舰链上,Offchain Labs会雇佣一些验证者。
  • 在链上有大规模资金的用户,如dapp开发者,交易所,高级用户,流动性提供者,它们都可能会选择成为验证者来保障自己的资产安全。
  • 基于任何其他理由的人。想要保护自己资产的人,或想要成为一个优秀公民的人。大部分普通用户不需要这么做,我们也认为这不会是大部分用户的选择。

AVM

Arbitrum虚拟机,AVM,是L1和L2的接口。L1提供了AVM接口并确保VM的正确运行。L2运行AVM并提供了一些功能,如部署合约,运行合约,追踪余额,以及所有有智能合约的区块链所需的能力。

每个Arbitrum链都只有一个AVM,执行所有运算,维护所有存储空间。有些其他系统对每个合约都单独运行一个虚拟机,而Arbitrum更像以太坊,一个虚拟机管理整条链。对多个合约的管理是由运行在AVM上的软件实现的。

从核心来看,一条链的VM的工作模型非常简单。读取收件箱中的信息,改变链的状态,并产生输出。

AVM设计的出发点是以太坊虚拟机EVM。因为AVM致力于高效地执行为EVM编写或编译的程序,所以在许多地方都保留了EVM的设计。例如,AVM采用了EVM的基础整型数据结构(256位大端序无符号整数,uint256),以及EVM中对整型操作的指令。

为什么AVM与EVM不同

由于L2协议的需要以及Arbitrum使用了交互式证明来解决争议,AVM需要与EVM有一些不同。

执行vs证明

不像EVM和其他类似架构,Arbitrum既支持执行(通过执行运算推进链状态向前,在Arbitrum中是在链下进行的)又支持证明(让L1合约或其他可信赖方相信某个断言是正确的)。EVM架构的系统通过再执行来解决争议,Arbitrum依赖于更加高效的挑战协议来验证最终证明。

将执行和证明分离显然是非常好的——完全不需要在L1上重新执行代码,所以二者在不同环境下就有了适合自己不同的优化。由于本地执行是常见状态,所以执行在本地的可信赖的环境中是为速度而优化的。另一方面,证明则没有那么常见,但仍需要足够高效的证明方式,从而在繁忙的以太坊L1上能够实现。大部分场景下不会用到证明检查,但一定要在任意时间确保证明是可用的。这种执行和证明分离设计在正常情况下,极大地提高了执行的效率。

ArbOS的需求

另一个在需求上的不同点是,Arbitrum使用了ArbOS,该『操作系统』运行在L2上。ArbOS控制着合约的执行,将各个合约彼此分隔,并追踪它们的资源使用情况。为了支持这一点,AVM中包含了一些指令,可以保存和恢复VM的栈,管理追踪资源使用的VM寄存器,以及接收外部调用者的信息。这些指令是ArbOS自身使用的,不过ArbOS能够确保它们不会出现在不受信任的代码中。

在L2可信的软件中支持这些功能,而非将其构建在L1强加的以太坊式的架构上,对节省成本有重大意义。因为我们不在L1 EthBridge合约中管理这些资源,而是将其放入了计算和存储更便宜的L2上。在L2构建一套可信赖的操作系统同样对灵活性有重大意义,因为L2与L1强制的VM架构相比,其代码更容易迭代也更容易定制。

使用L2可信赖的操作系统确实需要有VM指令集的支持,例如,允许OS限制并追踪合约使用的资源。

支持梅克尔化

任何L2依赖断言和争议解决系统的协议,都必须定义一套能够将系统完整状态进行梅克尔哈希的规则,使得状态断言能够高效地发布在L1上。由于在争议解决中需要用到,所以该规则必须是架构标准的一部分。同样,在验证者维护梅克尔哈希以及有需要时对其重计算时,也必须保持合理的效率。这些事情关系到架构如何构建其内存。比如,对任何大的且可变的数据结构进行梅克尔化都比较耗费资源,并且,对其进行梅克尔化的特定算法可能需成为架构标准中的一部分。

AVM对此的解决方案是,构建一种大小有限、不可更改的内存对象(元组,Tuples),通过引用来囊括其他元组。元组虽然不可原地改变,但有一个指令可以复制一个元组并对其进行修改。如此便能够构建大平面的树状内存结构。应用程序通过内部使用元组的库就能够使用一些诸如大平面数组,key-value存储,等等特性。

元组的特性令创造首尾循环的元组是不可能的,AVM可以通过引用计数、不可变结构安全地管理元组。每个元组的哈希只需计算一次,因为元组是不可变的。

CodePoint码点:为证明优化代码

传统的代码组织结构会维护一个线性的指令队列,并将进程计数器(PC)指向下一条要执行的指令。在这种传统方式下,由于需要对当前进程计数器提供梅克尔证明,证明一条指令需要花费对数时间和对数空间。

通过分离执行和证明,AVM在实现这一点上更加高效。执行使用了标准抽象的由进程计数器索引的指令队列,而证明则采用了CodePoint码点结构确保证明和证据核查在常量时间和常量空间内即可完成。在某个PC值下指令的码点是(opcode at PC, Hash(CodePoint at PC+1))。如果没有CodePoint at PC+1,则使用0。

为了进行证明,『进程计数器』被『当前码点哈希』值所代替,该值是VM状态的一部分。该哈希的原像包含了当前的操作码及下一个码点的哈希。如果该指令不是JUMP的话,这些信息对要检查操作码及执行后当前码点哈希值的证明核验者来说就是全集。

所有的JUMP指令的目的地都是码点。因此,一个JUMP指令执行的证明也是信手拈来的。不仅有JUMP后的进程计数器,还有『当前码点哈希』这个寄存器在JUMP执行后的值。在任何情况下,证明和验证都只需要常量时空。

在一般的执行中(此时不需要证明),会按照传统架构使用进程计数器。不过当需要单步证明时,提供者可以使用预设的查询表获取任何PC所对应的码点。

在运行时创建代码

代码以两种方式添加到AVM中。第一种,有些代码在AVM已启动就生成了。这些代码读取自AVM可执行文件(.mexe文件),并由AVM虚拟机进行了预加载。

第二种,AVM有三个指令创建新的码点:一个会创建new Error CodePoint,另外两个会根据操作码创建new CodePoints(其中一个有立即值另外一个没有),这两个有可能是一个是立即值,一个下一个码点。当翻译EVM代码时ArbOS使用了这些指令。(更多请见ArbOS

从收件箱中获取信息

收件箱指令会消耗VM收件箱中下一条信息,并将其压入Data Stack数据栈。如果所有收件箱中的信息都被消耗了,收件箱指令就阻塞了——VM无法执行任何收件箱指令,其余事情也干不了,直至收件箱中有新的消息到达,收件箱指令可以完成执行。如果收件箱是空的,任何对收件箱指令的单步证明都会被拒绝。 inboxpeek指令不消耗任何信息,只会报告是收件箱内否第有一条未消耗的信息及其所在的特定区块高度。如果没有未消耗的信息了,inboxpeek会阻塞,直至有新信息进入。

产生输出

AVM有两条指令可以生成输出:/send/和/log/。二者都会被哈希至输出哈希累计器中,该累积器记载了VM输出的哈希。使用send会导致其值作为calldata记录在L1上,但log并不会。这意味着经由send产生的输出对L1合约是可见的,而log产生的则不会。当然,send是比log更昂贵的。一种实用的设计模式是,用log生成一串值,然后对其进行梅克尔哈希,再用单一的send。这让L1的合约能看到完整输出序列的梅克尔哈希,因此可以校验其中的某个值。ArbOS实用了这种设计模式,下方详述。

ArbOS和燃气追踪

AVM中有一个概念叫ArbGas,和以太坊中的gas是差不多的。ArbGas基于验证者需要多久来执行一条指令,来计量该指令的使用成本。每个AVM指令都会消耗ArbGas。

Arbitrum指令与与其对应的以太坊指令有所不同,两个原因。第一,在以太坊和L2上执行指令的相对成本是不同的。例如,对add指令来说,在Arbitrum上使用存储空间就更便宜。ArbGas是基于Arbitrum上的相对成本的。

AVM架构中有一个叫ArbGas Remaining的寄存器。在执行任何指令之前,ArbGas成本会从该寄存器中扣除。如果造成了该寄存器下溢(也就是说该指令out of ArbGas)就会抛出硬错误,该寄存器的值也会重置为MaxUint256。

AVM用来获取和设定ArbGasRemaining寄存器的指令,ArbOS也用其来限制和计量用户合约所消耗的ArbGas。

关于ArbGas价格和其他相关内容请见费用章节。

错误处理

在AVM中可能出现几种形式的错误,包括栈下溢,ArbGas消耗殆尽,以及一些类型错误如尝试jump到某个不是码点的值。

AVM架构中有一个Error CodePoint寄存器,可以通过特殊指令读写。当错误发生时,Next CodePoint寄存器就会被设置为等于Error CodePoint寄存器,本质上是直接跳转到特定的错误处理器。

ArbOS

ArbOS是L2上可信赖的『操作系统』,它负责将各个不受信任的合约分隔开,追踪并限制其资源使用,管理着从用户收集资金以给支付验证者的经济模型。当Arbitrum链启动后,ArbOS已经预加载到AVM实例中,准备运行。经过一些初始化工作后,ArbOS启动了其主循环,从收件箱中读取信息,根据信息进行一些工作其中可能会产生一些输出,然后再返回读取下一条信息。

Why ArbOS?

在Arbitrum中,那些原本需要在昂贵的L1上进行的作业都在ArbOS中完成了,享受着L2的速度和低成本且无需信任。

在L2可信的软件中支持这些功能,而非将其构建在L1强加的以太坊式的架构上,对节省成本有重大意义。因为我们不在L1 EthBridge合约中管理这些资源,而是将其放入了计算和存储更便宜的L2上。在L2构建一套可信赖的操作系统同样对灵活性有重大意义,因为L2与L1强制的VM架构相比,其代码更容易迭代也更容易定制。

使用L2可信赖的操作系统确实需要有VM指令集的支持,例如,允许OS限制并追踪合约使用的资源。

客户端、EthBridge和ArbOS之间的通信信息格式,请见ArbOS信息与log格式

EVM兼容性

ArbOS提供了对虚拟以太坊兼容链运算的模拟。它能够记录账户,执行合约代码,处理EVM代码创建和运行的细节。 用户可以提交包含部署合约在内的EVM交易,ArbOS确保这些交易都是兼容的。

账户列表

ArbOS维护着一张账户列表,该列表记录了虚拟的以太坊链中每个账户的状态。一个账户的表项中包含了其账户余额,nounce,代码以及存储空间(如果是和合约账户的话),以及一些与Arbitrum特定特性相关的其他信息。一个账户的表项,在该账户第一次在Arbitrum链上有任何动作时就会初始化。

翻译EVM代码并运行在AVM上

EVM代码并不能直接运行在AVM架构上,因此ArbOS需要将EVM代码翻译成等价的AVM代码。翻译发生在ArbOS内以确保是免信任的。

(Arbitrum的部分老版本会使用单独的编译器来将EVM代码翻译为AVM代码,但这么做在安全性和功能性上都有明显短板,所以我们将其更改为了直接在ArbOS内翻译。)

ArbOS将EVM合约翻译为有相同功能的AVM代码片段。有些指令是可以直接翻译的;例如,EVM的ADD指令就可以直接翻译成单独的AVM ADD指令。其余指令会翻译为对ArbOS提供的一个库的调用。例如,EVM CREATE2指令,该指令会创建一个地址经过特殊计算的合约,它会被ArbOS翻译为ArbOS中evmOp_create2这个函数。

对于合约部署,ArbOS会将已提交的EVM构造函数翻译为AVM的构造函数并执行。如果返回成功,ArbOS会将其返回数据以EVM代码标准翻译为AVM代码,并将AVM代码安装至该新部署合约的地址中。未来对该合约进行调用,会直接跳至该合约AVM代码的起点。

更多EVM虚拟化细节

当一笔EVM转账运行时,ArbOS会记录EVM调用栈(Call Stack),该栈中包含了代表交易中嵌套调用的EVM调用帧(Call Frame)。每个EVM调用帧都记录了一级调用的数据。当一个内部调用返回或回滚了,ArbOS将清空其调用帧,并执行该call带来的结果(如果返回了)或丢弃(如果回滚了)。

EVM call系列指令(call,delegatecall等)会根据EVM标准的call类型,通过ArbOS库中的函数来创建EVM调用帧。其中包括对calldata和gas的传播。跟以太坊一样,调用后任何剩余的gas都会返回给调用者。

EVM执行中特定的错误,如栈下溢,都会在AVM模拟过程中触发相应的错误。ArbOS的错误处理器发现在模拟特定EVM调用时出现了错误,会根据错误将调用回滚,将控制权返回给其调用者并生成合适的交易信息。ArbOS通过检查ArbGasRemaining寄存器来区别out-of-gas错误与其他错误,在燃气不足时该寄存器的值会设为MaxUint256。

这些机制保证了Arbitrum可以兼容运行EVM代码。

在Arbitrum中EVM执行与在以太坊上有三个主要不同点。

第一点,DIFFICULTY 和 COINBASE这两个EVM指令在L2上是无意义的,所以返回固定常量。 第二点,BLOCKHASH指令会返回一个基于链历史摘要的伪随机值,但和以太坊在相同区块高度下的返回值并不相同。 第三点,Arbitrum使用了ArbGas系统,所以一切和燃气相关的都以ArbGas计价,包括运算的燃气成本,以及与燃气相关的指令的结果,诸如GASLIMIT和GASPRICE。

部署EVM合约

在以太坊上,部署EVM合约时会向null账户发送一笔附带着由合约构造函数组成的calldata的交易。以太坊执行构造函数,如果构造函数成功,其返回数据将设置为新合约的代码。

Arbitrum使用相似的模式。对于合约部署,ArbOS会将已提交的EVM构造函数翻译为AVM的构造函数并执行。如果返回成功,ArbOS会将其返回数据以EVM代码标准翻译为AVM代码,并将AVM代码安装至该新部署合约的地址中。未来对该合约进行调用,会直接跳至该合约AVM代码的起点。

交易结果

当EVM交易结束时,不论是否成功,ArbOS都会使用AVM log指令发布一个交易结果。该结果可由Arbitrum节点检测到,从而向使用标准以太坊RPC API的用户返回结果。

全节点

顾名思义,Arbitrum全节点和以太坊全节点的作用是相同的。他们知道链状态,并提供给他人能够与链通信的API。

Arbitrum全节点是在『线上方』工作的,也就是说他们并不关心rollup协议,只将Arbitrum链视为消耗输入并产生输出的计算机。Arbitrum全节点内置了AVM模拟器来实现该功能。

批量处理交易:聚合器

全节点另一个重要角色是作为聚合器为用户交易服务。聚合器将多笔用户签名的交易集成为一个批次,然后作为一个整体发送至收件箱。批量提交交易币单独提交更有效率,因为每笔交易在L1上都会提交到EthBridge,以太坊对每笔交易都收取21000 gas。将大批用户的交易批量提交则摊薄了该固定成本(以及其他的一些成本)。提交批量交易也是免许可的,任何用户都可以提交,甚至可以在有需求时提交一个只包含一个交易的『批次』。由此,Arbitrum具有和以太坊一样的抗审查能力。

压缩交易

除了批量处理交易外,全节点还可以压缩交易来节省L1上的calldata空间。压缩对于用户和合约来说是不可见的——它们只能看到未压缩的交易。其工作流程为:用户向全节点提交了一个正常的交易;全节点将其压缩并提交给链;ArbOS收到这笔交易并将其解压缩,还原至正常交易;ArbOS会验证该笔交易的签名再处理交易。

压缩减小了一笔交易的『头信息』的体积。全节点一般在提交交易时会同时使用压缩和批量处理。

聚合器成本和费用

为用户提交交易的聚合器由于需要在L1上向收件箱发送交易,是有成本的。Arbitrum采用了一套向用户收费的机制来补偿聚合器的费用。请见ArbGas和费用

序列器模式

序列器模式是Arbitrum链的一个可选特性,一条链既可以开启也可以关闭该特性。在主网启动时,Arbitrum旗舰链会使用Offchain Labs官方提供的序列器。

序列器是特别委任的全节点,具有有限的对交易进行排序的能力。因此序列器可以立即保证用户交易的结果,而不需要等待以太坊上发生任何事,如为区块确认等5分钟,也甚至不需要等以太坊上15秒一次的区块生成。

客户端与序列器的通信是与全节点通信完全相同的。例如,给钱包提供网络URL就可以直接指向序列器。

瞬时最终性

没有序列器的情况下,全节点可以预测用户转账的结果,但却不能100%确定,因为还有其他节点向收件箱中提交交易,它并不知道在收件箱中这些交易会如何排序。

序列器有排序控制的能力,可以将交易分配在收件箱队列中的某个位置,由此可即刻确保用户的交易的结果。虽然其排序能力也是有限制的(见下方)但仍然比其他任何一方都更有能力影响交易顺序。

两个收件箱,快箱与慢箱

当增加了序列器后,我们会将原来一个收件箱分为两个。

  • 正常的收件箱和以前的工作方式是一样的。任何人在任何时间都可以向这个收件箱内提交信息。信息进入收件箱后会标记上其以太坊区块编号和时间戳。
  • 序列器收件箱『隶属于』序列器。只有序列器能响其提交信息。在提交信息时,序列器可以通过赋予其过去的区块编号把信息的时间向前修改,但不得前调超过/delta_blocks/个区块(/delta_blocks/一般会设置为物理时钟上的十分钟)。类似地,序列器也可以给信息赋予一个过去的时间,但不得超过/delta_seconds/。另外,序列器在使用序列器收件箱时,必须以非下降的方式来安排区块编号和时间戳。

当ArbOS准备接收下一条信息时,它会获取到位于一个或另一个收件箱头部的信息,其区块高度是最低的。这样确保了信息是以非下降的方式消耗的,一切都保持正常。(如果需要让时间戳保持非下降的形式,ArbOS也会将信息时间戳向上调整)。

如果序列器是善意的

一个善意的序列器会接收所有请求者的交易并公平对待,给予每个用于尽可能快地提供交易结果。

他还会通过尽可能减小其前调时间来最小化它给非序列器交易带来的延迟,这与提供确定的交易结果的目的是一致的。比如说,如果序列器认为在以太坊上完成最终性需要20个区块,那它就会将交易向前调整20个区块。这已经足够确保了序列器精确地了解当前交易之前的交易是哪些,因为之前的交易已经有了最终性。对于善意的序列器来说不需要前调更多,所以它不会这么做。

这确实也意味着正常收件箱中的交易的最终性要等得久一点。其最终性时间大致会加倍:如果最终性需要C个区块确认,现有一发布于在区块B的常规收件箱中的常规交易,序列器在B+C-1时间点上提交了序列器交易(序列器将其标记在区块B-1上),区块B上的交易在序列器交易后才被处理,则在B+2C-1时间点之前不会有最终性。

这就是有序列器以后的折中:如果你使用序列器,最终性则可提前C个区块。如果不使用序列器,最终性会延后C个区块。这种折中一般是好的,因为大部分交易会使用序列器,而瞬时最终性和5分钟才有最终性的差距大于5分钟和10分钟最终性。

通常来说,如果序列器是善意的,则序列器是非常有益的。

如果序列器是恶意的

另一方面,如果序列器是恶意的,确实会造成一些损害。如果它拒绝提交你的交易,你就不得不在常规收件箱中等待更久。并且,恶意的序列器有强大的能力来抢跑每个人的交易,以用户为代价获得暴利。

在主网上线时,Offchain Labs会自己运行一个善意的序列器。这是实用的,但并不是去中心化的。过一段时间,我们会切换到去中心化的,公正的序列器,下方详述。

由于当前序列器在最开始是由可信任的第三方运行的,之后才会去中心化,所以我们目前还没有内建机制来惩罚恶意序列器。我们要求用户先信任中心化的序列器,直至我们后面采用去中心化的公正的序列器。

去中心化公正序列器

从大面上看,去中心化序列器并不复杂。我们不使用单一的中心化服务器,而使用一组服务器构成的委员会,只要委员会中三分之二的人是诚实的,序列器总能建立公正的交易排序。

但如何实现这一点是复杂的。Cornell Tech的研发团队,包含Offchain Labs的CEO和联合创始人Steven Goldfeder,开发出了第一个去中心化序列器算法。随着不断的发展,这些理念会成为去中心化序列器的长久解决方案的基础。

桥接

我们已经讲述了用户如何与L2合约互动——用户把信息放入收件箱中来提交交易,或者由全节点聚合器为其代劳。那我们再说一下L1和L2合约相互间是如何调用的。

L1和L2链的运行是异步的,所以跨链调用是不可能得到与调用者处在同一个交易中的结果的。相反,跨链调用必须是异步的,这意味着调用者在某个时间发起调用,然后该调用等一会之后才执行。因此,跨链的合约到合约的调用不可能产生对调用合约可见的结果(它只能知道调用提交成功了,之后会被执行)。

L1合约可以提交L2交易

像用户一样,一个L1合约可以通过EthBridge提交L2交易。该L2交易会之后执行,其产生的结果对L1调用者来说是不可见的。该交易会在L2上有L1调用者相同的以太坊区块高度和时间戳,但L1调用者无法在该交易中看到任何结果。

该方法的好处是,简单,低延迟。劣势是,和其他我们描述的方法相比,L2交易有可能由于L1调用者没有取得正确的L2 gas price和max gas amount而被回滚。由于L1调用者看不到其L2交易结果,它无法确定该L2调用是否成功。

这种情况会对一些特定类型的L1到L2的调用带来严重问题。想象一下向L2充值代币。如果L1上成功了但L2上回滚了,就导致你的代币被锁定到L1收件箱中而L2上什么也没有,在L1和L2上都不能恢复。这是非常可怕的。

L1到L2基于票据的交易

还好,我们还有另一种L1到L2的调用方法,在处理燃气失败相关问题上更加健壮,这就是基于票据的交易,ticket-based transaction。其思想是,L1合约可以提交一个『预打包』的交易并立刻收到一个该交易的『ticketID』。之后,任何人都可以在L2上通过一个特殊的预编译合约,凭借ticketID,来赎回该票据并执行其交易。

如果赎回成功了,该交易就完成了,同时返还一个交易结果,并且ticketID就失效了不能再次使用。如果赎回失败,比如是因为打包好的交易失败了,该赎回会返回错误,该ticketID还能继续使用。

作为一个可选项(我们认为这应该是默认的),在提交交易时原提交者应期望赎回会成功,并尝试在提交交易后立即赎回。因此,在上述的代币充值例子中,在乐观的正常的情况下,只需要用户的单次签名。如果即刻赎回失败了,该ticketID仍然有效,其他人之后可以帮其执行。

通过这种方式提交的交易,会要求提交者必须发送一定数量的ETH,其数量由交易的calldata大小确定。一经提交,该票据在一周内有效。只要有人每周为其支付租金,那就可以一直有效。如果租金未支付,票据还没赎回,就会被删除。

票据赎回时还有各种信息,发送者和原提交者,destination,callvalue,以及提交者在提交时提供的calldata(提交者可以指定如果ticket被销毁,callvalue会返还给某个地址)。

票据机制比直接的L1到L2交易更加复杂,不过其优势是提交成本是可预测的,票据在提交成本支付后是一直可赎回的。只要有用户愿意赎回该票据(如果需要并支付租金),该L2交易最终总会被执行而不会被无声丢弃。

L2到L1基于票据的调用

从L2到L1的操作是相同的,也基于票据。L2合约可以调用预编译的ArbSys合约中的方法,来向L1发送交易。当L2包含了提交的交易在L1上确认其执行后,EthBridge会为其创建一张票据。该票据可以被任何人凭借ticketID通过特定的EthBridge方法调用。只有在L1交易没有回滚时才会被标记为已赎回。

这些票据的寿命是无限长的,直至被成功赎回。也不需要为其支付租金,因为其成本已经被在Arbitrum其余地方搜集的网络覆盖了。

ArbGas和费用

ArbGas是Arbitrum用来管理链上执行成本的。与以太坊gas的理念一致,每个AVM指令都会有一定数量的ArbGas消耗,而一次运算的总成本是该运算包含的指令的ArbGas的加总。

ArbGas并不能直接与以太坊gas相比。Arbitrum并没有硬性的ArbGas limit,正常情况下Arbitrum链每秒可以消耗任意数量的ArbGas,而在以太坊中则有gas limit。开发者和用户应该把ArbGas理解为是比以太坊gas更加丰饶且便宜的。

Why ArbGas?

AVM的设计原则之一是,每个指令都应该对验证、证明和证据检验有可预测的执行时间。这就导致我们需要一种方式来计量或估算验证任何运算的时间。

有两个原因。第一,我们需要确保证据检验有可预测的成本,这样就能预测EthBridge需要多少L1 gas,以确保EthBridge不会接近L1的gas limit。

第二,精确的验证时间估算对最大化rollup链的吞吐量是很重要的,因为可以帮我们安全地确定链的速度限制。

Rollup区块中的ArbGas

每个rollup区块中都包含了迄今为止运算使用的ArbGas,这其中也暗含着自上一个rollup区块以来所消耗的ArbGas数量。像rollup区块中其他东西一样,该值只是由创建该区块的质押者提供的,如果其断言是错误的该区块仍会在挑战中被拒绝。

即使rollup区块中的ArbGas值可能是错误的,但还是可以可靠地作为用来估算该区块所需计算的上限。因为,检查该区块的验证者可以在所主张的ArbGas耗尽后就停止运算;如果所主张的ArbGas耗尽后运算还未终止,该rollup区块肯定是错的,检查者就可以安全地挑战它。因此,rollup协议就可以安全地使用rollup区块中对ArbGas的主张,减去之前区块中ArbGas的数量,作为验证该区块正确性的所需时间上限。

即使某个区块只有ArbGas这一个数据是错的,对该rollup区块进行挑战也是安全的。当对一个断言二分后,该断言会包含所主张的ArbGas使用情况,其和必须等于父断言的ArbGas消耗量。如果一个断言的ArbGas数量是错的,那么至少有一个子断言的ArbGas是错的。所以挑战者知道某个断言的ArbGas是错的,他一定能找出某个分段的ArbGas是错的。

最终争议会走到单条AVM指令,以及关于该指令的ArbGas断言。单步证明会检查该断言是否正确。因此,rollup区块中错误的ArbGas断言最终都能追查到错误ArbGas数量的单步指令,然后再通过EthBridge的单步验证进行检测。

AVM中的ArbGas计量

AVM也在内部处理ArbGas,它会使用ArbGasRemaining寄存器进行计量,该寄存器是一个256位的无符号整型,行为如下:

  • 该寄存器初始值为MaxUint256
  • 在执行任何指令之前,该指令的ArbGas会即刻从寄存器中扣除。如果这使寄存器的值小于0,会抛出错误并将寄存器重设为MaxUint256。(在AVM规范中说明了该错误会导致的控制权转移)
  • 有一条特殊指令可以读取该寄存器的值
  • 还有一条特殊指令可以将该寄存器的值设置为任意值

该机制确保了ArbOS能够控制并计算应用代码需要消耗的ArbGas。在调用该应用之前,ArbOS可以通过将寄存器设置至N来限制应用的调用至N个ArbGas,如果有out-of-ArbGas错误产生则接收该错误。如果该寄存器的读数接近MaxInt256,肯定是因为该应用发生了out-of-ArbGas错误。(情况可能是这样的:应用生成了其他的错误而还有一小部分ArbGas剩余,然后在错误处理器的开头出现了out-of-ArbGas错误。在这种情况下,第二个错误会将ArbGasRemaining设置为MaxInt256并将控制器返回给错误处理器的开头,导致错误处理器认为是该程序导致的out-of-ArbGas错误。我们认为这种合理的行为是正确的。)

如果程序在没有错误产生的情况下讲控制器返回给了运行时,运行时就可以读取ArbGasRemaining寄存器来确定程序调用消耗了多少ArbGas,并充值进程序的账户中。

运行时可以安全地忽略掉ArbGas计量机制。如果没有使用过特殊指令,寄存器会被设置为MaxInt256,其值会减少但实践中不可能到0,所以不会有错误产生。

EVM到AVM的翻译器不会使用任何设置该寄存器的指令,所以不受信任的代码无法操纵其gas分配。

速度上限

Arbitrum链的安全依赖于一个假设,当一名验证着创建一个rollup区块时,其他验证着会对其进行检测,如果有错误则发起挑战。这需要其他验证者有时间和资源来检查每个rollup区块并及时提出挑战。Arbitrum协议把该项作为设置rollup区块挑战截止时间的考虑因素。

这就给AVM的运行设置了实际的速度上限:长远来看VM不可能运行地比验证者检验速度还要快。如果rollup区块的发布速度比速度上限还快,那截止时间在未来就会越来越长。由于rollup合约强加未来截止时间最多有多长的限制,最终会使新的rollup区块生成变慢,来确保实际的速度上限。

能够精确设置速度上限的基础是能够以一定准确度估算对AVM运算进行验证的所需时间。安全起见,任何在验证时间估算方面的不确定性都迫使我们把速度上限设置得低一些。而我们确实不想把速度上限设低,所以会尝试精确的估算。

费用

用户的转账是需要费用的,来为整个链的运行买单。这些费用是在L2上经过ArbOS评估后由其收取的。以ETH计价。其中一部分费用会立即支付给提交该笔交易的聚合器(如果有的话。并且这也受到一些限制,下面会讨论)。另一部分则进入了网络费用资金池中,用来支付一些保障链安全运行的服务提供者,如验证者。

一笔交易会为四种资源交费:

  • L2 tx:每个L2交易的基础费用,支付交易的服务成本
  • L1 calldata:该交易对应的每单位L1 calldata的费用(每个非0字节的calldata都是16单位的,每个0字节的calldata都是4个单位)
  • 运算:该交易所使用的每单位ArbGas的费用
  • 存储:每单位EVM存储空间的费用,由该交易所增加的净EVM空间计算

上述四种资源每个都有价格,在不同时间会有所不同。其价格以ETH计量(准确来说是wei),设置如下:

L2 tx和L1 calldata

这两种资源的价格取决于L1燃气价格。ArbOS并不能直接获取L1燃气价格,所以会以特定地址的聚合器最近所支付的燃气价格的加权平均值对其进行估计。对近期的批次转账,ArbOS还会保留一个1/batchSize的移动平均值。

  • L2 tx的基础价格是CP/B,其中C是L1上聚合器提交空批次转账的成本,P是L1上预估的燃气价格,(1/B)是平均的(1/batchSize)。
  • 每单位的L1 calldata价格就是估算的L1燃气价格。

这些基础价格保证了聚合器收取的费用与其提交批次交易的成本是相等的,如果其批次交易的批次大小正常的话。

如果转账是由聚合器提交的,ArbOS会搜集L2 tx和L1 calldata的基础费用,并立即划转给聚合器。ArbOS也会多收取15%的费用,并将其存入网络费用账户中,来为经常性开支和其他成本所用。如果该交易不是由聚合器提交的,ArbOS只会收取多的15%那部分并提交给网络费用池。

为了能补偿聚合器提交交易的成本,下列三点需为真:

  1. 交易的nounce是正确的。[防止聚合器重放骗补。]
  2. 该笔交易的sender在L2上有ETH,以支付费用。
  3. 该聚合器是该sender的『委托聚合器』。(ArbOS为每个账户都记录了其委托聚合器。默认值是某个特定的聚合器,在Arbitrum旗舰链上是Offchain Labs官方运行的聚合器)[防止其他聚合器抢跑某聚合器的批次交易并偷走其补贴。]

如果任意一点为假,该交易就不会走聚合器,只会被收取网络资金池的那部分用。

存储空间价格

根据EVM合约存储空间的净增加,交易也会被收费。减少空间并不能获得补贴。每个空间的成本为2000倍的估算的L1燃气值。也就是说存储空间成本大概为L1上的10%。

ArbGas价格

ArbGas定价取决于最小价格,和拥堵价格模型。

最小ArbGas价格为预估的L1燃气价格的1/10000。ArbGas的价格不可能比该值还低。

如果链开始拥堵了,价格就会上升。该思想与以太坊的类似,都通过增加足够的燃气成本来调节供需,防止过载风险。该机制受EIP-1559启发而来,EIP-1559在每个区块的一开始有一个基础价格,根据链的忙碌程度乘以7/8和9/8两个因子。当需求超越供给,因子会大于1;当供给超越需求,因子会小于1。(但价格永不可能低于最小值。)

该动态调整机制基于ArbOS管理的『ArbGas池』。该池的最大容量等于以链全速运行的60秒的计算量。交易所使用的ArbGas从该池中扣除,在每个新以太坊区块的起始,会根据对应的自上一个区块至今的时间戳秒数来向池中加入ArbGas(会受限于池的最大容量)。

在向池中增加了新的gas后,如果新的燃气池的大小是G,则当前的ArbGas价格会乘以(1350S - G) / (1200S) ,其中S是该链的速度上限。当G=60S(最大池容量限制)时,该值为7/8;当G=0时该值为9/8。

拥挤限制

燃气池也用来限制在特定时间戳时可用的燃气数量。具体来说,如果一笔交易能试用的燃气比池子中可提供的还多,那么该交易不会被执行而会直接被拒绝,并返回一个由于拥堵交易失败的交易结果。这防止了链上挤压过多的交易——如果交易提交速度一直比可验证的速度还快,最终燃气池会被清空,交易会被抛弃。同时,交易价格也会增加,由此该机制调节供需至平衡态。

ArbGas计量与二价竞拍

像以太坊一样,Arbitrum交易也会提交一个最大燃气数量(此处简写为maxgas),以及燃气出价(简写为gasbid)。一笔交易最多会动用到其声明的maxgas,如果不够则该交易会被回滚。

在以太坊上,一笔交易的燃气价格等于其声明的gasbid。在Arbitrum则不同,gasbid会视作该交易所愿支付的最高价格。实际支付的价格是当前Arbitrum的ArbGas价格,不论该值是多少,只要它低于该笔交易的gasbid即可。如果gasbid低于当前当前的ArbGas价格,该交易会被拒绝,并报告被拒绝的交易结果:gasbid太低。

所以进行Arbitrum交易,不应该去赌如何把gasbid压得尽可能低至当前ArbGas价格,而应该设置你愿意出的最高价格,同时需要注意发送者必须有至少gasbid*maxgas个ETH在其L2账户中。

6_Arbitrum术语表1_前端集成