2

Optimistic Rollup 的挑戰機制(二):Optimism OVM 2.0

 2 years ago
source link: https://medium.com/taipei-ethereum-meetup/optimistic-rollup-%E7%9A%84%E6%8C%91%E6%88%B0%E6%A9%9F%E5%88%B6-%E4%BA%8C-optimism-ovm-2-0-4420e5adc734
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Optimistic Rollup 的挑戰機制(二):Optimism OVM 2.0

本文延續上一篇對 Optimism OVM 1.0 挑戰機制的,介紹 Optimism 正在開發中的 OVM 2.0 挑戰機制。

先備知識包含

  • State Transition Function 的意思
  • 區塊鏈客戶端是什麼,例如 Geth
  • EVM 大致的運作方式、opcodes、Stack、Memory
  • Merkle Tree
  • 知道指令集( Instruction Set)的概念

Recap: OVM 1.0

上一篇介紹的 OVM 1.0:L1 的 Challenge 合約會在挑戰發起時,將 L2 交易相關的合約都部署到 L1 上,並模擬一次 L2 的交易,然後由交易完的狀態作出裁決,看挑戰者和被挑戰者誰才是對的。而「模擬一次 L2 的交易」這個過程會在一筆 L1 交易裡完成,這樣的優點是直覺、簡單,但背後的缺點是 Compiler、Geth 都要做客製化的修改。

https://medium.com/ethereum-optimism/introducing-evm-equivalence-5c2021deb306

改了以後,不只 Optimism 團隊難維護,所有要部署到 Optimism 上面的項目都不能直接把 L1 的合約搬來用,甚至連平常熟悉的開發工具都需要配合修改才能使用。雖然在使用者眼中挑戰機制看起來簡單清楚,但苦的是背後的 Optimism 團隊和開發者。

Non-interactive Proving -> Interactive Proving

所以在 OVM 2.0 中,Optimism 改成採用 Interactive Proving 的方式,讓挑戰者和被挑戰者先透過一連串的互動,逐步縮小範圍,找出彼此意見分歧的 那一個 步驟,再於合約內實際執行過一次該步驟,並用結果來決定勝負。

Interactive Proving,不過實際上他們之間的交互過程會是發生在鏈上合約而不是私下執行

最小執行單位

當我們到鏈上的挑戰合約去執行,得到最終結果來作出仲裁的時候,這個被執行的東西是一個最小執行單位。在 OVM 1.0 中最小執行單位是一筆 L2 交易,但換成 Interactive Proving 後,這個最小執行單位變成是一個 opcode 。

Interactive Proving 的優缺點

Interactive Proving 的缺點是需要考慮時間因素(你不能要求挑戰者和非挑戰者在短時間內都要在線上應戰),這樣一來是影響成功挑戰的可能性(搞不好被挑戰者剛好去度假),二來是可能會拉長原本的挑戰期(在挑戰期快到才發起挑戰),影響使用者或 L1 的應用。

但優點是不必再糾結於要在一筆 L1 交易內完成 L2 交易的模擬,而是可以聚焦在單一個 opcode 的執行上,除了執行簡單和不需擔心 block gas limit 外,更重要的是這些 opcode 可以不再限於是 EVM 的 opcode,而是包含 EVM 以外的其他所有 opcode(只要你能在合約內模擬的出來),像是 WASM。

這表示你可以在鏈上合約驗證鏈下各種計算(就像上一篇提到過的 Truebit 的概念)。想像今天我們把 Solana 變成一個 Optimistic Rollup,在上面發起挑戰並在最後模擬執行 Rust 程式碼的其中一個 opcode。

From EVM-in-EVM to ?

如果你看過前一篇介紹 OVM 1.0 的挑戰機制,裡面有提到過 EVM-in-EVM 的執行方式:寫一個合約模擬 EVM opcode 的執行,所以實際執行時會是 EVM-in-EVM。因此當 OVM 2.0 改成 Interactive Proving :在找到最後一個 opcode 才到鏈上合約去執行,那 OVM 2.0 就勢必要寫一個合約去模擬 opcode 的執行。EVM-in-EVM 肯定是其中一個選項,但如同前一篇提到的,EVM-in-EVM 的複雜度很高,所以 Optimism 團隊採用了另一個方法。

