Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
13a12557
Unverified
Commit
13a12557
authored
Dec 06, 2023
by
Mark Tyneway
Committed by
GitHub
Dec 06, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #8139 from roberto-bayardo/rewrite-derivation-docs
improve chain derivation documentation
parents
44c0ba5c
47e9f92f
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
75 additions
and
67 deletions
+75
-67
derivation.md
specs/derivation.md
+75
-67
No files found.
specs/derivation.md
View file @
13a12557
...
@@ -24,6 +24,7 @@
...
@@ -24,6 +24,7 @@
[
g-sequencer-batch
]:
glossary.md#sequencer-batch
[
g-sequencer-batch
]:
glossary.md#sequencer-batch
[
g-l2-genesis
]:
glossary.md#l2-genesis-block
[
g-l2-genesis
]:
glossary.md#l2-genesis-block
[
g-l2-chain-inception
]:
glossary.md#L2-chain-inception
[
g-l2-chain-inception
]:
glossary.md#L2-chain-inception
[
g-l2-genesis-block
]:
glossary.md#l2-genesis-block
[
g-batcher-transaction
]:
glossary.md#batcher-transaction
[
g-batcher-transaction
]:
glossary.md#batcher-transaction
[
g-avail-provider
]:
glossary.md#data-availability-provider
[
g-avail-provider
]:
glossary.md#data-availability-provider
[
g-batcher
]:
glossary.md#batcher
[
g-batcher
]:
glossary.md#batcher
...
@@ -93,77 +94,84 @@
...
@@ -93,77 +94,84 @@
> **Note** the following assumes a single sequencer and batcher. In the future, the design will be adapted to
> **Note** the following assumes a single sequencer and batcher. In the future, the design will be adapted to
> accommodate multiple such entities.
> accommodate multiple such entities.
[
L2 chain derivation
][
g-derivation
]
— deriving L2
[
blocks
][
g-block
]
from L1 data — is one of the main responsibility of
[
L2 chain derivation
][
g-derivation
]
— deriving L2
[
blocks
][
g-block
]
from L1 data — is one of the main responsibilities
the
[
rollup node
][
g-rollup-node
]
, both in validator mode, and in sequencer mode (where derivation acts as a sanity check
of the
[
rollup node
][
g-rollup-node
]
, both in validator mode, and in sequencer mode (where derivation acts as a sanity
on sequencing, and enables detecting L1 chain
[
re-organizations
][
g-reorg
]
).
check on sequencing, and enables detecting L1 chain
[
re-organizations
][
g-reorg
]
).
The L2 chain is derived from the L1 chain. In particular, each L1 block is mapped to an L2
[
sequencing
The L2 chain is derived from the L1 chain. In particular, each L1 block following
[
L2 chain
epoch]
[
g-sequencing-epoch
]
comprising multiple L2 blocks. The epoch number is defined to be equal to the corresponding
inception]
[
g-l2-chain-inception
]
is mapped to a
[
sequencing epoch
][
g-sequencing-epoch
]
comprising
L1 block number.
at least one L2 block. Each L2 block belongs to exactly one epoch, and we call the corresponding L1
block its
[
L1 origin
][
l1-origin
]
. The epoch's number equals that of its L1 origin block.
To derive the L2 blocks in an epoch
`E`
, we need the following inputs:
To derive the L2 blocks of epoch number
`E`
, we need the following inputs:
-
The L1
[
sequencing window
][
g-sequencing-window
]
for epoch
`E`
: the L1 blocks in the range
`[E, E + SWS)`
where
`SWS`
is the sequencing window size (note that this means that epochs are overlapping). In particular, we need:
-
L1 blocks in the range
`[E, E + SWS)`
, called the
[
sequencing window
][
g-sequencing-window
]
of the epoch, and
`SWS`
-
The
[
batcher transactions
][
g-batcher-transaction
]
included in the sequencing window. These allow us to
the sequencing window size. (Note that sequencing windows overlap.)
reconstruct
[
sequencer batches
][
g-sequencer-batch
]
containing the transactions to include in L2 blocks (each batch
-
[
Batcher transactions
][
g-batcher-transaction
]
from blocks in the sequencing window.
contains a list of L2 blocks).
-
These transactions allow us to reconstruct the epoch's
[
sequencer batches
][
g-sequencer-batch
]
, each of
-
Note that it is impossible to have a batcher transaction containing a batch relative to epoch
`E`
on L1 block
which will produce one L2 block. Note that:
`E`
, as the batch must contain the hash of L1 block
`E`
.
-
The L1 origin will never contain any data needed to construct sequencer batches since
-
The
[
deposits
][
g-deposits
]
made in L1 block
`E`
(in the form of events emitted by the
[
deposit
each batch
[
must contain
](
#batch-format
)
the L1 origin hash.
contract]
[
g-deposit-contract
]
).
-
An epoch may have no sequencer batches.
-
The L1 block attributes from L1 block
`E`
(to derive the
[
L1 attributes deposited transaction
][
g-l1-attr-deposit
]
).
-
[
Deposits
][
g-deposits
]
made in the L1 origin (in the form of events emitted by the
[
deposit
-
The state of the L2 chain after the last L2 block of epoch
`E - 1`
, or — if epoch
`E - 1`
does not exist — the
contract]
[
g-deposit-contract
]
).
[
L2 genesis state
][
g-l2-genesis
]
.
-
L1 block attributes from the L1 origin (to derive the
[
L1 attributes deposited transaction
][
g-l1-attr-deposit
]
).
-
An epoch
`E`
does not exist if
`E <= L2CI`
, where
`L2CI`
is the
[
L2 chain inception
][
g-l2-chain-inception
]
.
-
The state of the L2 chain after the last L2 block of the previous epoch, or the
[
L2 genesis state
][
g-l2-genesis
]
if
`E`
is the first epoch.
To derive the whole L2 chain from scratch, we simply start with the
[
L2 genesis state
][
g-l2-genesis
]
, and the
[
L2 chain
inception]
[
g-l2-chain-inception
]
as first epoch, then process all sequencing windows in order. Refer to the
To derive the whole L2 chain from scratch, we start with the
[
L2 genesis state
][
g-l2-genesis
]
and
the
[
L2 genesis block
]
as the first L2 block. We then derive L2 blocks from each epoch in order,
starting at the first L1 block following
[
L2 chain inception
][
g-l2-chain-inception
]
. Refer to the
[
Architecture section
][
architecture
]
for more information on how we implement this in practice.
[
Architecture section
][
architecture
]
for more information on how we implement this in practice.
The L2 chain may contain pre-Bedrock history, but the L2 genesis here refers to the first Bedrock L2 block.
The L2 chain may contain pre-Bedrock history, but the L2 genesis here refers to the Bedrock L2
genesis block.
Each epoch may contain a variable number of L2 blocks (one every
`l2_block_time`
, 2s on Optimism), at the discretion of
[
the sequencer
][
g-sequencer
]
, but subject to the following constraints for each block:
Each L2
`block`
with origin
`l1_origin`
is subject to the following constraints (whose values are
denominated in seconds):
-
`min_l2_timestamp <= block.timestamp <= max_l2_timestamp`
, where
-
all these values are denominated in seconds
-
`block.timestamp = prev_l2_timestamp + l2_block_time`
-
`min_l2_timestamp = l1_timestamp`
-
`prev_l2_timestamp`
is the timestamp of the L2 block immediately preceeding this one. If there
-
This ensures that the L2 timestamp is not behind the L1 origin timestamp.
is no preceeding block, then this is the genesis block, and its timestamp is explicitly
-
`block.timestamp = prev_l2_timestamp + l2_block_time`
specified.
-
`prev_l2_timestamp`
is the timestamp of the last L2 block of the previous epoch
-
`l2_block_time`
is a configurable parameter of the time between L2 blocks (2s on Optimism).
-
`l2_block_time`
is a configurable parameter of the time between L2 blocks (on Optimism, 2s)
-
`max_l2_timestamp = max(l1_timestamp + max_sequencer_drift, min_l2_timestamp + l2_block_time)`
-
`l1_origin.timestamp <= block.timestamp <= max_l2_timestamp`
, where
-
`l1_timestamp`
is the timestamp of the L1 block associated with the L2 block's epoch
-
`max_l2_timestamp = max(l1_origin.timestamp + max_sequencer_drift, prev_l2_timestamp + l2_block_time)`
-
`max_sequencer_drift`
is the most a sequencer is allowed to get ahead of L1
-
`max_sequencer_drift`
is a configurable parameter that bounds how far the sequencer can get ahead of
the L1.
Put together, these constraints mean that there must be an L2 block every
`l2_block_time`
seconds, and that the
timestamp for the first L2 block of an epoch must never fall behind the timestamp of the L1 block matching the epoch.
Finally, each epoch must have at least one L2 block.
Post-merge, Ethereum has a fixed
[
block time
][
g-block-time
]
of 12s (though some slots can be skipped). It is thus
The first constraint means there must be an L2 block every
`l2_block_time`
seconds following L2
expected that with a 2-second L2 block time, most of the time, each epoch will contain
`12/2 = 6`
L2 blocks.
chain inception.
The sequencer can however lengthen or shorten epochs (subject to above constraints).
The rationale is to maintain liveness in case of either a skipped slot on L1, or a temporary loss of connection to L1 —
The second constraint ensures that an L2 block timestamp never precedes its L1 origin timestamp,
which requires longer epochs.
and is never more than
`max_sequencer_drift`
ahead of it, except only in the unusual case where it
Shorter epochs are then required to avoid L2 timestamps drifting further and further ahead of L1.
might prohibit an L2 block from being produced every l2_block_time seconds. (Such cases might arise
for example under a proof-of-work L1 that sees a period of rapid L1 block production.) In either
Note that
`min_l2_timestamp + l2_block_time`
ensures that a new L2 batch can always be processed, even if the
case, the sequencer enforces
`len(batch.transactions) == 0`
while
`max_sequencer_drift`
is
`max_sequencer_drift`
is exceeded. However, when exceeding the
`max_sequencer_drift`
, progression to the next L1 origin
exceeded. See
[
Batch Queue
](
#batch-queue
)
for more details.
is enforced, with an exception to ensure the minimum timestamp bound (based on this next L1 origin) can be met in the
next L2 batch, and
`len(batch.transactions) == 0`
continues to be enforced while the
`max_sequencer_drift`
is exceeded.
The final requirement that each epoch must have at least one L2 block ensures that all relevant
See
[
Batch Queue
]
for more details.
information from the L1 (e.g. deposits) is represented in the L2, even if it has no sequencer
batches.
Post-merge, Ethereum has a fixed 12s
[
block time
][
g-block-time
]
, though some slots can be
skipped. Under a 2s L2 block time, we thus expect each epoch to typically contain
`12/2 = 6`
L2
blocks. The sequencer will however produce bigger epochs in order to maintain liveness in case of
either a skipped slot on the L1 or a temporary loss of connection to it. For the lost connection
case, smaller epochs might be produced after the connection was restored to keep L2 timestamps from
drifting further and further ahead.
## Eager Block Derivation
## Eager Block Derivation
In practice, it is often not necessary to wait for a full sequencing window of L1 blocks in order to start deriving the
Deriving an L2 block requires that we have constructed its sequencer batch and derived all L2
L2 blocks in an epoch. Indeed, as long as we are able to reconstruct sequential batches, we can start deriving the
blocks and state updates prior to it. This means we can typically derive the L2 blocks of an epoch
corresponding L2 blocks. We call this
*eager block derivation*
.
*eagerly*
without waiting on the full sequencing window. The full sequencing window is required
before derivation only in the very worst case where some portion of the sequencer batch for the
However, in the very worst case, we can only reconstruct the batch for the first L2 block in the epoch by reading the
first block of the epoch appears in the very last L1 block of the window. Note that this only
last L1 block of the sequencing window. This happens when some data for that batch is included in the last L1 block of
applies to
*block*
derivation. Sequencer batches can still be derived and tentatively queued
the window. In that case, not only can we not derive the first L2 block in the epoch, we also cannot derive any further
without deriving blocks from them.
L2 block in the epoch until then, as they need the state that results from applying the epoch's first L2 block.
(Note that this only applies to
*block*
derivation. Batches can still be derived and tentatively queued,
we just won't be able to create blocks from them.)
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment