@@ -3704,14 +3704,27 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
37043704 Ok ( ( ) )
37053705 }
37063706
3707+ /// Determines the beacon proposer for the next slot. If that proposer is registered in the
3708+ /// `execution_layer`, provide the `execution_layer` with the necessary information to produce
3709+ /// `PayloadAttributes` for future calls to fork choice.
3710+ ///
3711+ /// The `PayloadAttributes` are used by the EL to give it a look-ahead for preparing an optimal
3712+ /// set of transactions for a new `ExecutionPayload`.
3713+ ///
3714+ /// This function will result in a call to `forkchoiceUpdated` on the EL if:
3715+ ///
3716+ /// 1. We're in the tail-end of the slot (as defined by PAYLOAD_PREPARATION_LOOKAHEAD_FACTOR)
3717+ /// 2. The head block is one slot (or less) behind the prepare slot (e.g., we're preparing for
3718+ /// the next slot and the block at the current slot is already known).
37073719 pub async fn prepare_beacon_proposer ( & self ) -> Result < ( ) , Error > {
37083720 let execution_layer = self
37093721 . execution_layer
37103722 . clone ( )
37113723 . ok_or ( Error :: ExecutionLayerMissing ) ?;
37123724
3725+ // Nothing to do if there are no proposers registered with the EL, exit early to avoid
3726+ // wasting cycles.
37133727 if !execution_layer. has_proposers ( ) . await {
3714- // Nothing to do if there are no proposers registered with the EL.
37153728 return Ok ( ( ) ) ;
37163729 }
37173730
@@ -3721,105 +3734,134 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
37213734 let prepare_slot = current_slot + 1 ;
37223735 let prepare_epoch = prepare_slot. epoch ( T :: EthSpec :: slots_per_epoch ( ) ) ;
37233736
3737+ // Ensure that the shuffling decision root is correct relative to the epoch we wish to
3738+ // query.
37243739 let shuffling_decision_root = if head_epoch == prepare_epoch {
37253740 head. proposer_shuffling_decision_root
37263741 } else {
37273742 head. block_root
37283743 } ;
37293744
3730- let proposer_opt = self
3745+ // Read the proposer from the proposer cache.
3746+ let proposer = if let Some ( proposer) = self
37313747 . beacon_proposer_cache
37323748 . lock ( )
3733- . get_slot :: < T :: EthSpec > ( shuffling_decision_root, prepare_slot) ;
3749+ . get_slot :: < T :: EthSpec > ( shuffling_decision_root, prepare_slot)
3750+ {
3751+ proposer
3752+ } else {
3753+ debug ! (
3754+ self . log,
3755+ "No proposers for preparation" ;
3756+ "prepare_slot" => prepare_slot,
3757+ ) ;
3758+ // Nothing more to do.
3759+ return Ok ( ( ) ) ;
3760+ } ;
37343761
3735- if let Some ( proposer) = proposer_opt {
3736- if execution_layer
3737- . has_proposer_preparation_data ( proposer. index as u64 )
3738- . await
3739- {
3740- let suggested_fee_recipient = execution_layer
3741- . get_suggested_fee_recipient ( proposer. index as u64 )
3742- . await ;
3743-
3744- let timestamp = self
3745- . slot_clock
3746- . start_of ( prepare_slot)
3747- . ok_or ( Error :: InvalidSlot ( prepare_slot) ) ?
3748- . as_secs ( ) ;
3749-
3750- let payload_attributes = PayloadAttributes {
3751- timestamp,
3752- random : head. random ,
3753- suggested_fee_recipient,
3754- } ;
3762+ // If the execution layer doesn't have any proposer data for this validator then we assume
3763+ // it's not connected to this BN and no action is required.
3764+ if !execution_layer
3765+ . has_proposer_preparation_data ( proposer. index as u64 )
3766+ . await
3767+ {
3768+ return Ok ( ( ) ) ;
3769+ }
37553770
3756- debug ! (
3757- self . log,
3758- "Preparing beacon proposer" ;
3759- "payload_attributes" => ?payload_attributes,
3760- "head_root" => ?head. block_root,
3761- "prepare_slot" => prepare_slot,
3762- "validator" => proposer. index,
3763- ) ;
3771+ let payload_attributes = PayloadAttributes {
3772+ timestamp : self
3773+ . slot_clock
3774+ . start_of ( prepare_slot)
3775+ . ok_or ( Error :: InvalidSlot ( prepare_slot) ) ?
3776+ . as_secs ( ) ,
3777+ random : head. random ,
3778+ suggested_fee_recipient : execution_layer
3779+ . get_suggested_fee_recipient ( proposer. index as u64 )
3780+ . await ,
3781+ } ;
37643782
3765- let already_known = execution_layer
3766- . insert_proposer (
3767- prepare_slot,
3768- head. block_root ,
3769- proposer. index as u64 ,
3770- payload_attributes,
3771- )
3772- . await ;
3783+ debug ! (
3784+ self . log,
3785+ "Preparing beacon proposer" ;
3786+ "payload_attributes" => ?payload_attributes,
3787+ "head_root" => ?head. block_root,
3788+ "prepare_slot" => prepare_slot,
3789+ "validator" => proposer. index,
3790+ ) ;
3791+
3792+ let already_known = execution_layer
3793+ . insert_proposer (
3794+ prepare_slot,
3795+ head. block_root ,
3796+ proposer. index as u64 ,
3797+ payload_attributes,
3798+ )
3799+ . await ;
3800+ // Only push a log to the user if this is the first time we've seen this proposer for this
3801+ // slot.
3802+ if !already_known {
3803+ info ! (
3804+ self . log,
3805+ "Prepared beacon proposer" ;
3806+ "already_known" => already_known,
3807+ "prepare_slot" => prepare_slot,
3808+ "validator" => proposer. index,
3809+ ) ;
3810+ }
37733811
3774- if !already_known {
3775- info ! (
3812+ // If the head block has has execution enabled, then it might be a good idea to push
3813+ // a `forkchoiceUpdated` message to the EL to provide the payload attributes.
3814+ //
3815+ // There is no payload preparation for the transition block (i.e., the first block
3816+ // with execution enabled in some chain).
3817+ if let Some ( head_block_hash) = head. execution_payload_block_hash {
3818+ // `SlotClock::duration_to_slot` will return `None` when we are past the start
3819+ // of `prepare_slot`. Don't bother sending a `forkchoiceUpdated` in that case,
3820+ // it's too late.
3821+ if let Some ( till_prepare_slot) = self . slot_clock . duration_to_slot ( prepare_slot) {
3822+ // If either of the following are true, send a fork-choice update message to the
3823+ // EL:
3824+ //
3825+ // 1. We're in the tail-end of the slot (as defined by
3826+ // PAYLOAD_PREPARATION_LOOKAHEAD_FACTOR)
3827+ // 2. The head block is one slot (or less) behind the prepare slot (e.g., we're
3828+ // preparing for the next slot and the block at the current slot is already
3829+ // known).
3830+ if till_prepare_slot
3831+ <= self . slot_clock . slot_duration ( ) / PAYLOAD_PREPARATION_LOOKAHEAD_FACTOR
3832+ || head. slot + 1 >= prepare_slot
3833+ {
3834+ debug ! (
37763835 self . log,
3777- "Prepared beacon proposer" ;
3778- "already_known" => already_known,
3779- "prepare_slot" => prepare_slot,
3780- "validator" => proposer. index,
3836+ "Pushing update to prepare proposer" ;
3837+ "till_prepare_slot" => ?till_prepare_slot,
3838+ "prepare_slot" => prepare_slot
37813839 ) ;
37823840
3783- if let Some ( duration) = self . slot_clock . duration_to_slot ( prepare_slot) {
3784- if duration
3785- <= self . slot_clock . slot_duration ( )
3786- / PAYLOAD_PREPARATION_LOOKAHEAD_FACTOR
3787- {
3788- if let Some ( head_block_hash) = head. execution_payload_block_hash {
3789- let finalized_root = head. finalized_checkpoint . root ;
3790- let finalized_hash = self
3791- . fork_choice
3792- . read ( )
3793- . get_block ( & finalized_root)
3794- . ok_or ( Error :: FinalizedBlockMissingFromForkChoice (
3795- finalized_root,
3796- ) ) ?
3797- . execution_status
3798- . block_hash ( )
3799- . unwrap_or_else ( ExecutionBlockHash :: zero) ;
3800-
3801- self . update_execution_engine_forkchoice_blocking (
3802- finalized_hash,
3803- head. block_root ,
3804- head_block_hash,
3805- current_slot,
3806- ) ?;
3807- }
3808- }
3809- } else {
3810- warn ! (
3811- self . log,
3812- "Delayed proposer preparation" ;
3813- "prepare_slot" => prepare_slot,
3814- "validator" => proposer. index,
3815- ) ;
3816- }
3841+ let finalized_root = head. finalized_checkpoint . root ;
3842+ let finalized_hash = self
3843+ . fork_choice
3844+ . read ( )
3845+ . get_block ( & finalized_root)
3846+ . ok_or ( Error :: FinalizedBlockMissingFromForkChoice ( finalized_root) ) ?
3847+ . execution_status
3848+ . block_hash ( )
3849+ . unwrap_or_else ( ExecutionBlockHash :: zero) ;
3850+
3851+ self . update_execution_engine_forkchoice_blocking (
3852+ finalized_hash,
3853+ head. block_root ,
3854+ head_block_hash,
3855+ current_slot,
3856+ ) ?;
38173857 }
38183858 } else {
3819- debug ! (
3859+ // This scenario might occur on an overloaded/under-resourced node.
3860+ warn ! (
38203861 self . log,
3821- "No proposers for preparation" ;
3862+ "Delayed proposer preparation" ;
38223863 "prepare_slot" => prepare_slot,
3864+ "validator" => proposer. index,
38233865 ) ;
38243866 }
38253867 }
0 commit comments