在介紹那個方法之前,要先介紹一下,要在鏈上去驗證一個 opcode 的執行,還會需要哪些資訊(單純寫出能模擬 opcode 執行的合約還不夠)。

挑戰者和被挑戰者所要解決的爭議

要解決爭議,除了能模擬 opcode 的執行還不夠,我們還會需要額外的資訊。例如:我們都同意 A transfer to B 這個動作會把錢從 A 身上轉給 B,但實際上 A 和 B 原本有多少錢?A 的錢夠嗎?如果挑戰機制沒辦法獲得這些資訊,那能執行 opcode 也沒有什麼用,還是會變成挑戰者、被挑戰者各執一詞。

需要全局狀態的資訊

這個全局狀態不只是我們平常認知的紀錄每個地址餘額的 state tree,而是包含(以 EVM 為例)執行過程中的 Memory、Stack 的狀態,甚至是 Program Counter(目前執行到合約的第幾個 opcode,以下以 PC 代稱)。每一個 opcode 的執行都會改變這些資訊,例如 ADD opcode 會從 Stack 上拿走兩個值,運算完後放回一個值,如果我們對 Stack 裡有什麼值沒有共識的話就沒辦法決定誰才是對的。

執行 ADD opcode,改變了 PC 及 Stack

或像是 PC,每次執行完一個 opcode,PC 就會 +1(除非是 JUMP),如果我們對合約程式碼目前執行到第幾個 opcode 都沒有共識(我說這一步應該要是 ADD,而你說應該要是 SUB ,雙方各執一詞),那挑戰機制根本沒有辦法決定誰才是正確的。

註:雙方在比對全局狀態的時候要比對的是全局狀態的 commit 值(例如把全局狀態 commit 成一棵 Merkle Tree,然後比對 Merkle Root),不會逐一比對全局狀態中哪一個值不一樣。

Binary Search 比對全局狀態,搜尋分歧的 opcode

以下會以圖例簡述這個過程:

Challenger 不同意這 500 個 opcode 執行後的 post state 而發起挑戰250 的結果一樣,表示一定是在之後的 opcode 產生分歧,試試 375375 的結果不一樣,表示一定是在更之前的 opcode 產生分歧,試試 312,然後再試試 343343 的結果不一樣,表示一定是在更之前的 opcode 產生分歧,試試 327,然後再試試 335

每次逐步縮減一半的搜尋範圍:[1, 500], [250, 500], [250, 375], [312, 375], [312, 343], [327, 343], [327, 335], [331, 335], [333, 335], [334, 335], [335],直到找到分歧的第 335 個 opcode。

接下來將介紹 OVM 2.0 的挑戰機制以及它是如何計算並 commit 全局狀態的。

Cannon

Cannon 是 OVM 2.0 將使用的 Interactive Proving 的挑戰機制,開發者是有名的 geohot,他也協助寫出 OVM 1.0 挑戰機制的 Compiler(正確來說應該是 Transpiler:input 一份合約的 opcode,將其中特定 opcode 替換成客製化 opcode),Cannon 正在積極地開發中。

State Transition Function

在進入到 Cannon 的細節之前,先講一下我對 State Transition Function 的理解(接下來會以這個解釋來進行):狀態經過一個事件(例如一筆交易)的執行後,變成另一個狀態。

  • pre state:交易執行前的狀態,例如我有 10 塊,你有 5 塊
  • 交易:例如我給你 5 塊
  • post state:交易執行後的狀態,例如我有 5 塊,你有 10 塊

而這個交易不一定是 EVM 交易,可以是其他任何會導致狀態改變的事件,例如一個購物網站新增一名會員,從原本的 99 名會員變成 100 名會員。

誰在跑 EVM,執行一筆交易,驗證其正確性?

答:客戶端,像是 Geth。Geth 載入 pre state,模擬 EVM 執行交易,得到 post state,完成狀態更新( State Transition)。

EVM 視角:聚焦在橘色的 EVM opcode 如何一個一個被執行

改成從 Geth 的視角來看待一筆交易的執行

想像一下今天我們不是從 EVM 的視角,照著 EVM opcode 來執行一筆交易,而是從 Geth 的視角,把 Geth 這一段 State Transition Function 的程式碼編譯成一堆 opcode(但不是 EVM opcode),然後載入 pre state,並照著這一堆 opcode 來執行,我們也會得到一樣的 post state。

聽起來可能有點抽象,但試著想像,你在執行一段 Geth 編譯成的 opcode(下圖中黃色的部分,以下簡稱 Geth compiled opcode),而 Geth compiled opcode 在做的事,是模擬 EVM opcode (下圖中橘色的部分)的執行。

把 Geth 這一段 STF 編譯成黃色的 opcodeGeth 視角:聚焦在黃色的 Geth compiled opcode 如何一個一個被執行

因此,與其思考如何設計一個機制來計算並 commit EVM opcode 執行前後的全局狀態(PC、stack、memory、storage 的狀態),我們其實就已經有一個現成、在做一模一樣的事的程式了,就是 Geth!Geth 程式的 memory 會記錄著模擬 EVM 執行當下 PC、stack、memory、storage 的狀態,所以我們只要把 Geth 程式的 memory commit 起來就行了!

像是 Geth 這個 Tracing 功能的介紹,你可以看到 Geth 能夠 output 出單一個 opcode 執行的相關資訊(包含 pcgasCoststackmemory 等),表示 Geth 在每一個 opcode 的執行都記錄著這些資訊:

https://geth.ethereum.org/docs/dapp/tracing

我們要做的就是在每個 Geth compiled opcode 的執行前後,把 Geth 程式裡的 memory commit 起來、做成一棵 Merkle Tree,並用 Merkle Root 來在挑戰機制裡比對。

現在我們有了 Geth compiled opcode,照著這些 opcode 執行就可以驗證一筆 EVM 交易的執行,而且在每一步前後都有全局狀態的 commit 值,那剩下的就是:當挑戰發生時,我們要能夠在 L1 EVM 去模擬 Geth compiled opcode。

minigeth to MIPS & MIPS-in-EVM!

首先先介紹下產生 Geth compiled opcode 的細節:把 Geth 裡不必要的 JSON-RPC 模組、PoW 模組拔掉(因為這些和挑戰機制無關),留下用來模擬 EVM、執行 STF 的模組,叫做 minigeth。接著把 minigeth 編譯成 MIPS opcode,這就是上面提到的(圖例中黃色的) Geth compiled opcode。

有了 Geth compiled opcode,剩下就是要能在 EVM 裡去模擬 Geth compiled opcode 的執行。所以 geohot 寫了一個合約用來模擬 MIPS opcode(MIPS-in-EVM),如此就可以在 EVM 裡去驗證 minigeth 的執行了。

鏈上的執行:在 EVM 裡透過 MIPS 合約模擬編譯成 MIPS 的 minigeth 的執行

當然你也可以選擇把 minigeth 編譯成 EVM opcode,然後用 EVM-in-EVM 去驗證 minigeth 的執行(先不論 EVM-in-EVM 的複雜度),但前提是你要先能把 Go 程式碼編譯成 EVM opcode。而因為已經有現成工具可以直接把 Go 程式碼編譯成 MIPS opcode,所以 Optimism 選擇寫一個模擬 MIPS 合約,用 MIPS-in-EVM 來驗證 minigeth 的執行。

那既然現在在 EVM 裡可以驗證「 minigeth 」的執行,這表示 minigeth 也可以驗證「EVM 驗證 minigeth 執行」的執行(因為 minigeth 本來就可以驗證 EVM 的執行)……

