前Arbitrum技术大使解读Arbitrum的组件结构(上)

新手2/27/2024, 2:27:46 AM
本文是Arbitrum前技术大使 及 智能合约自动化审计公司Goplus Security前联合创始人罗奔奔 对Arbitrum One的技术解读。

本文是Arbitrum前技术大使 及 智能合约自动化审计公司Goplus Security前联合创始人罗奔奔 对Arbitrum One的技术解读。

因为中文圈子里涉及Layer2的文章或资料,缺乏对Arbitrum乃至OP Rollup的专业解读,本文试图通过科普Arbitrum的运转机理,填补这一领域的空缺。由于Arbitrum本身的结构太复杂,全文在尽可能简化的基础上,还是超过了1万字篇幅,所以分成了上下两篇,建议作为参考资料收藏转发!

Rollup排序器简述

Rollup扩容的原理可以概括为两点:

成本优化:将⼤部分运算与存储任务移交至L1链下也即L2上。L2大多是运⾏在单台服务器也即排序器(Sequencer/Operator)上的⼀条链。

排序器在观感上接近于一台中心化服务器,在“区块链不可能三⻆”中舍弃“去中心化”来换取TPS与成本上的优势。 ⽤户可以让L2来代替以太坊处理交易指令,成本比在以太坊上交易要低得多。


(图源:BNB Chain)

安全保障:L2上的交易内容与交易后的状态,会同步⾄以太坊L1,通过合约来校验 状态转换的有效性。同时,以太坊上会保留L2的历史记录,排序器即便永久宕机,他⼈也可以通过以太坊上的记录,还原出整个L2的状态。

从根本上来说,Rollup的安全性是基于以太坊的。排序器如果不知道某个账户的私钥,就无法用该账户的名义发起交易,或者无法篡改该账户的资产余额(即便这么做了,也很快被识破)。

虽然排序器作为系统中枢带有中⼼化色彩,但在成熟度比较高的Rollup方案中,中心化排序器仅能实施交易审查等软性作恶⾏为,或者恶意宕机,但在理想状态的Rollup⽅案中,有相应的⼿段进⾏遏制(比如强制提款或排序证明等抗审查机制)。

(路印协议在L1上的合约源码中设置的,供用户调用的强制提款函数)

而防止Rollup排序器作恶的状态校验⽅式,分为欺诈证明(Fraud Proof)和有效性证明(Validity Proof)两类。使⽤欺诈证明的Rollup⽅案称为OP Rollup(Optimistic Rollup,OPR),⽽因为一些历史包袱,使⽤有效性证明的Rollup往往被称为ZK Rollup(Zero-knowledge Proof Rollup,ZKR),而不是Validity Rollup。

Arbitrum One是典型的OPR,它部署在L1上的合约,并不主动验证提交过来的数据,乐观地认为这些数据没有问题。如果提交的数据有错误,L2的验证者节点会主动发起挑战。

因此OPR也暗含一条信任假设:任意时刻⾄少有⼀个诚实的L2验证者节点。⽽ZKR的合约则通过密码学计算,主动但低成本地验证排序器提交的数据。

(乐观Rollup运转方式)

(ZK Rollup运转方式)

本文会深度介绍乐观式Rollup中的龙头项目——Arbitrum One,覆盖整个系统的方方面面,仔细阅读完后你将对Arbitrum和乐观式Rollup/OPR有深刻的理解。

Arbitrum的核心组件与工作流程

核心合约:

Arbitrum最重要的合约包括SequencerInbox, DelayedInbox, L1 Gateways, L2 Gateways, Outbox, RollupCore, Bridge等。后续将详细介绍。

排序器Sequencer:

接收用户交易并进行排序,计算交易结果,并迅速(通常<1s)返还给用户回执。用户往往在几秒内就能看到自己的交易在L2上链,体验就如同Web2平台。

同时,排序器还会在以太坊链下即时广播最新产生的L2 Block,任何一个Layer2节点都可以异步的接收。但此时,这些L2 Block不具备最终确定性,可以被排序器回滚掉。

每隔几分钟,排序器会将排序后的L2交易数据进行压缩,聚合成批次(Batch),提交至Layer1上的收件箱合约SequencerInbox,以保证数据可用性和Rollup协议的运转。一般而言,被提交至Layer1上的L2数据无法回滚,可以具备最终确定性。

从以上流程中我们可以概括:Layer2有自己的节点网络,但这些节点数量稀少,且一般没有公链惯用的共识协议,所以安全性是很差的,必须要依附于以太坊来保证,数据发布的可靠性与状态转换的有效性。

Arbitrum Rollup协议:

