mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-11 20:20:05 +00:00
c79ec31c17
There is a double lock race condition when NewSlot acquires the checkpoints lock first and the nodes lock later, while calls to Head() acquire the nodeslock first and the checkpoints lock later. This PR releases the checkpoints lock in NewSlot, to reaquire it later in updateUnrealizedCheckpoints after getting the nodes lock
76 lines
2.8 KiB
Go
76 lines
2.8 KiB
Go
package protoarray
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/prysmaticlabs/prysm/config/features"
|
|
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
|
|
"github.com/prysmaticlabs/prysm/time/slots"
|
|
)
|
|
|
|
// NewSlot mimics the implementation of `on_tick` in fork choice consensus spec.
|
|
// It resets the proposer boost root in fork choice, and it updates store's justified checkpoint
|
|
// if a better checkpoint on the store's finalized checkpoint chain.
|
|
// This should only be called at the start of every slot interval.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// # Reset store.proposer_boost_root if this is a new slot
|
|
// if current_slot > previous_slot:
|
|
// store.proposer_boost_root = Root()
|
|
//
|
|
// # Not a new epoch, return
|
|
// if not (current_slot > previous_slot and compute_slots_since_epoch_start(current_slot) == 0):
|
|
// return
|
|
//
|
|
// # Update store.justified_checkpoint if a better checkpoint on the store.finalized_checkpoint chain
|
|
// if store.best_justified_checkpoint.epoch > store.justified_checkpoint.epoch:
|
|
// finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
|
|
// ancestor_at_finalized_slot = get_ancestor(store, store.best_justified_checkpoint.root, finalized_slot)
|
|
// if ancestor_at_finalized_slot == store.finalized_checkpoint.root:
|
|
// store.justified_checkpoint = store.best_justified_checkpoint
|
|
func (f *ForkChoice) NewSlot(ctx context.Context, slot types.Slot) error {
|
|
// Reset proposer boost root
|
|
if err := f.ResetBoostedProposerRoot(ctx); err != nil {
|
|
return errors.Wrap(err, "could not reset boosted proposer root in fork choice")
|
|
}
|
|
|
|
// Return if it's not a new epoch.
|
|
if !slots.IsEpochStart(slot) {
|
|
return nil
|
|
}
|
|
|
|
// Update store.justified_checkpoint if a better checkpoint on the store.finalized_checkpoint chain
|
|
f.store.checkpointsLock.Lock()
|
|
|
|
bjcp := f.store.bestJustifiedCheckpoint
|
|
jcp := f.store.justifiedCheckpoint
|
|
fcp := f.store.finalizedCheckpoint
|
|
if bjcp.Epoch > jcp.Epoch {
|
|
finalizedSlot, err := slots.EpochStart(fcp.Epoch)
|
|
if err != nil {
|
|
f.store.checkpointsLock.Unlock()
|
|
return err
|
|
}
|
|
|
|
// We check that the best justified checkpoint is a descendant of the finalized checkpoint.
|
|
// This should always happen as forkchoice enforces that every node is a descendant of the
|
|
// finalized checkpoint. This check is here for additional security, consider removing the extra
|
|
// loop call here.
|
|
r, err := f.AncestorRoot(ctx, bjcp.Root, finalizedSlot)
|
|
if err != nil {
|
|
f.store.checkpointsLock.Unlock()
|
|
return err
|
|
}
|
|
if r == fcp.Root {
|
|
f.store.prevJustifiedCheckpoint = jcp
|
|
f.store.justifiedCheckpoint = bjcp
|
|
}
|
|
}
|
|
f.store.checkpointsLock.Unlock()
|
|
if !features.Get().DisablePullTips {
|
|
f.updateUnrealizedCheckpoints()
|
|
}
|
|
return nil
|
|
}
|