@@ -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
}
@@ -428,7 +428,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
428
428
if compat , ok := genesisErr .(* params.ConfigCompatError ); ok {
429
429
log .Warn ("Rewinding chain to upgrade configuration" , "err" , compat )
430
430
if compat .RewindToTime > 0 {
431
- log . Crit ( "Timestamp based rewinds not implemented yet /sad" )
431
+ bc . SetHeadWithTimestamp ( compat . RewindToTime )
432
432
} else {
433
433
bc .SetHead (compat .RewindToBlock )
434
434
}
@@ -536,7 +536,20 @@ func (bc *BlockChain) loadLastState() error {
536
536
// was fast synced or full synced and in which state, the method will try to
537
537
// delete minimal data from disk whilst retaining chain consistency.
538
538
func (bc * BlockChain ) SetHead (head uint64 ) error {
539
- 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 {
540
553
return err
541
554
}
542
555
// Send chain head event to update the transaction pool
@@ -573,8 +586,12 @@ func (bc *BlockChain) SetSafe(block *types.Block) {
573
586
// in which state, the method will try to delete minimal data from disk whilst
574
587
// retaining chain consistency.
575
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
+ //
576
593
// The method returns the block number where the requested root cap was found.
577
- 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 ) {
578
595
if ! bc .chainmu .TryLock () {
579
596
return 0 , errChainStopped
580
597
}
@@ -588,7 +605,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo
588
605
pivot := rawdb .ReadLastPivotNumber (bc .db )
589
606
frozen , _ := bc .db .Ancients ()
590
607
591
- updateFn := func (db ethdb.KeyValueWriter , header * types.Header ) (uint64 , bool ) {
608
+ updateFn := func (db ethdb.KeyValueWriter , header * types.Header ) (* types. Header , bool ) {
592
609
// Rewind the blockchain, ensuring we don't end up with a stateless head
593
610
// block. Note, depth equality is permitted to allow using SetHead as a
594
611
// chain reparation mechanism without deleting any data!
@@ -669,16 +686,18 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo
669
686
bc .currentFastBlock .Store (newHeadFastBlock )
670
687
headFastBlockGauge .Update (int64 (newHeadFastBlock .NumberU64 ()))
671
688
}
672
- head := bc .CurrentBlock ().NumberU64 ()
673
-
689
+ var (
690
+ headHeader = bc .CurrentBlock ().Header ()
691
+ headNumber = headHeader .Number .Uint64 ()
692
+ )
674
693
// If setHead underflown the freezer threshold and the block processing
675
694
// intent afterwards is full block importing, delete the chain segment
676
695
// between the stateful-block and the sethead target.
677
696
var wipe bool
678
- if head + 1 < frozen {
679
- wipe = pivot == nil || head >= * pivot
697
+ if headNumber + 1 < frozen {
698
+ wipe = pivot == nil || headNumber >= * pivot
680
699
}
681
- return head , wipe // Only force wipe if full synced
700
+ return headHeader , wipe // Only force wipe if full synced
682
701
}
683
702
// Rewind the header chain, deleting all block bodies until then
684
703
delFn := func (db ethdb.KeyValueWriter , hash common.Hash , num uint64 ) {
@@ -705,13 +724,18 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo
705
724
// touching the header chain altogether, unless the freezer is broken
706
725
if repair {
707
726
if target , force := updateFn (bc .db , bc .CurrentBlock ().Header ()); force {
708
- bc .hc .SetHead (target , updateFn , delFn )
727
+ bc .hc .SetHead (target . Number . Uint64 () , updateFn , delFn )
709
728
}
710
729
} else {
711
730
// Rewind the chain to the requested head and keep going backwards until a
712
731
// block with a state is found or fast sync pivot is passed
713
- log .Warn ("Rewinding blockchain" , "target" , head )
714
- 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
+ }
715
739
}
716
740
// Clear out any stale content from the caches
717
741
bc .bodyCache .Purge ()
0 commit comments