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