定义Rollup链的区块 RBlock 的结构,链的延续方式,RBlock的发布,以及挑战模式流程等⼀系列的合约。注意,这⾥说的Rollup链并不是大家理解的Layer2账本,而是Arbitrum One为了施展欺诈证明机制,而独立设置的一条抽象出来的“链状数据结构”。

⼀个RBlock可以包含多个L2区块的结果,⽽且数据也迥异,它的数据实体 RBlock 存储在RollupCore的⼀系列合约中。如果⼀个 RBlock 存在问题,Validator将⾯向该RBlock的提交者对其进⾏挑战。

验证者Validator:

Arbitrum的验证者节点其实是Layer2全节点的特殊子集,目前有白名单准入。


Validator根据排序器提交至SequencerInbox合约的交易批次batch,来创建新的RBlock(Rollup区块,也叫断⾔assertion),并监控当前Rollup链的状态,对排序器提交的错误数据进⾏挑战。

主动型的Validator需要事先在ETH链上质押资产,有时我们也称其为Staker。不进⾏质押的Layer2节点虽然也可以监控Rollup的运⾏动态,向⽤户发送异常报警等,但⽆法在ETH链上直接对排序器提交的错误数据进行⼲预。

挑战:

基础步骤可以概括为多轮互动式细分、单步证明。在细分环节,挑战双⽅先对有问题的交易数据进⾏多轮回合制细分,直⾄分解出有问题的那⼀步操作码指令,并进⾏验证。“多轮细分-单步证明” 这种范式,被Arbitrum开发者认为是欺诈证明中最节省gas的实现⽅式。所有环节都在合约控制之下,没有⼀⽅可以作弊。

挑战期:

由于OP Rollup的乐观optimistic本质,每个RBlock提交上链后,合约并不主动检查,预留给验证者一段时间窗⼝期去证伪。此时间窗⼝即为挑战期,在Arbitrum One主⽹上为1周。挑战期结束后,该RBlock才会被最终确认,块内对应的从L2传递到L1的消息(比如通过官方桥执行的提款操作)才能被放行。

ArbOS, Geth, WAVM:

Arbitrum采用的虚拟机名为AVM,包含Geth和ArbOS两部分。Geth是以太坊最常⽤的客户端软件,Arbitrum对其进⾏了轻量化的修改。ArbOS负责所有L2相关的特殊功能,如⽹络资源管理、⽣成L2区块、与EVM协同⼯作等。我们将两者的组合视为⼀个Native AVM,也就是Arbitrum采用的虚拟机。WAVM是把AVM的代码编译为Wasm后的结果。Arbitrum挑战流程中,最后的那个“单步证明”,验证的就是WAVM指令。

在此,我们可以将上述各个组件之间的关系和⼯作流⽤下图来表示:

L2交易生命周期

一笔L2交易的处理流程如下:

1.用户向排序器发送交易指令。

2.排序器先对待处理交易进数字签名等数据的验证,剔除无效交易,并进行排序和运算。

3.排序器将交易回执发送给⽤户(通常都⾮常快),但这只是排序器在ETH链下进行的“预处理”,处于Soft Finality的状态,并不可靠。但对于信任排序器的⽤户(⼤部分⽤户),可以乐观的认为交易已经完成,不会被回滚。

4.排序器将预处理后的交易原始数据,⾼度压缩后封装为⼀个Batch(批次)。

5.每隔⼀段时间(受到数据量、ETH拥堵程度等因素影响),排序器会向L1上的 Sequencer Inbox 合约发布交易Batch。此时可认为,交易已拥有最终性Hard Finality。

Sequencer Inbox合约

合约会接收排序器提交的交易batch,保证数据可⽤性。深⼊地看,SequencerInbox中的batch数据完整记录了Layer2的交易输入信息,即使排序器永久宕机,任何⼈都可以根据batch的记录还原Layer2的当前状态,接替故障/跑路的排序器。

⽤物理的⽅式理解,我们所看到的L2,只是 SequencerInbox 中batch的投影,光源则是STF。因为光源STF不会轻易变化,所以影⼦的形状只由充当物体的batch来决定。

Sequencer Inbox合约⼜称为快箱,排序器专门向其提交已经被预处理的交易,且只有排序器可向其提交数据。对应快箱的是慢箱Delayer Inbox,其功能在后续流程中会有描述。

Validator会一直监听SequencerInbox合约,每当排序器向该合约发布Batch后,就会抛出一个链上事件,Validator监听到这个事件发生后,就会去下载batch数据,在本地执⾏后,向ETH链上的Rollup协议合约发布RBlock 。

Arbitrum的bridge合约内有个叫累加器accumulator的参数,会针对新提交的L2 batch,以及慢Inbox上新接收的交易数和信息,进行记录。

