core, eth: add debug_iteratorAccountAt rpc call

This commit is contained in:
Delweng Zheng 2019-09-19 20:22:08 +08:00 committed by Martin Holst Swende
parent 05ccbb5edd
commit dd316619e3
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
2 changed files with 81 additions and 4 deletions

View file

@ -51,6 +51,13 @@ type iterativeDump struct {
*json.Encoder *json.Encoder
} }
// IteratorDump is a
type IteratorDump struct {
Root string `json:"root"`
Accounts map[common.Address]DumpAccount `json:"accounts"`
Next []byte `json:"next,omitempty"` // nil if no more accounts
}
// Collector interface which the state trie calls during iteration // Collector interface which the state trie calls during iteration
type collector interface { type collector interface {
onRoot(common.Hash) onRoot(common.Hash)
@ -64,6 +71,13 @@ func (d *Dump) onRoot(root common.Hash) {
func (d *Dump) onAccount(addr common.Address, account DumpAccount) { func (d *Dump) onAccount(addr common.Address, account DumpAccount) {
d.Accounts[addr] = account d.Accounts[addr] = account
} }
func (d *IteratorDump) onRoot(root common.Hash) {
d.Root = fmt.Sprintf("%x", root)
}
func (d *IteratorDump) onAccount(addr common.Address, account DumpAccount) {
d.Accounts[addr] = account
}
func (d iterativeDump) onAccount(addr common.Address, account DumpAccount) { func (d iterativeDump) onAccount(addr common.Address, account DumpAccount) {
dumpAccount := &DumpAccount{ dumpAccount := &DumpAccount{
@ -88,11 +102,13 @@ func (d iterativeDump) onRoot(root common.Hash) {
}{root}) }{root})
} }
func (s *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingPreimages bool) { func (s *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingPreimages bool, start []byte, maxResults int) (nextKey []byte) {
emptyAddress := (common.Address{}) emptyAddress := (common.Address{})
missingPreimages := 0 missingPreimages := 0
c.onRoot(s.trie.Hash()) c.onRoot(s.trie.Hash())
it := trie.NewIterator(s.trie.NodeIterator(nil))
var count int
it := trie.NewIterator(s.trie.NodeIterator(start))
for it.Next() { for it.Next() {
var data Account var data Account
if err := rlp.DecodeBytes(it.Value, &data); err != nil { if err := rlp.DecodeBytes(it.Value, &data); err != nil {
@ -130,10 +146,25 @@ func (s *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingP
} }
} }
c.onAccount(addr, account) c.onAccount(addr, account)
if maxResults <= 0 {
continue
} }
count++
if count >= maxResults {
if it.Next() {
nextKey = it.Key
}
break
}
}
if missingPreimages > 0 { if missingPreimages > 0 {
log.Warn("Dump incomplete due to missing preimages", "missing", missingPreimages) log.Warn("Dump incomplete due to missing preimages", "missing", missingPreimages)
} }
return nextKey
} }
// RawDump returns the entire state an a single large object // RawDump returns the entire state an a single large object
@ -141,7 +172,7 @@ func (s *StateDB) RawDump(excludeCode, excludeStorage, excludeMissingPreimages b
dump := &Dump{ dump := &Dump{
Accounts: make(map[common.Address]DumpAccount), Accounts: make(map[common.Address]DumpAccount),
} }
s.dump(dump, excludeCode, excludeStorage, excludeMissingPreimages) s.dump(dump, excludeCode, excludeStorage, excludeMissingPreimages, nil, 0)
return *dump return *dump
} }
@ -157,5 +188,14 @@ func (s *StateDB) Dump(excludeCode, excludeStorage, excludeMissingPreimages bool
// IterativeDump dumps out accounts as json-objects, delimited by linebreaks on stdout // IterativeDump dumps out accounts as json-objects, delimited by linebreaks on stdout
func (s *StateDB) IterativeDump(excludeCode, excludeStorage, excludeMissingPreimages bool, output *json.Encoder) { func (s *StateDB) IterativeDump(excludeCode, excludeStorage, excludeMissingPreimages bool, output *json.Encoder) {
s.dump(iterativeDump{output}, excludeCode, excludeStorage, excludeMissingPreimages) s.dump(iterativeDump{output}, excludeCode, excludeStorage, excludeMissingPreimages, nil, 0)
}
// IteratorDump dumps out a batch of accounts starts with the given start key
func (s *StateDB) IteratorDump(excludeCode, excludeStorage, excludeMissingPreimages bool, start []byte, maxResults int) IteratorDump {
iterator := &IteratorDump{
Accounts: make(map[common.Address]DumpAccount),
}
iterator.Next = s.dump(iterator, excludeCode, excludeStorage, excludeMissingPreimages, start, maxResults)
return *iterator
} }

View file

@ -301,6 +301,43 @@ func (api *PublicDebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error
return stateDb.RawDump(false, false, true), nil return stateDb.RawDump(false, false, true), nil
} }
// IteratorAccountMaxResults is the maximum number of results to be returned per request
const IteratorAccountMaxResults = 1000
// IteratorAccountAt enumerates all accounts in the given block and start point in paging request
func (api *PublicDebugAPI) IteratorAccountAt(blockNr rpc.BlockNumber, start []byte, maxResults int, nocode, nostorage, incompletes bool) (state.IteratorDump, error) {
var stateDb *state.StateDB
var err error
if blockNr == rpc.PendingBlockNumber {
// If we're dumping the pending state, we need to request
// both the pending block as well as the pending state from
// the miner and operate on those
_, stateDb = api.eth.miner.Pending()
} else {
var block *types.Block
if blockNr == rpc.LatestBlockNumber {
block = api.eth.blockchain.CurrentBlock()
} else {
block = api.eth.blockchain.GetBlockByNumber(uint64(blockNr))
}
if block == nil {
return state.IteratorDump{}, fmt.Errorf("block #%d not found", blockNr)
}
stateDb, err = api.eth.BlockChain().StateAt(block.Root())
if err != nil {
return state.IteratorDump{}, err
}
}
if maxResults > IteratorAccountMaxResults {
maxResults = IteratorAccountMaxResults
}
return stateDb.IteratorDump(nocode, nostorage, incompletes, start, maxResults), nil
}
// PrivateDebugAPI is the collection of Ethereum full node APIs exposed over // PrivateDebugAPI is the collection of Ethereum full node APIs exposed over
// the private debugging endpoint. // the private debugging endpoint.
type PrivateDebugAPI struct { type PrivateDebugAPI struct {