From 396b8bf970b192b8123a235e9249d8b4944794f2 Mon Sep 17 00:00:00 2001 From: Potuz Date: Thu, 4 Jan 2024 10:43:57 -0300 Subject: [PATCH] Simplify fcu 4 (#13403) * send two FCU when proposing * compute voting window at runtime --- beacon-chain/blockchain/process_block.go | 82 +++++++++++-------- beacon-chain/blockchain/process_block_test.go | 2 +- beacon-chain/core/helpers/validators.go | 2 +- time/slots/slottime.go | 4 +- 4 files changed, 53 insertions(+), 37 deletions(-) diff --git a/beacon-chain/blockchain/process_block.go b/beacon-chain/blockchain/process_block.go index a034aca26..5a4238f30 100644 --- a/beacon-chain/blockchain/process_block.go +++ b/beacon-chain/blockchain/process_block.go @@ -103,30 +103,6 @@ func (s *Service) postBlockProcess(ctx context.Context, signed interfaces.ReadOn proposingSlot: proposingSlot, } } else { - // Updating next slot state cache can happen in the background - // except in the epoch boundary in which case we lock to handle - // the shuffling and proposer caches updates. - // We handle these caches only on canonical - // blocks, otherwise this will be handled by lateBlockTasks - slot := postState.Slot() - if slots.IsEpochEnd(slot) { - if err := transition.UpdateNextSlotCache(ctx, blockRoot[:], postState); err != nil { - return errors.Wrap(err, "could not update next slot state cache") - } - if err := s.handleEpochBoundary(ctx, slot, postState, blockRoot[:]); err != nil { - return errors.Wrap(err, "could not handle epoch boundary") - } - } else { - go func() { - slotCtx, cancel := context.WithTimeout(context.Background(), slotDeadline) - defer cancel() - if err := transition.UpdateNextSlotCache(slotCtx, blockRoot[:], postState); err != nil { - log.WithError(err).Error("could not update next slot state cache") - } - }() - } - // verify conditions for FCU, notifies FCU, and saves the new head. - // This function also prunes attestations, other similar operations happen in prunePostBlockOperationPools. fcuArgs = &fcuConfig{ headState: postState, headBlock: signed, @@ -134,17 +110,37 @@ func (s *Service) postBlockProcess(ctx context.Context, signed interfaces.ReadOn proposingSlot: proposingSlot, } } + isEarly := slots.WithinVotingWindow(uint64(s.genesisTime.Unix())) + shouldOverrideFCU := false + slot := postState.Slot() if s.isNewHead(headRoot) { - shouldOverrideFCU := false - _, tracked := s.trackedProposer(fcuArgs.headState, proposingSlot) - if tracked { - shouldOverrideFCU = s.shouldOverrideFCU(headRoot, proposingSlot) - fcuArgs.attributes = s.getPayloadAttribute(ctx, fcuArgs.headState, proposingSlot, headRoot[:]) - } - if !shouldOverrideFCU { + // if the block is early send FCU without any payload attributes + if isEarly { if err := s.forkchoiceUpdateWithExecution(ctx, fcuArgs); err != nil { return err } + } else { + // if the block is late lock and update the caches + if blockRoot == headRoot { + if err := transition.UpdateNextSlotCache(ctx, blockRoot[:], postState); err != nil { + return errors.Wrap(err, "could not update next slot state cache") + } + if slots.IsEpochEnd(slot) { + if err := s.handleEpochBoundary(ctx, slot, postState, blockRoot[:]); err != nil { + return errors.Wrap(err, "could not handle epoch boundary") + } + } + } + _, tracked := s.trackedProposer(fcuArgs.headState, proposingSlot) + if tracked { + shouldOverrideFCU = s.shouldOverrideFCU(headRoot, proposingSlot) + fcuArgs.attributes = s.getPayloadAttribute(ctx, fcuArgs.headState, proposingSlot, headRoot[:]) + } + if !shouldOverrideFCU { + if err := s.forkchoiceUpdateWithExecution(ctx, fcuArgs); err != nil { + return err + } + } } } optimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(blockRoot) @@ -163,7 +159,29 @@ func (s *Service) postBlockProcess(ctx context.Context, signed interfaces.ReadOn Optimistic: optimistic, }, }) - + if blockRoot == headRoot && isEarly { + go func() { + slotCtx, cancel := context.WithTimeout(context.Background(), slotDeadline) + defer cancel() + if err := transition.UpdateNextSlotCache(slotCtx, blockRoot[:], postState); err != nil { + log.WithError(err).Error("could not update next slot state cache") + } + if slots.IsEpochEnd(slot) { + if err := s.handleEpochBoundary(ctx, slot, postState, blockRoot[:]); err != nil { + log.WithError(err).Error("could not handle epoch boundary") + } + } + if _, tracked := s.trackedProposer(fcuArgs.headState, proposingSlot); !tracked { + return + } + fcuArgs.attributes = s.getPayloadAttribute(ctx, fcuArgs.headState, proposingSlot, headRoot[:]) + s.cfg.ForkChoiceStore.RLock() + defer s.cfg.ForkChoiceStore.RUnlock() + if _, err := s.notifyForkchoiceUpdate(ctx, fcuArgs); err != nil { + log.WithError(err).Error("could not update forkchoice with payload attributes for proposal") + } + }() + } defer reportAttestationInclusion(b) onBlockProcessingTime.Observe(float64(time.Since(startTime).Milliseconds())) return nil diff --git a/beacon-chain/blockchain/process_block_test.go b/beacon-chain/blockchain/process_block_test.go index f313d5010..fc53ce923 100644 --- a/beacon-chain/blockchain/process_block_test.go +++ b/beacon-chain/blockchain/process_block_test.go @@ -2045,7 +2045,7 @@ func TestFillMissingBlockPayloadId_PrepareAllPayloads(t *testing.T) { // Helper function to simulate the block being on time or delayed for proposer // boost. It alters the genesisTime tracked by the store. func driftGenesisTime(s *Service, slot, delay int64) { - offset := slot*int64(params.BeaconConfig().SecondsPerSlot) - delay + offset := slot*int64(params.BeaconConfig().SecondsPerSlot) + delay s.SetGenesisTime(time.Unix(time.Now().Unix()-offset, 0)) } diff --git a/beacon-chain/core/helpers/validators.go b/beacon-chain/core/helpers/validators.go index c34d884a6..2fea24d71 100644 --- a/beacon-chain/core/helpers/validators.go +++ b/beacon-chain/core/helpers/validators.go @@ -302,7 +302,7 @@ func BeaconProposerIndexAtSlot(ctx context.Context, state state.ReadOnlyBeaconSt return pid, nil } if err := UpdateProposerIndicesInCache(ctx, state, e); err != nil { - return 0, errors.Wrap(err, "could not update committee cache") + return 0, errors.Wrap(err, "could not update proposer index cache") } pid, err = cachedProposerIndexAtSlot(slot, [32]byte(r)) if err == nil { diff --git a/time/slots/slottime.go b/time/slots/slottime.go index 61bea59c7..1e2f5cf28 100644 --- a/time/slots/slottime.go +++ b/time/slots/slottime.go @@ -16,9 +16,6 @@ import ( // incoming objects. (24 mins with mainnet spec) const MaxSlotBuffer = uint64(1 << 7) -// votingWindow specifies the deadline for attestations -var votingWindow = params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot - // startFromTime returns the slot start in terms of genesis time.Time func startFromTime(genesis time.Time, slot primitives.Slot) time.Time { duration := time.Second * time.Duration(slot.Mul(params.BeaconConfig().SecondsPerSlot)) @@ -271,5 +268,6 @@ func TimeIntoSlot(genesisTime uint64) time.Duration { // WithinVotingWindow returns whether the current time is within the voting window // (eg. 4 seconds on mainnet) of the current slot. func WithinVotingWindow(genesisTime uint64) bool { + votingWindow := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot return TimeIntoSlot(genesisTime) < time.Duration(votingWindow)*time.Second }