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