| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- // Copyright 2016 The go-ethereum Authors
- // This file is part of the go-ethereum library.
- //
- // The go-ethereum library is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Lesser General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // The go-ethereum library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
- // Package eth implements the Ethereum protocol.
- package eth
- import (
- "bytes"
- "encoding/binary"
- "fmt"
- "math/big"
- "time"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/logger/glog"
- "github.com/ethereum/go-ethereum/rlp"
- )
- var useSequentialKeys = []byte("dbUpgrade_20160530sequentialKeys")
- // upgradeSequentialKeys checks the chain database version and
- // starts a background process to make upgrades if necessary.
- // Returns a stop function that blocks until the process has
- // been safely stopped.
- func upgradeSequentialKeys(db ethdb.Database) (stopFn func()) {
- data, _ := db.Get(useSequentialKeys)
- if len(data) > 0 && data[0] == 42 {
- return nil // already converted
- }
- if data, _ := db.Get([]byte("LastHeader")); len(data) == 0 {
- db.Put(useSequentialKeys, []byte{42})
- return nil // empty database, nothing to do
- }
- glog.V(logger.Info).Infof("Upgrading chain database to use sequential keys")
- stopChn := make(chan struct{})
- stoppedChn := make(chan struct{})
- go func() {
- stopFn := func() bool {
- select {
- case <-time.After(time.Microsecond * 100): // make sure other processes don't get starved
- case <-stopChn:
- return true
- }
- return false
- }
- err, stopped := upgradeSequentialCanonicalNumbers(db, stopFn)
- if err == nil && !stopped {
- err, stopped = upgradeSequentialBlocks(db, stopFn)
- }
- if err == nil && !stopped {
- err, stopped = upgradeSequentialOrphanedReceipts(db, stopFn)
- }
- if err == nil && !stopped {
- glog.V(logger.Info).Infof("Database conversion successful")
- db.Put(useSequentialKeys, []byte{42})
- }
- if err != nil {
- glog.V(logger.Error).Infof("Database conversion failed: %v", err)
- }
- close(stoppedChn)
- }()
- return func() {
- close(stopChn)
- <-stoppedChn
- }
- }
- // upgradeSequentialCanonicalNumbers reads all old format canonical numbers from
- // the database, writes them in new format and deletes the old ones if successful.
- func upgradeSequentialCanonicalNumbers(db ethdb.Database, stopFn func() bool) (error, bool) {
- prefix := []byte("block-num-")
- it := db.(*ethdb.LDBDatabase).NewIterator()
- defer func() {
- it.Release()
- }()
- it.Seek(prefix)
- cnt := 0
- for bytes.HasPrefix(it.Key(), prefix) {
- keyPtr := it.Key()
- if len(keyPtr) < 20 {
- cnt++
- if cnt%100000 == 0 {
- it.Release()
- it = db.(*ethdb.LDBDatabase).NewIterator()
- it.Seek(keyPtr)
- glog.V(logger.Info).Infof("converting %d canonical numbers...", cnt)
- }
- number := big.NewInt(0).SetBytes(keyPtr[10:]).Uint64()
- newKey := []byte("h12345678n")
- binary.BigEndian.PutUint64(newKey[1:9], number)
- if err := db.Put(newKey, it.Value()); err != nil {
- return err, false
- }
- if err := db.Delete(keyPtr); err != nil {
- return err, false
- }
- }
- if stopFn() {
- return nil, true
- }
- it.Next()
- }
- if cnt > 0 {
- glog.V(logger.Info).Infof("converted %d canonical numbers...", cnt)
- }
- return nil, false
- }
- // upgradeSequentialBlocks reads all old format block headers, bodies, TDs and block
- // receipts from the database, writes them in new format and deletes the old ones
- // if successful.
- func upgradeSequentialBlocks(db ethdb.Database, stopFn func() bool) (error, bool) {
- prefix := []byte("block-")
- it := db.(*ethdb.LDBDatabase).NewIterator()
- defer func() {
- it.Release()
- }()
- it.Seek(prefix)
- cnt := 0
- for bytes.HasPrefix(it.Key(), prefix) {
- keyPtr := it.Key()
- if len(keyPtr) >= 38 {
- cnt++
- if cnt%10000 == 0 {
- it.Release()
- it = db.(*ethdb.LDBDatabase).NewIterator()
- it.Seek(keyPtr)
- glog.V(logger.Info).Infof("converting %d blocks...", cnt)
- }
- // convert header, body, td and block receipts
- var keyPrefix [38]byte
- copy(keyPrefix[:], keyPtr[0:38])
- hash := keyPrefix[6:38]
- if err := upgradeSequentialBlockData(db, hash); err != nil {
- return err, false
- }
- // delete old db entries belonging to this hash
- for bytes.HasPrefix(it.Key(), keyPrefix[:]) {
- if err := db.Delete(it.Key()); err != nil {
- return err, false
- }
- it.Next()
- }
- if err := db.Delete(append([]byte("receipts-block-"), hash...)); err != nil {
- return err, false
- }
- } else {
- it.Next()
- }
- if stopFn() {
- return nil, true
- }
- }
- if cnt > 0 {
- glog.V(logger.Info).Infof("converted %d blocks...", cnt)
- }
- return nil, false
- }
- // upgradeSequentialOrphanedReceipts removes any old format block receipts from the
- // database that did not have a corresponding block
- func upgradeSequentialOrphanedReceipts(db ethdb.Database, stopFn func() bool) (error, bool) {
- prefix := []byte("receipts-block-")
- it := db.(*ethdb.LDBDatabase).NewIterator()
- defer it.Release()
- it.Seek(prefix)
- cnt := 0
- for bytes.HasPrefix(it.Key(), prefix) {
- // phase 2 already converted receipts belonging to existing
- // blocks, just remove if there's anything left
- cnt++
- if err := db.Delete(it.Key()); err != nil {
- return err, false
- }
- if stopFn() {
- return nil, true
- }
- it.Next()
- }
- if cnt > 0 {
- glog.V(logger.Info).Infof("removed %d orphaned block receipts...", cnt)
- }
- return nil, false
- }
- // upgradeSequentialBlockData upgrades the header, body, td and block receipts
- // database entries belonging to a single hash (doesn't delete old data).
- func upgradeSequentialBlockData(db ethdb.Database, hash []byte) error {
- // get old chain data and block number
- headerRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-header")...))
- if len(headerRLP) == 0 {
- return nil
- }
- header := new(types.Header)
- if err := rlp.Decode(bytes.NewReader(headerRLP), header); err != nil {
- return err
- }
- number := header.Number.Uint64()
- bodyRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-body")...))
- tdRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-td")...))
- receiptsRLP, _ := db.Get(append([]byte("receipts-block-"), hash...))
- // store new hash -> number association
- encNum := make([]byte, 8)
- binary.BigEndian.PutUint64(encNum, number)
- if err := db.Put(append([]byte("H"), hash...), encNum); err != nil {
- return err
- }
- // store new chain data
- if err := db.Put(append(append([]byte("h"), encNum...), hash...), headerRLP); err != nil {
- return err
- }
- if len(tdRLP) != 0 {
- if err := db.Put(append(append(append([]byte("h"), encNum...), hash...), []byte("t")...), tdRLP); err != nil {
- return err
- }
- }
- if len(bodyRLP) != 0 {
- if err := db.Put(append(append([]byte("b"), encNum...), hash...), bodyRLP); err != nil {
- return err
- }
- }
- if len(receiptsRLP) != 0 {
- if err := db.Put(append(append([]byte("r"), encNum...), hash...), receiptsRLP); err != nil {
- return err
- }
- }
- return nil
- }
- // upgradeChainDatabase ensures that the chain database stores block split into
- // separate header and body entries.
- func upgradeChainDatabase(db ethdb.Database) error {
- // Short circuit if the head block is stored already as separate header and body
- data, err := db.Get([]byte("LastBlock"))
- if err != nil {
- return nil
- }
- head := common.BytesToHash(data)
- if block := core.GetBlockByHashOld(db, head); block == nil {
- return nil
- }
- // At least some of the database is still the old format, upgrade (skip the head block!)
- glog.V(logger.Info).Info("Old database detected, upgrading...")
- if db, ok := db.(*ethdb.LDBDatabase); ok {
- blockPrefix := []byte("block-hash-")
- for it := db.NewIterator(); it.Next(); {
- // Skip anything other than a combined block
- if !bytes.HasPrefix(it.Key(), blockPrefix) {
- continue
- }
- // Skip the head block (merge last to signal upgrade completion)
- if bytes.HasSuffix(it.Key(), head.Bytes()) {
- continue
- }
- // Load the block, split and serialize (order!)
- block := core.GetBlockByHashOld(db, common.BytesToHash(bytes.TrimPrefix(it.Key(), blockPrefix)))
- if err := core.WriteTd(db, block.Hash(), block.NumberU64(), block.DeprecatedTd()); err != nil {
- return err
- }
- if err := core.WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil {
- return err
- }
- if err := core.WriteHeader(db, block.Header()); err != nil {
- return err
- }
- if err := db.Delete(it.Key()); err != nil {
- return err
- }
- }
- // Lastly, upgrade the head block, disabling the upgrade mechanism
- current := core.GetBlockByHashOld(db, head)
- if err := core.WriteTd(db, current.Hash(), current.NumberU64(), current.DeprecatedTd()); err != nil {
- return err
- }
- if err := core.WriteBody(db, current.Hash(), current.NumberU64(), current.Body()); err != nil {
- return err
- }
- if err := core.WriteHeader(db, current.Header()); err != nil {
- return err
- }
- }
- return nil
- }
- func addMipmapBloomBins(db ethdb.Database) (err error) {
- const mipmapVersion uint = 2
- // check if the version is set. We ignore data for now since there's
- // only one version so we can easily ignore it for now
- var data []byte
- data, _ = db.Get([]byte("setting-mipmap-version"))
- if len(data) > 0 {
- var version uint
- if err := rlp.DecodeBytes(data, &version); err == nil && version == mipmapVersion {
- return nil
- }
- }
- defer func() {
- if err == nil {
- var val []byte
- val, err = rlp.EncodeToBytes(mipmapVersion)
- if err == nil {
- err = db.Put([]byte("setting-mipmap-version"), val)
- }
- return
- }
- }()
- latestHash := core.GetHeadBlockHash(db)
- latestBlock := core.GetBlock(db, latestHash, core.GetBlockNumber(db, latestHash))
- if latestBlock == nil { // clean database
- return
- }
- tstart := time.Now()
- glog.V(logger.Info).Infoln("upgrading db log bloom bins")
- for i := uint64(0); i <= latestBlock.NumberU64(); i++ {
- hash := core.GetCanonicalHash(db, i)
- if (hash == common.Hash{}) {
- return fmt.Errorf("chain db corrupted. Could not find block %d.", i)
- }
- core.WriteMipmapBloom(db, i, core.GetBlockReceipts(db, hash, i))
- }
- glog.V(logger.Info).Infoln("upgrade completed in", time.Since(tstart))
- return nil
- }
|