Merge pull request #1373 from obscuren/recovery-tools
core, cmd/geth: improved recover functionality
This commit is contained in:
		@@ -37,7 +37,10 @@ import (
 | 
				
			|||||||
	"github.com/ethereum/go-ethereum/accounts"
 | 
						"github.com/ethereum/go-ethereum/accounts"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/cmd/utils"
 | 
						"github.com/ethereum/go-ethereum/cmd/utils"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/common"
 | 
						"github.com/ethereum/go-ethereum/common"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/core"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/core/types"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/eth"
 | 
						"github.com/ethereum/go-ethereum/eth"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/ethdb"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/logger"
 | 
						"github.com/ethereum/go-ethereum/logger"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/logger/glog"
 | 
						"github.com/ethereum/go-ethereum/logger/glog"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/metrics"
 | 
						"github.com/ethereum/go-ethereum/metrics"
 | 
				
			||||||
@@ -72,10 +75,13 @@ func init() {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			Action: blockRecovery,
 | 
								Action: blockRecovery,
 | 
				
			||||||
			Name:   "recover",
 | 
								Name:   "recover",
 | 
				
			||||||
			Usage:  "attempts to recover a corrupted database by setting a new block head by number",
 | 
								Usage:  "attempts to recover a corrupted database by setting a new block by number or hash. See help recover.",
 | 
				
			||||||
			Description: `
 | 
								Description: `
 | 
				
			||||||
The recover commands will attempt to read out the last
 | 
					The recover commands will attempt to read out the last
 | 
				
			||||||
block based on that.
 | 
					block based on that.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					recover #number recovers by number
 | 
				
			||||||
 | 
					recover <hex> recovers by hash
 | 
				
			||||||
`,
 | 
					`,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		blocktestCommand,
 | 
							blocktestCommand,
 | 
				
			||||||
@@ -450,17 +456,33 @@ func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (pass
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func blockRecovery(ctx *cli.Context) {
 | 
					func blockRecovery(ctx *cli.Context) {
 | 
				
			||||||
	num := ctx.Args().First()
 | 
						arg := ctx.Args().First()
 | 
				
			||||||
	if len(ctx.Args()) < 1 {
 | 
						if len(ctx.Args()) < 1 && len(arg) > 0 {
 | 
				
			||||||
		glog.Fatal("recover requires block number")
 | 
							glog.Fatal("recover requires block number or hash")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
 | 
						cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
 | 
				
			||||||
	ethereum, err := eth.New(cfg)
 | 
						blockDb, err := ethdb.NewLDBDatabase(filepath.Join(cfg.DataDir, "blockchain"))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		utils.Fatalf("%v", err)
 | 
							glog.Fatalln("could not open db:", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ethereum.ChainManager().Recover(common.String2Big(num).Uint64())
 | 
					
 | 
				
			||||||
 | 
						var block *types.Block
 | 
				
			||||||
 | 
						if arg[0] == '#' {
 | 
				
			||||||
 | 
							block = core.GetBlockByNumber(blockDb, common.String2Big(arg[1:]).Uint64())
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							block = core.GetBlockByHash(blockDb, common.HexToHash(arg))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if block == nil {
 | 
				
			||||||
 | 
							glog.Fatalln("block not found. Recovery failed")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = core.WriteHead(blockDb, block)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							glog.Fatalln("block write err", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						glog.Infof("Recovery succesful. New HEAD %x\n", block.Hash())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func startEth(ctx *cli.Context, eth *eth.Ethereum) {
 | 
					func startEth(ctx *cli.Context, eth *eth.Ethereum) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
package core
 | 
					package core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"math/big"
 | 
						"math/big"
 | 
				
			||||||
@@ -17,7 +16,6 @@ import (
 | 
				
			|||||||
	"github.com/ethereum/go-ethereum/logger"
 | 
						"github.com/ethereum/go-ethereum/logger"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/logger/glog"
 | 
						"github.com/ethereum/go-ethereum/logger/glog"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/metrics"
 | 
						"github.com/ethereum/go-ethereum/metrics"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/params"
 | 
					 | 
				
			||||||
	"github.com/ethereum/go-ethereum/pow"
 | 
						"github.com/ethereum/go-ethereum/pow"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/rlp"
 | 
						"github.com/ethereum/go-ethereum/rlp"
 | 
				
			||||||
	"github.com/hashicorp/golang-lru"
 | 
						"github.com/hashicorp/golang-lru"
 | 
				
			||||||
@@ -40,53 +38,6 @@ const (
 | 
				
			|||||||
	checkpointLimit     = 200
 | 
						checkpointLimit     = 200
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CalcDifficulty is the difficulty adjustment algorithm. It returns
 | 
					 | 
				
			||||||
// the difficulty that a new block b should have when created at time
 | 
					 | 
				
			||||||
// given the parent block's time and difficulty.
 | 
					 | 
				
			||||||
func CalcDifficulty(time int64, parentTime int64, parentDiff *big.Int) *big.Int {
 | 
					 | 
				
			||||||
	diff := new(big.Int)
 | 
					 | 
				
			||||||
	adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor)
 | 
					 | 
				
			||||||
	if big.NewInt(time-parentTime).Cmp(params.DurationLimit) < 0 {
 | 
					 | 
				
			||||||
		diff.Add(parentDiff, adjust)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		diff.Sub(parentDiff, adjust)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if diff.Cmp(params.MinimumDifficulty) < 0 {
 | 
					 | 
				
			||||||
		return params.MinimumDifficulty
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return diff
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// CalcTD computes the total difficulty of block.
 | 
					 | 
				
			||||||
func CalcTD(block, parent *types.Block) *big.Int {
 | 
					 | 
				
			||||||
	if parent == nil {
 | 
					 | 
				
			||||||
		return block.Difficulty()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	d := block.Difficulty()
 | 
					 | 
				
			||||||
	d.Add(d, parent.Td)
 | 
					 | 
				
			||||||
	return d
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// CalcGasLimit computes the gas limit of the next block after parent.
 | 
					 | 
				
			||||||
// The result may be modified by the caller.
 | 
					 | 
				
			||||||
func CalcGasLimit(parent *types.Block) *big.Int {
 | 
					 | 
				
			||||||
	decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor)
 | 
					 | 
				
			||||||
	contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(3))
 | 
					 | 
				
			||||||
	contrib = contrib.Div(contrib, big.NewInt(2))
 | 
					 | 
				
			||||||
	contrib = contrib.Div(contrib, params.GasLimitBoundDivisor)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	gl := new(big.Int).Sub(parent.GasLimit(), decay)
 | 
					 | 
				
			||||||
	gl = gl.Add(gl, contrib)
 | 
					 | 
				
			||||||
	gl = gl.Add(gl, big.NewInt(1))
 | 
					 | 
				
			||||||
	gl.Set(common.BigMax(gl, params.MinGasLimit))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if gl.Cmp(params.GenesisGasLimit) < 0 {
 | 
					 | 
				
			||||||
		gl.Add(parent.GasLimit(), decay)
 | 
					 | 
				
			||||||
		gl.Set(common.BigMin(gl, params.GenesisGasLimit))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return gl
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type ChainManager struct {
 | 
					type ChainManager struct {
 | 
				
			||||||
	//eth          EthManager
 | 
						//eth          EthManager
 | 
				
			||||||
	blockDb      common.Database
 | 
						blockDb      common.Database
 | 
				
			||||||
@@ -238,16 +189,6 @@ func (self *ChainManager) setTransState(statedb *state.StateDB) {
 | 
				
			|||||||
	self.transState = statedb
 | 
						self.transState = statedb
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (bc *ChainManager) Recover(num uint64) {
 | 
					 | 
				
			||||||
	block := bc.GetBlockByNumber(num)
 | 
					 | 
				
			||||||
	if block != nil {
 | 
					 | 
				
			||||||
		bc.insert(block)
 | 
					 | 
				
			||||||
		glog.Infof("Recovery succesful. New HEAD %x\n", block.Hash())
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		glog.Fatalln("Recovery failed")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (bc *ChainManager) recover() bool {
 | 
					func (bc *ChainManager) recover() bool {
 | 
				
			||||||
	data, _ := bc.blockDb.Get([]byte("checkpoint"))
 | 
						data, _ := bc.blockDb.Get([]byte("checkpoint"))
 | 
				
			||||||
	if len(data) != 0 {
 | 
						if len(data) != 0 {
 | 
				
			||||||
@@ -378,12 +319,7 @@ func (self *ChainManager) ExportN(w io.Writer, first uint64, last uint64) error
 | 
				
			|||||||
// insert injects a block into the current chain block chain. Note, this function
 | 
					// insert injects a block into the current chain block chain. Note, this function
 | 
				
			||||||
// assumes that the `mu` mutex is held!
 | 
					// assumes that the `mu` mutex is held!
 | 
				
			||||||
func (bc *ChainManager) insert(block *types.Block) {
 | 
					func (bc *ChainManager) insert(block *types.Block) {
 | 
				
			||||||
	key := append(blockNumPre, block.Number().Bytes()...)
 | 
						err := WriteHead(bc.blockDb, block)
 | 
				
			||||||
	err := bc.blockDb.Put(key, block.Hash().Bytes())
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		glog.Fatal("db write fail:", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	err = bc.blockDb.Put([]byte("LastBlock"), block.Hash().Bytes())
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		glog.Fatal("db write fail:", err)
 | 
							glog.Fatal("db write fail:", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -458,20 +394,15 @@ func (self *ChainManager) GetBlock(hash common.Hash) *types.Block {
 | 
				
			|||||||
		return block.(*types.Block)
 | 
							return block.(*types.Block)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data, _ := self.blockDb.Get(append(blockHashPre, hash[:]...))
 | 
						block := GetBlockByHash(self.blockDb, hash)
 | 
				
			||||||
	if len(data) == 0 {
 | 
						if block == nil {
 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	var block types.StorageBlock
 | 
					 | 
				
			||||||
	if err := rlp.Decode(bytes.NewReader(data), &block); err != nil {
 | 
					 | 
				
			||||||
		glog.V(logger.Error).Infof("invalid block RLP for hash %x: %v", hash, err)
 | 
					 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Add the block to the cache
 | 
						// Add the block to the cache
 | 
				
			||||||
	self.cache.Add(hash, (*types.Block)(&block))
 | 
						self.cache.Add(hash, (*types.Block)(block))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return (*types.Block)(&block)
 | 
						return (*types.Block)(block)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
 | 
					func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
 | 
				
			||||||
@@ -497,12 +428,7 @@ func (self *ChainManager) GetBlocksFromHash(hash common.Hash, n int) (blocks []*
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// non blocking version
 | 
					// non blocking version
 | 
				
			||||||
func (self *ChainManager) getBlockByNumber(num uint64) *types.Block {
 | 
					func (self *ChainManager) getBlockByNumber(num uint64) *types.Block {
 | 
				
			||||||
	key, _ := self.blockDb.Get(append(blockNumPre, big.NewInt(int64(num)).Bytes()...))
 | 
						return GetBlockByNumber(self.blockDb, num)
 | 
				
			||||||
	if len(key) == 0 {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return self.GetBlock(common.BytesToHash(key))
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (self *ChainManager) GetUnclesInChain(block *types.Block, length int) (uncles []*types.Header) {
 | 
					func (self *ChainManager) GetUnclesInChain(block *types.Block, length int) (uncles []*types.Header) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										98
									
								
								core/chain_util.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								core/chain_util.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
				
			|||||||
 | 
					package core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"math/big"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/common"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/core/types"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/logger"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/logger/glog"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/params"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/rlp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CalcDifficulty is the difficulty adjustment algorithm. It returns
 | 
				
			||||||
 | 
					// the difficulty that a new block b should have when created at time
 | 
				
			||||||
 | 
					// given the parent block's time and difficulty.
 | 
				
			||||||
 | 
					func CalcDifficulty(time int64, parentTime int64, parentDiff *big.Int) *big.Int {
 | 
				
			||||||
 | 
						diff := new(big.Int)
 | 
				
			||||||
 | 
						adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor)
 | 
				
			||||||
 | 
						if big.NewInt(time-parentTime).Cmp(params.DurationLimit) < 0 {
 | 
				
			||||||
 | 
							diff.Add(parentDiff, adjust)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							diff.Sub(parentDiff, adjust)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if diff.Cmp(params.MinimumDifficulty) < 0 {
 | 
				
			||||||
 | 
							return params.MinimumDifficulty
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return diff
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CalcTD computes the total difficulty of block.
 | 
				
			||||||
 | 
					func CalcTD(block, parent *types.Block) *big.Int {
 | 
				
			||||||
 | 
						if parent == nil {
 | 
				
			||||||
 | 
							return block.Difficulty()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d := block.Difficulty()
 | 
				
			||||||
 | 
						d.Add(d, parent.Td)
 | 
				
			||||||
 | 
						return d
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CalcGasLimit computes the gas limit of the next block after parent.
 | 
				
			||||||
 | 
					// The result may be modified by the caller.
 | 
				
			||||||
 | 
					func CalcGasLimit(parent *types.Block) *big.Int {
 | 
				
			||||||
 | 
						decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor)
 | 
				
			||||||
 | 
						contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(3))
 | 
				
			||||||
 | 
						contrib = contrib.Div(contrib, big.NewInt(2))
 | 
				
			||||||
 | 
						contrib = contrib.Div(contrib, params.GasLimitBoundDivisor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gl := new(big.Int).Sub(parent.GasLimit(), decay)
 | 
				
			||||||
 | 
						gl = gl.Add(gl, contrib)
 | 
				
			||||||
 | 
						gl = gl.Add(gl, big.NewInt(1))
 | 
				
			||||||
 | 
						gl.Set(common.BigMax(gl, params.MinGasLimit))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if gl.Cmp(params.GenesisGasLimit) < 0 {
 | 
				
			||||||
 | 
							gl.Add(parent.GasLimit(), decay)
 | 
				
			||||||
 | 
							gl.Set(common.BigMin(gl, params.GenesisGasLimit))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return gl
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetBlockByHash returns the block corresponding to the hash or nil if not found
 | 
				
			||||||
 | 
					func GetBlockByHash(db common.Database, hash common.Hash) *types.Block {
 | 
				
			||||||
 | 
						data, _ := db.Get(append(blockHashPre, hash[:]...))
 | 
				
			||||||
 | 
						if len(data) == 0 {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var block types.StorageBlock
 | 
				
			||||||
 | 
						if err := rlp.Decode(bytes.NewReader(data), &block); err != nil {
 | 
				
			||||||
 | 
							glog.V(logger.Error).Infof("invalid block RLP for hash %x: %v", hash, err)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return (*types.Block)(&block)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetBlockByHash returns the canonical block by number or nil if not found
 | 
				
			||||||
 | 
					func GetBlockByNumber(db common.Database, number uint64) *types.Block {
 | 
				
			||||||
 | 
						key, _ := db.Get(append(blockNumPre, big.NewInt(int64(number)).Bytes()...))
 | 
				
			||||||
 | 
						if len(key) == 0 {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return GetBlockByHash(db, common.BytesToHash(key))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WriteHead force writes the current head
 | 
				
			||||||
 | 
					func WriteHead(db common.Database, block *types.Block) error {
 | 
				
			||||||
 | 
						key := append(blockNumPre, block.Number().Bytes()...)
 | 
				
			||||||
 | 
						err := db.Put(key, block.Hash().Bytes())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = db.Put([]byte("LastBlock"), block.Hash().Bytes())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user