-
Notifications
You must be signed in to change notification settings - Fork 3.9k
op-node: split driver state/model into L1 state, L1 origin selector, L2 sequencer, L2 derivation #3647
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
op-node: split driver state/model into L1 state, L1 origin selector, L2 sequencer, L2 derivation #3647
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| package driver | ||
|
|
||
| import ( | ||
| "github.com/ethereum/go-ethereum/log" | ||
|
|
||
| "github.com/ethereum-optimism/optimism/op-node/eth" | ||
| ) | ||
|
|
||
| type L1Metrics interface { | ||
| RecordL1ReorgDepth(d uint64) | ||
| RecordL1Ref(name string, ref eth.L1BlockRef) | ||
| } | ||
|
|
||
| // L1State tracks L1 head, safe and finalized blocks. It is not safe to write and read concurrently. | ||
| type L1State struct { | ||
| log log.Logger | ||
| metrics L1Metrics | ||
|
|
||
| // Latest recorded head, safe block and finalized block of the L1 Chain, independent of derivation work | ||
| l1Head eth.L1BlockRef | ||
| l1Safe eth.L1BlockRef | ||
| l1Finalized eth.L1BlockRef | ||
| } | ||
|
|
||
| func NewL1State(log log.Logger, metrics L1Metrics) *L1State { | ||
| return &L1State{ | ||
| log: log, | ||
| metrics: metrics, | ||
| } | ||
| } | ||
|
|
||
| func (s *L1State) HandleNewL1HeadBlock(head eth.L1BlockRef) { | ||
| // We don't need to do anything if the head hasn't changed. | ||
| if s.l1Head == (eth.L1BlockRef{}) { | ||
| s.log.Info("Received first L1 head signal", "l1_head", head) | ||
| } else if s.l1Head.Hash == head.Hash { | ||
| s.log.Trace("Received L1 head signal that is the same as the current head", "l1_head", head) | ||
| } else if s.l1Head.Hash == head.ParentHash { | ||
| // We got a new L1 block whose parent hash is the same as the current L1 head. Means we're | ||
| // dealing with a linear extension (new block is the immediate child of the old one). | ||
| s.log.Debug("L1 head moved forward", "l1_head", head) | ||
| } else { | ||
| if s.l1Head.Number >= head.Number { | ||
| s.metrics.RecordL1ReorgDepth(s.l1Head.Number - head.Number) | ||
| } | ||
| // New L1 block is not the same as the current head or a single step linear extension. | ||
| // This could either be a long L1 extension, or a reorg, or we simply missed a head update. | ||
| s.log.Warn("L1 head signal indicates a possible L1 re-org", "old_l1_head", s.l1Head, "new_l1_head_parent", head.ParentHash, "new_l1_head", head) | ||
| } | ||
| s.metrics.RecordL1Ref("l1_head", head) | ||
| s.l1Head = head | ||
| } | ||
|
|
||
| func (s *L1State) HandleNewL1SafeBlock(safe eth.L1BlockRef) { | ||
| s.log.Info("New L1 safe block", "l1_safe", safe) | ||
| s.metrics.RecordL1Ref("l1_safe", safe) | ||
| s.l1Safe = safe | ||
| } | ||
|
|
||
| func (s *L1State) HandleNewL1FinalizedBlock(finalized eth.L1BlockRef) { | ||
| s.log.Info("New L1 finalized block", "l1_finalized", finalized) | ||
| s.metrics.RecordL1Ref("l1_finalized", finalized) | ||
| s.l1Finalized = finalized | ||
| } | ||
|
|
||
| func (s *L1State) L1Head() eth.L1BlockRef { | ||
| return s.l1Head | ||
| } | ||
|
|
||
| func (s *L1State) L1Safe() eth.L1BlockRef { | ||
| return s.l1Safe | ||
| } | ||
|
|
||
| func (s *L1State) L1Finalized() eth.L1BlockRef { | ||
| return s.l1Finalized | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| package driver | ||
|
|
||
| import ( | ||
| "context" | ||
|
|
||
| "github.com/ethereum/go-ethereum/log" | ||
|
|
||
| "github.com/ethereum-optimism/optimism/op-node/eth" | ||
| "github.com/ethereum-optimism/optimism/op-node/rollup" | ||
| "github.com/ethereum-optimism/optimism/op-node/rollup/derive" | ||
| ) | ||
|
|
||
| type L1Blocks interface { | ||
| derive.L1BlockRefByHashFetcher | ||
| derive.L1BlockRefByNumberFetcher | ||
| } | ||
|
|
||
| type L1OriginSelector struct { | ||
| log log.Logger | ||
| cfg *rollup.Config | ||
|
|
||
| l1 L1Blocks | ||
| sequencingConfDepth uint64 | ||
| } | ||
|
|
||
| func NewL1OriginSelector(log log.Logger, cfg *rollup.Config, l1 L1Blocks, sequencingConfDepth uint64) *L1OriginSelector { | ||
| return &L1OriginSelector{ | ||
| log: log, | ||
| cfg: cfg, | ||
| l1: l1, | ||
| sequencingConfDepth: sequencingConfDepth, | ||
| } | ||
| } | ||
|
|
||
| // FindL1Origin determines what the next L1 Origin should be. | ||
| // The L1 Origin is either the L2 Head's Origin, or the following L1 block | ||
| // if the next L2 block's time is greater than or equal to the L2 Head's Origin. | ||
| func (los *L1OriginSelector) FindL1Origin(ctx context.Context, l1Head eth.L1BlockRef, l2Head eth.L2BlockRef) (eth.L1BlockRef, error) { | ||
protolambda marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // If we are at the head block, don't do a lookup. | ||
| if l2Head.L1Origin.Hash == l1Head.Hash { | ||
| return l1Head, nil | ||
| } | ||
|
|
||
| // Grab a reference to the current L1 origin block. | ||
| currentOrigin, err := los.l1.L1BlockRefByHash(ctx, l2Head.L1Origin.Hash) | ||
| if err != nil { | ||
| return eth.L1BlockRef{}, err | ||
| } | ||
|
|
||
| if currentOrigin.Number+1+los.sequencingConfDepth > l1Head.Number { | ||
| // TODO: we can decide to ignore confirmation depth if we would be forced | ||
| // to make an empty block (only deposits) by staying on the current origin. | ||
| log.Info("sequencing with old origin to preserve conf depth", | ||
| "current", currentOrigin, "current_time", currentOrigin.Time, | ||
| "l1_head", l1Head, "l1_head_time", l1Head.Time, | ||
| "l2_head", l2Head, "l2_head_time", l2Head.Time, | ||
| "depth", los.sequencingConfDepth) | ||
| return currentOrigin, nil | ||
| } | ||
|
|
||
| // Attempt to find the next L1 origin block, where the next origin is the immediate child of | ||
| // the current origin block. | ||
| nextOrigin, err := los.l1.L1BlockRefByNumber(ctx, currentOrigin.Number+1) | ||
| if err != nil { | ||
| log.Error("Failed to get next origin. Falling back to current origin", "err", err) | ||
| return currentOrigin, nil | ||
| } | ||
|
|
||
| // If the next L2 block time is greater than the next origin block's time, we can choose to | ||
| // start building on top of the next origin. Sequencer implementation has some leeway here and | ||
| // could decide to continue to build on top of the previous origin until the Sequencer runs out | ||
| // of slack. For simplicity, we implement our Sequencer to always start building on the latest | ||
| // L1 block when we can. | ||
| if l2Head.Time+los.cfg.BlockTime >= nextOrigin.Time { | ||
| return nextOrigin, nil | ||
| } | ||
|
|
||
| return currentOrigin, nil | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.