aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/geth/main.go36
-rw-r--r--core/chain_manager.go86
-rw-r--r--core/chain_util.go98
3 files changed, 133 insertions, 87 deletions
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index a7d3a73ef..645f51b9a 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -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)
+ }
+
+ 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)
}
- ethereum.ChainManager().Recover(common.String2Big(num).Uint64())
+ glog.Infof("Recovery succesful. New HEAD %x\n", block.Hash())
}
func startEth(ctx *cli.Context, eth *eth.Ethereum) {
diff --git a/core/chain_manager.go b/core/chain_manager.go
index a7b57b26b..70a8b11c6 100644
--- a/core/chain_manager.go
+++ b/core/chain_manager.go
@@ -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) {
diff --git a/core/chain_util.go b/core/chain_util.go
new file mode 100644
index 000000000..8051cc47a
--- /dev/null
+++ b/core/chain_util.go
@@ -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
+}