以太坊Rollup的抗审查交易如何触发?

中级6/11/2024, 3:45:38 PM
Metamask 母公司 Consensys 推出的以太坊二层 Linea 主动停机,声称是为了降低 Velocore 黑客攻击事件的影响。这类事件发生的核心原因在于基础设施的不完善,即不够去中心化。对于二层而言,排序器不去中心化最直接危害的在于抗审查性和活性。以太坊台北负责人 NIC Lin 实验了 4 个主流 Rollup 的抗审查交易功能,从工作流程和操作方法等方面深入的分析了 Force Inclusion 的机制设计

就在昨天发生了一起震惊无数人的事情:由Metamask母公司Consensys推出的以太坊二层Linea主动停机了,官方称这么做的目的是为了降低Velocore黑客攻击事件的影响。而这不由得让人想起之前BSC链(BNB Chain)为了降低黑客攻击的损失,在官方主动协调下停机一事。每当人们谈论起这种事情,都会对Web3倡导的去中心化价值感到怀疑。

当然,上述事件发生的核心原因,更多在于基础设施本身的不完善,即不够去中心化:如果一条链足够去中心化,那么就不该说停就停。由于以太坊二层的独特构造,大多数Layer2都依赖于中心化的Sequencer,虽然近些年去中心化排序器的论调越来越多,但考虑到二层的存在目的及其结构,我们大可以认为,Layer2的排序器大概率不会有多去中心化,最后可能还比不上BSC链的去中心化程度。如果事实真的如此,那么我们该怎么办?其实对于二层而言,排序器不去中心化带来的最直接危害,在于抗审查性和活性。如果处理交易的实体(Sequencer)很少,那么它在是否为你服务这件事上就掌握了绝对权力:想拒绝你就拒绝你,而你可能没有办法。如何解决Layer2的抗审查问题,显然是一个重要的话题。在过去的数年中,各大以太坊二层针对抗审查问题提出了各种各样的解决方案,比如Loopring和Degate以及StarkEx的强制提款与逃生舱功能、Arbitrum及其他OP Rollup的Force Inclusion功能,这些方法都可以在一定条件下对Sequencer产生制衡,以防止其无端拒绝任意用户的交易请求。在今天的文章中,来自台北以太坊协会的NIC Lin现身说法,亲自实验了4个主流Rollup的抗审查交易功能,从工作流程和操作方法等方面深入的分析了Force Inclusion的机制设计,这对于以太坊社区和手握巨额资产的大户而言尤其具有参考价值。

交易审查与Force Inclusion

交易抗审查性(Censorship Resistance)对一条区块链来说非常重要,如果区块链能够任意审查并拒绝用户发起的交易,那就和一个Web2服务器没有两样。以太坊目前的交易抗审查能力来自于它为数众多的Validator,如果有人想审查Bob的交易、不让他的交易上链,要么就尝试买通网络中大部分Validator,要不就Spam整个网路,不断送出手续费比Bob更高的垃圾交易来抢占区块空间。不管是哪种方式,成本都会非常高。

注:在Ethereum目前的PBS架构中,审查交易的成本会降低不少,可以参考配合OFAC审查Tornado Cash交易的区块比例。当前的抗审查能力仰赖在OFAC及政府管辖范围之外的独立验证者及Relay。

但Rollup呢?Rollup不需要一大堆的Validator来确保安全性,即便Rollup只有一个中心化的角色(Sequencer)来产出区块,它也和L1一样安全。但安全和抗审查能力是两回事,即便一个Rollup和以太坊一样安全,但在只有一个中心化Sequencer的情况下,想审查任何用户的交易都行。


Sequencer可以拒绝处理用户的交易,导致用户资金被扣留无法离开该Rollup

Force Inclusion机制

与其要求Rollup有大量的去中心化的Sequencer,还不如直接利用L1的抗审查能力:

本来Sequencer就是要将交易数据打包送到L1的Rollup合约中,不如在合约里加入一个设计,让用户可以自行把交易插入到Rollup合约,这个机制就称为“Force Inclusion”。只要Sequencer没办法在L1层面审查用户,它就没法阻止用户在L1强制插入交易。这样一来,Rollup就可以继承L1的抗审查能力。


Sequencer无法审查使用者的L1交易,除非付出很高的成本

强制交易应该怎么生效?

如果允许通过Force Inclusion把交易直接写入到Rollup合约中(也就是立即生效),那Rollup的状态就会马上改变,例如Bob透过Force Inclusion机制插入一笔“转1000 DAI给Carol”的交易,如果交易立即生效,那最新的状态中Bob的余额会少1000 DAI,Carol会多1000 DAI。

如果Force Inclusion能直接把交易写进Rollup合约中并马上生效,那状态就会马上改变如果此时Sequencer也在链下收集交易,并把下一批交易送到Rollup合约上,就有可能被Bob强制插入并立即生效的交易给影响到。这种问题要极力避免,因此Rollup一般不会让Force Inclusion交易立即生效,而是先让用户把交易插入到L1上的等待队列中,进入“准备中”状态。Sequencer在把链下交易打包送上Rollup合约时,选择是否在交易序列里塞入前述交易,如果Sequencer一直无视这些处于“准备中”状态的交易,等窗口期结束后,用户可以把这些交易强制插入到Rollup合约中。


