@@ -318,7 +318,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
318
318
if diskRoot != (common.Hash {}) {
319
319
log .Warn ("Head state missing, repairing" , "number" , head .Number (), "hash" , head .Hash (), "snaproot" , diskRoot )
320
320
321
- snapDisk , err := bc .setHeadBeyondRoot (head .NumberU64 (), diskRoot , true )
321
+ snapDisk , err := bc .setHeadBeyondRoot (head .NumberU64 (), 0 , diskRoot , true )
322
322
if err != nil {
323
323
return nil , err
324
324
}
@@ -328,7 +328,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
328
328
}
329
329
} else {
330
330
log .Warn ("Head state missing, repairing" , "number" , head .Number (), "hash" , head .Hash ())
331
- if _ , err := bc .setHeadBeyondRoot (head .NumberU64 (), common.Hash {}, true ); err != nil {
331
+ if _ , err := bc .setHeadBeyondRoot (head .NumberU64 (), 0 , common.Hash {}, true ); err != nil {
332
332
return nil , err
333
333
}
334
334
}
@@ -427,7 +427,11 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
427
427
// Rewind the chain in case of an incompatible config upgrade.
428
428
if compat , ok := genesisErr .(* params.ConfigCompatError ); ok {
429
429
log .Warn ("Rewinding chain to upgrade configuration" , "err" , compat )
430
- bc .SetHead (compat .RewindTo )
430
+ if compat .RewindToTime > 0 {
431
+ bc .SetHeadWithTimestamp (compat .RewindToTime )
432
+ } else {
433
+ bc .SetHead (compat .RewindToBlock )
434
+ }
431
435
rawdb .WriteChainConfig (db , genesisHash , chainConfig )
432
436
}
433
437
// Start tx indexer/unindexer if required.
@@ -532,7 +536,20 @@ func (bc *BlockChain) loadLastState() error {
532
536
// was fast synced or full synced and in which state, the method will try to
533
537
// delete minimal data from disk whilst retaining chain consistency.
534
538
func (bc * BlockChain ) SetHead (head uint64 ) error {
535
- if _ , err := bc .setHeadBeyondRoot (head , common.Hash {}, false ); err != nil {
539
+ if _ , err := bc .setHeadBeyondRoot (head , 0 , common.Hash {}, false ); err != nil {
540
+ return err
541
+ }
542
+ // Send chain head event to update the transaction pool
543
+ bc .chainHeadFeed .Send (ChainHeadEvent {Block : bc .CurrentBlock ()})
544
+ return nil
545
+ }
546
+
547
+ // SetHeadWithTimestamp rewinds the local chain to a new head that has at max
548
+ // the given timestamp. Depending on whether the node was fast synced or full
549
+ // synced and in which state, the method will try to delete minimal data from
550
+ // disk whilst retaining chain consistency.
551
+ func (bc * BlockChain ) SetHeadWithTimestamp (timestamp uint64 ) error {
552
+ if _ , err := bc .setHeadBeyondRoot (0 , timestamp , common.Hash {}, false ); err != nil {
536
553
return err
537
554
}
538
555
// Send chain head event to update the transaction pool
@@ -569,8 +586,12 @@ func (bc *BlockChain) SetSafe(block *types.Block) {
569
586
// in which state, the method will try to delete minimal data from disk whilst
570
587
// retaining chain consistency.
571
588
//
589
+ // The method also works in timestamp mode if `head == 0` but `time != 0`. In that
590
+ // case blocks are rolled back until the new head becomes older or equal to the
591
+ // requested time. If both `head` and `time` is 0, the chain is rewound to genesis.
592
+ //
572
593
// The method returns the block number where the requested root cap was found.
573
- func (bc * BlockChain ) setHeadBeyondRoot (head uint64 , root common.Hash , repair bool ) (uint64 , error ) {
594
+ func (bc * BlockChain ) setHeadBeyondRoot (head uint64 , time uint64 , root common.Hash , repair bool ) (uint64 , error ) {
574
595
if ! bc .chainmu .TryLock () {
575
596
return 0 , errChainStopped
576
597
}
@@ -584,7 +605,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo
584
605
pivot := rawdb .ReadLastPivotNumber (bc .db )
585
606
frozen , _ := bc .db .Ancients ()
586
607
587
- updateFn := func (db ethdb.KeyValueWriter , header * types.Header ) (uint64 , bool ) {
608
+ updateFn := func (db ethdb.KeyValueWriter , header * types.Header ) (* types. Header , bool ) {
588
609
// Rewind the blockchain, ensuring we don't end up with a stateless head
589
610
// block. Note, depth equality is permitted to allow using SetHead as a
590
611
// chain reparation mechanism without deleting any data!
@@ -665,16 +686,18 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo
665
686
bc .currentFastBlock .Store (newHeadFastBlock )
666
687
headFastBlockGauge .Update (int64 (newHeadFastBlock .NumberU64 ()))
667
688
}
668
- head := bc .CurrentBlock ().NumberU64 ()
669
-
689
+ var (
690
+ headHeader = bc .CurrentBlock ().Header ()
691
+ headNumber = headHeader .Number .Uint64 ()
692
+ )
670
693
// If setHead underflown the freezer threshold and the block processing
671
694
// intent afterwards is full block importing, delete the chain segment
672
695
// between the stateful-block and the sethead target.
673
696
var wipe bool
674
- if head + 1 < frozen {
675
- wipe = pivot == nil || head >= * pivot
697
+ if headNumber + 1 < frozen {
698
+ wipe = pivot == nil || headNumber >= * pivot
676
699
}
677
- return head , wipe // Only force wipe if full synced
700
+ return headHeader , wipe // Only force wipe if full synced
678
701
}
679
702
// Rewind the header chain, deleting all block bodies until then
680
703
delFn := func (db ethdb.KeyValueWriter , hash common.Hash , num uint64 ) {
@@ -701,13 +724,18 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo
701
724
// touching the header chain altogether, unless the freezer is broken
702
725
if repair {
703
726
if target , force := updateFn (bc .db , bc .CurrentBlock ().Header ()); force {
704
- bc .hc .SetHead (target , updateFn , delFn )
727
+ bc .hc .SetHead (target . Number . Uint64 () , updateFn , delFn )
705
728
}
706
729
} else {
707
730
// Rewind the chain to the requested head and keep going backwards until a
708
731
// block with a state is found or fast sync pivot is passed
709
- log .Warn ("Rewinding blockchain" , "target" , head )
710
- bc .hc .SetHead (head , updateFn , delFn )
732
+ if time > 0 {
733
+ log .Warn ("Rewinding blockchain to timestamp" , "target" , time )
734
+ bc .hc .SetHeadWithTimestamp (time , updateFn , delFn )
735
+ } else {
736
+ log .Warn ("Rewinding blockchain to block" , "target" , head )
737
+ bc .hc .SetHead (head , updateFn , delFn )
738
+ }
711
739
}
712
740
// Clear out any stale content from the caches
713
741
bc .bodyCache .Purge ()
0 commit comments