@@ -38,10 +38,14 @@ use eth_sparse_mpt::SparseTrieLocalCache;
3838use evm:: EthCachedEvmFactory ;
3939use jsonrpsee:: core:: Serialize ;
4040use parking_lot:: Mutex ;
41- use priority_update:: PriorityUpdatePool ;
41+ use priority_update:: {
42+ select_unwritten_slots,
43+ used_priority_update_tracer:: { UsedPriorityStateTracer , UsedStorageSlotStatus } ,
44+ PriorityUpdatePool ,
45+ } ;
4246use rbuilder_primitives:: {
43- mev_boost:: BidAdjustmentDataV3 , BlockSpace , Order , SimValue , SimulatedOrder ,
44- TransactionSignedEcRecoveredWithBlobs ,
47+ evm_inspector :: SlotKey , mev_boost:: BidAdjustmentDataV3 , BlockSpace , Order , SimValue ,
48+ SimulatedOrder , TransactionSignedEcRecoveredWithBlobs ,
4549} ;
4650use reth:: {
4751 payload:: PayloadId ,
@@ -586,6 +590,8 @@ pub enum ExecutionError {
586590 LowerInsertedValue { before : SimValue , inplace : SimValue } ,
587591 #[ error( "Priority update produced a delayed refund" ) ]
588592 PriorityUpdateProducedDelayedRefund ,
593+ #[ error( "Order did not use the committed priority update slots: {0:?}" ) ]
594+ PriorityUpdatesNotUsed ( Vec < ( SlotKey , UsedStorageSlotStatus ) > ) ,
589595}
590596
591597impl ExecutionError {
@@ -749,24 +755,31 @@ impl<Tracer: SimulationTracer, PartialBlockExecutionTracerType: PartialBlockExec
749755 where
750756 DB : Database < Error = ProviderError > ,
751757 {
758+ let unwritten_priorit_update_slots =
759+ select_unwritten_slots ( state, & order. used_priority_updates ) ;
752760 let priority_update_orders =
753- priority_update_pool. get_updates ( state, & order. used_priority_updates ) ;
761+ priority_update_pool. get_updates ( & unwritten_priorit_update_slots) ;
762+
763+ let pu_tracer = UsedPriorityStateTracer :: new ( std:: iter:: empty ( ) ) ;
764+ let pu_storage_tracker = pu_tracer. storage_tracker ( ) ;
754765
755766 let mut fork = PartialBlockFork :: new_with_execution_tracer (
756767 state,
757768 ctx,
758769 local_ctx,
759770 & mut self . partial_block_execution_tracer ,
760771 )
761- . with_tracer ( & mut self . tracer ) ;
772+ . with_tracer ( & mut self . tracer )
773+ . with_evm_inspector ( pu_tracer. clone ( ) ) ;
762774
763775 let rollback = fork. rollback_point ( ) ;
764776 let initial_space_state = self . space_state ;
765777
766778 let mut priority_updates: Vec < Result < ExecutionResult , ExecutionError > > =
767779 Vec :: with_capacity ( priority_update_orders. len ( ) ) ;
768780
769- for priority_update_order in priority_update_orders {
781+ for priority_update_sim in priority_update_orders {
782+ let priority_update_order = priority_update_sim. order . as_ref ( ) ;
770783 let pu_rollback = fork. rollback_point ( ) ;
771784 let exec_result = fork. commit_order (
772785 priority_update_order,
@@ -805,6 +818,13 @@ impl<Tracer: SimulationTracer, PartialBlockExecutionTracerType: PartialBlockExec
805818 } ) ) ;
806819 }
807820
821+ // Register the unwritten PU slots only after every PU has committed,
822+ // so SLOADs done by the PUs themselves don't pollute the recording —
823+ // we only want to see what the order reads.
824+ for slot in & unwritten_priorit_update_slots {
825+ pu_storage_tracker. track_slot ( slot. clone ( ) ) ;
826+ }
827+
808828 let exec_result = fork. commit_order (
809829 & order. order ,
810830 self . space_state ,
@@ -836,6 +856,24 @@ impl<Tracer: SimulationTracer, PartialBlockExecutionTracerType: PartialBlockExec
836856 } ) ;
837857 }
838858
859+ // Verify every tracked PU slot was actually read on the surviving
860+ // call path. Slots only surfaced through reverted subcalls (or not
861+ // read at all) mean the owning PU is unused — fail the whole commit
862+ // so the caller can re-plan.
863+ let unused_slots: Vec < ( SlotKey , UsedStorageSlotStatus ) > = pu_tracer
864+ . classification ( )
865+ . into_iter ( )
866+ . filter ( |( _, status) | !matches ! ( status, UsedStorageSlotStatus :: Read ) )
867+ . collect ( ) ;
868+ if !unused_slots. is_empty ( ) {
869+ fork. rollback ( rollback) ;
870+ self . space_state = initial_space_state;
871+ return Ok ( OrderCommitResult {
872+ priority_updates,
873+ order : Err ( ExecutionError :: PriorityUpdatesNotUsed ( unused_slots) ) ,
874+ } ) ;
875+ }
876+
839877 for priority_update in priority_updates. iter ( ) . flatten ( ) {
840878 self . coinbase_profit += priority_update. coinbase_profit ;
841879 self . executed_tx_infos
0 commit comments