forked from forks/go-ethereum
eth: use headers in debug.GetModifiedAccountsBy* (#31765)
Small optimization in debug_getModifiedAccountsBy* to avoid fetching block body.
This commit is contained in:
parent
7e79254605
commit
c8be0f9a74
2 changed files with 137 additions and 25 deletions
|
|
@ -271,26 +271,26 @@ func storageRangeAt(statedb *state.StateDB, root common.Hash, address common.Add
|
||||||
//
|
//
|
||||||
// With one parameter, returns the list of accounts modified in the specified block.
|
// With one parameter, returns the list of accounts modified in the specified block.
|
||||||
func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64) ([]common.Address, error) {
|
func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64) ([]common.Address, error) {
|
||||||
var startBlock, endBlock *types.Block
|
var startHeader, endHeader *types.Header
|
||||||
|
|
||||||
startBlock = api.eth.blockchain.GetBlockByNumber(startNum)
|
startHeader = api.eth.blockchain.GetHeaderByNumber(startNum)
|
||||||
if startBlock == nil {
|
if startHeader == nil {
|
||||||
return nil, fmt.Errorf("start block %x not found", startNum)
|
return nil, fmt.Errorf("start block %x not found", startNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
if endNum == nil {
|
if endNum == nil {
|
||||||
endBlock = startBlock
|
endHeader = startHeader
|
||||||
startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash())
|
startHeader = api.eth.blockchain.GetHeaderByHash(startHeader.ParentHash)
|
||||||
if startBlock == nil {
|
if startHeader == nil {
|
||||||
return nil, fmt.Errorf("block %x has no parent", endBlock.Number())
|
return nil, fmt.Errorf("block %x has no parent", endHeader.Number)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
endBlock = api.eth.blockchain.GetBlockByNumber(*endNum)
|
endHeader = api.eth.blockchain.GetHeaderByNumber(*endNum)
|
||||||
if endBlock == nil {
|
if endHeader == nil {
|
||||||
return nil, fmt.Errorf("end block %d not found", *endNum)
|
return nil, fmt.Errorf("end block %d not found", *endNum)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.getModifiedAccounts(startBlock, endBlock)
|
return api.getModifiedAccounts(startHeader, endHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetModifiedAccountsByHash returns all accounts that have changed between the
|
// GetModifiedAccountsByHash returns all accounts that have changed between the
|
||||||
|
|
@ -299,38 +299,38 @@ func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64
|
||||||
//
|
//
|
||||||
// With one parameter, returns the list of accounts modified in the specified block.
|
// With one parameter, returns the list of accounts modified in the specified block.
|
||||||
func (api *DebugAPI) GetModifiedAccountsByHash(startHash common.Hash, endHash *common.Hash) ([]common.Address, error) {
|
func (api *DebugAPI) GetModifiedAccountsByHash(startHash common.Hash, endHash *common.Hash) ([]common.Address, error) {
|
||||||
var startBlock, endBlock *types.Block
|
var startHeader, endHeader *types.Header
|
||||||
startBlock = api.eth.blockchain.GetBlockByHash(startHash)
|
startHeader = api.eth.blockchain.GetHeaderByHash(startHash)
|
||||||
if startBlock == nil {
|
if startHeader == nil {
|
||||||
return nil, fmt.Errorf("start block %x not found", startHash)
|
return nil, fmt.Errorf("start block %x not found", startHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
if endHash == nil {
|
if endHash == nil {
|
||||||
endBlock = startBlock
|
endHeader = startHeader
|
||||||
startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash())
|
startHeader = api.eth.blockchain.GetHeaderByHash(startHeader.ParentHash)
|
||||||
if startBlock == nil {
|
if startHeader == nil {
|
||||||
return nil, fmt.Errorf("block %x has no parent", endBlock.Number())
|
return nil, fmt.Errorf("block %x has no parent", endHeader.Number)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
endBlock = api.eth.blockchain.GetBlockByHash(*endHash)
|
endHeader = api.eth.blockchain.GetHeaderByHash(*endHash)
|
||||||
if endBlock == nil {
|
if endHeader == nil {
|
||||||
return nil, fmt.Errorf("end block %x not found", *endHash)
|
return nil, fmt.Errorf("end block %x not found", *endHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.getModifiedAccounts(startBlock, endBlock)
|
return api.getModifiedAccounts(startHeader, endHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *DebugAPI) getModifiedAccounts(startBlock, endBlock *types.Block) ([]common.Address, error) {
|
func (api *DebugAPI) getModifiedAccounts(startHeader, endHeader *types.Header) ([]common.Address, error) {
|
||||||
if startBlock.Number().Uint64() >= endBlock.Number().Uint64() {
|
if startHeader.Number.Uint64() >= endHeader.Number.Uint64() {
|
||||||
return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startBlock.Number().Uint64(), endBlock.Number().Uint64())
|
return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startHeader.Number.Uint64(), endHeader.Number.Uint64())
|
||||||
}
|
}
|
||||||
triedb := api.eth.BlockChain().TrieDB()
|
triedb := api.eth.BlockChain().TrieDB()
|
||||||
|
|
||||||
oldTrie, err := trie.NewStateTrie(trie.StateTrieID(startBlock.Root()), triedb)
|
oldTrie, err := trie.NewStateTrie(trie.StateTrieID(startHeader.Root), triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
newTrie, err := trie.NewStateTrie(trie.StateTrieID(endBlock.Root()), triedb)
|
newTrie, err := trie.NewStateTrie(trie.StateTrieID(endHeader.Root), triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,25 +18,74 @@ package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var dumper = spew.ConfigState{Indent: " "}
|
var dumper = spew.ConfigState{Indent: " "}
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
key *ecdsa.PrivateKey
|
||||||
|
addr common.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAccounts(n int) (accounts []Account) {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
accounts = append(accounts, Account{key: key, addr: addr})
|
||||||
|
}
|
||||||
|
slices.SortFunc(accounts, func(a, b Account) int { return a.addr.Cmp(b.addr) })
|
||||||
|
return accounts
|
||||||
|
}
|
||||||
|
|
||||||
|
// newTestBlockChain creates a new test blockchain. OBS: After test is done, teardown must be
|
||||||
|
// invoked in order to release associated resources.
|
||||||
|
func newTestBlockChain(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *core.BlockChain {
|
||||||
|
engine := ethash.NewFaker()
|
||||||
|
// Generate blocks for testing
|
||||||
|
_, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, n, generator)
|
||||||
|
|
||||||
|
// Import the canonical chain
|
||||||
|
cacheConfig := &core.CacheConfig{
|
||||||
|
TrieCleanLimit: 256,
|
||||||
|
TrieDirtyLimit: 256,
|
||||||
|
TrieTimeLimit: 5 * time.Minute,
|
||||||
|
SnapshotLimit: 0,
|
||||||
|
Preimages: true,
|
||||||
|
TrieDirtyDisabled: true, // Archive mode
|
||||||
|
}
|
||||||
|
chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), cacheConfig, gspec, nil, engine, vm.Config{}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
|
}
|
||||||
|
if n, err := chain.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||||
|
}
|
||||||
|
return chain
|
||||||
|
}
|
||||||
|
|
||||||
func accountRangeTest(t *testing.T, trie *state.Trie, statedb *state.StateDB, start common.Hash, requestedNum int, expectedNum int) state.Dump {
|
func accountRangeTest(t *testing.T, trie *state.Trie, statedb *state.StateDB, start common.Hash, requestedNum int, expectedNum int) state.Dump {
|
||||||
result := statedb.RawDump(&state.DumpConfig{
|
result := statedb.RawDump(&state.DumpConfig{
|
||||||
SkipCode: true,
|
SkipCode: true,
|
||||||
|
|
@ -224,3 +273,66 @@ func TestStorageRangeAt(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetModifiedAccounts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Initialize test accounts
|
||||||
|
accounts := newAccounts(4)
|
||||||
|
genesis := &core.Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: types.GenesisAlloc{
|
||||||
|
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[3].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
genBlocks := 1
|
||||||
|
signer := types.HomesteadSigner{}
|
||||||
|
blockChain := newTestBlockChain(t, genBlocks, genesis, func(_ int, b *core.BlockGen) {
|
||||||
|
// Transfer from account[0] to account[1]
|
||||||
|
// value: 1000 wei
|
||||||
|
// fee: 0 wei
|
||||||
|
for _, account := range accounts[:3] {
|
||||||
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
|
||||||
|
Nonce: 0,
|
||||||
|
To: &accounts[3].addr,
|
||||||
|
Value: big.NewInt(1000),
|
||||||
|
Gas: params.TxGas,
|
||||||
|
GasPrice: b.BaseFee(),
|
||||||
|
Data: nil}),
|
||||||
|
signer, account.key)
|
||||||
|
b.AddTx(tx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer blockChain.Stop()
|
||||||
|
|
||||||
|
// Create a debug API instance.
|
||||||
|
api := NewDebugAPI(&Ethereum{blockchain: blockChain})
|
||||||
|
|
||||||
|
// Test GetModifiedAccountsByNumber
|
||||||
|
t.Run("GetModifiedAccountsByNumber", func(t *testing.T) {
|
||||||
|
addrs, err := api.GetModifiedAccountsByNumber(uint64(genBlocks), nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, addrs, len(accounts)+1) // +1 for the coinbase
|
||||||
|
for _, account := range accounts {
|
||||||
|
if !slices.Contains(addrs, account.addr) {
|
||||||
|
t.Fatalf("account %s not found in modified accounts", account.addr.Hex())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test GetModifiedAccountsByHash
|
||||||
|
t.Run("GetModifiedAccountsByHash", func(t *testing.T) {
|
||||||
|
header := blockChain.GetHeaderByNumber(uint64(genBlocks))
|
||||||
|
addrs, err := api.GetModifiedAccountsByHash(header.Hash(), nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, addrs, len(accounts)+1) // +1 for the coinbase
|
||||||
|
for _, account := range accounts {
|
||||||
|
if !slices.Contains(addrs, account.addr) {
|
||||||
|
t.Fatalf("account %s not found in modified accounts", account.addr.Hex())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue