@@ -315,7 +315,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
315315 if diskRoot != (common.Hash {}) {
316316 log .Warn ("Head state missing, repairing" , "number" , head .Number (), "hash" , head .Hash (), "snaproot" , diskRoot )
317317
318- snapDisk , err := bc .setHeadBeyondRoot (head .NumberU64 (), diskRoot , true )
318+ snapDisk , err := bc .setHeadBeyondRoot (head .NumberU64 (), 0 , diskRoot , true )
319319 if err != nil {
320320 return nil , err
321321 }
@@ -325,7 +325,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
325325 }
326326 } else {
327327 log .Warn ("Head state missing, repairing" , "number" , head .Number (), "hash" , head .Hash ())
328- if _ , err := bc .setHeadBeyondRoot (head .NumberU64 (), common.Hash {}, true ); err != nil {
328+ if _ , err := bc .setHeadBeyondRoot (head .NumberU64 (), 0 , common.Hash {}, true ); err != nil {
329329 return nil , err
330330 }
331331 }
@@ -425,7 +425,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
425425 if compat , ok := genesisErr .(* params.ConfigCompatError ); ok {
426426 log .Warn ("Rewinding chain to upgrade configuration" , "err" , compat )
427427 if compat .RewindToTime > 0 {
428- log . Crit ( "Timestamp based rewinds not implemented yet /sad" )
428+ bc . SetHeadWithTimestamp ( compat . RewindToTime )
429429 } else {
430430 bc .SetHead (compat .RewindToBlock )
431431 }
@@ -533,7 +533,16 @@ func (bc *BlockChain) loadLastState() error {
533533// was fast synced or full synced and in which state, the method will try to
534534// delete minimal data from disk whilst retaining chain consistency.
535535func (bc * BlockChain ) SetHead (head uint64 ) error {
536- _ , err := bc .setHeadBeyondRoot (head , common.Hash {}, false )
536+ _ , err := bc .setHeadBeyondRoot (head , 0 , common.Hash {}, false )
537+ return err
538+ }
539+
540+ // SetHeadWithTimestamp rewinds the local chain to a new head that has at max
541+ // the given timestamp. Depending on whether the node was fast synced or full
542+ // synced and in which state, the method will try to delete minimal data from
543+ // disk whilst retaining chain consistency.
544+ func (bc * BlockChain ) SetHeadWithTimestamp (timestamp uint64 ) error {
545+ _ , err := bc .setHeadBeyondRoot (0 , timestamp , common.Hash {}, false )
537546 return err
538547}
539548
@@ -566,8 +575,12 @@ func (bc *BlockChain) SetSafe(block *types.Block) {
566575// in which state, the method will try to delete minimal data from disk whilst
567576// retaining chain consistency.
568577//
578+ // The method also works in timestamp mode if `head == 0` but `time != 0`. In that
579+ // case blocks are rolled back until the new head becomes older or equal to the
580+ // requested time. If both `head` and `time` is 0, the chain is rewound to genesis.
581+ //
569582// The method returns the block number where the requested root cap was found.
570- func (bc * BlockChain ) setHeadBeyondRoot (head uint64 , root common.Hash , repair bool ) (uint64 , error ) {
583+ func (bc * BlockChain ) setHeadBeyondRoot (head uint64 , time uint64 , root common.Hash , repair bool ) (uint64 , error ) {
571584 if ! bc .chainmu .TryLock () {
572585 return 0 , errChainStopped
573586 }
@@ -581,7 +594,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo
581594 pivot := rawdb .ReadLastPivotNumber (bc .db )
582595 frozen , _ := bc .db .Ancients ()
583596
584- updateFn := func (db ethdb.KeyValueWriter , header * types.Header ) (uint64 , bool ) {
597+ updateFn := func (db ethdb.KeyValueWriter , header * types.Header ) (* types. Header , bool ) {
585598 // Rewind the blockchain, ensuring we don't end up with a stateless head
586599 // block. Note, depth equality is permitted to allow using SetHead as a
587600 // chain reparation mechanism without deleting any data!
@@ -662,16 +675,18 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo
662675 bc .currentFastBlock .Store (newHeadFastBlock )
663676 headFastBlockGauge .Update (int64 (newHeadFastBlock .NumberU64 ()))
664677 }
665- head := bc .CurrentBlock ().NumberU64 ()
666-
678+ var (
679+ headHeader = bc .CurrentBlock ().Header ()
680+ headNumber = headHeader .Number .Uint64 ()
681+ )
667682 // If setHead underflown the freezer threshold and the block processing
668683 // intent afterwards is full block importing, delete the chain segment
669684 // between the stateful-block and the sethead target.
670685 var wipe bool
671- if head + 1 < frozen {
672- wipe = pivot == nil || head >= * pivot
686+ if headNumber + 1 < frozen {
687+ wipe = pivot == nil || headNumber >= * pivot
673688 }
674- return head , wipe // Only force wipe if full synced
689+ return headHeader , wipe // Only force wipe if full synced
675690 }
676691 // Rewind the header chain, deleting all block bodies until then
677692 delFn := func (db ethdb.KeyValueWriter , hash common.Hash , num uint64 ) {
@@ -698,13 +713,18 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo
698713 // touching the header chain altogether, unless the freezer is broken
699714 if repair {
700715 if target , force := updateFn (bc .db , bc .CurrentBlock ().Header ()); force {
701- bc .hc .SetHead (target , updateFn , delFn )
716+ bc .hc .SetHead (target . Number . Uint64 () , updateFn , delFn )
702717 }
703718 } else {
704719 // Rewind the chain to the requested head and keep going backwards until a
705720 // block with a state is found or fast sync pivot is passed
706- log .Warn ("Rewinding blockchain" , "target" , head )
707- bc .hc .SetHead (head , updateFn , delFn )
721+ if head != 0 || time == 0 {
722+ log .Warn ("Rewinding blockchain to block" , "target" , head )
723+ bc .hc .SetHead (head , updateFn , delFn )
724+ } else {
725+ log .Warn ("Rewinding blockchain to timestamp" , "target" , time )
726+ bc .hc .SetHeadWithTimestamp (time , updateFn , delFn )
727+ }
708728 }
709729 // Clear out any stale content from the caches
710730 bc .bodyCache .Purge ()
0 commit comments