Merge pull request #700 from bas-vk/issue_650
Added blockchain DB versioning support, closes #650
This commit is contained in:
		@@ -36,11 +36,13 @@ import (
 | 
			
		||||
	"github.com/ethereum/go-ethereum/accounts"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/cmd/utils"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/common"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/core"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/core/state"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/core/types"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/eth"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/logger"
 | 
			
		||||
	"github.com/peterh/liner"
 | 
			
		||||
	"path"
 | 
			
		||||
)
 | 
			
		||||
import _ "net/http/pprof"
 | 
			
		||||
 | 
			
		||||
@@ -208,12 +210,18 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
 | 
			
		||||
			Name:   "export",
 | 
			
		||||
			Usage:  `export blockchain into file`,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Action: upgradeDb,
 | 
			
		||||
			Name:   "upgradedb",
 | 
			
		||||
			Usage:  "upgrade chainblock database",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	app.Flags = []cli.Flag{
 | 
			
		||||
		utils.UnlockedAccountFlag,
 | 
			
		||||
		utils.PasswordFileFlag,
 | 
			
		||||
		utils.BootnodesFlag,
 | 
			
		||||
		utils.DataDirFlag,
 | 
			
		||||
		utils.BlockchainVersionFlag,
 | 
			
		||||
		utils.JSpathFlag,
 | 
			
		||||
		utils.ListenPortFlag,
 | 
			
		||||
		utils.MaxPeersFlag,
 | 
			
		||||
@@ -437,13 +445,29 @@ func importchain(ctx *cli.Context) {
 | 
			
		||||
	if len(ctx.Args()) != 1 {
 | 
			
		||||
		utils.Fatalf("This command requires an argument.")
 | 
			
		||||
	}
 | 
			
		||||
	chainmgr, _, _ := utils.GetChain(ctx)
 | 
			
		||||
 | 
			
		||||
	cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
 | 
			
		||||
	cfg.SkipBcVersionCheck = true
 | 
			
		||||
 | 
			
		||||
	ethereum, err := eth.New(cfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		utils.Fatalf("%v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	chainmgr := ethereum.ChainManager()
 | 
			
		||||
	start := time.Now()
 | 
			
		||||
	err := utils.ImportChain(chainmgr, ctx.Args().First())
 | 
			
		||||
	err = utils.ImportChain(chainmgr, ctx.Args().First())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		utils.Fatalf("Import error: %v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// force database flush
 | 
			
		||||
	ethereum.BlockDb().Close()
 | 
			
		||||
	ethereum.StateDb().Close()
 | 
			
		||||
	ethereum.ExtraDb().Close()
 | 
			
		||||
 | 
			
		||||
	fmt.Printf("Import done in %v", time.Since(start))
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -451,9 +475,18 @@ func exportchain(ctx *cli.Context) {
 | 
			
		||||
	if len(ctx.Args()) != 1 {
 | 
			
		||||
		utils.Fatalf("This command requires an argument.")
 | 
			
		||||
	}
 | 
			
		||||
	chainmgr, _, _ := utils.GetChain(ctx)
 | 
			
		||||
 | 
			
		||||
	cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
 | 
			
		||||
	cfg.SkipBcVersionCheck = true
 | 
			
		||||
 | 
			
		||||
	ethereum, err := eth.New(cfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		utils.Fatalf("%v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	chainmgr := ethereum.ChainManager()
 | 
			
		||||
	start := time.Now()
 | 
			
		||||
	err := utils.ExportChain(chainmgr, ctx.Args().First())
 | 
			
		||||
	err = utils.ExportChain(chainmgr, ctx.Args().First())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		utils.Fatalf("Export error: %v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -461,6 +494,60 @@ func exportchain(ctx *cli.Context) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func upgradeDb(ctx *cli.Context) {
 | 
			
		||||
	fmt.Println("Upgrade blockchain DB")
 | 
			
		||||
 | 
			
		||||
	cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
 | 
			
		||||
	cfg.SkipBcVersionCheck = true
 | 
			
		||||
 | 
			
		||||
	ethereum, err := eth.New(cfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		utils.Fatalf("%v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v, _ := ethereum.BlockDb().Get([]byte("BlockchainVersion"))
 | 
			
		||||
	bcVersion := int(common.NewValue(v).Uint())
 | 
			
		||||
 | 
			
		||||
	if bcVersion == 0 {
 | 
			
		||||
		bcVersion = core.BlockChainVersion
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	filename := fmt.Sprintf("blockchain_%d_%s.chain", bcVersion, time.Now().Format("2006-01-02_15:04:05"))
 | 
			
		||||
	exportFile := path.Join(ctx.GlobalString(utils.DataDirFlag.Name), filename)
 | 
			
		||||
 | 
			
		||||
	err = utils.ExportChain(ethereum.ChainManager(), exportFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		utils.Fatalf("Unable to export chain for reimport %s\n", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ethereum.BlockDb().Close()
 | 
			
		||||
	ethereum.StateDb().Close()
 | 
			
		||||
	ethereum.ExtraDb().Close()
 | 
			
		||||
 | 
			
		||||
	os.RemoveAll(path.Join(ctx.GlobalString(utils.DataDirFlag.Name), "blockchain"))
 | 
			
		||||
 | 
			
		||||
	ethereum, err = eth.New(cfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		utils.Fatalf("%v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ethereum.BlockDb().Put([]byte("BlockchainVersion"), common.NewValue(core.BlockChainVersion).Bytes())
 | 
			
		||||
 | 
			
		||||
	err = utils.ImportChain(ethereum.ChainManager(), exportFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		utils.Fatalf("Import error %v (a backup is made in %s, use the import command to import it)\n", err, exportFile)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// force database flush
 | 
			
		||||
	ethereum.BlockDb().Close()
 | 
			
		||||
	ethereum.StateDb().Close()
 | 
			
		||||
	ethereum.ExtraDb().Close()
 | 
			
		||||
 | 
			
		||||
	os.Remove(exportFile)
 | 
			
		||||
 | 
			
		||||
	fmt.Println("Import finished")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func dump(ctx *cli.Context) {
 | 
			
		||||
	chainmgr, _, stateDb := utils.GetChain(ctx)
 | 
			
		||||
	for _, arg := range ctx.Args() {
 | 
			
		||||
 
 | 
			
		||||
@@ -155,7 +155,11 @@ func ImportChain(chainmgr *core.ChainManager, fn string) error {
 | 
			
		||||
 | 
			
		||||
	chainmgr.Reset()
 | 
			
		||||
	stream := rlp.NewStream(fh)
 | 
			
		||||
	var i int
 | 
			
		||||
	var i, n int
 | 
			
		||||
 | 
			
		||||
	batchSize := 2500
 | 
			
		||||
	blocks := make(types.Blocks, batchSize)
 | 
			
		||||
 | 
			
		||||
	for ; ; i++ {
 | 
			
		||||
		var b types.Block
 | 
			
		||||
		if err := stream.Decode(&b); err == io.EOF {
 | 
			
		||||
@@ -163,10 +167,25 @@ func ImportChain(chainmgr *core.ChainManager, fn string) error {
 | 
			
		||||
		} else if err != nil {
 | 
			
		||||
			return fmt.Errorf("at block %d: %v", i, err)
 | 
			
		||||
		}
 | 
			
		||||
		if err := chainmgr.InsertChain(types.Blocks{&b}); err != nil {
 | 
			
		||||
			return fmt.Errorf("invalid block %d: %v", i, err)
 | 
			
		||||
 | 
			
		||||
		blocks[n] = &b
 | 
			
		||||
		n++
 | 
			
		||||
 | 
			
		||||
		if n == batchSize {
 | 
			
		||||
			if err := chainmgr.InsertChain(blocks); err != nil {
 | 
			
		||||
				return fmt.Errorf("invalid block %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			n = 0
 | 
			
		||||
			blocks = make(types.Blocks, batchSize)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if n > 0 {
 | 
			
		||||
		if err := chainmgr.InsertChain(blocks[:n]); err != nil {
 | 
			
		||||
			return fmt.Errorf("invalid block %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Printf("imported %d blocks\n", i)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import (
 | 
			
		||||
	"runtime"
 | 
			
		||||
 | 
			
		||||
	"github.com/codegangsta/cli"
 | 
			
		||||
	"github.com/ethereum/ethash"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/accounts"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/common"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/core"
 | 
			
		||||
@@ -83,6 +84,11 @@ var (
 | 
			
		||||
		Usage: "Network Id",
 | 
			
		||||
		Value: eth.NetworkId,
 | 
			
		||||
	}
 | 
			
		||||
	BlockchainVersionFlag = cli.IntFlag{
 | 
			
		||||
		Name:  "blockchainversion",
 | 
			
		||||
		Usage: "Blockchain version",
 | 
			
		||||
		Value: core.BlockChainVersion,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// miner settings
 | 
			
		||||
	MinerThreadsFlag = cli.IntFlag{
 | 
			
		||||
@@ -240,6 +246,8 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
 | 
			
		||||
		Name:               common.MakeName(clientID, version),
 | 
			
		||||
		DataDir:            ctx.GlobalString(DataDirFlag.Name),
 | 
			
		||||
		ProtocolVersion:    ctx.GlobalInt(ProtocolVersionFlag.Name),
 | 
			
		||||
		BlockChainVersion:  ctx.GlobalInt(BlockchainVersionFlag.Name),
 | 
			
		||||
		SkipBcVersionCheck: false,
 | 
			
		||||
		NetworkId:          ctx.GlobalInt(NetworkIdFlag.Name),
 | 
			
		||||
		LogFile:            ctx.GlobalString(LogFileFlag.Name),
 | 
			
		||||
		LogLevel:           ctx.GlobalInt(LogLevelFlag.Name),
 | 
			
		||||
@@ -260,6 +268,7 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
 | 
			
		||||
 | 
			
		||||
func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Database) {
 | 
			
		||||
	dataDir := ctx.GlobalString(DataDirFlag.Name)
 | 
			
		||||
 | 
			
		||||
	blockDb, err := ethdb.NewLDBDatabase(path.Join(dataDir, "blockchain"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		Fatalf("Could not open database: %v", err)
 | 
			
		||||
@@ -269,7 +278,20 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Dat
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		Fatalf("Could not open database: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return core.NewChainManager(blockDb, stateDb, new(event.TypeMux)), blockDb, stateDb
 | 
			
		||||
 | 
			
		||||
	extraDb, err := ethdb.NewLDBDatabase(path.Join(dataDir, "extra"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		Fatalf("Could not open database: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	eventMux := new(event.TypeMux)
 | 
			
		||||
	chainManager := core.NewChainManager(blockDb, stateDb, eventMux)
 | 
			
		||||
	pow := ethash.New(chainManager)
 | 
			
		||||
	txPool := core.NewTxPool(eventMux, chainManager.State)
 | 
			
		||||
	blockProcessor := core.NewBlockProcessor(stateDb, extraDb, pow, txPool, chainManager, eventMux)
 | 
			
		||||
	chainManager.SetProcessor(blockProcessor)
 | 
			
		||||
 | 
			
		||||
	return chainManager, blockDb, stateDb
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetAccountManager(ctx *cli.Context) *accounts.Manager {
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,12 @@ import (
 | 
			
		||||
	"gopkg.in/fatih/set.v0"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// must be bumped when consensus algorithm is changed, this forces the upgradedb
 | 
			
		||||
	// command to be run (forces the blocks to be imported again using the new algorithm)
 | 
			
		||||
	BlockChainVersion = 1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var statelogger = logger.NewLogger("BLOCK")
 | 
			
		||||
 | 
			
		||||
type BlockProcessor struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -284,11 +284,14 @@ func (self *ChainManager) Export(w io.Writer) error {
 | 
			
		||||
	defer self.mu.RUnlock()
 | 
			
		||||
	glog.V(logger.Info).Infof("exporting %v blocks...\n", self.currentBlock.Header().Number)
 | 
			
		||||
 | 
			
		||||
	for block := self.currentBlock; block != nil; block = self.GetBlock(block.Header().ParentHash) {
 | 
			
		||||
		if err := block.EncodeRLP(w); err != nil {
 | 
			
		||||
	last := self.currentBlock.NumberU64()
 | 
			
		||||
 | 
			
		||||
	for nr := uint64(0); nr <= last; nr++ {
 | 
			
		||||
		if err := self.GetBlockByNumber(nr).EncodeRLP(w); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,9 @@ type Config struct {
 | 
			
		||||
	ProtocolVersion int
 | 
			
		||||
	NetworkId       int
 | 
			
		||||
 | 
			
		||||
	BlockChainVersion  int
 | 
			
		||||
	SkipBcVersionCheck bool // e.g. blockchain export
 | 
			
		||||
 | 
			
		||||
	DataDir  string
 | 
			
		||||
	LogFile  string
 | 
			
		||||
	LogLevel int
 | 
			
		||||
@@ -149,7 +152,7 @@ type Ethereum struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(config *Config) (*Ethereum, error) {
 | 
			
		||||
	// Boostrap database
 | 
			
		||||
	// Bootstrap database
 | 
			
		||||
	logger.New(config.DataDir, config.LogFile, config.LogLevel)
 | 
			
		||||
	if len(config.LogJSON) > 0 {
 | 
			
		||||
		logger.NewJSONsystem(config.DataDir, config.LogJSON)
 | 
			
		||||
@@ -179,6 +182,16 @@ func New(config *Config) (*Ethereum, error) {
 | 
			
		||||
	saveProtocolVersion(blockDb, config.ProtocolVersion)
 | 
			
		||||
	glog.V(logger.Info).Infof("Protocol Version: %v, Network Id: %v", config.ProtocolVersion, config.NetworkId)
 | 
			
		||||
 | 
			
		||||
	if !config.SkipBcVersionCheck {
 | 
			
		||||
		b, _ := blockDb.Get([]byte("BlockchainVersion"))
 | 
			
		||||
		bcVersion := int(common.NewValue(b).Uint())
 | 
			
		||||
		if bcVersion != config.BlockChainVersion && bcVersion != 0 {
 | 
			
		||||
			return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d). Run geth upgradedb.\n", bcVersion, config.BlockChainVersion)
 | 
			
		||||
		}
 | 
			
		||||
		saveBlockchainVersion(blockDb, config.BlockChainVersion)
 | 
			
		||||
	}
 | 
			
		||||
	glog.V(logger.Info).Infof("Blockchain DB Version: %d", config.BlockChainVersion)
 | 
			
		||||
 | 
			
		||||
	eth := &Ethereum{
 | 
			
		||||
		shutdownChan:   make(chan bool),
 | 
			
		||||
		blockDb:        blockDb,
 | 
			
		||||
@@ -472,3 +485,12 @@ func saveProtocolVersion(db common.Database, protov int) {
 | 
			
		||||
		db.Put([]byte("ProtocolVersion"), common.NewValue(protov).Bytes())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func saveBlockchainVersion(db common.Database, bcVersion int) {
 | 
			
		||||
	d, _ := db.Get([]byte("BlockchainVersion"))
 | 
			
		||||
	blockchainVersion := common.NewValue(d).Uint()
 | 
			
		||||
 | 
			
		||||
	if blockchainVersion == 0 {
 | 
			
		||||
		db.Put([]byte("BlockchainVersion"), common.NewValue(bcVersion).Bytes())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user