@@ -2782,6 +2782,157 @@ async fn weak_subjectivity_sync_without_blobs() {
27822782 weak_subjectivity_sync_test ( slots, checkpoint_slot, None , false ) . await
27832783}
27842784
2785+ #[ tokio:: test]
2786+ async fn pruning_preserves_payload_aligned ( ) {
2787+ type E = MinimalEthSpec ;
2788+ let num_initial_slots = E :: slots_per_epoch ( ) * 11 ;
2789+ let checkpoint_slot = Slot :: new ( E :: slots_per_epoch ( ) * 9 ) ; // Aligned Slot
2790+
2791+ let slots = ( 1 ..num_initial_slots) . map ( Slot :: new) . collect :: < Vec < _ > > ( ) ;
2792+
2793+ verify_pruning_preserves_payload ( checkpoint_slot, slots) . await ;
2794+ }
2795+
2796+ #[ tokio:: test]
2797+ async fn pruning_preserves_payload_unaligned ( ) {
2798+ type E = MinimalEthSpec ;
2799+ let num_initial_slots = E :: slots_per_epoch ( ) * 11 ;
2800+ let checkpoint_slot = Slot :: new ( E :: slots_per_epoch ( ) * 9 - 3 ) ;
2801+
2802+ let slots = ( 1 ..num_initial_slots)
2803+ . map ( Slot :: new)
2804+ . filter ( |& slot| slot <= checkpoint_slot || slot > checkpoint_slot + 3 )
2805+ . collect :: < Vec < _ > > ( ) ;
2806+
2807+ verify_pruning_preserves_payload ( checkpoint_slot, slots) . await ;
2808+ }
2809+
2810+ async fn verify_pruning_preserves_payload (
2811+ checkpoint_slot : Slot ,
2812+ slots : Vec < Slot > ,
2813+ ) {
2814+ type E = MinimalEthSpec ;
2815+ let spec = test_spec :: < E > ( ) ;
2816+
2817+ // Requires Execution Payloads.
2818+ let Some ( _) = spec. deneb_fork_epoch else {
2819+ return ;
2820+ } ;
2821+
2822+ // Create a standard chain.
2823+ let temp1 = tempdir ( ) . unwrap ( ) ;
2824+ let full_store = get_store_generic ( & temp1, StoreConfig :: default ( ) , spec. clone ( ) ) ;
2825+
2826+ let harness = get_harness_import_all_data_columns ( full_store. clone ( ) , LOW_VALIDATOR_COUNT ) ;
2827+ let all_validators = ( 0 ..LOW_VALIDATOR_COUNT ) . collect :: < Vec < _ > > ( ) ;
2828+
2829+ let ( genesis_state, genesis_state_root) = harness. get_current_state_and_root ( ) ;
2830+ harness
2831+ . add_attested_blocks_at_slots (
2832+ genesis_state. clone ( ) ,
2833+ genesis_state_root,
2834+ & slots,
2835+ & all_validators,
2836+ )
2837+ . await ;
2838+
2839+ // Extract snapshot data.
2840+ let wss_block_root = harness
2841+ . chain
2842+ . block_root_at_slot ( checkpoint_slot, WhenSlotSkipped :: Prev )
2843+ . unwrap ( )
2844+ . unwrap ( ) ;
2845+ let wss_state_root = harness
2846+ . chain
2847+ . state_root_at_slot ( checkpoint_slot)
2848+ . unwrap ( )
2849+ . unwrap ( ) ;
2850+
2851+ let wss_block = harness
2852+ . chain
2853+ . store
2854+ . get_full_block ( & wss_block_root)
2855+ . unwrap ( )
2856+ . unwrap ( ) ;
2857+ let wss_blobs_opt = harness
2858+ . chain
2859+ . get_or_reconstruct_blobs ( & wss_block_root)
2860+ . unwrap ( ) ;
2861+ let wss_state = full_store
2862+ . get_state ( & wss_state_root, Some ( checkpoint_slot) , CACHE_STATE_IN_TESTS )
2863+ . unwrap ( )
2864+ . unwrap ( ) ;
2865+
2866+ // Ensure client with `prune_payloads = true`.
2867+ let temp2 = tempdir ( ) . unwrap ( ) ;
2868+ let store_config = StoreConfig {
2869+ prune_payloads : true ,
2870+ ..StoreConfig :: default ( )
2871+ } ;
2872+
2873+ let store = get_store_generic ( & temp2, store_config, spec. clone ( ) ) ;
2874+ let slot_clock = TestingSlotClock :: new (
2875+ Slot :: new ( 0 ) ,
2876+ Duration :: from_secs ( harness. chain . genesis_time ) ,
2877+ Duration :: from_secs ( spec. seconds_per_slot ) ,
2878+ ) ;
2879+ slot_clock. set_slot ( harness. get_current_slot ( ) . as_u64 ( ) ) ;
2880+
2881+ let chain_config = ChainConfig {
2882+ reconstruct_historic_states : true ,
2883+ ..ChainConfig :: default ( )
2884+ } ;
2885+
2886+ let trusted_setup = get_kzg ( & spec) ;
2887+ let ( shutdown_tx, _shutdown_rx) = futures:: channel:: mpsc:: channel ( 1 ) ;
2888+ let mock = mock_execution_layer_from_parts (
2889+ harness. spec . clone ( ) ,
2890+ harness. runtime . task_executor . clone ( ) ,
2891+ ) ;
2892+ let all_custody_columns = ( 0 ..spec. number_of_custody_groups ) . collect :: < Vec < _ > > ( ) ;
2893+
2894+ let beacon_chain = BeaconChainBuilder :: < DiskHarnessType < E > > :: new ( MinimalEthSpec , trusted_setup)
2895+ . chain_config ( chain_config)
2896+ . store ( store. clone ( ) )
2897+ . custom_spec ( spec. clone ( ) . into ( ) )
2898+ . task_executor ( harness. chain . task_executor . clone ( ) )
2899+ . weak_subjectivity_state (
2900+ wss_state,
2901+ wss_block. clone ( ) ,
2902+ wss_blobs_opt. clone ( ) ,
2903+ genesis_state,
2904+ )
2905+ . unwrap ( )
2906+ . store_migrator_config ( MigratorConfig :: default ( ) . blocking ( ) )
2907+ . slot_clock ( slot_clock)
2908+ . shutdown_sender ( shutdown_tx)
2909+ . event_handler ( Some ( ServerSentEventHandler :: new_with_capacity ( 1 ) ) )
2910+ . execution_layer ( Some ( mock. el ) )
2911+ . ordered_custody_column_indices ( all_custody_columns)
2912+ . rng ( Box :: new ( StdRng :: seed_from_u64 ( 42 ) ) )
2913+ . build ( ) ;
2914+
2915+ assert ! ( beacon_chain. is_ok( ) , "Beacon Chain failed to build" ) ;
2916+ let chain = beacon_chain. as_ref ( ) . unwrap ( ) ;
2917+
2918+ // Trigger Pruning Explicitly.
2919+ chain
2920+ . store
2921+ . try_prune_execution_payloads ( true )
2922+ . expect ( "Pruning should succeed" ) ;
2923+
2924+ // Assert the Split Payload still exists.
2925+ let payload_exists = chain
2926+ . store
2927+ . execution_payload_exists ( & wss_block_root)
2928+ . unwrap_or ( false ) ;
2929+
2930+ assert ! (
2931+ payload_exists,
2932+ "Split block payload must exist after pruning"
2933+ ) ;
2934+ }
2935+
27852936async fn weak_subjectivity_sync_test (
27862937 slots : Vec < Slot > ,
27872938 checkpoint_slot : Slot ,
0 commit comments