add: keep only debug_batchGetStorage; add tests

This commit is contained in:
quantumshiro 2025-09-12 02:58:19 +09:00 committed by Sina Mahmoodi
parent 086e2be246
commit 7a5fffa380
3 changed files with 111 additions and 37 deletions

View file

@ -1925,33 +1925,7 @@ type DebugAPI struct {
// NewDebugAPI creates a new instance of DebugAPI. // NewDebugAPI creates a new instance of DebugAPI.
func NewDebugAPI(b Backend) *DebugAPI { func NewDebugAPI(b Backend) *DebugAPI {
return &DebugAPI{b: b} 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
} }
// BatchGetStorage returns multiple storage slots for multiple accounts at the given block. // BatchGetStorage returns multiple storage slots for multiple accounts at the given block.

View file

@ -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
)

View file

@ -415,16 +415,11 @@ web3._extend({
call: 'debug_storageRangeAt', call: 'debug_storageRangeAt',
params: 5, params: 5,
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'batchGetStorageAt', name: 'batchGetStorage',
call: 'debug_batchGetStorageAt', call: 'debug_batchGetStorage',
params: 3, params: 2,
}), }),
new web3._extend.Method({
name: 'batchGetStorage',
call: 'debug_batchGetStorage',
params: 2,
}),
new web3._extend.Method({ new web3._extend.Method({
name: 'getModifiedAccountsByNumber', name: 'getModifiedAccountsByNumber',
call: 'debug_getModifiedAccountsByNumber', call: 'debug_getModifiedAccountsByNumber',