PoE - 1.5

Glossary

  • PoE > Proof-of-Efficiency model, consensus mechanism
  • bridge > contract that handles asset transfers between networks
  • GlobalExitRootManager > contract responsible for managing exit roots across multiple networks
  • GlobalExitRootManagerL2 > contract responsible for managing global exit root in zkEVM, this contract will be managed by the circuit

Motivation

For the implementation of the zk-EVM, a consensus mechanism for a decentralized L2 protocol is necessary.

This consensus mechanism defines a two-step model where:

  • Permissionless Sequencers as benefited participants in the protocol
  • The computation of a "virtual" state from the data availability and a "final" state based on the validity proofs
  • Space for permissionless Aggregators as the agents to perform the specialized task of cryptographic proof generation

Specification

This protocol separates batch sent and validated. Therefore, we find two parts:

  • Sequencers: collect L2 transactions from users. They select and create L2 batch in the network (sending ethereum transaction (encoded as RLP) with data of all selected L2 txs)
    • collect L2 txs and propose batches
    • pay MATIC to SC to propose a new batch
    • 1 sequencer / 1 chainID

The sequencer needs to register to get a chainID (Users needs to chose a chainID to sign. There's a default chainID valid for all sequencers. A sequencer can only send transactions with his chainID the default one

  • Aggregators: create validity ZK proof of a new state of the L2 (for one or multiple batches)
    • they have specialized hardware to create ZKP
    • the first aggregator that sends a valid proof wins the race and get rewarded (MATIC sent by the sequencer)
    • invalid transactions are skipped
      • The proof must process all the transactions, or either proof that the transactions are invalid.

Then, the two steps are done one for each part:

  • sendBatch: the sequencer sends a group of L2 transactions
  • validateBatch: the aggregator validates de batch

There are two state types:

  • Virtual state: state calculated from all pending batches of transactions
  • Validated state: state confirmed by zpk

Smart contract

Actions

  • registerSequencer
  • sendBatch
  • validateBatch

registerSequencer

  • Parameters:
    • sequencer RPC URL
  • Smart Contract Actions:
    • mapping[address => Sequencer] > Sequencer = {sequencerURL,chainID}
registerSequencer(sequencerURL) {
    mappingSeq[address] = { sequencerURL, chainID }
}

sendBatch

  • Parameters:
    • transactions bytes > The transactions should be encoded in the following format:[\(tx_0\) # \(tx_1\) # \(tx_2\) # # \(tx_n\) ] where a tx should be encoded as following:
    • tx = rlp(nonce, gasprice, gasLimit, to, value, data, chainid, 0, 0,) || v || r || s
    • matic max amount > uint256
  • Smart contract Actions:
    • Compute sequencer collateral (could be changed)
    ​​​​  function calculateSequencerCollateral() public view returns (uint256) {
    ​​​​    return 1 ether * (1 + lastBatchSent - lastVerifiedBatch);
    ​​​​}
    
    • Get bridge global exit root
    ​​​​ bytes32 lastGlobalExitRoot = bridge.getLastGlobalExitRoot();
    
    • Set chainID: default if the sequencer is not registered.

    • State updates > mapping[numBatch]

      • batchHashData = { H(txs, lastGlobalExitRoot, block.timestamp, msg.sender (sequencer address), batchChainID, numBatch)}
      • maticCollateral
    • Emit sendBatch event:

    ​​​​emit SendBatch(lastBatchSent, msg.sender, batchChainID, lastGlobalExitRoot);
    
sendBatch(bytes l2TxsData, uint256 maticAmount){
    maticCollateral = calculateMaticCollateral
    transfer(maticCollateral)
    lastBatchSent++
    currentGlobalExitRoot = bridge.currentGlobalExitRoot();
    mappingSentBatches[lastBatchSent] = batchHashData, maticCollateral})
    emit event SendBatch
}

invalid L2 tx are selected as NOP

verifyBatch

  • Parameters:
    • newLocalExitRoot
    • newStateRoot
    • numBatch (sanity check)
    • proofA, proofB, proofC
  • Smart contract Actions:
    • input:
      • currentStateRoot: current state root
      • currentLocalExitRoot: current local (rollup) exit root
      • newStateRoot: new state root
      • newLocalExitRoot: new local (rollup) exit root
      • batchHashData
    ​​​​**Buffer bytes notation**
    ​​​​[ 256 bits ] currentStateRoot
    ​​​​[ 256 bits ] currentLocalExitRoot
    ​​​​[ 256 bits ] newStateRoot
    ​​​​[ 256 bits ] newLocalExitRoot
    ​​​​[ 256 bits ] batchHashData
    
    • verify proof
    • update state
    • Communicate with bridge
      • push newLocalExitRoot
    • Get MATIC reward
    • Emit verify Batch event
    ​​​​ emit VerifyBatch(numBatch, msg.sender);
    
verifyBatch(newLocalExitRoot, newStateRoot, batchNum, proofA, proofB, proofC) {
    require(batchNum == lastConfirmedBatch + 1)
    input = calculateInput()
    require(verifyProof)
    lastVerifiedBatch++
    currentStateRoot = newStateRoot;
    currentLocalExitRoot = newLocalExitRoot;
    bridge.updateRollupExitRoot(currentLocalExitRoot);
    matic.transfer()
    emit event VerifyBatch
}
Select a repo