@@ -23,6 +23,7 @@ package blockchain
2323import (
2424 "bytes"
2525 "context"
26+ "encoding/hex"
2627 "fmt"
2728 "time"
2829
@@ -155,6 +156,7 @@ func (s *Service) ProcessProposal(
155156 ctx ,
156157 consensusBlk ,
157158 bytes .Equal (thisNodeAddress , req .NextProposerAddress ),
159+ req .NextProposerAddress ,
158160 )
159161 if err != nil {
160162 s .logger .Error ("failed to verify incoming block" , "error" , err )
@@ -254,11 +256,12 @@ func (s *Service) VerifyIncomingBlobSidecars(
254256// VerifyIncomingBlock verifies the state root of an incoming block
255257// and logs the process.
256258//
257- //nolint:funlen // abundantly commented
259+ //nolint:funlen,gocognit // abundantly commented
258260func (s * Service ) VerifyIncomingBlock (
259261 ctx context.Context ,
260262 blk * types.ConsensusBlock ,
261263 isNextBlockProposer bool ,
264+ nextProposerAddress []byte ,
262265) (transition.ValidatorUpdates , error ) {
263266 beaconBlk := blk .GetBeaconBlock ()
264267 state := s .storageBackend .StateFromContext (ctx )
@@ -299,24 +302,50 @@ func (s *Service) VerifyIncomingBlock(
299302 return nil , ErrUnexpectedBlockSlot
300303 }
301304
302- var (
303- nextBlockData * builder.RequestPayloadData
304- errFetch error
305- shouldBuildNextPayload = s .shouldBuildNextPayload (isNextBlockProposer )
306- )
305+ // Determine if we should optimistically build a payload for the next slot.
306+ var shouldBuildNextPayload bool
307+ if s .localBuilder .Enabled () {
308+ switch {
309+ case isNextBlockProposer && ! s .preconfCfg .ShouldFetchFromSequencer ():
310+ // We're the next proposer and not fetching from sequencer,
311+ // build optimistically on local EL for ourselves.
312+ shouldBuildNextPayload = true
313+ s .logger .Info ("Next proposer is this node, building optimistically" ,
314+ "current_block_slot" , blkSlot .Base10 (),
315+ "target_build_slot" , (blkSlot + 1 ).Base10 (),
316+ )
317+ case s .preconfCfg .IsSequencer ():
318+ // We're the sequencer, build for whitelisted validators.
319+ // Only check whitelist if we can resolve the next proposer pubkey.
320+ expectedProposerPubkey , pubkeyErr := s .getNextProposerPubkey (state , nextProposerAddress )
321+ if pubkeyErr != nil {
322+ s .logger .Error ("Failed to get next proposer pubkey" , "error" , pubkeyErr )
323+ } else {
324+ shouldBuildNextPayload = s .preconfWhitelist .IsWhitelisted (expectedProposerPubkey )
325+ }
326+ s .logger .Info ("Sequencer mode: determined next proposer" ,
327+ "current_block_slot" , blkSlot .Base10 (),
328+ "target_build_slot" , (blkSlot + 1 ).Base10 (),
329+ "next_proposer_address" , hex .EncodeToString (nextProposerAddress ),
330+ "expected_proposer_pubkey" , expectedProposerPubkey .String (),
331+ "is_whitelisted" , shouldBuildNextPayload ,
332+ )
333+ }
334+ }
307335
336+ var nextBlockData * builder.RequestPayloadData
308337 if shouldBuildNextPayload {
309338 // makes sure that preFetchBuildData does not affect state
310339 ephemeralState := state .Protect (ctx )
311- nextBlockData , errFetch = s .preFetchBuildData (ephemeralState , blk .GetConsensusTime ())
312- if errFetch != nil {
340+ nextBlockData , err = s .preFetchBuildData (ephemeralState , blk .GetConsensusTime ())
341+ if err != nil {
313342 // We don't return with err if pre-fetch fails. Instead we log the issue
314343 // and still move to process the current block. Next block can always be
315344 // built right after current height is finalized.
316345 s .logger .Warn (
317346 "Failed pre fetching data for optimistic block building" ,
318- "case" , "block rejectiong " ,
319- "err" , errFetch ,
347+ "case" , "block rejection " ,
348+ "err" , err ,
320349 )
321350 }
322351 }
@@ -349,18 +378,19 @@ func (s *Service) VerifyIncomingBlock(
349378 if shouldBuildNextPayload {
350379 // makes sure that preFetchBuildDataForSuccess does not affect state
351380 ephemeralState := state .Protect (ctx )
352- nextBlockData , errFetch = s .preFetchBuildData (ephemeralState , blk .GetConsensusTime ())
353- if errFetch != nil {
381+ nextBlockData , err = s .preFetchBuildData (ephemeralState , blk .GetConsensusTime ())
382+ if err != nil {
354383 // We don't mark the block as rejected if it is valid but pre-fetch fails.
355384 // Instead we log the issue and move to process the current block.
356385 // Next block can always be built right after current height is finalized.
357386 s .logger .Warn (
358387 "Failed pre fetching data for optimistic block building" ,
359388 "case" , "block success" ,
360- "err" , errFetch ,
389+ "err" , err ,
361390 )
362391 return valUpdates , nil
363392 }
393+ s .optimisticBuildTriggered .Store (true )
364394 go s .handleOptimisticPayloadBuild (ctx , nextBlockData )
365395 }
366396
@@ -398,8 +428,22 @@ func (s *Service) verifyStateRoot(
398428 return valUpdates , err
399429}
400430
401- // shouldBuildNextPayload returns true if optimistic
402- // payload builds are enabled.
403- func (s * Service ) shouldBuildNextPayload (isNextBlockProposer bool ) bool {
404- return isNextBlockProposer && s .localBuilder .Enabled ()
431+ // getNextProposerPubkey retrieves the BLS public key for the next proposer given their CometBFT address.
432+ func (s * Service ) getNextProposerPubkey (st * statedb.StateDB , nextProposerAddress []byte ) (crypto.BLSPubkey , error ) {
433+ // Get all validators and find the one matching the CometBFT address
434+ validators , err := st .GetValidators ()
435+ if err != nil {
436+ return crypto.BLSPubkey {}, fmt .Errorf ("failed to get validators: %w" , err )
437+ }
438+
439+ // Find the validator whose BLS pubkey produces the given CometBFT address
440+ for _ , validator := range validators {
441+ pubkey := validator .GetPubkey ()
442+ computedAddr , _ := crypto .GetAddressFromPubKey (pubkey )
443+ if bytes .Equal (computedAddr , nextProposerAddress ) {
444+ return pubkey , nil
445+ }
446+ }
447+
448+ return crypto.BLSPubkey {}, fmt .Errorf ("validator not found for address: %x" , nextProposerAddress )
405449}
0 commit comments