@@ -18,6 +18,7 @@ use crate::routing::router::{PaymentParameters, RouteParameters};
1818use crate :: sign:: EntropySource ;
1919use crate :: chain:: transaction:: OutPoint ;
2020use crate :: events:: { ClosureReason , Event , HTLCHandlingFailureType } ;
21+ use crate :: ln:: chan_utils:: HTLCClaim ;
2122use crate :: ln:: channelmanager:: { ChannelManager , ChannelManagerReadArgs , PaymentId , RAACommitmentOrder } ;
2223use crate :: ln:: outbound_payment:: RecipientOnionFields ;
2324use crate :: ln:: msgs;
@@ -2247,3 +2248,217 @@ fn test_reload_with_mpp_claims_on_same_channel() {
22472248 // nodes[0] should now have received both fulfills and generate PaymentSent.
22482249 expect_payment_sent ( & nodes[ 0 ] , payment_preimage, None , true , true ) ;
22492250}
2251+
2252+ #[ test]
2253+ fn test_reload_with_in_flight_preimage_claim ( ) {
2254+ do_test_reload_with_in_flight_preimage_claim ( false ) ;
2255+ do_test_reload_with_in_flight_preimage_claim ( true ) ;
2256+ }
2257+
2258+ fn do_test_reload_with_in_flight_preimage_claim ( close_channel : bool ) {
2259+ // Test that if a node receives a payment and calls `claim_funds`, but the
2260+ // `ChannelMonitorUpdate` containing the preimage is still in-flight (not yet persisted),
2261+ // then after a restart the payment claim completes correctly using the preimage from the
2262+ // in-flight monitor update.
2263+ //
2264+ // If close_channel is set, the channel is force-closed before reload to test that in-flight
2265+ // monitor updates are preserved across channel closure.
2266+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
2267+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
2268+ let persister;
2269+ let new_chain_monitor;
2270+ let persister_2;
2271+ let new_chain_monitor_2;
2272+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
2273+ let nodes_1_deserialized;
2274+ let nodes_1_deserialized_2;
2275+ let mut nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
2276+
2277+ let node_0_id = nodes[ 0 ] . node . get_our_node_id ( ) ;
2278+ let node_1_id = nodes[ 1 ] . node . get_our_node_id ( ) ;
2279+
2280+ let chan_id = create_announced_chan_between_nodes ( & nodes, 0 , 1 ) . 2 ;
2281+
2282+ // Send a payment from nodes[0] to nodes[1].
2283+ let amt_msat = 1_000_000 ;
2284+ let ( route, payment_hash, payment_preimage, payment_secret) =
2285+ get_route_and_payment_hash ! ( nodes[ 0 ] , nodes[ 1 ] , amt_msat) ;
2286+ send_along_route_with_secret (
2287+ & nodes[ 0 ] , route, & [ & [ & nodes[ 1 ] ] ] , amt_msat, payment_hash, payment_secret,
2288+ ) ;
2289+
2290+ // Serialize the monitor before claiming so it doesn't have the preimage update.
2291+ let mon_serialized_pre_claim = get_monitor ! ( nodes[ 1 ] , chan_id) . encode ( ) ;
2292+
2293+ // Set the persister to return InProgress so the preimage monitor update will be stored as
2294+ // in-flight.
2295+ chanmon_cfgs[ 1 ] . persister . set_update_ret ( ChannelMonitorUpdateStatus :: InProgress ) ;
2296+
2297+ nodes[ 1 ] . node . claim_funds ( payment_preimage) ;
2298+ check_added_monitors ( & nodes[ 1 ] , 1 ) ;
2299+
2300+ // The PaymentClaimed event is held back until the monitor update completes.
2301+ assert ! ( nodes[ 1 ] . node. get_and_clear_pending_events( ) . is_empty( ) ) ;
2302+
2303+ // Disconnect peers before reload/close.
2304+ nodes[ 0 ] . node . peer_disconnected ( node_1_id) ;
2305+ nodes[ 1 ] . node . peer_disconnected ( node_0_id) ;
2306+
2307+ let ( commitment_tx, coinbase_tx) = if close_channel {
2308+ // Provide anchor reserves for fee bumping (anchors are enabled by default).
2309+ let coinbase_tx = provide_anchor_reserves ( & nodes) ;
2310+
2311+ // Force close the channel - the in-flight preimage update should be preserved
2312+ nodes[ 1 ] . node . force_close_broadcasting_latest_txn ( & chan_id, & node_0_id, "test" . to_string ( ) ) . unwrap ( ) ;
2313+ check_closed_broadcast ( & nodes[ 1 ] , 1 , false ) ;
2314+ check_added_monitors ( & nodes[ 1 ] , 1 ) ;
2315+ let reason = ClosureReason :: HolderForceClosed { broadcasted_latest_txn : Some ( true ) , message : "test" . to_string ( ) } ;
2316+ check_closed_event ( & nodes[ 1 ] , 1 , reason, & [ node_0_id] , 100_000 ) ;
2317+ // Handle the bump event to broadcast the commitment tx (anchors are enabled by default).
2318+ handle_bump_close_event ( & nodes[ 1 ] ) ;
2319+ let txn = nodes[ 1 ] . tx_broadcaster . txn_broadcast ( ) ;
2320+ assert_eq ! ( txn. len( ) , 1 ) ;
2321+ ( Some ( txn. into_iter ( ) . next ( ) . unwrap ( ) ) , Some ( coinbase_tx) )
2322+ } else {
2323+ ( None , None )
2324+ } ;
2325+
2326+ // Serialize the ChannelManager containing the in-flight preimage monitor update.
2327+ let node_1_serialized = nodes[ 1 ] . node . encode ( ) ;
2328+
2329+ reload_node ! (
2330+ nodes[ 1 ] ,
2331+ node_1_serialized,
2332+ & [ & mon_serialized_pre_claim] ,
2333+ persister,
2334+ new_chain_monitor,
2335+ nodes_1_deserialized
2336+ ) ;
2337+
2338+ // The PaymentClaimed event should be regenerated from the in-flight update.
2339+ expect_payment_claimed ! ( nodes[ 1 ] , payment_hash, amt_msat) ;
2340+
2341+ if close_channel {
2342+ check_added_monitors ( & nodes[ 1 ] , 4 ) ;
2343+ {
2344+ let monitor_updates = nodes[ 1 ] . chain_monitor . monitor_updates . lock ( ) . unwrap ( ) ;
2345+ let updates = monitor_updates. get ( & chan_id) . unwrap ( ) ;
2346+ for ( i, update) in updates. iter ( ) . rev ( ) . take ( 4 ) . enumerate ( ) {
2347+ match i {
2348+ 0 => {
2349+ // The latest update should be because we processed PaymentClaimed on a closed channel.
2350+ assert_eq ! ( update. updates. len( ) , 1 ) ;
2351+ assert ! ( matches!( update. updates[ 0 ] , ChannelMonitorUpdateStep :: InboundPaymentClaimed { .. } ) ) ;
2352+ } ,
2353+ 1 => {
2354+ // Because pre-reload our preimage update was in-flight, we will still generate a
2355+ // redundant one on startup
2356+ assert_eq ! ( update. updates. len( ) , 1 ) ;
2357+ assert ! ( matches!( update. updates[ 0 ] , ChannelMonitorUpdateStep :: PaymentPreimage { payment_info: None , .. } ) )
2358+ } ,
2359+ 2 => {
2360+ // The force close update
2361+ assert_eq ! ( update. updates. len( ) , 1 ) ;
2362+ assert ! ( matches!( update. updates[ 0 ] , ChannelMonitorUpdateStep :: ChannelForceClosed { should_broadcast: true } ) ) ;
2363+ } ,
2364+ 3 => {
2365+ // The original in-flight claim with full payment info and counterparty commitment
2366+ assert_eq ! ( update. updates. len( ) , 2 ) ;
2367+ assert ! ( matches!( update. updates[ 0 ] , ChannelMonitorUpdateStep :: PaymentPreimage { payment_info: Some ( _) , .. } ) ) ;
2368+ assert ! ( matches!( update. updates[ 1 ] , ChannelMonitorUpdateStep :: LatestCounterpartyCommitmentTXInfo { .. } ) ) ;
2369+ } ,
2370+ _ => panic ! ( "Unexpected update index" ) ,
2371+ }
2372+ }
2373+ }
2374+ } else {
2375+ check_added_monitors ( & nodes[ 1 ] , 1 ) ;
2376+ {
2377+ let monitor_updates = nodes[ 1 ] . chain_monitor . monitor_updates . lock ( ) . unwrap ( ) ;
2378+ let updates = monitor_updates. get ( & chan_id) . unwrap ( ) ;
2379+ let update = updates. last ( ) . unwrap ( ) ;
2380+ assert_eq ! ( update. updates. len( ) , 2 ) ;
2381+ assert ! ( matches!( update. updates[ 0 ] , ChannelMonitorUpdateStep :: PaymentPreimage { payment_info: Some ( _) , .. } ) ) ;
2382+ assert ! ( matches!( update. updates[ 1 ] , ChannelMonitorUpdateStep :: LatestCounterpartyCommitmentTXInfo { .. } ) ) ;
2383+ }
2384+ }
2385+
2386+ // Verify the monitor now has the preimage (the in-flight update was applied during reload).
2387+ assert ! (
2388+ get_monitor!( nodes[ 1 ] , chan_id) . test_get_all_stored_preimages( ) . contains_key( & payment_hash) ,
2389+ "Monitor should have preimage after in-flight update replay"
2390+ ) ;
2391+
2392+ // Second reload to test for redundant PaymentClaimed events.
2393+ let node_1_serialized_2 = nodes[ 1 ] . node . encode ( ) ;
2394+ let mon_serialized_2 = get_monitor ! ( nodes[ 1 ] , chan_id) . encode ( ) ;
2395+
2396+ reload_node ! (
2397+ nodes[ 1 ] ,
2398+ node_1_serialized_2,
2399+ & [ & mon_serialized_2] ,
2400+ persister_2,
2401+ new_chain_monitor_2,
2402+ nodes_1_deserialized_2
2403+ ) ;
2404+
2405+ // The second reload should not replay any monitor updates (they were already applied).
2406+ check_added_monitors ( & nodes[ 1 ] , 0 ) ;
2407+
2408+ if !close_channel {
2409+ // If the channel is still open, there will be a redundant PaymentClaimed event generated each
2410+ // restart until the HTLC is removed.
2411+ expect_payment_claimed ! ( nodes[ 1 ] , payment_hash, amt_msat) ;
2412+
2413+ // Complete the payment. Use pending_htlc_claims instead of pending_cell_htlc_claims
2414+ // because the latter expects a monitor update, but the claim is already in the monitor.
2415+ let mut reconnect_args = ReconnectArgs :: new ( & nodes[ 1 ] , & nodes[ 0 ] ) ;
2416+ reconnect_args. pending_htlc_claims = ( 0 , 1 ) ;
2417+ reconnect_nodes ( reconnect_args) ;
2418+
2419+ expect_payment_sent ( & nodes[ 0 ] , payment_preimage, None , true , true ) ;
2420+ } else {
2421+ // No redundant PaymentClaimed event.
2422+ assert ! ( nodes[ 1 ] . node. get_and_clear_pending_events( ) . is_empty( ) ) ;
2423+
2424+ // Mine the commitment tx on both nodes so nodes[0] sees the channel is closed.
2425+ let commitment_tx = commitment_tx. unwrap ( ) ;
2426+ let coinbase_tx = coinbase_tx. unwrap ( ) ;
2427+ mine_transaction ( & nodes[ 0 ] , & commitment_tx) ;
2428+ mine_transaction ( & nodes[ 1 ] , & commitment_tx) ;
2429+
2430+ // Peers are disconnected, so no error message is sent.
2431+ check_closed_broadcast ( & nodes[ 0 ] , 1 , false ) ;
2432+ check_added_monitors ( & nodes[ 0 ] , 1 ) ;
2433+ check_closed_event ( & nodes[ 0 ] , 1 , ClosureReason :: CommitmentTxConfirmed , & [ node_1_id] , 100_000 ) ;
2434+
2435+ // nodes[1] broadcasts HTLC claim tx with the preimage.
2436+ // We get 2 BumpTransaction events: ChannelClose (for anchor) and HTLCResolution.
2437+ let events = nodes[ 1 ] . chain_monitor . chain_monitor . get_and_clear_pending_events ( ) ;
2438+ assert ! ( events. len( ) <= 2 ) ;
2439+ for event in events {
2440+ if let Event :: BumpTransaction ( bump) = event {
2441+ nodes[ 1 ] . bump_tx_handler . handle_event ( & bump) ;
2442+ } else {
2443+ panic ! ( "Unexpected event: {:?}" , event) ;
2444+ }
2445+ }
2446+ // Filter for HTLC claim tx by checking for preimage in the witness.
2447+ let htlc_claim_txn: Vec < _ > = nodes[ 1 ]
2448+ . tx_broadcaster
2449+ . txn_broadcast ( )
2450+ . into_iter ( )
2451+ . filter ( |tx| {
2452+ tx. input . iter ( ) . any ( |inp| {
2453+ matches ! ( HTLCClaim :: from_witness( & inp. witness) , Some ( HTLCClaim :: AcceptedPreimage ) )
2454+ } )
2455+ } )
2456+ . collect ( ) ;
2457+ assert_eq ! ( htlc_claim_txn. len( ) , 1 ) ;
2458+ check_spends ! ( htlc_claim_txn[ 0 ] , commitment_tx, coinbase_tx) ;
2459+
2460+ // Mine the HTLC claim on nodes[0] - it learns the preimage and generates PaymentSent.
2461+ mine_transaction ( & nodes[ 0 ] , & htlc_claim_txn[ 0 ] ) ;
2462+ expect_payment_sent ( & nodes[ 0 ] , payment_preimage, None , true , true ) ;
2463+ }
2464+ }
0 commit comments