Sequencer可以决定在什么时候“顺便收入”等待队列中的交易

Sequencer还是可以拒绝处理等待队列中的交易

如果Sequencer长期拒绝,一段时间后任何人都可以通过Force Inclusion功能把交易强行插入到Rollup合约中接下来我们将依序介绍Optimism、Arbitrum、StarkNet及zkSync等四个较有名的Rollup的Force Inclusion机制实现。

Optimism的Force Inclusion机制

首先介绍Optimism的Deposit流程,这个Deposit不单是指把钱存进Optimism,还包括“把用户向L2发送的信息”送进L2。L2节点在收到新存入的消息后,会将消息转换成一笔L2交易去执行,送到消息指定的接收方。


使用者从L1 Deposit给L2的消息

L1CrossDomainMessenger合约

当一个用户要把ETH或ERC-20代币存进Optimism时,他会通过前端网页和L1上的L1StandardBridge合约互动,指定要存多少金额以及由哪个L2地址接收这些资产。L1StandardBridge合约会将消息传递至下一层的L1CrossDomainMessenger合约,这个合约主要作为L1与L2之间互相通讯的组件,L1StandardBridge便通过这个通用的通讯组件和L2上的L2StandardBridge交流,决定谁可以在L2铸造代币,或是谁可以从L1解锁代币。如果开发者需要开发一个在L1与L2之间互通、同步状态的合约,那他就可以搭建在L1CrossDomainMessenger合约之上。


使用者的消息透过CrossDomainMessenger合约从L1传递到L2

注:本文的部分图片中将CrossDomainMessager写成了CrossChainMessager

OptimismPortal合约

L1CrossDomainMessenger合约会再将消息送至最底层的OptimismPortal合约,OptimismPortal合约处理完后会抛出一个名为TransactionDeposited的事件,参数包含“发消息的人”、“收消息的人”,以及相关的执行参数。接著L2的Optimism节点会监听OptimismPortal合约抛出的Transaction Deposited事件,并把event里的参数转换为一笔L2交易,这个交易的发起者会是Transaction Deposited事件参数里指明的“发消息的人”,交易接收者就是事件参数里“收消息的人”,其他交易参数也是由上述事件中的参数而来。


L2节点会将OptimismPortalemit的Transaction Deposited事件参数转换成一笔L2交易

例如,这是某个用户透过L1StandardBridge合约存款0.01ETH的交易,这个消息及ETH一路传到OptimismPortal合约(地址是0xbEb5…06Ed),然后几分钟后被转换成L2交易:消息发起者是L1CrossDomainMessenger合约;接收者是L2上的L2CrossDomainMessenger合约;消息内容是L1StandardBridge收到了BoB的0.01ETH存款。这之后还会触发一些流程,比如为L2StandardBridge增发0.01枚ETH,再由后者转给Bob。

具体怎么触发

当你想把交易强制收纳进Optimism的Rollup合约中时,你要达到的效果是让一笔“从你的L2地址在L2上发起并要执行的交易”能顺利执行,这时你应该用自己的L2地址把消息直接提交给OptimismPortal合约(注意OptimismPortal合约其实在L1上,但OP的地址格式和L1地址格式一致,你直接用和L2账户相同地址的L1账户调用上述合约即可)。之后该合约抛出的Transaction Deposited事件转化的L2交易的“发起者”,才会是你的L2账户,此时交易格式和正常的L2交易一致。


从Transaction Deposited事件转换而成的L2交易中,发起人会是Bob自己;接收人是Uniswap合约;而且会附带指定的ETH,就像Bob自己发起L2交易一样

如果要调用Optimism的Force Inclusion功能,你要直接调用OptimismPortal合约的depositTransaction函数,将你想在L2执行的交易的参数填入我做了一个简单的Force Inclusion实验,这条交易想达成这样一件事:在L2上用我的地址自转账(0xeDc1…6909),并附带一个“force inclusion”的文字讯息。这是我透过OptimismPortal合约执行depositTransaction函数的L1交易,可以看到在其抛出的Transaction Deposited事件中,from和to都是我自己


剩下的opaque Data一栏里的值则编码了“调用deposit Transaction函数的人附带了多少ETH”、“L2交易发起者要把多少ETH发给接收者”、“L2交易GasLimit”及“给L2接收者的Data”等等信息。将上述信息解码后分别会得到:“调用deposit Transaction的人附加了多少ETH”:0,因为我并不是从L1存ETH到L2;“L2交易发起者要把多少ETH发给接收者”:5566(wei)“L2交易的GasLimit”:50000“给L2接收者的Data”:0x666f72636520696e636c7573696f6e,也就是“force inclusion”这个字串的16进制编码接着没多久就出现转换后的L2交易:一笔我转钱给自己的L2交易,金额是5566 wei,Data是“force inclusion”字串。而且可以注意到,在图中倒数第二行的Other Attributes中的TxnType(交易类型),显示是系统交易126(System),表示这笔交易不是我自己在L2发起的,是由L1交易的Deposited事件转换而来。


转换而成的L2交易

