77///
88/// Reference: https://eips.ethereum.org/EIPS/eip-7732#beacon-chain-changes
99use crate :: beacon_chain:: {
10+ constants:: DOMAIN_BEACON_BUILDER ,
1011 containers:: { BuilderPendingPayment , BuilderPendingWithdrawal , SignedExecutionPayloadBid } ,
11- types:: { BuilderIndex , Gwei , Slot } ,
12+ types:: { BLSPubkey , BuilderIndex , Gwei , Slot } ,
1213} ;
14+ use crate :: utils:: { crypto, ssz} ;
1315use thiserror:: Error ;
1416
1517#[ derive( Debug , Error ) ]
@@ -28,11 +30,15 @@ pub enum PayloadBidError {
2830
2931 #[ error( "Parent block hash mismatch" ) ]
3032 ParentHashMismatch ,
33+
34+ #[ error( "Builder pubkey missing for index {0}" ) ]
35+ MissingPubkey ( BuilderIndex ) ,
3136}
3237
3338/// Minimal beacon state surface needed by this function.
3439pub trait BeaconStateMut {
3540 fn builder_balance ( & self , index : BuilderIndex ) -> Option < Gwei > ;
41+ fn builder_pubkey ( & self , index : BuilderIndex ) -> Option < BLSPubkey > ;
3642 fn deduct_builder_balance ( & mut self , index : BuilderIndex , amount : Gwei ) ;
3743 fn push_pending_payment ( & mut self , payment : BuilderPendingPayment ) ;
3844 fn current_slot ( & self ) -> Slot ;
@@ -68,10 +74,7 @@ pub fn process_execution_payload_bid<S: BeaconStateMut>(
6874 return Err ( PayloadBidError :: ParentHashMismatch ) ;
6975 }
7076
71- // Step 3 — BLS signature (stub — wire to blst crate in full impl)
72- verify_builder_signature ( signed_bid) ?;
73-
74- // Step 4 + 5 — balance check and deduction
77+ // Step 3 — balance check
7578 let balance = state
7679 . builder_balance ( bid. builder_index )
7780 . ok_or ( PayloadBidError :: BuilderNotFound ( bid. builder_index ) ) ?;
@@ -83,6 +86,10 @@ pub fn process_execution_payload_bid<S: BeaconStateMut>(
8386 } ) ;
8487 }
8588
89+ // Step 4 — BLS signature
90+ verify_builder_signature ( state, signed_bid) ?;
91+
92+ // Step 5 — deduct balance
8693 state. deduct_builder_balance ( bid. builder_index , bid. value ) ;
8794
8895 // Step 6 — queue pending payment
@@ -100,9 +107,17 @@ pub fn process_execution_payload_bid<S: BeaconStateMut>(
100107}
101108
102109/// Stub — replace with blst domain-separated BLS verify in full impl.
103- fn verify_builder_signature (
104- _signed_bid : & SignedExecutionPayloadBid ,
110+ fn verify_builder_signature < S : BeaconStateMut > (
111+ state : & S ,
112+ signed_bid : & SignedExecutionPayloadBid ,
105113) -> Result < ( ) , PayloadBidError > {
106- // TODO: compute signing_root with DOMAIN_BEACON_BUILDER and verify
107- Ok ( ( ) )
114+ let message = & signed_bid. message ;
115+ let pk = state
116+ . builder_pubkey ( message. builder_index )
117+ . ok_or ( PayloadBidError :: MissingPubkey ( message. builder_index ) ) ?;
118+
119+ let domain = ssz:: compute_domain_simple ( DOMAIN_BEACON_BUILDER ) ;
120+ let signing_root = ssz:: signing_root ( message, domain) ;
121+ crypto:: bls_verify ( & pk, & signing_root, & signed_bid. signature )
122+ . map_err ( |_| PayloadBidError :: InvalidSignature )
108123}
0 commit comments