Skip to content

Commit 73f5bcb

Browse files
authored
core, accounts, eth, trie: handle genesis state missing (#28171)
* core, accounts, eth, trie: handle genesis state missing * core, eth, trie: polish * core: manage txpool subscription in mainpool * eth/backend: fix test * cmd, eth: fix test * core/rawdb, trie/triedb/pathdb: address comments * eth, trie: address comments * eth: inline the function * eth: use synced flag * core/txpool: revert changes in txpool * core, eth, trie: rename functions
1 parent a081130 commit 73f5bcb

File tree

21 files changed

+244
-148
lines changed

21 files changed

+244
-148
lines changed

accounts/abi/bind/backends/simulated.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,6 @@ func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address,
199199
if err != nil {
200200
return nil, err
201201
}
202-
203202
return stateDB.GetCode(contract), nil
204203
}
205204

@@ -212,7 +211,6 @@ func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Addres
212211
if err != nil {
213212
return nil, err
214213
}
215-
216214
return stateDB.GetBalance(contract), nil
217215
}
218216

@@ -225,7 +223,6 @@ func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address,
225223
if err != nil {
226224
return 0, err
227225
}
228-
229226
return stateDB.GetNonce(contract), nil
230227
}
231228

@@ -238,7 +235,6 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
238235
if err != nil {
239236
return nil, err
240237
}
241-
242238
val := stateDB.GetState(contract, key)
243239
return val[:], nil
244240
}
@@ -700,8 +696,10 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
700696
}
701697
block.AddTxWithChain(b.blockchain, tx)
702698
})
703-
stateDB, _ := b.blockchain.State()
704-
699+
stateDB, err := b.blockchain.State()
700+
if err != nil {
701+
return err
702+
}
705703
b.pendingBlock = blocks[0]
706704
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
707705
b.pendingReceipts = receipts[0]
@@ -821,11 +819,12 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
821819
blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
822820
block.OffsetTime(int64(adjustment.Seconds()))
823821
})
824-
stateDB, _ := b.blockchain.State()
825-
822+
stateDB, err := b.blockchain.State()
823+
if err != nil {
824+
return err
825+
}
826826
b.pendingBlock = blocks[0]
827827
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
828-
829828
return nil
830829
}
831830

