github.com/beyonderyue/gochain@v2.2.26+incompatible/eth/db_upgrade.go (about)

     1  // Copyright 2016 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 eth implements the GoChain protocol.
    18  package eth
    19  
    20  /*
    21  import (
    22  	"bytes"
    23  	"time"
    24  
    25  	"github.com/gochain-io/gochain/common"
    26  	"github.com/gochain-io/gochain/core"
    27  	"github.com/gochain-io/gochain/ethdb"
    28  	"github.com/gochain-io/gochain/log"
    29  	"github.com/gochain-io/gochain/rlp"
    30  )
    31  
    32  var deduplicateData = []byte("dbUpgrade_20170714deduplicateData")
    33  
    34  // upgradeDeduplicateData checks the chain database version and
    35  // starts a background process to make upgrades if necessary.
    36  // Returns a stop function that blocks until the process has
    37  // been safely stopped.
    38  func upgradeDeduplicateData(db common.Database) func() error {
    39  	// If the database is already converted or empty, bail out
    40  	data, _ := db.Get(deduplicateData)
    41  	if len(data) > 0 && data[0] == 42 {
    42  		return nil
    43  	}
    44  	if data, _ := db.Get([]byte("LastHeader")); len(data) == 0 {
    45  		if err := db.Put(deduplicateData, []byte{42}); err != nil {
    46  			log.Error("Cannot update deduplicate data key", "err", err)
    47  		}
    48  		return nil
    49  	}
    50  	// Start the deduplication upgrade on a new goroutine
    51  	log.Warn("Upgrading database to use lookup entries")
    52  	stop := make(chan chan error)
    53  
    54  	go func() {
    55  		// Create an iterator to read the entire database and covert old lookup entires
    56  		it := db.(*ethdb.LDBDatabase).NewIterator(nil, nil)
    57  		defer func() {
    58  			if it != nil {
    59  				it.Release()
    60  			}
    61  		}()
    62  
    63  		var (
    64  			converted uint64
    65  			failed    error
    66  		)
    67  		for failed == nil && it.Next() {
    68  			// Skip any entries that don't look like old transaction meta entires (<hash>0x01)
    69  			key := it.Key()
    70  			if len(key) != common.HashLength+1 || key[common.HashLength] != 0x01 {
    71  				continue
    72  			}
    73  			// Skip any entries that don't contain metadata (name clash between <hash>0x01 and <some-prefix><hash>)
    74  			var meta struct {
    75  				BlockHash  common.Hash
    76  				BlockIndex uint64
    77  				Index      uint64
    78  			}
    79  			if err := rlp.DecodeBytes(it.Value(), &meta); err != nil {
    80  				continue
    81  			}
    82  			// Skip any already upgraded entries (clash due to <hash> ending with 0x01 (old suffix))
    83  			hash := key[:common.HashLength]
    84  
    85  			if hash[0] == byte('l') {
    86  				// Potential clash, the "old" `hash` must point to a live transaction.
    87  				if tx, _, _, _ := rawdb.ReadTransaction(db, common.BytesToHash(hash)); tx == nil || !bytes.Equal(tx.Hash().Bytes(), hash) {
    88  					continue
    89  				}
    90  			}
    91  			// Convert the old metadata to a new lookup entry, delete duplicate data
    92  			if failed = db.Put(append([]byte("l"), hash...), it.Value()); failed == nil { // Write the new looku entry
    93  				if failed = db.Delete(hash); failed == nil { // Delete the duplicate transaction data
    94  					if failed = db.Delete(append([]byte("receipts-"), hash...)); failed == nil { // Delete the duplicate receipt data
    95  						if failed = db.Delete(key); failed != nil { // Delete the old transaction metadata
    96  							break
    97  						}
    98  					}
    99  				}
   100  			}
   101  			// Bump the conversion counter, and recreate the iterator occasionally to
   102  			// avoid too high memory consumption.
   103  			converted++
   104  			if converted%100000 == 0 {
   105  				it.Release()
   106  				it = db.(*ethdb.LDBDatabase).NewIterator(nil, nil)
   107  				it.Seek(key)
   108  
   109  				log.Info("Deduplicating database entries", "deduped", converted)
   110  			}
   111  			// Check for termination, or continue after a bit of a timeout
   112  			select {
   113  			case errc := <-stop:
   114  				errc <- nil
   115  				return
   116  			case <-time.After(time.Microsecond * 100):
   117  			}
   118  		}
   119  		// Upgrade finished, mark a such and terminate
   120  		if failed == nil {
   121  			log.Info("Database deduplication successful", "deduped", converted)
   122  			if err := db.Put(deduplicateData, []byte{42}); err != nil {
   123  				log.Error("Cannot update deduplicate data key on success", "err", err)
   124  			}
   125  		} else {
   126  			log.Error("Database deduplication failed", "deduped", converted, "err", failed)
   127  		}
   128  		it.Release()
   129  		it = nil
   130  
   131  		errc := <-stop
   132  		errc <- failed
   133  	}()
   134  	// Assembly the cancellation callback
   135  	return func() error {
   136  		errc := make(chan error)
   137  		stop <- errc
   138  		return <-errc
   139  	}
   140  }
   141  */