(排序器向SequencerInbox不断提交batch)

(Batch的具体信息,data字段对应着Batch数据,这部分数据尺寸很大,截图没显示完)

SequencerInbox合约有两个主要函数:

add Sequencer L2Batch From Origin(),排序器每次都会调用该函数向Sequencer Inox合约提交Batch数据。

force Inclusion(),该函数任何人都可以调用,用于实现抗审查交易。这个函数的生效方式,会在后面谈到Delayed Inbox合约时详细解释。

上述两个函数都会调用 bridge.enqueueSequencerMessage(),来更新bridge合约内的累加器参数accumulator。

Gas定价

显然,L2的交易不可能免费,因为这样会引来DoS攻击,另外则是排序器L2本身的运⾏成本,以及在L1上提交数据都会有开销。⽤户在Layer2网络内发起交易时,gas费的结构如下:

占用Layer1资源产生的数据发布成本,主要来自于排序器提交的batch(每个batch有很多用户的交易),成本最终由交易发起者们均摊。数据发布产生的手续费定价算法是动态的,排序器会根据近期的盈亏状况、batch⼤⼩、当前以太坊gas价格进⾏定价。

用户因占用Layer2资源产生的成本,设定了⼀个可以保证系统稳定运⾏的,每秒处理的gas上限(⽬前Arbitrum One是700万)。L1和L2的gas指导价格均由ArbOS跟踪并调整,公式暂时不在此赘述。

虽然具体的gas价格计算过程⽐较复杂,但⽤户无需感知到这些细节,可以明显感到 Rollup交易费⽤比ETH主网便宜的多。

乐观式欺诈证明

回顾上文,L2实际上只是排序器在快箱中提交的交易输入batch的投影,也即:

Transaction Inputs -> STF -> State Outputs。输入已经确定,STF是不变的,则输出结果也是确定的,而欺诈证明和Arbitrum Rollup协议这套系统就是把输出的状态根,以RBlock (aka断言)的形式发布到L1上并对其进行乐观式证明的一套系统。

在L1上有排序器发布的输⼊数据,也有验证者发布的输出状态。我们再仔细考量⼀下,是否有必要向链上发布Layer2的状态呢?

因为输⼊已经完全决定了输出,而输入数据是公开可见的,再提交输出结果-状态似乎是多余的?但这种想法忽略了L1-L2两个系统之间实际上需要状态结算,也即L2向L1⽅向的提现⾏为,需要有对状态的证明。

在搭建Rollup的时候,⼀条最核⼼的思想就是把⼤部分运算和存储放到L2上来规避L1⾼昂的费⽤,这也就意味着,L1并不知道L2的状态,它仅仅帮助L2排序器发布全体交易的输入数据,但并不负责计算出L2的状态。

⽽提现⾏为,本质上是依照L2给出的跨链消息,从L1的合约⾥解锁相应资⾦,划转到⽤户的L1账户中或完成其他事情。

此时Layer1的合约就会问:你在Layer2上的状态是怎样的,怎么证明你真的拥有这些声明要跨走的资产。这个时候用户要给出对应该的Merkle Proof等。

所以,如果我们构建⼀条没有提现功能的Rollup,理论上不向L1进⾏状态同步是可以的,也不需要欺诈证明等状态证明系统(虽然可能带来其他问题)。但在现实应⽤中,这显然是不可⾏的。

所谓的乐观式证明中,合约不会去检查提交到L1的输出状态是否正确,乐观地认为一切都是准确无误的。乐观证明系统会假设,在任意时刻都有⾄少⼀名诚实的Validator,如果出现错误的状态,则通过欺诈证明进⾏挑战。

这么设计的好处是,不需要主动验证每⼀个发布到L1上的RBlock,避免浪费gas。实际上对于OPR⽽⾔,对每⼀个断⾔进⾏验证也是不现实的,因为每个 Rblock都包含着一或多个L2区块,要在L1上去对每笔交易重新执⾏⼀遍,与直接在L1上执行L2交易无异,这就失去了Layer2扩容的意义。

⽽ZKR不存在这个问题,因为ZK Proof有简洁性,只需要验证⼀个很⼩的Proof,不需要真地去执⾏该Proof背后所对应的许多条交易。所以ZKR并不是乐观式运⾏,每次发布状态都会有Verfier合约进⾏数学验证。

欺诈证明虽然不能像零知识证明那样具有⾼度的简洁性,但Arbitrum使⽤了⼀种“多轮分割-单步证明”的轮流式交互流程,最终需要证明的仅仅是单⼀的虚拟机操作码,成本相对较⼩。

Rollup协议

我们先来看一下,发起挑战和启动证明的入口,也即Rollup协议是如何工作的。

