From 4910cb6b0b4c547bd556fe050df1c7ab46b33884 Mon Sep 17 00:00:00 2001 From: Ivan Wang Date: Tue, 5 Mar 2024 12:06:52 +0800 Subject: [PATCH] Migrate GetBlockRange to op-geth --- ethclient/ethclient_test.go | 48 +++++++++++++++++++++++++++++++++++++ internal/ethapi/api.go | 32 +++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index c22446f6f3..9ca1b85b25 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -25,6 +25,7 @@ import ( "net" "net/http" "reflect" + "strings" "testing" "time" @@ -343,6 +344,15 @@ func TestEthClientHistoricalBackend(t *testing.T) { testHistoricalRPC(t, client) } +func TestEthClientGetBlockRangeBackend(t *testing.T) { + backend, _ := newTestBackend(t, true) + client, _ := backend.Attach() + defer backend.Close() + defer client.Close() + + testGetBlockRangeRPC(t, client) +} + func TestEthClient(t *testing.T) { backend, chain := newTestBackend(t, false) client, _ := backend.Attach() @@ -802,6 +812,44 @@ func testEstimateGas(t *testing.T, client *rpc.Client) { } } +func testGetBlockRangeRPC(t *testing.T, client *rpc.Client) { + type args struct { + startNumber string + endNumber string + fullTx bool + } + tests := []struct { + name string + args args + wantBlocksLength int + wantErr bool + containsErr string + }{ + {"0-0", args{"0x0", "0x0", true}, 1, false, ""}, + {"0-1", args{"0x0", "0x1", true}, 2, false, ""}, + {"0-2", args{"0x0", "0x2", true}, 3, false, ""}, + {"0-30", args{"0x0", "0x1e", true}, 0, true, "block in range not indexed, this should never happen"}, + {"0-1030", args{"0x0", "0x406", true}, 0, true, "requested block range is too large (max is 1000"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := make([]map[string]interface{}, 0) + err := client.CallContext(context.Background(), &got, "eth_getBlockRange", tt.args.startNumber, tt.args.endNumber, tt.args.fullTx) + if (err != nil) != tt.wantErr { + t.Errorf("BlockChainAPI.GetBlockRange() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(len(got), tt.wantBlocksLength) { + t.Errorf("BlockChainAPI.GetBlockRange() = %v, want %v", len(got), tt.wantBlocksLength) + } + if tt.wantErr && !strings.Contains(err.Error(), tt.containsErr) { + t.Errorf("BlockChainAPI.GetBlockRange() error = %v, wantErr %v", err, tt.containsErr) + return + } + }) + } +} + func testHistoricalRPC(t *testing.T, client *rpc.Client) { ec := NewClient(client) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index aee442fb56..e2aa47050b 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -952,6 +952,38 @@ func headerByNumberOrHash(ctx context.Context, b Backend, blockNrOrHash rpc.Bloc return header, err } +func (s *BlockChainAPI) GetBlockRange(ctx context.Context, startNumber rpc.BlockNumber, endNumber rpc.BlockNumber, fullTx bool) ([]map[string]interface{}, error) { + // Basic assertions about start and end block numbers. + if endNumber < startNumber { + return nil, fmt.Errorf("start of block range (%d) is greater than end of block range (%d)", startNumber, endNumber) + } + + // Assert that the number of blocks is < 1k (? configurable?). + if endNumber-startNumber > 1000 { + return nil, fmt.Errorf("requested block range is too large (max is 1000, requested %d blocks)", endNumber-startNumber) + } + + // Make sure the end exists. If start doesn't exist, will be caught immediately below. + if _, err := s.GetBlockByNumber(ctx, endNumber, fullTx); err != nil { + return nil, fmt.Errorf("end of requested block range (%d) does not exist: %w", endNumber, err) + } + + // Create an empty output array. + blocks := make([]map[string]interface{}, 0) + // For each block in range, get block and append to array. + for number := startNumber; number <= endNumber; number++ { + block, err := s.GetBlockByNumber(ctx, number, fullTx) + if err != nil { + return nil, err + } + if block == nil { + return nil, errors.New("block in range not indexed, this should never happen") + } + blocks = append(blocks, block) + } + return blocks, nil +} + // OverrideAccount indicates the overriding fields of account during the execution // of a message call. // Note, state and stateDiff can't be specified at the same time. If state is