如果你要通过Force Inclusion调用L2合约、发送不同的Data,那无非就是将参数一一填入前面的deposit Transaction函数,只是要记得,要用和自己L2账户相同的L1地址去调用deposit Transaction函数,这样当Deposited Event转化为L2交易时,发起者就是你的L2账户。SequencerWindow前面提到的Optimism L2节点将Transaction Deposited事件转换成L2交易,其实这个Optimism节点指的是Sequencer,毕竟这关系到交易排序,所以只有Sequencer可以决定何时要将前述事件转换成L2交易。在监听到TransactionDeposited事件时,Sequencer并不一定会马上将event转换成L2交易,可以有一段延时,这段时间的最大值称为SequencerWindow。目前Optimism主网上的Sequencer Window为24小时,也就是当用户从L1存入一笔钱或Force Inclusion一条交易,最糟情况是24小时后才被收入到L2交易历史中。

Arbitrum的Force Inclusion机制

在Optimism中L1的Deposit操作会抛出一个Transaction Deposited事件,剩下的就是等待Sequencer收录上述操作;但在Arbitrum中发生于L1的操作(存钱或传消息给L2等)会被存在L1上的一个队列里,而不是单纯抛出个事件。Sequencer会被给予一段时间将上述队列里的交易纳入L2交易历史,如果时间到了Sequencer都没有作为,那任何人都可以去替Sequencer完成。


Arbitrum会在L1合约维护一个Queue,如果Sequencer没有主动处理Queue里的交易,时间到了任何人都可以把Queue里的交易强制收录到L2交易历史中Arbitrum的设计中,L1上发生的如存款等操作都要经由Delayed Inbox合约,顾名思义这里的操作都会延迟生效;另一个合约则是Sequencer Inbox,是Sequencer把L2交易上传到L1时的直接场所。每次Sequencer上传L2交易时,都可以顺便从Delayed Inbox取出一些待处理的交易一并写进交易历史中。


Sequencer写入新交易时可以顺便从DelayedInbox拿出交易一起写入

复杂的设计以及凡善可陈的参考资料

如果读者直接参考Arbitrum官方关于Sequencer及Force Inclusion的章节,会看到里面提到了Force Inclusion大致如何运作,以及一些参数名称和函数名称:使用者先去DelayedInbox合约调用sendUnsignedTransaction函数,如果Sequencer没在约24小时内收录,那使用者可以调用SequencerInbox合约的forceInclusion函数。然后Arbitrum官方也没把函数的链接附加在官网文档里,只能自己去看合约代码里相对应的函数。当找到sendUnsignedTransaction函数后,你发现竟然要自己填nonce值还有maxFeePerGas值。是哪个地址的nonce?是哪个网络上的maxFeePerGas?要怎么填比较好?没有文件参考,连Natpsec都没有。然后你还会在Arbitrum合约里发现一堆看着相似的函数:sendL1FundedUnsignedTransaction、sendUnsignedTransactionToFork、sendContractTransaction、sendL1FundedContractTransaction,一样没有文件告诉你这些函数的区别、该怎么用、参数该怎么填,连Natpsec都没有。

你抱著姑且一试的心态来试填参数并送出交易,想用试错的方式看能不能找出正确的用法,但发现这些函数全都会把你的L1地址做AddressAliasing,导致最终在L2上发起交易时的Sender根本是不一样的地址,于是你的L2地址一动不动。sendL2Message后来偶然点开Google搜索,才发现原来Arbitrum自己有一个Tutorial程式库,裡面有脚本示范怎么从L1发送L2交易(也就是Force Inclusion的意思),然后它列举的函数完全不是上面提到的任何一个,而是一个叫sendL2Message的函数,而且message参数要带入的竟然是用L2账户签完名的交易?谁会知道要“通过Force Inclusion送给L2的消息”竟然会是一笔“签完名的L2交易”?而且没有任何文件及Natspec解释什么时候用及如何使用这个函数。

结论:要手动产生一个Arbitrum的强制交易比较麻烦,建议就照著官方Tutorial跑Arbitrum SDK呗。Arbitrum不像其他Rollup有清楚的开发者文件及程式码附注,许多函数的用途和参数缺乏说明,导致开发者得花费比预期多更多的时间来接入和使用。我也在Arbitrum Discord上询问Arbitrum的人,但并没有得到令人满意的答案。在Discord上询问,对方也只会叫我去看sendL2Message,没有想要解释其他函数的功能(甚至是Force Inclusion文档里提到的sendUnsignedTransaction)是什么用途、怎么用、什么时候用。

StarkNet的ForceInclusion机制

很遗憾地,StarkNet目前还没有ForceInclusion机制。只有两篇在官方论坛上讨论到Censorship及ForceInclusion的文章。无法证明失败的交易上述原因其实是因为,StarkNet的零知识证明系统没办法证明一笔失败的交易,所以不能允许Force Inclusion。因为如果有人恶意(或无意)Force Include一笔失败的、无法被证明的交易,那StarkNet就会直接卡住:因为交易被强制收入后,Prover就必须证明该笔失败交易,但它却没办法证明。而StarkNet预期在v0.15.0版引入证明失败交易的功能,之后应该就可以进一步实现Force Inclusion机制。

zkSync的ForceInclusion机制

