@@ -181,7 +181,7 @@ type BlockChain struct {
181
181
// NewBlockChain returns a fully initialised block chain using information
182
182
// available in the database. It initialises the default Ethereum Validator and
183
183
// Processor.
184
- func NewBlockChain (db ethdb.Database , cacheConfig * CacheConfig , chainConfig * params.ChainConfig , engine consensus.Engine , vmConfig vm.Config , shouldPreserve func (block * types.Block ) bool ) (* BlockChain , error ) {
184
+ 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 ) {
185
185
if cacheConfig == nil {
186
186
cacheConfig = & CacheConfig {
187
187
TrieCleanLimit : 256 ,
@@ -235,6 +235,27 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
235
235
if err := bc .loadLastState (); err != nil {
236
236
return nil , err
237
237
}
238
+
239
+ if rollbackNumber != nil {
240
+ number := bc .CurrentHeader ().Number .Uint64 ()
241
+ var rollback uint64
242
+ if rollbackNumber .Sign () >= 0 {
243
+ rollback = rollbackNumber .Uint64 ()
244
+ } else {
245
+ r := uint64 (- rollbackNumber .Int64 ())
246
+ if number > r {
247
+ rollback = number - r
248
+ }
249
+ }
250
+ if rollback > number {
251
+ log .Error ("Rollback number is older than chain head" , "number" , number , "rollback" , rollback )
252
+ } else {
253
+ log .Warn ("Rolling-back chain as requested" , "number" , number , "rollback" , rollback )
254
+ bc .SetHead (rollback )
255
+ log .Error ("Chain rollback was successful, resuming normal operation" )
256
+ }
257
+ }
258
+
238
259
// The first thing the node will do is reconstruct the verification data for
239
260
// the head block (ethash cache or clique voting snapshot). Might as well do
240
261
// it in advance.
@@ -390,27 +411,38 @@ func (bc *BlockChain) SetHead(head uint64) error {
390
411
updateFn := func (db ethdb.KeyValueWriter , header * types.Header ) {
391
412
// Rewind the block chain, ensuring we don't end up with a stateless head block
392
413
if currentBlock := bc .CurrentBlock (); currentBlock != nil && header .Number .Uint64 () < currentBlock .NumberU64 () {
393
- newHeadBlock := bc .GetBlock (header .Hash (), header .Number .Uint64 ())
394
- if newHeadBlock == nil {
395
- newHeadBlock = bc .genesisBlock
396
- } else {
397
- if _ , err := state .New (newHeadBlock .Root (), bc .stateCache ); err != nil {
398
- // Rewound state missing, rolled back to before pivot, reset to genesis
399
- newHeadBlock = bc .genesisBlock
414
+ newHeadBlock := func () * types.Block {
415
+ number := header .Number .Uint64 ()
416
+ // rolling back until a block with state found
417
+ for n := number ; n > 0 ; n -- {
418
+ b := bc .GetBlockByNumber (n )
419
+ if b == nil {
420
+ continue
421
+ }
422
+ if _ , err := state .New (b .Root (), bc .stateCache ); err != nil {
423
+ continue
424
+ }
425
+ return b
400
426
}
401
- }
427
+ return bc .genesisBlock
428
+ }()
402
429
rawdb .WriteHeadBlockHash (db , newHeadBlock .Hash ())
403
430
bc .currentBlock .Store (newHeadBlock )
404
431
headBlockGauge .Update (int64 (newHeadBlock .NumberU64 ()))
405
432
}
406
433
407
434
// Rewind the fast block in a simpleton way to the target head
408
435
if currentFastBlock := bc .CurrentFastBlock (); currentFastBlock != nil && header .Number .Uint64 () < currentFastBlock .NumberU64 () {
409
- newHeadFastBlock := bc .GetBlock (header .Hash (), header .Number .Uint64 ())
410
- // If either blocks reached nil, reset to the genesis state
411
- if newHeadFastBlock == nil {
412
- newHeadFastBlock = bc .genesisBlock
413
- }
436
+ newHeadFastBlock := func () * types.Block {
437
+ // rolling back until a block is found
438
+ for n := header .Number .Uint64 (); n > 0 ; n -- {
439
+ if b := bc .GetBlockByNumber (n ); b != nil {
440
+ return b
441
+ }
442
+ }
443
+ return bc .genesisBlock
444
+ }()
445
+
414
446
rawdb .WriteHeadFastBlockHash (db , newHeadFastBlock .Hash ())
415
447
bc .currentFastBlock .Store (newHeadFastBlock )
416
448
headFastBlockGauge .Update (int64 (newHeadFastBlock .NumberU64 ()))
@@ -1730,7 +1762,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
1730
1762
// The method writes all (header-and-body-valid) blocks to disk, then tries to
1731
1763
// switch over to the new chain if the TD exceeded the current chain.
1732
1764
func (bc * BlockChain ) insertSideChain (block * types.Block , it * insertIterator ) (int , []interface {}, []* types.Log , error ) {
1733
- externTd := bc .GetTd (block .ParentHash (), block .NumberU64 ()- 1 )
1765
+ externHash := block .ParentHash ()
1766
+ externTd := bc .GetTd (externHash , block .NumberU64 ()- 1 )
1734
1767
current := bc .CurrentBlock ()
1735
1768
// The first sidechain block error is already verified to be ErrPrunedAncestor.
1736
1769
// Since we don't import them here, we expect ErrUnknownAncestor for the remaining
@@ -1766,6 +1799,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
1766
1799
return it .index , nil , nil , errors .New ("sidechain ghost-state attack" )
1767
1800
}
1768
1801
}
1802
+ externHash = block .Hash ()
1769
1803
externTd = new (big.Int ).Add (externTd , block .Difficulty ())
1770
1804
1771
1805
if ! bc .HasBlock (block .Hash (), block .NumberU64 ()) {
@@ -1786,7 +1820,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
1786
1820
// If the externTd was larger than our local TD, we now need to reimport the previous
1787
1821
// blocks to regenerate the required state
1788
1822
localTd := bc .GetTd (current .Hash (), current .NumberU64 ())
1789
- if ChainCompare (localTd , externTd , current .Hash (), block . Hash () ) > 0 {
1823
+ if ChainCompare (localTd , externTd , current .Hash (), externHash ) > 0 {
1790
1824
log .Info ("Sidechain written to disk" , "start" , it .first ().NumberU64 (), "end" , it .previous ().Number , "sidetd" , externTd , "localtd" , localTd )
1791
1825
return it .index , nil , nil , err
1792
1826
}
0 commit comments