Native rollups must at some point consume data from L1. The most straightforward example is deposits, but there might be many other use cases.
Then, the question is: How can the native rollup access L1 data in a verifiable way?
tl;dr: The L1 rollup contract constructs l1Context
(a subset of L1 state) in memory and passes it to EXECUTE
. L2 contracts can read this data and process it in any way.
We add a new argument l1Context
to EXECUTE
:
In the L1 rollup contract, we construct the L1 context as a list of deposit hashes and pass it to EXECUTE
.
After verifying the batch, we do a state read from L2 to see how many messages were actually processed in this batch.
On L2, we add a new precompile that allows reading from l1Context
(essentially a new data location). L2 contracts can read arbitrary data, the format and way to process this data varies by each rollup.
Relayers/sequencers can execute deposits by calling L2Bridge.relay
, using any transaction type.
We assume that the ETH total supply is pre-minted to L2Bridge
. This way we do not need to add a new transaction type to mint ETH.
Pros of this approach:
EXECUTE
would be the public input to the proof.Cons:
Option 2. Pass the L1 block hash.
The L1 block hash (or state root) can be part of payload
. Then, any L2 EVM contract inside the native rollup can query parts of the L1 state or ledger.
Cons:
Option 3. Native rollup node fetches data through standard JSON-RPC.
Since native-geth
can fully trust geth
, it can simply fetch any data using standard RPC calls. As long as we define what's allowed (e.g. only allow querying the recent 100 blocks), this is deterministic.
Cons:
Option 4. L1 rollup contract constructs batch with deposits.
Process deposits by constructing payload
inside L1 EVM and passing it to EXECUTE
.
Cons: