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  }