https://tenor.com/view/confused-no-nope-gif-13134027

所以 Cannon 實際上長這樣(官方 repo 的描述):

Readme in Cannon repo: https://github.com/ethereum-optimism/cannon/

一段 Go 程式碼(minigeth)在執行 EVM,EVM 裡在模擬 MIPS 執行一段 Go 程式碼(minigeth),那段 Go 程式碼在執行 EVM…

我第一次看到這一段 Cannon readme 裡的解釋時,我來回看了二十次還是看不懂「a Go code that runs an EVM emulating a MIPS machine running compiled Go code that runs an EVM」

一樣需要提供並證明 state

和 OVM 1.0 一樣,L1 沒辦法自己生出 state,還是需要挑戰者或被挑戰者自己去提供並證明 state。所以我們要再修改一下 minigeth,我們要把 minigeth 從資料庫讀取 Ethereum state 的邏輯,換成一個叫 Preimage Oracle 的元件。

首先,以下是 Geth 資料庫存資料的方式。即便 Ethereum state 是一棵 Merkle Tree,但實際上它不會是以這樣一個直白的方式存在資料庫裡。而是一筆一筆的 key to value 映射:

資料庫儲存 state 的方式,實際上是一堆 key value mappingvalue 的值是 key 的值的 Preimage

假設想拿到 Node4 的值:先知道 Root,再從資料庫透過 Root 拿到 Node1Node2,接著再透過 Node1 拿到 Node4,最後再拿到 Node4 的內容(Preimage)。其實這個過程就是一直在重複拿 Preimage 的過程!

所以 Preimage Oracle 就要來充當回應 Preimage 請求的角色,minigeth 問 Preimage Oracle Root 對應的值是什麼、Node1 對應到的值是什麼、Node4 對應到的值是什麼。

鏈上模擬時沒有一個實際的 DB 可以去讀取把讀取 DB 邏輯抽象化,換成去跟一個 Oracle 要 Preimage,如此鏈上鏈下都能模擬

而在鏈上模擬取資料這一段的方式就是挑戰者或被挑戰者預先塞好會被問到的資料,例如 Root -> (Node1, Node2)Node1 -> (Node3, Node4) 等等,然後開始模擬執行。平常在鏈下執行時則是透過 JSON RPC call 去取回 Preimage。

請求 Preimage 實際上是去讀取 memory ,所以這邊用 MIPS memory 的名稱取代 Preimage Oracle

註:請求 Preimage 的 opcode 會是一段去讀取 memory 的 opcode。這裡可以看到 MIPS 的 ReadMemory 裡是去 GetPreimage,而這個AddPreimage 的函式。

以下是 Cannon 的介紹文章、Twitter threads 以及 Cannon 的 Github repo

EVM Equivalence

OVM 2.0 改成驗證 Geth 執行而不是 EVM 執行後,就不再需要 Transpiler 來用客製化 opcode 替換特定的 EVM opcode,開發工具或是節點也不再需要配合 OVM 來修改,甚至未來遇到 EVM 升級,也不必煩惱要去更新模擬 EVM 的合約(例如新增一個 opcode),就只是改成驗證新版的 Geth 而已。相比於 OVM 1.0,OVM 2.0 可說是大幅地提升開發者的開發體驗以及大幅地減少 Optimism 團隊自己本身的技術債。

Cannon 目前還在開發中,且 OVM 1.0 的挑戰機制已經被暫停(表示沒有辦法被挑戰),所以 Optimism 的 operator(Optimism 團隊擔任)其實是可以提交一個假的 state,等待挑戰期結束,然後把錢都轉走的。

不過這其實和 Optimism L1 合約都是可升級合約的風險一樣,都要相信 Optimism 團隊。等到 Cannon 上線、operator 去中心化、Optimism 穩定並移除升級機制後,才能解除對 Optimism 團隊的信任。

L2BEATS 有提供詳細的 Optimism (或其他 L2)當前的風險評估:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK