作者NIC Lin分享了關於Rollup的Force Inclusion機制的介紹。文章提到了以太坊二層Linea請求國有化以及BSC鏈為降低駭客攻擊的方式,探討了Web3的中心化價值。在介紹四個主流Rollup的抗交易功能時,特別強調了Force Inclusion的重要性。文章詳細分析了Optimism、Arbitrum、StarkNet和zkSync等Rollup的實作機制,並解釋了各自的操作方式。最後指出了Rollup的Force Inclusion機制在確保L2交易安全性和活性的重要性和局限性,以及各平台的設計差異。文章總結了Rollup的強制納入對於避免交易被拒絕和保障用戶資產無法離開的重要性。
作者:NIC Lin,台北以太坊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的機制設計,該設計針對以太坊社區和持有巨額資產的大戶而言尤其具有參考價值。
交易審查與強制納入
以太坊區塊鏈在我看來非常重要,如果區塊鏈能夠任意使用並且拒絕用戶開發工具包,那就和Web2Server沒有一樣的。以太坊區塊鏈在我看來能夠任意使用工具包,並且拒絕用戶開發工具包,那就和Web2Server沒有一樣的。以太坊區塊鏈在我看來能夠任意使用工具包,並且拒絕用戶開發工具包,那就和Web2Server沒有一樣的。以太坊區塊鏈在我看來能夠任意使用工具包,並且拒絕用戶開發工具包,那就一個Web2Server沒有一樣的。以太坊區塊鏈在我看來能夠任意使用工具包,並且拒絕用戶開發工具包,那就一個Web2Server沒有一樣的。
註:在Ethereum面前PBS架構中,我們降低了不少成本,可以參考配合OFAC審查Tornado Cash交易的區塊比例。目前的抗審查能力賴在OFAC及政府管轄範圍之外的獨立驗證者及Relay。
Rollup不需要一大堆的Validator來確保安全性,即便Rollup只有一個中心化角色(Sequencer)來產出區塊,央行和L1一樣安全。但同時,它還要抗衡審查能力是兩回事,即便一個Rollup和以太坊一樣安全,但在只有一個中心化Sequencer的情況下,想任何用戶的交易所都行。
Sequencer 可以拒絕處理用戶項目,導致用戶資金被扣留無法離開該Rollup
力量包容機制
它要求Rollup有大量的去中心化的Sequencer,但不如直接利用L1的抗審查能力:
原本Sequencer就是要將交易資料打包送到L1的Rollup合約中,不如在合約裡加入一個設計,讓人們自行把交易插入到Rollup合約,這個機制就稱為「強制包含」。只要Sequencer沒辦法在L1層面上用戶,它就沒辦法阻止用戶在L1強制插入交易。這樣一來,Rollup就可以繼承L1的抗審查能力。
Sequencer 無法使用其L1 交易,除非付出巨大的努力
直接交易應該生效嗎?
如果允許透過Force Inclusion把交易直接寫入Rollup合約中(也就立即生效),那麼Rollup的狀態就會馬上改變,例如Bob透過Force Inclusion機制插入金額「轉1000 DAI給Carol」的交易,如果交易立即生效,那麼最新的狀態中Bob的餘額會少1000 DAI,Carol會多1000 DAI。
如果強制納入能直接把交易寫進Rollup合約中並馬上生效,該狀態就會馬上改變
如果此時Sequencer也在鏈下收集交易,並把下一批交易送到Rollup合約上,就有可能被Bob強制插入並立即生效給交易到。這個問題要極力避免,因此Rollup一般不會讓Force Inclusion交易立即生效,而是先讓用戶把交易插入到L1上的等待隊列中,進入「準備中」狀態。
Sequencer 在把鏈下交易打包送上Rollup合約時,選擇是否在交易序列裡塞入初始交易,如果Sequencer 一直處於「準備中」狀態,那麼我們就可以把這些交易強制插入到Rollup合約中。
Sequencer可以決定什麼時候「順便收入」等待隊列中的交易
Sequencer 仍然可以拒絕等待隊列中的交易
如果Sequencer長期拒絕,一段時間後我可以透過Forceclusion功能把交易強行插入到Rollup合約中
接下來我們將依序介紹Optimism、Arbitrum、StarkNet及zkSync等四個較有名的Rollup的Force Inclusion機制實作。
樂觀的力量包容機制
首先介紹Optimism的Deposit流程,這個Deposit不單是指把錢存入Optimism,每當「把用戶向L2發送訊息」送入L2。 L2節點在收到新存入訊息後,會將訊息轉換為一筆L2交易去執行,傳送到訊息指定的接收方。
用戶從L1存款給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事件,並把事件裡的參數轉化為一筆L2交易,這個交易的發起者會是Transaction Deposited事件參數裡指明的“發訊息的人”,交易接收者就是事件參數裡“接收訊息的人”,其他交易參數也是由上述事件中的參數而來。
L2節點將OptimismPortalemit的Transaction Deposit事件參數得出一筆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交易一致。
從交易Deposited事件轉換而成的L2交易中,發起人是Bob自己;接收人是Uniswap合約;而且會附帶指定的ETH,就像Bob自己發起L2交易一樣
如果要呼叫Optimism的Force Inclusion功能,你要直接呼叫OptimismPortal合約的depositTransaction函數,然後你想要在L2執行伺服器上的參數填入
我做了一個簡單的強制包含實驗,該交易想實現這樣的一件事:在L2上用我的地址自轉帳(0xeDc1…6909),並附帶一個「強制包含」的文字訊息。
這是我透過OptimismPortal合約執行depositTransaction函數的L1交易,可以看到在其中拋出的Transaction Deposited事件中,從和到都是我自己
剩下的不透明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,也就是「強制包含」這個字串的16進位編碼
接著沒多久就出現轉換後的L2交易:一筆我轉錢給自己的L2交易,金額是5566威,資料是「強制包含」字串。而且可以注意,在圖中倒數第二行的Other Attributes中的TxnType(交易類型),顯示是系統交易126(System),表示這筆交易不是我自己在L2發起的,是由L1交易的Deposited事件轉換而來的。
轉換而成的L2交易
如果你要透過Force Inclusion呼叫L2合約、發送不同的Data,那無非就是將參數一一填入前面的deposit Transaction函數,只是要記得,要用和自己L2帳戶相同的L1地址去調用deposit Transaction函數,這樣當Deposited Event轉換為L2交易時,發起者就是你的L2帳戶。
序列視窗
前面提到的Optimism L2節點將Transaction Deposited事件轉換為L2交易,其實這個Optimism節點指的是Sequencer,畢竟這關係到交易排序,所以只有Sequencer可以決定何時要將先前的事件轉換為L2交易。
當監聽到TransactionDeposited事件時,Sequencer並不一定會馬上將事件轉換成L2交易,可以有一段延遲,這段時間的最大值稱為SequencerWindow。
目前Optimism主網上的Sequencer Window為24小時,也就是當用戶從L1存入一筆錢或強制包含一條交易,最糟的情況是24小時後才被收入到L2交易歷史上。
Arbitrum的強制納入機制
在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位址一動也不動。
發送L2訊息
後來偶然點開Google搜索,才發現原來Arbitrum自己有一個教程程式庫,裡面有腳本示範怎麼從L1發送L2交易(也就是強制包含的意思),然後它列出的函數完全不是上面提到的每一個,而一個叫sendL2Message的函數,而且訊息參數要帶入的竟然是用L2帳戶簽完名的線程礦池?
誰會知道要「透過Force Inclusion贈送L2訊息」竟然會是一筆「簽完名的L2交易」?而且沒有任何檔案及Natspec解釋什麼時候用及如何使用這個函數。
結論:要手動產生一個Arbitrum的強制交易比較問題,建議就照著官方Tutorial運行Arbitrum SDK唄。 Arbitrum不像其他Rollup有清楚的開發者文件及程式碼附註,許多函數的用途和參數缺乏說明,導致開發者花費比預期更多的時間來識別和使用。我也在Arbitrum Discord上詢問Arbitrum的人,但並沒有得到很好的答案。
在Discord上詢問,對方也會只會叫我去看sendL2Message,沒有想要解釋其他函數的功能(甚至是強制包含文件裡提到的sendUnsignedTransaction)是什麼用途、怎麼用、什麼時候用。
StarkNet 的ForceInclusion 機制
很遺憾地,StarkNet 目前還沒有ForceInclusion 機制。只有兩篇在官方論壇上討論到Censorship 及ForceInclusion 的文章。
無法設計
上述原因其實是因為,StarkNet的零知識證明系統沒辦法證明跨境失敗的交易,所以不能允許強制納入跨境交易。因為有些人惡意(或無意)強制納入跨境失敗的、無法被證明的交易,那StarkNet就會卡:因為交易被強制收入後,證明就必須證明該交易,但它卻沒辦法證明。
而StarkNet預期在v0.15.0版證明失敗的功能,之後應該可以進一步實現Force Inclusion機制。
zkSync的ForceInclusion機制
zkSync的L1->L2訊息傳送以及強制包含機制,都是透過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帶入「forceclusion」字符串的HEX編碼。
接著它被轉換成L2上一筆自己轉自己的交易,calldata裡是「forceclusion」的十六進位字串:0x666f72636520696e636c7573696f6e。
當Sequencer把交易從PriorityQueue拿出來並寫入交易歷史中,在L2上就會轉換成相對應的L2交易
透過requestL2Transaction函數,您可以使用和L2位址一樣的L1帳戶,在L1提交資料,指定L2接收者、附帶的ETH金額以及calldata。如果使用者要呼叫其他合約、帶上不同的數據,那就像將參數一一填入requestL2Transaction函數。
尚未讓使用者強制收錄功能
雖然L2交易放到優先隊列後,會順便計算出這筆L2交易被Sequencer收錄的等待期限,但目前zkSync設計中並沒有讓用戶能強制執行的Force Inclusion函數,等於只做半套。也就是雖然有“收錄等待期限”,但實際上還是“看Sequencer要不要收入”:Sequencer可以等到過期後才收入,也可以永遠不再收入優先隊列中任何交易。
未來zkSync應該要加入相關函數,讓用戶可以收入有效期過了但還沒有被Sequeuncer收錄時,能強制把交易包含進L2交易歷史,如此才真正有效的強制包含機制。
總結
L1靠為眾多的驗證者們來確保網路的“安全性”“抗能力”,Rollup因為都是由少數甚至單一的Sequencer來寫入交易,抗能力較弱。因此Rollup需要有強制包含機制來讓使用者可以環繞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 目前還沒有強制包含機制;zkSync包含了像是做了半套的強制包含,—有PriorityQueue且每個Queue裡的L2交易都有收錄有效期限,但這個有效期限目前只是裝飾用,實際上Sequencer可以選擇完全不收入任何PriorityQueue裡的L2交易
資訊來源:0x資訊編譯自網際網路。版權歸作者極客Web3所有,未經許可,不得轉載