From 7a5fffa38092eb64c8cd743e99bb2031cf6f265c Mon Sep 17 00:00:00 2001 From: quantumshiro Date: Fri, 12 Sep 2025 02:58:19 +0900 Subject: [PATCH] add: keep only debug_batchGetStorage; add tests --- internal/ethapi/api.go | 28 +------ internal/ethapi/batch_storage_test.go | 105 ++++++++++++++++++++++++++ internal/web3ext/web3ext.go | 15 ++-- 3 files changed, 111 insertions(+), 37 deletions(-) create mode 100644 internal/ethapi/batch_storage_test.go diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 34daa9d589..d50a8a9e90 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1925,33 +1925,7 @@ type DebugAPI struct { // NewDebugAPI creates a new instance of DebugAPI. func NewDebugAPI(b Backend) *DebugAPI { - return &DebugAPI{b: b} -} - -// BatchGetStorageAt returns multiple storage slots for a single account at the given block. -// Params: address, [hexKey...], blockNrOrHash -// Returns values in the same order as input keys. -func (api *DebugAPI) BatchGetStorageAt(ctx context.Context, address common.Address, hexKeys []string, blockNrOrHash rpc.BlockNumberOrHash) ([]hexutil.Bytes, error) { - state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) - if state == nil || err != nil { - return nil, err - } - // Preallocate results - res := make([]hexutil.Bytes, len(hexKeys)) - for i, k := range hexKeys { - key, _, err := decodeHash(k) - if err != nil { - return nil, fmt.Errorf("unable to decode storage key at index %d: %w", i, err) - } - v := state.GetState(address, key) - vv := make([]byte, len(v)) - copy(vv, v[:]) - res[i] = vv - } - if err := state.Error(); err != nil { - return nil, err - } - return res, nil + return &DebugAPI{b: b} } // BatchGetStorage returns multiple storage slots for multiple accounts at the given block. diff --git a/internal/ethapi/batch_storage_test.go b/internal/ethapi/batch_storage_test.go new file mode 100644 index 0000000000..2afeb802c1 --- /dev/null +++ b/internal/ethapi/batch_storage_test.go @@ -0,0 +1,105 @@ +package ethapi + +import ( + "context" + "testing" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" +) + +// storageBackend composes backendMock and overrides StateAndHeaderByNumberOrHash +// to return a prepared in-memory StateDB for testing. +type storageBackend struct { + *backendMock + state *state.StateDB +} + +func (b *storageBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) { + return b.state, nil, nil +} + +// Ensure storageBackend still satisfies the Backend interface by forwarding +// the remaining methods via the embedded backendMock (compile-time check). +var _ Backend = (*storageBackend)(nil) + +func TestDebugBatchGetStorage(t *testing.T) { + t.Parallel() + + // Prepare an in-memory state database and set some storage values. + sdb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) + + addr1 := common.HexToAddress("0x1000000000000000000000000000000000000001") + addr2 := common.HexToAddress("0x2000000000000000000000000000000000000002") + + keyA := common.HexToHash("0x01") + keyB := common.HexToHash("0x02") + keyC := common.HexToHash("0x03") + + valA := common.HexToHash("0xaaa") + valB := common.HexToHash("0xbbb") + valC := common.HexToHash("0xccc") + + sdb.SetState(addr1, keyA, valA) + sdb.SetState(addr1, keyB, valB) + sdb.SetState(addr2, keyC, valC) + + // Wire the API with a backend that returns our state. + base := newBackendMock() + b := &storageBackend{backendMock: base, state: sdb} + api := NewDebugAPI(b) + + // Build request: addr1 asks [keyB, keyA] (order test), addr2 asks [keyC, missing] + req := map[common.Address][]string{ + addr1: {"0x02", "0x01"}, + addr2: {"0x03", "0x04"}, + } + + got, err := api.BatchGetStorage(context.Background(), req, rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)) + if err != nil { + t.Fatalf("BatchGetStorage returned error: %v", err) + } + + // Validate addr1 order and values + if len(got[addr1]) != 2 { + t.Fatalf("addr1 length mismatch: got %d want 2", len(got[addr1])) + } + if hexutil.Bytes(got[addr1][0]).String() != hexutil.Bytes(valB[:]).String() { + t.Fatalf("addr1[0] mismatch: got %s want %s", hexutil.Bytes(got[addr1][0]).String(), hexutil.Bytes(valB[:]).String()) + } + if hexutil.Bytes(got[addr1][1]).String() != hexutil.Bytes(valA[:]).String() { + t.Fatalf("addr1[1] mismatch: got %s want %s", hexutil.Bytes(got[addr1][1]).String(), hexutil.Bytes(valA[:]).String()) + } + + // Validate addr2 values (existing and zero for missing) + if len(got[addr2]) != 2 { + t.Fatalf("addr2 length mismatch: got %d want 2", len(got[addr2])) + } + if hexutil.Bytes(got[addr2][0]).String() != hexutil.Bytes(valC[:]).String() { + t.Fatalf("addr2[0] mismatch: got %s want %s", hexutil.Bytes(got[addr2][0]).String(), hexutil.Bytes(valC[:]).String()) + } + if hexutil.Bytes(got[addr2][1]).String() != hexutil.Bytes(common.Hash{}[:]).String() { + t.Fatalf("addr2[1] mismatch: got %s want %s (zero)", hexutil.Bytes(got[addr2][1]).String(), hexutil.Bytes(common.Hash{}[:]).String()) + } +} + +// Ensure backendMock compiles in this file by referencing needed imports +// (no-ops; prevents unused import errors if interface evolves). +var ( + _ = accounts.Account{} + _ = core.ChainEvent{} + _ vm.Config + _ ethdb.Database + _ event.Subscription + _ params.ChainConfig +) + diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index a490967321..f3132d7692 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -415,16 +415,11 @@ web3._extend({ call: 'debug_storageRangeAt', params: 5, }), - new web3._extend.Method({ - name: 'batchGetStorageAt', - call: 'debug_batchGetStorageAt', - params: 3, - }), - new web3._extend.Method({ - name: 'batchGetStorage', - call: 'debug_batchGetStorage', - params: 2, - }), + new web3._extend.Method({ + name: 'batchGetStorage', + call: 'debug_batchGetStorage', + params: 2, + }), new web3._extend.Method({ name: 'getModifiedAccountsByNumber', call: 'debug_getModifiedAccountsByNumber',