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/cmd/utils"
 | 
			
		||||
	"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/ethdb"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/logger"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/logger/glog"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/metrics"
 | 
			
		||||
@@ -72,10 +75,13 @@ func init() {
 | 
			
		||||
		{
 | 
			
		||||
			Action: blockRecovery,
 | 
			
		||||
			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: `
 | 
			
		||||
The recover commands will attempt to read out the last
 | 
			
		||||
block based on that.
 | 
			
		||||
 | 
			
		||||
recover #number recovers by number
 | 
			
		||||
recover <hex> recovers by hash
 | 
			
		||||
`,
 | 
			
		||||
		},
 | 
			
		||||
		blocktestCommand,
 | 
			
		||||
@@ -450,17 +456,33 @@ func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (pass
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func blockRecovery(ctx *cli.Context) {
 | 
			
		||||
	num := ctx.Args().First()
 | 
			
		||||
	if len(ctx.Args()) < 1 {
 | 
			
		||||
		glog.Fatal("recover requires block number")
 | 
			
		||||
	arg := ctx.Args().First()
 | 
			
		||||
	if len(ctx.Args()) < 1 && len(arg) > 0 {
 | 
			
		||||
		glog.Fatal("recover requires block number or hash")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
 | 
			
		||||
	ethereum, err := eth.New(cfg)
 | 
			
		||||
	blockDb, err := ethdb.NewLDBDatabase(filepath.Join(cfg.DataDir, "blockchain"))
 | 
			
		||||
	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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
package core
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math/big"
 | 
			
		||||
@@ -17,7 +16,6 @@ import (
 | 
			
		||||
	"github.com/ethereum/go-ethereum/logger"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/logger/glog"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/metrics"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/params"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/pow"
 | 
			
		||||
	"github.com/ethereum/go-ethereum/rlp"
 | 
			
		||||
	"github.com/hashicorp/golang-lru"
 | 
			
		||||
@@ -40,53 +38,6 @@ const (
 | 
			
		||||
	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 {
 | 
			
		||||
	//eth          EthManager
 | 
			
		||||
	blockDb      common.Database
 | 
			
		||||
@@ -238,16 +189,6 @@ func (self *ChainManager) setTransState(statedb *state.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 {
 | 
			
		||||
	data, _ := bc.blockDb.Get([]byte("checkpoint"))
 | 
			
		||||
	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
 | 
			
		||||
// assumes that the `mu` mutex is held!
 | 
			
		||||
func (bc *ChainManager) insert(block *types.Block) {
 | 
			
		||||
	key := append(blockNumPre, block.Number().Bytes()...)
 | 
			
		||||
	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())
 | 
			
		||||
	err := WriteHead(bc.blockDb, block)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		glog.Fatal("db write fail:", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -458,20 +394,15 @@ func (self *ChainManager) GetBlock(hash common.Hash) *types.Block {
 | 
			
		||||
		return block.(*types.Block)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data, _ := self.blockDb.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)
 | 
			
		||||
	block := GetBlockByHash(self.blockDb, hash)
 | 
			
		||||
	if block == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 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 {
 | 
			
		||||
@@ -497,12 +428,7 @@ func (self *ChainManager) GetBlocksFromHash(hash common.Hash, n int) (blocks []*
 | 
			
		||||
 | 
			
		||||
// non blocking version
 | 
			
		||||
func (self *ChainManager) getBlockByNumber(num uint64) *types.Block {
 | 
			
		||||
	key, _ := self.blockDb.Get(append(blockNumPre, big.NewInt(int64(num)).Bytes()...))
 | 
			
		||||
	if len(key) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return self.GetBlock(common.BytesToHash(key))
 | 
			
		||||
	return GetBlockByNumber(self.blockDb, num)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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