github.com/bamzi/go-ethereum@v1.6.7-0.20170704111104-138f26c93af1/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 Ethereum protocol.
    18  package eth
    19  
    20  import (
    21  	"bytes"
    22  	"encoding/binary"
    23  	"fmt"
    24  	"math/big"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/core"
    29  	"github.com/ethereum/go-ethereum/core/types"
    30  	"github.com/ethereum/go-ethereum/ethdb"
    31  	"github.com/ethereum/go-ethereum/log"
    32  	"github.com/ethereum/go-ethereum/rlp"
    33  )
    34  
    35  var useSequentialKeys = []byte("dbUpgrade_20160530sequentialKeys")
    36  
    37  // upgradeSequentialKeys checks the chain database version and
    38  // starts a background process to make upgrades if necessary.
    39  // Returns a stop function that blocks until the process has
    40  // been safely stopped.
    41  func upgradeSequentialKeys(db ethdb.Database) (stopFn func()) {
    42  	data, _ := db.Get(useSequentialKeys)
    43  	if len(data) > 0 && data[0] == 42 {
    44  		return nil // already converted
    45  	}
    46  
    47  	if data, _ := db.Get([]byte("LastHeader")); len(data) == 0 {
    48  		db.Put(useSequentialKeys, []byte{42})
    49  		return nil // empty database, nothing to do
    50  	}
    51  
    52  	log.Warn("Upgrading chain database to use sequential keys")
    53  
    54  	stopChn := make(chan struct{})
    55  	stoppedChn := make(chan struct{})
    56  
    57  	go func() {
    58  		stopFn := func() bool {
    59  			select {
    60  			case <-time.After(time.Microsecond * 100): // make sure other processes don't get starved
    61  			case <-stopChn:
    62  				return true
    63  			}
    64  			return false
    65  		}
    66  
    67  		err, stopped := upgradeSequentialCanonicalNumbers(db, stopFn)
    68  		if err == nil && !stopped {
    69  			err, stopped = upgradeSequentialBlocks(db, stopFn)
    70  		}
    71  		if err == nil && !stopped {
    72  			err, stopped = upgradeSequentialOrphanedReceipts(db, stopFn)
    73  		}
    74  		if err == nil && !stopped {
    75  			log.Info("Database conversion successful")
    76  			db.Put(useSequentialKeys, []byte{42})
    77  		}
    78  		if err != nil {
    79  			log.Error("Database conversion failed", "err", err)
    80  		}
    81  		close(stoppedChn)
    82  	}()
    83  
    84  	return func() {
    85  		close(stopChn)
    86  		<-stoppedChn
    87  	}
    88  }
    89  
    90  // upgradeSequentialCanonicalNumbers reads all old format canonical numbers from
    91  // the database, writes them in new format and deletes the old ones if successful.
    92  func upgradeSequentialCanonicalNumbers(db ethdb.Database, stopFn func() bool) (error, bool) {
    93  	prefix := []byte("block-num-")
    94  	it := db.(*ethdb.LDBDatabase).NewIterator()
    95  	defer func() {
    96  		it.Release()
    97  	}()
    98  	it.Seek(prefix)
    99  	cnt := 0
   100  	for bytes.HasPrefix(it.Key(), prefix) {
   101  		keyPtr := it.Key()
   102  		if len(keyPtr) < 20 {
   103  			cnt++
   104  			if cnt%100000 == 0 {
   105  				it.Release()
   106  				it = db.(*ethdb.LDBDatabase).NewIterator()
   107  				it.Seek(keyPtr)
   108  				log.Info("Converting canonical numbers", "count", cnt)
   109  			}
   110  			number := big.NewInt(0).SetBytes(keyPtr[10:]).Uint64()
   111  			newKey := []byte("h12345678n")
   112  			binary.BigEndian.PutUint64(newKey[1:9], number)
   113  			if err := db.Put(newKey, it.Value()); err != nil {
   114  				return err, false
   115  			}
   116  			if err := db.Delete(keyPtr); err != nil {
   117  				return err, false
   118  			}
   119  		}
   120  
   121  		if stopFn() {
   122  			return nil, true
   123  		}
   124  		it.Next()
   125  	}
   126  	if cnt > 0 {
   127  		log.Info("converted canonical numbers", "count", cnt)
   128  	}
   129  	return nil, false
   130  }
   131  
   132  // upgradeSequentialBlocks reads all old format block headers, bodies, TDs and block
   133  // receipts from the database, writes them in new format and deletes the old ones
   134  // if successful.
   135  func upgradeSequentialBlocks(db ethdb.Database, stopFn func() bool) (error, bool) {
   136  	prefix := []byte("block-")
   137  	it := db.(*ethdb.LDBDatabase).NewIterator()
   138  	defer func() {
   139  		it.Release()
   140  	}()
   141  	it.Seek(prefix)
   142  	cnt := 0
   143  	for bytes.HasPrefix(it.Key(), prefix) {
   144  		keyPtr := it.Key()
   145  		if len(keyPtr) >= 38 {
   146  			cnt++
   147  			if cnt%10000 == 0 {
   148  				it.Release()
   149  				it = db.(*ethdb.LDBDatabase).NewIterator()
   150  				it.Seek(keyPtr)
   151  				log.Info("Converting blocks", "count", cnt)
   152  			}
   153  			// convert header, body, td and block receipts
   154  			var keyPrefix [38]byte
   155  			copy(keyPrefix[:], keyPtr[0:38])
   156  			hash := keyPrefix[6:38]
   157  			if err := upgradeSequentialBlockData(db, hash); err != nil {
   158  				return err, false
   159  			}
   160  			// delete old db entries belonging to this hash
   161  			for bytes.HasPrefix(it.Key(), keyPrefix[:]) {
   162  				if err := db.Delete(it.Key()); err != nil {
   163  					return err, false
   164  				}
   165  				it.Next()
   166  			}
   167  			if err := db.Delete(append([]byte("receipts-block-"), hash...)); err != nil {
   168  				return err, false
   169  			}
   170  		} else {
   171  			it.Next()
   172  		}
   173  
   174  		if stopFn() {
   175  			return nil, true
   176  		}
   177  	}
   178  	if cnt > 0 {
   179  		log.Info("Converted blocks", "count", cnt)
   180  	}
   181  	return nil, false
   182  }
   183  
   184  // upgradeSequentialOrphanedReceipts removes any old format block receipts from the
   185  // database that did not have a corresponding block
   186  func upgradeSequentialOrphanedReceipts(db ethdb.Database, stopFn func() bool) (error, bool) {
   187  	prefix := []byte("receipts-block-")
   188  	it := db.(*ethdb.LDBDatabase).NewIterator()
   189  	defer it.Release()
   190  	it.Seek(prefix)
   191  	cnt := 0
   192  	for bytes.HasPrefix(it.Key(), prefix) {
   193  		// phase 2 already converted receipts belonging to existing
   194  		// blocks, just remove if there's anything left
   195  		cnt++
   196  		if err := db.Delete(it.Key()); err != nil {
   197  			return err, false
   198  		}
   199  
   200  		if stopFn() {
   201  			return nil, true
   202  		}
   203  		it.Next()
   204  	}
   205  	if cnt > 0 {
   206  		log.Info("Removed orphaned block receipts", "count", cnt)
   207  	}
   208  	return nil, false
   209  }
   210  
   211  // upgradeSequentialBlockData upgrades the header, body, td and block receipts
   212  // database entries belonging to a single hash (doesn't delete old data).
   213  func upgradeSequentialBlockData(db ethdb.Database, hash []byte) error {
   214  	// get old chain data and block number
   215  	headerRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-header")...))
   216  	if len(headerRLP) == 0 {
   217  		return nil
   218  	}
   219  	header := new(types.Header)
   220  	if err := rlp.Decode(bytes.NewReader(headerRLP), header); err != nil {
   221  		return err
   222  	}
   223  	number := header.Number.Uint64()
   224  	bodyRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-body")...))
   225  	tdRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-td")...))
   226  	receiptsRLP, _ := db.Get(append([]byte("receipts-block-"), hash...))
   227  	// store new hash -> number association
   228  	encNum := make([]byte, 8)
   229  	binary.BigEndian.PutUint64(encNum, number)
   230  	if err := db.Put(append([]byte("H"), hash...), encNum); err != nil {
   231  		return err
   232  	}
   233  	// store new chain data
   234  	if err := db.Put(append(append([]byte("h"), encNum...), hash...), headerRLP); err != nil {
   235  		return err
   236  	}
   237  	if len(tdRLP) != 0 {
   238  		if err := db.Put(append(append(append([]byte("h"), encNum...), hash...), []byte("t")...), tdRLP); err != nil {
   239  			return err
   240  		}
   241  	}
   242  	if len(bodyRLP) != 0 {
   243  		if err := db.Put(append(append([]byte("b"), encNum...), hash...), bodyRLP); err != nil {
   244  			return err
   245  		}
   246  	}
   247  	if len(receiptsRLP) != 0 {
   248  		if err := db.Put(append(append([]byte("r"), encNum...), hash...), receiptsRLP); err != nil {
   249  			return err
   250  		}
   251  	}
   252  	return nil
   253  }
   254  
   255  func addMipmapBloomBins(db ethdb.Database) (err error) {
   256  	const mipmapVersion uint = 2
   257  
   258  	// check if the version is set. We ignore data for now since there's
   259  	// only one version so we can easily ignore it for now
   260  	var data []byte
   261  	data, _ = db.Get([]byte("setting-mipmap-version"))
   262  	if len(data) > 0 {
   263  		var version uint
   264  		if err := rlp.DecodeBytes(data, &version); err == nil && version == mipmapVersion {
   265  			return nil
   266  		}
   267  	}
   268  
   269  	defer func() {
   270  		if err == nil {
   271  			var val []byte
   272  			val, err = rlp.EncodeToBytes(mipmapVersion)
   273  			if err == nil {
   274  				err = db.Put([]byte("setting-mipmap-version"), val)
   275  			}
   276  			return
   277  		}
   278  	}()
   279  	latestHash := core.GetHeadBlockHash(db)
   280  	latestBlock := core.GetBlock(db, latestHash, core.GetBlockNumber(db, latestHash))
   281  	if latestBlock == nil { // clean database
   282  		return
   283  	}
   284  
   285  	tstart := time.Now()
   286  	log.Warn("Upgrading db log bloom bins")
   287  	for i := uint64(0); i <= latestBlock.NumberU64(); i++ {
   288  		hash := core.GetCanonicalHash(db, i)
   289  		if (hash == common.Hash{}) {
   290  			return fmt.Errorf("chain db corrupted. Could not find block %d.", i)
   291  		}
   292  		core.WriteMipmapBloom(db, i, core.GetBlockReceipts(db, hash, i))
   293  	}
   294  	log.Info("Bloom-bin upgrade completed", "elapsed", common.PrettyDuration(time.Since(tstart)))
   295  	return nil
   296  }