zkSync的L1->L2讯息传送以及Force Inclusion机制,都是透过MailBox合约的requestL2Transaction函数进行,使用者指定L2地址、calldata、附加的ETH数量、L2GasLimit值等,requestL2Transaction会将这些参数组合成一个L2交易,然后放进优先队列(PriorityQueue)中,Sequencer会在交易打包上传到L1时(通过commitBatches函数),说明要顺便从优先队列中拿出多少笔交易一起收录进L2交易记录中。zkSync在Force Inclusion形式上和Optimism很像,都是以发起者的L2地址(与L1地址一致)去调用相关函数,并填入资料(被呼叫者、calldata等等),而不是像Arbitrum一样是填一笔签完名的L2交易;但在设计上则是和Arbitrum一样,都是在L1维护一个队列Queue,并由Sequencer从Queue中拿出用户直接提交的待处理交易,并写入交易历史中。


如果你透过zkSync的官方桥去Deposit ETH,像是这笔交易,它便是去呼叫MailBox合约的requestL2Transaction函数,它会将这个Deposit ETH的L2交易放进优先队列中抛出一个NewPriorityRequest事件。因为合约把L2交易资料编码成一串bytes字串所以不易读,改成看这笔L1交易的参数的话,会看到参数中L2的接收方也是交易的发起人(因为是Deposit给自己),所以过一阵子这笔L2交易被Sequeuncer从优先队列拿出,并收录进交易历史时,它会在L2上被转换成一笔自己转给自己的交易,而转帐的金额就是交易发起人在L1的Deposit ETH交易中带上的ETH金额。


L1Deposit交易中,交易发起者和接收者都是0xeDc1…6909,金额是0.03ETH,calldata为空

L2上会出现一笔0xeDc1…6909自己转帐给自己的交易,交易类型(TxnType)是255,也就是系统交易接着我直接像之前实验OP的强制交易功能一样,调用zkSync的requestL2Transaction函数,发了一笔自转账:没有带任何ETH,calldata带入“force inclusion”字串的HEX编码。接著它被转换成L2上一笔自己转自己的交易,calldata裡是“force inclusion”的十六进制字串:0x666f72636520696e636c7573696f6e。


当Sequencer把交易从PriorityQueue拿出来并写进交易历史中,在L2上就会转换成相对应的L2交易透过requestL2Transaction函式,使用者可以用和L2地址一样的L1账户,在L1提交资料,指定L2接收方、附带的ETH金额以及calldata。如果使用者要call其他合约、带不同Data,那一样就是将参数一一填入requestL2Transaction函数。

还没有让使用者强制收录的功能

虽然L2交易放到优先队列中后,会顺便计算出这笔L2交易被Sequencer收录的等待期限,但目前zkSync设计中并没有让使用者能强制执行的Force Inclusion函数,等于是只做半套。也就是虽然有“收录等待期限”,但实际上还是“看Sequencer要不要收入”:Sequencer可以等到过期后才收入,也可以永远不再收入优先队列中任何交易。未来zkSync应该要加入相关函数,让使用者可以在收入有效期过了但都还没被Sequeuncer收录时,能强制把交易包含进L2交易历史,如此才是真正有效的Force Inclusion机制。

总结

L1靠为数众多的验证者们来确保网路的“安全性”及“抗审查能力”,Rollup因为都是由少数甚至单一的Sequencer来写入交易,抗审查能力更弱。因此Rollup需要有Force Inclusion机制来让使用者可以绕过Sequencer,将交易写入历史中,避免被Sequencer审查导致无法使用也无法把资金撤离该Rollup。Force Inclusion让使用者可以强制将交易写入历史中,但在设计上需在“交易是否能立即插入历史、立即生效”上做选择。如果允许交易立即生效,那就会对Sequencer产生负面影响,因为L2上等待被收入的交易都可能会被L1强制收入的交易所影响。因此目前Rollup的Force Inclusion机制都会先让L1上插入的交易进入等待状态,并让Sequencer有一段时间窗口来反应、来选择要不要收入这些等待中的交易。zkSync和Arbitrum都是在L1维护一个队列Queue,用来管理使用者从L1送出的L2交易或给L2的讯息。Arbitrum称为DelayedInbox;zkSync称为PriorityQueue但zkSync送出L2交易的方式和Optimism比较像,都是以L2地址去L1上发送消息,如此转换为L2交易后,其发起人才会是该L2地址。Optimism送L2交易的函数称为depositTransaction;zkSync称为requestL2Transaction。而Arbitrum则是生成一笔完整的L2交易并签名,然后透过sendL2Message函数送出,Arbitrum在L2上会透过签名还原签名者来作为L2交易的发起人。StarkNet目前还没有Force Inclusion机制;zkSync则是像做了半套的Force Inclusion,—有PriorityQueue且每个Queue裡的L2交易都有收录有效期限,但这个有效期限目前只是装饰用,实际上Sequencer可以选择完全不收入任何PriorityQueue裡的L2交易

声明:

  1. 本文转载自[极客 Web3],原文标题“理论与实践:以太坊Rollup的抗审查交易如何触发?”,著作权归属原作者[NIC Lin,Taipei Ethereum Meetup负责人],如对转载有异议,请联系Gate Learn团队,团队会根据相关流程尽速处理。

  2. 免责声明:本文所表达的观点和意见仅代表作者个人观点,不构成任何投资建议。

  3. 文章其他语言版本由Gate Learn团队翻译, 在未提及Gate.io的情况下不得复制、传播或抄袭经翻译文章。

以太坊Rollup的抗审查交易如何触发?

