From d761acaa0660307cd44d62a6514835848b4cdeef Mon Sep 17 00:00:00 2001 From: Delweng Date: Wed, 2 Jul 2025 21:39:02 +0800 Subject: [PATCH 1/9] triedb: get the first state block Signed-off-by: Delweng --- triedb/database.go | 10 ++++++++++ triedb/pathdb/database.go | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/triedb/database.go b/triedb/database.go index d2637bd909..a77ddb8c7c 100644 --- a/triedb/database.go +++ b/triedb/database.go @@ -384,3 +384,13 @@ func (db *Database) SnapshotCompleted() bool { } return pdb.SnapshotCompleted() } + +// FirstStateBlock +func (db *Database) FirstStateBlock() (uint64, error) { + pdb, ok := db.backend.(*pathdb.Database) + if !ok { + // Ignore in hash scheme + return 0, nil + } + return pdb.FirstStateBlock() +} diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index ae9574963e..a9c528f8e7 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -692,3 +692,25 @@ func (db *Database) SnapshotCompleted() bool { } return db.tree.bottom().genComplete() } + +// FirstStateBlock returns the block number of the oldest state snapshot in the freezer or disk layer. +func (db *Database) FirstStateBlock() (uint64, error) { + var ( + m meta + err error + tailID = db.tree.bottom().stateID() + ) + + if db.stateFreezer != nil { + tailID, err = db.stateFreezer.Tail() + if err != nil { + return 0, err + } + } + + blob := rawdb.ReadStateHistoryMeta(db.diskdb, tailID) + if err := m.decode(blob); err != nil { + return 0, err + } + return m.block, nil +} From 8788d378295b5f28e8f97338d607b0e8971610c2 Mon Sep 17 00:00:00 2001 From: Delweng Date: Wed, 2 Jul 2025 21:40:25 +0800 Subject: [PATCH 2/9] core: check rewind head is later than older Signed-off-by: Delweng --- core/blockchain.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/blockchain.go b/core/blockchain.go index c97897cd70..fe6de0979e 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -735,6 +735,14 @@ func (bc *BlockChain) initializeHistoryPruning(latest uint64) error { // was snap synced or full synced and in which state, the method will try to // delete minimal data from disk whilst retaining chain consistency. func (bc *BlockChain) SetHead(head uint64) error { + // Only allowed to rewind to a block that is later than the oldest state block. + firstStateBlock, err := bc.triedb.FirstStateBlock() + if err != nil { + return err + } + if head < firstStateBlock { + return fmt.Errorf("cannot rewind to block %d, oldest available state is at block %d", head, firstStateBlock) + } if _, err := bc.setHeadBeyondRoot(head, 0, common.Hash{}, false); err != nil { return err } From 7f61b8b7e427e1392e6b0f9ef6f1ae511d5c70b2 Mon Sep 17 00:00:00 2001 From: Delweng Date: Wed, 2 Jul 2025 22:13:10 +0800 Subject: [PATCH 3/9] check first Signed-off-by: Delweng --- core/blockchain.go | 13 +++++------- eth/api_backend.go | 12 +++++++++-- internal/ethapi/api.go | 3 +-- internal/ethapi/api_test.go | 2 +- internal/ethapi/backend.go | 2 +- internal/ethapi/transaction_args_test.go | 2 +- triedb/database.go | 2 +- triedb/pathdb/database.go | 27 +++++++++++++----------- 8 files changed, 35 insertions(+), 28 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index fe6de0979e..8d64d5bc7a 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -735,14 +735,6 @@ func (bc *BlockChain) initializeHistoryPruning(latest uint64) error { // was snap synced or full synced and in which state, the method will try to // delete minimal data from disk whilst retaining chain consistency. func (bc *BlockChain) SetHead(head uint64) error { - // Only allowed to rewind to a block that is later than the oldest state block. - firstStateBlock, err := bc.triedb.FirstStateBlock() - if err != nil { - return err - } - if head < firstStateBlock { - return fmt.Errorf("cannot rewind to block %d, oldest available state is at block %d", head, firstStateBlock) - } if _, err := bc.setHeadBeyondRoot(head, 0, common.Hash{}, false); err != nil { return err } @@ -2827,3 +2819,8 @@ func (bc *BlockChain) GetTrieFlushInterval() time.Duration { func (bc *BlockChain) StateSizer() *state.SizeTracker { return bc.stateSizer } + +// FirstStateBlock returns the first available state block number that is stored in the database. +func (bc *BlockChain) FirstStateBlock() (uint64, error) { + return bc.triedb.FirstStateBlock() +} diff --git a/eth/api_backend.go b/eth/api_backend.go index 3ae73e78af..d830dc4a6f 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -19,6 +19,7 @@ package eth import ( "context" "errors" + "fmt" "math/big" "time" @@ -61,9 +62,16 @@ func (b *EthAPIBackend) CurrentBlock() *types.Header { return b.eth.blockchain.CurrentBlock() } -func (b *EthAPIBackend) SetHead(number uint64) { +func (b *EthAPIBackend) SetHead(number uint64) error { + firstStateBlock, err := b.eth.blockchain.FirstStateBlock() + if err != nil { + return err + } + if number < firstStateBlock { + return fmt.Errorf("cannot rewind to block %d, oldest available state is at block %d", number, firstStateBlock) + } 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) { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 554f525290..56c67cf70f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1933,8 +1933,7 @@ func (api *DebugAPI) SetHead(number hexutil.Uint64) error { if header.Number.Uint64() <= uint64(number) { return errors.New("not allowed to rewind to a future block") } - api.b.SetHead(uint64(number)) - return nil + return api.b.SetHead(uint64(number)) } // NetAPI offers network related RPC methods diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index c0a8fe9a58..69635b9435 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -487,7 +487,7 @@ func (b testBackend) RPCGasCap() uint64 { return 10000000 func (b testBackend) RPCEVMTimeout() time.Duration { return time.Second } func (b testBackend) RPCTxFeeCap() float64 { return 0 } 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) { if number == rpc.LatestBlockNumber { return b.chain.CurrentBlock(), nil diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index f709a1fcdc..d9c98dbfe4 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -55,7 +55,7 @@ type Backend interface { UnprotectedAllowed() bool // allows only for EIP155 transactions. // Blockchain API - SetHead(number uint64) + SetHead(number uint64) error HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index 30791f32b5..aa1c110bca 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -336,7 +336,7 @@ func (b *backendMock) RPCGasCap() uint64 { return 0 } func (b *backendMock) RPCEVMTimeout() time.Duration { return time.Second } func (b *backendMock) RPCTxFeeCap() float64 { return 0 } 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) { return nil, nil } diff --git a/triedb/database.go b/triedb/database.go index a77ddb8c7c..2c12a8abce 100644 --- a/triedb/database.go +++ b/triedb/database.go @@ -385,7 +385,7 @@ func (db *Database) SnapshotCompleted() bool { return pdb.SnapshotCompleted() } -// FirstStateBlock +// FirstStateBlock returns the first available state block number that is stored in the database. func (db *Database) FirstStateBlock() (uint64, error) { pdb, ok := db.backend.(*pathdb.Database) if !ok { diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index a9c528f8e7..f4ccd5cde5 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -695,20 +695,23 @@ func (db *Database) SnapshotCompleted() bool { // FirstStateBlock returns the block number of the oldest state snapshot in the freezer or disk layer. func (db *Database) FirstStateBlock() (uint64, error) { - var ( - m meta - err error - tailID = db.tree.bottom().stateID() - ) - - if db.stateFreezer != nil { - tailID, err = db.stateFreezer.Tail() - if err != nil { - return 0, err - } + freezer := db.stateFreezer + if freezer == nil { + return 0, errors.New("freezer is not available") } - blob := rawdb.ReadStateHistoryMeta(db.diskdb, tailID) + tailID, err := freezer.Tail() + if err != nil { + return 0, err + } + + // No state has been persistent + 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 } From 2fd686acbc915691cf893c72ae4fde89161e2dbc Mon Sep 17 00:00:00 2001 From: Delweng Date: Thu, 3 Jul 2025 10:12:50 +0800 Subject: [PATCH 4/9] doc Signed-off-by: Delweng --- triedb/pathdb/database.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index f4ccd5cde5..08cd6cc045 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -693,7 +693,7 @@ func (db *Database) SnapshotCompleted() bool { return db.tree.bottom().genComplete() } -// FirstStateBlock returns the block number of the oldest state snapshot in the freezer or disk layer. +// FirstStateBlock returns the block number of the oldest state in the freezer. func (db *Database) FirstStateBlock() (uint64, error) { freezer := db.stateFreezer if freezer == nil { @@ -705,7 +705,7 @@ func (db *Database) FirstStateBlock() (uint64, error) { return 0, err } - // No state has been persistent + // No state has been persistent, return the genesis block number. if tailID == 0 { return 0, nil } From ddc42b837657b8ab35e63b78695197e2207e944c Mon Sep 17 00:00:00 2001 From: jsvisa Date: Thu, 11 Sep 2025 16:05:46 +0000 Subject: [PATCH 5/9] rename to freezer tail block Signed-off-by: jsvisa --- core/blockchain.go | 6 +++--- triedb/database.go | 7 +++---- triedb/pathdb/database.go | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 8d64d5bc7a..4e1bf2b317 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2820,7 +2820,7 @@ func (bc *BlockChain) StateSizer() *state.SizeTracker { return bc.stateSizer } -// FirstStateBlock returns the first available state block number that is stored in the database. -func (bc *BlockChain) FirstStateBlock() (uint64, error) { - return bc.triedb.FirstStateBlock() +// FrezzerTailBlock returns the block number of the oldest state in the freezer. +func (bc *BlockChain) FreezerTailBlock() (uint64, error) { + return bc.triedb.FreezerTailBlock() } diff --git a/triedb/database.go b/triedb/database.go index 2c12a8abce..62d66be2e8 100644 --- a/triedb/database.go +++ b/triedb/database.go @@ -385,12 +385,11 @@ func (db *Database) SnapshotCompleted() bool { return pdb.SnapshotCompleted() } -// FirstStateBlock returns the first available state block number that is stored in the database. -func (db *Database) FirstStateBlock() (uint64, error) { +// 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 { - // Ignore in hash scheme return 0, nil } - return pdb.FirstStateBlock() + return pdb.FrezzerTailBlock() } diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index 08cd6cc045..40fe19f566 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -693,8 +693,8 @@ func (db *Database) SnapshotCompleted() bool { return db.tree.bottom().genComplete() } -// FirstStateBlock returns the block number of the oldest state in the freezer. -func (db *Database) FirstStateBlock() (uint64, error) { +// FrezzerTailBlock returns the block number of the oldest state in the freezer. +func (db *Database) FrezzerTailBlock() (uint64, error) { freezer := db.stateFreezer if freezer == nil { return 0, errors.New("freezer is not available") From aff6811e0948a2916d3dbedb4f737890f611bd15 Mon Sep 17 00:00:00 2001 From: jsvisa Date: Fri, 12 Sep 2025 01:54:36 +0000 Subject: [PATCH 6/9] fix: use the bottom layer's block instead Signed-off-by: jsvisa --- triedb/pathdb/database.go | 6 +++--- triedb/pathdb/disklayer.go | 21 ++++++++++++++------- triedb/pathdb/generate.go | 2 +- triedb/pathdb/journal.go | 31 ++++++++++++++++++++++++++----- triedb/pathdb/layertree_test.go | 2 +- 5 files changed, 45 insertions(+), 17 deletions(-) diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index 40fe19f566..53eacc5861 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -697,7 +697,7 @@ func (db *Database) SnapshotCompleted() bool { func (db *Database) FrezzerTailBlock() (uint64, error) { freezer := db.stateFreezer if freezer == nil { - return 0, errors.New("freezer is not available") + return db.tree.bottom().blockNumber(), nil } tailID, err := freezer.Tail() @@ -705,9 +705,9 @@ func (db *Database) FrezzerTailBlock() (uint64, error) { return 0, err } - // No state has been persistent, return the genesis block number. + // No state has been persistent, get the block number from in-memory state. if tailID == 0 { - return 0, nil + return db.tree.bottom().blockNumber(), nil } blob := rawdb.ReadStateHistoryMeta(freezer, tailID+1) diff --git a/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go index 2042e91611..1f83ae88a5 100644 --- a/triedb/pathdb/disklayer.go +++ b/triedb/pathdb/disklayer.go @@ -31,9 +31,10 @@ import ( // diskLayer is a low level persistent layer built on top of a key-value store. type diskLayer struct { - root common.Hash // Immutable, root hash to which this layer was made for - id uint64 // Immutable, corresponding state id - db *Database // Path-based trie database + root common.Hash // Immutable, root hash to which this layer was made for + id uint64 // Immutable, corresponding state id + block uint64 // Immutable, associated block number + db *Database // Path-based trie database // These two caches must be maintained separately, because the key // for the root node of the storage trie (accountHash) is identical @@ -54,7 +55,7 @@ type diskLayer struct { } // newDiskLayer creates a new disk layer based on the passing arguments. -func newDiskLayer(root common.Hash, id uint64, db *Database, nodes *fastcache.Cache, states *fastcache.Cache, buffer *buffer, frozen *buffer) *diskLayer { +func newDiskLayer(root common.Hash, id uint64, block uint64, db *Database, nodes *fastcache.Cache, states *fastcache.Cache, buffer *buffer, frozen *buffer) *diskLayer { // Initialize the clean caches if the memory allowance is not zero // or reuse the provided caches if they are not nil (inherited from // the original disk layer). @@ -67,6 +68,7 @@ func newDiskLayer(root common.Hash, id uint64, db *Database, nodes *fastcache.Ca return &diskLayer{ root: root, id: id, + block: block, db: db, nodes: nodes, states: states, @@ -85,6 +87,11 @@ func (dl *diskLayer) stateID() uint64 { return dl.id } +// blockNumber returns the associated block number of disk layer. +func (dl *diskLayer) blockNumber() uint64 { + return dl.block +} + // parentLayer implements the layer interface, returning nil as there's no layer // below the disk. func (dl *diskLayer) parentLayer() layer { @@ -471,7 +478,7 @@ func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) { combined = newBuffer(dl.db.config.WriteBufferSize, nil, nil, 0) } // Link the generator if snapshot is not yet completed - ndl := newDiskLayer(bottom.root, bottom.stateID(), dl.db, dl.nodes, dl.states, combined, dl.frozen) + ndl := newDiskLayer(bottom.root, bottom.stateID(), bottom.block, dl.db, dl.nodes, dl.states, combined, dl.frozen) if dl.generator != nil { ndl.setGenerator(dl.generator) } @@ -520,7 +527,7 @@ func (dl *diskLayer) revert(h *stateHistory) (*diskLayer, error) { if err != nil { return nil, err } - ndl := newDiskLayer(h.meta.parent, dl.id-1, dl.db, dl.nodes, dl.states, dl.buffer, dl.frozen) + ndl := newDiskLayer(h.meta.parent, dl.id-1, h.meta.block-1, dl.db, dl.nodes, dl.states, dl.buffer, dl.frozen) // Link the generator if it exists if dl.generator != nil { @@ -559,7 +566,7 @@ func (dl *diskLayer) revert(h *stateHistory) (*diskLayer, error) { } // Link the generator and resume generation if the snapshot is not yet // fully completed. - ndl := newDiskLayer(h.meta.parent, dl.id-1, dl.db, dl.nodes, dl.states, dl.buffer, dl.frozen) + ndl := newDiskLayer(h.meta.parent, dl.id-1, h.meta.block-1, dl.db, dl.nodes, dl.states, dl.buffer, dl.frozen) if dl.generator != nil && !dl.generator.completed() { ndl.generator = dl.generator ndl.generator.run(h.meta.parent) diff --git a/triedb/pathdb/generate.go b/triedb/pathdb/generate.go index 2efbbbb4e1..f17988076e 100644 --- a/triedb/pathdb/generate.go +++ b/triedb/pathdb/generate.go @@ -186,7 +186,7 @@ func generateSnapshot(triedb *Database, root common.Hash, noBuild bool) *diskLay stats = &generatorStats{start: time.Now()} genMarker = []byte{} // Initialized but empty! ) - dl := newDiskLayer(root, 0, triedb, nil, nil, newBuffer(triedb.config.WriteBufferSize, nil, nil, 0), nil) + dl := newDiskLayer(root, 0, 0, triedb, nil, nil, newBuffer(triedb.config.WriteBufferSize, nil, nil, 0), nil) dl.setGenerator(newGenerator(triedb.diskdb, noBuild, genMarker, stats)) if !noBuild { diff --git a/triedb/pathdb/journal.go b/triedb/pathdb/journal.go index 02bdef5d34..cb75ac4f1a 100644 --- a/triedb/pathdb/journal.go +++ b/triedb/pathdb/journal.go @@ -177,7 +177,19 @@ func (db *Database) loadLayers() layer { log.Info("Failed to load journal, discard it", "err", err) } // Return single layer with persistent state. - return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, nil, newBuffer(db.config.WriteBufferSize, nil, nil, 0), nil) + stateID := rawdb.ReadPersistentStateID(db.diskdb) + var block uint64 + if stateID > 0 { + // Try to get block number from state history metadata + blob := rawdb.ReadStateHistoryMeta(db.diskdb, stateID) + if blob != nil { + var m meta + if err := m.decode(blob); err == nil { + block = m.block + } + } + } + return newDiskLayer(root, stateID, block, db, nil, nil, newBuffer(db.config.WriteBufferSize, nil, nil, 0), nil) } // loadDiskLayer reads the binary blob from the layer journal, reconstructing @@ -195,6 +207,11 @@ func (db *Database) loadDiskLayer(r *rlp.Stream) (layer, error) { if err := r.Decode(&id); err != nil { return nil, fmt.Errorf("load state id: %v", err) } + // Resolve the block number of disk layer + var block uint64 + if err := r.Decode(&block); err != nil { + return nil, fmt.Errorf("load block number: %v", err) + } stored := rawdb.ReadPersistentStateID(db.diskdb) if stored > id { return nil, fmt.Errorf("invalid state id: stored %d resolved %d", stored, id) @@ -209,7 +226,7 @@ func (db *Database) loadDiskLayer(r *rlp.Stream) (layer, error) { if err := states.decode(r); err != nil { return nil, err } - return newDiskLayer(root, id, db, nil, nil, newBuffer(db.config.WriteBufferSize, &nodes, &states, id-stored), nil), nil + return newDiskLayer(root, id, block, db, nil, nil, newBuffer(db.config.WriteBufferSize, &nodes, &states, id-stored), nil), nil } // loadDiffLayer reads the next sections of a layer journal, reconstructing a new @@ -259,15 +276,19 @@ func (dl *diskLayer) journal(w io.Writer) error { if err := rlp.Encode(w, dl.id); err != nil { return err } - // Step three, write the accumulated trie nodes into the journal + // Step three, write the corresponding block number into the journal + if err := rlp.Encode(w, dl.block); err != nil { + return err + } + // Step four, write the accumulated trie nodes into the journal if err := dl.buffer.nodes.encode(w); err != nil { return err } - // Step four, write the accumulated flat states into the journal + // Step five, write the accumulated flat states into the journal if err := dl.buffer.states.encode(w); err != nil { return err } - log.Debug("Journaled pathdb disk layer", "root", dl.root, "id", dl.id) + log.Debug("Journaled pathdb disk layer", "root", dl.root, "id", dl.id, "block", dl.block) return nil } diff --git a/triedb/pathdb/layertree_test.go b/triedb/pathdb/layertree_test.go index a74c6eb045..d74a51f0a6 100644 --- a/triedb/pathdb/layertree_test.go +++ b/triedb/pathdb/layertree_test.go @@ -26,7 +26,7 @@ import ( func newTestLayerTree() *layerTree { db := New(rawdb.NewMemoryDatabase(), nil, false) - l := newDiskLayer(common.Hash{0x1}, 0, db, nil, nil, newBuffer(0, nil, nil, 0), nil) + l := newDiskLayer(common.Hash{0x1}, 0, 0, db, nil, nil, newBuffer(0, nil, nil, 0), nil) t := newLayerTree(l) return t } From 131e89f7252fba266f6e8f540515573e8bd0afad Mon Sep 17 00:00:00 2001 From: jsvisa Date: Fri, 12 Sep 2025 04:36:07 +0000 Subject: [PATCH 7/9] Revert "fix: use the bottom layer's block instead" This reverts commit aff6811e0948a2916d3dbedb4f737890f611bd15. Signed-off-by: jsvisa --- triedb/pathdb/database.go | 6 +++--- triedb/pathdb/disklayer.go | 21 +++++++-------------- triedb/pathdb/generate.go | 2 +- triedb/pathdb/journal.go | 31 +++++-------------------------- triedb/pathdb/layertree_test.go | 2 +- 5 files changed, 17 insertions(+), 45 deletions(-) diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index 53eacc5861..40fe19f566 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -697,7 +697,7 @@ func (db *Database) SnapshotCompleted() bool { func (db *Database) FrezzerTailBlock() (uint64, error) { freezer := db.stateFreezer if freezer == nil { - return db.tree.bottom().blockNumber(), nil + return 0, errors.New("freezer is not available") } tailID, err := freezer.Tail() @@ -705,9 +705,9 @@ func (db *Database) FrezzerTailBlock() (uint64, error) { return 0, err } - // No state has been persistent, get the block number from in-memory state. + // No state has been persistent, return the genesis block number. if tailID == 0 { - return db.tree.bottom().blockNumber(), nil + return 0, nil } blob := rawdb.ReadStateHistoryMeta(freezer, tailID+1) diff --git a/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go index 1f83ae88a5..2042e91611 100644 --- a/triedb/pathdb/disklayer.go +++ b/triedb/pathdb/disklayer.go @@ -31,10 +31,9 @@ import ( // diskLayer is a low level persistent layer built on top of a key-value store. type diskLayer struct { - root common.Hash // Immutable, root hash to which this layer was made for - id uint64 // Immutable, corresponding state id - block uint64 // Immutable, associated block number - db *Database // Path-based trie database + root common.Hash // Immutable, root hash to which this layer was made for + id uint64 // Immutable, corresponding state id + db *Database // Path-based trie database // These two caches must be maintained separately, because the key // for the root node of the storage trie (accountHash) is identical @@ -55,7 +54,7 @@ type diskLayer struct { } // newDiskLayer creates a new disk layer based on the passing arguments. -func newDiskLayer(root common.Hash, id uint64, block uint64, db *Database, nodes *fastcache.Cache, states *fastcache.Cache, buffer *buffer, frozen *buffer) *diskLayer { +func newDiskLayer(root common.Hash, id uint64, db *Database, nodes *fastcache.Cache, states *fastcache.Cache, buffer *buffer, frozen *buffer) *diskLayer { // Initialize the clean caches if the memory allowance is not zero // or reuse the provided caches if they are not nil (inherited from // the original disk layer). @@ -68,7 +67,6 @@ func newDiskLayer(root common.Hash, id uint64, block uint64, db *Database, nodes return &diskLayer{ root: root, id: id, - block: block, db: db, nodes: nodes, states: states, @@ -87,11 +85,6 @@ func (dl *diskLayer) stateID() uint64 { return dl.id } -// blockNumber returns the associated block number of disk layer. -func (dl *diskLayer) blockNumber() uint64 { - return dl.block -} - // parentLayer implements the layer interface, returning nil as there's no layer // below the disk. func (dl *diskLayer) parentLayer() layer { @@ -478,7 +471,7 @@ func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) { combined = newBuffer(dl.db.config.WriteBufferSize, nil, nil, 0) } // Link the generator if snapshot is not yet completed - ndl := newDiskLayer(bottom.root, bottom.stateID(), bottom.block, dl.db, dl.nodes, dl.states, combined, dl.frozen) + ndl := newDiskLayer(bottom.root, bottom.stateID(), dl.db, dl.nodes, dl.states, combined, dl.frozen) if dl.generator != nil { ndl.setGenerator(dl.generator) } @@ -527,7 +520,7 @@ func (dl *diskLayer) revert(h *stateHistory) (*diskLayer, error) { if err != nil { return nil, err } - ndl := newDiskLayer(h.meta.parent, dl.id-1, h.meta.block-1, dl.db, dl.nodes, dl.states, dl.buffer, dl.frozen) + ndl := newDiskLayer(h.meta.parent, dl.id-1, dl.db, dl.nodes, dl.states, dl.buffer, dl.frozen) // Link the generator if it exists if dl.generator != nil { @@ -566,7 +559,7 @@ func (dl *diskLayer) revert(h *stateHistory) (*diskLayer, error) { } // Link the generator and resume generation if the snapshot is not yet // fully completed. - ndl := newDiskLayer(h.meta.parent, dl.id-1, h.meta.block-1, dl.db, dl.nodes, dl.states, dl.buffer, dl.frozen) + ndl := newDiskLayer(h.meta.parent, dl.id-1, dl.db, dl.nodes, dl.states, dl.buffer, dl.frozen) if dl.generator != nil && !dl.generator.completed() { ndl.generator = dl.generator ndl.generator.run(h.meta.parent) diff --git a/triedb/pathdb/generate.go b/triedb/pathdb/generate.go index f17988076e..2efbbbb4e1 100644 --- a/triedb/pathdb/generate.go +++ b/triedb/pathdb/generate.go @@ -186,7 +186,7 @@ func generateSnapshot(triedb *Database, root common.Hash, noBuild bool) *diskLay stats = &generatorStats{start: time.Now()} genMarker = []byte{} // Initialized but empty! ) - dl := newDiskLayer(root, 0, 0, triedb, nil, nil, newBuffer(triedb.config.WriteBufferSize, nil, nil, 0), nil) + dl := newDiskLayer(root, 0, triedb, nil, nil, newBuffer(triedb.config.WriteBufferSize, nil, nil, 0), nil) dl.setGenerator(newGenerator(triedb.diskdb, noBuild, genMarker, stats)) if !noBuild { diff --git a/triedb/pathdb/journal.go b/triedb/pathdb/journal.go index cb75ac4f1a..02bdef5d34 100644 --- a/triedb/pathdb/journal.go +++ b/triedb/pathdb/journal.go @@ -177,19 +177,7 @@ func (db *Database) loadLayers() layer { log.Info("Failed to load journal, discard it", "err", err) } // Return single layer with persistent state. - stateID := rawdb.ReadPersistentStateID(db.diskdb) - var block uint64 - if stateID > 0 { - // Try to get block number from state history metadata - blob := rawdb.ReadStateHistoryMeta(db.diskdb, stateID) - if blob != nil { - var m meta - if err := m.decode(blob); err == nil { - block = m.block - } - } - } - return newDiskLayer(root, stateID, block, db, nil, nil, newBuffer(db.config.WriteBufferSize, nil, nil, 0), nil) + return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, nil, newBuffer(db.config.WriteBufferSize, nil, nil, 0), nil) } // loadDiskLayer reads the binary blob from the layer journal, reconstructing @@ -207,11 +195,6 @@ func (db *Database) loadDiskLayer(r *rlp.Stream) (layer, error) { if err := r.Decode(&id); err != nil { return nil, fmt.Errorf("load state id: %v", err) } - // Resolve the block number of disk layer - var block uint64 - if err := r.Decode(&block); err != nil { - return nil, fmt.Errorf("load block number: %v", err) - } stored := rawdb.ReadPersistentStateID(db.diskdb) if stored > id { return nil, fmt.Errorf("invalid state id: stored %d resolved %d", stored, id) @@ -226,7 +209,7 @@ func (db *Database) loadDiskLayer(r *rlp.Stream) (layer, error) { if err := states.decode(r); err != nil { return nil, err } - return newDiskLayer(root, id, block, db, nil, nil, newBuffer(db.config.WriteBufferSize, &nodes, &states, id-stored), nil), nil + return newDiskLayer(root, id, db, nil, nil, newBuffer(db.config.WriteBufferSize, &nodes, &states, id-stored), nil), nil } // loadDiffLayer reads the next sections of a layer journal, reconstructing a new @@ -276,19 +259,15 @@ func (dl *diskLayer) journal(w io.Writer) error { if err := rlp.Encode(w, dl.id); err != nil { return err } - // Step three, write the corresponding block number into the journal - if err := rlp.Encode(w, dl.block); err != nil { - return err - } - // Step four, write the accumulated trie nodes into the journal + // Step three, write the accumulated trie nodes into the journal if err := dl.buffer.nodes.encode(w); err != nil { return err } - // Step five, write the accumulated flat states into the journal + // Step four, write the accumulated flat states into the journal if err := dl.buffer.states.encode(w); err != nil { return err } - log.Debug("Journaled pathdb disk layer", "root", dl.root, "id", dl.id, "block", dl.block) + log.Debug("Journaled pathdb disk layer", "root", dl.root, "id", dl.id) return nil } diff --git a/triedb/pathdb/layertree_test.go b/triedb/pathdb/layertree_test.go index d74a51f0a6..a74c6eb045 100644 --- a/triedb/pathdb/layertree_test.go +++ b/triedb/pathdb/layertree_test.go @@ -26,7 +26,7 @@ import ( func newTestLayerTree() *layerTree { db := New(rawdb.NewMemoryDatabase(), nil, false) - l := newDiskLayer(common.Hash{0x1}, 0, 0, db, nil, nil, newBuffer(0, nil, nil, 0), nil) + l := newDiskLayer(common.Hash{0x1}, 0, db, nil, nil, newBuffer(0, nil, nil, 0), nil) t := newLayerTree(l) return t } From be7f19a5d6f362751ac6a3bcf98acf6976c118af Mon Sep 17 00:00:00 2001 From: jsvisa Date: Fri, 12 Sep 2025 04:38:22 +0000 Subject: [PATCH 8/9] name Signed-off-by: jsvisa --- eth/api_backend.go | 6 +++--- triedb/database.go | 2 +- triedb/pathdb/database.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eth/api_backend.go b/eth/api_backend.go index d830dc4a6f..45ad31675a 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -63,12 +63,12 @@ func (b *EthAPIBackend) CurrentBlock() *types.Header { } func (b *EthAPIBackend) SetHead(number uint64) error { - firstStateBlock, err := b.eth.blockchain.FirstStateBlock() + tailBlock, err := b.eth.blockchain.FreezerTailBlock() if err != nil { return err } - if number < firstStateBlock { - return fmt.Errorf("cannot rewind to block %d, oldest available state is at block %d", number, firstStateBlock) + 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() return b.eth.blockchain.SetHead(number) diff --git a/triedb/database.go b/triedb/database.go index 62d66be2e8..044019e020 100644 --- a/triedb/database.go +++ b/triedb/database.go @@ -391,5 +391,5 @@ func (db *Database) FreezerTailBlock() (uint64, error) { if !ok { return 0, nil } - return pdb.FrezzerTailBlock() + return pdb.FreezerTailBlock() } diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index 40fe19f566..a5f7f33650 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -693,8 +693,8 @@ func (db *Database) SnapshotCompleted() bool { return db.tree.bottom().genComplete() } -// FrezzerTailBlock returns the block number of the oldest state in the freezer. -func (db *Database) FrezzerTailBlock() (uint64, error) { +// 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") From f6618ad85d99dbc08996b1cefe4fc745deb78607 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 26 Nov 2025 16:25:42 +0100 Subject: [PATCH 9/9] Update blockchain.go --- core/blockchain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/blockchain.go b/core/blockchain.go index 4e1bf2b317..60c89f4ae0 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2820,7 +2820,7 @@ func (bc *BlockChain) StateSizer() *state.SizeTracker { return bc.stateSizer } -// FrezzerTailBlock returns the block number of the oldest state in the freezer. +// FreezerTailBlock returns the block number of the oldest state in the freezer. func (bc *BlockChain) FreezerTailBlock() (uint64, error) { return bc.triedb.FreezerTailBlock() }