@@ -340,28 +340,38 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
340
340
// Make sure the state associated with the block is available
341
341
head := bc .CurrentBlock ()
342
342
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 ()
361
351
} 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
+ }
365
375
}
366
376
}
367
377
}
@@ -620,6 +630,28 @@ func (bc *BlockChain) SetSafe(header *types.Header) {
620
630
}
621
631
}
622
632
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
+
623
655
// setHeadBeyondRoot rewinds the local chain to a new head with the extra condition
624
656
// that the rewind must pass the specified state root. This method is meant to be
625
657
// 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
646
678
pivot := rawdb .ReadLastPivotNumber (bc .db )
647
679
frozen , _ := bc .db .Ancients ()
648
680
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
- }
668
681
updateFn := func (db ethdb.KeyValueWriter , header * types.Header ) (* types.Header , bool ) {
669
682
// Rewind the blockchain, ensuring we don't end up with a stateless head
670
683
// 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
674
687
if newHeadBlock == nil {
675
688
log .Error ("Gap in the chain, rewinding to genesis" , "number" , header .Number , "hash" , header .Hash ())
676
689
newHeadBlock = bc .genesisBlock
677
- resetState ()
690
+ bc . resetState ()
678
691
} else {
679
692
// Block exists, keep rewinding until we find one with state,
680
693
// keeping rewinding until we exceed the optional threshold
@@ -703,7 +716,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
703
716
}
704
717
if beyondRoot || newHeadBlock .NumberU64 () == 0 {
705
718
if newHeadBlock .NumberU64 () == 0 {
706
- resetState ()
719
+ bc . resetState ()
707
720
} else if ! bc .HasState (newHeadBlock .Root ()) {
708
721
// Rewind to a block with recoverable state. If the state is
709
722
// missing, run the state recovery here.
0 commit comments