mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
triedb, core: limit the chain rewinding
This commit is contained in:
parent
10840515a2
commit
cfacd19ce7
3 changed files with 73 additions and 15 deletions
|
|
@ -653,31 +653,55 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
||||||
newHeadBlock = bc.genesisBlock
|
newHeadBlock = bc.genesisBlock
|
||||||
} else {
|
} else {
|
||||||
// Block exists. Keep rewinding until either we find one with state
|
// Block exists. Keep rewinding until either we find one with state
|
||||||
// or until we exceed the optional threshold root hash
|
// or until we exceed the optional threshold root hash. The chain
|
||||||
beyondRoot := (root == common.Hash{}) // Flag whether we're beyond the requested root (no root, always true)
|
// will be reset to genesis if rewinding exceeds the maximum allowed
|
||||||
|
// depth.
|
||||||
|
var (
|
||||||
|
limit uint64 // The oldest block that will be searched for this rewinding
|
||||||
|
beyondRoot = root == common.Hash{} // Flag whether we're beyond the requested root (no root, always true)
|
||||||
|
)
|
||||||
|
if currentBlock.Number.Uint64() > params.FullImmutabilityThreshold {
|
||||||
|
limit = currentBlock.Number.Uint64() - params.FullImmutabilityThreshold
|
||||||
|
}
|
||||||
|
if oldest, err := bc.triedb.OldestState(); err != nil && oldest < limit {
|
||||||
|
limit = oldest
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
// If a root threshold was requested but not yet crossed, check
|
// If a root threshold was requested but not yet crossed, check
|
||||||
if root != (common.Hash{}) && !beyondRoot && newHeadBlock.Root() == root {
|
if !beyondRoot && newHeadBlock.Root() == root {
|
||||||
beyondRoot, rootNumber = true, newHeadBlock.NumberU64()
|
beyondRoot, rootNumber = true, newHeadBlock.NumberU64()
|
||||||
}
|
}
|
||||||
|
// If the associated state of new head block is not available
|
||||||
|
// and impossible to be recovered, rewind further.
|
||||||
if !bc.HasState(newHeadBlock.Root()) && !bc.stateRecoverable(newHeadBlock.Root()) {
|
if !bc.HasState(newHeadBlock.Root()) && !bc.stateRecoverable(newHeadBlock.Root()) {
|
||||||
log.Trace("Block state missing, rewinding further", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
|
log.Trace("Block state missing, rewinding further", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
|
||||||
if pivot == nil || newHeadBlock.NumberU64() > *pivot {
|
|
||||||
|
if pivot != nil && newHeadBlock.NumberU64() <= *pivot {
|
||||||
|
// Stop rewinding if the oldest state by snap sync has already
|
||||||
|
// been crossed.
|
||||||
|
log.Trace("Rewind passed pivot, aiming genesis", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash(), "pivot", *pivot)
|
||||||
|
newHeadBlock = bc.genesisBlock
|
||||||
|
} else {
|
||||||
|
// Either the state was synced via a full sync process, or the
|
||||||
|
// oldest state has not been reached yet, continue rewinding
|
||||||
|
// until an available state is found.
|
||||||
parent := bc.GetBlock(newHeadBlock.ParentHash(), newHeadBlock.NumberU64()-1)
|
parent := bc.GetBlock(newHeadBlock.ParentHash(), newHeadBlock.NumberU64()-1)
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
log.Error("Missing block in the middle, aiming genesis", "number", newHeadBlock.NumberU64()-1, "hash", newHeadBlock.ParentHash())
|
log.Error("Missing block in the middle, aiming genesis", "number", newHeadBlock.NumberU64()-1, "hash", newHeadBlock.ParentHash())
|
||||||
newHeadBlock = bc.genesisBlock
|
newHeadBlock = bc.genesisBlock
|
||||||
} else {
|
} else {
|
||||||
newHeadBlock = parent
|
newHeadBlock = parent
|
||||||
if newHeadBlock.NumberU64() != 0 {
|
if newHeadBlock.NumberU64() != 0 && newHeadBlock.NumberU64() >= limit {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Info("Genesis block is reached", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
|
if newHeadBlock.NumberU64() == 0 {
|
||||||
|
log.Info("Genesis block is reached", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
|
||||||
|
}
|
||||||
|
if newHeadBlock.NumberU64() < limit {
|
||||||
|
newHeadBlock = bc.genesisBlock
|
||||||
|
log.Info("Rewinding limit is reached, aiming genesis", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
log.Trace("Rewind passed pivot, aiming genesis", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash(), "pivot", *pivot)
|
|
||||||
newHeadBlock = bc.genesisBlock
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if beyondRoot || newHeadBlock.NumberU64() == 0 {
|
if beyondRoot || newHeadBlock.NumberU64() == 0 {
|
||||||
|
|
@ -704,12 +728,20 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
||||||
bc.currentBlock.Store(newHeadBlock.Header())
|
bc.currentBlock.Store(newHeadBlock.Header())
|
||||||
headBlockGauge.Update(int64(newHeadBlock.NumberU64()))
|
headBlockGauge.Update(int64(newHeadBlock.NumberU64()))
|
||||||
|
|
||||||
// The head state is missing, which is only possible in the path-based
|
// The associated head state is missing, which might occur if chain head
|
||||||
// scheme. This situation occurs when the chain head is rewound below
|
// is rewound even below the oldest available state. The whole chain is
|
||||||
// the pivot point. In this scenario, there is no possible recovery
|
// reset to genesis in case.
|
||||||
// approach except for rerunning a snap sync. Do nothing here until the
|
//
|
||||||
// state syncer picks it up.
|
// In this scenario, there is no possible recovery approach except for
|
||||||
|
// rerunning a snap sync. Do nothing here until the state syncer picks
|
||||||
|
// it up.
|
||||||
if !bc.HasState(newHeadBlock.Root()) {
|
if !bc.HasState(newHeadBlock.Root()) {
|
||||||
|
// Disable snapshot repairing explicitly, as a new snap sync
|
||||||
|
// is expected and the whole snapshot needs to be rebuilt.
|
||||||
|
rootNumber = 0
|
||||||
|
if newHeadBlock.NumberU64() != 0 {
|
||||||
|
log.Crit("Chain is stateless at a non-genesis block")
|
||||||
|
}
|
||||||
log.Info("Chain is stateless, wait state sync", "number", newHeadBlock.Number(), "hash", newHeadBlock.Hash())
|
log.Info("Chain is stateless, wait state sync", "number", newHeadBlock.Number(), "hash", newHeadBlock.Hash())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -332,6 +332,15 @@ func (db *Database) SetBufferSize(size int) error {
|
||||||
return pdb.SetBufferSize(size)
|
return pdb.SetBufferSize(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OldestState returns the oldest state maintained by database.
|
||||||
|
func (db *Database) OldestState() (uint64, error) {
|
||||||
|
pdb, ok := db.backend.(*pathdb.Database)
|
||||||
|
if !ok {
|
||||||
|
return 0, errors.New("not supported")
|
||||||
|
}
|
||||||
|
return pdb.OldestState()
|
||||||
|
}
|
||||||
|
|
||||||
// IsVerkle returns the indicator if the database is holding a verkle tree.
|
// IsVerkle returns the indicator if the database is holding a verkle tree.
|
||||||
func (db *Database) IsVerkle() bool {
|
func (db *Database) IsVerkle() bool {
|
||||||
return db.config.IsVerkle
|
return db.config.IsVerkle
|
||||||
|
|
|
||||||
|
|
@ -474,6 +474,23 @@ func (db *Database) Scheme() string {
|
||||||
return rawdb.PathScheme
|
return rawdb.PathScheme
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OldestState returns the oldest state available in the database, no matter
|
||||||
|
// it's available or recoverable.
|
||||||
|
func (db *Database) OldestState() (uint64, error) {
|
||||||
|
if db.freezer == nil {
|
||||||
|
return 0, errors.New("state rollback is non-supported")
|
||||||
|
}
|
||||||
|
tail, err := db.freezer.Tail()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
h, err := readHistory(db.freezer, tail+1)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return h.meta.block, nil
|
||||||
|
}
|
||||||
|
|
||||||
// modifyAllowed returns the indicator if mutation is allowed. This function
|
// modifyAllowed returns the indicator if mutation is allowed. This function
|
||||||
// assumes the db.lock is already held.
|
// assumes the db.lock is already held.
|
||||||
func (db *Database) modifyAllowed() error {
|
func (db *Database) modifyAllowed() error {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue