具體的效能和安全性對比,感興趣的小夥伴可以直接看這篇文章。個人覺得,因為方案都不夠成熟,目前方案能夠達到的TPS都只是理論值。沒必要太多的討論。主要說說兩種Rollup的技術實現的區別:
兩種方案都是Rollup,Layer2的所有Transaction的資訊都會作為CallData“儲存”在Layer1,並且Layer2的狀態也及時同步到Layer1。兩者的區別在於,Layer2的狀態在Layer1的正確性保證。Optimistic Rollup採用的是“檢察”的方式,任何一個節點發現Layer2的狀態的錯誤,提交相關的證明。如果錯誤的狀態被驗證,在Layer1的Layer2的狀態需要回滾,提交錯誤狀態的節點被懲罰。zk Rollup採用的方式直接了當,在向Layer1提交Layer2狀態的同時,提交相關狀態改變的證明。這些證明都是在Layer2生成。也就是說,zk Rollup在向Layer1提交Layer2狀態的同時,同時提交了Layer2狀態轉換的計算證明。這個計算證明是透過零知識證明的演算法產生。簡單的說,如果轉換的狀態複雜,生成零知識證明的時間越長。
目前,zk Rollup只是支援簡單的賬戶系統以及世界狀態,並不能支援智慧合約等複雜的世界狀態。Optimistic Rollup雖然能支援智慧合約,事實上,因為Layer1的計算能力比較弱,智慧合約的支援也比較有限。Optimistic Rollup支援的智慧合約的執行環境,類似EVM,稱為 OVM(Optimistic Virtual Machine)。
2. OVM
OVM - Optimistic Virtual Machine。OVM是Layer2交易的執行環境。因為提交到Layer1的狀態需要檢驗正確性,Layer1需要“重放”Layer2的交易,也就是說,Layer1在有些情況下需要執行OVM交易的執行。Optimistic Rollup最複雜的地方也在於此,用EVM模擬OVM,並執行Layer2的交易。
Optimism實現了EVM模擬OVM的邏輯,相關的專案的Github地址:
https://github.com/ethereum-optimism/contracts-v2
本文中使用的程式碼的最後一個提交資訊如下:
commit ca1fede6c8cb9e4eacd8205c1d53284d0c8debdc
Author: Mark Tyneway <admin@chaindaily>
Date: Fri Oct 30 12:14:50 2020 -0700
deploy: use layer 2 chainid (#42)
核心程式碼在contracts-v2/contracts/optimistic-ethereum/OVM目錄中。除了OVM目錄,iOVM目錄是介面定義,libraries目錄是各種庫的實現,包括編解碼,二叉樹等等。
2.1 OVM/chain
Layer1的智慧合約中用兩條鏈維護交易資訊和狀態資訊,分別是CanonicalTransactionChain和StateCommitmentChain。
Layer2的所有的交易資訊,一個個Batch的透過CallData提交到Layer1。每個Batch中的交易的Hash資訊組織成Merkle樹。簡單的說,CanonicalTransactionChain儲存的是一個個Batch交易的Merkle樹根。這些樹根用來判斷某個具體的交易是否在鏈中。
Layer2的世界狀態,透過一個個交易的狀態改變來表示。每個交易後的狀態也是透過一個個Batch提交到Layer1。每個Batch中的狀態,也再次組織成Merkle樹。這些樹根用來判斷某個狀態是否在鏈中。
具體兩條鏈的儲存資訊,可以檢視原始碼:OVM_CanonicalTransactionChain.sol和OVM_StateCommitmentChain.sol。
2.2 OVM/execute
execute是OVM在EVM執行的核心邏輯,包括ExecuteManager,StateManager以及SafetyChecker。對應的原始碼分別是:OVM_ExecutionManager.sol,OVM_SafetyChecker.sol和OVM_StateManager.sol。
ExecuteManager是整個智慧合約執行環境以及指令集的處理。OVM其實和EVM邏輯上採用同樣的指令集,但是在OVM的環境下,特別在Layer1的EVM執行OVM時,需要將這些指令集“轉義”。之所以叫OVM的原因,可能很大程度為了區分EVM,表述方便。蠻多指令需要轉義,把OVM在Layer1的實現想象成虛擬機器。這些指令包括:TIMESTAMP,CALL,STATICCALL,DELEGATECALL,GASLIMIT,SLOAD,SSTORE等等。一個交易的執行從ExecuteManager的run函式開始:
function run(
Lib_OVMCodec.Transaction memory _transaction,
address _ovmStateManager
)
run函式提供了執行的交易,以及執行交易前的狀態。
StateManager實現了智慧合約以及賬戶的儲存狀態管理。ExecuteManager在執行一個交易時會透過StateManager更新狀態。
SafetyChecker檢查OVM的指令合約中的指令集是否正常,有沒有超過目前可以執行的範圍。安全性檢查透過OVM_SafetyChecker.sol的isBytecodeSafe函式實現。
function isBytecodeSafe(
bytes memory _bytecode
)
override
external
pure
returns (bool)
{
2.3 OVM/verification
verification是OVM呼叫的業務邏輯。在Layer1,只是在驗證的時候才需要透過OVM執行判斷某個交易執行是否正確。verification邏輯中包括了BondManager(抵押管理),StateTransitioner(狀態轉換管理)和FraudVerifier(錯誤狀態驗證邏輯)。FraudVerifier邏輯是最核心的邏輯。整個驗證過程的邏輯呼叫關係如下:
透過呼叫initializeFraudVerification函式,開始讓Layer1開始驗證某個交易執行後的狀態是否正確。StateTransitioner準備交易之前的世界狀態以及交易執行的中間狀態儲存。在世界狀態準備就緒後(proveContractState/proveStorageSlot),透過呼叫ExecutionManager的run函式執行交易並更新狀態。更新後的狀態透過StateTransitioner的completeTransition函式生成世界狀態。生成的世界狀態和提交的世界狀態進行對比,如果不一致,之前提交世界狀態的節點透過BondManager進行懲罰。
仔細的分析一下FraudVerifier的initializeFraudVerification和finalizeFraudVerification函式。先從initializeFraudVerification函式開始:
function initializeFraudVerification(
bytes32 _preStateRoot,
Lib_OVMCodec.ChainBatchHeader memory _preStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _preStateRootProof,
Lib_OVMCodec.Transaction memory _transaction,
Lib_OVMCodec.TransactionChainElement memory _txChainElement,
Lib_OVMCodec.ChainBatchHeader memory _transactionBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _transactionProof
)
_preStateRoot是之前的世界狀態的Merkle樹根。透過_preStateRootBatchHeader和_preStateRootProof可以驗證某個狀態是在StateCommitmentChain上。
require(
ovmStateCommitmentChain.verifyStateCommitment(
_preStateRoot,
_preStateRootBatchHeader,
_preStateRootProof
),
"Invalid pre-state root inclusion proof."
);
_transction資訊是需要驗證的交易資訊。透過_txChainElement,_transactionBatchHeader以及_transactionProof可以驗證某個交易是否在CanonicalTransactionChain上。
require(
ovmCanonicalTransactionChain.verifyTransaction(
_transaction,
_txChainElement,
_transactionBatchHeader,
_transactionProof
),
"Invalid transaction inclusion proof."
);
在確定了交易以及狀態都合法後,建立StateTransitioner準備執行交易。
transitioners[_preStateRoot] = iOVM_StateTransitionerFactory(
resolve("OVM_StateTransitionerFactory")
).create(
address(libAddressManager),
_preStateRootProof.index,
_preStateRoot,
Lib_OVMCodec.hashTransaction(_transaction)
);
執行交易的邏輯,直接忽略,感興趣的小夥伴可以看OVM_StateTransitioner.sol的applyTransaction函式。交易執行完,透過finalizeFraudVerification函式檢查執行後的世界狀態的結果。
function finalizeFraudVerification(
bytes32 _preStateRoot,
Lib_OVMCodec.ChainBatchHeader memory _preStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _preStateRootProof,
bytes32 _postStateRoot,
Lib_OVMCodec.ChainBatchHeader memory _postStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _postStateRootProof
)
先檢查提供的兩個世界狀態是否在StateCommitmentChain上存在:
require(
ovmStateCommitmentChain.verifyStateCommitment(
_preStateRoot,
_preStateRootBatchHeader,
_preStateRootProof
),
"Invalid pre-state root inclusion proof."
);
require(
ovmStateCommitmentChain.verifyStateCommitment(
_postStateRoot,
_postStateRootBatchHeader,
_postStateRootProof
),
"Invalid post-state root inclusion proof."
);
並且,保證兩個狀態是連續的:
require(
_postStateRootProof.index == _preStateRootProof.index + 1,
"Invalid post-state root index."
);
檢視OVM執行的世界狀態是否和提交的狀態一致:
require(
_postStateRoot != transitioner.getPostStateRoot(),
"State transition has not been proven fraudulent."
);
如果不一致,需要回滾世界狀態:
ovmStateCommitmentChain.deleteStateBatch(
_postStateRootBatchHeader
);
並且對提交世界狀態的節點進行懲罰:
ovmBondManager.finalize(
_preStateRoot,
_postStateRootBatchHeader.batchIndex,
publisher,
timestamp
);
簡單的看,OVM在EVM的模擬,涉及到兩個重要的點:1/之前世界狀態的表示 2/當前交易的執行。整個邏輯涉及到多次Layer1的交易,除此之外,還需要足夠的時間保證鏈上資料能夠同步並檢查。目前,世界狀態的挑戰過程必須在相應交易後的7天內完成:
/// The dispute period
uint256 public constant disputePeriodSeconds = 7 days;
總結:
Optimistic Rollup是Layer2潛在的一種方案。和zk Rollup一樣,所有Transaction的資訊都會作為CallData“儲存”在Layer1。在Layer2, Optimistic Rollup透過OVM執行智慧合約,並使用“檢察”的方式確定Layer2世界狀態在Layer1的正確性。Optimistic Rollup的難點也在OVM,需要在EVM的基礎上模擬OVM的執行,並判斷狀態的正確性。目前,Optimistic Rollup的挑戰期為7天。也就是說,只有7天前的狀態是“確定”的,不會回滾。