Rollup协议的核心合约是RollupProxy.sol,在保证数据结构一致的情况下,使用了一个罕见的双重代理结构,一个代理对应两个实现RollupUserLogic.sol和RollupAdminLogic.sol,在Scan等工具中目前还无法很好的解析。

另外还有ChallengeManager.sol合约负责管理挑战,OneStepProver系列合约来判定欺诈证明。

(图源:L2BEAT官网)

在RollupProxy中,记录由不同Validator提交的一系列RBlock(aka断言),也即下图中的方块:绿色-已确认,蓝色-未确认,黄色-已证伪。

RBlock中包含了自上一个RBlock以来,一个或多个L2区块执行后的最终状态。这些RBlock在形态上构成了一条形式上的Rollup Chain(注意L2账本本身相区别)。在乐观情况下,这条Rollup Chain应该是没有分叉的,因为有分叉意味着有Validator提交了彼此冲突的Rollup Block。

要提出或认同断言,需要验证者先为该断言质押一定数量的ETH,成为Staker。这样在发生挑战/欺诈证明时,输者的质押品将被罚没,这是保障验证者诚实行为的经济学基础。

图中右下角的111号蓝色块最终会被证伪,因为其父块104号区块是错误的(黄色)。

此外,验证者A提出了106号Rollup Block,而B不同意,对其进行挑战。

在B发起挑战后,ChallengeManager合约负责验证对挑战步骤的细分过程:

1.细分是一个双方轮流互动的过程,一方对某个Rollup Block中包含的历史数据进行分段,另一方指出是哪部分数据片段有问题。类似于二分法(实际是N/K)不断渐进缩小范围的一个过程。

2.之后,可以继续定位至哪条交易及结果有问题,再进一步细分至该交易中有争议的某条机器指令。

3.ChallengeManager合约只检查对原始数据进行细分后,产生的『数据片段』是否有效。

4.当挑战者和被挑战者定位到了将被挑战的那条机器指令后,挑战者调用oneStepProveExecution(),发送单步欺诈证明,证明这条机器指令的执行结果有问题。

单步证明

单步证明是整个Arbitrum的欺诈证明的核心。我们看一下单步证明具体证明的是什么内容。

这需要先理解WAVM,Wasm Arbitrum Virtual Machine,它是一个由ArbOS模块和Geth(以太坊客户端)核心模块共同编译成的虚拟机。由于L2与L1有许多截然不同的地方,原始的Geth核心必须经过轻量修改,并且配合ArbOS一起工作。

所以,L2上的状态转换其实是ArbOS+Geth Core的共同手笔。

Arbitrum的节点客户端(排序器、验证者、全节点等),是将上述ArbOS+Geth Core处理的程序,编译为节点主机能直接处理的原生机器代码(for x86/ARM/PC/Mac/etc.)。

如果把编译后得到的目标语言更改为Wasm,就得到了验证者生成欺诈证明时使用的WAVM,而验证单步证明的合约上,模拟的也是WAVM虚拟机的功能。

那为什么在生成欺诈证明时,要编译为Wasm字节码?主要还是因为,验证单步欺诈证明的合约,要用以太坊智能合约模拟出 能处理某套指令集的虚拟机VM,而WASM易于在合约上实现模拟。

但WASM相比于Native机器代码,运行速度略慢,所以只有在欺诈证明生成及验证的时候,Arbitrum的节点/合约才会用到WAVM。

在之前的多轮互动细分后,单步证明最终证明的是WAVM指令集中的单步指令。

下面的代码中可以看到,OneStepProofEntry首先要判定,待证明指令的操作码属于哪个类别,再调用相应的prover如Mem,Math等,将单步指令传入该prover合约。

最终结果afterHash会回到ChallengeManager,如果该哈希与Rollup Block上记录的,指令运算后的哈希不一致,则挑战成功。如果一致,则说明Rollup Block上记录的这个指令运行结果没问题,挑战失败。

声明:

  1. 本文转载自[极客 Web3],著作权归属原作者[罗奔奔,前Arbitrum技术大使,极客web3贡献者],如对转载有异议,请联系Gate Learn团队,团队会根据相关流程尽速处理。
  2. 免责声明:本文所表达的观点和意见仅代表作者个人观点,不构成任何投资建议。
  3. 文章其他语言版本由Gate Learn团队翻译, 在未提及Gate.io的情况下不得复制、传播或抄袭经翻译文章。

前Arbitrum技术大使解读Arbitrum的组件结构(上)

新手2/27/2024, 2:27:46 AM
本文是Arbitrum前技术大使 及 智能合约自动化审计公司Goplus Security前联合创始人罗奔奔 对Arbitrum One的技术解读。

