@@ -27,8 +27,9 @@ alloy_sol_types::sol!(
2727 "../solidity/out/ValidatorManager.sol/ValidatorManager.json"
2828) ;
2929
30- use crate :: state:: { assemble_value_from_parts, decode_value, State } ;
31- use crate :: sync_handler:: { get_decided_value_for_sync, validate_payload} ;
30+ use crate :: payload:: validate_execution_payload;
31+ use crate :: state:: { decode_value, State } ;
32+ use crate :: sync_handler:: get_decided_value_for_sync;
3233
3334pub async fn initialize_state_from_genesis ( state : & mut State , engine : & Engine ) -> eyre:: Result < ( ) > {
3435 // Get the genesis block from the execution engine
@@ -396,54 +397,21 @@ pub async fn on_started_round(
396397 ) ;
397398
398399 for parts in & pending_parts {
399- match state. validate_proposal_parts ( parts) {
400- Ok ( ( ) ) => {
401- // Validate execution payload with the execution engine before storing it as undecided proposal
402- let ( value, data) = assemble_value_from_parts ( parts. clone ( ) ) ;
403-
404- let validity = state
405- . validate_execution_payload (
406- & data,
407- parts. height ,
408- parts. round ,
409- engine,
410- & emerald_config. retry_config ,
411- )
412- . await ?;
413-
414- if validity == Validity :: Invalid {
415- warn ! (
416- height = %parts. height,
417- round = %parts. round,
418- "Pending proposal has invalid execution payload, rejecting"
419- ) ;
420- continue ;
421- }
400+ // Validate and store the pending proposal
401+ let result = state
402+ . process_complete_proposal_parts ( parts, engine, & emerald_config. retry_config )
403+ . await ?;
422404
423- state. store . store_undecided_proposal ( value. clone ( ) ) . await ?;
405+ if result. is_some ( ) {
406+ info ! (
407+ height = %parts. height,
408+ round = %parts. round,
409+ proposer = %parts. proposer,
410+ "Moved valid pending proposal to undecided after validation"
411+ ) ;
412+ }
424413
425- state
426- . store
427- . store_undecided_block_data ( value. height , value. round , value. value . id ( ) , data)
428- . await ?;
429- info ! (
430- height = %parts. height,
431- round = %parts. round,
432- proposer = %parts. proposer,
433- "Moved valid pending proposal to undecided after validation"
434- ) ;
435- }
436- Err ( error) => {
437- // Validation failed, log error
438- error ! (
439- height = %parts. height,
440- round = %parts. round,
441- proposer = %parts. proposer,
442- error = ?error,
443- "Removed invalid pending proposal"
444- ) ;
445- }
446- } // Remove the parts from pending
414+ // Remove the parts from pending regardless of validation outcome
447415 state
448416 . store
449417 . remove_pending_proposal_parts ( parts. clone ( ) )
@@ -596,15 +564,20 @@ pub async fn on_received_proposal_part(
596564 "Received proposal part"
597565 ) ;
598566
599- // Try to reassemble the proposal from received parts. If present,
600- // validate it with the execution engine and mark invalid when
601- // parsing or validation fails. Keep the outer `Option` and send it
602- // back to the caller (consensus) regardless.
603- let proposed_value = state
604- . received_proposal_part ( from, part, engine, & emerald_config. retry_config )
605- . await ?;
567+ // Try to reassemble the proposal from received parts
568+ let parts = state. reassemble_proposal ( from, part) . await ?;
606569
607- if let Some ( proposed_value) = proposed_value. clone ( ) {
570+ // If we have complete parts, validate and store the proposal
571+ let proposed_value = match parts {
572+ Some ( parts) => {
573+ state
574+ . process_complete_proposal_parts ( & parts, engine, & emerald_config. retry_config )
575+ . await ?
576+ }
577+ None => None ,
578+ } ;
579+
580+ if let Some ( ref proposed_value) = proposed_value {
608581 debug ! ( "✅ Received complete proposal: {:?}" , proposed_value) ;
609582 }
610583
@@ -680,36 +653,16 @@ pub async fn on_decided(
680653 . block_hash ;
681654 assert_eq ! ( latest_block_hash, parent_block_hash) ;
682655
683- // Get validation status from cache or call newPayload
684- let validity = if let Some ( cached) = state. validated_cache_mut ( ) . get ( & block_hash) {
685- cached
686- } else {
687- // Collect hashes from blob transactions
688- let block: Block = execution_payload. clone ( ) . try_into_block ( ) . map_err ( |e| {
689- eyre:: eyre!(
690- "Failed to convert decided ExecutionPayloadV3 to Block at height {}: {}" ,
691- height,
692- e
693- )
694- } ) ?;
695- let versioned_hashes: Vec < BlockHash > =
696- block. body . blob_versioned_hashes_iter ( ) . copied ( ) . collect ( ) ;
697-
698- // Ask the EL to validate the execution payload
699- let payload_status = engine
700- . notify_new_block ( execution_payload, versioned_hashes)
701- . await ?;
702-
703- let validity = if payload_status. status . is_valid ( ) {
704- Validity :: Valid
705- } else {
706- Validity :: Invalid
707- } ;
708-
709- // TODO: insert validation outcome into cache also when calling notify_new_block_with_retry in validate_payload
710- state. validated_cache_mut ( ) . insert ( block_hash, validity) ;
711- validity
712- } ;
656+ // Validate the execution payload (uses cache internally)
657+ let validity = validate_execution_payload (
658+ state. validated_cache_mut ( ) ,
659+ & block_bytes,
660+ height,
661+ round,
662+ engine,
663+ & emerald_config. retry_config ,
664+ )
665+ . await ?;
713666
714667 if validity == Validity :: Invalid {
715668 return Err ( eyre ! ( "Block validation failed for hash: {}" , block_hash) ) ;
@@ -808,38 +761,16 @@ pub async fn on_process_synced_value(
808761 info ! ( %height, %round, "🟢🟢 Processing synced value" ) ;
809762
810763 let value = decode_value ( value_bytes) ;
811-
812- // Extract execution payload from the synced value for validation
813764 let block_bytes = value. extensions . clone ( ) ;
814- let execution_payload = ExecutionPayloadV3 :: from_ssz_bytes ( & block_bytes) . map_err ( |e| {
815- eyre:: eyre!(
816- "Failed to decode synced ExecutionPayloadV3 at height {}: {:?}" ,
817- height,
818- e
819- )
820- } ) ?;
821- let new_block_hash = execution_payload. payload_inner . payload_inner . block_hash ;
822-
823- // Collect hashes from blob transactions
824- let block: Block = execution_payload. clone ( ) . try_into_block ( ) . map_err ( |e| {
825- eyre:: eyre!(
826- "Failed to convert synced ExecutionPayloadV3 to Block at height {}: {}" ,
827- height,
828- e
829- )
830- } ) ?;
831- let versioned_hashes: Vec < BlockHash > =
832- block. body . blob_versioned_hashes_iter ( ) . copied ( ) . collect ( ) ;
833765
834766 // Validate the synced block
835- let validity = validate_payload (
767+ let validity = validate_execution_payload (
836768 state. validated_cache_mut ( ) ,
837- engine,
838- & execution_payload,
839- & versioned_hashes,
840- & emerald_config. retry_config ,
769+ & block_bytes,
841770 height,
842771 round,
772+ engine,
773+ & emerald_config. retry_config ,
843774 )
844775 . await ?;
845776
@@ -861,10 +792,7 @@ pub async fn on_process_synced_value(
861792 return Ok ( ( ) ) ;
862793 }
863794
864- debug ! (
865- "💡 Sync block validated at height {} with hash: {}" ,
866- height, new_block_hash
867- ) ;
795+ debug ! ( %height, "💡 Sync block validated" ) ;
868796 let proposed_value: ProposedValue < EmeraldContext > = ProposedValue {
869797 height,
870798 round,
@@ -875,16 +803,7 @@ pub async fn on_process_synced_value(
875803 } ;
876804
877805 if let Err ( e) = state
878- . store
879- . store_undecided_block_data ( height, round, proposed_value. value . id ( ) , block_bytes)
880- . await
881- {
882- error ! ( %height, %round, error = %e, "Failed to store synced block data" ) ;
883- }
884- // Store the synced value and block data
885- if let Err ( e) = state
886- . store
887- . store_undecided_proposal ( proposed_value. clone ( ) )
806+ . store_undecided_value ( & proposed_value, block_bytes)
888807 . await
889808 {
890809 error ! ( %height, %round, error = %e, "Failed to store synced value" ) ;
0 commit comments