From eb3a3ecd7636f94e63076bb1d88e2f49d896caab Mon Sep 17 00:00:00 2001 From: Cal Bera Date: Thu, 10 Jul 2025 17:54:13 -0700 Subject: [PATCH 1/2] chore(state-processor): only retry NewPayload on SYNCING status if not in FinalizeBlock --- state-transition/core/state_processor.go | 5 +++-- state-transition/core/state_processor_payload.go | 12 +++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/state-transition/core/state_processor.go b/state-transition/core/state_processor.go index dab8685cd7..dddf765672 100644 --- a/state-transition/core/state_processor.go +++ b/state-transition/core/state_processor.go @@ -106,7 +106,7 @@ func (sp *StateProcessor) Transition( } // Process the block. - if err = sp.ProcessBlock(ctx, st, blk); err != nil { + if err = sp.ProcessBlock(ctx, st, blk, inFinalizeBlock); err != nil { return nil, err } @@ -207,12 +207,13 @@ func (sp *StateProcessor) ProcessBlock( ctx ReadOnlyContext, st *state.StateDB, blk *ctypes.BeaconBlock, + inFinalizeBlock bool, ) error { if err := sp.processBlockHeader(ctx, st, blk); err != nil { return err } - if err := sp.processExecutionPayload(ctx, st, blk); err != nil { + if err := sp.processExecutionPayload(ctx, st, blk, inFinalizeBlock); err != nil { return err } diff --git a/state-transition/core/state_processor_payload.go b/state-transition/core/state_processor_payload.go index 8644aacdde..2123e7f1cb 100644 --- a/state-transition/core/state_processor_payload.go +++ b/state-transition/core/state_processor_payload.go @@ -38,6 +38,7 @@ func (sp *StateProcessor) processExecutionPayload( txCtx ReadOnlyContext, st *statedb.StateDB, blk *ctypes.BeaconBlock, + inFinalizeBlock bool, ) error { var ( body = blk.GetBody() @@ -75,7 +76,7 @@ func (sp *StateProcessor) processExecutionPayload( // Perform payload verification only if the context is configured as such. if txCtx.VerifyPayload() { g.Go(func() error { - return sp.validateExecutionPayload(ctx, txCtx.ConsensusTime(), st, blk) + return sp.validateExecutionPayload(ctx, txCtx.ConsensusTime(), st, blk, inFinalizeBlock) }) } @@ -107,11 +108,12 @@ func (sp *StateProcessor) validateExecutionPayload( consensusTime math.U64, st ReadOnlyBeaconState, blk *ctypes.BeaconBlock, + inFinalizeBlock bool, ) error { if err := sp.validateStatelessPayload(blk); err != nil { return err } - return sp.validateStatefulPayload(ctx, consensusTime, st, blk) + return sp.validateStatefulPayload(ctx, consensusTime, st, blk, inFinalizeBlock) } // validateStatelessPayload performs stateless checks on the execution payload. @@ -140,6 +142,7 @@ func (sp *StateProcessor) validateStatefulPayload( consensusTime math.U64, st ReadOnlyBeaconState, blk *ctypes.BeaconBlock, + inFinalizeBlock bool, ) error { body := blk.GetBody() payload := body.GetExecutionPayload() @@ -181,9 +184,8 @@ func (sp *StateProcessor) validateStatefulPayload( return err } - // TODO: set retryOnSyncingStatus to false if we are in FinalizeBlock. - // Otherwise leave as true. This is ok to leave this way for now. - if err = sp.executionEngine.NotifyNewPayload(ctx, payloadReq, true); err != nil { + // We only retry on syncing status if we are not in FinalizeBlock. + if err = sp.executionEngine.NotifyNewPayload(ctx, payloadReq, !inFinalizeBlock); err != nil { return err } From b554d31b6a033ed7b11bd26b43003169568431c5 Mon Sep 17 00:00:00 2001 From: Cal Bera Date: Thu, 10 Jul 2025 18:05:47 -0700 Subject: [PATCH 2/2] remove now unneeded forceSyncUponFinalize --- beacon/blockchain/finalize_block.go | 9 ----- beacon/blockchain/payload.go | 56 ----------------------------- beacon/blockchain/service.go | 3 +- 3 files changed, 1 insertion(+), 67 deletions(-) diff --git a/beacon/blockchain/finalize_block.go b/beacon/blockchain/finalize_block.go index f2dccffe46..c31c85b75f 100644 --- a/beacon/blockchain/finalize_block.go +++ b/beacon/blockchain/finalize_block.go @@ -59,15 +59,6 @@ func (s *Service) FinalizeBlock( ) blk := signedBlk.GetBeaconBlock() - // Send an FCU to force the HEAD of the chain on the EL on startup. - var finalizeErr error - s.forceStartupSyncOnce.Do(func() { - finalizeErr = s.forceSyncUponFinalize(ctx, blk) - }) - if finalizeErr != nil { - return nil, finalizeErr - } - // STEP 2: Finalize sidecars first (block will check for sidecar availability). // SyncingToHeight is always the tip of the chain both during sync and when // caught up. We don't need to process sidecars unless they are within DA period. diff --git a/beacon/blockchain/payload.go b/beacon/blockchain/payload.go index 86a5fd6b86..bff9ef81bc 100644 --- a/beacon/blockchain/payload.go +++ b/beacon/blockchain/payload.go @@ -27,8 +27,6 @@ import ( payloadtime "github.com/berachain/beacon-kit/beacon/payload-time" ctypes "github.com/berachain/beacon-kit/consensus-types/types" engineprimitives "github.com/berachain/beacon-kit/engine-primitives/engine-primitives" - engineerrors "github.com/berachain/beacon-kit/engine-primitives/errors" - "github.com/berachain/beacon-kit/errors" "github.com/berachain/beacon-kit/payload/builder" "github.com/berachain/beacon-kit/primitives/math" statedb "github.com/berachain/beacon-kit/state-transition/core/state" @@ -73,60 +71,6 @@ func (s *Service) forceSyncUponProcess( } } -// forceSyncUponFinalize sends a new payload and force startup FCU to the Execution -// Layer client. This informs the EL client of the new head and forces a SYNC -// if blocks are missing. This function should only be run once at startup. -func (s *Service) forceSyncUponFinalize( - ctx context.Context, - beaconBlock *ctypes.BeaconBlock, -) error { - // NewPayload call first to load payload into EL client. - executionPayload := beaconBlock.GetBody().GetExecutionPayload() - payloadReq, err := ctypes.BuildNewPayloadRequestFromFork(beaconBlock) - if err != nil { - return err - } - - if err = payloadReq.HasValidVersionedAndBlockHashes(); err != nil { - return err - } - - // We set retryOnSyncingStatus to false here. We can ignore SYNCING status and proceed - // to the FCU. - err = s.executionEngine.NotifyNewPayload(ctx, payloadReq, false) - if err != nil { - return fmt.Errorf("startSyncUponFinalize NotifyNewPayload failed: %w", err) - } - - // Submit the forkchoice update to the EL client. This will ensure that it is either synced or - // starts up a sync. - req := ctypes.BuildForkchoiceUpdateRequestNoAttrs( - &engineprimitives.ForkchoiceStateV1{ - HeadBlockHash: executionPayload.GetBlockHash(), - SafeBlockHash: executionPayload.GetParentHash(), - FinalizedBlockHash: executionPayload.GetParentHash(), - }, - s.chainSpec.ActiveForkVersionForTimestamp(executionPayload.GetTimestamp()), - ) - - switch _, err = s.executionEngine.NotifyForkchoiceUpdate(ctx, req); { - case err == nil: - return nil - - case errors.IsAny(err, - engineerrors.ErrSyncingPayloadStatus, - engineerrors.ErrAcceptedPayloadStatus): - s.logger.Warn( - //nolint:lll // long message on one line for readability. - `Your execution client is syncing. It should be downloading eth blocks from its peers. Restart the beacon node once the execution client is caught up.`, - ) - return err - - default: - return fmt.Errorf("force startup NotifyForkchoiceUpdate failed: %w", err) - } -} - // Once you provide the right state, we really need to carry out the very same operations // to extract the data necessary to build the next block, whether current block is // being rejected or accepted. This is way there can be (and so should be) diff --git a/beacon/blockchain/service.go b/beacon/blockchain/service.go index 52ed959174..0a60abd68b 100644 --- a/beacon/blockchain/service.go +++ b/beacon/blockchain/service.go @@ -63,7 +63,7 @@ type Service struct { // builder is enabled. optimisticPayloadBuilds bool // forceStartupSyncOnce is used to force a sync of the startup head. - forceStartupSyncOnce *sync.Once + forceStartupSyncOnce sync.Once } // NewService creates a new validator service. @@ -92,7 +92,6 @@ func NewService( stateProcessor: stateProcessor, metrics: newChainMetrics(telemetrySink), optimisticPayloadBuilds: optimisticPayloadBuilds, - forceStartupSyncOnce: new(sync.Once), } }