@@ -34,14 +34,17 @@ use reth_optimism_evm::{OpEvmConfig, OpNextBlockEnvAttributes};
3434use reth_optimism_forks:: OpHardforks ;
3535use reth_optimism_node:: { OpBuiltPayload , OpPayloadBuilderAttributes } ;
3636use reth_optimism_primitives:: { OpReceipt , OpTransactionSigned } ;
37+ use reth_payload_primitives:: BuiltPayload ;
3738use reth_payload_util:: BestPayloadTransactions ;
3839use reth_primitives_traits:: RecoveredBlock ;
3940use reth_provider:: {
4041 ExecutionOutcome , HashedPostStateProvider , ProviderError , StateRootProvider ,
4142 StorageRootProvider ,
4243} ;
4344use reth_revm:: {
44- State , database:: StateProviderDatabase , db:: states:: bundle_state:: BundleRetention ,
45+ State ,
46+ database:: StateProviderDatabase ,
47+ db:: { BundleState , states:: bundle_state:: BundleRetention } ,
4548} ;
4649use reth_transaction_pool:: TransactionPool ;
4750use reth_trie:: { HashedPostState , updates:: TrieUpdates } ;
@@ -331,7 +334,7 @@ where
331334 async fn build_payload (
332335 & self ,
333336 args : BuildArguments < OpPayloadBuilderAttributes < OpTransactionSigned > , OpBuiltPayload > ,
334- best_payload : BlockCell < OpBuiltPayload > ,
337+ resolve_payload : BlockCell < OpBuiltPayload > ,
335338 ) -> Result < ( ) , PayloadBuilderError > {
336339 let block_build_start_time = Instant :: now ( ) ;
337340 let BuildArguments {
@@ -400,25 +403,14 @@ where
400403 ) ;
401404 } ;
402405
403- let ( payload, fb_payload) = build_block (
404- & mut state,
405- & ctx,
406- & mut info,
407- !disable_state_root || ctx. attributes ( ) . no_tx_pool , // need to calculate state root for CL sync
408- ) ?;
409-
406+ // We should always calculate state root for fallback payload
407+ let ( fallback_payload, fb_payload, bundle_state) =
408+ build_block ( & mut state, & ctx, & mut info, true ) ?;
410409 self . built_fb_payload_tx
411- . send ( payload . clone ( ) )
410+ . send ( fallback_payload . clone ( ) )
412411 . await
413412 . map_err ( PayloadBuilderError :: other) ?;
414- if let Err ( e) = self . built_payload_tx . send ( payload. clone ( ) ) . await {
415- warn ! (
416- target: "payload_builder" ,
417- error = %e,
418- "Failed to send updated payload"
419- ) ;
420- }
421- best_payload. set ( payload) ;
413+ let mut best_payload = ( fallback_payload. clone ( ) , bundle_state) ;
422414
423415 info ! (
424416 target: "payload_builder" ,
@@ -458,6 +450,14 @@ where
458450 . set ( info. executed_transactions . len ( ) as f64 ) ;
459451
460452 // return early since we don't need to build a block with transactions from the pool
453+ self . resolve_best_payload (
454+ & mut state,
455+ & ctx,
456+ best_payload,
457+ fallback_payload,
458+ & resolve_payload,
459+ )
460+ . await ;
461461 return Ok ( ( ) ) ;
462462 }
463463 // We adjust our flashblocks timings based on time the fcu block building signal arrived
@@ -614,6 +614,14 @@ where
614614 ctx = ctx. with_cancel( new_fb_cancel) ;
615615 } ,
616616 _ = block_cancel. cancelled( ) => {
617+ self . resolve_best_payload(
618+ & mut state,
619+ & ctx,
620+ best_payload,
621+ fallback_payload,
622+ & resolve_payload,
623+ )
624+ . await ;
617625 self . record_flashblocks_metrics(
618626 & ctx,
619627 & info,
@@ -637,6 +645,14 @@ where
637645 let _entered = fb_span. enter ( ) ;
638646
639647 if ctx. flashblock_index ( ) > ctx. target_flashblock_count ( ) {
648+ self . resolve_best_payload (
649+ & mut state,
650+ & ctx,
651+ best_payload,
652+ fallback_payload,
653+ & resolve_payload,
654+ )
655+ . await ;
640656 self . record_flashblocks_metrics (
641657 & ctx,
642658 & info,
@@ -656,13 +672,21 @@ where
656672 & state_provider,
657673 & mut best_txs,
658674 & block_cancel,
659- & best_payload,
675+ & mut best_payload,
660676 & fb_span,
661677 )
662678 . await
663679 {
664680 Ok ( Some ( next_flashblocks_ctx) ) => next_flashblocks_ctx,
665681 Ok ( None ) => {
682+ self . resolve_best_payload (
683+ & mut state,
684+ & ctx,
685+ best_payload,
686+ fallback_payload,
687+ & resolve_payload,
688+ )
689+ . await ;
666690 self . record_flashblocks_metrics (
667691 & ctx,
668692 & info,
@@ -680,6 +704,14 @@ where
680704 ctx. block_number( ) ,
681705 err
682706 ) ;
707+ self . resolve_best_payload (
708+ & mut state,
709+ & ctx,
710+ best_payload,
711+ fallback_payload,
712+ & resolve_payload,
713+ )
714+ . await ;
683715 return Err ( PayloadBuilderError :: Other ( err. into ( ) ) ) ;
684716 }
685717 } ;
@@ -700,7 +732,7 @@ where
700732 state_provider : impl reth:: providers:: StateProvider + Clone ,
701733 best_txs : & mut NextBestFlashblocksTxs < Pool > ,
702734 block_cancel : & CancellationToken ,
703- best_payload : & BlockCell < OpBuiltPayload > ,
735+ best_payload : & mut ( OpBuiltPayload , BundleState ) ,
704736 span : & tracing:: Span ,
705737 ) -> eyre:: Result < Option < FlashblocksExtraCtx > > {
706738 let flashblock_index = ctx. flashblock_index ( ) ;
@@ -840,7 +872,7 @@ where
840872 ctx. metrics . invalid_built_blocks_count . increment ( 1 ) ;
841873 Err ( err) . wrap_err ( "failed to build payload" )
842874 }
843- Ok ( ( new_payload, mut fb_payload) ) => {
875+ Ok ( ( new_payload, mut fb_payload, bundle_state ) ) => {
844876 fb_payload. index = flashblock_index;
845877 fb_payload. base = None ;
846878
@@ -864,14 +896,7 @@ where
864896 . send ( new_payload. clone ( ) )
865897 . await
866898 . wrap_err ( "failed to send built payload to handler" ) ?;
867- if let Err ( e) = self . built_payload_tx . send ( new_payload. clone ( ) ) . await {
868- warn ! (
869- target: "payload_builder" ,
870- error = %e,
871- "Failed to send updated payload"
872- ) ;
873- }
874- best_payload. set ( new_payload) ;
899+ * best_payload = ( new_payload, bundle_state) ;
875900
876901 // Record flashblock build duration
877902 ctx. metrics
@@ -925,6 +950,93 @@ where
925950 }
926951 }
927952
953+ async fn resolve_best_payload <
954+ DB : Database < Error = ProviderError > + std:: fmt:: Debug + AsRef < P > ,
955+ P : StateRootProvider + HashedPostStateProvider + StorageRootProvider ,
956+ > (
957+ & self ,
958+ state : & mut State < DB > ,
959+ ctx : & OpPayloadBuilderCtx < FlashblocksExtraCtx > ,
960+ best_payload : ( OpBuiltPayload , BundleState ) ,
961+ fallback_payload : OpBuiltPayload ,
962+ resolve_payload : & BlockCell < OpBuiltPayload > ,
963+ ) {
964+ if resolve_payload. get ( ) . is_some ( ) {
965+ return ;
966+ }
967+
968+ let payload = match best_payload. 0 . block ( ) . header ( ) . state_root {
969+ B256 :: ZERO => {
970+ info ! ( target: "payload_builder" , "Resolving payload with zero state root" ) ;
971+ self . resolve_zero_state_root ( state, ctx, best_payload)
972+ . await
973+ . unwrap_or_else ( |err| {
974+ warn ! (
975+ target: "payload_builder" ,
976+ error = %err,
977+ "Failed to calculate state root, falling back to fallback payload"
978+ ) ;
979+ fallback_payload
980+ } )
981+ }
982+ _ => best_payload. 0 ,
983+ } ;
984+ resolve_payload. set ( payload) ;
985+ }
986+
987+ async fn resolve_zero_state_root <
988+ DB : Database < Error = ProviderError > + std:: fmt:: Debug + AsRef < P > ,
989+ P : StateRootProvider + HashedPostStateProvider + StorageRootProvider ,
990+ > (
991+ & self ,
992+ state : & mut State < DB > ,
993+ ctx : & OpPayloadBuilderCtx < FlashblocksExtraCtx > ,
994+ best_payload : ( OpBuiltPayload , BundleState ) ,
995+ ) -> Result < OpBuiltPayload , PayloadBuilderError > {
996+ let ( state_root, trie_updates, hashed_state) =
997+ calculate_state_root_on_resolve ( state, ctx, best_payload. 1 ) ?;
998+
999+ let payload_id = best_payload. 0 . id ( ) ;
1000+ let fees = best_payload. 0 . fees ( ) ;
1001+ let executed_block = best_payload. 0 . executed_block ( ) . ok_or_else ( || {
1002+ PayloadBuilderError :: Other (
1003+ eyre:: eyre!( "No executed block available in best payload for payload resolution" )
1004+ . into ( ) ,
1005+ )
1006+ } ) ?;
1007+ let block = best_payload. 0 . into_sealed_block ( ) . into_block ( ) ;
1008+ let ( mut header, body) = block. split ( ) ;
1009+ header. state_root = state_root;
1010+ let updated_block = alloy_consensus:: Block :: < OpTransactionSigned > :: new ( header, body) ;
1011+ let recovered_block = RecoveredBlock :: new_unhashed (
1012+ updated_block. clone ( ) ,
1013+ executed_block. recovered_block ( ) . senders ( ) . to_vec ( ) ,
1014+ ) ;
1015+ let sealed_block = Arc :: new ( updated_block. seal_slow ( ) ) ;
1016+
1017+ let executed = ExecutedBlock {
1018+ recovered_block : Arc :: new ( recovered_block) ,
1019+ execution_output : executed_block. execution_output . clone ( ) ,
1020+ hashed_state : Arc :: new ( hashed_state) ,
1021+ trie_updates : Arc :: new ( trie_updates) ,
1022+ } ;
1023+ let updated_payload = OpBuiltPayload :: new ( payload_id, sealed_block, fees, Some ( executed) ) ;
1024+ if let Err ( e) = self . built_payload_tx . send ( updated_payload. clone ( ) ) . await {
1025+ warn ! (
1026+ target: "payload_builder" ,
1027+ error = %e,
1028+ "Failed to send updated payload"
1029+ ) ;
1030+ }
1031+ debug ! (
1032+ target: "payload_builder" ,
1033+ state_root = %state_root,
1034+ "Updated payload with calculated state root"
1035+ ) ;
1036+
1037+ Ok ( updated_payload)
1038+ }
1039+
9281040 /// Do some logging and metric recording when we stop build flashblocks
9291041 fn record_flashblocks_metrics (
9301042 & self ,
@@ -1220,7 +1332,7 @@ pub(super) fn build_block<DB, P, ExtraCtx>(
12201332 ctx : & OpPayloadBuilderCtx < ExtraCtx > ,
12211333 info : & mut ExecutionInfo < FlashblocksExecutionInfo > ,
12221334 calculate_state_root : bool ,
1223- ) -> Result < ( OpBuiltPayload , OpFlashblockPayload ) , PayloadBuilderError >
1335+ ) -> Result < ( OpBuiltPayload , OpFlashblockPayload , BundleState ) , PayloadBuilderError >
12241336where
12251337 DB : Database < Error = ProviderError > + AsRef < P > ,
12261338 P : StateRootProvider + HashedPostStateProvider + StorageRootProvider ,
@@ -1438,7 +1550,7 @@ where
14381550 } ;
14391551
14401552 // We clean bundle and place initial state transaction back
1441- state. take_bundle ( ) ;
1553+ let bundle_state = state. take_bundle ( ) ;
14421554 state. transition_state = untouched_transition_state;
14431555
14441556 Ok ( (
@@ -1449,5 +1561,43 @@ where
14491561 Some ( executed) ,
14501562 ) ,
14511563 fb_payload,
1564+ bundle_state,
14521565 ) )
14531566}
1567+
1568+ /// Calculates only the state root for an existing payload
1569+ fn calculate_state_root_on_resolve < DB , P , ExtraCtx > (
1570+ state : & mut State < DB > ,
1571+ ctx : & OpPayloadBuilderCtx < ExtraCtx > ,
1572+ bundle_state : BundleState ,
1573+ ) -> Result < ( B256 , TrieUpdates , HashedPostState ) , PayloadBuilderError >
1574+ where
1575+ DB : Database < Error = ProviderError > + AsRef < P > ,
1576+ P : StateRootProvider + HashedPostStateProvider + StorageRootProvider ,
1577+ ExtraCtx : std:: fmt:: Debug + Default ,
1578+ {
1579+ let state_root_start_time = Instant :: now ( ) ;
1580+ let state_provider = state. database . as_ref ( ) ;
1581+ let hashed_state = state_provider. hashed_post_state ( & bundle_state) ;
1582+ let state_root_updates = state
1583+ . database
1584+ . as_ref ( )
1585+ . state_root_with_updates ( hashed_state. clone ( ) )
1586+ . inspect_err ( |err| {
1587+ warn ! ( target: "payload_builder" ,
1588+ parent_header=%ctx. parent( ) . hash( ) ,
1589+ %err,
1590+ "failed to calculate state root for payload"
1591+ ) ;
1592+ } ) ?;
1593+
1594+ let state_root_calculation_time = state_root_start_time. elapsed ( ) ;
1595+ ctx. metrics
1596+ . state_root_calculation_duration
1597+ . record ( state_root_calculation_time) ;
1598+ ctx. metrics
1599+ . state_root_calculation_gauge
1600+ . set ( state_root_calculation_time) ;
1601+
1602+ Ok ( ( state_root_updates. 0 , state_root_updates. 1 , hashed_state) )
1603+ }
0 commit comments