Skip to content

Commit

Permalink
Merge pull request ethereum#293 from nextyio/rollback
Browse files Browse the repository at this point in the history
Rollback and syncing
  • Loading branch information
Zergity authored Sep 19, 2019
2 parents 64b9d34 + 9787b34 commit 801570d
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 38 deletions.
2 changes: 1 addition & 1 deletion accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ type SimulatedBackend struct {
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
genesis.MustCommit(database)
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil)
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil)

backend := &SimulatedBackend{
database: database,
Expand Down
2 changes: 1 addition & 1 deletion cmd/gonex/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
utils.SetDashboardConfig(ctx, &cfg.Dashboard)

if ctx.GlobalIsSet(utils.RollbackFlag.Name) {
cfg.Eth.RollbackNumber = ctx.GlobalInt64(utils.RollbackFlag.Name)
cfg.Eth.RollbackNumber = big.NewInt(ctx.GlobalInt64(utils.RollbackFlag.Name))
}

return stack, cfg
Expand Down
2 changes: 1 addition & 1 deletion cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -1767,7 +1767,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
cache.TrieDirtyLimit = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
}
vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)}
chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil)
chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, nil)
if err != nil {
Fatalf("Can't create BlockChain: %v", err)
}
Expand Down
66 changes: 50 additions & 16 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ type BlockChain struct {
// NewBlockChain returns a fully initialised block chain using information
// available in the database. It initialises the default Ethereum Validator and
// Processor.
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(block *types.Block) bool) (*BlockChain, error) {
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(block *types.Block) bool, rollbackNumber *big.Int) (*BlockChain, error) {
if cacheConfig == nil {
cacheConfig = &CacheConfig{
TrieCleanLimit: 256,
Expand Down Expand Up @@ -235,6 +235,27 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
if err := bc.loadLastState(); err != nil {
return nil, err
}

if rollbackNumber != nil {
number := bc.CurrentHeader().Number.Uint64()
var rollback uint64
if rollbackNumber.Sign() >= 0 {
rollback = rollbackNumber.Uint64()
} else {
r := uint64(-rollbackNumber.Int64())
if number > r {
rollback = number - r
}
}
if rollback > number {
log.Error("Rollback number is older than chain head", "number", number, "rollback", rollback)
} else {
log.Warn("Rolling-back chain as requested", "number", number, "rollback", rollback)
bc.SetHead(rollback)
log.Error("Chain rollback was successful, resuming normal operation")
}
}

// The first thing the node will do is reconstruct the verification data for
// the head block (ethash cache or clique voting snapshot). Might as well do
// it in advance.
Expand Down Expand Up @@ -390,27 +411,38 @@ func (bc *BlockChain) SetHead(head uint64) error {
updateFn := func(db ethdb.KeyValueWriter, header *types.Header) {
// Rewind the block chain, ensuring we don't end up with a stateless head block
if currentBlock := bc.CurrentBlock(); currentBlock != nil && header.Number.Uint64() < currentBlock.NumberU64() {
newHeadBlock := bc.GetBlock(header.Hash(), header.Number.Uint64())
if newHeadBlock == nil {
newHeadBlock = bc.genesisBlock
} else {
if _, err := state.New(newHeadBlock.Root(), bc.stateCache); err != nil {
// Rewound state missing, rolled back to before pivot, reset to genesis
newHeadBlock = bc.genesisBlock
newHeadBlock := func() *types.Block {
number := header.Number.Uint64()
// rolling back until a block with state found
for n := number; n > 0; n-- {
b := bc.GetBlockByNumber(n)
if b == nil {
continue
}
if _, err := state.New(b.Root(), bc.stateCache); err != nil {
continue
}
return b
}
}
return bc.genesisBlock
}()
rawdb.WriteHeadBlockHash(db, newHeadBlock.Hash())
bc.currentBlock.Store(newHeadBlock)
headBlockGauge.Update(int64(newHeadBlock.NumberU64()))
}

// Rewind the fast block in a simpleton way to the target head
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock != nil && header.Number.Uint64() < currentFastBlock.NumberU64() {
newHeadFastBlock := bc.GetBlock(header.Hash(), header.Number.Uint64())
// If either blocks reached nil, reset to the genesis state
if newHeadFastBlock == nil {
newHeadFastBlock = bc.genesisBlock
}
newHeadFastBlock := func() *types.Block {
// rolling back until a block is found
for n := header.Number.Uint64(); n > 0; n-- {
if b := bc.GetBlockByNumber(n); b != nil {
return b
}
}
return bc.genesisBlock
}()

rawdb.WriteHeadFastBlockHash(db, newHeadFastBlock.Hash())
bc.currentFastBlock.Store(newHeadFastBlock)
headFastBlockGauge.Update(int64(newHeadFastBlock.NumberU64()))
Expand Down Expand Up @@ -1730,7 +1762,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
// The method writes all (header-and-body-valid) blocks to disk, then tries to
// switch over to the new chain if the TD exceeded the current chain.
func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (int, []interface{}, []*types.Log, error) {
externTd := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
externHash := block.ParentHash()
externTd := bc.GetTd(externHash, block.NumberU64()-1)
current := bc.CurrentBlock()
// The first sidechain block error is already verified to be ErrPrunedAncestor.
// Since we don't import them here, we expect ErrUnknownAncestor for the remaining
Expand Down Expand Up @@ -1766,6 +1799,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
return it.index, nil, nil, errors.New("sidechain ghost-state attack")
}
}
externHash = block.Hash()
externTd = new(big.Int).Add(externTd, block.Difficulty())

if !bc.HasBlock(block.Hash(), block.NumberU64()) {
Expand All @@ -1786,7 +1820,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
// If the externTd was larger than our local TD, we now need to reimport the previous
// blocks to regenerate the required state
localTd := bc.GetTd(current.Hash(), current.NumberU64())
if ChainCompare(localTd, externTd, current.Hash(), block.Hash()) > 0 {
if ChainCompare(localTd, externTd, current.Hash(), externHash) > 0 {
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().Number, "sidetd", externTd, "localtd", localTd)
return it.index, nil, nil, err
}
Expand Down
17 changes: 1 addition & 16 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,26 +187,11 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
TrieTimeLimit: config.TrieTimeout,
}
)
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve)
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, config.RollbackNumber)
if err != nil {
return nil, err
}

number := eth.blockchain.CurrentHeader().Number.Uint64()
var rollback uint64
if config.RollbackNumber > 0 {
rollback = uint64(config.RollbackNumber)
} else if config.RollbackNumber < 0 {
rollback = uint64(number - uint64(-config.RollbackNumber))
}
if rollback > number {
log.Error("Rollback number is older than chain head", "number", number, "rollback", rollback)
} else if rollback > 0 {
log.Warn("Rolling-back chain as requested", "number", number, "rollback", rollback)
eth.blockchain.SetHead(rollback)
log.Error("Chain rollback was successful, resuming normal operation")
}

// Rewind the chain in case of an incompatible config upgrade.
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
log.Warn("Rewinding chain to upgrade configuration", "err", compat)
Expand Down
4 changes: 2 additions & 2 deletions eth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ type Config struct {
// Istanbul block override (TODO: remove after the fork)
OverrideIstanbul *big.Int

// Rollback is the block number the chain will be rolled back to.
RollbackNumber int64
// RollbackNumber is the block number the chain will be rolled back to.
RollbackNumber *big.Int `toml:",omitempty"`

// PriceServiceURL is service enpoint for the consensus.
PriceServiceURL string
Expand Down
2 changes: 1 addition & 1 deletion tests/block_test_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func (t *BlockTest) Run() error {
} else {
engine = ethash.NewShared()
}
chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieCleanLimit: 0}, config, engine, vm.Config{}, nil)
chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieCleanLimit: 0}, config, engine, vm.Config{}, nil, nil)
if err != nil {
return err
}
Expand Down

0 comments on commit 801570d

Please sign in to comment.