中级6/11/2024, 3:45:38 PM
Metamask 母公司 Consensys 推出的以太坊二层 Linea 主动停机,声称是为了降低 Velocore 黑客攻击事件的影响。这类事件发生的核心原因在于基础设施的不完善,即不够去中心化。对于二层而言,排序器不去中心化最直接危害的在于抗审查性和活性。以太坊台北负责人 NIC Lin 实验了 4 个主流 Rollup 的抗审查交易功能,从工作流程和操作方法等方面深入的分析了 Force Inclusion 的机制设计

就在昨天发生了一起震惊无数人的事情:由Metamask母公司Consensys推出的以太坊二层Linea主动停机了,官方称这么做的目的是为了降低Velocore黑客攻击事件的影响。而这不由得让人想起之前BSC链(BNB Chain)为了降低黑客攻击的损失,在官方主动协调下停机一事。每当人们谈论起这种事情,都会对Web3倡导的去中心化价值感到怀疑。

当然,上述事件发生的核心原因,更多在于基础设施本身的不完善,即不够去中心化:如果一条链足够去中心化,那么就不该说停就停。由于以太坊二层的独特构造,大多数Layer2都依赖于中心化的Sequencer,虽然近些年去中心化排序器的论调越来越多,但考虑到二层的存在目的及其结构,我们大可以认为,Layer2的排序器大概率不会有多去中心化,最后可能还比不上BSC链的去中心化程度。如果事实真的如此,那么我们该怎么办?其实对于二层而言,排序器不去中心化带来的最直接危害,在于抗审查性和活性。如果处理交易的实体(Sequencer)很少,那么它在是否为你服务这件事上就掌握了绝对权力:想拒绝你就拒绝你,而你可能没有办法。如何解决Layer2的抗审查问题,显然是一个重要的话题。在过去的数年中,各大以太坊二层针对抗审查问题提出了各种各样的解决方案,比如Loopring和Degate以及StarkEx的强制提款与逃生舱功能、Arbitrum及其他OP Rollup的Force Inclusion功能,这些方法都可以在一定条件下对Sequencer产生制衡,以防止其无端拒绝任意用户的交易请求。在今天的文章中,来自台北以太坊协会的NIC Lin现身说法,亲自实验了4个主流Rollup的抗审查交易功能,从工作流程和操作方法等方面深入的分析了Force Inclusion的机制设计,这对于以太坊社区和手握巨额资产的大户而言尤其具有参考价值。

交易审查与Force Inclusion

交易抗审查性(Censorship Resistance)对一条区块链来说非常重要,如果区块链能够任意审查并拒绝用户发起的交易,那就和一个Web2服务器没有两样。以太坊目前的交易抗审查能力来自于它为数众多的Validator,如果有人想审查Bob的交易、不让他的交易上链,要么就尝试买通网络中大部分Validator,要不就Spam整个网路,不断送出手续费比Bob更高的垃圾交易来抢占区块空间。不管是哪种方式,成本都会非常高。

注:在Ethereum目前的PBS架构中,审查交易的成本会降低不少,可以参考配合OFAC审查Tornado Cash交易的区块比例。当前的抗审查能力仰赖在OFAC及政府管辖范围之外的独立验证者及Relay。

但Rollup呢?Rollup不需要一大堆的Validator来确保安全性,即便Rollup只有一个中心化的角色(Sequencer)来产出区块,它也和L1一样安全。但安全和抗审查能力是两回事,即便一个Rollup和以太坊一样安全,但在只有一个中心化Sequencer的情况下,想审查任何用户的交易都行。


Sequencer可以拒绝处理用户的交易,导致用户资金被扣留无法离开该Rollup

Force Inclusion机制

与其要求Rollup有大量的去中心化的Sequencer,还不如直接利用L1的抗审查能力:

本来Sequencer就是要将交易数据打包送到L1的Rollup合约中,不如在合约里加入一个设计,让用户可以自行把交易插入到Rollup合约,这个机制就称为“Force Inclusion”。只要Sequencer没办法在L1层面审查用户,它就没法阻止用户在L1强制插入交易。这样一来,Rollup就可以继承L1的抗审查能力。


Sequencer无法审查使用者的L1交易,除非付出很高的成本

强制交易应该怎么生效?

如果允许通过Force Inclusion把交易直接写入到Rollup合约中(也就是立即生效),那Rollup的状态就会马上改变,例如Bob透过Force Inclusion机制插入一笔“转1000 DAI给Carol”的交易,如果交易立即生效,那最新的状态中Bob的余额会少1000 DAI,Carol会多1000 DAI。

如果Force Inclusion能直接把交易写进Rollup合约中并马上生效,那状态就会马上改变如果此时Sequencer也在链下收集交易,并把下一批交易送到Rollup合约上,就有可能被Bob强制插入并立即生效的交易给影响到。这种问题要极力避免,因此Rollup一般不会让Force Inclusion交易立即生效,而是先让用户把交易插入到L1上的等待队列中,进入“准备中”状态。Sequencer在把链下交易打包送上Rollup合约时,选择是否在交易序列里塞入前述交易,如果Sequencer一直无视这些处于“准备中”状态的交易,等窗口期结束后,用户可以把这些交易强制插入到Rollup合约中。


Sequencer可以决定在什么时候“顺便收入”等待队列中的交易

