@@ -47,7 +47,7 @@ use crate::blinded_path::IntroductionNode;
4747use crate :: blinded_path:: message:: BlindedMessagePath ;
4848use crate :: blinded_path:: payment:: { Bolt12OfferContext , Bolt12RefundContext , PaymentContext } ;
4949use crate :: blinded_path:: message:: { MessageContext , OffersContext } ;
50- use crate :: events:: { Event , MessageSendEventsProvider , PaymentFailureReason , PaymentPurpose } ;
50+ use crate :: events:: { ClosureReason , Event , MessageSendEventsProvider , PaymentFailureReason , PaymentPurpose } ;
5151use crate :: ln:: channelmanager:: { Bolt12PaymentError , MAX_SHORT_LIVED_RELATIVE_EXPIRY , PaymentId , RecentPaymentDetails , Retry , self } ;
5252use crate :: ln:: features:: Bolt12InvoiceFeatures ;
5353use crate :: ln:: functional_test_utils:: * ;
@@ -64,6 +64,7 @@ use crate::onion_message::offers::OffersMessage;
6464use crate :: onion_message:: packet:: ParsedOnionMessageContents ;
6565use crate :: routing:: gossip:: { NodeAlias , NodeId } ;
6666use crate :: sign:: { NodeSigner , Recipient } ;
67+ use crate :: util:: ser:: Writeable ;
6768
6869use crate :: prelude:: * ;
6970
@@ -2253,3 +2254,92 @@ fn fails_paying_invoice_with_unknown_required_features() {
22532254 _ => panic ! ( "Expected Event::PaymentFailed with reason" ) ,
22542255 }
22552256}
2257+
2258+ #[ test]
2259+ fn no_double_pay_with_stale_channelmanager ( ) {
2260+ // This tests the following bug:
2261+ // - An outbound payment is AwaitingInvoice
2262+ // - We receive an invoice and lock the HTLCs into the relevant ChannelMonitors
2263+ // - The monitors are successfully persisted, but the ChannelManager fails to persist, so the
2264+ // payment remains AwaitingInvoice
2265+ // - We restart, causing the channels to close due to a stale ChannelManager
2266+ // - We receive a duplicate invoice, and attempt to pay it again due to the payment still being
2267+ // AwaitingInvoice in the stale ChannelManager
2268+ // After the fix for this, we will notice that the payment is already locked into the monitors on
2269+ // startup and transition the incorrectly-AwaitingInvoice payment to Retryable, which prevents
2270+ // double-paying on duplicate invoice receipt.
2271+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
2272+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
2273+ let persister;
2274+ let chain_monitor;
2275+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
2276+ let alice_deserialized;
2277+ let mut nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
2278+ let chan_id_0 = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 10_000_000 , 1_000_000_000 ) . 2 ;
2279+ let chan_id_1 = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 10_000_000 , 1_000_000_000 ) . 2 ;
2280+
2281+ let alice_id = nodes[ 0 ] . node . get_our_node_id ( ) ;
2282+ let bob_id = nodes[ 1 ] . node . get_our_node_id ( ) ;
2283+
2284+ let amt_msat = nodes[ 0 ] . node . list_usable_channels ( ) [ 0 ] . next_outbound_htlc_limit_msat + 1 ; // Force MPP
2285+ let offer = nodes[ 1 ] . node
2286+ . create_offer_builder ( None ) . unwrap ( )
2287+ . clear_paths ( )
2288+ . amount_msats ( amt_msat)
2289+ . build ( ) . unwrap ( ) ;
2290+ assert_eq ! ( offer. signing_pubkey( ) , Some ( bob_id) ) ;
2291+ assert ! ( offer. paths( ) . is_empty( ) ) ;
2292+
2293+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
2294+ nodes[ 0 ] . node . pay_for_offer ( & offer, None , None , None , payment_id, Retry :: Attempts ( 0 ) , None ) . unwrap ( ) ;
2295+ expect_recent_payment ! ( nodes[ 0 ] , RecentPaymentDetails :: AwaitingInvoice , payment_id) ;
2296+
2297+ let invreq_om = nodes[ 0 ] . onion_messenger . next_onion_message_for_peer ( bob_id) . unwrap ( ) ;
2298+ nodes[ 1 ] . onion_messenger . handle_onion_message ( alice_id, & invreq_om) ;
2299+
2300+ // Save the manager while the payment is in state AwaitingInvoice so we can reload it later.
2301+ let alice_chan_manager_serialized = nodes[ 0 ] . node . encode ( ) ;
2302+
2303+ let invoice_om = nodes[ 1 ] . onion_messenger . next_onion_message_for_peer ( alice_id) . unwrap ( ) ;
2304+ nodes[ 0 ] . onion_messenger . handle_onion_message ( bob_id, & invoice_om) ;
2305+ let payment_hash = extract_invoice ( & nodes[ 0 ] , & invoice_om) . 0 . payment_hash ( ) ;
2306+
2307+ let expected_route: & [ & [ & Node ] ] = & [ & [ & nodes[ 1 ] ] , & [ & nodes[ 1 ] ] ] ;
2308+ let mut events = nodes[ 0 ] . node . get_and_clear_pending_msg_events ( ) ;
2309+ assert_eq ! ( events. len( ) , 2 ) ;
2310+ check_added_monitors ! ( nodes[ 0 ] , 2 ) ;
2311+
2312+ let ev = remove_first_msg_event_to_node ( & bob_id, & mut events) ;
2313+ let args = PassAlongPathArgs :: new ( & nodes[ 0 ] , expected_route[ 0 ] , amt_msat, payment_hash, ev)
2314+ . without_clearing_recipient_events ( ) ;
2315+ do_pass_along_path ( args) ;
2316+
2317+ let ev = remove_first_msg_event_to_node ( & bob_id, & mut events) ;
2318+ let args = PassAlongPathArgs :: new ( & nodes[ 0 ] , expected_route[ 0 ] , amt_msat, payment_hash, ev)
2319+ . without_clearing_recipient_events ( ) ;
2320+ do_pass_along_path ( args) ;
2321+
2322+ expect_recent_payment ! ( nodes[ 0 ] , RecentPaymentDetails :: Pending , payment_id) ;
2323+ match get_event ! ( nodes[ 1 ] , Event :: PaymentClaimable ) {
2324+ Event :: PaymentClaimable { .. } => { } ,
2325+ _ => panic ! ( "No Event::PaymentClaimable" ) ,
2326+ }
2327+
2328+ // Reload with the stale manager and check that receiving the invoice again won't result in a
2329+ // duplicate payment attempt.
2330+ let monitor_0 = get_monitor ! ( nodes[ 0 ] , chan_id_0) . encode ( ) ;
2331+ let monitor_1 = get_monitor ! ( nodes[ 0 ] , chan_id_1) . encode ( ) ;
2332+ reload_node ! ( nodes[ 0 ] , & alice_chan_manager_serialized, & [ & monitor_0, & monitor_1] , persister, chain_monitor, alice_deserialized) ;
2333+ // The stale manager results in closing the channels.
2334+ check_closed_event ! ( nodes[ 0 ] , 2 , ClosureReason :: OutdatedChannelManager , [ bob_id, bob_id] , 10_000_000 ) ;
2335+ check_added_monitors ! ( nodes[ 0 ] , 2 ) ;
2336+
2337+ // Alice receives a duplicate invoice, but the payment should be transitioned to Retryable by now.
2338+ nodes[ 0 ] . onion_messenger . handle_onion_message ( bob_id, & invoice_om) ;
2339+ // Previously, Alice would've attempted to pay the invoice a 2nd time. In this test case, this 2nd
2340+ // attempt would have resulted in a PaymentFailed event here, since the only channels between
2341+ // Alice and Bob is closed. Since no 2nd attempt should be made, check that no events are
2342+ // generated in response to the duplicate invoice.
2343+ assert ! ( nodes[ 0 ] . node. get_and_clear_pending_events( ) . is_empty( ) ) ;
2344+ }
2345+
0 commit comments