mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 21:08:10 -05:00
<!-- Thanks for sending a PR! Before submitting: 1. If this is your first PR, check out our contribution guide here https://docs.prylabs.network/docs/contribute/contribution-guidelines You will then need to sign our Contributor License Agreement (CLA), which will show up as a comment from a bot in this pull request after you open it. We cannot review code without a signed CLA. 2. Please file an associated tracking issue if this pull request is non-trivial and requires context for our team to understand. All features and most bug fixes should have an associated issue with a design discussed and decided upon. Small bug fixes and documentation improvements don't need issues. 3. New features and bug fixes must have tests. Documentation may need to be updated. If you're unsure what to update, send the PR, and we'll discuss in review. 4. Note that PRs updating dependencies and new Go versions are not accepted. Please file an issue instead. 5. A changelog entry is required for user facing issues. --> **What type of PR is this?** Other **What does this PR do? Why is it needed?** The for loop in MigrateToCold function was brute force in nature. It could be improved by just directly jumping by `slotsPerArchivedPoint` rather than going over every single slot. ``` for slot := oldFSlot; slot < fSlot; slot++ { ... if slot%s.slotsPerArchivedPoint == 0 && slot != 0 { ``` No need to do the modulo for every single slot. We could just find the correct starting point and jump by slotsPerArchivedPoint at a time. **Which issues(s) does this PR fix?** Fixes # **Other notes for review** **Acknowledgements** - [x] I have read [CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md). - [x] I have included a uniquely named [changelog fragment file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd). - [x] I have added a description to this PR with sufficient context for reviewers to understand this PR. --------- Co-authored-by: Potuz <potuz@prysmaticlabs.com>
132 lines
4.0 KiB
Go
132 lines
4.0 KiB
Go
package stategen
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"fmt"
|
|
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
|
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
|
|
"github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// MigrateToCold advances the finalized info in between the cold and hot state sections.
|
|
// It moves the recent finalized states from the hot section to the cold section and
|
|
// only preserves the ones that are on archived point.
|
|
func (s *State) MigrateToCold(ctx context.Context, fRoot [32]byte) error {
|
|
ctx, span := trace.StartSpan(ctx, "stateGen.MigrateToCold")
|
|
defer span.End()
|
|
|
|
// When migrating states we choose to acquire the migration lock before
|
|
// proceeding. This is to prevent multiple migration routines from overwriting each
|
|
// other.
|
|
s.migrationLock.Lock()
|
|
defer s.migrationLock.Unlock()
|
|
|
|
s.finalizedInfo.lock.RLock()
|
|
oldFSlot := s.finalizedInfo.slot
|
|
s.finalizedInfo.lock.RUnlock()
|
|
|
|
fBlock, err := s.beaconDB.Block(ctx, fRoot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fSlot := fBlock.Block().Slot()
|
|
if oldFSlot > fSlot {
|
|
return nil
|
|
}
|
|
|
|
// Calculate the first archived point slot >= oldFSlot (but > 0).
|
|
// This avoids iterating through every slot and only visits archived points directly.
|
|
var startSlot primitives.Slot
|
|
if oldFSlot == 0 {
|
|
startSlot = s.slotsPerArchivedPoint
|
|
} else {
|
|
// Round up to the next archived point
|
|
startSlot = (oldFSlot + s.slotsPerArchivedPoint - 1) / s.slotsPerArchivedPoint * s.slotsPerArchivedPoint
|
|
}
|
|
|
|
// Start at the first archived point after old finalized slot, stop before current finalized slot.
|
|
// Jump directly between archived points.
|
|
for slot := startSlot; slot < fSlot; slot += s.slotsPerArchivedPoint {
|
|
if ctx.Err() != nil {
|
|
return ctx.Err()
|
|
}
|
|
|
|
cached, exists, err := s.epochBoundaryStateCache.getBySlot(slot)
|
|
if err != nil {
|
|
return fmt.Errorf("could not get epoch boundary state for slot %d", slot)
|
|
}
|
|
|
|
var aRoot [32]byte
|
|
var aState state.BeaconState
|
|
|
|
// When the epoch boundary state is not in cache due to skip slot scenario,
|
|
// we have to regenerate the state which will represent epoch boundary.
|
|
// By finding the highest available block below epoch boundary slot, we
|
|
// generate the state for that block root.
|
|
if exists {
|
|
aRoot = cached.root
|
|
aState = cached.state
|
|
} else {
|
|
_, roots, err := s.beaconDB.HighestRootsBelowSlot(ctx, slot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Given the block has been finalized, the db should not have more than one block in a given slot.
|
|
// We should error out when this happens.
|
|
if len(roots) != 1 {
|
|
return errUnknownBlock
|
|
}
|
|
aRoot = roots[0]
|
|
// There's no need to generate the state if the state already exists in the DB.
|
|
// We can skip saving the state.
|
|
if !s.beaconDB.HasState(ctx, aRoot) {
|
|
aState, err = s.StateByRoot(ctx, aRoot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
if s.beaconDB.HasState(ctx, aRoot) {
|
|
// If you are migrating a state and its already part of the hot state cache saved to the db,
|
|
// you can just remove it from the hot state cache as it becomes redundant.
|
|
s.saveHotStateDB.lock.Lock()
|
|
roots := s.saveHotStateDB.blockRootsOfSavedStates
|
|
for i := range roots {
|
|
if aRoot == roots[i] {
|
|
s.saveHotStateDB.blockRootsOfSavedStates = append(roots[:i], roots[i+1:]...)
|
|
// There shouldn't be duplicated roots in `blockRootsOfSavedStates`.
|
|
// Break here is ok.
|
|
break
|
|
}
|
|
}
|
|
s.saveHotStateDB.lock.Unlock()
|
|
continue
|
|
}
|
|
|
|
if err := s.beaconDB.SaveState(ctx, aState, aRoot); err != nil {
|
|
return err
|
|
}
|
|
log.WithFields(
|
|
logrus.Fields{
|
|
"slot": aState.Slot(),
|
|
"root": hex.EncodeToString(bytesutil.Trunc(aRoot[:])),
|
|
}).Info("Saved state in DB")
|
|
}
|
|
|
|
// Update finalized info in memory.
|
|
fInfo, ok, err := s.epochBoundaryStateCache.getByBlockRoot(fRoot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if ok {
|
|
s.SaveFinalizedState(fSlot, fRoot, fInfo.state)
|
|
}
|
|
|
|
return nil
|
|
}
|