本文是Arbitrum前技术大使 及 智能合约自动化审计公司Goplus Security前联合创始人罗奔奔 对Arbitrum One的技术解读。

因为中文圈子里涉及Layer2的文章或资料,缺乏对Arbitrum乃至OP Rollup的专业解读,本文试图通过科普Arbitrum的运转机理,填补这一领域的空缺。由于Arbitrum本身的结构太复杂,全文在尽可能简化的基础上,还是超过了1万字篇幅,所以分成了上下两篇,建议作为参考资料收藏转发!

Rollup排序器简述

Rollup扩容的原理可以概括为两点:

成本优化:将⼤部分运算与存储任务移交至L1链下也即L2上。L2大多是运⾏在单台服务器也即排序器(Sequencer/Operator)上的⼀条链。

排序器在观感上接近于一台中心化服务器,在“区块链不可能三⻆”中舍弃“去中心化”来换取TPS与成本上的优势。 ⽤户可以让L2来代替以太坊处理交易指令,成本比在以太坊上交易要低得多。


(图源:BNB Chain)

安全保障:L2上的交易内容与交易后的状态,会同步⾄以太坊L1,通过合约来校验 状态转换的有效性。同时,以太坊上会保留L2的历史记录,排序器即便永久宕机,他⼈也可以通过以太坊上的记录,还原出整个L2的状态。

从根本上来说,Rollup的安全性是基于以太坊的。排序器如果不知道某个账户的私钥,就无法用该账户的名义发起交易,或者无法篡改该账户的资产余额(即便这么做了,也很快被识破)。

虽然排序器作为系统中枢带有中⼼化色彩,但在成熟度比较高的Rollup方案中,中心化排序器仅能实施交易审查等软性作恶⾏为,或者恶意宕机,但在理想状态的Rollup⽅案中,有相应的⼿段进⾏遏制(比如强制提款或排序证明等抗审查机制)。

(路印协议在L1上的合约源码中设置的,供用户调用的强制提款函数)

而防止Rollup排序器作恶的状态校验⽅式,分为欺诈证明(Fraud Proof)和有效性证明(Validity Proof)两类。使⽤欺诈证明的Rollup⽅案称为OP Rollup(Optimistic Rollup,OPR),⽽因为一些历史包袱,使⽤有效性证明的Rollup往往被称为ZK Rollup(Zero-knowledge Proof Rollup,ZKR),而不是Validity Rollup。

Arbitrum One是典型的OPR,它部署在L1上的合约,并不主动验证提交过来的数据,乐观地认为这些数据没有问题。如果提交的数据有错误,L2的验证者节点会主动发起挑战。

因此OPR也暗含一条信任假设:任意时刻⾄少有⼀个诚实的L2验证者节点。⽽ZKR的合约则通过密码学计算,主动但低成本地验证排序器提交的数据。

(乐观Rollup运转方式)

(ZK Rollup运转方式)

本文会深度介绍乐观式Rollup中的龙头项目——Arbitrum One,覆盖整个系统的方方面面,仔细阅读完后你将对Arbitrum和乐观式Rollup/OPR有深刻的理解。

Arbitrum的核心组件与工作流程

核心合约:

Arbitrum最重要的合约包括SequencerInbox, DelayedInbox, L1 Gateways, L2 Gateways, Outbox, RollupCore, Bridge等。后续将详细介绍。

排序器Sequencer:

接收用户交易并进行排序,计算交易结果,并迅速(通常<1s)返还给用户回执。用户往往在几秒内就能看到自己的交易在L2上链,体验就如同Web2平台。

同时,排序器还会在以太坊链下即时广播最新产生的L2 Block,任何一个Layer2节点都可以异步的接收。但此时,这些L2 Block不具备最终确定性,可以被排序器回滚掉。

每隔几分钟,排序器会将排序后的L2交易数据进行压缩,聚合成批次(Batch),提交至Layer1上的收件箱合约SequencerInbox,以保证数据可用性和Rollup协议的运转。一般而言,被提交至Layer1上的L2数据无法回滚,可以具备最终确定性。

从以上流程中我们可以概括:Layer2有自己的节点网络,但这些节点数量稀少,且一般没有公链惯用的共识协议,所以安全性是很差的,必须要依附于以太坊来保证,数据发布的可靠性与状态转换的有效性。

Arbitrum Rollup协议:

定义Rollup链的区块 RBlock 的结构,链的延续方式,RBlock的发布,以及挑战模式流程等⼀系列的合约。注意,这⾥说的Rollup链并不是大家理解的Layer2账本,而是Arbitrum One为了施展欺诈证明机制,而独立设置的一条抽象出来的“链状数据结构”。

