升級
的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取出一些待处理的交易一并写进交易历史中。
复杂的设计以及凡善可陈的参考资料
如果读者直接参考Arbitrum官方关于Sequencer及Force Inclusion的章节,会看到里面提到了Force Inclusion大致如何运作,以及一些参数名称和函数名称:
使用者先去Delayed Inbox合约调用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金额。
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交易。
标签
DAI
DATA
LRC
Rollup
以太坊
以太坊L2
币安币
平台币
来源链接:
https://mp.weixin.qq.com/s/5h0TXPegnyzmPl3X-nnqlg
说明:比推所有文章只代表作者观点,不构成投资建议
原文链接:https://www.bitpush.news/articles/6837311
相关新闻
V神的傲慢与偏见
吴说周精选:MtGOX 大范围转移、ETH ETF 或 6 月底启动、Babylon 获投资 7000 万美金与新闻Top10
寻找牛市催化剂: 加密消费应用
深度解读:台湾与东南亚的加密货币行业监管现状如何?
34 项指控罪名成立,「加密太祖」特朗普会下「诏狱」还是再临白宫?
Add A Comment