github.com/Steality/go-ethereum@v1.9.7/core/rawdb/freezer_reinit.go (about) 1 // Copyright 2019 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 rawdb 18 19 import ( 20 "errors" 21 "runtime" 22 "sync/atomic" 23 "time" 24 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/common/prque" 27 "github.com/ethereum/go-ethereum/core/types" 28 "github.com/ethereum/go-ethereum/ethdb" 29 "github.com/ethereum/go-ethereum/log" 30 ) 31 32 // InitDatabaseFromFreezer reinitializes an empty database from a previous batch 33 // of frozen ancient blocks. The method iterates over all the frozen blocks and 34 // injects into the database the block hash->number mappings and the transaction 35 // lookup entries. 36 func InitDatabaseFromFreezer(db ethdb.Database) error { 37 // If we can't access the freezer or it's empty, abort 38 frozen, err := db.Ancients() 39 if err != nil || frozen == 0 { 40 return err 41 } 42 // Blocks previously frozen, iterate over- and hash them concurrently 43 var ( 44 number = ^uint64(0) // -1 45 results = make(chan *types.Block, 4*runtime.NumCPU()) 46 ) 47 abort := make(chan struct{}) 48 defer close(abort) 49 50 for i := 0; i < runtime.NumCPU(); i++ { 51 go func() { 52 for { 53 // Fetch the next task number, terminating if everything's done 54 n := atomic.AddUint64(&number, 1) 55 if n >= frozen { 56 return 57 } 58 // Retrieve the block from the freezer (no need for the hash, we pull by 59 // number from the freezer). If successful, pre-cache the block hash and 60 // the individual transaction hashes for storing into the database. 61 block := ReadBlock(db, common.Hash{}, n) 62 if block != nil { 63 block.Hash() 64 for _, tx := range block.Transactions() { 65 tx.Hash() 66 } 67 } 68 // Feed the block to the aggregator, or abort on interrupt 69 select { 70 case results <- block: 71 case <-abort: 72 return 73 } 74 } 75 }() 76 } 77 // Reassemble the blocks into a contiguous stream and push them out to disk 78 var ( 79 queue = prque.New(nil) 80 next = int64(0) 81 82 batch = db.NewBatch() 83 start = time.Now() 84 logged time.Time 85 ) 86 for i := uint64(0); i < frozen; i++ { 87 // Retrieve the next result and bail if it's nil 88 block := <-results 89 if block == nil { 90 return errors.New("broken ancient database") 91 } 92 // Push the block into the import queue and process contiguous ranges 93 queue.Push(block, -int64(block.NumberU64())) 94 for !queue.Empty() { 95 // If the next available item is gapped, return 96 if _, priority := queue.Peek(); -priority != next { 97 break 98 } 99 // Next block available, pop it off and index it 100 block = queue.PopItem().(*types.Block) 101 next++ 102 103 // Inject hash<->number mapping and txlookup indexes 104 WriteHeaderNumber(batch, block.Hash(), block.NumberU64()) 105 WriteTxLookupEntries(batch, block) 106 107 // If enough data was accumulated in memory or we're at the last block, dump to disk 108 if batch.ValueSize() > ethdb.IdealBatchSize || uint64(next) == frozen { 109 if err := batch.Write(); err != nil { 110 return err 111 } 112 batch.Reset() 113 } 114 // If we've spent too much time already, notify the user of what we're doing 115 if time.Since(logged) > 8*time.Second { 116 log.Info("Initializing chain from ancient data", "number", block.Number(), "hash", block.Hash(), "total", frozen-1, "elapsed", common.PrettyDuration(time.Since(start))) 117 logged = time.Now() 118 } 119 } 120 } 121 hash := ReadCanonicalHash(db, frozen-1) 122 WriteHeadHeaderHash(db, hash) 123 WriteHeadFastBlockHash(db, hash) 124 125 log.Info("Initialized chain from ancient data", "number", frozen-1, "hash", hash, "elapsed", common.PrettyDuration(time.Since(start))) 126 return nil 127 }