mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
eth: add debug_accountRange rpc call
This commit is contained in:
parent
72029f0f88
commit
a6c2a5b05d
2 changed files with 112 additions and 0 deletions
65
eth/api.go
65
eth/api.go
|
|
@ -17,6 +17,7 @@
|
||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
@ -334,6 +335,70 @@ func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs,
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccountRangeResult struct {
|
||||||
|
Preimages []common.Hash `json:"preimages"`
|
||||||
|
Next common.Hash `json:"nextKey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func accountRange(st state.Trie, start *common.Hash, maxResults int) (AccountRangeResult, error) {
|
||||||
|
it := trie.NewIterator(st.NodeIterator(start[:]))
|
||||||
|
result := AccountRangeResult{Preimages: []common.Hash{}, Next: common.Hash{}}
|
||||||
|
zeros := make([]byte, common.HashLength)
|
||||||
|
|
||||||
|
if bytes.Equal(start.Bytes()[:], zeros) {
|
||||||
|
start = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxResults > 100 {
|
||||||
|
maxResults = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
// dont return the first account preimage
|
||||||
|
if !it.Next() && start != &(common.Hash{}) {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < maxResults-1 && it.Next(); i++ {
|
||||||
|
result.Preimages = append(result.Preimages, common.BytesToHash(it.Key))
|
||||||
|
}
|
||||||
|
|
||||||
|
if it.Next() {
|
||||||
|
result.Next = common.BytesToHash(it.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//enumerate and return account preimages in the state at the current block
|
||||||
|
func (api *PrivateDebugAPI) AccountRange(ctx context.Context, startAddr *common.Hash, maxResults int) (AccountRangeResult, error) {
|
||||||
|
var statedb *state.StateDB = nil
|
||||||
|
var err error = nil
|
||||||
|
var block = api.eth.blockchain.CurrentBlock()
|
||||||
|
|
||||||
|
if len(block.Transactions()) == 0 {
|
||||||
|
parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||||
|
if parent == nil {
|
||||||
|
return AccountRangeResult{}, fmt.Errorf("parent %x not found", block.ParentHash())
|
||||||
|
}
|
||||||
|
statedb, err = api.computeStateDB(parent, defaultTraceReexec)
|
||||||
|
if err != nil {
|
||||||
|
return AccountRangeResult{}, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_, _, statedb, err = api.computeTxEnv(block.Hash(), len(block.Transactions())-1, 0)
|
||||||
|
if err != nil {
|
||||||
|
return AccountRangeResult{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trie, err := statedb.Database().OpenTrie(block.Header().Root)
|
||||||
|
if err != nil {
|
||||||
|
return AccountRangeResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return accountRange(trie, startAddr, maxResults)
|
||||||
|
}
|
||||||
|
|
||||||
// StorageRangeResult is the result of a debug_storageRangeAt API call.
|
// StorageRangeResult is the result of a debug_storageRangeAt API call.
|
||||||
type StorageRangeResult struct {
|
type StorageRangeResult struct {
|
||||||
Storage storageMap `json:"storage"`
|
Storage storageMap `json:"storage"`
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,9 @@ package eth
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"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/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
|
@ -28,6 +30,51 @@ import (
|
||||||
|
|
||||||
var dumper = spew.ConfigState{Indent: " "}
|
var dumper = spew.ConfigState{Indent: " "}
|
||||||
|
|
||||||
|
func TestAccountRange(t *testing.T) {
|
||||||
|
state, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||||
|
expectedPreimages := make(map[common.Hash]interface{})
|
||||||
|
|
||||||
|
// create some accounts
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
address := common.Address{byte(i)}
|
||||||
|
obj := state.GetOrNewStateObject(address)
|
||||||
|
expectedPreimages[crypto.Keccak256Hash(address.Bytes())] = true
|
||||||
|
obj.AddBalance(big.NewInt(22))
|
||||||
|
obj.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
|
||||||
|
|
||||||
|
//root = state.Commit(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
root, err := state.Commit(false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
state.Finalise(false)
|
||||||
|
|
||||||
|
trie, err := state.Database().OpenTrie(root)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
zeroHash := crypto.Keccak256Hash(common.Hash{}.Bytes())
|
||||||
|
result, err := accountRange(trie, &zeroHash, 256)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result.Preimages) != 99 {
|
||||||
|
t.Fatalf("maximum number of results returned should be 99, returned %d", len(result.Preimages))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range result.Preimages {
|
||||||
|
if _, ok := expectedPreimages[v]; !ok {
|
||||||
|
t.Fatalf("expected to find preimage %s in result", v.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func TestStorageRangeAt(t *testing.T) {
|
func TestStorageRangeAt(t *testing.T) {
|
||||||
// Create a state where account 0x010000... has a few storage entries.
|
// Create a state where account 0x010000... has a few storage entries.
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue