Original title: Bedrock Explainer
Written by: Optimism Official
Compiled by: Frank, Foresight News
Bedrock is the name of the first official version of the OP Stack, a set of free and open source modular components designed to power the development of Optimism.
Improvement Summary
Bedrock improves upon its predecessor by:
Lower transaction fees using optimized batched transaction compression and Ethereum as a data availability layer;
Improved latency in packaging L1 transactions into rollups through better handling of L1 reorganizations;
Enabling modular proof systems through code reuse;
Improve node performance by eliminating design debt.
Lower costs
Bedrock uses an optimized data compression strategy to minimize data costs. We are currently benchmarking the impact of this change, but we expect it to significantly reduce fees.
Bedrock also eliminates all gas costs associated with EVM execution when submitting data to L1, which will reduce fees by an additional 10% compared to previous versions of the protocol.
Shorter deposit crediting time
Bedrock introduced support for L1 reorganizations in the node software, which significantly reduced the time users had to wait for their deposits to be credited.
Previous versions of the protocol could take up to 10 minutes for deposits to be confirmed, but with Bedrock we expect deposits to be confirmed within 3 minutes.
Improved modular proof
Bedrock abstracts the proof system from the OP Stack so that rollups can use proofs of fault or validity (such as zk-SNARKs) to prove correct execution of inputs to the rollup. This abstraction will also allow the use of Cannon to prove faults in the system.
Improved node performance
Node software performance is significantly improved by executing multiple transactions in a single rollup "block" instead of the "one transaction per block" model of previous versions.
This allows the cost of Merkle Trie updates to be amortized across multiple transactions, which at current transaction volumes should reduce the growth of state data by approximately 15GB per year.
Node performance has also been further improved by eliminating technical debt from previous versions of the protocol, including removing the need for a separate “data transfer layer” node to index L1 and updating node software to efficiently query transaction data from L1.
Improved Ethereum Equivalency
Bedrock was designed from the outset to be as “consistent” with Ethereum as possible, and many deviations from Ethereum in previous versions of the protocol have been eliminated, including:
The "one transaction per block" model;
Custom opcode to get block information of L1;
Separate L1/L2 fee fields in JSON-RPC API;
A custom ERC20 representation of your ETH balance.
Bedrock also adds support for EIP-1559, blockchain reorganizations, and other Ethereum features that exist on L1.
Design Principles
Bedrock is built to be modular and upgradeable, while reusing Ethereum’s existing code and aiming to be as 100% Ethereum equivalent as possible.
Modularity
By using well-defined interfaces and a versioning scheme, Bedrock makes it easy to replace different components in the OP stack and add new features.
This allows it to adapt to future developments in the Ethereum ecosystem through a flexible architecture, such as:
The rollup node is separated from the execution client;
Modular fail-safe design.
Code Reuse
Bedrock uses existing Ethereum architecture and infrastructure as much as possible, an approach that enables OP Stack to inherit the security and Lindy effect benefits from the battle-tested codebase used by the Ethereum mainnet.
You'll find examples of this throughout the design, including:
Minimal modification of the execution client;
EVM contracts instead of precompiled client code.
Ethereum Equivalence
Bedrock is designed to be as compatible as possible with the existing Ethereum developer experience, but there are some exceptions due to the fundamental differences between L1 and rollup: different fee models, faster block times (2 seconds vs. 12 seconds), and special transaction types including L1 deposit transactions.
These exceptions include:
A proof of failure designed to demonstrate minimally modified Ethereum execution clients;
The Ethereum execution client reuses code for use by nodes and sorters in the L2 network.
protocol
Rollups are built on the availability of data (usually an L1 blockchain like Ethereum), and in the most common configuration, the rollup protocol derives a "canonical L2 chain" from two main sources of information:
Transaction data is published to L1 by the sequencer;
Deposit transactions are submitted by accounts and smart contracts to the bridge contract on L1.
The following are the basic components of the protocol:
Writing deposits to the “canonical L2 chain” by interacting directly with smart contracts on L1;
Withdrawals are written to the "canonical L2 chain" and implicitly trigger interactions with smart contracts and accounts on L1;
Batches are data writes corresponding to batches on the rollup;
How block derivation interprets data read on L1 to understand the “canonical L2 chain”;
The proof system defines the finality of the output roots published on L1 so that they can be executed (e.g. to perform withdrawals).
deposit
A deposit is a transaction on L1 and will be included in the rollup. By definition, a deposit is guaranteed to be included in the "canonical L2 chain" as a means of preventing censorship or control of L2.
Any message passed from L1
A deposit transaction is a rollup transaction that is made as part of a deposit. With Bedrock, deposits are fully generic Ethereum transactions, e.g. an account or smart contract on Ethereum can create a "deposit" contract.
Bedrock defines a deposit contract available on L1: it is a smart contract that L1 accounts and smart contracts can interact with to write to L2. Deposit transactions on L2 are derived from transactions issued by this deposit contract, including expected parameters such as from, to, and data.
See the Deposit Contract Protocol Specification section for more details.
Buying Secured L2 Gas on L1
Bedrock also clarified the Gas burning mechanism and deposit fee market. The Gas spent by deposit transactions on L2 is purchased on L1 through Gas burning.
This Gas is specifically purchased on the fee market, and there is a hard cap on the total amount of Gas provided to all deposit transactions in a single L1 block. This mechanism is used to prevent Denial of Service attacks (DoS) that could occur when writing transactions from L1 to L2 because these transactions are expensive on L2 but cheap on L1.
The Gas provided to the deposit transaction is sometimes referred to as “Guaranteed Gas.” Guaranteed Gas is unique in that it is paid for by burning Gas on L1 and is therefore non-refundable.
The total amount of L1 Gas that must be burned per unit of secured L2 Gas depends on the L2 Gas price reported by the EIP-1559-style fee mechanism. In addition, users receive a dynamic gas allowance based on the amount of L1 Gas spent to calculate fee mechanism updates.
For a more in-depth explanation, please read the protocol specification in the deposit section.
Withdrawals
Withdrawals are cross-layer transactions initiated on L2 and completed by transactions executed on L1. It is worth noting that L2 accounts can use withdrawals to call L1 contracts or transfer ETH from L2 accounts to L1 accounts.
Withdrawals are initiated by calling the Message Passer pre-deployed contract on L2, which records the important properties of the message in its storage, and then completes the withdrawal on L1 by calling the OptimismPortal contract to prove the inclusion of this withdrawal message.
In this way, withdrawals are different from deposits: withdrawal transactions must be completed using smart contracts on L1, rather than relying on information derived from the blockchain.
Two-step withdrawal process
Withdrawal proof validation errors have been the root cause of many cross-bridge hacks over the past few years. The Bedrock release introduces an extra step in the withdrawal process over previous releases that is designed to provide additional defense against these types of errors.
In the two-step withdrawal process, each withdrawal must submit a Merkle proof corresponding to the withdrawal 7 days before the final exit. This new security mechanism gives monitoring tools a full 7 days to find and detect invalid withdrawal proofs.
If a withdrawal proof is found to be invalid during this period, a smart contract fix can be deployed before the funds are lost, which greatly reduces the risk of cross-chain bridge compromise.
See the Withdrawal Protocol Specification section for more details.
Batches
In Bedrock, a wire format is defined for messaging between L1 and L2 (i.e., L2 derives blocks from L1, and L2 writes transactions to L1), which is designed to minimize the cost and software complexity of writing to L1.
Optimizing Data Compression
To optimize data compression, L2 transaction lists (called sequencer batches) are organized into groups of objects (called channels). The maximum size of each channel can be defined in a configurable parameter and will initially be set to 9.5M. These channels are expected to be compressed using the compression function and submitted to L1.
Batch parallel submission
To parallelize sequencer messages from submitting compressed channel data to L1, channels are further decomposed into "channel frames", which are chunks of compressed channel data that can fit into a single L1 transaction.
Assuming that "channel frames" are independent of each other and their order is known, Ethereum transactions sent by the sequencer to L1 can be sent in parallel, minimizing the complexity of the sequencer software and allowing all available data space on L1 to be filled.
Minimizing Ethereum Gas
Bedrock removes all execution gas used by the L1 system to submit channel data to L1 in transactions called "batcher transactions". All verification logic that previously occurred on L1 smart contracts has been moved into the block derivation logic. Instead, "batcher transactions" are sent to a single EOA address on Ethereum, called the "batch inbox address".
Batches are still subject to validity checks (i.e. they must be correctly encoded), as are individual transactions within a batch (e.g. signatures must be valid), invalid batches and invalid individual transactions within valid batches are considered discarded and irrelevant to the system.
NOTE: Ethereum will soon be upgraded to a new version containing EIP-4844, which introduces a separate market for data write fees and increases the upper limit on the amount of data the Ethereum protocol is willing to store, a change that is expected to further reduce the costs associated with publishing data to L1.
For a more in-depth explanation, read the wire format specification.
Block Derivation
In Bedrock, the protocol is designed to guarantee that the timing of deposits on L1 is tied to block derivations from the “canonical L2 chain.” This is done as a pure function of writing data to L1 via sequencers, deposits, and L1 block attributes.
To achieve this, the protocol defines strategies for guaranteeing deposits, handling L1 and L2 timestamps, and handling ordering windows in channels to ensure correct ordering.
Guaranteed deposit
The goals of the block derivation protocol are defined as follows:
After each "L2 block interval" has passed, there must be an L2 block, and the timestamp of the L2 block is kept in sync with the timestamp of the L1 (i.e. ensuring that the deposits are included in the logical time order).
In Bedrock, the concept of "sequencing epoch" is introduced: it is a range of L2 blocks derived from a series of L1 blocks, and each epoch is identified by an "epoch number", which is equal to the block number of the first L1 block in the sorting window. Subject to some restrictions, the size of epochs can vary.
The batch derived channel considers the timestamp of the L1 block associated with the "epoch number" as an anchor for determining the order of transactions on L2. The protocol guarantees that the first L2 block of an epoch will never lag behind the timestamp of the L1 block of the matching epoch. The first block of an epoch must contain a deposit on L1 to guarantee that the deposit will be processed.
Note that in the Bedrock release, the block interval target on L2 is configured to be 2 seconds.
Handling L1 and L2 timestamps
Bedrock attempts to solve the problem of reconciling timestamps on L2 with timestamps on L1 present in deposit transactions.
It does this by allowing a short time window for ordering to freely apply timestamps on L2 transactions between epochs.
A sequencing window is a sequence of L1 blocks from which epochs can be derived. The first L1 block numbered N in a sequencing window contains the "batcher transactions" of the epoch.
The ordering window contains blocks, which depends on the size of the ordering window: a fixed rollup level configuration parameter that must be at least 2, increasing it gives the sequencer more opportunities to order L2 transactions on deposits, lowering it introduces a stricter time window for the sequencer to submit "batcher transactions". This is a trade-off between creating MEV opportunities and increasing software complexity.
A protocol constant called max sequencer drift controls the maximum timestamp a block can have within its epoch; this drift allows the sequencer to remain active during temporary issues connecting to L1.
Block Derivation Pipeline
A “canonical L2 chain” can be processed from scratch by starting from the L2 genesis state, setting the L2 chain start to the first epoch, and then processing all ordering windows to determine the correct order of sequencer batches and deposits according to the following simplified pipeline:

