This commit is contained in:
Delweng 2026-04-30 22:00:27 -07:00 committed by GitHub
commit 8afdfe98f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 53 additions and 7 deletions

View file

@ -2988,3 +2988,8 @@ func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
func (bc *BlockChain) StateSizer() *state.SizeTracker { func (bc *BlockChain) StateSizer() *state.SizeTracker {
return bc.stateSizer return bc.stateSizer
} }
// FreezerTailBlock returns the block number of the oldest state in the freezer.
func (bc *BlockChain) FreezerTailBlock() (uint64, error) {
return bc.triedb.FreezerTailBlock()
}

View file

@ -19,6 +19,7 @@ package eth
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"math/big" "math/big"
"time" "time"
@ -61,9 +62,16 @@ func (b *EthAPIBackend) CurrentBlock() *types.Header {
return b.eth.blockchain.CurrentBlock() return b.eth.blockchain.CurrentBlock()
} }
func (b *EthAPIBackend) SetHead(number uint64) { func (b *EthAPIBackend) SetHead(number uint64) error {
tailBlock, err := b.eth.blockchain.FreezerTailBlock()
if err != nil {
return err
}
if number < tailBlock {
return fmt.Errorf("cannot rewind to block %d, oldest available state is at block %d", number, tailBlock)
}
b.eth.handler.downloader.Cancel() b.eth.handler.downloader.Cancel()
b.eth.blockchain.SetHead(number) return b.eth.blockchain.SetHead(number)
} }
func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {

View file

@ -2119,8 +2119,7 @@ func (api *DebugAPI) SetHead(number hexutil.Uint64) error {
if header.Number.Uint64() <= uint64(number) { if header.Number.Uint64() <= uint64(number) {
return errors.New("not allowed to rewind to a future block") return errors.New("not allowed to rewind to a future block")
} }
api.b.SetHead(uint64(number)) return api.b.SetHead(uint64(number))
return nil
} }
// NetAPI offers network related RPC methods // NetAPI offers network related RPC methods

View file

@ -507,7 +507,7 @@ func (b testBackend) RPCGasCap() uint64 { return 10000000
func (b testBackend) RPCEVMTimeout() time.Duration { return time.Second } func (b testBackend) RPCEVMTimeout() time.Duration { return time.Second }
func (b testBackend) RPCTxFeeCap() float64 { return 0 } func (b testBackend) RPCTxFeeCap() float64 { return 0 }
func (b testBackend) UnprotectedAllowed() bool { return false } func (b testBackend) UnprotectedAllowed() bool { return false }
func (b testBackend) SetHead(number uint64) {} func (b testBackend) SetHead(number uint64) error { return nil }
func (b testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { func (b testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
if number == rpc.LatestBlockNumber { if number == rpc.LatestBlockNumber {
return b.chain.CurrentBlock(), nil return b.chain.CurrentBlock(), nil

View file

@ -57,7 +57,7 @@ type Backend interface {
RPCTxSyncMaxTimeout() time.Duration RPCTxSyncMaxTimeout() time.Duration
// Blockchain API // Blockchain API
SetHead(number uint64) SetHead(number uint64) error
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error)

View file

@ -336,7 +336,7 @@ func (b *backendMock) RPCGasCap() uint64 { return 0 }
func (b *backendMock) RPCEVMTimeout() time.Duration { return time.Second } func (b *backendMock) RPCEVMTimeout() time.Duration { return time.Second }
func (b *backendMock) RPCTxFeeCap() float64 { return 0 } func (b *backendMock) RPCTxFeeCap() float64 { return 0 }
func (b *backendMock) UnprotectedAllowed() bool { return false } func (b *backendMock) UnprotectedAllowed() bool { return false }
func (b *backendMock) SetHead(number uint64) {} func (b *backendMock) SetHead(number uint64) error { return nil }
func (b *backendMock) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { func (b *backendMock) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
return nil, nil return nil, nil
} }

View file

@ -393,3 +393,12 @@ func (db *Database) SnapshotCompleted() bool {
} }
return pdb.SnapshotCompleted() return pdb.SnapshotCompleted()
} }
// FrezzerTailBlock returns the block number of the oldest state in the freezer.
func (db *Database) FreezerTailBlock() (uint64, error) {
pdb, ok := db.backend.(*pathdb.Database)
if !ok {
return 0, nil
}
return pdb.FreezerTailBlock()
}

View file

@ -684,3 +684,28 @@ func (db *Database) SnapshotCompleted() bool {
} }
return db.tree.bottom().genComplete() return db.tree.bottom().genComplete()
} }
// FreezerTailBlock returns the block number of the oldest state in the freezer.
func (db *Database) FreezerTailBlock() (uint64, error) {
freezer := db.stateFreezer
if freezer == nil {
return 0, errors.New("freezer is not available")
}
tailID, err := freezer.Tail()
if err != nil {
return 0, err
}
// No state has been persistent, return the genesis block number.
if tailID == 0 {
return 0, nil
}
blob := rawdb.ReadStateHistoryMeta(freezer, tailID+1)
var m meta
if err := m.decode(blob); err != nil {
return 0, err
}
return m.block, nil
}