Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions op-node/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,30 @@ func (n *OpNode) initP2PSigner(ctx context.Context, cfg *Config) (err error) {
}

func (n *OpNode) Start(ctx context.Context) error {
// If n.cfg.Driver.SequencerUseFinalized is true, sequencer does not use non-finalized L1 blocks as L1 origin
// The OpNode periodically fetches the latest safe and finalized L1 block heights (1 epoch ≒ 6.4 minutes by default),
// but these values are not available immediately after startup until the first polling occurs.
// In some cases, this can cause the sequencer to get stuck because it fails to retrieve the next L1 block.
// To prevent this, fetch and initialize the latest safe and finalized L1 block references at startup.
if n.cfg.Driver.SequencerUseFinalized {
reqCtx, reqCancel := context.WithTimeout(ctx, time.Second*20)
defer reqCancel()

finalizedRef, err := n.l1Source.L1BlockRefByLabel(reqCtx, eth.Finalized)
if err != nil {
log.Warn("failed to fetch L1 block", "label", eth.Finalized, "err", err)
} else if finalizedRef != (eth.L1BlockRef{}) {
n.OnNewL1Finalized(reqCtx, finalizedRef)
}

safeRef, err := n.l1Source.L1BlockRefByLabel(reqCtx, eth.Safe)
if err != nil {
log.Warn("failed to fetch L1 block", "label", eth.Safe, "err", err)
} else if safeRef != (eth.L1BlockRef{}) {
n.OnNewL1Safe(reqCtx, safeRef)
}
}

if n.interopSys != nil {
if err := n.interopSys.Start(ctx); err != nil {
n.log.Error("Could not start interop sub system", "err", err)
Expand Down
2 changes: 1 addition & 1 deletion op-node/rollup/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ func NewDriver(
attrBuilder := derive.NewFetchingAttributesBuilder(cfg, l1, l2)
var seqL1Blocks sequencing.L1Blocks
if driverCfg.SequencerUseFinalized {
seqL1Blocks = finalized.NewFinalized(statusTracker.L1Finalized, l1)
seqL1Blocks = finalized.NewFinalized(statusTracker.L1Finalized, l1, log)
} else {
seqL1Blocks = confdepth.NewConfDepth(driverCfg.SequencerConfDepth, statusTracker.L1Head, l1)
}
Expand Down
7 changes: 5 additions & 2 deletions op-node/rollup/finalized/finalized.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/log"

"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-service/eth"
Expand All @@ -12,17 +13,19 @@ import (
type finalized struct {
derive.L1Fetcher
l1Finalized func() eth.L1BlockRef
log log.Logger
}

func NewFinalized(l1Finalized func() eth.L1BlockRef, fetcher derive.L1Fetcher) *finalized {
return &finalized{L1Fetcher: fetcher, l1Finalized: l1Finalized}
func NewFinalized(l1Finalized func() eth.L1BlockRef, fetcher derive.L1Fetcher, log log.Logger) *finalized {
return &finalized{L1Fetcher: fetcher, l1Finalized: l1Finalized, log: log}
}

func (f *finalized) L1BlockRefByNumber(ctx context.Context, num uint64) (eth.L1BlockRef, error) {
l1Finalized := f.l1Finalized()
if num == 0 || num <= l1Finalized.Number {
return f.L1Fetcher.L1BlockRefByNumber(ctx, num)
}
f.log.Warn("requested L1 block is beyond local finalized height", "requested_block", num, "finalized_block", l1Finalized.Number)
return eth.L1BlockRef{}, ethereum.NotFound
}

Expand Down
3 changes: 2 additions & 1 deletion op-node/rollup/finalized/finalized_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"

"github.com/ethereum-optimism/optimism/op-service/eth"
Expand All @@ -27,7 +28,7 @@ func (ft *finalizedTest) Run(t *testing.T) {
l1Finalized := eth.L1BlockRef{Number: ft.final, Hash: ft.hash}
l1FinalizedGetter := func() eth.L1BlockRef { return l1Finalized }

f := NewFinalized(l1FinalizedGetter, l1Fetcher)
f := NewFinalized(l1FinalizedGetter, l1Fetcher, log.New())

if ft.pass {
// no calls to the l1Fetcher are made if the block number is not finalized yet
Expand Down