Hardcore | In-depth understanding of Optimistic Rollup execution environment OVM

Hardcore | In-depth understanding of Optimistic Rollup execution environment OVM

Loading

The difficulty of Optimistic Rollup lies in OVM, which needs to simulate the execution of OVM on the basis of EVM and judge the correctness of the state.

Original title: “L2-Deep Understanding of OVM”
Written by: Star Li

Optimistic Rollup is a potential solution for Layer 2. I had some time on the weekend, and I flipped through the Internet. On the Internet, there are not many articles about Optimistic Rollup in-depth technology, and there are fewer articles about the details of the underlying technology of OVM. I am interested to take a look at the smart contracts related to the OVM function implemented by Optimism, which is very helpful for the understanding of Optimistic Rollup. To sum up, interested friends can take a look.

Optimistic Rollup vs. ZK Rollup

There are not many articles comparing these two Rollup programs on the Internet.

The main article:

https://medium.com/matter-labs/optimistic-vs-zk-rollup-deep-pe-ea141e71e075

Corresponding translated article:

https://www.chainnews.com/articles/932935429481.htm

For specific performance and safety comparisons, interested friends can directly read this article. I personally feel that because the solutions are not mature enough, the TPS that the current solutions can achieve are only theoretical values. There is no need for much discussion. Mainly talk about the difference between the two Rollup technologies:

Hardcore | In-depth understanding of Optimistic Rollup execution environment OVM

Both solutions are Rollup. All transaction information of Layer 2 will be “stored” in Layer 1 as CallData, and the status of Layer 2 will be synchronized to Layer 1 in time. The difference between the two is that the state of Layer 2 is guaranteed to be correct in Layer 1. Optimistic Rollup adopts the “inspection” method. Any node finds the status of Layer 2 and submits relevant proofs. If the wrong state is verified, the state of Layer 2 in Layer 1 needs to be rolled back, and the node that submits the wrong state is punished. The method adopted by ZK Rollup is straightforward. While submitting the status of Layer 2 to Layer 1, it also submits proof of related status changes. These certificates are all generated in Layer 2. In other words, when ZK Rollup submits Layer 2 status to Layer 1, it also submits the calculation proof of Layer 2 status transition. This proof of calculation is generated by a zero-knowledge proof algorithm. Simply put, if the state of the transition is complex, the longer it takes to generate a zero-knowledge proof.

Currently, ZK Rollup only supports simple account systems and world states, and cannot support complex world states such as smart contracts. Although Optimistic Rollup can support smart contracts, in fact, because the computing power of Layer 1 is relatively weak, smart contract support is also relatively limited. The execution environment of smart contracts supported by Optimistic Rollup is similar to EVM and is called OVM (Optimistic Virtual Machine).

OVM

OVM-Optimistic Virtual Machine. OVM is the execution environment for Layer 2 transactions. Because the status submitted to Layer 1 needs to be verified for correctness, Layer 1 needs to “replay” Layer 2 transactions, that is, Layer 1 needs to execute OVM transactions in some cases. The most complicated part of Optimistic Rollup is also here. It uses EVM to simulate OVM and executes Layer 2 transactions.

Hardcore | In-depth understanding of Optimistic Rollup execution environment OVM

Optimism implements the logic of EVM to simulate OVM, and the Github address of related projects.

