|
|
@ -19,6 +19,7 @@ package state |
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"encoding/json" |
|
|
|
"encoding/json" |
|
|
|
"fmt" |
|
|
|
"fmt" |
|
|
|
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common" |
|
|
|
"github.com/ethereum/go-ethereum/common" |
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil" |
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil" |
|
|
@ -27,6 +28,16 @@ import ( |
|
|
|
"github.com/ethereum/go-ethereum/trie" |
|
|
|
"github.com/ethereum/go-ethereum/trie" |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// DumpConfig is a set of options to control what portions of the statewill be
|
|
|
|
|
|
|
|
// iterated and collected.
|
|
|
|
|
|
|
|
type DumpConfig struct { |
|
|
|
|
|
|
|
SkipCode bool |
|
|
|
|
|
|
|
SkipStorage bool |
|
|
|
|
|
|
|
OnlyWithAddresses bool |
|
|
|
|
|
|
|
Start []byte |
|
|
|
|
|
|
|
Max uint64 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// DumpCollector interface which the state trie calls during iteration
|
|
|
|
// DumpCollector interface which the state trie calls during iteration
|
|
|
|
type DumpCollector interface { |
|
|
|
type DumpCollector interface { |
|
|
|
// OnRoot is called with the state root
|
|
|
|
// OnRoot is called with the state root
|
|
|
@ -39,9 +50,9 @@ type DumpCollector interface { |
|
|
|
type DumpAccount struct { |
|
|
|
type DumpAccount struct { |
|
|
|
Balance string `json:"balance"` |
|
|
|
Balance string `json:"balance"` |
|
|
|
Nonce uint64 `json:"nonce"` |
|
|
|
Nonce uint64 `json:"nonce"` |
|
|
|
Root string `json:"root"` |
|
|
|
Root hexutil.Bytes `json:"root"` |
|
|
|
CodeHash string `json:"codeHash"` |
|
|
|
CodeHash hexutil.Bytes `json:"codeHash"` |
|
|
|
Code string `json:"code,omitempty"` |
|
|
|
Code hexutil.Bytes `json:"code,omitempty"` |
|
|
|
Storage map[common.Hash]string `json:"storage,omitempty"` |
|
|
|
Storage map[common.Hash]string `json:"storage,omitempty"` |
|
|
|
Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode
|
|
|
|
Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode
|
|
|
|
SecureKey hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key
|
|
|
|
SecureKey hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key
|
|
|
@ -111,38 +122,50 @@ func (d iterativeDump) OnRoot(root common.Hash) { |
|
|
|
}{root}) |
|
|
|
}{root}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (s *StateDB) DumpToCollector(c DumpCollector, excludeCode, excludeStorage, excludeMissingPreimages bool, start []byte, maxResults int) (nextKey []byte) { |
|
|
|
// DumpToCollector iterates the state according to the given options and inserts
|
|
|
|
missingPreimages := 0 |
|
|
|
// the items into a collector for aggregation or serialization.
|
|
|
|
|
|
|
|
func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []byte) { |
|
|
|
|
|
|
|
// Sanitize the input to allow nil configs
|
|
|
|
|
|
|
|
if conf == nil { |
|
|
|
|
|
|
|
conf = new(DumpConfig) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
var ( |
|
|
|
|
|
|
|
missingPreimages int |
|
|
|
|
|
|
|
accounts uint64 |
|
|
|
|
|
|
|
start = time.Now() |
|
|
|
|
|
|
|
logged = time.Now() |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
log.Info("Trie dumping started", "root", s.trie.Hash()) |
|
|
|
c.OnRoot(s.trie.Hash()) |
|
|
|
c.OnRoot(s.trie.Hash()) |
|
|
|
|
|
|
|
|
|
|
|
var count int |
|
|
|
it := trie.NewIterator(s.trie.NodeIterator(conf.Start)) |
|
|
|
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 { |
|
|
|
panic(err) |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
} |
|
|
|
account := DumpAccount{ |
|
|
|
account := DumpAccount{ |
|
|
|
Balance: data.Balance.String(), |
|
|
|
Balance: data.Balance.String(), |
|
|
|
Nonce: data.Nonce, |
|
|
|
Nonce: data.Nonce, |
|
|
|
Root: common.Bytes2Hex(data.Root[:]), |
|
|
|
Root: data.Root[:], |
|
|
|
CodeHash: common.Bytes2Hex(data.CodeHash), |
|
|
|
CodeHash: data.CodeHash, |
|
|
|
|
|
|
|
SecureKey: it.Key, |
|
|
|
} |
|
|
|
} |
|
|
|
addrBytes := s.trie.GetKey(it.Key) |
|
|
|
addrBytes := s.trie.GetKey(it.Key) |
|
|
|
if addrBytes == nil { |
|
|
|
if addrBytes == nil { |
|
|
|
// Preimage missing
|
|
|
|
// Preimage missing
|
|
|
|
missingPreimages++ |
|
|
|
missingPreimages++ |
|
|
|
if excludeMissingPreimages { |
|
|
|
if conf.OnlyWithAddresses { |
|
|
|
continue |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
account.SecureKey = it.Key |
|
|
|
account.SecureKey = it.Key |
|
|
|
} |
|
|
|
} |
|
|
|
addr := common.BytesToAddress(addrBytes) |
|
|
|
addr := common.BytesToAddress(addrBytes) |
|
|
|
obj := newObject(s, addr, data) |
|
|
|
obj := newObject(s, addr, data) |
|
|
|
if !excludeCode { |
|
|
|
if !conf.SkipCode { |
|
|
|
account.Code = common.Bytes2Hex(obj.Code(s.db)) |
|
|
|
account.Code = obj.Code(s.db) |
|
|
|
} |
|
|
|
} |
|
|
|
if !excludeStorage { |
|
|
|
if !conf.SkipStorage { |
|
|
|
account.Storage = make(map[common.Hash]string) |
|
|
|
account.Storage = make(map[common.Hash]string) |
|
|
|
storageIt := trie.NewIterator(obj.getTrie(s.db).NodeIterator(nil)) |
|
|
|
storageIt := trie.NewIterator(obj.getTrie(s.db).NodeIterator(nil)) |
|
|
|
for storageIt.Next() { |
|
|
|
for storageIt.Next() { |
|
|
@ -155,8 +178,13 @@ func (s *StateDB) DumpToCollector(c DumpCollector, excludeCode, excludeStorage, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
c.OnAccount(addr, account) |
|
|
|
c.OnAccount(addr, account) |
|
|
|
count++ |
|
|
|
accounts++ |
|
|
|
if maxResults > 0 && count >= maxResults { |
|
|
|
if time.Since(logged) > 8*time.Second { |
|
|
|
|
|
|
|
log.Info("Trie dumping in progress", "at", it.Key, "accounts", accounts, |
|
|
|
|
|
|
|
"elapsed", common.PrettyDuration(time.Since(start))) |
|
|
|
|
|
|
|
logged = time.Now() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if conf.Max > 0 && accounts >= conf.Max { |
|
|
|
if it.Next() { |
|
|
|
if it.Next() { |
|
|
|
nextKey = it.Key |
|
|
|
nextKey = it.Key |
|
|
|
} |
|
|
|
} |
|
|
@ -166,22 +194,24 @@ func (s *StateDB) DumpToCollector(c DumpCollector, excludeCode, excludeStorage, |
|
|
|
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) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
log.Info("Trie dumping complete", "accounts", accounts, |
|
|
|
|
|
|
|
"elapsed", common.PrettyDuration(time.Since(start))) |
|
|
|
|
|
|
|
|
|
|
|
return nextKey |
|
|
|
return nextKey |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// RawDump returns the entire state an a single large object
|
|
|
|
// RawDump returns the entire state an a single large object
|
|
|
|
func (s *StateDB) RawDump(excludeCode, excludeStorage, excludeMissingPreimages bool) Dump { |
|
|
|
func (s *StateDB) RawDump(opts *DumpConfig) Dump { |
|
|
|
dump := &Dump{ |
|
|
|
dump := &Dump{ |
|
|
|
Accounts: make(map[common.Address]DumpAccount), |
|
|
|
Accounts: make(map[common.Address]DumpAccount), |
|
|
|
} |
|
|
|
} |
|
|
|
s.DumpToCollector(dump, excludeCode, excludeStorage, excludeMissingPreimages, nil, 0) |
|
|
|
s.DumpToCollector(dump, opts) |
|
|
|
return *dump |
|
|
|
return *dump |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Dump returns a JSON string representing the entire state as a single json-object
|
|
|
|
// Dump returns a JSON string representing the entire state as a single json-object
|
|
|
|
func (s *StateDB) Dump(excludeCode, excludeStorage, excludeMissingPreimages bool) []byte { |
|
|
|
func (s *StateDB) Dump(opts *DumpConfig) []byte { |
|
|
|
dump := s.RawDump(excludeCode, excludeStorage, excludeMissingPreimages) |
|
|
|
dump := s.RawDump(opts) |
|
|
|
json, err := json.MarshalIndent(dump, "", " ") |
|
|
|
json, err := json.MarshalIndent(dump, "", " ") |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
fmt.Println("Dump err", err) |
|
|
|
fmt.Println("Dump err", err) |
|
|
@ -190,15 +220,15 @@ 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(opts *DumpConfig, output *json.Encoder) { |
|
|
|
s.DumpToCollector(iterativeDump{output}, excludeCode, excludeStorage, excludeMissingPreimages, nil, 0) |
|
|
|
s.DumpToCollector(iterativeDump{output}, opts) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// IteratorDump dumps out a batch of accounts starts with the given start key
|
|
|
|
// 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 { |
|
|
|
func (s *StateDB) IteratorDump(opts *DumpConfig) IteratorDump { |
|
|
|
iterator := &IteratorDump{ |
|
|
|
iterator := &IteratorDump{ |
|
|
|
Accounts: make(map[common.Address]DumpAccount), |
|
|
|
Accounts: make(map[common.Address]DumpAccount), |
|
|
|
} |
|
|
|
} |
|
|
|
iterator.Next = s.DumpToCollector(iterator, excludeCode, excludeStorage, excludeMissingPreimages, start, maxResults) |
|
|
|
iterator.Next = s.DumpToCollector(iterator, opts) |
|
|
|
return *iterator |
|
|
|
return *iterator |
|
|
|
} |
|
|
|
} |
|
|
|