Sequencer还是可以拒绝处理等待队列中的交易

如果Sequencer长期拒绝,一段时间后任何人都可以通过Force Inclusion功能把交易强行插入到Rollup合约中接下来我们将依序介绍Optimism、Arbitrum、StarkNet及zkSync等四个较有名的Rollup的Force Inclusion机制实现。

Optimism的Force Inclusion机制

首先介绍Optimism的Deposit流程,这个Deposit不单是指把钱存进Optimism,还包括“把用户向L2发送的信息”送进L2。L2节点在收到新存入的消息后,会将消息转换成一笔L2交易去执行,送到消息指定的接收方。


使用者从L1 Deposit给L2的消息

L1CrossDomainMessenger合约

当一个用户要把ETH或ERC-20代币存进Optimism时,他会通过前端网页和L1上的L1StandardBridge合约互动,指定要存多少金额以及由哪个L2地址接收这些资产。L1StandardBridge合约会将消息传递至下一层的L1CrossDomainMessenger合约,这个合约主要作为L1与L2之间互相通讯的组件,L1StandardBridge便通过这个通用的通讯组件和L2上的L2StandardBridge交流,决定谁可以在L2铸造代币,或是谁可以从L1解锁代币。如果开发者需要开发一个在L1与L2之间互通、同步状态的合约,那他就可以搭建在L1CrossDomainMessenger合约之上。


使用者的消息透过CrossDomainMessenger合约从L1传递到L2

注:本文的部分图片中将CrossDomainMessager写成了CrossChainMessager

OptimismPortal合约

L1CrossDomainMessenger合约会再将消息送至最底层的OptimismPortal合约,OptimismPortal合约处理完后会抛出一个名为TransactionDeposited的事件,参数包含“发消息的人”、“收消息的人”,以及相关的执行参数。接著L2的Optimism节点会监听OptimismPortal合约抛出的Transaction Deposited事件,并把event里的参数转换为一笔L2交易,这个交易的发起者会是Transaction Deposited事件参数里指明的“发消息的人”,交易接收者就是事件参数里“收消息的人”,其他交易参数也是由上述事件中的参数而来。


L2节点会将OptimismPortalemit的Transaction Deposited事件参数转换成一笔L2交易

例如,这是某个用户透过L1StandardBridge合约存款0.01ETH的交易,这个消息及ETH一路传到OptimismPortal合约(地址是0xbEb5…06Ed),然后几分钟后被转换成L2交易:消息发起者是L1CrossDomainMessenger合约;接收者是L2上的L2CrossDomainMessenger合约;消息内容是L1StandardBridge收到了BoB的0.01ETH存款。这之后还会触发一些流程,比如为L2StandardBridge增发0.01枚ETH,再由后者转给Bob。

具体怎么触发

当你想把交易强制收纳进Optimism的Rollup合约中时,你要达到的效果是让一笔“从你的L2地址在L2上发起并要执行的交易”能顺利执行,这时你应该用自己的L2地址把消息直接提交给OptimismPortal合约(注意OptimismPortal合约其实在L1上,但OP的地址格式和L1地址格式一致,你直接用和L2账户相同地址的L1账户调用上述合约即可)。之后该合约抛出的Transaction Deposited事件转化的L2交易的“发起者”,才会是你的L2账户,此时交易格式和正常的L2交易一致。


从Transaction Deposited事件转换而成的L2交易中,发起人会是Bob自己;接收人是Uniswap合约;而且会附带指定的ETH,就像Bob自己发起L2交易一样

如果要调用Optimism的Force Inclusion功能,你要直接调用OptimismPortal合约的depositTransaction函数,将你想在L2执行的交易的参数填入我做了一个简单的Force Inclusion实验,这条交易想达成这样一件事:在L2上用我的地址自转账(0xeDc1…6909),并附带一个“force inclusion”的文字讯息。这是我透过OptimismPortal合约执行depositTransaction函数的L1交易,可以看到在其抛出的Transaction Deposited事件中,from和to都是我自己


剩下的opaque Data一栏里的值则编码了“调用deposit Transaction函数的人附带了多少ETH”、“L2交易发起者要把多少ETH发给接收者”、“L2交易GasLimit”及“给L2接收者的Data”等等信息。将上述信息解码后分别会得到:“调用deposit Transaction的人附加了多少ETH”:0,因为我并不是从L1存ETH到L2;“L2交易发起者要把多少ETH发给接收者”:5566(wei)“L2交易的GasLimit”:50000“给L2接收者的Data”:0x666f72636520696e636c7573696f6e,也就是“force inclusion”这个字串的16进制编码接着没多久就出现转换后的L2交易:一笔我转钱给自己的L2交易,金额是5566 wei,Data是“force inclusion”字串。而且可以注意到,在图中倒数第二行的Other Attributes中的TxnType(交易类型),显示是系统交易126(System),表示这笔交易不是我自己在L2发起的,是由L1交易的Deposited事件转换而来。


转换而成的L2交易

如果你要通过Force Inclusion调用L2合约、发送不同的Data,那无非就是将参数一一填入前面的deposit Transaction函数,只是要记得,要用和自己L2账户相同的L1地址去调用deposit Transaction函数,这样当Deposited Event转化为L2交易时,发起者就是你的L2账户。SequencerWindow前面提到的Optimism L2节点将Transaction Deposited事件转换成L2交易,其实这个Optimism节点指的是Sequencer,毕竟这关系到交易排序,所以只有Sequencer可以决定何时要将前述事件转换成L2交易。在监听到TransactionDeposited事件时,Sequencer并不一定会马上将event转换成L2交易,可以有一段延时,这段时间的最大值称为SequencerWindow。目前Optimism主网上的Sequencer Window为24小时,也就是当用户从L1存入一笔钱或Force Inclusion一条交易,最糟情况是24小时后才被收入到L2交易历史中。

Arbitrum的Force Inclusion机制

在Optimism中L1的Deposit操作会抛出一个Transaction Deposited事件,剩下的就是等待Sequencer收录上述操作;但在Arbitrum中发生于L1的操作(存钱或传消息给L2等)会被存在L1上的一个队列里,而不是单纯抛出个事件。Sequencer会被给予一段时间将上述队列里的交易纳入L2交易历史,如果时间到了Sequencer都没有作为,那任何人都可以去替Sequencer完成。


Arbitrum会在L1合约维护一个Queue,如果Sequencer没有主动处理Queue里的交易,时间到了任何人都可以把Queue里的交易强制收录到L2交易历史中Arbitrum的设计中,L1上发生的如存款等操作都要经由Delayed Inbox合约,顾名思义这里的操作都会延迟生效;另一个合约则是Sequencer Inbox,是Sequencer把L2交易上传到L1时的直接场所。每次Sequencer上传L2交易时,都可以顺便从Delayed Inbox取出一些待处理的交易一并写进交易历史中。


Sequencer写入新交易时可以顺便从DelayedInbox拿出交易一起写入

复杂的设计以及凡善可陈的参考资料

如果读者直接参考Arbitrum官方关于Sequencer及Force Inclusion的章节,会看到里面提到了Force Inclusion大致如何运作,以及一些参数名称和函数名称:使用者先去DelayedInbox合约调用sendUnsignedTransaction函数,如果Sequencer没在约24小时内收录,那使用者可以调用SequencerInbox合约的forceInclusion函数。然后Arbitrum官方也没把函数的链接附加在官网文档里,只能自己去看合约代码里相对应的函数。当找到sendUnsignedTransaction函数后,你发现竟然要自己填nonce值还有maxFeePerGas值。是哪个地址的nonce?是哪个网络上的maxFeePerGas?要怎么填比较好?没有文件参考,连Natpsec都没有。然后你还会在Arbitrum合约里发现一堆看着相似的函数:sendL1FundedUnsignedTransaction、sendUnsignedTransactionToFork、sendContractTransaction、sendL1FundedContractTransaction,一样没有文件告诉你这些函数的区别、该怎么用、参数该怎么填,连Natpsec都没有。

你抱著姑且一试的心态来试填参数并送出交易,想用试错的方式看能不能找出正确的用法,但发现这些函数全都会把你的L1地址做AddressAliasing,导致最终在L2上发起交易时的Sender根本是不一样的地址,于是你的L2地址一动不动。sendL2Message后来偶然点开Google搜索,才发现原来Arbitrum自己有一个Tutorial程式库,裡面有脚本示范怎么从L1发送L2交易(也就是Force Inclusion的意思),然后它列举的函数完全不是上面提到的任何一个,而是一个叫sendL2Message的函数,而且message参数要带入的竟然是用L2账户签完名的交易?谁会知道要“通过Force Inclusion送给L2的消息”竟然会是一笔“签完名的L2交易”?而且没有任何文件及Natspec解释什么时候用及如何使用这个函数。

结论:要手动产生一个Arbitrum的强制交易比较麻烦,建议就照著官方Tutorial跑Arbitrum SDK呗。Arbitrum不像其他Rollup有清楚的开发者文件及程式码附注,许多函数的用途和参数缺乏说明,导致开发者得花费比预期多更多的时间来接入和使用。我也在Arbitrum Discord上询问Arbitrum的人,但并没有得到令人满意的答案。在Discord上询问,对方也只会叫我去看sendL2Message,没有想要解释其他函数的功能(甚至是Force Inclusion文档里提到的sendUnsignedTransaction)是什么用途、怎么用、什么时候用。

StarkNet的ForceInclusion机制

很遗憾地,StarkNet目前还没有ForceInclusion机制。只有两篇在官方论坛上讨论到Censorship及ForceInclusion的文章。无法证明失败的交易上述原因其实是因为,StarkNet的零知识证明系统没办法证明一笔失败的交易,所以不能允许Force Inclusion。因为如果有人恶意(或无意)Force Include一笔失败的、无法被证明的交易,那StarkNet就会直接卡住:因为交易被强制收入后,Prover就必须证明该笔失败交易,但它却没办法证明。而StarkNet预期在v0.15.0版引入证明失败交易的功能,之后应该就可以进一步实现Force Inclusion机制。

zkSync的ForceInclusion机制

