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