From a2a89cfca15952687e7f517dba8f461f5dbfb991 Mon Sep 17 00:00:00 2001 From: Shawn <44221603+shaspitz@users.noreply.github.com> Date: Sun, 3 Aug 2025 19:13:48 -0700 Subject: [PATCH 1/8] wip --- cl/blockbuilder/blockbuilder.go | 151 +++++++------------ cl/blockbuilder/blockbuilder_test.go | 6 +- cl/redisapp/leaderfollower/leaderfollower.go | 1 - 3 files changed, 60 insertions(+), 98 deletions(-) diff --git a/cl/blockbuilder/blockbuilder.go b/cl/blockbuilder/blockbuilder.go index 2d6fcd239..41c1f7e00 100644 --- a/cl/blockbuilder/blockbuilder.go +++ b/cl/blockbuilder/blockbuilder.go @@ -55,9 +55,11 @@ type BlockBuilder struct { buildDelay time.Duration buildEmptyBlocksDelay time.Duration buildDelayMs uint64 - lastBlockTime time.Time feeRecipient common.Address - executionHead *types.ExecutionHead + // Execution head of the previous block. + // This field should only be set upon restart in setExecutionHeadFromRPC, + // OR at the end of FinalizeBlock. + executionHead *types.ExecutionHead } func NewBlockBuilder( @@ -77,7 +79,6 @@ func NewBlockBuilder( buildDelayMs: uint64(buildDelay.Milliseconds()), buildEmptyBlocksDelay: buildDelayEmptyBlocks, feeRecipient: common.HexToAddress(feeReceipt), - lastBlockTime: time.Now().Add(-buildDelayEmptyBlocks), rpcClient: rpcClient, } } @@ -89,12 +90,8 @@ func NewMemberBlockBuilder(engineCL EngineClient, logger *slog.Logger) *BlockBui } } -func (bb *BlockBuilder) SetLastCallTimeToZero() { - bb.lastBlockTime = time.Time{} -} - -func (bb *BlockBuilder) startBuild(ctx context.Context, head *types.ExecutionHead, ts uint64) (engine.ForkChoiceResponse, error) { - hash := common.BytesToHash(head.BlockHash) +func (bb *BlockBuilder) startBuild(ctx context.Context, ts uint64) (engine.ForkChoiceResponse, error) { + hash := common.BytesToHash(bb.executionHead.BlockHash) fcs := engine.ForkchoiceStateV1{ HeadBlockHash: hash, @@ -123,19 +120,41 @@ func (bb *BlockBuilder) startBuild(ctx context.Context, head *types.ExecutionHea func (bb *BlockBuilder) GetPayload(ctx context.Context) error { var ( payloadID *engine.PayloadID - head *types.ExecutionHead err error ) - currentCallTime := time.Now() + if bb.executionHead == nil { + bb.logger.Debug("executionHead is nil, it'll be set by RPC. CL is likely being restarted") + err = util.RetryWithBackoff(ctx, maxAttempts, bb.logger, func() error { + innerErr := bb.setExecutionHeadFromRPC(ctx) + if innerErr != nil { + bb.logger.Warn( + "Failed to set execution head from rpc, retrying...", + "error", innerErr, + ) + return innerErr // Will retry + } + return nil // Success + }) + if err != nil { + return fmt.Errorf("failed to set execution head from rpc: %w", err) + } + } else { + bb.logger.Debug("executionHead is not nil, using cached value") + } + + beforeMempoolStatus := time.Now() mempoolStatus, err := bb.GetMempoolStatus(ctx) if err != nil { return fmt.Errorf("failed to get pending transaction count: %w", err) } - bb.logger.Debug("GetMempoolStatus rpc duration", "duration", time.Since(currentCallTime)) + bb.logger.Debug("GetMempoolStatus rpc duration", "duration", time.Since(beforeMempoolStatus)) + + lastBlockTime := time.UnixMilli(int64(bb.executionHead.BlockTime)) + bb.logger.Debug("lastBlockTime from execution head", "lastBlockTime", lastBlockTime) if mempoolStatus.Pending == 0 { - timeSinceLastBlock := currentCallTime.Sub(bb.lastBlockTime) + timeSinceLastBlock := time.Since(lastBlockTime) if timeSinceLastBlock < bb.buildEmptyBlocksDelay { bb.logger.Debug( "Leader: Skipping empty block", @@ -153,50 +172,18 @@ func (bb *BlockBuilder) GetPayload(ctx context.Context) error { ) } - // Load execution head to get previous block timestamp - err = util.RetryWithBackoff(ctx, maxAttempts, bb.logger, func() error { - head, err = bb.loadExecutionHead(ctx) - if err != nil { - bb.logger.Warn( - "Failed to load execution head, retrying...", - "error", err, - ) - return err // Will retry - } - return nil // Success - }) - if err != nil { - return fmt.Errorf("latest execution block: %w", err) - } - - prevTimestamp := head.BlockTime - - var ts uint64 - - if bb.lastBlockTime.IsZero() { - // First block, initialize LastCallTime and set default timestamp - ts = uint64(time.Now().UnixMilli()) + bb.buildDelayMs - } else { - // Compute diff in milliseconds - diff := currentCallTime.Sub(bb.lastBlockTime) - diffMillis := diff.Milliseconds() - - if uint64(diffMillis) <= bb.buildDelayMs { - ts = prevTimestamp + bb.buildDelayMs - } else { - // For every multiple of buildDelay that diff exceeds, increment the block time by that multiple. - multiples := (uint64(diffMillis) + bb.buildDelayMs - 1) / bb.buildDelayMs // Round up to next multiple of buildDelay - ts = prevTimestamp + multiples*bb.buildDelayMs - } - } - - // Very low chance to happen, only after restart and time.Now is broken - if ts <= head.BlockTime { - ts = head.BlockTime + 1 // Subsequent blocks must have a higher timestamp. + ts := uint64(time.Now().UnixMilli()) + if ts <= bb.executionHead.BlockTime { + bb.logger.Warn("Leader: Current timestamp is less than or equal to the previous block timestamp", + "current_ts", ts, + "prev_head_block_time", bb.executionHead.BlockTime, + ) + ts = bb.executionHead.BlockTime + 1 + time.Sleep(time.Until(time.UnixMilli(int64(ts)))) } err = util.RetryWithBackoff(ctx, maxAttempts, bb.logger, func() error { - response, err := bb.startBuild(ctx, head, ts) + response, err := bb.startBuild(ctx, ts) if err != nil { bb.logger.Warn( "Failed to build new EVM payload, will retry", @@ -275,8 +262,6 @@ func (bb *BlockBuilder) GetPayload(ctx context.Context) error { "Leader: BuildBlock completed and block is distributed", "PayloadID", payloadIDStr, ) - - bb.lastBlockTime = time.Now() return nil } @@ -418,30 +403,13 @@ func (bb *BlockBuilder) FinalizeBlock(ctx context.Context, payloadIDStr, executi return fmt.Errorf("failed to deserialize ExecutionPayload: %w", err) } - var head *types.ExecutionHead - err = util.RetryWithBackoff(ctx, maxAttempts, bb.logger, func() error { - head, err = bb.loadExecutionHead(ctx) - if err != nil { - bb.logger.Warn( - "Failed to load execution head, retrying...", - "error", err, - ) - return err // Will retry - } - return nil // Success - }) - if err != nil { - return fmt.Errorf("failed to load execution head: %w", err) - } - - if err := bb.validateExecutionPayload(executionPayload, head); err != nil { + if err := bb.validateExecutionPayload(executionPayload); err != nil { return fmt.Errorf("failed to validate execution payload: %w", err) } - hash := common.BytesToHash(head.BlockHash) retryFunc := bb.selectRetryFunction(ctx, msgID) - if err := bb.pushNewPayload(ctx, executionPayload, hash, retryFunc); err != nil { + if err := bb.pushNewPayload(ctx, executionPayload, retryFunc); err != nil { return fmt.Errorf("failed to push new payload: %w", err) } @@ -464,20 +432,20 @@ func (bb *BlockBuilder) FinalizeBlock(ctx context.Context, payloadIDStr, executi return nil } -func (bb *BlockBuilder) validateExecutionPayload(executionPayload engine.ExecutableData, head *types.ExecutionHead) error { - if executionPayload.Number != head.BlockHeight+1 { - return fmt.Errorf("invalid block height: %d, expected: %d", executionPayload.Number, head.BlockHeight+1) +func (bb *BlockBuilder) validateExecutionPayload(executionPayload engine.ExecutableData) error { + if executionPayload.Number != bb.executionHead.BlockHeight+1 { + return fmt.Errorf("invalid block height: %d, expected: %d", executionPayload.Number, bb.executionHead.BlockHeight+1) } - if executionPayload.ParentHash != common.Hash(head.BlockHash) { - return fmt.Errorf("invalid parent hash: %s, head: %s", executionPayload.ParentHash, head.BlockHash) + if executionPayload.ParentHash != common.Hash(bb.executionHead.BlockHash) { + return fmt.Errorf("invalid parent hash: %s, head: %s", executionPayload.ParentHash, bb.executionHead.BlockHash) } - minTimestamp := head.BlockTime + 1 + minTimestamp := bb.executionHead.BlockTime + 1 if executionPayload.Timestamp < minTimestamp && executionPayload.Number != 1 { return fmt.Errorf("invalid timestamp: %d, min: %d", executionPayload.Timestamp, minTimestamp) } - hash := common.BytesToHash(head.BlockHash) + hash := common.BytesToHash(bb.executionHead.BlockHash) if executionPayload.Random != hash { - return fmt.Errorf("invalid random: %s, head: %s", executionPayload.Random, head.BlockHash) + return fmt.Errorf("invalid random: %s, head: %s", executionPayload.Random, bb.executionHead.BlockHash) } return nil } @@ -493,10 +461,11 @@ func (bb *BlockBuilder) selectRetryFunction(ctx context.Context, msgID string) f } } -func (bb *BlockBuilder) pushNewPayload(ctx context.Context, executionPayload engine.ExecutableData, hash common.Hash, retryFunc func(f func() error) error) error { +func (bb *BlockBuilder) pushNewPayload(ctx context.Context, executionPayload engine.ExecutableData, retryFunc func(f func() error) error) error { emptyVersionHashes := []common.Hash{} + parentHash := common.BytesToHash(bb.executionHead.BlockHash) return retryFunc(func() error { - status, err := bb.engineCl.NewPayloadV4(ctx, executionPayload, emptyVersionHashes, &hash, []hexutil.Bytes{}) + status, err := bb.engineCl.NewPayloadV4(ctx, executionPayload, emptyVersionHashes, &parentHash, []hexutil.Bytes{}) bb.logger.Debug("newPayload result", "status", status.Status, "validationError", status.ValidationError, @@ -534,23 +503,17 @@ func (bb *BlockBuilder) updateForkChoice(ctx context.Context, fcs engine.Forkcho }) } -func (bb *BlockBuilder) loadExecutionHead(ctx context.Context) (*types.ExecutionHead, error) { - if bb.executionHead != nil { - return bb.executionHead, nil - } - +func (bb *BlockBuilder) setExecutionHeadFromRPC(ctx context.Context) error { header, err := bb.engineCl.HeaderByNumber(ctx, nil) // nil for the latest block if err != nil { - return nil, fmt.Errorf("failed to get the latest block header: %w", err) + return fmt.Errorf("failed to get the latest block header: %w", err) } - bb.executionHead = &types.ExecutionHead{ BlockHeight: header.Number.Uint64(), BlockHash: header.Hash().Bytes(), BlockTime: header.Time, } - - return bb.executionHead, nil + return nil } func (bb *BlockBuilder) GetExecutionHead() *types.ExecutionHead { diff --git a/cl/blockbuilder/blockbuilder_test.go b/cl/blockbuilder/blockbuilder_test.go index 45c34950b..338ec742d 100644 --- a/cl/blockbuilder/blockbuilder_test.go +++ b/cl/blockbuilder/blockbuilder_test.go @@ -117,7 +117,7 @@ func TestBlockBuilder_startBuild(t *testing.T) { } mockEngineClient.On("ForkchoiceUpdatedV3", mock.Anything, expectedFCS, mock.MatchedBy(matchPayloadAttributes(hash, executionHead.BlockTime))).Return(forkChoiceResponse, nil) - resp, err := blockBuilder.startBuild(ctx, executionHead, uint64(timestamp.UnixMilli())) + resp, err := blockBuilder.startBuild(ctx, uint64(timestamp.UnixMilli())) require.NoError(t, err) assert.Equal(t, forkChoiceResponse, resp) @@ -327,7 +327,7 @@ func TestBlockBuilder_startBuild_ForkchoiceUpdatedError(t *testing.T) { mockEngineClient.On("ForkchoiceUpdatedV3", mock.Anything, expectedFCS, mock.MatchedBy(matchPayloadAttributes(hash, executionHead.BlockTime))).Return(engine.ForkChoiceResponse{}, errors.New("engine error")) - resp, err := blockBuilder.startBuild(ctx, executionHead, uint64(timestamp.UnixMilli())) + resp, err := blockBuilder.startBuild(ctx, uint64(timestamp.UnixMilli())) require.Error(t, err) assert.Contains(t, err.Error(), "forkchoice update") @@ -378,7 +378,7 @@ func TestBlockBuilder_startBuild_InvalidPayloadStatus(t *testing.T) { } mockEngineClient.On("ForkchoiceUpdatedV3", mock.Anything, expectedFCS, mock.MatchedBy(matchPayloadAttributes(hash, executionHead.BlockTime))).Return(forkChoiceResponse, nil) - resp, err := blockBuilder.startBuild(ctx, executionHead, uint64(timestamp.UnixMilli())) + resp, err := blockBuilder.startBuild(ctx, uint64(timestamp.UnixMilli())) require.NoError(t, err) assert.Equal(t, forkChoiceResponse, resp) diff --git a/cl/redisapp/leaderfollower/leaderfollower.go b/cl/redisapp/leaderfollower/leaderfollower.go index f7b38d30f..1090c9dd6 100644 --- a/cl/redisapp/leaderfollower/leaderfollower.go +++ b/cl/redisapp/leaderfollower/leaderfollower.go @@ -263,7 +263,6 @@ func (lfm *LeaderFollowerManager) leaderWork(ctx context.Context) error { lfm.logger.Error("Leader: failed to reach geth node after max attempts, exiting") stopElecErr := lfm.leaderProc.Stop() // todo: refactor to generate timestamp outside blockbuilder - lfm.blockBuilder.SetLastCallTimeToZero() if stopElecErr != nil { lfm.logger.Error( "Leader: Failed to stop leader election", From 5d543f7244375d7c2eea7e0db8e6d9fdf7a0ea20 Mon Sep 17 00:00:00 2001 From: Shawn <44221603+shaspitz@users.noreply.github.com> Date: Sun, 3 Aug 2025 19:27:34 -0700 Subject: [PATCH 2/8] rm buildDelayMs --- cl/blockbuilder/blockbuilder.go | 2 -- cl/blockbuilder/blockbuilder_test.go | 8 -------- 2 files changed, 10 deletions(-) diff --git a/cl/blockbuilder/blockbuilder.go b/cl/blockbuilder/blockbuilder.go index 41c1f7e00..376e41f72 100644 --- a/cl/blockbuilder/blockbuilder.go +++ b/cl/blockbuilder/blockbuilder.go @@ -54,7 +54,6 @@ type BlockBuilder struct { logger *slog.Logger buildDelay time.Duration buildEmptyBlocksDelay time.Duration - buildDelayMs uint64 feeRecipient common.Address // Execution head of the previous block. // This field should only be set upon restart in setExecutionHeadFromRPC, @@ -76,7 +75,6 @@ func NewBlockBuilder( engineCl: engineCl, logger: logger, buildDelay: buildDelay, - buildDelayMs: uint64(buildDelay.Milliseconds()), buildEmptyBlocksDelay: buildDelayEmptyBlocks, feeRecipient: common.HexToAddress(feeReceipt), rpcClient: rpcClient, diff --git a/cl/blockbuilder/blockbuilder_test.go b/cl/blockbuilder/blockbuilder_test.go index 338ec742d..3914db61b 100644 --- a/cl/blockbuilder/blockbuilder_test.go +++ b/cl/blockbuilder/blockbuilder_test.go @@ -97,7 +97,6 @@ func TestBlockBuilder_startBuild(t *testing.T) { engineCl: mockEngineClient, rpcClient: createMockRPCClient(), buildDelay: buildDelay, - buildDelayMs: uint64(buildDelay.Milliseconds()), logger: stLog, } timestamp := time.Now() @@ -162,7 +161,6 @@ func TestBlockBuilder_getPayload(t *testing.T) { engineCl: mockEngineClient, rpcClient: createMockRPCClient(), buildDelay: buildDelay, - buildDelayMs: uint64(buildDelay.Milliseconds()), logger: stLog, } @@ -229,7 +227,6 @@ func TestBlockBuilder_FinalizeBlock(t *testing.T) { engineCl: mockEngineClient, rpcClient: createMockRPCClient(), buildDelay: buildDelay, - buildDelayMs: uint64(buildDelay.Milliseconds()), logger: stLog, } @@ -312,7 +309,6 @@ func TestBlockBuilder_startBuild_ForkchoiceUpdatedError(t *testing.T) { engineCl: mockEngineClient, rpcClient: createMockRPCClient(), buildDelay: buildDelay, - buildDelayMs: uint64(buildDelay.Milliseconds()), logger: stLog, } @@ -357,7 +353,6 @@ func TestBlockBuilder_startBuild_InvalidPayloadStatus(t *testing.T) { engineCl: mockEngineClient, rpcClient: createMockRPCClient(), buildDelay: buildDelay, - buildDelayMs: uint64(buildDelay.Milliseconds()), logger: stLog, } @@ -459,7 +454,6 @@ func TestBlockBuilder_FinalizeBlock_InvalidBlockHeight(t *testing.T) { engineCl: mockEngineClient, rpcClient: createMockRPCClient(), buildDelay: buildDelay, - buildDelayMs: uint64(buildDelay.Milliseconds()), logger: stLog, } @@ -515,7 +509,6 @@ func TestBlockBuilder_FinalizeBlock_NewPayloadInvalidStatus(t *testing.T) { engineCl: mockEngineClient, rpcClient: createMockRPCClient(), buildDelay: buildDelay, - buildDelayMs: uint64(buildDelay.Milliseconds()), logger: stLog, } @@ -576,7 +569,6 @@ func TestBlockBuilder_FinalizeBlock_ForkchoiceUpdatedInvalidStatus(t *testing.T) engineCl: mockEngineClient, rpcClient: createMockRPCClient(), buildDelay: buildDelay, - buildDelayMs: uint64(buildDelay.Milliseconds()), logger: stLog, } From 12ce686d3ec557b4a63b1c48a4fc1317766037e3 Mon Sep 17 00:00:00 2001 From: Shawn <44221603+shaspitz@users.noreply.github.com> Date: Sun, 3 Aug 2025 19:29:41 -0700 Subject: [PATCH 3/8] nit --- cl/blockbuilder/blockbuilder.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cl/blockbuilder/blockbuilder.go b/cl/blockbuilder/blockbuilder.go index 376e41f72..9f9e3d402 100644 --- a/cl/blockbuilder/blockbuilder.go +++ b/cl/blockbuilder/blockbuilder.go @@ -122,7 +122,7 @@ func (bb *BlockBuilder) GetPayload(ctx context.Context) error { ) if bb.executionHead == nil { - bb.logger.Debug("executionHead is nil, it'll be set by RPC. CL is likely being restarted") + bb.logger.Info("executionHead is nil, it'll be set by RPC. CL is likely being restarted") err = util.RetryWithBackoff(ctx, maxAttempts, bb.logger, func() error { innerErr := bb.setExecutionHeadFromRPC(ctx) if innerErr != nil { @@ -172,7 +172,8 @@ func (bb *BlockBuilder) GetPayload(ctx context.Context) error { ts := uint64(time.Now().UnixMilli()) if ts <= bb.executionHead.BlockTime { - bb.logger.Warn("Leader: Current timestamp is less than or equal to the previous block timestamp", + bb.logger.Warn(`Leader: Current timestamp is less than or equal to + previous block timestamp. Genesis timestamp could have been set incorrectly`, "current_ts", ts, "prev_head_block_time", bb.executionHead.BlockTime, ) From 54cce9e4d58f7b6666c20bd4c8346e40540bb4f1 Mon Sep 17 00:00:00 2001 From: Shawn <44221603+shaspitz@users.noreply.github.com> Date: Sun, 3 Aug 2025 19:36:54 -0700 Subject: [PATCH 4/8] Update blockbuilder.go --- cl/blockbuilder/blockbuilder.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cl/blockbuilder/blockbuilder.go b/cl/blockbuilder/blockbuilder.go index 9f9e3d402..2f60bbbdb 100644 --- a/cl/blockbuilder/blockbuilder.go +++ b/cl/blockbuilder/blockbuilder.go @@ -55,10 +55,7 @@ type BlockBuilder struct { buildDelay time.Duration buildEmptyBlocksDelay time.Duration feeRecipient common.Address - // Execution head of the previous block. - // This field should only be set upon restart in setExecutionHeadFromRPC, - // OR at the end of FinalizeBlock. - executionHead *types.ExecutionHead + executionHead *types.ExecutionHead } func NewBlockBuilder( From 04d826f84150369fd582c5bb90e4adf7dd9dc7ba Mon Sep 17 00:00:00 2001 From: Shawn <44221603+shaspitz@users.noreply.github.com> Date: Sun, 3 Aug 2025 19:38:09 -0700 Subject: [PATCH 5/8] Update blockbuilder.go --- cl/blockbuilder/blockbuilder.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cl/blockbuilder/blockbuilder.go b/cl/blockbuilder/blockbuilder.go index 2f60bbbdb..da4f0aebb 100644 --- a/cl/blockbuilder/blockbuilder.go +++ b/cl/blockbuilder/blockbuilder.go @@ -138,12 +138,10 @@ func (bb *BlockBuilder) GetPayload(ctx context.Context) error { bb.logger.Debug("executionHead is not nil, using cached value") } - beforeMempoolStatus := time.Now() mempoolStatus, err := bb.GetMempoolStatus(ctx) if err != nil { return fmt.Errorf("failed to get pending transaction count: %w", err) } - bb.logger.Debug("GetMempoolStatus rpc duration", "duration", time.Since(beforeMempoolStatus)) lastBlockTime := time.UnixMilli(int64(bb.executionHead.BlockTime)) bb.logger.Debug("lastBlockTime from execution head", "lastBlockTime", lastBlockTime) From d70be889537f9eb9c66bbd54a42365b7d30915fc Mon Sep 17 00:00:00 2001 From: Shawn <44221603+shaspitz@users.noreply.github.com> Date: Sun, 3 Aug 2025 19:42:51 -0700 Subject: [PATCH 6/8] fix tests --- cl/blockbuilder/blockbuilder_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cl/blockbuilder/blockbuilder_test.go b/cl/blockbuilder/blockbuilder_test.go index 3914db61b..fa2a5028e 100644 --- a/cl/blockbuilder/blockbuilder_test.go +++ b/cl/blockbuilder/blockbuilder_test.go @@ -116,6 +116,8 @@ func TestBlockBuilder_startBuild(t *testing.T) { } mockEngineClient.On("ForkchoiceUpdatedV3", mock.Anything, expectedFCS, mock.MatchedBy(matchPayloadAttributes(hash, executionHead.BlockTime))).Return(forkChoiceResponse, nil) + blockBuilder.executionHead = executionHead + resp, err := blockBuilder.startBuild(ctx, uint64(timestamp.UnixMilli())) require.NoError(t, err) @@ -323,6 +325,8 @@ func TestBlockBuilder_startBuild_ForkchoiceUpdatedError(t *testing.T) { mockEngineClient.On("ForkchoiceUpdatedV3", mock.Anything, expectedFCS, mock.MatchedBy(matchPayloadAttributes(hash, executionHead.BlockTime))).Return(engine.ForkChoiceResponse{}, errors.New("engine error")) + blockBuilder.executionHead = executionHead + resp, err := blockBuilder.startBuild(ctx, uint64(timestamp.UnixMilli())) require.Error(t, err) @@ -373,6 +377,8 @@ func TestBlockBuilder_startBuild_InvalidPayloadStatus(t *testing.T) { } mockEngineClient.On("ForkchoiceUpdatedV3", mock.Anything, expectedFCS, mock.MatchedBy(matchPayloadAttributes(hash, executionHead.BlockTime))).Return(forkChoiceResponse, nil) + blockBuilder.executionHead = executionHead + resp, err := blockBuilder.startBuild(ctx, uint64(timestamp.UnixMilli())) require.NoError(t, err) From a51a3ab07bde238bec353d717bd6bac1cfa29413 Mon Sep 17 00:00:00 2001 From: Shawn <44221603+shaspitz@users.noreply.github.com> Date: Sun, 3 Aug 2025 19:48:13 -0700 Subject: [PATCH 7/8] Update leaderfollower.go --- cl/redisapp/leaderfollower/leaderfollower.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/cl/redisapp/leaderfollower/leaderfollower.go b/cl/redisapp/leaderfollower/leaderfollower.go index 1090c9dd6..a34b5ac35 100644 --- a/cl/redisapp/leaderfollower/leaderfollower.go +++ b/cl/redisapp/leaderfollower/leaderfollower.go @@ -39,9 +39,6 @@ type blockBuilder interface { // Processes any unfinished payload from a previous session ProcessLastPayload(ctx context.Context) error - - // Sets the last call time to zero - SetLastCallTimeToZero() } // todo: work with block state through block builder, not directly From 62e7c282238c8c5134dd2e22f0fa95b564336406 Mon Sep 17 00:00:00 2001 From: Shawn <44221603+shaspitz@users.noreply.github.com> Date: Mon, 4 Aug 2025 12:30:37 -0700 Subject: [PATCH 8/8] fix: also handle ctx cancel during sleep --- cl/blockbuilder/blockbuilder.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cl/blockbuilder/blockbuilder.go b/cl/blockbuilder/blockbuilder.go index da4f0aebb..78378287a 100644 --- a/cl/blockbuilder/blockbuilder.go +++ b/cl/blockbuilder/blockbuilder.go @@ -173,7 +173,12 @@ func (bb *BlockBuilder) GetPayload(ctx context.Context) error { "prev_head_block_time", bb.executionHead.BlockTime, ) ts = bb.executionHead.BlockTime + 1 - time.Sleep(time.Until(time.UnixMilli(int64(ts)))) + select { + case <-ctx.Done(): + return fmt.Errorf("context cancelled") + case <-time.After(time.Until(time.UnixMilli(int64(ts)))): + bb.logger.Info("Leader: Waited until proper block timestamp", "ts", ts) + } } err = util.RetryWithBackoff(ctx, maxAttempts, bb.logger, func() error {