Comment on page
Refers to the Ethereum blockchain, used in contrast to layer 2, which refers to Kroma.
Refers to the Kroma blockchain (specified in this repository), used in contrast to layer 1, which refers to the Ethereum blockchain.
Can refer to an L1 block, or to an L2 block, which are structured similarly.
It is useful to distinguish between input block properties, which are known before executing the transactions in the block, and output block properties, which are derived after executing the block's transactions. These include various Merkle Patricia Trie roots that notably commit to the L2 state and to the log events emitted during execution.
"Externally Owned Account", an Ethereum term to designate addresses operated by users, as opposed to contract addresses.
This means all the leaves of a State Trie. In other words, it means all the accounts. A Block contains the State Root to represent State as a commitment.
A Merkle Patricia Trie that represents state.
A merkle root of State Trie.
A Merkle Patricia Trie that represents storage slots.
A merkle root of Storage Trie.
A Merkle Patricia Trie (MPT) is a sparse trie, which is a tree-like structure that maps keys to values. The root hash of a MPT is a commitment to the contents of the tree, which allows a proof to be constructed for any key-value mapping encoded in the tree. Such a proof is called a Merkle proof, and can be verified against the Merkle root.
A ZK Trie (ZKT) is a binary sparse trie, which is a tree-like structure that maps keys to values. The root hash of a ZKT is a commitment to the contents of the tree, which allows a proof to be constructed for any key-value mapping encoded in the tree. Such a proof is called a Merkle proof, and can be verified against the Merkle root.
Whereas L1 uses MPT to represent state, L2 uses ZKT. This is because ZKT enables faster proof generation by avoiding Keccak and RLP encoding. To accomplish this, ZKT uses poseidon hash to calculate the path and concatenates leaf values in bytes.
A re-organization, or re-org for short, is whenever the head of a blockchain (its last block) changes (as dictated by the fork choice rule) to a block that is not a child of the previous head.
L1 re-orgs can happen because of network conditions or attacks. L2 re-orgs are a consequence of L1 re-orgs, mediated via L2 chain derivation.
A contract placed in the L2 genesis state (i.e. at the start of the chain).
All predeploy contracts are specified in the predeploys specification.
Receipts are not stored in blocks, but blocks store a Merkle Patricia Trie root for a tree containing the receipt for every transaction in the block.
Ethereum provides a mechanism (as described in EIP-2718) for defining different transaction types. Different transaction types can contain different payloads, and be handled differently by the protocol.
The fork choice rule is the rule used to determined which block is to be considered as the head of a blockchain. On L1, this is determined by the proof of stake rules.
L2 also has a fork choice rule, although the rules vary depending on whether we want the safe L2 head, the unsafe L2 head or the finalized L2 head.
Transactions in ethereum are ordered by the price that the transaction pays to the miner. Priority Gas Auctions (PGAs) occur when multiple parties are competing to be the first transaction in a block. Each party continuously updates the gas price of their transaction. PGAs occur when there is value in submitting a transaction before other parties (like being the first deposit or submitting a deposit before there is not more guaranteed gas remaining). PGAs tend to have negative externalities on the network due to a large amount of transactions being submitted in a very short amount of time.
User can send transaction to a sequencer in two ways:
- Through a deposited transaction on L1
- Through a regular transaction on L2
Sequencing is creating block body by collecting transaction from above sources. In other words, sequencing refers to all the actions that are able to determine block body, which are inclusion, exclusion and ordering. The transactions can be collected with different strategies to get MEV. A simple example is to collect the transaction whose priority fee is high so that sequencer can earn maximum priority fee as a result.
A sequencer is either a rollup node ran in sequencer mode, or the operator of this rollup node.
The sequencer is a priviledged actor, which receives L2 transactions from L2 users, creates L2 blocks using them, which it then submits to data availability provider (via a batcher). It also submits output roots to L1.
TODO In the initial release, the sequencer role is merged to the sequencer. In the future, the sequencer role is separated and is planned to be decentralized.
After sequencing, the block can be constructed.
Constructed blocks should be proposed to network in two ways:
A sequencing window is a range of L1 blocks from which a sequencing epoch can be derived.
A sequencing window whose first L1 block has number
Ncontains batcher transactions for epoch
N. The window contains blocks
[N, N + SWS)where
SWSis the sequencer window size.
TODO specify sequencer window size
Additionally, the first block in the window defines the depositing transactions which determine the deposits to be included in the first L2 block of the epoch.
A sequencing epoch is sequential range of L2 blocks derived from a sequencing window of L1 blocks.
Each epoch is identified by an epoch number, which is equal to the block number of the first L1 block in the sequencing window.
Epochs can have variable size, subject to some constraints. See the L2 chain derivation specification for more details.
The L1 origin of an L2 block is the L1 block corresponding to its sequencing epoch.
Because transactions are visible to anyone, nodes can derive state. Registered validators can submit checkpoint output every validating epoch. Validators receive rewards in return. If the checkpoint output turns out to be invalid, this is challenged by another validator who acts as a challenger. As a result, validator who submitted invalid checkpoint output will lose their bond.
Checkpoint output is the l2 output root that denotes state transition during validating epoch.
A validator is a decentralized actor, who does validation. To participate network as a validator, one needs to deposit to ValidatorPool contract. Then the validator is allowed to be able to submit checkpoint.
A trusted validator is an actor who is run by Lightscale. If validations are not done until submission interval, trusted validator will submit output for liveness of L2.
A validating epoch is a sequential range of L2 blocks where needs to be checkpointed.
Each epoch is identified by an epoch number, which is incremented by 1.
Epochs have fixed size, which can be different in every networks. In Kroma mainnet, it will be fixed to
1800blocks, identical to 1 hour.
A submission timeout is the maximum duration that the trusted validator waits for decentralized validators to do validations.
A priority round is the period during which validator with output submission priority can submit the output.
A public round is the time period during which any account registered as a Validator can submit the output.
Any validator can participate in the public round, but only one validator is fully recognized as an output submitter.
Consider a case where multiple validators competitively submit output in a public round.
Except for the first-place validator with a valid output submission transaction in the L1 block,
the remaining validators' transactions will fail and will not be recognized as output submitters.
However, some of them will have already spent their gas.
To handle this situation, we provide a option flag to indicate whether or not to participate in the public round.
Since we're taking a conservative approach, the default value is set to false.
The validator reward is calculated using the following formula:
(L2 base fee + L2 priority fee) * validator reward scalar / 10000.
In general, a deposit is an L2 transaction derived from an L1 block (by the rollup driver).
While transaction deposits are notably (but not only) used to "deposit" (bridge) ETH and tokens to L2, the word deposit should be understood as "a transaction deposited to L2 from L1".
This term deposit is somewhat ambiguous as these "transactions" exist at multiple levels. This section disambiguates all deposit-related terms.
Notably, a deposit can refer to:
- A deposited transaction (on L2) that is part of a deposit block.
- A depositing call that causes a deposited transaction to be derived.
- The event/log data generated by the depositing call, which is what the rollup driver reads to derive the deposited transaction.
We sometimes also talk about user deposit which is a similar term that explicitly excludes L1 attributes deposited transactions.
Deposits are specified in the deposits specification.
A deposited transaction is a L2 transaction that was derived from L1 and included in a L2 block.
There are two kinds of deposited transactions:
- L1 attributes deposited transaction, which submits the L1 block's attributes to the L1 Attributes Predeployed Contract.
- User-deposited transactions, which are transactions derived from an L1 call to the deposit contract.
An L1 attributes deposited transaction is deposited transaction that is used to register the L1 block attributes (number, timestamp, ...) on L2 via a call to the L1 Attributes Predeployed Contract. That contract can then be used to read the attributes of the L1 block corresponding to the current L2 block.
L1 attributes deposited transactions are specified in the L1 Attributes Deposit section of the deposits specification.
A user-deposited transaction is a deposited transaction which is derived from an L1 call to the deposit contract (a depositing call).
User-deposited transactions are specified in the Transaction Deposits section of the deposits specification.
A depositing call is an L1 call to the deposit contract, which will be derived to a user-deposited transaction by the rollup driver.
This call specifies all the data (destination, value, calldata, ...) for the deposited transaction.
A depositing transaction is an L1 transaction that makes one or more depositing calls.
The depositor is the L1 account (contract or EOA) that makes (is the
msg.senderof) the depositing call. The depositor is NOT the originator of the depositing transaction (i.e.
See the corresponding section of the deposits spec for more information.
The deposit contract is an L1 contract to which EOAs and contracts may send deposits. The deposits are emitted as log records (in Solidity, these are called events) for consumption by rollup nodes.
Advanced note: the deposits are not stored in calldata because they can be sent by contracts, in which case the calldata is part of the internal execution between contracts, and this intermediate calldata is not captured in one of the Merkle Patricia Trie roots included in the L1 block.
cf. Deposits Specification
TODO expand this whole section to be clearer
In general, a withdrawal is a transaction sent from L2 to L1 that may transfer data and/or value.
The term withdrawal is somewhat ambiguous as these "transactions" exist at multiple levels. In order to differentiate between the L1 and L2 components of a withdrawal we introduce the following terms:
- A withdrawal initiating transaction refers specifically to a transaction on L2 sent to the Withdrawals predeploy.
- A withdrawal finalizing transaction refers specifically to an L1 transaction which finalizes and relays the withdrawal.
An EOA on L1 which finalizes a withdrawal by submitting the data necessary to verify its inclusion on L2.
The finalization period — sometimes also called withdrawal delay — is the minimum amount of time (in seconds) that must elapse before a withdrawal can be finalized.
The finalization period is necessary to afford sufficient time for validators to make a ZK fault proof.
TODO specify current value for finalization period
Data availability is the guarantee that some data will be "available" (i.e. retrievable) during a reasonably long time window. In Optimism's case, the data in question are sequencer batches that validators needs in order to verify the sequencer's work and validate the L2 chain.
The finalization period should be taken as the lower bound on the availability window, since that is when data availability is the most crucial, as it is needed to perform a ZK fault proof.
"Availability" does not mean guaranteed long-term storage of the data.
A data availability provider is a service that can be used to make data available. See the Data Availability for more information on what this means.
Ideally, a good data availability provider provides strong verifiable guarantees of data availability
A sequencer batch is list of L2 transactions (that were submitted to a sequencer) tagged with an epoch number and an L2 block timestamp (which can trivially be converted to a block number, given our block time is constant).
Sequencer batches are part of the L2 derivation inputs. Each batch represents the inputs needed to build one L2 block (given the existing L2 chain state) — except for the first block of each epoch, which also needs information about deposits (cf. the section on L2 derivation inputs).
A channel is a sequence of sequencer batches (for sequential blocks) compressed together. The reason to group multiple batches together is simply to obtain a better compression rate, hence reducing data availability costs.
A channel can be split in frames in order to be transmitted via batcher transactions. The reason to split a channel into frames is that a channel might be too large to include in a single batcher transaction.
A channel is uniquely identified by its timestamp (UNIX time at which the channel was created) and a random value. See the Frame Format section of the L2 Chain Derivation specification for more information.
On the side of the rollup node (which is the consumer of channels), a channel is considered to be opened if its final frame (explicitly marked as such) has not been read, or closed otherwise.
A channel frame is a chunk of data belonging to a channel. Batcher transactions carry one or multiple frames. The reason to split a channel into frames is that a channel might too large to include in a single batcher transaction.
A batcher is a software component (independent program) that is responsible to make channels available on a data availability provider. The batcher communicates with the rollup node in order to retrieve the channels. The channels are then made available using batcher transactions.
TODO In the future, we might want to make the batcher responsible for constructing the channels, letting it only query the rollup node for L2 block inputs.
A batcher transaction is a transaction submitted by a batcher to a data availability provider, in order to make channels available. These transactions carry one or more full frames, which may belong to different channels. A channel's frame may be split between multiple batcher transactions.
When submitted to Ethereum calldata, the batcher transaction's receiver must be the batch inbox address. The transaction must also be signed by a recognized batch submitter account.
TODO specify where these recognized batch submitter accounts are stored
The channel timeout is a duration (in L1 blocks) during which channel frames may land on L1 within batcher transactions.
The acceptable time range for the frames of a channel is
[channel_id.timestamp, channel_id.timestamp + CHANNEL_TIMEOUT]. The acceptable L1 block range for these frames are any L1 block whose timestamp falls inside this time range. (Note that
channel_id.timetampmust be lower than the L1 block timestamp of any L1 block in which frames of the channel are seen, or else these frames are ignored.)
The purpose of channel timeouts is dual:
- Avoid keeping old unclosed channel data around forever (an unclosed channel is a channel whose final frame was not sent).
- Bound the number of L1 blocks we have to look back in order to decode sequencer batches from channels. This is particularly relevant during L1 re-orgs, see the Resetting Channel Buffering section of the L2 Chain Derivation specifiction for more information.
L2 chain derivation is a process that reads L2 derivation inputs from L1 in order to derive the L2 chain.
See the L2 chain derivation specification for more details.
This term refers to data that is found in L1 blocks and is read by the rollup node to construct payload attributes.
L2 derivation inputs include:
- L1 block attributes
- block number
- deposits (as log data)
- sequencer batches (as transaction data)
- System configuration updates (as log data)
This term refers to the collection of dynamically configurable rollup parameters maintained by the
SystemConfigcontract on L1 and read by the L2 derivation process. These parameters enable keys to be rotated regularly and external cost parameters to be adjusted without the network upgrade overhead of a hardfork.
This term refers to an object that can be derived from L2 chain derivation inputs found on L1, which are then passed to the execution engine to construct L2 blocks.
The payload attributes object essentially encodes a block without output properties.
See also the Building The Payload Attributes section of the rollup node specification.
The L2 genesis block is the first block of the L2 chain in its current version.
The state of the L2 genesis block contains Predeployed contracts.
The timestamp of the L2 genesis block must be a multiple of the block time (i.e. a even number, since the block time is 2 seconds).
When updating the rollup protocol to a new version, we may perform a squash fork, a process that entails the creation of a new L2 genesis block. This new L2 genesis block will have block number
X + 1, where
Xis the block number of the final L2 block before the update.
A squash fork is not to be confused with a re-genesis, a similar process that we employed in the past, which also resets L2 block numbers, such that the new L2 genesis block has number 0. We will not employ re-genesis in the future.
Squash forks are superior to re-geneses because they avoid duplicating L2 block numbers, which breaks a lot of external tools.
The L1 block number at which the output roots for the genesis block were proposed on the output oracle contract.
In the current implementation, this is the L1 block number at which the output oracle contract was deployed or upgraded.
A safe L2 block is an L2 block that can be derived entirely from L1 by a rollup node. This can vary between different nodes, based on their view of the L1 chain.
The safe L2 head is the highest safe L2 block that a rollup node knows about.
An unsafe L2 block is an L2 block that a rollup node knows about, but which was not derived from the L1 chain. In sequencer mode, this will be a block sequenced by the sequencer itself. In syncer mode, this will be a block acquired from the sequencer via unsafe sync.
The unsafe L2 head is the highest unsafe L2 block that a rollup node knows about.
Unsafe block consolidation is the process through which the rollup node attempts to move the [safe L2 head] a block forward, so that the oldest unsafe L2 block becomes the new safe L2 head.
In order to perform consolidation, the node verifies that the payload attributes derived from the L1 chain match the oldest unsafe L2 block exactly.
See the Engine Queue section of the L2 chain derivation spec for more information.
When a contract submits a deposit from L1 to L2, it's address (as returned by
CALLER) will be aliased with a modified representation of the address of a contract.
- cf. Deposit Specification
The rollup node is responsible for deriving the L2 chain from the L1 chain (L1 blocks and their associated receipts).
The rollup node can run either in syncer or sequencer mode.
In sequencer mode, the rollup node receives L2 transactions from users, which it uses to create L2 blocks. These are then submitted to a data availability provider via batch submission. The L2 chain derivation then acts as a sanity check and a way to detect L1 chain re-orgs.
In syncer mode, the rollup node performs derivation as indicated above, but is also able to "run ahead" of the L1 chain by getting blocks directly from the sequencer, in which case derivation serves to validate the sequencer's behavior.
A rollup node running in syncer mode is sometimes called a replica.
TODO expand this to include output root submission
See the rollup node specification for more information.
The rollup driver is the rollup node component responsible for deriving the L2 chain from the L1 chain (L1 blocks and their associated receipts).
TODO delete this entry, alongside its reference — can be replaced by "derivation process" or "derivation logic" where needed
A predeployed contract on L2 that can be used to retrieve the L1 block attributes of L1 blocks with a given block number or a given block hash.
cf. L1 Attributes Predeployed Contract Specification
A 32 byte value which serves as a commitment to the current state of the L2 chain.
cf. Submitting L2 output commitments
An L1 contract to which L2 output roots are posted by the validator.
An L1 contract that determines validator eligibility, selects the validator of next round, and manages bonding for L2 output roots submissions.
An L1 contract in which the proposer and challenger argue with each other to fix invalid L2 output roots.
An on-chain interactive proof, performed by validators, that demonstrates that a sequencer provided erroneous output roots using zkEVM.
A group of entities composed of trusted parties responsible for the security of blockchain, such as fault-proof system, withdrawals, and contract upgrades.
On L2, there is a block every 2 second (this duration is known as the block time).
We say that there is a "time slot" every multiple of 2s after the timestamp of the L2 genesis block.
The L2 block time is 2 second, meaning there is an L2 block at every 2s time slot.
Pre-merge, the L1 block time is variable, though it is on average 13s.
Unsafe sync is the process through which a validator learns about unsafe L2 blocks from the sequencer.
These unsafe blocks will later need to be confirmed by the L1 chain (via unsafe block consolidation).
The execution engine is responsible for executing transactions in blocks and computing the resulting state roots, receipts roots and block hash.
On L1, the executed blocks can come from L1 block synchronization; or from a block freshly minted by the execution engine (using transactions from the L1 mempool), at the request of the L1 consensus layer.
On L2, the executed blocks are freshly minted by the execution engine at the request of the rollup node, using transactions derived from L1 blocks.
In these specifications, "execution engine" always refer to the L2 execution engine, unless otherwise specified.
- cf. Execution Engine Specification