@@ -318,7 +318,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
318318 if diskRoot != (common.Hash {}) {
319319 log .Warn ("Head state missing, repairing" , "number" , head .Number (), "hash" , head .Hash (), "snaproot" , diskRoot )
320320
321- snapDisk , err := bc .setHeadBeyondRoot (head .NumberU64 (), diskRoot , true )
321+ snapDisk , err := bc .setHeadBeyondRoot (head .NumberU64 (), 0 , diskRoot , true )
322322 if err != nil {
323323 return nil , err
324324 }
@@ -328,7 +328,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
328328 }
329329 } else {
330330 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 {
332332 return nil , err
333333 }
334334 }
@@ -427,7 +427,11 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
427427 // Rewind the chain in case of an incompatible config upgrade.
428428 if compat , ok := genesisErr .(* params.ConfigCompatError ); ok {
429429 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+ }
431435 rawdb .WriteChainConfig (db , genesisHash , chainConfig )
432436 }
433437 // Start tx indexer/unindexer if required.
@@ -532,7 +536,20 @@ func (bc *BlockChain) loadLastState() error {
532536// was fast synced or full synced and in which state, the method will try to
533537// delete minimal data from disk whilst retaining chain consistency.
534538func (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 {
536553 return err
537554 }
538555 // Send chain head event to update the transaction pool
@@ -569,8 +586,12 @@ func (bc *BlockChain) SetSafe(block *types.Block) {
569586// in which state, the method will try to delete minimal data from disk whilst
570587// retaining chain consistency.
571588//
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+ //
572593// 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 ) {
574595 if ! bc .chainmu .TryLock () {
575596 return 0 , errChainStopped
576597 }
@@ -584,7 +605,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo
584605 pivot := rawdb .ReadLastPivotNumber (bc .db )
585606 frozen , _ := bc .db .Ancients ()
586607
587- updateFn := func (db ethdb.KeyValueWriter , header * types.Header ) (uint64 , bool ) {
608+ updateFn := func (db ethdb.KeyValueWriter , header * types.Header ) (* types. Header , bool ) {
588609 // Rewind the blockchain, ensuring we don't end up with a stateless head
589610 // block. Note, depth equality is permitted to allow using SetHead as a
590611 // chain reparation mechanism without deleting any data!
@@ -665,16 +686,18 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo
665686 bc .currentFastBlock .Store (newHeadFastBlock )
666687 headFastBlockGauge .Update (int64 (newHeadFastBlock .NumberU64 ()))
667688 }
668- head := bc .CurrentBlock ().NumberU64 ()
669-
689+ var (
690+ headHeader = bc .CurrentBlock ().Header ()
691+ headNumber = headHeader .Number .Uint64 ()
692+ )
670693 // If setHead underflown the freezer threshold and the block processing
671694 // intent afterwards is full block importing, delete the chain segment
672695 // between the stateful-block and the sethead target.
673696 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
676699 }
677- return head , wipe // Only force wipe if full synced
700+ return headHeader , wipe // Only force wipe if full synced
678701 }
679702 // Rewind the header chain, deleting all block bodies until then
680703 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
701724 // touching the header chain altogether, unless the freezer is broken
702725 if repair {
703726 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 )
705728 }
706729 } else {
707730 // Rewind the chain to the requested head and keep going backwards until a
708731 // 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+ }
711739 }
712740 // Clear out any stale content from the caches
713741 bc .bodyCache .Purge ()
0 commit comments