@@ -11,10 +11,7 @@ use ethlambda_types::{
1111 AggregatedAttestation , AggregationBits , Attestation , AttestationData ,
1212 SignedAggregatedAttestation , SignedAttestation , validator_indices,
1313 } ,
14- block:: {
15- AggregatedAttestations , AggregatedSignatureProof , Block , BlockBody ,
16- SignedBlockWithAttestation ,
17- } ,
14+ block:: { AggregatedAttestations , AggregatedSignatureProof , Block , BlockBody , SignedBlock } ,
1815 checkpoint:: Checkpoint ,
1916 primitives:: { H256 , ssz:: TreeHash } ,
2017 signature:: ValidatorSignature ,
@@ -160,7 +157,7 @@ fn aggregate_committee_signatures(store: &mut Store) -> Vec<SignedAggregatedAtte
160157 let Some ( validator) = validators. get ( * vid as usize ) else {
161158 continue ;
162159 } ;
163- let Ok ( pubkey) = validator. get_pubkey ( ) else {
160+ let Ok ( pubkey) = validator. get_attestation_pubkey ( ) else {
164161 continue ;
165162 } ;
166163 sigs. push ( sig. clone ( ) ) ;
@@ -378,7 +375,7 @@ pub fn on_gossip_attestation(
378375 return Err ( StoreError :: InvalidValidatorIndex ) ;
379376 }
380377 let validator_pubkey = target_state. validators [ validator_id as usize ]
381- . get_pubkey ( )
378+ . get_attestation_pubkey ( )
382379 . map_err ( |_| StoreError :: PubkeyDecodingFailed ( validator_id) ) ?;
383380
384381 // Verify the validator's XMSS signature
@@ -449,7 +446,7 @@ pub fn on_gossip_aggregated_attestation(
449446 . iter ( )
450447 . map ( |& vid| {
451448 validators[ vid as usize ]
452- . get_pubkey ( )
449+ . get_attestation_pubkey ( )
453450 . map_err ( |_| StoreError :: PubkeyDecodingFailed ( vid) )
454451 } )
455452 . collect :: < Result < _ , _ > > ( ) ?;
@@ -508,7 +505,8 @@ pub fn on_gossip_aggregated_attestation(
508505/// and stores them for future block building. Use this for all production paths.
509506pub fn on_block (
510507 store : & mut Store ,
511- signed_block : SignedBlockWithAttestation ,
508+ signed_block : SignedBlock ,
509+ local_validator_ids : & [ u64 ] ,
512510) -> Result < ( ) , StoreError > {
513511 on_block_core ( store, signed_block, true )
514512}
@@ -519,7 +517,7 @@ pub fn on_block(
519517/// where signatures are absent or irrelevant (e.g., fork choice spec tests).
520518pub fn on_block_without_verification (
521519 store : & mut Store ,
522- signed_block : SignedBlockWithAttestation ,
520+ signed_block : SignedBlock ,
523521) -> Result < ( ) , StoreError > {
524522 on_block_core ( store, signed_block, false )
525523}
@@ -530,13 +528,14 @@ pub fn on_block_without_verification(
530528/// for future block building. When false, all signature checks are skipped.
531529fn on_block_core (
532530 store : & mut Store ,
533- signed_block : SignedBlockWithAttestation ,
531+ signed_block : SignedBlock ,
534532 verify : bool ,
533+ _local_validator_ids : & [ u64 ] ,
535534) -> Result < ( ) , StoreError > {
536535 let _timing = metrics:: time_fork_choice_block_processing ( ) ;
537536 let block_start = std:: time:: Instant :: now ( ) ;
538537
539- let block = & signed_block. block . block ;
538+ let block = & signed_block. message ;
540539 let block_root = block. tree_hash_root ( ) ;
541540 let slot = block. slot ;
542541
@@ -563,8 +562,7 @@ fn on_block_core(
563562 }
564563 let sig_verification = sig_verification_start. elapsed ( ) ;
565564
566- let block = signed_block. block . block . clone ( ) ;
567- let proposer_attestation = signed_block. block . proposer_attestation . clone ( ) ;
565+ let block = signed_block. message . clone ( ) ;
568566
569567 // Execute state transition function to compute post-block state
570568 let state_transition_start = std:: time:: Instant :: now ( ) ;
@@ -594,7 +592,6 @@ fn on_block_core(
594592 let aggregated_attestations = & block. body . attestations ;
595593 let attestation_signatures = & signed_block. signature . attestation_signatures ;
596594
597- // Process block body attestations.
598595 // Store attestation data by root and proofs in known aggregated payloads.
599596 let mut att_data_entries: Vec < ( H256 , AttestationData ) > = Vec :: new ( ) ;
600597 let mut known_entries: Vec < ( SignatureKey , StoredAggregatedPayload ) > = Vec :: new ( ) ;
@@ -617,43 +614,13 @@ fn on_block_core(
617614 }
618615 }
619616
620- // Process proposer attestation as pending (enters "new" stage via gossip path)
621- // The proposer's attestation should NOT affect this block's fork choice position.
622- let proposer_vid = proposer_attestation. validator_id ;
623- let proposer_data_root = proposer_attestation. data . tree_hash_root ( ) ;
624- att_data_entries. push ( ( proposer_data_root, proposer_attestation. data . clone ( ) ) ) ;
625-
626- // Batch-insert all attestation data (body + proposer) in a single commit
617+ // Batch-insert attestation data and known aggregated payloads
627618 store. insert_attestation_data_by_root_batch ( att_data_entries) ;
628619 store. insert_known_aggregated_payloads_batch ( known_entries) ;
629620
630621 // Update forkchoice head based on new block and attestations
631- // IMPORTANT: This must happen BEFORE processing proposer attestation
632- // to prevent the proposer from gaining circular weight advantage.
633622 update_head ( store, false ) ;
634623
635- if !verify {
636- // Without sig verification, insert directly with a dummy proof
637- let participants = aggregation_bits_from_validator_indices ( & [ proposer_vid] ) ;
638- let payload = StoredAggregatedPayload {
639- slot : proposer_attestation. data . slot ,
640- proof : AggregatedSignatureProof :: empty ( participants) ,
641- } ;
642- store. insert_new_aggregated_payload ( ( proposer_vid, proposer_data_root) , payload) ;
643- } else {
644- // Store the proposer's signature unconditionally for future block building.
645- // Subnet filtering is handled at the P2P subscription layer.
646- let proposer_sig =
647- ValidatorSignature :: from_bytes ( & signed_block. signature . proposer_signature )
648- . map_err ( |_| StoreError :: SignatureDecodingFailed ) ?;
649- store. insert_gossip_signature (
650- proposer_data_root,
651- proposer_attestation. data . slot ,
652- proposer_vid,
653- proposer_sig,
654- ) ;
655- }
656-
657624 let block_total = block_start. elapsed ( ) ;
658625 info ! (
659626 %slot,
@@ -944,14 +911,6 @@ pub enum StoreError {
944911
945912 #[ error( "Validator {validator_index} is not the proposer for slot {slot}" ) ]
946913 NotProposer { validator_index : u64 , slot : u64 } ,
947-
948- #[ error(
949- "Proposer attestation validator_id {attestation_id} does not match block proposer_index {proposer_index}"
950- ) ]
951- ProposerAttestationMismatch {
952- attestation_id : u64 ,
953- proposer_index : u64 ,
954- } ,
955914}
956915
957916/// Build an AggregationBits bitfield from a list of validator indices.
@@ -1147,16 +1106,13 @@ fn build_block(
11471106/// Verify all signatures in a signed block.
11481107///
11491108/// Each attestation has a corresponding proof in the signature list.
1150- fn verify_signatures (
1151- state : & State ,
1152- signed_block : & SignedBlockWithAttestation ,
1153- ) -> Result < ( ) , StoreError > {
1109+ fn verify_signatures ( state : & State , signed_block : & SignedBlock ) -> Result < ( ) , StoreError > {
11541110 use ethlambda_crypto:: verify_aggregated_signature;
11551111 use ethlambda_types:: signature:: ValidatorSignature ;
11561112
11571113 let total_start = std:: time:: Instant :: now ( ) ;
11581114
1159- let block = & signed_block. block . block ;
1115+ let block = & signed_block. message ;
11601116 let attestations = & block. body . attestations ;
11611117 let attestation_signatures = & signed_block. signature . attestation_signatures ;
11621118
@@ -1179,14 +1135,14 @@ fn verify_signatures(
11791135 let slot: u32 = attestation. data . slot . try_into ( ) . expect ( "slot exceeds u32" ) ;
11801136 let message = attestation. data . tree_hash_root ( ) ;
11811137
1182- // Collect public keys with bounds check in a single pass
1138+ // Collect attestation public keys with bounds check in a single pass
11831139 let public_keys: Vec < _ > = validator_indices ( & attestation. aggregation_bits )
11841140 . map ( |vid| {
11851141 if vid >= num_validators {
11861142 return Err ( StoreError :: InvalidValidatorIndex ) ;
11871143 }
11881144 validators[ vid as usize ]
1189- . get_pubkey ( )
1145+ . get_attestation_pubkey ( )
11901146 . map_err ( |_| StoreError :: PubkeyDecodingFailed ( vid) )
11911147 } )
11921148 . collect :: < Result < _ , _ > > ( ) ?;
@@ -1207,15 +1163,7 @@ fn verify_signatures(
12071163
12081164 let proposer_start = std:: time:: Instant :: now ( ) ;
12091165
1210- let proposer_attestation = & signed_block. block . proposer_attestation ;
1211-
1212- if proposer_attestation. validator_id != block. proposer_index {
1213- return Err ( StoreError :: ProposerAttestationMismatch {
1214- attestation_id : proposer_attestation. validator_id ,
1215- proposer_index : block. proposer_index ,
1216- } ) ;
1217- }
1218-
1166+ // Verify proposer signature over block root using proposal key
12191167 let proposer_signature =
12201168 ValidatorSignature :: from_bytes ( & signed_block. signature . proposer_signature )
12211169 . map_err ( |_| StoreError :: ProposerSignatureDecodingFailed ) ?;
@@ -1225,17 +1173,13 @@ fn verify_signatures(
12251173 . ok_or ( StoreError :: InvalidValidatorIndex ) ?;
12261174
12271175 let proposer_pubkey = proposer
1228- . get_pubkey ( )
1176+ . get_proposal_pubkey ( )
12291177 . map_err ( |_| StoreError :: PubkeyDecodingFailed ( proposer. index ) ) ?;
12301178
1231- let slot = proposer_attestation
1232- . data
1233- . slot
1234- . try_into ( )
1235- . expect ( "slot exceeds u32" ) ;
1236- let message = proposer_attestation. data . tree_hash_root ( ) ;
1179+ let slot: u32 = block. slot . try_into ( ) . expect ( "slot exceeds u32" ) ;
1180+ let block_root = block. tree_hash_root ( ) ;
12371181
1238- if !proposer_signature. is_valid ( & proposer_pubkey, slot, & message ) {
1182+ if !proposer_signature. is_valid ( & proposer_pubkey, slot, & block_root ) {
12391183 return Err ( StoreError :: ProposerSignatureVerificationFailed ) ;
12401184 }
12411185 let proposer_elapsed = proposer_start. elapsed ( ) ;
@@ -1303,11 +1247,8 @@ fn reorg_depth(old_head: H256, new_head: H256, store: &Store) -> Option<u64> {
13031247mod tests {
13041248 use super :: * ;
13051249 use ethlambda_types:: {
1306- attestation:: { AggregatedAttestation , AggregationBits , Attestation , AttestationData } ,
1307- block:: {
1308- AggregatedSignatureProof , BlockBody , BlockSignatures , BlockWithAttestation ,
1309- SignedBlockWithAttestation ,
1310- } ,
1250+ attestation:: { AggregatedAttestation , AggregationBits , AttestationData } ,
1251+ block:: { AggregatedSignatureProof , BlockBody , BlockSignatures , SignedBlock } ,
13111252 checkpoint:: Checkpoint ,
13121253 state:: State ,
13131254 } ;
@@ -1336,26 +1277,20 @@ mod tests {
13361277
13371278 let attestation = AggregatedAttestation {
13381279 aggregation_bits : attestation_bits,
1339- data : attestation_data. clone ( ) ,
1280+ data : attestation_data,
13401281 } ;
13411282 let proof = AggregatedSignatureProof :: empty ( proof_bits) ;
13421283
13431284 let attestations = AggregatedAttestations :: new ( vec ! [ attestation] ) . unwrap ( ) ;
13441285 let attestation_signatures = ssz_types:: VariableList :: new ( vec ! [ proof] ) . unwrap ( ) ;
13451286
1346- let signed_block = SignedBlockWithAttestation {
1347- block : BlockWithAttestation {
1348- block : Block {
1349- slot : 0 ,
1350- proposer_index : 0 ,
1351- parent_root : H256 :: ZERO ,
1352- state_root : H256 :: ZERO ,
1353- body : BlockBody { attestations } ,
1354- } ,
1355- proposer_attestation : Attestation {
1356- validator_id : 0 ,
1357- data : attestation_data,
1358- } ,
1287+ let signed_block = SignedBlock {
1288+ message : Block {
1289+ slot : 0 ,
1290+ proposer_index : 0 ,
1291+ parent_root : H256 :: ZERO ,
1292+ state_root : H256 :: ZERO ,
1293+ body : BlockBody { attestations } ,
13591294 } ,
13601295 signature : BlockSignatures {
13611296 attestation_signatures,
0 commit comments