mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-12 01:41:36 +00:00
When gob.Decode fails in LoadTransitionState, the function previously
returned nil, which triggered the fallback path that creates a fresh
TransitionState{Ended: isVerkle}. On a verkle-enabled node, this would
incorrectly mark the transition as complete, potentially causing the
node to use BinaryTrie for a partially-transitioned state.
Instead of returning nil, return a non-nil TransitionState with both
Started and Ended set to false. This ensures:
- InTransition() returns false (no partial transition in progress)
- Transitioned() returns false (transition has not completed)
- The fallback path is not triggered, preventing incorrect state
The root hash is also logged in the error message to aid debugging
corruption incidents.
112 lines
3.9 KiB
Go
112 lines
3.9 KiB
Go
// Copyright 2025 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package overlay
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/gob"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
|
"github.com/ethereum/go-ethereum/ethdb"
|
|
"github.com/ethereum/go-ethereum/log"
|
|
)
|
|
|
|
// TransitionState is a structure that holds the progress markers of the
|
|
// translation process.
|
|
type TransitionState struct {
|
|
CurrentAccountAddress *common.Address // addresss of the last translated account
|
|
CurrentSlotHash common.Hash // hash of the last translated storage slot
|
|
CurrentPreimageOffset int64 // next byte to read from the preimage file
|
|
Started, Ended bool
|
|
|
|
// Mark whether the storage for an account has been processed. This is useful if the
|
|
// maximum number of leaves of the conversion is reached before the whole storage is
|
|
// processed.
|
|
StorageProcessed bool
|
|
|
|
BaseRoot common.Hash // hash of the last read-only MPT base tree
|
|
}
|
|
|
|
// InTransition returns true if the translation process is in progress.
|
|
func (ts *TransitionState) InTransition() bool {
|
|
return ts != nil && ts.Started && !ts.Ended
|
|
}
|
|
|
|
// Transitioned returns true if the translation process has been completed.
|
|
func (ts *TransitionState) Transitioned() bool {
|
|
return ts != nil && ts.Ended
|
|
}
|
|
|
|
// Copy returns a deep copy of the TransitionState object.
|
|
func (ts *TransitionState) Copy() *TransitionState {
|
|
ret := &TransitionState{
|
|
Started: ts.Started,
|
|
Ended: ts.Ended,
|
|
CurrentSlotHash: ts.CurrentSlotHash,
|
|
CurrentPreimageOffset: ts.CurrentPreimageOffset,
|
|
StorageProcessed: ts.StorageProcessed,
|
|
BaseRoot: ts.BaseRoot,
|
|
}
|
|
if ts.CurrentAccountAddress != nil {
|
|
addr := *ts.CurrentAccountAddress
|
|
ret.CurrentAccountAddress = &addr
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// LoadTransitionState retrieves the Verkle transition state associated with
|
|
// the given state root hash from the database.
|
|
func LoadTransitionState(db ethdb.KeyValueReader, root common.Hash, isVerkle bool) *TransitionState {
|
|
var ts *TransitionState
|
|
|
|
data, _ := rawdb.ReadVerkleTransitionState(db, root)
|
|
|
|
// if a state could be read from the db, attempt to decode it
|
|
if len(data) > 0 {
|
|
var (
|
|
newts TransitionState
|
|
buf = bytes.NewBuffer(data[:])
|
|
dec = gob.NewDecoder(buf)
|
|
)
|
|
// Decode transition state
|
|
err := dec.Decode(&newts)
|
|
if err != nil {
|
|
log.Error("failed to decode transition state", "root", root, "err", err)
|
|
// Corrupted transition state data must not silently restart
|
|
// the transition via the fallback path. Return an ended state
|
|
// rather than nil to avoid triggering the fresh-start fallback,
|
|
// which would incorrectly mark the transition as complete when
|
|
// the on-disk data was merely corrupted.
|
|
ts = &TransitionState{Ended: false, Started: false}
|
|
return ts
|
|
}
|
|
ts = &newts
|
|
}
|
|
|
|
// Fallback that should only happen before the transition
|
|
if ts == nil {
|
|
// Initialize the first transition state, with the "ended"
|
|
// field set to true if the database was created
|
|
// as a verkle database.
|
|
log.Debug("no transition state found, starting fresh", "verkle", isVerkle)
|
|
|
|
// Start with a fresh state
|
|
ts = &TransitionState{Ended: isVerkle}
|
|
}
|
|
return ts
|
|
}
|