@@ -2458,12 +2458,15 @@ fn test_restored_packages_retry() {
24582458fn do_test_duplicate_delayed_holder_htlc_claims_after_claim_funds_replay ( p2a_anchor : bool ) {
24592459 let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
24602460 let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
2461+ let persister;
2462+ let new_chain_monitor;
2463+ let node_deserialized;
24612464 let mut anchors_config = test_default_channel_config ( ) ;
24622465 anchors_config. channel_handshake_config . negotiate_anchors_zero_fee_htlc_tx = true ;
24632466 anchors_config. channel_handshake_config . negotiate_anchor_zero_fee_commitments = p2a_anchor;
24642467 let node_chanmgrs =
24652468 create_node_chanmgrs ( 2 , & node_cfgs, & [ Some ( anchors_config. clone ( ) ) , Some ( anchors_config) ] ) ;
2466- let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
2469+ let mut nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
24672470
24682471 let coinbase_tx = provide_anchor_reserves ( & nodes) ;
24692472 let ( _, _, chan_id, funding_tx) =
@@ -2542,11 +2545,14 @@ fn do_test_duplicate_delayed_holder_htlc_claims_after_claim_funds_replay(p2a_anc
25422545 // the delayed package's outpoints.
25432546 connect_blocks ( & nodes[ 0 ] , TEST_FINAL_CLTV + 1 ) ;
25442547
2545- let mut htlc_event_sizes = nodes[ 0 ]
2548+ let events = nodes[ 0 ]
25462549 . chain_monitor
25472550 . chain_monitor
25482551 . get_and_clear_pending_events ( )
25492552 . into_iter ( )
2553+ . collect :: < Vec < _ > > ( ) ;
2554+ let mut htlc_event_sizes = events
2555+ . iter ( )
25502556 . filter_map ( |event| {
25512557 if let Event :: BumpTransaction ( BumpTransactionEvent :: HTLCResolution {
25522558 htlc_descriptors, ..
@@ -2560,6 +2566,80 @@ fn do_test_duplicate_delayed_holder_htlc_claims_after_claim_funds_replay(p2a_anc
25602566 . collect :: < Vec < _ > > ( ) ;
25612567 htlc_event_sizes. sort_unstable ( ) ;
25622568 assert_eq ! ( htlc_event_sizes, vec![ 1 , 2 ] ) ;
2569+
2570+ // Drive only the replayed single-HTLC event on-chain so we can replay the
2571+ // preimage once the spend is anti-reorg final, then again after reload.
2572+ for event in events {
2573+ if let Event :: BumpTransaction ( event) = event {
2574+ let is_single_htlc = if let BumpTransactionEvent :: HTLCResolution {
2575+ ref htlc_descriptors,
2576+ ..
2577+ } = event
2578+ {
2579+ htlc_descriptors. len ( ) == 1
2580+ } else {
2581+ false
2582+ } ;
2583+ if is_single_htlc {
2584+ nodes[ 0 ] . bump_tx_handler . handle_event ( & event) ;
2585+ break ;
2586+ }
2587+ }
2588+ }
2589+ let mut htlc_txn = nodes[ 0 ] . tx_broadcaster . unique_txn_broadcast ( ) ;
2590+ assert_eq ! ( htlc_txn. len( ) , 1 ) ;
2591+ let htlc_tx = htlc_txn. pop ( ) . unwrap ( ) ;
2592+ mine_transaction ( & nodes[ 0 ] , & htlc_tx) ;
2593+ connect_blocks ( & nodes[ 0 ] , ANTI_REORG_DELAY - 1 ) ;
2594+ assert ! ( nodes[ 0 ] . chain_monitor. chain_monitor. get_and_clear_pending_events( ) . is_empty( ) ) ;
2595+
2596+ // The spend has passed anti-reorg finality, but its CSV-delayed output is
2597+ // not yet spendable. Replaying the preimage in this window must not create
2598+ // a new conflicting claim for the already-spent commitment HTLC output.
2599+ get_monitor ! ( nodes[ 0 ] , chan_id) . provide_payment_preimage_unsafe_legacy (
2600+ & claim_hash,
2601+ & claim_preimage,
2602+ & node_cfgs[ 0 ] . tx_broadcaster ,
2603+ & LowerBoundedFeeEstimator :: new ( node_cfgs[ 0 ] . fee_estimator ) ,
2604+ & nodes[ 0 ] . logger ,
2605+ ) ;
2606+ assert ! ( nodes[ 0 ] . chain_monitor. chain_monitor. get_and_clear_pending_events( ) . is_empty( ) ) ;
2607+ let balances = nodes[ 0 ]
2608+ . chain_monitor
2609+ . chain_monitor
2610+ . get_monitor ( chan_id)
2611+ . unwrap ( )
2612+ . get_claimable_balances ( ) ;
2613+ assert ! ( balances. iter( ) . any( |balance| matches!(
2614+ balance,
2615+ Balance :: ClaimableAwaitingConfirmations {
2616+ amount_satoshis: 12_000 ,
2617+ source: BalanceSource :: Htlc ,
2618+ ..
2619+ }
2620+ ) ) ) ;
2621+
2622+ connect_blocks ( & nodes[ 0 ] , BREAKDOWN_TIMEOUT as u32 - ANTI_REORG_DELAY ) ;
2623+ let _ = nodes[ 0 ] . chain_monitor . chain_monitor . get_and_clear_pending_events ( ) ;
2624+
2625+ // Reload before replaying the preimage so the regression covers persisted
2626+ // resolution state, not only in-memory filtering.
2627+ let serialized_channel_manager = nodes[ 0 ] . node . encode ( ) ;
2628+ let serialized_monitor = get_monitor ! ( nodes[ 0 ] , chan_id) . encode ( ) ;
2629+ reload_node ! (
2630+ nodes[ 0 ] , & serialized_channel_manager, & [ & serialized_monitor] , persister,
2631+ new_chain_monitor, node_deserialized
2632+ ) ;
2633+
2634+ // Replaying the preimage update must not regenerate a claim for the HTLC
2635+ // whose commitment output has anti-reorg persisted resolution state.
2636+ get_monitor ! ( nodes[ 0 ] , chan_id) . provide_payment_preimage_unsafe_legacy (
2637+ & claim_hash, & claim_preimage, & node_cfgs[ 0 ] . tx_broadcaster ,
2638+ & LowerBoundedFeeEstimator :: new ( node_cfgs[ 0 ] . fee_estimator ) , & nodes[ 0 ] . logger ,
2639+ ) ;
2640+ assert ! ( nodes[ 0 ] . chain_monitor. chain_monitor. get_and_clear_pending_events( ) . is_empty( ) ) ;
2641+ expect_payment_claimed ! ( nodes[ 0 ] , claim_hash, 12_000_000 ) ;
2642+ check_added_monitors ( & nodes[ 0 ] , 1 ) ;
25632643}
25642644
25652645#[ test]
0 commit comments