core, eth: reject the reset target if the reverse diffs are pruned

This commit is contained in:
Gary Rong 2026-07-01 10:11:58 +08:00
parent 3958630ef8
commit 282aa6be0f
2 changed files with 20 additions and 0 deletions

View file

@ -407,6 +407,15 @@ func (bc *BlockChain) stateRecoverable(root common.Hash) bool {
return result
}
// StateRecoverable checks if the specified state is recoverable by applying
// state histories on top of the persistent state. It's the exported variant
// of stateRecoverable, and shares the same semantics: it's only meaningful for
// the path scheme, and returns false if the state is already available or the
// hash scheme is used.
func (bc *BlockChain) StateRecoverable(root common.Hash) bool {
return bc.stateRecoverable(root)
}
// ContractCodeWithPrefix retrieves a blob of data associated with a contract
// hash either from ephemeral in-memory cache, or from persistent storage.
func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) []byte {

View file

@ -72,6 +72,17 @@ func (b *EthAPIBackend) SetHead(number uint64) error {
if pivot := rawdb.ReadLastPivotNumber(b.eth.ChainDb()); pivot != nil && number < *pivot {
return fmt.Errorf("rewind target %d is before the snap-sync pivot %d", number, *pivot)
}
// In path mode the deepest reachable state is bounded by the amount of state
// histories retained. If the reverse diffs for the target have already been
// pruned, its state is no longer recoverable.
bc := b.eth.blockchain
if bc.TrieDB().Scheme() == rawdb.PathScheme {
if header := bc.GetHeaderByNumber(number); header != nil {
if !bc.HasState(header.Root) && !bc.StateRecoverable(header.Root) {
return errors.New("rewind target is not recoverable")
}
}
}
b.eth.handler.downloader.Cancel()
return b.eth.blockchain.SetHead(number)
}