github.com/theQRL/go-zond@v0.1.1/core/state/dump.go (about) 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 17 package state 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "time" 23 24 "github.com/theQRL/go-zond/common" 25 "github.com/theQRL/go-zond/common/hexutil" 26 "github.com/theQRL/go-zond/core/types" 27 "github.com/theQRL/go-zond/log" 28 "github.com/theQRL/go-zond/rlp" 29 "github.com/theQRL/go-zond/trie" 30 ) 31 32 // DumpConfig is a set of options to control what portions of the state will be 33 // iterated and collected. 34 type DumpConfig struct { 35 SkipCode bool 36 SkipStorage bool 37 OnlyWithAddresses bool 38 Start []byte 39 Max uint64 40 } 41 42 // DumpCollector interface which the state trie calls during iteration 43 type DumpCollector interface { 44 // OnRoot is called with the state root 45 OnRoot(common.Hash) 46 // OnAccount is called once for each account in the trie 47 OnAccount(*common.Address, DumpAccount) 48 } 49 50 // DumpAccount represents an account in the state. 51 type DumpAccount struct { 52 Balance string `json:"balance"` 53 Nonce uint64 `json:"nonce"` 54 Root hexutil.Bytes `json:"root"` 55 CodeHash hexutil.Bytes `json:"codeHash"` 56 Code hexutil.Bytes `json:"code,omitempty"` 57 Storage map[common.Hash]string `json:"storage,omitempty"` 58 Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode 59 SecureKey hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key 60 61 } 62 63 // Dump represents the full dump in a collected format, as one large map. 64 type Dump struct { 65 Root string `json:"root"` 66 Accounts map[common.Address]DumpAccount `json:"accounts"` 67 } 68 69 // OnRoot implements DumpCollector interface 70 func (d *Dump) OnRoot(root common.Hash) { 71 d.Root = fmt.Sprintf("%x", root) 72 } 73 74 // OnAccount implements DumpCollector interface 75 func (d *Dump) OnAccount(addr *common.Address, account DumpAccount) { 76 if addr != nil { 77 d.Accounts[*addr] = account 78 } 79 } 80 81 // IteratorDump is an implementation for iterating over data. 82 type IteratorDump struct { 83 Root string `json:"root"` 84 Accounts map[common.Address]DumpAccount `json:"accounts"` 85 Next []byte `json:"next,omitempty"` // nil if no more accounts 86 } 87 88 // OnRoot implements DumpCollector interface 89 func (d *IteratorDump) OnRoot(root common.Hash) { 90 d.Root = fmt.Sprintf("%x", root) 91 } 92 93 // OnAccount implements DumpCollector interface 94 func (d *IteratorDump) OnAccount(addr *common.Address, account DumpAccount) { 95 if addr != nil { 96 d.Accounts[*addr] = account 97 } 98 } 99 100 // iterativeDump is a DumpCollector-implementation which dumps output line-by-line iteratively. 101 type iterativeDump struct { 102 *json.Encoder 103 } 104 105 // OnAccount implements DumpCollector interface 106 func (d iterativeDump) OnAccount(addr *common.Address, account DumpAccount) { 107 dumpAccount := &DumpAccount{ 108 Balance: account.Balance, 109 Nonce: account.Nonce, 110 Root: account.Root, 111 CodeHash: account.CodeHash, 112 Code: account.Code, 113 Storage: account.Storage, 114 SecureKey: account.SecureKey, 115 Address: addr, 116 } 117 d.Encode(dumpAccount) 118 } 119 120 // OnRoot implements DumpCollector interface 121 func (d iterativeDump) OnRoot(root common.Hash) { 122 d.Encode(struct { 123 Root common.Hash `json:"root"` 124 }{root}) 125 } 126 127 // DumpToCollector iterates the state according to the given options and inserts 128 // the items into a collector for aggregation or serialization. 129 func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []byte) { 130 // Sanitize the input to allow nil configs 131 if conf == nil { 132 conf = new(DumpConfig) 133 } 134 var ( 135 missingPreimages int 136 accounts uint64 137 start = time.Now() 138 logged = time.Now() 139 ) 140 log.Info("Trie dumping started", "root", s.trie.Hash()) 141 c.OnRoot(s.trie.Hash()) 142 143 trieIt, err := s.trie.NodeIterator(conf.Start) 144 if err != nil { 145 return nil 146 } 147 it := trie.NewIterator(trieIt) 148 for it.Next() { 149 var data types.StateAccount 150 if err := rlp.DecodeBytes(it.Value, &data); err != nil { 151 panic(err) 152 } 153 account := DumpAccount{ 154 Balance: data.Balance.String(), 155 Nonce: data.Nonce, 156 Root: data.Root[:], 157 CodeHash: data.CodeHash, 158 SecureKey: it.Key, 159 } 160 var ( 161 addrBytes = s.trie.GetKey(it.Key) 162 addr = common.BytesToAddress(addrBytes) 163 address *common.Address 164 ) 165 if addrBytes == nil { 166 // Preimage missing 167 missingPreimages++ 168 if conf.OnlyWithAddresses { 169 continue 170 } 171 } else { 172 address = &addr 173 } 174 obj := newObject(s, addr, &data) 175 if !conf.SkipCode { 176 account.Code = obj.Code() 177 } 178 if !conf.SkipStorage { 179 account.Storage = make(map[common.Hash]string) 180 tr, err := obj.getTrie() 181 if err != nil { 182 log.Error("Failed to load storage trie", "err", err) 183 continue 184 } 185 trieIt, err := tr.NodeIterator(nil) 186 if err != nil { 187 log.Error("Failed to create trie iterator", "err", err) 188 continue 189 } 190 storageIt := trie.NewIterator(trieIt) 191 for storageIt.Next() { 192 _, content, _, err := rlp.Split(storageIt.Value) 193 if err != nil { 194 log.Error("Failed to decode the value returned by iterator", "error", err) 195 continue 196 } 197 account.Storage[common.BytesToHash(s.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(content) 198 } 199 } 200 c.OnAccount(address, account) 201 accounts++ 202 if time.Since(logged) > 8*time.Second { 203 log.Info("Trie dumping in progress", "at", it.Key, "accounts", accounts, 204 "elapsed", common.PrettyDuration(time.Since(start))) 205 logged = time.Now() 206 } 207 if conf.Max > 0 && accounts >= conf.Max { 208 if it.Next() { 209 nextKey = it.Key 210 } 211 break 212 } 213 } 214 if missingPreimages > 0 { 215 log.Warn("Dump incomplete due to missing preimages", "missing", missingPreimages) 216 } 217 log.Info("Trie dumping complete", "accounts", accounts, 218 "elapsed", common.PrettyDuration(time.Since(start))) 219 220 return nextKey 221 } 222 223 // RawDump returns the entire state an a single large object 224 func (s *StateDB) RawDump(opts *DumpConfig) Dump { 225 dump := &Dump{ 226 Accounts: make(map[common.Address]DumpAccount), 227 } 228 s.DumpToCollector(dump, opts) 229 return *dump 230 } 231 232 // Dump returns a JSON string representing the entire state as a single json-object 233 func (s *StateDB) Dump(opts *DumpConfig) []byte { 234 dump := s.RawDump(opts) 235 json, err := json.MarshalIndent(dump, "", " ") 236 if err != nil { 237 fmt.Println("Dump err", err) 238 } 239 return json 240 } 241 242 // IterativeDump dumps out accounts as json-objects, delimited by linebreaks on stdout 243 func (s *StateDB) IterativeDump(opts *DumpConfig, output *json.Encoder) { 244 s.DumpToCollector(iterativeDump{output}, opts) 245 } 246 247 // IteratorDump dumps out a batch of accounts starts with the given start key 248 func (s *StateDB) IteratorDump(opts *DumpConfig) IteratorDump { 249 iterator := &IteratorDump{ 250 Accounts: make(map[common.Address]DumpAccount), 251 } 252 iterator.Next = s.DumpToCollector(iterator, opts) 253 return *iterator 254 }