⼀个RBlock可以包含多个L2区块的结果,⽽且数据也迥异,它的数据实体 RBlock 存储在RollupCore的⼀系列合约中。如果⼀个 RBlock 存在问题,Validator将⾯向该RBlock的提交者对其进⾏挑战。

验证者Validator:

Arbitrum的验证者节点其实是Layer2全节点的特殊子集,目前有白名单准入。


Validator根据排序器提交至SequencerInbox合约的交易批次batch,来创建新的RBlock(Rollup区块,也叫断⾔assertion),并监控当前Rollup链的状态,对排序器提交的错误数据进⾏挑战。

主动型的Validator需要事先在ETH链上质押资产,有时我们也称其为Staker。不进⾏质押的Layer2节点虽然也可以监控Rollup的运⾏动态,向⽤户发送异常报警等,但⽆法在ETH链上直接对排序器提交的错误数据进行⼲预。

挑战:

基础步骤可以概括为多轮互动式细分、单步证明。在细分环节,挑战双⽅先对有问题的交易数据进⾏多轮回合制细分,直⾄分解出有问题的那⼀步操作码指令,并进⾏验证。“多轮细分-单步证明” 这种范式,被Arbitrum开发者认为是欺诈证明中最节省gas的实现⽅式。所有环节都在合约控制之下,没有⼀⽅可以作弊。

挑战期:

由于OP Rollup的乐观optimistic本质,每个RBlock提交上链后,合约并不主动检查,预留给验证者一段时间窗⼝期去证伪。此时间窗⼝即为挑战期,在Arbitrum One主⽹上为1周。挑战期结束后,该RBlock才会被最终确认,块内对应的从L2传递到L1的消息(比如通过官方桥执行的提款操作)才能被放行。

ArbOS, Geth, WAVM:

Arbitrum采用的虚拟机名为AVM,包含Geth和ArbOS两部分。Geth是以太坊最常⽤的客户端软件,Arbitrum对其进⾏了轻量化的修改。ArbOS负责所有L2相关的特殊功能,如⽹络资源管理、⽣成L2区块、与EVM协同⼯作等。我们将两者的组合视为⼀个Native AVM,也就是Arbitrum采用的虚拟机。WAVM是把AVM的代码编译为Wasm后的结果。Arbitrum挑战流程中,最后的那个“单步证明”,验证的就是WAVM指令。

在此,我们可以将上述各个组件之间的关系和⼯作流⽤下图来表示:

L2交易生命周期

一笔L2交易的处理流程如下:

1.用户向排序器发送交易指令。

2.排序器先对待处理交易进数字签名等数据的验证,剔除无效交易,并进行排序和运算。

3.排序器将交易回执发送给⽤户(通常都⾮常快),但这只是排序器在ETH链下进行的“预处理”,处于Soft Finality的状态,并不可靠。但对于信任排序器的⽤户(⼤部分⽤户),可以乐观的认为交易已经完成,不会被回滚。

4.排序器将预处理后的交易原始数据,⾼度压缩后封装为⼀个Batch(批次)。

5.每隔⼀段时间(受到数据量、ETH拥堵程度等因素影响),排序器会向L1上的 Sequencer Inbox 合约发布交易Batch。此时可认为,交易已拥有最终性Hard Finality。

Sequencer Inbox合约

合约会接收排序器提交的交易batch,保证数据可⽤性。深⼊地看,SequencerInbox中的batch数据完整记录了Layer2的交易输入信息,即使排序器永久宕机,任何⼈都可以根据batch的记录还原Layer2的当前状态,接替故障/跑路的排序器。

⽤物理的⽅式理解,我们所看到的L2,只是 SequencerInbox 中batch的投影,光源则是STF。因为光源STF不会轻易变化,所以影⼦的形状只由充当物体的batch来决定。

Sequencer Inbox合约⼜称为快箱,排序器专门向其提交已经被预处理的交易,且只有排序器可向其提交数据。对应快箱的是慢箱Delayer Inbox,其功能在后续流程中会有描述。

Validator会一直监听SequencerInbox合约,每当排序器向该合约发布Batch后,就会抛出一个链上事件,Validator监听到这个事件发生后,就会去下载batch数据,在本地执⾏后,向ETH链上的Rollup协议合约发布RBlock 。

Arbitrum的bridge合约内有个叫累加器accumulator的参数,会针对新提交的L2 batch,以及慢Inbox上新接收的交易数和信息,进行记录。

(排序器向SequencerInbox不断提交batch)

(Batch的具体信息,data字段对应着Batch数据,这部分数据尺寸很大,截图没显示完)

SequencerInbox合约有两个主要函数:

