Bedrock Notes
TLDR changes
- Optimised batch compression
- Shortening delays of including L1 transactions into rollups
- Handing re-orgs more gracefully
- Enabling modular proof systems
- Improving performance (technical debt removed)
Lower Fees
- Optimised data compression strategy applied (think zlib?)
- Bedrock removes all gas costs associated with EVM execution when submitting data to L1.
- This reduces fees by an additional 10%.
Shorter deposit times
- L1 re-org support (thus reducing amount of time a user must wait for deposits)
- Improvement from 10 min → 3 min
Improved proof modularity
- Bedrock abstracts the proof system from the OP Stack so that a rollup may use either a fault proof or validity proof (e.g. a zk-SNARK) to prove correct execution of inputs on the rollup.
- This abstraction enables systems like Cannon to be used to prove faults in the system.
Improved node performance
- Technical debt removed
- This includes removing the need for a separate “data transport layer” node to index L1.
Improved Ethereum equivalence
- Bedrock aims to be as close to Ethereum as possible.
- Bedrock adds support for:
- EIP-1559
- Chain re-orgs
- and more
Design Principles
Modularity
- Separation of rollup node and execution client.
- Modular fault proof design
Code re-use
- Bedrock uses existing Ethereum architecture and infrastructure as much as possible.
- Minimally modified execution clients
- EVM contracts instead of precompiled client code
Ethereum equivalence
- Bedrock is designed to have maximum compatibility with the existing Ethereum developer experience.
- Fault proof designed to prove faults of minimally modified Ethereum execution client.
- Code re-use of Ethereum execution client for use by nodes in the L2 network and sequencers.
Protocol
Rollups are derived from a DA source.
In the most common configuration, rollup protocols derive a “canonical L2 chain” from 2 primary sources of information:
- Transaction data posted by sequencers to L1
- Deposit transactions posted by accounts and contracts to a bridge contract on L1
The following are fundamental components of the protocol:
- Deposits are ‘writes’ to the canonical L2 chain - they interact with smart contracts on L1
- Withdrawals are ‘writes’ to the canonical L2 chain - they implicitly trigger interactions with contracts and accounts on L1
- Batches are writes of data corresponding to batches on the rollup
- Block derivation is how reads of data on the L1 are interpreted to understand the canonical L2 chain
- Proof systems define finality of posted output roots on L1 (they may executed upon - e.g. to execute withdrawals)
Deposits
A deposit is a transaction on L1 that is to be included in the rollup.
- Deposits are guaranteed by definition to be included in the canonical L2 chain as a mean of preventing censorship or control of the L2.
Arbitrary message passing from L1
- Deposited transaction → the transaction on the rollup that is made as part of a deposit.
- With Bedrock, deposits are fully generalised Ethereum transactions.
- E.g. an account or contract on Ethereum can deposit a contract creation.
- Bedrock defines a deposit contract that is available on the L1.
- It is a smart contract that L1 accounts (EOAs, contracts) can interact with to write to the L2.
- Deposited transactions on the L2 are derived from the values in the event(s) emitted by this deposit contact (which excludes parameters such as from, to, and data)
Purchasing guaranteed L2 gas on L1
Bedrock also specifies a gas burn mechanism and a fee market for deposits.
- The gas that deposited transactions spend on an L2 is bought on L1 via a gas burn.
- This gas is purchased on a fee market.
- There is a hard cap on the amount of gas provided to all deposits in a single L1 block.
- This mechanism is used to prevent DOS attacks that can occur by writing transactions to L2 from L1 that are gas-intensive on L2, but cheap on L1.
- This gas deposit is sometimes called guaranteed gas.
- Guaranteed gas is paid for by burning gas on L1 (thus non-refundable)
- The total amount of L1 gas that must be burnt per unit of guaranteed L2 gas depends on the price of L2 gas (reported by a EIP-1559-like fee mechanism).
- Users receive a dynamic gas stipend based on the amount of L1 gas spent to compute updates to the fee mechanism.
Withdrawals
Withdrawals are cross-domain transactions that are initiated on L2 and finalised by a transaction executed on L1.
- Withdrawals may be used:
- by an L2 account to call an L1 contract.
- by an L2 account to transfer ETH to an L1 account.
- Withdrawals are initiated on L2 via a call to the Message Passer predeploy contract.
- Message Passer records the important properties of the message in its storage.
- Withdrawals are finalised on L1 via a call to the OptimismPortal contract.
- This contract proves the inclusion of this withdrawal message.
Withdrawals are different from deposits where instead of relying on block derivation, withdrawal transactions must use smart contracts on L1 for finalisation.
Two-step withdrawals
- A merkle proof corresponding to the withdrawal must be submitted 7 days before the withdrawal can be finalised.
Batches
A wire format is defined for messaging between the L1 and L2 (for L2 deriving blocks from L1 and for L2 to write transactions to L1).
This wire format is designed to:
- minimise costs
- minimise software complexity for writing to L1
Optimised data compression
- Sequencer batches = list of L2 transactions
- Channels = sequencer batches organised into a group of objects
- Both sequencer batches and channels have a maximum size defined in a configurable parameter (initially to be set to ~9.5 Mb).
- Channels are to be compressed and submitted to L1.
Parallelised batch submission
- Messages from the sequencer (containing compressed channel data) are parallelised by:
- Breaking down channels into channel frames.
- Channel frames = chunks of compressed channel data that can fit inside a single L1 transaction.
Minimised usage of Ethereum gas
- Batcher transactions → L1 transactions that submit channel data (thus removing all execution gas used by the L1).
- All validation logic relies on block derivation logic (instead of L1 smart contracts).
- Batcher transactions are sent to a single EOA on Ethereum known as the batch inbox address.
Block Derivation
The protocol is designed to guarantee that the timing of deposits on L1 is respected with regards to the block derivation of the canonical L2 chain.
We achieve this with a pure function of data written to the L1 by:
- Sequencers
- Deposits
- L1 block attributes
The protocol thus defines strategies for:
- Guaranteeing inclusion of deposits
- Handling L1 and L2 timestamps
- Processing sequencing windows in a pipeline to ensure correct ordering
Guaranteed inclusion of deposits
- Block derivation protocol needs to ensure that:
- An L2 block is created every “L2 block time” number of seconds.
- The timestamp of an L2 block stays in sync with the timestamps of L1 (important to ensure deposits are acknowledged in a logical temporal order).
- Sequencing epoch = a range of L2 blocks derived from a range of L1 blocks
- Each epoch is identified by an epoch number (block number of first L1 block in the sequencing window).
- Batch derivation pipeline treats the timestamps of the L1 associated with epoch number as the anchor point for determining the order of transactions on the L2.
- The protocol guarantees that the first L2 block of an epoch never falls behind the timestamp of the L1 block matching the epoch.
- The first blocks of an epoch must contain deposits on L1 in order to guarantee that deposits will be processed.
The target configuration for the block time on L2 in Bedrock is 2 seconds.
Handling L1 and L2 timestamps
Bedrock attempts to reconcile the timestamps on L2 with the timestamps on L1 present in deposited transactions.
This is achieved allowing a short window of time for sequencing to liberally apply timestamps on L2 transactions between epochs.
- A sequencing window is a sequence of L1 blocks from which an epoch can be derived.
- A sequencing window whose first L1 block has the number
N
contains batcher transactions for epochN
.
The sequencing window contains blocks
[N, N + SWS)
- SWS
is the sequencer window size (a fixed rollup-level configuration parameter).
Sequencer window size must be atleast 2.
* Increasing it → allows sequencer to order more L2 transactions wrt deposits.
* Decreasing it → stricter windows of time for sequencers to submit batch transactions.- Max sequencer drift = a protocol constant that governs the maximum timestamp a block can have within its epoch.
- This drift allows the sequencer to maintain liveness in case of temporary problems connecting to L1.
Each L2 block’s timestamp fits within the following range:
l1_timestamp <= l2_block.timestamp <= max(l1_timestamp + max_sequencer_drift, l1_timestamp + l2_block_time)
Block derivation pipeline
The canonical L2 chain can be processed from start with L2 genesis state, setting the L2 chain inception as the 1st epoch.
Afterwards, all sequencing windows are processed to determine the correct ordering of sequencer batches and deposits.
The following pipeline applies:
- Read from L1
- Epochs are defined by L1 blocks.
- L2 blocks contain batcher transactions or deposits which must be included in the canonical L2 chain.
- Buffer and decode into channels
- Data from L1 blocks contains unordered channel frames, which must all be collected before reconstructing them into channels.
- Decompress channels into batches
- Channels need to be decompressed (channels were compressed to minimise data fee costs on the L1).
- Queue batches into sequential order
- With the latest info from L1, batches can be validated and processed sequentially.
- Interpret as L2 blocks
- At this point, the correct ordering of batches can be determined.
- Thereafter, the execution client can interpret them into L2 blocks.
Fault Proofs
- After a sequencer processes one or more L2 blocks, the outputs computed from executing transactions in those blocks will need to be written with L1 for trustless execution of L2-to-L1 messaging, such as withdrawals.
- In Bedrock, outputs are hashed in a tree-structured form which minimises the cost of proving any piece of data captured by the outputs.
- Proposers periodically submit output roots that are Merkle roots of the entire canonical L2 chain to the L1.
Future upgrades of the OP Stack will include a specification for a variation of fault proof with bonding included to create incentives for proposers to propose correct output roots.
Implementation
Execution Client
- Execution client = the system that sequencers and other kind of node operators use to determine the state of the canonical L2 chain.
- Also performs stuff like processing inbound transactions and communicating them P2P, and handling the state of the system to process queries against it.
Handling deposited transactions
- To represent deposited transactions in the rollup, there is an additional transaction type introduced (implemented according to EIP-2718 typed transactions).
Charging transaction fees
Rollups have 2 kinds of fees associated with transactions:
- Sequencer fees
- Sequencer operation costs are computed using the same gas table as Ethereum and the same EIP-1559 algorithm.
- These fees go to the protocol for operating the sequencers and fluctuate based on the congestion of the network.
- Data availability fees
- DA costs are associated with writing batcher transactions to L1. These fees are intended to cover the cost that sequencers need to pay to submit batcher transactions to L1.
- The DA portion of the fee is determined based on information in a system contract on the rollup called a GasPriceOracle.
- This contract is updated during block derivation from the the gas pricing information retrieved from the L1 block attributes that get inserted at the beginning of every epoch.
Both these fees are added up into a single
gasPrice
field when using the JSON-RPC.Rollup Node
Verifying the canonical L2 chain
Participating in the L2 network
Sequencing transactions
Batcher
Standard Bridge Contracts