@@ -379,31 +379,41 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
379379 // Make sure the state associated with the block is available
380380 head := bc .CurrentBlock ()
381381 if ! bc .HasState (head .Root ) {
382- // Head state is missing, before the state recovery, find out the
383- // disk layer point of snapshot(if it's enabled). Make sure the
384- // rewound point is lower than disk layer.
385- var diskRoot common.Hash
386- if bc .cacheConfig .SnapshotLimit > 0 {
387- diskRoot = rawdb .ReadSnapshotRoot (bc .db )
388- }
389- if diskRoot != (common.Hash {}) {
390- log .Warn ("Head state missing, repairing" , "number" , head .Number , "hash" , head .Hash (), "snaproot" , diskRoot )
391-
392- snapDisk , diskRootFound , err := bc .setHeadBeyondRoot (head .Number .Uint64 (), 0 , diskRoot , true , bc .cacheConfig .SnapshotRestoreMaxGas )
393- if err != nil {
394- return nil , err
382+ if head .Number .Uint64 () <= bc .genesisBlock .NumberU64 () {
383+ // The genesis state is missing, which is only possible in the path-based
384+ // scheme. This situation occurs when the state syncer overwrites it.
385+ //
386+ // The solution is to reset the state to the genesis state. Although it may not
387+ // match the sync target, the state healer will later address and correct any
388+ // inconsistencies.
389+ bc .resetState ()
390+ } else {
391+ // Head state is missing, before the state recovery, find out the
392+ // disk layer point of snapshot(if it's enabled). Make sure the
393+ // rewound point is lower than disk layer.
394+ var diskRoot common.Hash
395+ if bc .cacheConfig .SnapshotLimit > 0 {
396+ diskRoot = rawdb .ReadSnapshotRoot (bc .db )
395397 }
396- // Chain rewound, persist old snapshot number to indicate recovery procedure
397- if diskRootFound {
398- rawdb .WriteSnapshotRecoveryNumber (bc .db , snapDisk )
398+ if diskRoot != (common.Hash {}) {
399+ log .Warn ("Head state missing, repairing" , "number" , head .Number , "hash" , head .Hash (), "snaproot" , diskRoot )
400+
401+ snapDisk , diskRootFound , err := bc .setHeadBeyondRoot (head .Number .Uint64 (), 0 , diskRoot , true , bc .cacheConfig .SnapshotRestoreMaxGas )
402+ if err != nil {
403+ return nil , err
404+ }
405+ // Chain rewound, persist old snapshot number to indicate recovery procedure
406+ if diskRootFound {
407+ rawdb .WriteSnapshotRecoveryNumber (bc .db , snapDisk )
408+ } else {
409+ log .Warn ("Snapshot root not found or too far back. Recreating snapshot from scratch." )
410+ rawdb .DeleteSnapshotRecoveryNumber (bc .db )
411+ }
399412 } else {
400- log .Warn ("Snapshot root not found or too far back. Recreating snapshot from scratch." )
401- rawdb .DeleteSnapshotRecoveryNumber (bc .db )
402- }
403- } else {
404- log .Warn ("Head state missing, repairing" , "number" , head .Number , "hash" , head .Hash ())
405- if _ , _ , err := bc .setHeadBeyondRoot (head .Number .Uint64 (), 0 , common.Hash {}, true , 0 ); err != nil {
406- return nil , err
413+ log .Warn ("Head state missing, repairing" , "number" , head .Number , "hash" , head .Hash ())
414+ if _ , _ , err := bc .setHeadBeyondRoot (head .Number .Uint64 (), 0 , common.Hash {}, true , 0 ); err != nil {
415+ return nil , err
416+ }
407417 }
408418 }
409419 }
@@ -662,6 +672,28 @@ func (bc *BlockChain) SetSafe(header *types.Header) {
662672 }
663673}
664674
675+ // resetState resets the persistent state to genesis state if it's not present.
676+ func (bc * BlockChain ) resetState () {
677+ // Short circuit if the genesis state is already present.
678+ root := bc .genesisBlock .Root ()
679+ if bc .HasState (root ) {
680+ return
681+ }
682+ // Reset the state database to empty for committing genesis state.
683+ // Note, it should only happen in path-based scheme and Reset function
684+ // is also only call-able in this mode.
685+ if bc .triedb .Scheme () == rawdb .PathScheme {
686+ if err := bc .triedb .Reset (types .EmptyRootHash ); err != nil {
687+ log .Crit ("Failed to clean state" , "err" , err ) // Shouldn't happen
688+ }
689+ }
690+ // Write genesis state into database.
691+ if err := CommitGenesisState (bc .db , bc .triedb , bc .genesisBlock .Hash ()); err != nil {
692+ log .Crit ("Failed to commit genesis state" , "err" , err )
693+ }
694+ log .Info ("Reset state to genesis" , "root" , root )
695+ }
696+
665697// setHeadBeyondRoot rewinds the local chain to a new head with the extra condition
666698// that the rewind must pass the specified state root. The extra condition is
667699// ignored if it causes rolling back more than rewindLimit Gas (0 meaning infinte).
@@ -691,25 +723,6 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
691723 pivot := rawdb .ReadLastPivotNumber (bc .db )
692724 frozen , _ := bc .db .Ancients ()
693725
694- // resetState resets the persistent state to genesis if it's not available.
695- resetState := func () {
696- // Short circuit if the genesis state is already present.
697- if bc .HasState (bc .genesisBlock .Root ()) {
698- return
699- }
700- // Reset the state database to empty for committing genesis state.
701- // Note, it should only happen in path-based scheme and Reset function
702- // is also only call-able in this mode.
703- if bc .triedb .Scheme () == rawdb .PathScheme {
704- if err := bc .triedb .Reset (types .EmptyRootHash ); err != nil {
705- log .Crit ("Failed to clean state" , "err" , err ) // Shouldn't happen
706- }
707- }
708- // Write genesis state into database.
709- if err := CommitGenesisState (bc .db , bc .triedb , bc .genesisBlock .Hash ()); err != nil {
710- log .Crit ("Failed to commit genesis state" , "err" , err )
711- }
712- }
713726 updateFn := func (db ethdb.KeyValueWriter , header * types.Header ) (* types.Header , bool ) {
714727 // Rewind the blockchain, ensuring we don't end up with a stateless head
715728 // block. Note, depth equality is permitted to allow using SetHead as a
@@ -719,7 +732,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
719732 if newHeadBlock == nil {
720733 log .Error ("Gap in the chain, rewinding to genesis" , "number" , header .Number , "hash" , header .Hash ())
721734 newHeadBlock = bc .genesisBlock
722- resetState ()
735+ bc . resetState ()
723736 } else {
724737 // Block exists, keep rewinding until we find one with state,
725738 // keeping rewinding until we exceed the optional threshold
@@ -772,7 +785,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
772785 }
773786 if rootFound || newHeadBlock .NumberU64 () <= bc .genesisBlock .NumberU64 () {
774787 if newHeadBlock .NumberU64 () <= bc .genesisBlock .NumberU64 () {
775- resetState ()
788+ bc . resetState ()
776789 } else if ! bc .HasState (newHeadBlock .Root ()) {
777790 // Rewind to a block with recoverable state. If the state is
778791 // missing, run the state recovery here.
0 commit comments