Merge pull request #1373 from obscuren/recovery-tools
core, cmd/geth: improved recover functionality
This commit is contained in:
commit
507869bff1
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue