From 465ac9d67bb05832fb16f20ee2676d7bb76c5bef Mon Sep 17 00:00:00 2001 From: Arkadiy Kukarkin Date: Fri, 1 May 2026 19:29:46 +0100 Subject: [PATCH 1/2] jrpc2: surface hresp.Error in eth_getBlockByNumber failure --- jrpc2/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jrpc2/client.go b/jrpc2/client.go index f972cca2..0e912fc3 100644 --- a/jrpc2/client.go +++ b/jrpc2/client.go @@ -787,7 +787,7 @@ func (c *Client) logs(ctx context.Context, url string, filter *glf.Filter, bm bl ) switch { case hresp.Error.Exists(): - return fmt.Errorf("rpc=eth_getLogs/eth_getBlockByNumber %w", lresp.Error) + return fmt.Errorf("rpc=eth_getBlockByNumber %w", hresp.Error) case lresp.Error.Exists(): return fmt.Errorf("rpc=eth_getLogs %w", lresp.Error) case hresp.Header == nil: From e5c6add716cff28a881673239194c7c7b6d4e4a9 Mon Sep 17 00:00:00 2001 From: Arkadiy Kukarkin Date: Fri, 1 May 2026 19:30:17 +0100 Subject: [PATCH 2/2] jrpc2: skip null-round blocks --- jrpc2/client.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/jrpc2/client.go b/jrpc2/client.go index 0e912fc3..269fc9dc 100644 --- a/jrpc2/client.go +++ b/jrpc2/client.go @@ -548,6 +548,13 @@ func (c *cache) get(nocache bool, ctx context.Context, url string, start, limit return seg.d, nil } +// some EVM implementations may not produce a block for a given epoch, +// especially in testnet context; treat eth_getBlockByNumber's "null round" +// failure as an empty block so the indexer cursor can advance past the gap +func isNullRoundError(e Error) bool { + return e.Code == 12 && strings.Contains(e.Message, "null round") +} + func (c *Client) blocks(ctx context.Context, url string, start, limit uint64) ([]eth.Block, error) { var ( t0 = time.Now() @@ -570,6 +577,10 @@ func (c *Client) blocks(ctx context.Context, url string, start, limit uint64) ([ } for i := range resps { if resps[i].Error.Exists() { + if isNullRoundError(resps[i].Error) { + blocks[i].Header.Number = eth.Uint64(start + uint64(i)) + continue + } const tag = "eth_getBlockByNumber" return nil, fmt.Errorf("rpc=%s %w", tag, resps[i].Error) } @@ -634,6 +645,10 @@ func (c *Client) headers(ctx context.Context, url string, start, limit uint64) ( } for i := range resps { if resps[i].Error.Exists() { + if isNullRoundError(resps[i].Error) { + blocks[i].Header.Number = eth.Uint64(start + uint64(i)) + continue + } const tag = "eth_getBlockByNumber/headers" return nil, fmt.Errorf("rpc=%s %w", tag, resps[i].Error) } @@ -787,6 +802,12 @@ func (c *Client) logs(ctx context.Context, url string, filter *glf.Filter, bm bl ) switch { case hresp.Error.Exists(): + if isNullRoundError(hresp.Error) { + if b, ok := bm[toBlock]; ok { + b.Header.Number = eth.Uint64(toBlock) + } + break + } return fmt.Errorf("rpc=eth_getBlockByNumber %w", hresp.Error) case lresp.Error.Exists(): return fmt.Errorf("rpc=eth_getLogs %w", lresp.Error)