1+ use self :: errors:: ExecutionPayloadBidInvalid ;
12use crate :: consensus_context:: ConsensusContext ;
23use errors:: { BlockOperationError , BlockProcessingError , HeaderInvalid } ;
34use rayon:: prelude:: * ;
45use safe_arith:: { ArithError , SafeArith , SafeArithIter } ;
5- use signature_sets:: { block_proposal_signature_set, get_pubkey_from_state, randao_signature_set} ;
6+ use signature_sets:: {
7+ block_proposal_signature_set, execution_payload_bid_signature_set, get_pubkey_from_state,
8+ randao_signature_set,
9+ } ;
610use std:: borrow:: Cow ;
711use tree_hash:: TreeHash ;
812use types:: * ;
@@ -173,9 +177,7 @@ pub fn per_block_processing<E: EthSpec, Payload: AbstractExecPayload<E>>(
173177 let body = block. body ( ) ;
174178 if state. fork_name_unchecked ( ) . gloas_enabled ( ) {
175179 process_withdrawals:: gloas:: process_withdrawals :: < E > ( state, spec) ?;
176-
177- // TODO(EIP-7732): build out process_execution_bid
178- // process_execution_bid(state, block, verify_signatures, spec)?;
180+ process_execution_payload_bid ( state, block, verify_signatures, spec) ?;
179181 } else {
180182 process_withdrawals:: capella:: process_withdrawals :: < E , Payload > (
181183 state,
@@ -625,7 +627,7 @@ pub fn get_expected_withdrawals<E: EthSpec>(
625627 index : withdrawal_index,
626628 validator_index : withdrawal. validator_index ,
627629 address : validator
628- . get_execution_withdrawal_address ( spec)
630+ . get_execution_withdrawal_address ( spec, state . fork_name_unchecked ( ) )
629631 . ok_or ( BeaconStateError :: NonExecutionAddressWithdrawalCredential ) ?,
630632 amount : withdrawable_balance,
631633 } ) ;
@@ -662,7 +664,7 @@ pub fn get_expected_withdrawals<E: EthSpec>(
662664 index : withdrawal_index,
663665 validator_index,
664666 address : validator
665- . get_execution_withdrawal_address ( spec)
667+ . get_execution_withdrawal_address ( spec, state . fork_name_unchecked ( ) )
666668 . ok_or ( BlockProcessingError :: WithdrawalCredentialsInvalid ) ?,
667669 amount : balance,
668670 } ) ;
@@ -672,7 +674,7 @@ pub fn get_expected_withdrawals<E: EthSpec>(
672674 index : withdrawal_index,
673675 validator_index,
674676 address : validator
675- . get_execution_withdrawal_address ( spec)
677+ . get_execution_withdrawal_address ( spec, state . fork_name_unchecked ( ) )
676678 . ok_or ( BlockProcessingError :: WithdrawalCredentialsInvalid ) ?,
677679 amount : balance. safe_sub ( validator. get_max_effective_balance ( spec, fork_name) ) ?,
678680 } ) ;
@@ -692,3 +694,159 @@ pub fn get_expected_withdrawals<E: EthSpec>(
692694 processed_partial_withdrawals_count,
693695 ) )
694696}
697+
698+ pub fn process_execution_payload_bid < E : EthSpec , Payload : AbstractExecPayload < E > > (
699+ state : & mut BeaconState < E > ,
700+ block : BeaconBlockRef < ' _ , E , Payload > ,
701+ verify_signatures : VerifySignatures ,
702+ spec : & ChainSpec ,
703+ ) -> Result < ( ) , BlockProcessingError > {
704+ // Verify the bid signature
705+ let signed_bid = block. body ( ) . signed_execution_payload_bid ( ) ?;
706+
707+ let bid = & signed_bid. message ;
708+ let amount = bid. value ;
709+ let builder_index = bid. builder_index ;
710+ let builder = state. get_validator ( builder_index as usize ) ?;
711+
712+ // For self-builds, amount must be zero regardless of withdrawal credential prefix
713+ if builder_index == block. proposer_index ( ) {
714+ block_verify ! ( amount == 0 , ExecutionPayloadBidInvalid :: BadAmount . into( ) ) ;
715+ // TODO(EIP-7732): check with team if we should use ExecutionPayloadBidInvalid::BadSignature or a new error variant for this, like BadSelfBuildSignature
716+ block_verify ! (
717+ signed_bid. signature. is_infinity( ) ,
718+ ExecutionPayloadBidInvalid :: BadSignature . into( )
719+ ) ;
720+ } else {
721+ // Non-self builds require builder withdrawal credential
722+ block_verify ! (
723+ builder. has_builder_withdrawal_credential( spec) ,
724+ ExecutionPayloadBidInvalid :: BadWithdrawalCredentials . into( )
725+ ) ;
726+ if verify_signatures. is_true ( ) {
727+ block_verify ! (
728+ execution_payload_bid_signature_set(
729+ state,
730+ |i| get_pubkey_from_state( state, i) ,
731+ signed_bid,
732+ spec
733+ ) ?
734+ . verify( ) ,
735+ ExecutionPayloadBidInvalid :: BadSignature . into( )
736+ ) ;
737+ }
738+ }
739+
740+ // Verify builder is active and not slashed
741+ block_verify ! (
742+ builder. is_active_at( state. current_epoch( ) ) ,
743+ ExecutionPayloadBidInvalid :: BuilderNotActive ( builder_index) . into( )
744+ ) ;
745+ block_verify ! (
746+ !builder. slashed,
747+ ExecutionPayloadBidInvalid :: BuilderSlashed ( builder_index) . into( )
748+ ) ;
749+
750+ // Only perform payment related checks if amount > 0
751+ if amount > 0 {
752+ // Check that the builder has funds to cover the bid
753+ let pending_payments = state
754+ . builder_pending_payments ( ) ?
755+ . iter ( )
756+ . filter_map ( |payment| {
757+ if payment. withdrawal . builder_index == builder_index {
758+ Some ( payment. withdrawal . amount )
759+ } else {
760+ None
761+ }
762+ } )
763+ . safe_sum ( ) ?;
764+
765+ let pending_withdrawals = state
766+ . builder_pending_withdrawals ( ) ?
767+ . iter ( )
768+ . filter_map ( |withdrawal| {
769+ if withdrawal. builder_index == builder_index {
770+ Some ( withdrawal. amount )
771+ } else {
772+ None
773+ }
774+ } )
775+ . safe_sum ( ) ?;
776+
777+ let builder_balance = state. get_balance ( builder_index as usize ) ?;
778+
779+ block_verify ! (
780+ builder_balance
781+ >= amount
782+ . safe_add( pending_payments) ?
783+ . safe_add( pending_withdrawals) ?
784+ . safe_add( spec. min_activation_balance) ?,
785+ ExecutionPayloadBidInvalid :: InsufficientBalance {
786+ builder_index,
787+ builder_balance,
788+ bid_value: amount,
789+ }
790+ . into( )
791+ ) ;
792+ }
793+
794+ // Verify that the bid is for the current slot
795+ block_verify ! (
796+ bid. slot == block. slot( ) ,
797+ ExecutionPayloadBidInvalid :: SlotMismatch {
798+ state_slot: block. slot( ) ,
799+ bid_slot: bid. slot,
800+ }
801+ . into( )
802+ ) ;
803+
804+ // Verify that the bid is for the right parent block
805+ let latest_block_hash = state. latest_block_hash ( ) ?;
806+ block_verify ! (
807+ bid. parent_block_hash == * latest_block_hash,
808+ ExecutionPayloadBidInvalid :: ParentBlockHashMismatch {
809+ state_block_hash: * latest_block_hash,
810+ bid_parent_hash: bid. parent_block_hash,
811+ }
812+ . into( )
813+ ) ;
814+
815+ block_verify ! (
816+ bid. parent_block_root == block. parent_root( ) ,
817+ ExecutionPayloadBidInvalid :: ParentBlockRootMismatch {
818+ block_parent_root: block. parent_root( ) ,
819+ bid_parent_root: bid. parent_block_root,
820+ }
821+ . into( )
822+ ) ;
823+
824+ // Record the pending payment if there is some payment
825+ if amount > 0 {
826+ let pending_payment = BuilderPendingPayment {
827+ weight : 0 ,
828+ withdrawal : BuilderPendingWithdrawal {
829+ fee_recipient : bid. fee_recipient ,
830+ amount,
831+ builder_index,
832+ withdrawable_epoch : spec. far_future_epoch ,
833+ } ,
834+ } ;
835+
836+ let payment_index = ( E :: slots_per_epoch ( )
837+ . safe_add ( bid. slot . as_u64 ( ) . safe_rem ( E :: slots_per_epoch ( ) ) ?) ?)
838+ as usize ;
839+
840+ * state
841+ . builder_pending_payments_mut ( ) ?
842+ . get_mut ( payment_index)
843+ . ok_or ( BlockProcessingError :: BeaconStateError (
844+ BeaconStateError :: BuilderPendingPaymentsIndexNotSupported ( payment_index) ,
845+ ) ) ? = pending_payment;
846+ }
847+
848+ // Cache the execution bid
849+ * state. latest_execution_payload_bid_mut ( ) ? = bid. clone ( ) ;
850+
851+ Ok ( ( ) )
852+ }
0 commit comments