diff --git a/l1/l1.go b/l1/l1.go index 0dbf8227c3..e6414a4230 100644 --- a/l1/l1.go +++ b/l1/l1.go @@ -21,6 +21,8 @@ type Client struct { network *networks.Network resubscribeDelay time.Duration pollFinalisedInterval time.Duration + // CatchUpChunkSize is the L1 block range per backward eth_getLogs request + // during the startup catch-up scan. catchUpChunkSize uint64 nonFinalisedLogs map[uint64]*StateUpdate listener EventListener @@ -36,9 +38,8 @@ type options struct { EventListener EventListener ResubscribeDelay time.Duration PollFinalisedInterval time.Duration - // CatchUpChunkSize is the L1 block range per backward eth_getLogs request - // during the startup catch-up scan. - CatchUpChunkSize uint64 + CatchUpChunkSize uint64 + Logger log.StructuredLogger } // Option is a functional option for configuring l1 client options. @@ -64,10 +65,14 @@ func WithCatchUpChunkSize(size uint64) Option { return func(o *options) { o.CatchUpChunkSize = size } } +// WithLogger sets the structured logger for the l1 client. +func WithLogger(logger log.StructuredLogger) Option { + return func(o *options) { o.Logger = logger } +} + func NewClient( provider L1StateProvider, chain *blockchain.Blockchain, - logger log.StructuredLogger, opts ...Option, ) *Client { o := options{ @@ -75,6 +80,7 @@ func NewClient( ResubscribeDelay: 10 * time.Second, PollFinalisedInterval: time.Minute, CatchUpChunkSize: defaultCatchUpChunkSize, + Logger: log.NewNopZapLogger(), } for _, opt := range opts { opt(&o) @@ -82,7 +88,7 @@ func NewClient( return &Client{ provider: provider, l2Chain: chain, - logger: logger, + logger: o.Logger, network: chain.Network(), resubscribeDelay: o.ResubscribeDelay, pollFinalisedInterval: o.PollFinalisedInterval, diff --git a/l1/l1_pkg_test.go b/l1/l1_pkg_test.go index 6b8f316516..3d6b2eb95c 100644 --- a/l1/l1_pkg_test.go +++ b/l1/l1_pkg_test.go @@ -365,7 +365,7 @@ func TestClient(t *testing.T) { client := NewClient( nil, chain, - nopLog, + l1.WithLogger(nopLog), WithResubscribeDelay(0), WithPollFinalisedInterval(time.Nanosecond), ) @@ -447,7 +447,7 @@ func TestUnreliableSubscription(t *testing.T) { client := NewClient( nil, chain, - nopLog, + l1.WithLogger(nopLog), WithResubscribeDelay(0), WithPollFinalisedInterval(time.Nanosecond), ) @@ -574,7 +574,7 @@ func TestCatchUpSetsL1HeadOnStart(t *testing.T) { Times(1) client := NewClient( - provider, chain, nopLog, + provider, chain, l1.WithLogger(nopLog), WithResubscribeDelay(0), WithPollFinalisedInterval(time.Hour), WithCatchUpChunkSize(10), @@ -629,7 +629,7 @@ func TestCatchUpMultiChunk(t *testing.T) { After(secondCall) client := NewClient( - provider, chain, nopLog, + provider, chain, l1.WithLogger(nopLog), WithResubscribeDelay(0), WithPollFinalisedInterval(time.Hour), WithCatchUpChunkSize(10), @@ -668,7 +668,7 @@ func TestCatchUpFilterError(t *testing.T) { // Best-effort: catch-up error must NOT terminate Run. It logs and falls // through to the live subscription, which we let idle until ctx expires. client := NewClient( - provider, chain, nopLog, + provider, chain, l1.WithLogger(nopLog), WithResubscribeDelay(0), WithPollFinalisedInterval(time.Hour), ) @@ -715,7 +715,7 @@ func TestCatchUpHeadAndCachePartition(t *testing.T) { Times(1) client := NewClient( - provider, chain, nopLog, + provider, chain, l1.WithLogger(nopLog), WithResubscribeDelay(0), WithPollFinalisedInterval(time.Hour), ) @@ -781,7 +781,7 @@ func TestCatchUpPartialProgressPreserved(t *testing.T) { // Poll interval is 1h so the live loop never ticks setL1Head — the only // thing that could populate nonFinalisedLogs is the catch-up walk. client := NewClient( - provider, chain, nopLog, + provider, chain, l1.WithLogger(nopLog), WithResubscribeDelay(0), WithPollFinalisedInterval(time.Hour), ) @@ -825,7 +825,7 @@ func TestFinalisedHeightReturnsPromptlyOnCancel(t *testing.T) { Return(uint64(0), errors.New("boom")). MinTimes(1) - client := NewClient(provider, chain, nopLog, WithResubscribeDelay(time.Hour)) + client := NewClient(provider, chain, l1.WithLogger(nopLog), WithResubscribeDelay(time.Hour)) ctx, cancel := context.WithCancel(t.Context()) type result struct { @@ -873,7 +873,7 @@ func TestSubscribeToUpdatesReturnsPromptlyOnCancel(t *testing.T) { Return(nil, errors.New("boom")). MinTimes(1) - client := NewClient(provider, chain, nopLog, WithResubscribeDelay(time.Hour)) + client := NewClient(provider, chain, l1.WithLogger(nopLog), WithResubscribeDelay(time.Hour)) ctx, cancel := context.WithCancel(t.Context()) done := make(chan Subscription, 1) diff --git a/l1/l1_test.go b/l1/l1_test.go index 699d70c6ce..4593792e86 100644 --- a/l1/l1_test.go +++ b/l1/l1_test.go @@ -65,7 +65,7 @@ func TestFailToCreateSubscription(t *testing.T) { client := l1.NewClient( provider, chain, - nopLog, + l1.WithLogger(nopLog), l1.WithResubscribeDelay(0), l1.WithPollFinalisedInterval(time.Nanosecond), ) @@ -101,7 +101,7 @@ func TestMismatchedChainID(t *testing.T) { client := l1.NewClient( provider, chain, - nopLog, + l1.WithLogger(nopLog), l1.WithResubscribeDelay(0), l1.WithPollFinalisedInterval(time.Nanosecond), ) @@ -139,7 +139,7 @@ func TestChainIDCheckTimeout(t *testing.T) { }). Times(1) - client := l1.NewClient(provider, chain, nopLog) + client := l1.NewClient(provider, chain, l1.WithLogger(nopLog)) err := client.CatchUpL1Head(t.Context()) require.ErrorContains(t, err, "eth_chainId did not respond within") @@ -170,7 +170,7 @@ func TestChainIDFetchError(t *testing.T) { Return(nil, rpcErr). Times(1) - client := l1.NewClient(provider, chain, nopLog) + client := l1.NewClient(provider, chain, l1.WithLogger(nopLog)) err := client.CatchUpL1Head(t.Context()) require.ErrorContains(t, err, "retrieving Ethereum chain ID") @@ -218,7 +218,7 @@ func TestTransientChainIDErrorDoesNotShutDownNode(t *testing.T) { // entering catch-up or the watch loop, so no other L1StateProvider calls occur. client := l1.NewClient( - provider, chain, nopLog, + provider, chain, l1.WithLogger(nopLog), l1.WithResubscribeDelay(0), l1.WithPollFinalisedInterval(time.Nanosecond), ) @@ -258,7 +258,7 @@ func TestFinalisedHeightTimeoutDuringCatchUp(t *testing.T) { }). Times(1) - err := l1.NewClient(provider, chain, nopLog).CatchUpL1Head(t.Context()) + err := l1.NewClient(provider, chain, l1.WithLogger(nopLog)).CatchUpL1Head(t.Context()) require.ErrorContains(t, err, `eth_getBlockByNumber("finalized")`) require.ErrorContains(t, err, "did not respond within") require.ErrorContains(t, err, "--eth-node") @@ -291,7 +291,7 @@ func TestLatestHeightTimeoutDuringCatchUp(t *testing.T) { }). Times(1) - err := l1.NewClient(provider, chain, nopLog).CatchUpL1Head(t.Context()) + err := l1.NewClient(provider, chain, l1.WithLogger(nopLog)).CatchUpL1Head(t.Context()) require.ErrorContains(t, err, "eth_blockNumber did not respond within") require.ErrorContains(t, err, "--eth-node") }) @@ -325,7 +325,7 @@ func TestFilterStateUpdateTimeoutDuringCatchUp(t *testing.T) { }). Times(1) - err := l1.NewClient(provider, chain, nopLog).CatchUpL1Head(t.Context()) + err := l1.NewClient(provider, chain, l1.WithLogger(nopLog)).CatchUpL1Head(t.Context()) require.ErrorContains(t, err, "eth_getLogs did not respond within") require.ErrorContains(t, err, "--eth-node") }) @@ -400,7 +400,7 @@ func TestFinalisedHeightRetryLoopProgressesPastHang(t *testing.T) { Return([]*l1.StateUpdate{event}, nil). Times(1) - client := l1.NewClient(provider, chain, nopLog, l1.WithResubscribeDelay(time.Second)) + client := l1.NewClient(provider, chain, l1.WithLogger(nopLog), l1.WithResubscribeDelay(time.Second)) require.NoError(t, client.CatchUpL1Head(t.Context())) got, err := chain.L1Head() @@ -466,7 +466,7 @@ func TestEventListener(t *testing.T) { var got *core.L1Head client := l1.NewClient( - provider, chain, nopLog, + provider, chain, l1.WithLogger(nopLog), l1.WithResubscribeDelay(0), l1.WithPollFinalisedInterval(time.Nanosecond), l1.WithEventListener(l1.SelectiveListener{ @@ -533,7 +533,7 @@ func TestEventListenerCatchUp(t *testing.T) { var got *core.L1Head client := l1.NewClient( - provider, chain, nopLog, + provider, chain, l1.WithLogger(nopLog), l1.WithResubscribeDelay(0), l1.WithPollFinalisedInterval(time.Hour), l1.WithEventListener(l1.SelectiveListener{ @@ -595,7 +595,7 @@ func TestCatchUpL1Head(t *testing.T) { AnyTimes() provider.EXPECT().Close().AnyTimes() - client := l1.NewClient(provider, chain, nopLog) + client := l1.NewClient(provider, chain, l1.WithLogger(nopLog)) require.NoError(t, client.CatchUpL1Head(t.Context())) persisted, err := chain.L1Head() @@ -623,7 +623,7 @@ func TestCatchUpL1Head_ChainIDMismatch(t *testing.T) { provider.EXPECT().ChainID(gomock.Any()).Return(big.NewInt(999), nil) provider.EXPECT().Close() - err := l1.NewClient(provider, chain, nopLog).CatchUpL1Head(t.Context()) + err := l1.NewClient(provider, chain, l1.WithLogger(nopLog)).CatchUpL1Head(t.Context()) require.ErrorContains(t, err, "mismatched network id between L1 and L2") require.ErrorContains(t, err, "--eth-node") } diff --git a/node/migration.go b/node/migration.go index 0d5bc5e3a3..2c976730ce 100644 --- a/node/migration.go +++ b/node/migration.go @@ -115,7 +115,7 @@ func fetchL1HeadIfMissing( return fmt.Errorf("creating L1 state provider: %w", err) } - client := l1.NewClient(provider, chain, logger) + client := l1.NewClient(provider, chain, l1.WithLogger(logger)) if err := client.CatchUpL1Head(ctx); err != nil { return fmt.Errorf("catching up to the latest L1 head: %w", err) } diff --git a/node/node.go b/node/node.go index ca00dfc5f9..b917d8a3e2 100644 --- a/node/node.go +++ b/node/node.go @@ -720,8 +720,8 @@ func newL1Client( if includeMetrics { registerL1Metrics(provider) } - - return l1.NewClient(provider, chain, logger, l1Opts...), provider, nil + l1Opts = append(l1Opts, l1.WithLogger(logger)) + return l1.NewClient(provider, chain, l1Opts...), provider, nil } // newGethL1StateProvider validates the Ethereum endpoint URL and dials the L1