Failure Proof
After the sequencer processes one or more L2 blocks, the outputs computed from transactions executed in those blocks will need to be written in L1 to enable trustless execution of L2 to L1 messaging, such as withdrawals.
In Bedrock, outputs are hashed in a tree structure, minimizing the cost of proving any piece of data captured by the output. Proposers periodically submit to L1 an output root that serves as the Merkle root of the entire "canonical L2 chain".
Future upgrades to the OP Stack should include the specification of a variant of fault proofs that incorporates bindings to incentivize proposers to propose correct output roots.
For full details, read the protocol specification in the L2 Output Roots proposal section.
implement
In Bedrock, the OP Stack has to rely heavily on the technical separation of concerns specified by Ethereum by mirroring the separation between Ethereum’s execution and consensus layers.
So Bedrock introduced the separation of execution clients and rollup nodes in the same way.
Execute the client
The execution client is the system that sequencers and other types of node operators run to determine the state of the "canonical L2 chain". It also performs other functions such as processing inbound transactions and peer-to-peer communication, and processing the system state to handle queries against it.
With Bedrock, OP Stack aims to reuse Ethereum's own execution client specification and many of its execution operations. In this release, Bedrock presents extremely limited modifications to the Ethereum client go-ethereum, with a difference of less than 2000 lines of code.
There are two fundamental reasons for the difference: processing deposit transactions and collecting transaction fees.
Processing deposit transactions
In order to represent deposited transactions in a rollup, an additional transaction type is introduced. The execution client implements this new transaction type according to the EIP-2718 type transaction standard.
Transaction fees
Rollups also have fundamentally two types of fees associated with transactions:
One is the sequencer fee. The cost of operating the sequencer is calculated using the same gas table and the same EIP-1559 algorithm as Ethereum. These fees are used to operate the sequencer's protocol and fluctuate at any time depending on network congestion.
Another data availability fee. Data availability costs are associated with writing batches of transactions to L1. These fees are designed to cover the costs that sequencers pay to submit batches of transactions to L1.
In Bedrock, data availability for fees is determined in part based on information in a rollup system smart contract called the GasPriceOracle, which is updated during block derivation based on gas price information retrieved from L1 block attributes inserted at the start of each epoch.
Bedrock specifies that both fees be added into a single field when using JSON-RPC.
Rollup Node
Unlike Ethereum, Bedrock does not have PoS consensus. Instead, the consensus of the "canonical L2 chain" is defined by block derivation. OP Stack's execution client communicates with a new component that implements block derivation called a rollup node, which communicates with the execution client using the exact same engine API as Ethereum.
A rollup node is a stateless component responsible for inferring the state of the system by reading data and deposits on L1. In Bedrock, rollup nodes can be used to order incoming transactions from users or other rollup nodes, or to verify confirmed transactions published on L1 by relying solely on L1.
The various uses of rollup nodes are outlined below:
Verifying a “standard L2 chain”
The simplest mode of running a rollup node is to just follow the "canonical L2 chain". In this mode, the rollup node has no peers and is strictly used to read data from L1 and interpret it according to the block derivation protocol rules.
One purpose of such a node is to verify that any output roots shared or published on L1 by other nodes are correct according to the protocol definition. In addition, proposers who intend to submit output roots to L1 can themselves generate the output roots they need using optimism_outputAtBlock, which returns a 32-byte hash value corresponding to the L2 output root.
To do this, nodes should only need to follow “finalized” block headers. The term “finalized” refers to the Ethereum PoS consensus (i.e. canonical and almost irreversible), while “finalized” L2 block headers are the block headers of the “canonical L2 chain” derived only from “finalized” L1 blocks.
Participating in L2 Networks
The most common way to use a rollup node is to participate in a network of other rollup nodes, tracking the progress and state of L2. In this mode, a rollup node simultaneously reads data and deposits it observes from L1, interprets them as blocks, and accepts inbound transactions from users and peers in the network of other rollup nodes.
Nodes participating in the network can use both secure and unsecure block headers of the L2 they are syncing to.
Secure L2 block headers mean that rollups can be constructed where every block (including headers) can be fully derived from the reference L1 chain, before L1 must be “finalized” (i.e. reorgs may still happen on L1);
Unsafe L2 headers include unsafe blocks that have not yet been derived from L1. These blocks come either from operating a rollup node as a sequencer or from an unsafe sync-and-sequencer. This is also known as the "latest" header. In the event of a disagreement, secure L2 headers are always chosen over unsafe L2 headers. When a disagreement occurs, the unsafe part of the chain will be reorganized;
In most cases, for end-user applications, rollup nodes in the L2 network will reference insecure L2 block headers.
Transaction Order
The third way to use a rollup node is to sequence transactions. In this mode, the rollup node will create new blocks on top of unsecured L2 block headers. Currently, there is only one sequencer per OP Stack network.
The sequencer is also responsible for publishing batches of transactions to L1 so that other nodes in the network can sync from them.
Batcher
The role of a sequencer is to produce batches of transactions, to do this, a sequencer can run a rollup node and have separate processes that perform batching by reading from the trusted rollup nodes they run.
This warrants an additional component of the OP Stack, called the batcher, which reads transaction data from rollup nodes and interprets it as batched transactions to be written to L1. The batcher component is responsible for reading the unsafe L2 block headers of the rollup nodes run by the sequencer, creating batcher transactions, and writing them to L1.
Standard bridging contract
Bedrock also includes a pair of bridge contracts for the most common types of deposits, called standard bridge contracts. These contracts wrap the deposit and withdrawal contracts, providing a simple interface for deposits and withdrawals of ETH and ERC-20 tokens.
These bridge contracts are designed with a native token on one side of the cross-chain bridge and a wrapped token on the other side that can manage minting and destruction. Bridging native tokens involves locking the native token in the contract and then minting an equal amount of wrapped tokens on the other side of the cross-chain bridge.
For more details, see the Standard Cross-Chain Bridge Protocol Specification section.
Cannon
While fault-proof construction and verification are implemented in Cannon, the fault proof game specification and integration of output root challengers into rollup nodes are part of subsequent specification milestones.
Further reading
Protocol Specification
The protocol specification defines the technical details of the OP Stack, which is the up-to-date source of truth about the inner workings of the protocol.
The Bedrock Difference
For a more in-depth look at the differences between Bedrock and previous versions, see How Bedrock is different.
