github.com/klaytn/klaytn@v1.12.1/blockchain/state/iterator.go (about) 1 // Copyright 2015 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 // This file is derived from ethdb/iterator.go (2020/05/20). 18 // Modified and improved for the klaytn development. 19 20 package state 21 22 import ( 23 "bytes" 24 "fmt" 25 "time" 26 27 "github.com/klaytn/klaytn/blockchain/types/account" 28 "github.com/klaytn/klaytn/common" 29 "github.com/klaytn/klaytn/log" 30 "github.com/klaytn/klaytn/rlp" 31 "github.com/klaytn/klaytn/storage/statedb" 32 "github.com/pkg/errors" 33 ) 34 35 var ( 36 errIterator = errors.New("iterator error") 37 errStopByQuit = errors.New("stopByQuit") 38 ) 39 40 // NodeIterator is an iterator to traverse the entire state trie post-order, 41 // including all of the contract code and contract state tries. 42 type NodeIterator struct { 43 state *StateDB // State being iterated 44 45 stateIt statedb.NodeIterator // Primary iterator for the global state trie 46 dataIt statedb.NodeIterator // Secondary iterator for the data trie of a contract 47 48 accountHash common.Hash // Hash of the node containing the account 49 codeHash common.Hash // Hash of the contract source code 50 Code []byte // Source code associated with a contract 51 52 Type string 53 Hash common.Hash // Hash of the current entry being iterated (nil if not standalone) 54 Parent common.Hash // Hash of the first full ancestor node (nil if current is the root) 55 Path []byte // the hex-encoded path to the current node. 56 57 Error error // Failure set in case of an internal error in the iteratord 58 } 59 60 // NewNodeIterator creates an post-order state node iterator. 61 func NewNodeIterator(state *StateDB) *NodeIterator { 62 return &NodeIterator{ 63 state: state, 64 } 65 } 66 67 // Next moves the iterator to the next node, returning whether there are any 68 // further nodes. In case of an internal error this method returns false and 69 // sets the Error field to the encountered failure. 70 func (it *NodeIterator) Next() bool { 71 // If the iterator failed previously, don't do anything 72 if it.Error != nil { 73 return false 74 } 75 // Otherwise step forward with the iterator and report any errors 76 if err := it.step(); err != nil { 77 it.Error = err 78 return false 79 } 80 return it.retrieve() 81 } 82 83 // step moves the iterator to the next entry of the state trie. 84 func (it *NodeIterator) step() error { 85 // Abort if we reached the end of the iteration 86 if it.state == nil { 87 return nil 88 } 89 // Initialize the iterator if we've just started 90 if it.stateIt == nil { 91 it.stateIt = it.state.trie.NodeIterator(nil) 92 } 93 // If we had data nodes previously, we surely have at least state nodes 94 if it.dataIt != nil { 95 if cont := it.dataIt.Next(true); !cont { 96 if it.dataIt.Error() != nil { 97 return it.dataIt.Error() 98 } 99 it.dataIt = nil 100 } 101 return nil 102 } 103 // If we had source code previously, discard that 104 if it.Code != nil { 105 it.Code = nil 106 return nil 107 } 108 // Step to the next state trie node, terminating if we're out of nodes 109 if cont := it.stateIt.Next(true); !cont { 110 if it.stateIt.Error() != nil { 111 return it.stateIt.Error() 112 } 113 it.state, it.stateIt = nil, nil 114 return nil 115 } 116 // If the state trie node is an internal entry, leave as is 117 if !it.stateIt.Leaf() { 118 return nil 119 } 120 // Otherwise we've reached an account node, initiate data iteration 121 122 serializer := account.NewAccountSerializer() 123 if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob()), serializer); err != nil { 124 return err 125 } 126 obj := serializer.GetAccount() 127 128 if pa := account.GetProgramAccount(obj); pa != nil { 129 dataTrie, err := it.state.db.OpenStorageTrie(pa.GetStorageRoot(), nil) 130 if err != nil { 131 return err 132 } 133 it.dataIt = dataTrie.NodeIterator(nil) 134 if !it.dataIt.Next(true) { 135 it.dataIt = nil 136 } 137 138 if codeHash := pa.GetCodeHash(); !bytes.Equal(codeHash, emptyCodeHash) { 139 it.codeHash = common.BytesToHash(codeHash) 140 // addrHash := common.BytesToHash(it.stateIt.LeafKey()) 141 it.Code, err = it.state.db.ContractCode(common.BytesToHash(codeHash)) 142 if err != nil { 143 return fmt.Errorf("code %x: %v", codeHash, err) 144 } 145 } 146 } 147 it.accountHash = it.stateIt.Parent() 148 return nil 149 } 150 151 // retrieve pulls and caches the current state entry the iterator is traversing. 152 // The method returns whether there are any more data left for inspection. 153 func (it *NodeIterator) retrieve() bool { 154 // Clear out any previously set values 155 it.Hash = common.Hash{} 156 it.Path = []byte{} 157 158 // If the iteration's done, return no available data 159 if it.state == nil { 160 return false 161 } 162 // Otherwise retrieve the current entry 163 switch { 164 case it.dataIt != nil: 165 it.Type = "storage" 166 if it.dataIt.Leaf() { 167 it.Type = "storage_leaf" 168 } 169 170 it.Hash, it.Parent, it.Path = it.dataIt.Hash(), it.dataIt.Parent(), it.dataIt.Path() 171 172 if it.Parent == (common.Hash{}) { 173 it.Parent = it.accountHash 174 } 175 case it.Code != nil: 176 it.Type = "code" 177 it.Hash, it.Parent = it.codeHash, it.accountHash 178 case it.stateIt != nil: 179 it.Type = "state" 180 if it.stateIt.Leaf() { 181 it.Type = "state_leaf" 182 } 183 184 it.Hash, it.Parent, it.Path = it.stateIt.Hash(), it.stateIt.Parent(), it.stateIt.Path() 185 } 186 return true 187 } 188 189 // CheckStateConsistencyParallel checks the consistency of all state/storage trie of given two state databases in parallel. 190 func CheckStateConsistencyParallel(oldDB Database, newDB Database, root common.Hash, quitCh chan struct{}) error { 191 // Check if the tries can be called 192 _, err := oldDB.OpenTrie(root, nil) 193 if err != nil { 194 return errors.WithMessage(err, "can not open oldDB trie") 195 } 196 _, err = newDB.OpenTrie(root, nil) 197 if err != nil { 198 return errors.WithMessage(err, "can not open newDB trie") 199 } 200 // get children hash 201 children, err := oldDB.TrieDB().NodeChildren(root.ExtendZero()) // does not work with Live Pruning 202 if err != nil { 203 logger.Error("cannot start CheckStateConsistencyParallel", "err", err) 204 return errors.WithMessage(err, "cannot get children before consistency check") 205 } 206 207 logger.Info("CheckStateConsistencyParallel is started", "root", root.String(), "len(children)", len(children)) 208 209 iteratorQuitCh := make(chan struct{}) 210 resultHashCh := make(chan struct{}, 10000) 211 resultErrCh := make(chan error) 212 213 // checks the consistency for each child in parallel 214 for _, child := range children { 215 go concurrentIterator(oldDB, newDB, child, iteratorQuitCh, resultHashCh, resultErrCh) 216 } 217 218 var resultErr error 219 cnt := 0 220 logged := time.Now() 221 for finishCnt := 0; finishCnt < len(children); { 222 select { 223 case <-resultHashCh: 224 cnt++ 225 if time.Since(logged) > log.StatsReportLimit { 226 logged = time.Now() 227 logger.Info("CheckStateConsistencyParallel progress", "cnt", cnt) 228 } 229 case err := <-resultErrCh: 230 if err != nil { 231 logger.Warn("CheckStateConsistencyParallel got an error", "err", err) 232 close(iteratorQuitCh) 233 return err 234 } 235 236 finishCnt++ 237 logger.Debug("CheckStateConsistencyParallel is being finished", "finishCnt", finishCnt, "err", err) 238 case <-quitCh: 239 close(iteratorQuitCh) 240 return nil 241 } 242 } 243 logger.Info("CheckStateConsistencyParallel is done", "cnt", cnt, "err", resultErr) 244 245 return resultErr 246 } 247 248 // concurrentIterator checks the consistency of all state/storage trie of given two state database 249 // and pass the result via the channel. The 'root' here can be a subtrie root. 250 // Does not work with Live Pruning. 251 func concurrentIterator(oldDB Database, newDB Database, root common.ExtHash, quit chan struct{}, resultCh chan struct{}, finishCh chan error) (resultErr error) { 252 defer func() { 253 finishCh <- resultErr 254 }() 255 256 // Create and iterate a state trie rooted in a sub-node 257 oldState, err := New(root.Unextend(), oldDB, nil, nil) 258 if err != nil { 259 return errors.WithMessage(err, "can not open oldDB trie") 260 } 261 262 newState, err := New(root.Unextend(), newDB, nil, nil) 263 if err != nil { 264 return errors.WithMessage(err, "can not open newDB trie") 265 } 266 267 oldIt := NewNodeIterator(oldState) 268 newIt := NewNodeIterator(newState) 269 270 for oldIt.Next() { 271 if !newIt.Next() { 272 return fmt.Errorf("newDB iterator finished earlier : oldIt.Hash(%v) oldIt.Parent(%v) newIt.Error(%v)", 273 oldIt.Hash.String(), oldIt.Parent.String(), newIt.Error) 274 } 275 276 if oldIt.Hash != newIt.Hash { 277 return fmt.Errorf("mismatched hash oldIt.Hash : oldIt.Hash(%v) newIt.Hash(%v)", oldIt.Hash.String(), newIt.Hash.String()) 278 } 279 280 if oldIt.Parent != newIt.Parent { 281 return fmt.Errorf("mismatched parent hash : oldIt.Parent(%v) newIt.Parent(%v)", oldIt.Parent.String(), newIt.Parent.String()) 282 } 283 284 if !bytes.Equal(oldIt.Path, newIt.Path) { 285 return fmt.Errorf("mismatched path : oldIt.path(%v) newIt.path(%v)", 286 statedb.HexPathToString(oldIt.Path), statedb.HexPathToString(newIt.Path)) 287 } 288 289 if oldIt.Code != nil { 290 if newIt.Code != nil { 291 if !bytes.Equal(oldIt.Code, newIt.Code) { 292 return fmt.Errorf("mismatched code : oldIt.Code(%v) newIt.Code(%v)", string(oldIt.Code), string(newIt.Code)) 293 } 294 } else { 295 return fmt.Errorf("mismatched code : oldIt.Code(%v) newIt.Code(nil)", string(oldIt.Code)) 296 } 297 } else { 298 if newIt.Code != nil { 299 return fmt.Errorf("mismatched code : oldIt.Code(nil) newIt.Code(%v)", string(newIt.Code)) 300 } 301 } 302 303 resultCh <- struct{}{} 304 305 if quit != nil { 306 select { 307 case <-quit: 308 logger.Warn("CheckStateConsistency is stop") 309 return errStopByQuit 310 default: 311 } 312 } 313 } 314 315 if newIt.Next() { 316 return fmt.Errorf("oldDB iterator finished earlier : newIt.Hash(%v) newIt.Parent(%v) oldIt.Error(%v)", 317 newIt.Hash, newIt.Parent, oldIt.Error) 318 } 319 320 if oldIt.Error != nil || newIt.Error != nil { 321 return fmt.Errorf("%w : oldIt.Error(%v), newIt.Error(%v)", errIterator, oldIt.Error, newIt.Error) 322 } 323 324 return nil 325 } 326 327 // CheckStateConsistency checks the consistency of all state/storage trie of given two state database. 328 func CheckStateConsistency(oldDB Database, newDB Database, root common.Hash, mapSize int, quit chan struct{}) error { 329 // Create and iterate a state trie rooted in a sub-node 330 oldState, err := New(root, oldDB, nil, nil) 331 if err != nil { 332 return err 333 } 334 335 newState, err := New(root, newDB, nil, nil) 336 if err != nil { 337 return err 338 } 339 340 oldIt := NewNodeIterator(oldState) 341 newIt := NewNodeIterator(newState) 342 343 cnt := 0 344 nodes := make(map[common.Hash]struct{}, mapSize) 345 346 lastTime := time.Now() 347 report := func() { 348 elapsed := time.Since(lastTime) 349 if elapsed > log.StatsReportLimit { 350 logger.Info("CheckStateConsistency next", 351 "idx", cnt, 352 "type", oldIt.Type, 353 "hash", oldIt.Hash.String(), 354 "parent", oldIt.Parent.String(), 355 "path", statedb.HexPathToString(oldIt.Path)) 356 lastTime = time.Now() 357 } 358 } 359 360 for oldIt.Next() { 361 cnt++ 362 report() 363 364 if !newIt.Next() { 365 return fmt.Errorf("newDB iterator finished earlier : oldIt.Hash(%v) oldIt.Parent(%v) newIt.Error(%v)", 366 oldIt.Hash.String(), oldIt.Parent.String(), newIt.Error) 367 } 368 369 if oldIt.Hash != newIt.Hash { 370 return fmt.Errorf("mismatched hash oldIt.Hash : oldIt.Hash(%v) newIt.Hash(%v)", oldIt.Hash.String(), newIt.Hash.String()) 371 } 372 373 if oldIt.Parent != newIt.Parent { 374 return fmt.Errorf("mismatched parent hash : oldIt.Parent(%v) newIt.Parent(%v)", oldIt.Parent.String(), newIt.Parent.String()) 375 } 376 377 if !bytes.Equal(oldIt.Path, newIt.Path) { 378 return fmt.Errorf("mismatched path : oldIt.path(%v) newIt.path(%v)", 379 statedb.HexPathToString(oldIt.Path), statedb.HexPathToString(newIt.Path)) 380 } 381 382 if oldIt.Code != nil { 383 if newIt.Code != nil { 384 if !bytes.Equal(oldIt.Code, newIt.Code) { 385 return fmt.Errorf("mismatched code : oldIt.Code(%v) newIt.Code(%v)", string(oldIt.Code), string(newIt.Code)) 386 } 387 } else { 388 return fmt.Errorf("mismatched code : oldIt.Code(%v) newIt.Code(nil)", string(oldIt.Code)) 389 } 390 } else { 391 if newIt.Code != nil { 392 return fmt.Errorf("mismatched code : oldIt.Code(nil) newIt.Code(%v)", string(newIt.Code)) 393 } 394 } 395 396 if !common.EmptyHash(oldIt.Hash) { 397 nodes[oldIt.Hash] = struct{}{} 398 } 399 400 if quit != nil { 401 select { 402 case <-quit: 403 logger.Warn("CheckStateConsistency is stop", "cnt", cnt, "cnt without duplication", len(nodes)) 404 return errStopByQuit 405 default: 406 } 407 } 408 } 409 410 if newIt.Next() { 411 return fmt.Errorf("oldDB iterator finished earlier : newIt.Hash(%v) newIt.Parent(%v) oldIt.Error(%v)", 412 newIt.Hash, newIt.Parent, oldIt.Error) 413 } 414 415 if oldIt.Error != nil || newIt.Error != nil { 416 return fmt.Errorf("%w : oldIt.Error(%v), newIt.Error(%v)", errIterator, oldIt.Error, newIt.Error) 417 } 418 419 logger.Info("CheckStateConsistency is completed", "cnt", cnt, "cnt without duplication", len(nodes)) 420 return nil 421 }