cmd/devp2p/internal/ethtest/suite_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ func setupGeth(stack *node.Node) error {
120120
if err != nil {
121121
return err
122122
}
123+
backend.SetSynced()
123124

124125
_, err = backend.BlockChain().InsertChain(chain.blocks[1:])
125126
return err

core/blockchain.go

Lines changed: 19 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -337,17 +337,17 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
337337
if err := bc.loadLastState(); err != nil {
338338
return nil, err
339339
}
340-
// Make sure the state associated with the block is available
340+
// Make sure the state associated with the block is available, or log out
341+
// if there is no available state, waiting for state sync.
341342
head := bc.CurrentBlock()
342343
if !bc.HasState(head.Root) {
343344
if head.Number.Uint64() == 0 {
344345
// 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()
346+
// scheme. This situation occurs when the initial state sync is not finished
347+
// yet, or the chain head is rewound below the pivot point. In both scenario,
348+
// there is no possible recovery approach except for rerunning a snap sync.
349+
// Do nothing here until the state syncer picks it up.
350+
log.Info("Genesis state is missing, wait state sync")
351351
} else {
352352
// Head state is missing, before the state recovery, find out the
353353
// disk layer point of snapshot(if it's enabled). Make sure the
@@ -630,28 +630,6 @@ func (bc *BlockChain) SetSafe(header *types.Header) {
630630
}
631631
}
632632

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-
655633
// setHeadBeyondRoot rewinds the local chain to a new head with the extra condition
656634
// that the rewind must pass the specified state root. This method is meant to be
657635
// used when rewinding with snapshots enabled to ensure that we go back further than
@@ -687,7 +665,6 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
687665
if newHeadBlock == nil {
688666
log.Error("Gap in the chain, rewinding to genesis", "number", header.Number, "hash", header.Hash())
689667
newHeadBlock = bc.genesisBlock
690-
bc.resetState()
691668
} else {
692669
// Block exists, keep rewinding until we find one with state,
693670
// keeping rewinding until we exceed the optional threshold
@@ -715,16 +692,14 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
715692
}
716693
}
717694
if beyondRoot || newHeadBlock.NumberU64() == 0 {
718-
if newHeadBlock.NumberU64() == 0 {
719-
bc.resetState()
720-
} else if !bc.HasState(newHeadBlock.Root()) {
695+
if !bc.HasState(newHeadBlock.Root()) && bc.stateRecoverable(newHeadBlock.Root()) {
721696
// Rewind to a block with recoverable state. If the state is
722697
// missing, run the state recovery here.
723698
if err := bc.triedb.Recover(newHeadBlock.Root()); err != nil {
724699
log.Crit("Failed to rollback state", "err", err) // Shouldn't happen
725700
}
701+
log.Debug("Rewound to block with state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
726702
}
727-
log.Debug("Rewound to block with state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
728703
break
729704
}
730705
log.Debug("Skipping block with threshold state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash(), "root", newHeadBlock.Root())
@@ -739,6 +714,15 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
739714
// to low, so it's safe to update in-memory markers directly.
740715
bc.currentBlock.Store(newHeadBlock.Header())
741716
headBlockGauge.Update(int64(newHeadBlock.NumberU64()))
717+
718+
// The head state is missing, which is only possible in the path-based
719+
// scheme. This situation occurs when the chain head is rewound below
720+
// the pivot point. In this scenario, there is no possible recovery
721+
// approach except for rerunning a snap sync. Do nothing here until the
722+
// state syncer picks it up.
723+
if !bc.HasState(newHeadBlock.Root()) {
724+
log.Info("Chain is stateless, wait state sync", "number", newHeadBlock.Number(), "hash", newHeadBlock.Hash())
725+
}
742726
}
743727
// Rewind the snap block in a simpleton way to the target head
744728
if currentSnapBlock := bc.CurrentSnapBlock(); currentSnapBlock != nil && header.Number.Uint64() < currentSnapBlock.Number.Uint64() {
@@ -838,7 +822,7 @@ func (bc *BlockChain) SnapSyncCommitHead(hash common.Hash) error {
838822
// Reset the trie database with the fresh snap synced state.
839823
root := block.Root()
840824
if bc.triedb.Scheme() == rawdb.PathScheme {
841-
if err := bc.triedb.Reset(root); err != nil {
825+
if err := bc.triedb.Enable(root); err != nil {
842826
return err
843827
}
844828
}

core/rawdb/accessors_sync.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,25 @@ func DeleteSkeletonHeader(db ethdb.KeyValueWriter, number uint64) {
7676
log.Crit("Failed to delete skeleton header", "err", err)
7777
}
7878
}
79+
80+
const (
81+
StateSyncUnknown = uint8(0) // flags the state snap sync is unknown
82+
StateSyncRunning = uint8(1) // flags the state snap sync is not completed yet
83+
StateSyncFinished = uint8(2) // flags the state snap sync is completed
84+
)
85+
86+
// ReadSnapSyncStatusFlag retrieves the state snap sync status flag.
87+
func ReadSnapSyncStatusFlag(db ethdb.KeyValueReader) uint8 {
88+
blob, err := db.Get(snapSyncStatusFlagKey)
89+
if err != nil || len(blob) != 1 {
90+
return StateSyncUnknown
91+
}
92+
return blob[0]
93+
}
94+
95+
// WriteSnapSyncStatusFlag stores the state snap sync status flag into database.
96+
func WriteSnapSyncStatusFlag(db ethdb.KeyValueWriter, flag uint8) {
97+
if err := db.Put(snapSyncStatusFlagKey, []byte{flag}); err != nil {
98+
log.Crit("Failed to store sync status flag", "err", err)
99+
}
100+
}

core/rawdb/database.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
555555
lastPivotKey, fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey,
556556
snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey,
557557
uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey,
558-
persistentStateIDKey, trieJournalKey, snapshotSyncStatusKey,
558+
persistentStateIDKey, trieJournalKey, snapshotSyncStatusKey, snapSyncStatusFlagKey,
559559
} {
560560
if bytes.Equal(key, meta) {
561561
metadata.Add(size)

core/rawdb/schema.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ var (
9191
// transitionStatusKey tracks the eth2 transition status.
9292
transitionStatusKey = []byte("eth2-transition")
9393

94+
// snapSyncStatusFlagKey flags that status of snap sync.
95+
snapSyncStatusFlagKey = []byte("SnapSyncStatus")
96+
9497
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
9598
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
9699
headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td

core/txpool/blobpool/blobpool.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,13 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr
355355
return err
356356
}
357357
}
358+
// Initialize the state with head block, or fallback to empty one in
359+
// case the head state is not available(might occur when node is not
360+
// fully synced).
358361
state, err := p.chain.StateAt(head.Root)
362+
if err != nil {
363+
state, err = p.chain.StateAt(types.EmptyRootHash)
364+
}
359365
if err != nil {
360366
return err
361367
}

core/txpool/legacypool/legacypool.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,20 @@ func (pool *LegacyPool) Init(gasTip *big.Int, head *types.Header, reserve txpool
298298

299299
// Set the basic pool parameters
300300
pool.gasTip.Store(gasTip)
301-
pool.reset(nil, head)
301+
302+
// Initialize the state with head block, or fallback to empty one in
303+
// case the head state is not available(might occur when node is not
304+
// fully synced).
305+
statedb, err := pool.chain.StateAt(head.Root)
306+
if err != nil {
307+
statedb, err = pool.chain.StateAt(types.EmptyRootHash)
308+
}
309+
if err != nil {
310+
return err
311+
}
312+
pool.currentHead.Store(head)
313+
pool.currentState = statedb
314+
pool.pendingNonces = newNoncer(statedb)
302315

303316
// Start the reorg loop early, so it can handle requests generated during
304317
// journal loading.

eth/api_backend.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,10 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B
204204
return nil, nil, errors.New("header not found")
205205
}
206206
stateDb, err := b.eth.BlockChain().StateAt(header.Root)
207-
return stateDb, header, err
207+
if err != nil {
208+
return nil, nil, err
209+
}
210+
return stateDb, header, nil
208211
}
209212

210213
func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
@@ -223,7 +226,10 @@ func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockN
223226
return nil, nil, errors.New("hash is not currently canonical")
224227
}
225228
stateDb, err := b.eth.BlockChain().StateAt(header.Root)
226-
return stateDb, header, err
229+
if err != nil {
230+
return nil, nil, err
231+
}
232+
return stateDb, header, nil
227233
}
228234
return nil, nil, errors.New("invalid arguments; neither block nor hash specified")
229235
}

eth/backend.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ func (s *Ethereum) Engine() consensus.Engine { return s.engine }
474474
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
475475
func (s *Ethereum) IsListening() bool { return true } // Always listening
476476
func (s *Ethereum) Downloader() *downloader.Downloader { return s.handler.downloader }
477-
func (s *Ethereum) Synced() bool { return s.handler.acceptTxs.Load() }
477+
func (s *Ethereum) Synced() bool { return s.handler.synced.Load() }
478478
func (s *Ethereum) SetSynced() { s.handler.enableSyncedFeatures() }
479479
func (s *Ethereum) ArchiveMode() bool { return s.config.NoPruning }
480480
func (s *Ethereum) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer }

0 commit comments

Comments
 (0)