@@ -340,28 +340,38 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
340340 // Make sure the state associated with the block is available
341341 head := bc .CurrentBlock ()
342342 if ! bc .HasState (head .Root ) {
343- // Head state is missing, before the state recovery, find out the
344- // disk layer point of snapshot(if it's enabled). Make sure the
345- // rewound point is lower than disk layer.
346- var diskRoot common.Hash
347- if bc .cacheConfig .SnapshotLimit > 0 {
348- diskRoot = rawdb .ReadSnapshotRoot (bc .db )
349- }
350- if diskRoot != (common.Hash {}) {
351- log .Warn ("Head state missing, repairing" , "number" , head .Number , "hash" , head .Hash (), "snaproot" , diskRoot )
352-
353- snapDisk , err := bc .setHeadBeyondRoot (head .Number .Uint64 (), 0 , diskRoot , true )
354- if err != nil {
355- return nil , err
356- }
357- // Chain rewound, persist old snapshot number to indicate recovery procedure
358- if snapDisk != 0 {
359- rawdb .WriteSnapshotRecoveryNumber (bc .db , snapDisk )
360- }
343+ if head .Number .Uint64 () == 0 {
344+ // The genesis state is missing, which is only possible in the path-based
345+ // scheme. This situation occurs when the state syncer overwrites it.
346+ //
347+ // The solution is to reset the state to the genesis state. Although it may not
348+ // match the sync target, the state healer will later address and correct any
349+ // inconsistencies.
350+ bc .resetState ()
361351 } else {
362- log .Warn ("Head state missing, repairing" , "number" , head .Number , "hash" , head .Hash ())
363- if _ , err := bc .setHeadBeyondRoot (head .Number .Uint64 (), 0 , common.Hash {}, true ); err != nil {
364- return nil , err
352+ // Head state is missing, before the state recovery, find out the
353+ // disk layer point of snapshot(if it's enabled). Make sure the
354+ // rewound point is lower than disk layer.
355+ var diskRoot common.Hash
356+ if bc .cacheConfig .SnapshotLimit > 0 {
357+ diskRoot = rawdb .ReadSnapshotRoot (bc .db )
358+ }
359+ if diskRoot != (common.Hash {}) {
360+ log .Warn ("Head state missing, repairing" , "number" , head .Number , "hash" , head .Hash (), "snaproot" , diskRoot )
361+
362+ snapDisk , err := bc .setHeadBeyondRoot (head .Number .Uint64 (), 0 , diskRoot , true )
363+ if err != nil {
364+ return nil , err
365+ }
366+ // Chain rewound, persist old snapshot number to indicate recovery procedure
367+ if snapDisk != 0 {
368+ rawdb .WriteSnapshotRecoveryNumber (bc .db , snapDisk )
369+ }
370+ } else {
371+ log .Warn ("Head state missing, repairing" , "number" , head .Number , "hash" , head .Hash ())
372+ if _ , err := bc .setHeadBeyondRoot (head .Number .Uint64 (), 0 , common.Hash {}, true ); err != nil {
373+ return nil , err
374+ }
365375 }
366376 }
367377 }
@@ -620,6 +630,28 @@ func (bc *BlockChain) SetSafe(header *types.Header) {
620630 }
621631}
622632
633+ // resetState resets the persistent state to genesis state if it's not present.
634+ func (bc * BlockChain ) resetState () {
635+ // Short circuit if the genesis state is already present.
636+ root := bc .genesisBlock .Root ()
637+ if bc .HasState (root ) {
638+ return
639+ }
640+ // Reset the state database to empty for committing genesis state.
641+ // Note, it should only happen in path-based scheme and Reset function
642+ // is also only call-able in this mode.
643+ if bc .triedb .Scheme () == rawdb .PathScheme {
644+ if err := bc .triedb .Reset (types .EmptyRootHash ); err != nil {
645+ log .Crit ("Failed to clean state" , "err" , err ) // Shouldn't happen
646+ }
647+ }
648+ // Write genesis state into database.
649+ if err := CommitGenesisState (bc .db , bc .triedb , bc .genesisBlock .Hash ()); err != nil {
650+ log .Crit ("Failed to commit genesis state" , "err" , err )
651+ }
652+ log .Info ("Reset state to genesis" , "root" , root )
653+ }
654+
623655// setHeadBeyondRoot rewinds the local chain to a new head with the extra condition
624656// that the rewind must pass the specified state root. This method is meant to be
625657// used when rewinding with snapshots enabled to ensure that we go back further than
@@ -646,25 +678,6 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
646678 pivot := rawdb .ReadLastPivotNumber (bc .db )
647679 frozen , _ := bc .db .Ancients ()
648680
649- // resetState resets the persistent state to genesis if it's not available.
650- resetState := func () {
651- // Short circuit if the genesis state is already present.
652- if bc .HasState (bc .genesisBlock .Root ()) {
653- return
654- }
655- // Reset the state database to empty for committing genesis state.
656- // Note, it should only happen in path-based scheme and Reset function
657- // is also only call-able in this mode.
658- if bc .triedb .Scheme () == rawdb .PathScheme {
659- if err := bc .triedb .Reset (types .EmptyRootHash ); err != nil {
660- log .Crit ("Failed to clean state" , "err" , err ) // Shouldn't happen
661- }
662- }
663- // Write genesis state into database.
664- if err := CommitGenesisState (bc .db , bc .triedb , bc .genesisBlock .Hash ()); err != nil {
665- log .Crit ("Failed to commit genesis state" , "err" , err )
666- }
667- }
668681 updateFn := func (db ethdb.KeyValueWriter , header * types.Header ) (* types.Header , bool ) {
669682 // Rewind the blockchain, ensuring we don't end up with a stateless head
670683 // block. Note, depth equality is permitted to allow using SetHead as a
@@ -674,7 +687,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
674687 if newHeadBlock == nil {
675688 log .Error ("Gap in the chain, rewinding to genesis" , "number" , header .Number , "hash" , header .Hash ())
676689 newHeadBlock = bc .genesisBlock
677- resetState ()
690+ bc . resetState ()
678691 } else {
679692 // Block exists, keep rewinding until we find one with state,
680693 // keeping rewinding until we exceed the optional threshold
@@ -703,7 +716,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
703716 }
704717 if beyondRoot || newHeadBlock .NumberU64 () == 0 {
705718 if newHeadBlock .NumberU64 () == 0 {
706- resetState ()
719+ bc . resetState ()
707720 } else if ! bc .HasState (newHeadBlock .Root ()) {
708721 // Rewind to a block with recoverable state. If the state is
709722 // missing, run the state recovery here.
0 commit comments