市場新聞

理論與實踐:以太坊Rollup的抗審查交易如何觸發?

By 極客 Web3 2024-06-03 03:52
理論與實踐:以太坊Rollup的抗審查交易如何觸發?

作者:NIC Lin,Taipei Ethereum Meetup負責人

原文標題:《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交易

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播信息之目的,不構成任何投資建議,如有侵權行為,請第一時間聯絡我們修改或刪除,多謝。

標題:理論與實踐:以太坊Rollup的抗審查交易如何觸發?

地址:https://www.pressbased.com/post/6141.html