Skip to content

Commit c8be0f9

Browse files
authored
eth: use headers in debug.GetModifiedAccountsBy* (#31765)
Small optimization in debug_getModifiedAccountsBy* to avoid fetching block body.
1 parent 7e79254 commit c8be0f9

File tree

2 files changed

+137
-25
lines changed

2 files changed

+137
-25
lines changed

eth/api_debug.go

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -271,26 +271,26 @@ func storageRangeAt(statedb *state.StateDB, root common.Hash, address common.Add
271271
//
272272
// With one parameter, returns the list of accounts modified in the specified block.
273273
func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64) ([]common.Address, error) {
274-
var startBlock, endBlock *types.Block
274+
var startHeader, endHeader *types.Header
275275

276-
startBlock = api.eth.blockchain.GetBlockByNumber(startNum)
277-
if startBlock == nil {
276+
startHeader = api.eth.blockchain.GetHeaderByNumber(startNum)
277+
if startHeader == nil {
278278
return nil, fmt.Errorf("start block %x not found", startNum)
279279
}
280280

281281
if endNum == nil {
282-
endBlock = startBlock
283-
startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash())
284-
if startBlock == nil {
285-
return nil, fmt.Errorf("block %x has no parent", endBlock.Number())
282+
endHeader = startHeader
283+
startHeader = api.eth.blockchain.GetHeaderByHash(startHeader.ParentHash)
284+
if startHeader == nil {
285+
return nil, fmt.Errorf("block %x has no parent", endHeader.Number)
286286
}
287287
} else {
288-
endBlock = api.eth.blockchain.GetBlockByNumber(*endNum)
289-
if endBlock == nil {
288+
endHeader = api.eth.blockchain.GetHeaderByNumber(*endNum)
289+
if endHeader == nil {
290290
return nil, fmt.Errorf("end block %d not found", *endNum)
291291
}
292292
}
293-
return api.getModifiedAccounts(startBlock, endBlock)
293+
return api.getModifiedAccounts(startHeader, endHeader)
294294
}
295295

296296
// GetModifiedAccountsByHash returns all accounts that have changed between the
@@ -299,38 +299,38 @@ func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64
299299
//
300300
// With one parameter, returns the list of accounts modified in the specified block.
301301
func (api *DebugAPI) GetModifiedAccountsByHash(startHash common.Hash, endHash *common.Hash) ([]common.Address, error) {
302-
var startBlock, endBlock *types.Block
303-
startBlock = api.eth.blockchain.GetBlockByHash(startHash)
304-
if startBlock == nil {
302+
var startHeader, endHeader *types.Header
303+
startHeader = api.eth.blockchain.GetHeaderByHash(startHash)
304+
if startHeader == nil {
305305
return nil, fmt.Errorf("start block %x not found", startHash)
306306
}
307307

308308
if endHash == nil {
309-
endBlock = startBlock
310-
startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash())
311-
if startBlock == nil {
312-
return nil, fmt.Errorf("block %x has no parent", endBlock.Number())
309+
endHeader = startHeader
310+
startHeader = api.eth.blockchain.GetHeaderByHash(startHeader.ParentHash)
311+
if startHeader == nil {
312+
return nil, fmt.Errorf("block %x has no parent", endHeader.Number)
313313
}
314314
} else {
315-
endBlock = api.eth.blockchain.GetBlockByHash(*endHash)
316-
if endBlock == nil {
315+
endHeader = api.eth.blockchain.GetHeaderByHash(*endHash)
316+
if endHeader == nil {
317317
return nil, fmt.Errorf("end block %x not found", *endHash)
318318
}
319319
}
320-
return api.getModifiedAccounts(startBlock, endBlock)
320+
return api.getModifiedAccounts(startHeader, endHeader)
321321
}
322322

323-
func (api *DebugAPI) getModifiedAccounts(startBlock, endBlock *types.Block) ([]common.Address, error) {
324-
if startBlock.Number().Uint64() >= endBlock.Number().Uint64() {
325-
return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startBlock.Number().Uint64(), endBlock.Number().Uint64())
323+
func (api *DebugAPI) getModifiedAccounts(startHeader, endHeader *types.Header) ([]common.Address, error) {
324+
if startHeader.Number.Uint64() >= endHeader.Number.Uint64() {
325+
return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startHeader.Number.Uint64(), endHeader.Number.Uint64())
326326
}
327327
triedb := api.eth.BlockChain().TrieDB()
328328

329-
oldTrie, err := trie.NewStateTrie(trie.StateTrieID(startBlock.Root()), triedb)
329+
oldTrie, err := trie.NewStateTrie(trie.StateTrieID(startHeader.Root), triedb)
330330
if err != nil {
331331
return nil, err
332332
}
333-
newTrie, err := trie.NewStateTrie(trie.StateTrieID(endBlock.Root()), triedb)
333+
newTrie, err := trie.NewStateTrie(trie.StateTrieID(endHeader.Root), triedb)
334334
if err != nil {
335335
return nil, err
336336
}

eth/api_debug_test.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,74 @@ package eth
1818

1919
import (
2020
"bytes"
21+
"crypto/ecdsa"
2122
"fmt"
23+
"math/big"
2224
"reflect"
2325
"slices"
2426
"strings"
2527
"testing"
28+
"time"
2629

2730
"github.com/davecgh/go-spew/spew"
2831
"github.com/ethereum/go-ethereum/common"
32+
"github.com/ethereum/go-ethereum/consensus/ethash"
33+
"github.com/ethereum/go-ethereum/core"
2934
"github.com/ethereum/go-ethereum/core/rawdb"
3035
"github.com/ethereum/go-ethereum/core/state"
3136
"github.com/ethereum/go-ethereum/core/tracing"
3237
"github.com/ethereum/go-ethereum/core/types"
38+
"github.com/ethereum/go-ethereum/core/vm"
3339
"github.com/ethereum/go-ethereum/crypto"
40+
"github.com/ethereum/go-ethereum/params"
3441
"github.com/ethereum/go-ethereum/triedb"
3542
"github.com/holiman/uint256"
43+
"github.com/stretchr/testify/assert"
3644
)
3745

3846
var dumper = spew.ConfigState{Indent: " "}
3947

48+
type Account struct {
49+
key *ecdsa.PrivateKey
50+
addr common.Address
51+
}
52+
53+
func newAccounts(n int) (accounts []Account) {
54+
for i := 0; i < n; i++ {
55+
key, _ := crypto.GenerateKey()
56+
addr := crypto.PubkeyToAddress(key.PublicKey)
57+
accounts = append(accounts, Account{key: key, addr: addr})
58+
}
59+
slices.SortFunc(accounts, func(a, b Account) int { return a.addr.Cmp(b.addr) })
60+
return accounts
61+
}
62+
63+
// newTestBlockChain creates a new test blockchain. OBS: After test is done, teardown must be
64+
// invoked in order to release associated resources.
65+
func newTestBlockChain(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *core.BlockChain {
66+
engine := ethash.NewFaker()
67+
// Generate blocks for testing
68+
_, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, n, generator)
69+
70+
// Import the canonical chain
71+
cacheConfig := &core.CacheConfig{
72+
TrieCleanLimit: 256,
73+
TrieDirtyLimit: 256,
74+
TrieTimeLimit: 5 * time.Minute,
75+
SnapshotLimit: 0,
76+
Preimages: true,
77+
TrieDirtyDisabled: true, // Archive mode
78+
}
79+
chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), cacheConfig, gspec, nil, engine, vm.Config{}, nil)
80+
if err != nil {
81+
t.Fatalf("failed to create tester chain: %v", err)
82+
}
83+
if n, err := chain.InsertChain(blocks); err != nil {
84+
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
85+
}
86+
return chain
87+
}
88+
4089
func accountRangeTest(t *testing.T, trie *state.Trie, statedb *state.StateDB, start common.Hash, requestedNum int, expectedNum int) state.Dump {
4190
result := statedb.RawDump(&state.DumpConfig{
4291
SkipCode: true,
@@ -224,3 +273,66 @@ func TestStorageRangeAt(t *testing.T) {
224273
}
225274
}
226275
}
276+
277+
func TestGetModifiedAccounts(t *testing.T) {
278+
t.Parallel()
279+
280+
// Initialize test accounts
281+
accounts := newAccounts(4)
282+
genesis := &core.Genesis{
283+
Config: params.TestChainConfig,
284+
Alloc: types.GenesisAlloc{
285+
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
286+
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
287+
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
288+
accounts[3].addr: {Balance: big.NewInt(params.Ether)},
289+
},
290+
}
291+
genBlocks := 1
292+
signer := types.HomesteadSigner{}
293+
blockChain := newTestBlockChain(t, genBlocks, genesis, func(_ int, b *core.BlockGen) {
294+
// Transfer from account[0] to account[1]
295+
// value: 1000 wei
296+
// fee: 0 wei
297+
for _, account := range accounts[:3] {
298+
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
299+
Nonce: 0,
300+
To: &accounts[3].addr,
301+
Value: big.NewInt(1000),
302+
Gas: params.TxGas,
303+
GasPrice: b.BaseFee(),
304+
Data: nil}),
305+
signer, account.key)
306+
b.AddTx(tx)
307+
}
308+
})
309+
defer blockChain.Stop()
310+
311+
// Create a debug API instance.
312+
api := NewDebugAPI(&Ethereum{blockchain: blockChain})
313+
314+
// Test GetModifiedAccountsByNumber
315+
t.Run("GetModifiedAccountsByNumber", func(t *testing.T) {
316+
addrs, err := api.GetModifiedAccountsByNumber(uint64(genBlocks), nil)
317+
assert.NoError(t, err)
318+
assert.Len(t, addrs, len(accounts)+1) // +1 for the coinbase
319+
for _, account := range accounts {
320+
if !slices.Contains(addrs, account.addr) {
321+
t.Fatalf("account %s not found in modified accounts", account.addr.Hex())
322+
}
323+
}
324+
})
325+
326+
// Test GetModifiedAccountsByHash
327+
t.Run("GetModifiedAccountsByHash", func(t *testing.T) {
328+
header := blockChain.GetHeaderByNumber(uint64(genBlocks))
329+
addrs, err := api.GetModifiedAccountsByHash(header.Hash(), nil)
330+
assert.NoError(t, err)
331+
assert.Len(t, addrs, len(accounts)+1) // +1 for the coinbase
332+
for _, account := range accounts {
333+
if !slices.Contains(addrs, account.addr) {
334+
t.Fatalf("account %s not found in modified accounts", account.addr.Hex())
335+
}
336+
}
337+
})
338+
}

0 commit comments

Comments
 (0)