add Sequencer L2Batch From Origin(),排序器每次都会调用该函数向Sequencer Inox合约提交Batch数据。

force Inclusion(),该函数任何人都可以调用,用于实现抗审查交易。这个函数的生效方式,会在后面谈到Delayed Inbox合约时详细解释。

上述两个函数都会调用 bridge.enqueueSequencerMessage(),来更新bridge合约内的累加器参数accumulator。

Gas定价

显然,L2的交易不可能免费,因为这样会引来DoS攻击,另外则是排序器L2本身的运⾏成本,以及在L1上提交数据都会有开销。⽤户在Layer2网络内发起交易时,gas费的结构如下:

占用Layer1资源产生的数据发布成本,主要来自于排序器提交的batch(每个batch有很多用户的交易),成本最终由交易发起者们均摊。数据发布产生的手续费定价算法是动态的,排序器会根据近期的盈亏状况、batch⼤⼩、当前以太坊gas价格进⾏定价。

用户因占用Layer2资源产生的成本,设定了⼀个可以保证系统稳定运⾏的,每秒处理的gas上限(⽬前Arbitrum One是700万)。L1和L2的gas指导价格均由ArbOS跟踪并调整,公式暂时不在此赘述。

虽然具体的gas价格计算过程⽐较复杂,但⽤户无需感知到这些细节,可以明显感到 Rollup交易费⽤比ETH主网便宜的多。

乐观式欺诈证明

回顾上文,L2实际上只是排序器在快箱中提交的交易输入batch的投影,也即:

Transaction Inputs -> STF -> State Outputs。输入已经确定,STF是不变的,则输出结果也是确定的,而欺诈证明和Arbitrum Rollup协议这套系统就是把输出的状态根,以RBlock (aka断言)的形式发布到L1上并对其进行乐观式证明的一套系统。

在L1上有排序器发布的输⼊数据,也有验证者发布的输出状态。我们再仔细考量⼀下,是否有必要向链上发布Layer2的状态呢?

因为输⼊已经完全决定了输出,而输入数据是公开可见的,再提交输出结果-状态似乎是多余的?但这种想法忽略了L1-L2两个系统之间实际上需要状态结算,也即L2向L1⽅向的提现⾏为,需要有对状态的证明。

在搭建Rollup的时候,⼀条最核⼼的思想就是把⼤部分运算和存储放到L2上来规避L1⾼昂的费⽤,这也就意味着,L1并不知道L2的状态,它仅仅帮助L2排序器发布全体交易的输入数据,但并不负责计算出L2的状态。

⽽提现⾏为,本质上是依照L2给出的跨链消息,从L1的合约⾥解锁相应资⾦,划转到⽤户的L1账户中或完成其他事情。

此时Layer1的合约就会问:你在Layer2上的状态是怎样的,怎么证明你真的拥有这些声明要跨走的资产。这个时候用户要给出对应该的Merkle Proof等。

所以,如果我们构建⼀条没有提现功能的Rollup,理论上不向L1进⾏状态同步是可以的,也不需要欺诈证明等状态证明系统(虽然可能带来其他问题)。但在现实应⽤中,这显然是不可⾏的。

所谓的乐观式证明中,合约不会去检查提交到L1的输出状态是否正确,乐观地认为一切都是准确无误的。乐观证明系统会假设,在任意时刻都有⾄少⼀名诚实的Validator,如果出现错误的状态,则通过欺诈证明进⾏挑战。

这么设计的好处是,不需要主动验证每⼀个发布到L1上的RBlock,避免浪费gas。实际上对于OPR⽽⾔,对每⼀个断⾔进⾏验证也是不现实的,因为每个 Rblock都包含着一或多个L2区块,要在L1上去对每笔交易重新执⾏⼀遍,与直接在L1上执行L2交易无异,这就失去了Layer2扩容的意义。

⽽ZKR不存在这个问题,因为ZK Proof有简洁性,只需要验证⼀个很⼩的Proof,不需要真地去执⾏该Proof背后所对应的许多条交易。所以ZKR并不是乐观式运⾏,每次发布状态都会有Verfier合约进⾏数学验证。

欺诈证明虽然不能像零知识证明那样具有⾼度的简洁性,但Arbitrum使⽤了⼀种“多轮分割-单步证明”的轮流式交互流程,最终需要证明的仅仅是单⼀的虚拟机操作码,成本相对较⼩。

Rollup协议

我们先来看一下,发起挑战和启动证明的入口,也即Rollup协议是如何工作的。

Rollup协议的核心合约是RollupProxy.sol,在保证数据结构一致的情况下,使用了一个罕见的双重代理结构,一个代理对应两个实现RollupUserLogic.sol和RollupAdminLogic.sol,在Scan等工具中目前还无法很好的解析。

