dump.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. // Copyright 2014 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package state
  17. import (
  18. "encoding/json"
  19. "fmt"
  20. "time"
  21. "github.com/ethereum/go-ethereum/common"
  22. "github.com/ethereum/go-ethereum/common/hexutil"
  23. "github.com/ethereum/go-ethereum/core/types"
  24. "github.com/ethereum/go-ethereum/log"
  25. "github.com/ethereum/go-ethereum/rlp"
  26. "github.com/ethereum/go-ethereum/trie"
  27. )
  28. // DumpConfig is a set of options to control what portions of the statewill be
  29. // iterated and collected.
  30. type DumpConfig struct {
  31. SkipCode bool
  32. SkipStorage bool
  33. OnlyWithAddresses bool
  34. Start []byte
  35. Max uint64
  36. }
  37. // DumpCollector interface which the state trie calls during iteration
  38. type DumpCollector interface {
  39. // OnRoot is called with the state root
  40. OnRoot(common.Hash)
  41. // OnAccount is called once for each account in the trie
  42. OnAccount(common.Address, DumpAccount)
  43. }
  44. // DumpAccount represents an account in the state.
  45. type DumpAccount struct {
  46. Balance string `json:"balance"`
  47. Nonce uint64 `json:"nonce"`
  48. Root hexutil.Bytes `json:"root"`
  49. CodeHash hexutil.Bytes `json:"codeHash"`
  50. Code hexutil.Bytes `json:"code,omitempty"`
  51. Storage map[common.Hash]string `json:"storage,omitempty"`
  52. Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode
  53. SecureKey hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key
  54. }
  55. // Dump represents the full dump in a collected format, as one large map.
  56. type Dump struct {
  57. Root string `json:"root"`
  58. Accounts map[common.Address]DumpAccount `json:"accounts"`
  59. }
  60. // OnRoot implements DumpCollector interface
  61. func (d *Dump) OnRoot(root common.Hash) {
  62. d.Root = fmt.Sprintf("%x", root)
  63. }
  64. // OnAccount implements DumpCollector interface
  65. func (d *Dump) OnAccount(addr common.Address, account DumpAccount) {
  66. d.Accounts[addr] = account
  67. }
  68. // IteratorDump is an implementation for iterating over data.
  69. type IteratorDump struct {
  70. Root string `json:"root"`
  71. Accounts map[common.Address]DumpAccount `json:"accounts"`
  72. Next []byte `json:"next,omitempty"` // nil if no more accounts
  73. }
  74. // OnRoot implements DumpCollector interface
  75. func (d *IteratorDump) OnRoot(root common.Hash) {
  76. d.Root = fmt.Sprintf("%x", root)
  77. }
  78. // OnAccount implements DumpCollector interface
  79. func (d *IteratorDump) OnAccount(addr common.Address, account DumpAccount) {
  80. d.Accounts[addr] = account
  81. }
  82. // iterativeDump is a DumpCollector-implementation which dumps output line-by-line iteratively.
  83. type iterativeDump struct {
  84. *json.Encoder
  85. }
  86. // OnAccount implements DumpCollector interface
  87. func (d iterativeDump) OnAccount(addr common.Address, account DumpAccount) {
  88. dumpAccount := &DumpAccount{
  89. Balance: account.Balance,
  90. Nonce: account.Nonce,
  91. Root: account.Root,
  92. CodeHash: account.CodeHash,
  93. Code: account.Code,
  94. Storage: account.Storage,
  95. SecureKey: account.SecureKey,
  96. Address: nil,
  97. }
  98. if addr != (common.Address{}) {
  99. dumpAccount.Address = &addr
  100. }
  101. d.Encode(dumpAccount)
  102. }
  103. // OnRoot implements DumpCollector interface
  104. func (d iterativeDump) OnRoot(root common.Hash) {
  105. d.Encode(struct {
  106. Root common.Hash `json:"root"`
  107. }{root})
  108. }
  109. // DumpToCollector iterates the state according to the given options and inserts
  110. // the items into a collector for aggregation or serialization.
  111. func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []byte) {
  112. // Sanitize the input to allow nil configs
  113. if conf == nil {
  114. conf = new(DumpConfig)
  115. }
  116. var (
  117. missingPreimages int
  118. accounts uint64
  119. start = time.Now()
  120. logged = time.Now()
  121. )
  122. log.Info("Trie dumping started", "root", s.trie.Hash())
  123. c.OnRoot(s.trie.Hash())
  124. it := trie.NewIterator(s.trie.NodeIterator(conf.Start))
  125. for it.Next() {
  126. var data types.StateAccount
  127. if err := rlp.DecodeBytes(it.Value, &data); err != nil {
  128. panic(err)
  129. }
  130. account := DumpAccount{
  131. Balance: data.Balance.String(),
  132. Nonce: data.Nonce,
  133. Root: data.Root[:],
  134. CodeHash: data.CodeHash,
  135. SecureKey: it.Key,
  136. }
  137. addrBytes := s.trie.GetKey(it.Key)
  138. if addrBytes == nil {
  139. // Preimage missing
  140. missingPreimages++
  141. if conf.OnlyWithAddresses {
  142. continue
  143. }
  144. account.SecureKey = it.Key
  145. }
  146. addr := common.BytesToAddress(addrBytes)
  147. obj := newObject(s, addr, data)
  148. if !conf.SkipCode {
  149. account.Code = obj.Code(s.db)
  150. }
  151. if !conf.SkipStorage {
  152. account.Storage = make(map[common.Hash]string)
  153. storageIt := trie.NewIterator(obj.getTrie(s.db).NodeIterator(nil))
  154. for storageIt.Next() {
  155. _, content, _, err := rlp.Split(storageIt.Value)
  156. if err != nil {
  157. log.Error("Failed to decode the value returned by iterator", "error", err)
  158. continue
  159. }
  160. account.Storage[common.BytesToHash(s.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(content)
  161. }
  162. }
  163. c.OnAccount(addr, account)
  164. accounts++
  165. if time.Since(logged) > 8*time.Second {
  166. log.Info("Trie dumping in progress", "at", it.Key, "accounts", accounts,
  167. "elapsed", common.PrettyDuration(time.Since(start)))
  168. logged = time.Now()
  169. }
  170. if conf.Max > 0 && accounts >= conf.Max {
  171. if it.Next() {
  172. nextKey = it.Key
  173. }
  174. break
  175. }
  176. }
  177. if missingPreimages > 0 {
  178. log.Warn("Dump incomplete due to missing preimages", "missing", missingPreimages)
  179. }
  180. log.Info("Trie dumping complete", "accounts", accounts,
  181. "elapsed", common.PrettyDuration(time.Since(start)))
  182. return nextKey
  183. }
  184. // RawDump returns the entire state an a single large object
  185. func (s *StateDB) RawDump(opts *DumpConfig) Dump {
  186. dump := &Dump{
  187. Accounts: make(map[common.Address]DumpAccount),
  188. }
  189. s.DumpToCollector(dump, opts)
  190. return *dump
  191. }
  192. // Dump returns a JSON string representing the entire state as a single json-object
  193. func (s *StateDB) Dump(opts *DumpConfig) []byte {
  194. dump := s.RawDump(opts)
  195. json, err := json.MarshalIndent(dump, "", " ")
  196. if err != nil {
  197. fmt.Println("Dump err", err)
  198. }
  199. return json
  200. }
  201. // IterativeDump dumps out accounts as json-objects, delimited by linebreaks on stdout
  202. func (s *StateDB) IterativeDump(opts *DumpConfig, output *json.Encoder) {
  203. s.DumpToCollector(iterativeDump{output}, opts)
  204. }
  205. // IteratorDump dumps out a batch of accounts starts with the given start key
  206. func (s *StateDB) IteratorDump(opts *DumpConfig) IteratorDump {
  207. iterator := &IteratorDump{
  208. Accounts: make(map[common.Address]DumpAccount),
  209. }
  210. iterator.Next = s.DumpToCollector(iterator, opts)
  211. return *iterator
  212. }