diff --git a/op-node/node/node.go b/op-node/node/node.go index 1a1c8dea37fdf..c4982424a946d 100644 --- a/op-node/node/node.go +++ b/op-node/node/node.go @@ -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) diff --git a/op-node/rollup/driver/driver.go b/op-node/rollup/driver/driver.go index 451ba1580cde5..4b9583dcadcf5 100644 --- a/op-node/rollup/driver/driver.go +++ b/op-node/rollup/driver/driver.go @@ -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) } diff --git a/op-node/rollup/finalized/finalized.go b/op-node/rollup/finalized/finalized.go index 47fbcc077e4f3..fd10253efd174 100644 --- a/op-node/rollup/finalized/finalized.go +++ b/op-node/rollup/finalized/finalized.go @@ -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" @@ -12,10 +13,11 @@ 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) { @@ -23,6 +25,7 @@ func (f *finalized) L1BlockRefByNumber(ctx context.Context, num uint64) (eth.L1B 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 } diff --git a/op-node/rollup/finalized/finalized_test.go b/op-node/rollup/finalized/finalized_test.go index 8fa397bf076ce..1f7df16731c04 100644 --- a/op-node/rollup/finalized/finalized_test.go +++ b/op-node/rollup/finalized/finalized_test.go @@ -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" @@ -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