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