另外还有ChallengeManager.sol合约负责管理挑战,OneStepProver系列合约来判定欺诈证明。

(图源:L2BEAT官网)

在RollupProxy中,记录由不同Validator提交的一系列RBlock(aka断言),也即下图中的方块:绿色-已确认,蓝色-未确认,黄色-已证伪。

RBlock中包含了自上一个RBlock以来,一个或多个L2区块执行后的最终状态。这些RBlock在形态上构成了一条形式上的Rollup Chain(注意L2账本本身相区别)。在乐观情况下,这条Rollup Chain应该是没有分叉的,因为有分叉意味着有Validator提交了彼此冲突的Rollup Block。

要提出或认同断言,需要验证者先为该断言质押一定数量的ETH,成为Staker。这样在发生挑战/欺诈证明时,输者的质押品将被罚没,这是保障验证者诚实行为的经济学基础。

图中右下角的111号蓝色块最终会被证伪,因为其父块104号区块是错误的(黄色)。

此外,验证者A提出了106号Rollup Block,而B不同意,对其进行挑战。

在B发起挑战后,ChallengeManager合约负责验证对挑战步骤的细分过程:

1.细分是一个双方轮流互动的过程,一方对某个Rollup Block中包含的历史数据进行分段,另一方指出是哪部分数据片段有问题。类似于二分法(实际是N/K)不断渐进缩小范围的一个过程。

2.之后,可以继续定位至哪条交易及结果有问题,再进一步细分至该交易中有争议的某条机器指令。

3.ChallengeManager合约只检查对原始数据进行细分后,产生的『数据片段』是否有效。

4.当挑战者和被挑战者定位到了将被挑战的那条机器指令后,挑战者调用oneStepProveExecution(),发送单步欺诈证明,证明这条机器指令的执行结果有问题。

单步证明

单步证明是整个Arbitrum的欺诈证明的核心。我们看一下单步证明具体证明的是什么内容。

这需要先理解WAVM,Wasm Arbitrum Virtual Machine,它是一个由ArbOS模块和Geth(以太坊客户端)核心模块共同编译成的虚拟机。由于L2与L1有许多截然不同的地方,原始的Geth核心必须经过轻量修改,并且配合ArbOS一起工作。

所以,L2上的状态转换其实是ArbOS+Geth Core的共同手笔。

Arbitrum的节点客户端(排序器、验证者、全节点等),是将上述ArbOS+Geth Core处理的程序,编译为节点主机能直接处理的原生机器代码(for x86/ARM/PC/Mac/etc.)。

如果把编译后得到的目标语言更改为Wasm,就得到了验证者生成欺诈证明时使用的WAVM,而验证单步证明的合约上,模拟的也是WAVM虚拟机的功能。

那为什么在生成欺诈证明时,要编译为Wasm字节码?主要还是因为,验证单步欺诈证明的合约,要用以太坊智能合约模拟出 能处理某套指令集的虚拟机VM,而WASM易于在合约上实现模拟。

但WASM相比于Native机器代码,运行速度略慢,所以只有在欺诈证明生成及验证的时候,Arbitrum的节点/合约才会用到WAVM。

在之前的多轮互动细分后,单步证明最终证明的是WAVM指令集中的单步指令。

下面的代码中可以看到,OneStepProofEntry首先要判定,待证明指令的操作码属于哪个类别,再调用相应的prover如Mem,Math等,将单步指令传入该prover合约。

最终结果afterHash会回到ChallengeManager,如果该哈希与Rollup Block上记录的,指令运算后的哈希不一致,则挑战成功。如果一致,则说明Rollup Block上记录的这个指令运行结果没问题,挑战失败。

声明:

  1. 本文转载自[极客 Web3],著作权归属原作者[罗奔奔,前Arbitrum技术大使,极客web3贡献者],如对转载有异议,请联系Gate Learn团队,团队会根据相关流程尽速处理。
  2. 免责声明:本文所表达的观点和意见仅代表作者个人观点,不构成任何投资建议。
  3. 文章其他语言版本由Gate Learn团队翻译, 在未提及Gate.io的情况下不得复制、传播或抄袭经翻译文章。
Comece agora
Registe-se e ganhe um cupão de
100 USD
!
It seems that you are attempting to access our services from a Restricted Location where Gate.io is unable to provide services. We apologize for any inconvenience this may cause. Currently, the Restricted Locations include but not limited to: the United States of America, Canada, Cambodia, Thailand, Cuba, Iran, North Korea and so on. For more information regarding the Restricted Locations, please refer to the User Agreement. Should you have any other questions, please contact our Customer Support Team.