The last submission of the code used in this article is as follows:

 commit ca1fede6c8cb9e4eacd8205c1d53284d0c8debdc  
    Author: Mark Tyneway   
    Date: Fri Oct 30 12:14:50 2020 -0700  

        deploy: use layer 2 chainid (#42)

The core code is in the contracts-v2/contracts/optimistic-ethereum/OVM directory. In addition to the OVM directory, the iOVM directory is the interface definition, and the libraries directory is the implementation of various libraries, including codecs, binary trees, and so on.

OVM/chain

The smart contract of Layer 1 uses two chains to maintain transaction information and state information, CanonicalTransactionChain and StateCommitmentChain.

Hardcore | In-depth understanding of Optimistic Rollup execution environment OVM

All transaction information of Layer 2 is submitted to Layer 1 through CallData batch by batch. The hash information of the transactions in each batch is organized into a Merkle tree. Simply put, CanonicalTransactionChain stores the Merkle tree root of batch transactions. These tree roots are used to determine whether a specific transaction is in the chain.

Hardcore | In-depth understanding of Optimistic Rollup execution environment OVM

The world state of Layer 2 is represented by the state changes of each transaction. The status of each transaction is also submitted to Layer 1 through batches. The states in each batch are also organized into a Merkle tree again. These tree roots are used to determine whether a certain state is in the chain.

For the specific storage information of the two chains, you can view the source code: OVM_CanonicalTransactionChain.sol and OVM_StateCommitmentChain.sol.

OVM/execute

Execute is the core logic executed by OVM in EVM, including ExecuteManager , StateManager and SafetyChecker . The corresponding source codes are: OVM_ExecutionManager.sol, OVM_SafetyChecker.sol and OVM_StateManager.sol.

ExecuteManager is the processing of the entire smart contract execution environment and instruction set. OVM actually uses the same instruction set logically as EVM, but in the OVM environment, especially when the EVM of Layer 1 executes OVM, these instruction sets need to be “escaped”. The reason why it is called OVM may be to a large extent to distinguish EVM and make it easy to express. A lot of instructions need to be escaped. Think of the implementation of OVM in Layer 1 as a virtual machine. These instructions include: TIMESTAMP, CALL, STATICCALL, DELEGATECALL, GASLIMIT, SLOAD, SSTORE, etc. The execution of a transaction starts from the run function of ExecuteManager:

 function run(  
             Lib_OVMCodec.Transaction memory_transaction,  
             address_ovmStateManager  
         )

The run function provides the executed transaction and the state before executing the transaction.

StateManager implements smart contract and account storage state management. ExecuteManager updates the state through StateManager when executing a transaction.

SafetyChecker checks whether the instruction set in the OVM instruction contract is normal and whether it exceeds the current executable range. The safety check is implemented by the isBytecodeSafe function of OVM_SafetyChecker.sol.

 function isBytecodeSafe(  
             bytes memory_bytecode  
         )  
             override  
             external  
             pure  
             returns (bool)  
         {

OVM/verification

Verification is the business logic called by OVM. In Layer 1, OVM execution is only needed to determine whether a transaction is executed correctly during verification. The verification logic includes BondManager (mortgage management), StateTransitioner (state transition management) and FraudVerifier (error state verification logic). FraudVerifier logic is the core logic. The logic call relationship of the entire verification process is as follows:

Hardcore | In-depth understanding of Optimistic Rollup execution environment OVM

By calling the initializeFraudVerification function, Layer 1 starts to verify whether the status of a certain transaction is correct. StateTransitioner prepares the world state before the transaction and the intermediate state storage of the transaction execution. After the world state is ready (proveContractState/proveStorageSlot), execute the transaction and update the state by calling the run function of ExecutionManager. The updated state generates the world state through the completeTransition function of StateTransitioner. The generated world state is compared with the submitted world state. If it is inconsistent, the node that previously submitted the world state will be punished by BondManager.

Carefully analyze the initializeFraudVerification and finalizeFraudVerification functions of FraudVerifier. Start with the initializeFraudVerification function:

 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 is the Merkle tree root of the previous world state. Through _preStateRootBatchHeader and _preStateRootProof, you can verify that a certain state is on the StateCommitmentChain.

 require(  
                 ovmStateCommitmentChain.verifyStateCommitment(  
                   _preStateRoot,  
                   _preStateRootBatchHeader,  
                   _preStateRootProof  
                 ),  
                 "Invalid pre-state root inclusion proof."  
             );

_transction information is the transaction information that needs to be verified. Through _txChainElement, _transactionBatchHeader and _transactionProof, you can verify whether a transaction is on CanonicalTransactionChain.

 require(  
                 ovmCanonicalTransactionChain.verifyTransaction(  
                   _transaction,  
                   _txChainElement,  
                   _transactionBatchHeader,  
                   _transactionProof  
                 ),  
                 "Invalid transaction inclusion proof."  
             );

After confirming that the transaction and state are legal, create a StateTransitioner to prepare to execute the transaction.

 transitioners[_preStateRoot] = iOVM_StateTransitionerFactory(  
                 resolve("OVM_StateTransitionerFactory")  
             ).create(  
                 address(libAddressManager),  
               _preStateRootProof.index,  
               _preStateRoot,  
                 Lib_OVMCodec.hashTransaction(_transaction)  
             );

The logic of executing the transaction is directly ignored. Interested friends can see the applyTransaction function of OVM_StateTransitioner.sol. After the transaction is executed, use the finalizeFraudVerification function to check the result of the world state after execution.

 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  
         )

First check whether the two world states provided exist on the 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."  
             );

And, ensure that the two states are continuous:

 require(  
               _postStateRootProof.index ==_preStateRootProof.index + 1,  
                 "Invalid post-state root index."  
             );

Check whether the world state executed by OVM is consistent with the submitted state:

 require(  
               _postStateRoot != transitioner.getPostStateRoot(),  
                 "State transition has not been proven fraudulent."  
             );

If inconsistent, you need to roll back the world state:

 ovmStateCommitmentChain.deleteStateBatch(  
               _postStateRootBatchHeader  
             );

And penalize the node that submits the world state:

 ovmBondManager.finalize(  
               _preStateRoot,  
               _postStateRootBatchHeader.batchIndex,  
                 publisher,  
                 timestamp  
             );

Simply put, the simulation of OVM in EVM involves two important points: 1/representation of the previous world state 2/execution of the current transaction. The whole logic involves multiple Layer 1 transactions. In addition, enough time is needed to ensure that the data on the chain can be synchronized and checked. Currently, the challenge process of the world state must be completed within 7 days after the corresponding transaction:

 /// The dispute period  
         uint256 public constant disputePeriodSeconds = 7 days;

to sum up

Optimistic Rollup is a potential solution for Layer 2. Like ZK Rollup, all transaction information will be “stored” in Layer 1 as CallData. In Layer 2, Optimistic Rollup executes smart contracts through OVM, and uses “inspection” to determine the correctness of the world state of Layer 2 in Layer 1. The difficulty of Optimistic Rollup is also in OVM. It is necessary to simulate the execution of OVM on the basis of EVM and judge the correctness of the state. Currently, the challenge period for Optimistic Rollup is 7 days. In other words, only the status 7 days ago is “OK” and will not be rolled back.

Source link: mp.weixin.qq.com