2023-01-31 19:17:16 +00:00
|
|
|
package blockchain
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-03-09 01:51:50 +00:00
|
|
|
"fmt"
|
|
|
|
"time"
|
2023-01-31 19:17:16 +00:00
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
2023-03-17 18:52:56 +00:00
|
|
|
doublylinkedtree "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/doubly-linked-tree"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/config/features"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/config/params"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
2023-03-09 01:51:50 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2023-04-25 15:45:08 +00:00
|
|
|
"go.opencensus.io/trace"
|
2023-01-31 19:17:16 +00:00
|
|
|
)
|
|
|
|
|
2023-03-04 23:19:23 +00:00
|
|
|
func (s *Service) isNewProposer(slot primitives.Slot) bool {
|
|
|
|
_, _, ok := s.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(slot, [32]byte{} /* root */)
|
2023-04-13 14:47:13 +00:00
|
|
|
return ok || features.Get().PrepareAllPayloads
|
2023-01-31 19:17:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Service) isNewHead(r [32]byte) bool {
|
|
|
|
s.headLock.RLock()
|
|
|
|
defer s.headLock.RUnlock()
|
|
|
|
|
|
|
|
currentHeadRoot := s.originBlockRoot
|
|
|
|
if s.head != nil {
|
|
|
|
currentHeadRoot = s.headRoot()
|
|
|
|
}
|
|
|
|
|
|
|
|
return r != currentHeadRoot || r == [32]byte{}
|
|
|
|
}
|
|
|
|
|
2023-02-09 09:23:32 +00:00
|
|
|
func (s *Service) getStateAndBlock(ctx context.Context, r [32]byte) (state.BeaconState, interfaces.ReadOnlySignedBeaconBlock, error) {
|
2023-01-31 19:17:16 +00:00
|
|
|
if !s.hasBlockInInitSyncOrDB(ctx, r) {
|
|
|
|
return nil, nil, errors.New("block does not exist")
|
|
|
|
}
|
|
|
|
newHeadBlock, err := s.getBlock(ctx, r)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
headState, err := s.cfg.StateGen.StateByRoot(ctx, r)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return headState, newHeadBlock, nil
|
|
|
|
}
|
|
|
|
|
2023-03-08 12:36:46 +00:00
|
|
|
// fockchoiceUpdateWithExecution is a wrapper around notifyForkchoiceUpdate. It decides whether a new call to FCU should be made.
|
2023-04-27 17:13:58 +00:00
|
|
|
// it returns true if the new head is updated
|
|
|
|
func (s *Service) forkchoiceUpdateWithExecution(ctx context.Context, newHeadRoot [32]byte, proposingSlot primitives.Slot) (bool, error) {
|
2023-04-25 15:45:08 +00:00
|
|
|
_, span := trace.StartSpan(ctx, "beacon-chain.blockchain.forkchoiceUpdateWithExecution")
|
|
|
|
defer span.End()
|
|
|
|
// Note: Use the service context here to avoid the parent context being ended during a forkchoice update.
|
|
|
|
ctx = trace.NewContext(s.ctx, span)
|
|
|
|
|
2023-01-31 19:17:16 +00:00
|
|
|
isNewHead := s.isNewHead(newHeadRoot)
|
2023-03-08 12:36:46 +00:00
|
|
|
if !isNewHead {
|
2023-04-27 17:13:58 +00:00
|
|
|
return false, nil
|
2023-01-31 19:17:16 +00:00
|
|
|
}
|
2023-03-08 12:36:46 +00:00
|
|
|
isNewProposer := s.isNewProposer(proposingSlot)
|
|
|
|
if isNewProposer && !features.Get().DisableReorgLateBlocks {
|
2023-03-09 01:51:50 +00:00
|
|
|
if s.shouldOverrideFCU(newHeadRoot, proposingSlot) {
|
2023-04-27 17:13:58 +00:00
|
|
|
return false, nil
|
2023-03-04 23:19:23 +00:00
|
|
|
}
|
2023-03-08 12:36:46 +00:00
|
|
|
}
|
|
|
|
headState, headBlock, err := s.getStateAndBlock(ctx, newHeadRoot)
|
|
|
|
if err != nil {
|
|
|
|
log.WithError(err).Error("Could not get forkchoice update argument")
|
2023-04-27 17:13:58 +00:00
|
|
|
return false, nil
|
2023-01-31 19:17:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_, err = s.notifyForkchoiceUpdate(ctx, ¬ifyForkchoiceUpdateArg{
|
|
|
|
headState: headState,
|
2023-03-08 12:36:46 +00:00
|
|
|
headRoot: newHeadRoot,
|
2023-01-31 19:17:16 +00:00
|
|
|
headBlock: headBlock.Block(),
|
|
|
|
})
|
|
|
|
if err != nil {
|
2023-04-27 17:13:58 +00:00
|
|
|
return false, errors.Wrap(err, "could not notify forkchoice update")
|
2023-01-31 19:17:16 +00:00
|
|
|
}
|
|
|
|
|
2023-03-08 12:36:46 +00:00
|
|
|
if err := s.saveHead(ctx, newHeadRoot, headBlock, headState); err != nil {
|
|
|
|
log.WithError(err).Error("could not save head")
|
2023-01-31 19:17:16 +00:00
|
|
|
}
|
|
|
|
|
2023-03-08 12:36:46 +00:00
|
|
|
// Only need to prune attestations from pool if the head has changed.
|
|
|
|
if err := s.pruneAttsFromPool(headBlock); err != nil {
|
|
|
|
log.WithError(err).Error("could not prune attestations from pool")
|
|
|
|
}
|
2023-04-27 17:13:58 +00:00
|
|
|
return true, nil
|
2023-01-31 19:17:16 +00:00
|
|
|
}
|
2023-03-09 01:51:50 +00:00
|
|
|
|
|
|
|
// shouldOverrideFCU checks whether the incoming block is still subject to being
|
|
|
|
// reorged or not by the next proposer.
|
|
|
|
func (s *Service) shouldOverrideFCU(newHeadRoot [32]byte, proposingSlot primitives.Slot) bool {
|
2023-03-22 01:12:54 +00:00
|
|
|
headWeight, err := s.cfg.ForkChoiceStore.Weight(newHeadRoot)
|
2023-03-09 01:51:50 +00:00
|
|
|
if err != nil {
|
|
|
|
log.WithError(err).WithField("root", fmt.Sprintf("%#x", newHeadRoot)).Warn("could not determine node weight")
|
|
|
|
}
|
|
|
|
currentSlot := s.CurrentSlot()
|
|
|
|
if proposingSlot == currentSlot {
|
2023-03-22 01:12:54 +00:00
|
|
|
proposerHead := s.cfg.ForkChoiceStore.GetProposerHead()
|
2023-03-09 01:51:50 +00:00
|
|
|
if proposerHead != newHeadRoot {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
log.WithFields(logrus.Fields{
|
|
|
|
"root": fmt.Sprintf("%#x", newHeadRoot),
|
|
|
|
"weight": headWeight,
|
|
|
|
}).Infof("Attempted late block reorg aborted due to attestations at %d seconds",
|
|
|
|
params.BeaconConfig().SecondsPerSlot)
|
|
|
|
lateBlockFailedAttemptSecondThreshold.Inc()
|
|
|
|
} else {
|
2023-03-22 01:12:54 +00:00
|
|
|
if s.cfg.ForkChoiceStore.ShouldOverrideFCU() {
|
2023-03-09 01:51:50 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
secs, err := slots.SecondsSinceSlotStart(currentSlot,
|
|
|
|
uint64(s.genesisTime.Unix()), uint64(time.Now().Unix()))
|
|
|
|
if err != nil {
|
|
|
|
log.WithError(err).Error("could not compute seconds since slot start")
|
|
|
|
}
|
|
|
|
if secs >= doublylinkedtree.ProcessAttestationsThreshold {
|
|
|
|
log.WithFields(logrus.Fields{
|
|
|
|
"root": fmt.Sprintf("%#x", newHeadRoot),
|
|
|
|
"weight": headWeight,
|
|
|
|
}).Infof("Attempted late block reorg aborted due to attestations at %d seconds",
|
|
|
|
doublylinkedtree.ProcessAttestationsThreshold)
|
|
|
|
lateBlockFailedAttemptFirstThreshold.Inc()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|