github.com/klaytn/klaytn@v1.10.2/blockchain/state_migration.go (about) 1 // Copyright 2020 The klaytn Authors 2 // This file is part of the klaytn library. 3 // 4 // The klaytn 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 klaytn 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 klaytn library. If not, see <http://www.gnu.org/licenses/>. 16 17 package blockchain 18 19 import ( 20 "errors" 21 "fmt" 22 "runtime" 23 "strconv" 24 "strings" 25 "time" 26 27 "github.com/VictoriaMetrics/fastcache" 28 "github.com/klaytn/klaytn/blockchain/types" 29 30 "github.com/alecthomas/units" 31 lru "github.com/hashicorp/golang-lru" 32 "github.com/klaytn/klaytn/blockchain/state" 33 "github.com/klaytn/klaytn/common" 34 "github.com/klaytn/klaytn/common/mclock" 35 "github.com/klaytn/klaytn/log" 36 "github.com/klaytn/klaytn/storage/database" 37 "github.com/klaytn/klaytn/storage/statedb" 38 ) 39 40 var ( 41 stopWarmUpErr = errors.New("warm-up terminate by StopWarmUp") 42 blockChainStopWarmUpErr = errors.New("warm-up terminate as blockchain stopped") 43 ) 44 45 type stateTrieMigrationDB struct { 46 database.DBManager 47 } 48 49 func (td *stateTrieMigrationDB) ReadCachedTrieNode(hash common.Hash) ([]byte, error) { 50 return td.ReadCachedTrieNodeFromNew(hash) 51 } 52 53 func (td *stateTrieMigrationDB) ReadCachedTrieNodePreimage(secureKey []byte) ([]byte, error) { 54 return td.ReadCachedTrieNodePreimageFromNew(secureKey) 55 } 56 57 func (td *stateTrieMigrationDB) ReadStateTrieNode(key []byte) ([]byte, error) { 58 return td.ReadStateTrieNodeFromNew(key) 59 } 60 61 func (td *stateTrieMigrationDB) HasStateTrieNode(key []byte) (bool, error) { 62 return td.HasStateTrieNodeFromNew(key) 63 } 64 65 func (td *stateTrieMigrationDB) HasCodeWithPrefix(hash common.Hash) bool { 66 return td.HasCodeWithPrefixFromNew(hash) 67 } 68 69 func (td *stateTrieMigrationDB) ReadPreimage(hash common.Hash) []byte { 70 return td.ReadPreimageFromNew(hash) 71 } 72 73 func (bc *BlockChain) stateMigrationCommit(s *statedb.TrieSync, batch database.Batch) (int, error) { 74 written, err := s.Commit(batch) 75 if written == 0 || err != nil { 76 return written, err 77 } 78 79 if batch.ValueSize() > database.IdealBatchSize { 80 if err := batch.Write(); err != nil { 81 return 0, fmt.Errorf("DB write error: %v", err) 82 } 83 batch.Reset() 84 } 85 86 return written, nil 87 } 88 89 func (bc *BlockChain) concurrentRead(db state.Database, quitCh chan struct{}, hashCh chan common.Hash, resultCh chan statedb.SyncResult) { 90 for { 91 select { 92 case <-quitCh: 93 return 94 case hash := <-hashCh: 95 data, err := db.TrieDB().NodeFromOld(hash) 96 if err != nil { 97 data, err = db.ContractCode(hash) 98 } 99 if err != nil { 100 resultCh <- statedb.SyncResult{Hash: hash, Err: err} 101 continue 102 } 103 resultCh <- statedb.SyncResult{Hash: hash, Data: data} 104 } 105 } 106 } 107 108 // migrateState is the core implementation of state trie migration. 109 // This migrates a trie from StateTrieDB to StateTrieMigrationDB. 110 // Reading StateTrieDB happens in parallel and writing StateTrieMigrationDB happens in batch write. 111 // 112 // Before this function is called, StateTrieMigrationDB should be set. 113 // After the migration finish, the original StateTrieDB is removed and StateTrieMigrationDB becomes a new StateTrieDB. 114 func (bc *BlockChain) migrateState(rootHash common.Hash) (returnErr error) { 115 bc.migrationErr = nil 116 defer func() { 117 bc.migrationErr = returnErr 118 // If migration stops by quit signal, it doesn't finish migration and it it will restart again. 119 if returnErr != ErrQuitBySignal { 120 // lock to prevent from a conflict of state DB close and state DB write 121 bc.mu.Lock() 122 bc.db.FinishStateMigration(returnErr == nil) 123 bc.mu.Unlock() 124 } 125 }() 126 127 start := time.Now() 128 129 srcState := bc.StateCache() 130 dstState := state.NewDatabase(&stateTrieMigrationDB{bc.db}) 131 132 // NOTE: lruCache is mandatory when state migration and block processing are executed simultaneously 133 lruCache, _ := lru.New(int(2 * units.Giga / common.HashLength)) // 2GB for 62,500,000 common.Hash key values 134 trieSync := state.NewStateSync(rootHash, dstState.TrieDB().DiskDB(), nil, lruCache, nil) 135 var queue []common.Hash 136 137 quitCh := make(chan struct{}) 138 defer close(quitCh) 139 140 // Prepare concurrent read goroutines 141 threads := runtime.NumCPU() 142 hashCh := make(chan common.Hash, threads) 143 resultCh := make(chan statedb.SyncResult, threads) 144 145 for th := 0; th < threads; th++ { 146 go bc.concurrentRead(srcState, quitCh, hashCh, resultCh) 147 } 148 149 stateTrieBatch := dstState.TrieDB().DiskDB().NewBatch(database.StateTrieDB) 150 stats := migrationStats{initialStartTime: start, startTime: mclock.Now()} 151 152 if bc.testMigrationHook != nil { 153 bc.testMigrationHook() 154 } 155 156 // Migration main loop 157 for trieSync.Pending() > 0 { 158 nodes, _, codes := trieSync.Missing(1024) 159 queue = append(queue[:0], append(nodes, codes...)...) 160 results := make([]statedb.SyncResult, len(queue)) 161 162 // Read the trie nodes 163 startRead := time.Now() 164 go func() { 165 for _, hash := range queue { 166 hashCh <- hash 167 } 168 }() 169 170 for i := 0; i < len(queue); i++ { 171 result := <-resultCh 172 if result.Err != nil { 173 logger.Error("State migration is failed by resultCh", 174 "result.hash", result.Hash.String(), "result.Err", result.Err) 175 return fmt.Errorf("failed to retrieve node data for %x: %v", result.Hash, result.Err) 176 } 177 results[i] = result 178 } 179 stats.read += len(queue) 180 stats.readElapsed += time.Since(startRead) 181 182 // Process trie nodes 183 startProcess := time.Now() 184 for index, result := range results { 185 if err := trieSync.Process(result); err != nil { 186 logger.Error("State migration is failed by process error", "err", err) 187 return fmt.Errorf("failed to process result #%d: %v", index, err) 188 } 189 } 190 stats.processElapsed += time.Since(startProcess) 191 192 // Commit trie nodes 193 startWrite := time.Now() 194 written, err := bc.stateMigrationCommit(trieSync, stateTrieBatch) 195 if err != nil { 196 logger.Error("State migration is failed by commit error", "err", err) 197 return fmt.Errorf("failed to commit data #%d: %v", written, err) 198 } 199 stats.committed += written 200 stats.writeElapsed += time.Since(startWrite) 201 202 // Report progress 203 stats.stateMigrationReport(false, trieSync.Pending(), trieSync.CalcProgressPercentage()) 204 205 select { 206 case <-bc.stopStateMigration: 207 logger.Info("State migration terminated by request") 208 return errors.New("stop state migration") 209 case <-bc.quit: 210 logger.Info("State migration stopped by quit signal; should continue on node restart") 211 return ErrQuitBySignal 212 default: 213 } 214 215 bc.readCnt, bc.committedCnt, bc.pendingCnt, bc.progress = stats.totalRead, stats.totalCommitted, trieSync.Pending(), stats.progress 216 } 217 218 // Flush trie nodes which is not written yet. 219 if err := stateTrieBatch.Write(); err != nil { 220 logger.Error("State migration is failed by commit error", "err", err) 221 return fmt.Errorf("DB write error: %v", err) 222 } 223 224 stats.stateMigrationReport(true, trieSync.Pending(), trieSync.CalcProgressPercentage()) 225 bc.readCnt, bc.committedCnt, bc.pendingCnt, bc.progress = stats.totalRead, stats.totalCommitted, trieSync.Pending(), stats.progress 226 227 // Clear memory of trieSync 228 trieSync = nil 229 230 elapsed := time.Since(start) 231 speed := float64(stats.totalCommitted) / elapsed.Seconds() 232 logger.Info("State migration : Copy is done", 233 "totalRead", stats.totalRead, "totalCommitted", stats.totalCommitted, 234 "totalElapsed", elapsed, "committed per second", speed) 235 236 startCheck := time.Now() 237 if err := state.CheckStateConsistencyParallel(srcState, dstState, rootHash, bc.quit); err != nil { 238 logger.Error("State migration : copied stateDB is invalid", "err", err) 239 return err 240 } 241 checkElapsed := time.Since(startCheck) 242 logger.Info("State migration is completed", "copyElapsed", elapsed, "checkElapsed", checkElapsed) 243 return nil 244 } 245 246 // migrationStats tracks and reports on state migration. 247 type migrationStats struct { 248 read, committed, totalRead, totalCommitted, pending int 249 progress float64 250 initialStartTime time.Time 251 startTime mclock.AbsTime 252 readElapsed time.Duration 253 processElapsed time.Duration 254 writeElapsed time.Duration 255 } 256 257 func (st *migrationStats) stateMigrationReport(force bool, pending int, progress float64) { 258 var ( 259 now = mclock.Now() 260 elapsed = time.Duration(now) - time.Duration(st.startTime) 261 ) 262 263 if force || elapsed >= log.StatsReportLimit { 264 st.totalRead += st.read 265 st.totalCommitted += st.committed 266 st.pending, st.progress = pending, progress 267 268 progressStr := strconv.FormatFloat(st.progress, 'f', 4, 64) 269 progressStr = strings.TrimRight(progressStr, "0") 270 progressStr = strings.TrimRight(progressStr, ".") + "%" 271 272 logger.Info("State migration progress", 273 "progress", progressStr, 274 "totalRead", st.totalRead, "totalCommitted", st.totalCommitted, "pending", st.pending, 275 "read", st.read, "readElapsed", st.readElapsed, "processElapsed", st.processElapsed, 276 "written", st.committed, "writeElapsed", st.writeElapsed, 277 "elapsed", common.PrettyDuration(elapsed), 278 "totalElapsed", time.Since(st.initialStartTime)) 279 280 st.read, st.committed = 0, 0 281 st.startTime = now 282 } 283 } 284 285 func (bc *BlockChain) checkTrieContents(oldDB, newDB *statedb.Database, root common.Hash) ([]common.Address, error) { 286 oldTrie, err := statedb.NewSecureTrie(root, oldDB) 287 if err != nil { 288 return nil, err 289 } 290 291 newTrie, err := statedb.NewSecureTrie(root, newDB) 292 if err != nil { 293 return nil, err 294 } 295 296 diff, _ := statedb.NewDifferenceIterator(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{})) 297 iter := statedb.NewIterator(diff) 298 299 var dirty []common.Address 300 301 for iter.Next() { 302 key := newTrie.GetKey(iter.Key) 303 if key == nil { 304 return nil, fmt.Errorf("no preimage found for hash %x", iter.Key) 305 } 306 307 dirty = append(dirty, common.BytesToAddress(key)) 308 } 309 310 return dirty, nil 311 } 312 313 // restartStateMigration is called when a server is restarted while migration. The migration continues. 314 func (bc *BlockChain) restartStateMigration() { 315 if bc.db.InMigration() { 316 number := bc.db.MigrationBlockNumber() 317 318 block := bc.GetBlockByNumber(number) 319 if block == nil { 320 logger.Error("failed to get migration block number", "blockNumber", number) 321 return 322 } 323 324 root := block.Root() 325 logger.Warn("State migration : Restarted", "blockNumber", number, "root", root.String()) 326 327 bc.wg.Add(1) 328 go func() { 329 bc.migrateState(root) 330 bc.wg.Done() 331 }() 332 } 333 } 334 335 // PrepareStateMigration sets prepareStateMigration to be called in checkStartStateMigration. 336 func (bc *BlockChain) PrepareStateMigration() error { 337 if bc.db.InMigration() || bc.prepareStateMigration { 338 return errors.New("migration already started") 339 } 340 341 bc.prepareStateMigration = true 342 logger.Info("State migration is prepared", "expectedMigrationStartingBlockNumber", bc.CurrentBlock().NumberU64()+1) 343 344 return nil 345 } 346 347 func (bc *BlockChain) checkStartStateMigration(number uint64, root common.Hash) bool { 348 if bc.prepareStateMigration { 349 logger.Info("State migration is started", "block", number, "root", root) 350 351 if err := bc.StartStateMigration(number, root); err != nil { 352 logger.Error("Failed to start state migration", "err", err) 353 } 354 355 bc.prepareStateMigration = false 356 357 return true 358 } 359 360 return false 361 } 362 363 // migrationPrerequisites is a collection of functions that needs to be run 364 // before state trie migration. If one of the functions fails to run, 365 // the migration will not start. 366 var migrationPrerequisites []func(uint64) error 367 368 func RegisterMigrationPrerequisites(f func(uint64) error) { 369 migrationPrerequisites = append(migrationPrerequisites, f) 370 } 371 372 // StartStateMigration checks prerequisites, configures DB and starts migration. 373 func (bc *BlockChain) StartStateMigration(number uint64, root common.Hash) error { 374 if bc.db.InMigration() { 375 return errors.New("migration already started") 376 } 377 378 for _, f := range migrationPrerequisites { 379 if err := f(number); err != nil { 380 return err 381 } 382 } 383 384 if err := bc.db.CreateMigrationDBAndSetStatus(number); err != nil { 385 return err 386 } 387 388 bc.wg.Add(1) 389 go func() { 390 bc.migrateState(root) 391 bc.wg.Done() 392 }() 393 394 return nil 395 } 396 397 func (bc *BlockChain) StopStateMigration() error { 398 if !bc.db.InMigration() { 399 return errors.New("not in migration") 400 } 401 402 bc.stopStateMigration <- struct{}{} 403 404 return nil 405 } 406 407 // StateMigrationStatus returns if it is in migration, the block number of in migration, 408 // number of committed blocks and number of pending blocks 409 func (bc *BlockChain) StateMigrationStatus() (bool, uint64, int, int, int, float64, error) { 410 return bc.db.InMigration(), bc.db.MigrationBlockNumber(), bc.readCnt, bc.committedCnt, bc.pendingCnt, bc.progress, bc.migrationErr 411 } 412 413 // iterateStateTrie runs state.Iterator, generated from the given state trie node hash, 414 // until it reaches end. If it reaches end, it will send a nil error to errCh to indicate that 415 // it has been finished. 416 func (bc *BlockChain) iterateStateTrie(root common.Hash, db state.Database, resultCh chan struct{}, errCh chan error) (resultErr error) { 417 defer func() { errCh <- resultErr }() 418 419 stateDB, err := state.New(root, db, nil) 420 if err != nil { 421 return err 422 } 423 424 it := state.NewNodeIterator(stateDB) 425 for it.Next() { 426 resultCh <- struct{}{} 427 select { 428 case <-bc.quitWarmUp: 429 return stopWarmUpErr 430 case <-bc.quit: 431 return blockChainStopWarmUpErr 432 default: 433 } 434 } 435 return nil 436 } 437 438 // warmUpChecker receives errors from each warm-up goroutine. 439 // If it receives a nil error, it means a child goroutine is successfully terminated. 440 // It also periodically checks and logs warm-up progress. 441 func (bc *BlockChain) warmUpChecker(mainTrieDB *statedb.Database, numChildren int, 442 resultCh chan struct{}, errCh chan error, 443 ) { 444 defer func() { bc.quitWarmUp = nil }() 445 446 cache := mainTrieDB.TrieNodeCache() 447 mainTrieCacheLimit := mainTrieDB.GetTrieNodeLocalCacheByteLimit() 448 logged := time.Now() 449 var context []interface{} 450 var percent uint64 451 var cnt int 452 453 updateContext := func() { 454 switch c := cache.(type) { 455 case *statedb.FastCache: 456 stats := c.UpdateStats().(fastcache.Stats) 457 percent = stats.BytesSize * 100 / mainTrieCacheLimit 458 context = []interface{}{ 459 "warmUpCnt", cnt, 460 "cacheLimit", units.Base2Bytes(mainTrieCacheLimit).String(), 461 "cachedSize", units.Base2Bytes(stats.BytesSize).String(), 462 "percent", percent, 463 } 464 default: 465 context = []interface{}{ 466 "warmUpCnt", cnt, 467 "cacheLimit", units.Base2Bytes(mainTrieCacheLimit).String(), 468 } 469 } 470 } 471 472 var resultErr error 473 for childCnt := 0; childCnt < numChildren; { 474 select { 475 case <-resultCh: 476 cnt++ 477 if time.Since(logged) < log.StatsReportLimit { 478 continue 479 } 480 481 logged = time.Now() 482 483 updateContext() 484 if percent > 90 { // more than 90% 485 close(bc.quitWarmUp) 486 logger.Info("Warm up is completed", context...) 487 return 488 } 489 490 logger.Info("Warm up progress", context...) 491 case err := <-errCh: 492 // if errCh returns nil, it means success. 493 if err != nil { 494 resultErr = err 495 logger.Warn("Warm up got an error", "err", err) 496 } 497 498 childCnt++ 499 logger.Debug("Warm up a child trie is finished", "childCnt", childCnt, "err", err) 500 } 501 } 502 503 updateContext() 504 context = append(context, "resultErr", resultErr) 505 logger.Info("Warm up is completed", context...) 506 } 507 508 // StartWarmUp retrieves all state/storage tries of the latest state root and caches the tries. 509 func (bc *BlockChain) StartWarmUp() error { 510 block, db, mainTrieDB, err := bc.prepareWarmUp() 511 if err != nil { 512 return err 513 } 514 // retrieve children nodes of state trie root node 515 children, err := db.TrieDB().NodeChildren(block.Root()) 516 if err != nil { 517 return err 518 } 519 // run goroutine for each child node 520 resultCh := make(chan struct{}, 10000) 521 errCh := make(chan error) 522 bc.quitWarmUp = make(chan struct{}) 523 for _, child := range children { 524 go bc.iterateStateTrie(child, db, resultCh, errCh) 525 } 526 // run a warm-up checker routine 527 go bc.warmUpChecker(mainTrieDB, len(children), resultCh, errCh) 528 logger.Info("State trie warm-up is started", "blockNum", block.NumberU64(), 529 "root", block.Root().String(), "len(children)", len(children)) 530 return nil 531 } 532 533 // StopWarmUp stops the warming up process. 534 func (bc *BlockChain) StopWarmUp() error { 535 if bc.quitWarmUp == nil { 536 return ErrNotInWarmUp 537 } 538 539 close(bc.quitWarmUp) 540 541 return nil 542 } 543 544 // StartCollectingTrieStats collects state or storage trie statistics. 545 func (bc *BlockChain) StartCollectingTrieStats(contractAddr common.Address) error { 546 block := bc.GetBlockByNumber(bc.lastCommittedBlock) 547 if block == nil { 548 return fmt.Errorf("Block #%d not found", bc.lastCommittedBlock) 549 } 550 551 mainTrieDB := bc.StateCache().TrieDB() 552 cache := mainTrieDB.TrieNodeCache() 553 if cache == nil { 554 return fmt.Errorf("target cache is nil") 555 } 556 db := state.NewDatabaseWithExistingCache(bc.db, cache) 557 558 startNode := block.Root() 559 // If the contractAddr is given, start collecting stats from the root of storage trie 560 if !common.EmptyAddress(contractAddr) { 561 var err error 562 startNode, err = bc.GetContractStorageRoot(block, db, contractAddr) 563 if err != nil { 564 logger.Error("Failed to get the contract storage root", 565 "contractAddr", contractAddr.String(), "rootHash", block.Root().String(), 566 "err", err) 567 return err 568 } 569 } 570 571 children, err := db.TrieDB().NodeChildren(startNode) 572 if err != nil { 573 logger.Error("Failed to retrieve the children of start node", "err", err) 574 return err 575 } 576 577 logger.Info("Started collecting trie statistics", 578 "blockNum", block.NumberU64(), "root", block.Root().String(), "len(children)", len(children)) 579 go collectTrieStats(db, startNode) 580 581 return nil 582 } 583 584 // collectChildrenStats wraps CollectChildrenStats, in order to send finish signal to resultCh. 585 func collectChildrenStats(db state.Database, child common.Hash, resultCh chan<- statedb.NodeInfo) { 586 db.TrieDB().CollectChildrenStats(child, 2, resultCh) 587 resultCh <- statedb.NodeInfo{Finished: true} 588 } 589 590 // collectTrieStats is the main function of collecting trie statistics. 591 // It spawns goroutines for the upper-most children and iterates each sub-trie. 592 func collectTrieStats(db state.Database, startNode common.Hash) { 593 children, err := db.TrieDB().NodeChildren(startNode) 594 if err != nil { 595 logger.Error("Failed to retrieve the children of start node", "err", err) 596 } 597 598 // collecting statistics by running individual goroutines for each child 599 resultCh := make(chan statedb.NodeInfo, 10000) 600 for _, child := range children { 601 go collectChildrenStats(db, child, resultCh) 602 } 603 604 numGoRoutines := len(children) 605 ticker := time.NewTicker(1 * time.Minute) 606 607 numNodes, numLeafNodes, maxDepth := 0, 0, 0 608 depthCounter := make(map[int]int) 609 begin := time.Now() 610 for { 611 select { 612 case result := <-resultCh: 613 if result.Finished { 614 numGoRoutines-- 615 if numGoRoutines == 0 { 616 logger.Info("Finished collecting trie statistics", "elapsed", time.Since(begin), 617 "numNodes", numNodes, "numLeafNodes", numLeafNodes, "maxDepth", maxDepth) 618 printDepthStats(depthCounter) 619 return 620 } 621 continue 622 } 623 numNodes++ 624 // if a leaf node, collect the depth data 625 if result.Depth != 0 { 626 numLeafNodes++ 627 depthCounter[result.Depth]++ 628 if result.Depth > maxDepth { 629 maxDepth = result.Depth 630 } 631 } 632 case <-ticker.C: 633 // leave a periodic log 634 logger.Info("Collecting trie statistics is in progress...", "elapsed", time.Since(begin), 635 "numGoRoutines", numGoRoutines, "numNodes", numNodes, "numLeafNodes", numLeafNodes, "maxDepth", maxDepth) 636 printDepthStats(depthCounter) 637 } 638 } 639 } 640 641 // printDepthStats leaves logs containing the depth and the number of nodes in the depth. 642 func printDepthStats(depthCounter map[int]int) { 643 // max depth 20 is set by heuristically 644 for depth := 2; depth < 20; depth++ { 645 if depthCounter[depth] == 0 { 646 continue 647 } 648 logger.Info("number of leaf nodes in a depth", 649 "depth", depth, "numNodes", depthCounter[depth]) 650 } 651 } 652 653 // GetContractStorageRoot returns the storage root of a contract based on the given block. 654 func (bc *BlockChain) GetContractStorageRoot(block *types.Block, db state.Database, contractAddr common.Address) (common.Hash, error) { 655 stateDB, err := state.New(block.Root(), db, nil) 656 if err != nil { 657 return common.Hash{}, fmt.Errorf("failed to get StateDB - %w", err) 658 } 659 return stateDB.GetContractStorageRoot(contractAddr) 660 } 661 662 // prepareWarmUp creates and returns resources needed for state warm-up. 663 func (bc *BlockChain) prepareWarmUp() (*types.Block, state.Database, *statedb.Database, error) { 664 // There is a chance of concurrent access to quitWarmUp, though not likely to happen. 665 if bc.quitWarmUp != nil { 666 return nil, nil, nil, fmt.Errorf("already warming up") 667 } 668 669 block := bc.GetBlockByNumber(bc.lastCommittedBlock) 670 if block == nil { 671 return nil, nil, nil, fmt.Errorf("block #%d not found", bc.lastCommittedBlock) 672 } 673 674 mainTrieDB := bc.StateCache().TrieDB() 675 cache := mainTrieDB.TrieNodeCache() 676 if cache == nil { 677 return nil, nil, nil, fmt.Errorf("target cache is nil") 678 } 679 db := state.NewDatabaseWithExistingCache(bc.db, cache) 680 return block, db, mainTrieDB, nil 681 } 682 683 // iterateStorageTrie runs statedb.Iterator, generated from the given storage trie node hash, 684 // until it reaches end. If it reaches end, it will send a nil error to errCh to indicate that 685 // it has been finished. 686 func (bc *BlockChain) iterateStorageTrie(child common.Hash, storageTrie state.Trie, resultCh chan struct{}, errCh chan error) (resultErr error) { 687 defer func() { errCh <- resultErr }() 688 689 itr := statedb.NewIterator(storageTrie.NodeIterator(child[:])) 690 for itr.Next() { 691 resultCh <- struct{}{} 692 select { 693 case <-bc.quitWarmUp: 694 return stopWarmUpErr 695 case <-bc.quit: 696 return blockChainStopWarmUpErr 697 default: 698 } 699 } 700 return nil 701 } 702 703 func prepareContractWarmUp(block *types.Block, db state.Database, contractAddr common.Address) (common.Hash, state.Trie, error) { 704 stateDB, err := state.New(block.Root(), db, nil) 705 if err != nil { 706 return common.Hash{}, nil, fmt.Errorf("failed to get StateDB, err: %w", err) 707 } 708 storageTrieRoot, err := stateDB.GetContractStorageRoot(contractAddr) 709 if err != nil { 710 return common.Hash{}, nil, err 711 } 712 storageTrie, err := db.OpenStorageTrie(storageTrieRoot) 713 if err != nil { 714 return common.Hash{}, nil, err 715 } 716 return storageTrieRoot, storageTrie, nil 717 } 718 719 // StartContractWarmUp retrieves a storage trie of the latest state root and caches the trie 720 // corresponding to the given contract address. 721 func (bc *BlockChain) StartContractWarmUp(contractAddr common.Address) error { 722 block, db, mainTrieDB, err := bc.prepareWarmUp() 723 if err != nil { 724 return err 725 } 726 // prepare contract storage trie specific resources - storageTrieRoot and storageTrie 727 storageTrieRoot, storageTrie, err := prepareContractWarmUp(block, db, contractAddr) 728 if err != nil { 729 return fmt.Errorf("failed to prepare contract warm-up, err: %w", err) 730 } 731 // retrieve children nodes of contract storage trie root node 732 children, err := db.TrieDB().NodeChildren(storageTrieRoot) 733 if err != nil { 734 return err 735 } 736 // run goroutine for each child node 737 resultCh := make(chan struct{}, 10000) 738 errCh := make(chan error) 739 bc.quitWarmUp = make(chan struct{}) 740 for _, child := range children { 741 go bc.iterateStorageTrie(child, storageTrie, resultCh, errCh) 742 } 743 // run a warm-up checker routine 744 go bc.warmUpChecker(mainTrieDB, len(children), resultCh, errCh) 745 logger.Info("Contract storage trie warm-up is started", 746 "blockNum", block.NumberU64(), "root", block.Root().String(), "contractAddr", contractAddr.String(), 747 "contractStorageRoot", storageTrieRoot.String(), "len(children)", len(children)) 748 return nil 749 }