Optimism OVM

OVM ( Optimistic Virtual Machine)

OVM是相对于EVM的概念,是Layer2上交易的执行环境,但既然Layer2上也有EVM,为什么还要做个OVM呢?

作用 (欺诈证明)

这是因为Layer2上的交易最终要把交易的执行状态存储在layer1上(SCC Chain 合约)。但为了防止有人做恶,要验证这个交易的状态是否正确。就需要在Layer1上 重新运行这个Layer2的交易(重放交易)。所以Layer2上的chainID要和Layer1上的chainID要保持一致。

OVM OPCODE

为了保持这个交易在Layer2上和Layer1上运行的结果一样,就需要修改EVM上的某些OpCode,,例如加载或存储状态或获取当前时间戳,则它们在L1和L2上的行为将不同。例如:

L2交易调用TIMESTAMP 操作码,例如返回1610889676, 一个小时后,如果我要在layer1上验证这个交易,交易都必须在以太坊L1上重放,如果继续使用EVM 执行这个OPCODE的话,则返回的是 1610889676 +3600 。这就会导致两次执行的交易结果不一致。达不到 验证交易的效果。如果使用OVM的TIMESTAMP,则可以返回
1610889676 。因为OVM重写这个操作码后,返回的是当时这个交易的timestamp.这就保证一致性了。这个TIMESTAMP OPCODE在OVM叫 ovmTIMESTAMP。

所有的这种与当时环境相关的opcde会改为ovm{opcode}

这些不安全操作码包括下面的操作,如果EVM中没有实现的操作符也是不安全因素。也是不允许的。

The following opcodes are disallowed:

ADDRESS
BALANCE
ORIGIN
EXTCODESIZE
EXTCODECOPY
EXTCODEHASH
BLOCKHASH
COINBASE
TIMESTAMP
NUMBER
DIFFICULTY
GASLIMIT
GASPRICE
CREATE
CREATE2
CALLCODE
DELEGATECALL
STATICCALL
SELFDESTRUCT
SELFBALANCE
SSTORE
SLOAD
CHAINID
CALLER*
CALL*
REVERT*
* The CALLER, CALL, and REVERT opcodes are also disallowed, except in the special case that they appear as part of one of the following strings of bytecode:

CALLER PUSH1 0x00 SWAP1 GAS CALL PC PUSH1 0x0E ADD JUMPI RETURNDATASIZE PUSH1 0x00 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x00 REVERT JUMPDEST RETURNDATASIZE PUSH1 0x01 EQ ISZERO PC PUSH1 0x0a ADD JUMPI PUSH1 0x01 PUSH1 0x00 RETURN JUMPDEST
CALLER POP PUSH1 0x00 PUSH1 0x04 GAS CALL

OPCODE验证

那如何验证某个合约是否包含“非法”的opcode呢?在OVM 中实现了 OVM_SafetyChecker.sol,里面的isBytecodeSafe方法会在create布署合约的时候进行opcode的检查。如果包含非法操作符则return 0.否则返回1.

OVM编译器

既然OVM有单独的opcode,但我们又是用solidity写的合约。所以为了和EVM的opcode进行区分。Optimism自己实现了个OVM编译器。 https://github.com/ethereum-optimism/solidity 。

Layer2 客户端 (Optimistic Geth)

因为我们要操作laye2,比如,创建和发送交易。总要有个入口。为了更好的利用现有的资源,otimistic 就直接使用了以太坊最流行的客户端(go-ethereum). 我们叫做 L2Geth,在L2Geth中执行交易的过程和在Layer1上执行的过程是一样的。不一样的是交易的结构进行了修改,从下面的代码中可以看到增加了TransactionMeta这个 关于当前Layer1的信息。

type Transaction struct {
    data txdata
    meta TransactionMeta
    // caches
    hash atomic.Value
    size atomic.Value
    from atomic.Value
}

type TransactionMeta struct {
    L1BlockNumber     *big.Int          `json:"l1BlockNumber"`
    L1Timestamp       uint64            `json:"l1Timestamp"`
    L1MessageSender   *common.Address   `json:"l1MessageSender" gencodec:"required"`
    SignatureHashType SignatureHashType `json:"signatureHashType" gencodec:"required"`
    QueueOrigin       *big.Int          `json:"queueOrigin" gencodec:"required"`
    // The canonical transaction chain index
    Index *uint64 `json:"index" gencodec:"required"`
    // The queue index, nil for queue origin sequencer transactions
    QueueIndex     *uint64 `json:"queueIndex" gencodec:"required"`
    RawTransaction []byte  `json:"rawTransaction" gencodec:"required"`
}

通过这些信息,如果这个交易是从Layer1传过来的,则会由Sequener进行填充。如果是直接在Layer2上创建,则在 batch_submitter提交的时候,根据当 前Layer1的信息进行填充。

  private async _getBlock(blockNumber: number): Promise {
    const block = (await this.l2Provider.getBlockWithTransactions(
      blockNumber
    )) as L2Block
    // Convert the tx type to a number
    block.transactions[0].txType = txTypePlainText[block.transactions[0].txType]
    block.transactions[0].queueOrigin =
      queueOriginPlainText[block.transactions[0].queueOrigin]
    // For now just set the l1BlockNumber based on the current l1 block number
    if (!block.transactions[0].l1BlockNumber) {
      block.transactions[0].l1BlockNumber = this.lastL1BlockNumber
    }

    return block
  }
  

L2Geth打包交易后,会被batch_submitter 每隔一段时间,将 一批交易组成batch 交易,提交到layer1的CTC Chain合约中。调用的CTC Chain的合约方法是:appendSequencerBatch

你可能感兴趣的:(Optimism OVM)