zkSync的L1->L2讯息传送以及Force Inclusion机制,都是透过MailBox合约的requestL2Transaction函数进行,使用者指定L2地址、calldata、附加的ETH数量、L2GasLimit值等,requestL2Transaction会将这些参数组合成一个L2交易,然后放进优先队列(PriorityQueue)中,Sequencer会在交易打包上传到L1时(通过commitBatches函数),说明要顺便从优先队列中拿出多少笔交易一起收录进L2交易记录中。zkSync在Force Inclusion形式上和Optimism很像,都是以发起者的L2地址(与L1地址一致)去调用相关函数,并填入资料(被呼叫者、calldata等等),而不是像Arbitrum一样是填一笔签完名的L2交易;但在设计上则是和Arbitrum一样,都是在L1维护一个队列Queue,并由Sequencer从Queue中拿出用户直接提交的待处理交易,并写入交易历史中。


如果你透过zkSync的官方桥去Deposit ETH,像是这笔交易,它便是去呼叫MailBox合约的requestL2Transaction函数,它会将这个Deposit ETH的L2交易放进优先队列中抛出一个NewPriorityRequest事件。因为合约把L2交易资料编码成一串bytes字串所以不易读,改成看这笔L1交易的参数的话,会看到参数中L2的接收方也是交易的发起人(因为是Deposit给自己),所以过一阵子这笔L2交易被Sequeuncer从优先队列拿出,并收录进交易历史时,它会在L2上被转换成一笔自己转给自己的交易,而转帐的金额就是交易发起人在L1的Deposit ETH交易中带上的ETH金额。


L1Deposit交易中,交易发起者和接收者都是0xeDc1…6909,金额是0.03ETH,calldata为空

L2上会出现一笔0xeDc1…6909自己转帐给自己的交易,交易类型(TxnType)是255,也就是系统交易接着我直接像之前实验OP的强制交易功能一样,调用zkSync的requestL2Transaction函数,发了一笔自转账:没有带任何ETH,calldata带入“force inclusion”字串的HEX编码。接著它被转换成L2上一笔自己转自己的交易,calldata裡是“force inclusion”的十六进制字串:0x666f72636520696e636c7573696f6e。


当Sequencer把交易从PriorityQueue拿出来并写进交易历史中,在L2上就会转换成相对应的L2交易透过requestL2Transaction函式,使用者可以用和L2地址一样的L1账户,在L1提交资料,指定L2接收方、附带的ETH金额以及calldata。如果使用者要call其他合约、带不同Data,那一样就是将参数一一填入requestL2Transaction函数。

还没有让使用者强制收录的功能

虽然L2交易放到优先队列中后,会顺便计算出这笔L2交易被Sequencer收录的等待期限,但目前zkSync设计中并没有让使用者能强制执行的Force Inclusion函数,等于是只做半套。也就是虽然有“收录等待期限”,但实际上还是“看Sequencer要不要收入”:Sequencer可以等到过期后才收入,也可以永远不再收入优先队列中任何交易。未来zkSync应该要加入相关函数,让使用者可以在收入有效期过了但都还没被Sequeuncer收录时,能强制把交易包含进L2交易历史,如此才是真正有效的Force Inclusion机制。

总结

L1靠为数众多的验证者们来确保网路的“安全性”及“抗审查能力”,Rollup因为都是由少数甚至单一的Sequencer来写入交易,抗审查能力更弱。因此Rollup需要有Force Inclusion机制来让使用者可以绕过Sequencer,将交易写入历史中,避免被Sequencer审查导致无法使用也无法把资金撤离该Rollup。Force Inclusion让使用者可以强制将交易写入历史中,但在设计上需在“交易是否能立即插入历史、立即生效”上做选择。如果允许交易立即生效,那就会对Sequencer产生负面影响,因为L2上等待被收入的交易都可能会被L1强制收入的交易所影响。因此目前Rollup的Force Inclusion机制都会先让L1上插入的交易进入等待状态,并让Sequencer有一段时间窗口来反应、来选择要不要收入这些等待中的交易。zkSync和Arbitrum都是在L1维护一个队列Queue,用来管理使用者从L1送出的L2交易或给L2的讯息。Arbitrum称为DelayedInbox;zkSync称为PriorityQueue但zkSync送出L2交易的方式和Optimism比较像,都是以L2地址去L1上发送消息,如此转换为L2交易后,其发起人才会是该L2地址。Optimism送L2交易的函数称为depositTransaction;zkSync称为requestL2Transaction。而Arbitrum则是生成一笔完整的L2交易并签名,然后透过sendL2Message函数送出,Arbitrum在L2上会透过签名还原签名者来作为L2交易的发起人。StarkNet目前还没有Force Inclusion机制;zkSync则是像做了半套的Force Inclusion,—有PriorityQueue且每个Queue裡的L2交易都有收录有效期限,但这个有效期限目前只是装饰用,实际上Sequencer可以选择完全不收入任何PriorityQueue裡的L2交易

声明:

  1. 本文转载自[极客 Web3],原文标题“理论与实践:以太坊Rollup的抗审查交易如何触发?”,著作权归属原作者[NIC Lin,Taipei Ethereum Meetup负责人],如对转载有异议,请联系Gate Learn团队,团队会根据相关流程尽速处理。

  2. 免责声明:本文所表达的观点和意见仅代表作者个人观点,不构成任何投资建议。

  3. 文章其他语言版本由Gate Learn团队翻译, 在未提及Gate.io的情况下不得复制、传播或抄袭经翻译文章。

即刻开始交易
注册并交易即可获得
$100
和价值
$5500
理财体验金奖励!
It seems that you are attempting to access our services from a Restricted Location where Gate 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.