github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/core/rawdb/freezer_reinit.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 package rawdb 19 20 import ( 21 "errors" 22 "runtime" 23 "sync/atomic" 24 "time" 25 26 "github.com/AigarNetwork/aigar/common" 27 "github.com/AigarNetwork/aigar/common/prque" 28 "github.com/AigarNetwork/aigar/core/types" 29 "github.com/AigarNetwork/aigar/ethdb" 30 "github.com/AigarNetwork/aigar/log" 31 ) 32 33 // InitDatabaseFromFreezer reinitializes an empty database from a previous batch 34 // of frozen ancient blocks. The method iterates over all the frozen blocks and 35 // injects into the database the block hash->number mappings and the transaction 36 // lookup entries. 37 func InitDatabaseFromFreezer(db ethdb.Database) error { 38 // If we can't access the freezer or it's empty, abort 39 frozen, err := db.Ancients() 40 if err != nil || frozen == 0 { 41 return err 42 } 43 // Blocks previously frozen, iterate over- and hash them concurrently 44 var ( 45 number = ^uint64(0) // -1 46 results = make(chan *types.Block, 4*runtime.NumCPU()) 47 ) 48 abort := make(chan struct{}) 49 defer close(abort) 50 51 for i := 0; i < runtime.NumCPU(); i++ { 52 go func() { 53 for { 54 // Fetch the next task number, terminating if everything's done 55 n := atomic.AddUint64(&number, 1) 56 if n >= frozen { 57 return 58 } 59 // Retrieve the block from the freezer (no need for the hash, we pull by 60 // number from the freezer). If successful, pre-cache the block hash and 61 // the individual transaction hashes for storing into the database. 62 block := ReadBlock(db, common.Hash{}, n) 63 if block != nil { 64 block.Hash() 65 for _, tx := range block.Transactions() { 66 tx.Hash() 67 } 68 } 69 // Feed the block to the aggregator, or abort on interrupt 70 select { 71 case results <- block: 72 case <-abort: 73 return 74 } 75 } 76 }() 77 } 78 // Reassemble the blocks into a contiguous stream and push them out to disk 79 var ( 80 queue = prque.New(nil) 81 next = int64(0) 82 83 batch = db.NewBatch() 84 start = time.Now() 85 logged time.Time 86 ) 87 for i := uint64(0); i < frozen; i++ { 88 // Retrieve the next result and bail if it's nil 89 block := <-results 90 if block == nil { 91 return errors.New("broken ancient database") 92 } 93 // Push the block into the import queue and process contiguous ranges 94 queue.Push(block, -int64(block.NumberU64())) 95 for !queue.Empty() { 96 // If the next available item is gapped, return 97 if _, priority := queue.Peek(); -priority != next { 98 break 99 } 100 // Next block available, pop it off and index it 101 block = queue.PopItem().(*types.Block) 102 next++ 103 104 // Inject hash<->number mapping and txlookup indexes 105 WriteHeaderNumber(batch, block.Hash(), block.NumberU64()) 106 WriteTxLookupEntries(batch, block) 107 108 // If enough data was accumulated in memory or we're at the last block, dump to disk 109 if batch.ValueSize() > ethdb.IdealBatchSize || uint64(next) == frozen { 110 if err := batch.Write(); err != nil { 111 return err 112 } 113 batch.Reset() 114 } 115 // If we've spent too much time already, notify the user of what we're doing 116 if time.Since(logged) > 8*time.Second { 117 log.Info("Initializing chain from ancient data", "number", block.Number(), "hash", block.Hash(), "total", frozen-1, "elapsed", common.PrettyDuration(time.Since(start))) 118 logged = time.Now() 119 } 120 } 121 } 122 hash := ReadCanonicalHash(db, frozen-1) 123 WriteHeadHeaderHash(db, hash) 124 WriteHeadFastBlockHash(db, hash) 125 126 log.Info("Initialized chain from ancient data", "number", frozen-1, "hash", hash, "elapsed", common.PrettyDuration(time.Since(start))) 127 return nil 128 }