@@ -35,8 +35,18 @@ use crate::util::ser::WithoutLength;
3535use crate :: util:: test_utils;
3636use lightning_invoice:: RawBolt11Invoice ;
3737#[ cfg( async_payments) ] use {
38+ crate :: blinded_path:: message:: { BlindedMessagePath , MessageContext , OffersContext } ,
3839 crate :: ln:: inbound_payment,
40+ crate :: ln:: msgs:: OnionMessageHandler ,
41+ crate :: offers:: nonce:: Nonce ,
42+ crate :: onion_message:: async_payments:: { AsyncPaymentsMessage , AsyncPaymentsMessageHandler , ReleaseHeldHtlc } ,
43+ crate :: onion_message:: messenger:: { Destination , MessageSendInstructions , MessageRouter , PeeledOnion } ,
44+ crate :: onion_message:: offers:: OffersMessage ,
45+ crate :: onion_message:: packet:: ParsedOnionMessageContents ,
46+ crate :: types:: features:: Bolt12InvoiceFeatures ,
3947 crate :: types:: payment:: PaymentPreimage ,
48+
49+ core:: convert:: Infallible ,
4050} ;
4151
4252fn blinded_payment_path (
@@ -101,6 +111,25 @@ pub fn get_blinded_route_parameters(
101111 )
102112}
103113
114+ #[ cfg( async_payments) ]
115+ fn extract_invoice_request_reply_path < ' a , ' b , ' c > (
116+ invreq_recipient : & Node < ' a , ' b , ' c > , message : & msgs:: OnionMessage
117+ ) -> BlindedMessagePath {
118+ match invreq_recipient. onion_messenger . peel_onion_message ( message) {
119+ Ok ( PeeledOnion :: Receive ( invreq, context, reply_path) ) => {
120+ assert ! (
121+ matches!( invreq, ParsedOnionMessageContents :: Offers ( OffersMessage :: InvoiceRequest ( _) ) )
122+ ) ;
123+ assert ! (
124+ matches!( context, Some ( MessageContext :: Offers ( OffersContext :: InvoiceRequest { .. } ) ) )
125+ ) ;
126+ reply_path. unwrap ( )
127+ } ,
128+ Ok ( PeeledOnion :: Forward ( _, _) ) => panic ! ( "Unexpected onion message forward" ) ,
129+ Err ( e) => panic ! ( "Failed to process onion message {:?}" , e) ,
130+ }
131+ }
132+
104133#[ test]
105134fn one_hop_blinded_path ( ) {
106135 do_one_hop_blinded_path ( true ) ;
@@ -1416,6 +1445,210 @@ fn custom_tlvs_to_blinded_path() {
14161445 ) ;
14171446}
14181447
1448+ #[ test]
1449+ #[ cfg( async_payments) ]
1450+ fn static_invoice_unknown_required_features ( ) {
1451+ // Test that we will fail to pay a static invoice with unsupported required features.
1452+ let secp_ctx = Secp256k1 :: new ( ) ;
1453+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
1454+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
1455+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
1456+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
1457+ create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 0 ) ;
1458+ create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
1459+
1460+ let blinded_paths_to_always_online_node = nodes[ 1 ] . message_router . create_blinded_paths (
1461+ nodes[ 1 ] . node . get_our_node_id ( ) ,
1462+ MessageContext :: Offers ( OffersContext :: InvoiceRequest { nonce : Nonce ( [ 42 ; 16 ] ) } ) ,
1463+ Vec :: new ( ) , & secp_ctx
1464+ ) . unwrap ( ) ;
1465+ let ( offer_builder, nonce) =
1466+ nodes[ 2 ] . node . create_async_receive_offer_builder ( blinded_paths_to_always_online_node) . unwrap ( ) ;
1467+ let offer = offer_builder. build ( ) . unwrap ( ) ;
1468+ let static_invoice_unknown_req_features = nodes[ 2 ] . node . create_static_invoice_builder (
1469+ & offer, nonce, None
1470+ )
1471+ . unwrap ( )
1472+ . features_unchecked ( Bolt12InvoiceFeatures :: unknown ( ) )
1473+ . build_and_sign ( & secp_ctx) . unwrap ( ) ;
1474+
1475+ let amt_msat = 5000 ;
1476+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
1477+ nodes[ 0 ] . node . pay_for_offer ( & offer, None , Some ( amt_msat) , None , payment_id, Retry :: Attempts ( 0 ) , None ) . unwrap ( ) ;
1478+
1479+ // Don't forward the invreq since we don't support retrieving the static invoice from the
1480+ // recipient's LSP yet, instead manually construct the response.
1481+ let invreq_om = nodes[ 0 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 1 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1482+ let invreq_reply_path = extract_invoice_request_reply_path ( & nodes[ 1 ] , & invreq_om) ;
1483+ nodes[ 1 ] . onion_messenger . send_onion_message (
1484+ ParsedOnionMessageContents :: < Infallible > :: Offers ( OffersMessage :: StaticInvoice ( static_invoice_unknown_req_features) ) ,
1485+ MessageSendInstructions :: WithoutReplyPath { destination : Destination :: BlindedPath ( invreq_reply_path) }
1486+ ) . unwrap ( ) ;
1487+
1488+ let static_invoice_om = nodes[ 1 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 0 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1489+ nodes[ 0 ] . onion_messenger . handle_onion_message ( nodes[ 1 ] . node . get_our_node_id ( ) , & static_invoice_om) ;
1490+ let events = nodes[ 0 ] . node . get_and_clear_pending_events ( ) ;
1491+ assert_eq ! ( events. len( ) , 1 ) ;
1492+ match events[ 0 ] {
1493+ Event :: PaymentFailed { payment_hash, payment_id : ev_payment_id, reason } => {
1494+ assert_eq ! ( payment_hash, None ) ;
1495+ assert_eq ! ( payment_id, ev_payment_id) ;
1496+ assert_eq ! ( reason, Some ( PaymentFailureReason :: UnknownRequiredFeatures ) ) ;
1497+ } ,
1498+ _ => panic ! ( )
1499+ }
1500+ }
1501+
1502+ #[ test]
1503+ #[ cfg( async_payments) ]
1504+ fn ignore_unexpected_static_invoice ( ) {
1505+ // Test that we'll ignore unexpected static invoices, invoices that don't match our invoice
1506+ // request, and duplicate invoices.
1507+ let secp_ctx = Secp256k1 :: new ( ) ;
1508+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
1509+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
1510+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
1511+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
1512+ create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 0 ) ;
1513+ create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
1514+
1515+ // Initiate payment to the sender's intended offer.
1516+ let blinded_paths_to_always_online_node = nodes[ 1 ] . message_router . create_blinded_paths (
1517+ nodes[ 1 ] . node . get_our_node_id ( ) ,
1518+ MessageContext :: Offers ( OffersContext :: InvoiceRequest { nonce : Nonce ( [ 42 ; 16 ] ) } ) ,
1519+ Vec :: new ( ) , & secp_ctx
1520+ ) . unwrap ( ) ;
1521+ let ( offer_builder, offer_nonce) = nodes[ 2 ] . node . create_async_receive_offer_builder ( blinded_paths_to_always_online_node. clone ( ) ) . unwrap ( ) ;
1522+ let offer = offer_builder. build ( ) . unwrap ( ) ;
1523+ let amt_msat = 5000 ;
1524+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
1525+ nodes[ 0 ] . node . pay_for_offer ( & offer, None , Some ( amt_msat) , None , payment_id, Retry :: Attempts ( 0 ) , None ) . unwrap ( ) ;
1526+
1527+ // Don't forward the invreq since we don't support retrieving the static invoice from the
1528+ // recipient's LSP yet, instead manually construct the responses below.
1529+ let invreq_om = nodes[ 0 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 1 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1530+ let invreq_reply_path = extract_invoice_request_reply_path ( & nodes[ 1 ] , & invreq_om) ;
1531+
1532+ // Create a static invoice to be sent over the reply path containing the original payment_id, but
1533+ // the static invoice corresponds to a different offer than was originally paid.
1534+ let unexpected_static_invoice = {
1535+ let ( offer_builder, nonce) = nodes[ 2 ] . node . create_async_receive_offer_builder ( blinded_paths_to_always_online_node) . unwrap ( ) ;
1536+ let sender_unintended_offer = offer_builder. build ( ) . unwrap ( ) ;
1537+
1538+ nodes[ 2 ] . node . create_static_invoice_builder (
1539+ & sender_unintended_offer, nonce, None
1540+ ) . unwrap ( ) . build_and_sign ( & secp_ctx) . unwrap ( )
1541+ } ;
1542+
1543+ // Check that we'll ignore the unexpected static invoice.
1544+ nodes[ 1 ] . onion_messenger . send_onion_message (
1545+ ParsedOnionMessageContents :: < Infallible > :: Offers ( OffersMessage :: StaticInvoice ( unexpected_static_invoice) ) ,
1546+ MessageSendInstructions :: WithoutReplyPath { destination : Destination :: BlindedPath ( invreq_reply_path. clone ( ) ) }
1547+ ) . unwrap ( ) ;
1548+ let unexpected_static_invoice_om = nodes[ 1 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 0 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1549+ nodes[ 0 ] . onion_messenger . handle_onion_message ( nodes[ 1 ] . node . get_our_node_id ( ) , & unexpected_static_invoice_om) ;
1550+ let async_pmts_msgs = AsyncPaymentsMessageHandler :: release_pending_messages ( nodes[ 0 ] . node ) ;
1551+ assert ! ( async_pmts_msgs. is_empty( ) ) ;
1552+ assert ! ( nodes[ 0 ] . node. get_and_clear_pending_events( ) . is_empty( ) ) ;
1553+
1554+ // A valid static invoice corresponding to the correct offer will succeed and cause us to send a
1555+ // held_htlc_available onion message.
1556+ let valid_static_invoice = nodes[ 2 ] . node . create_static_invoice_builder (
1557+ & offer, offer_nonce, None
1558+ ) . unwrap ( ) . build_and_sign ( & secp_ctx) . unwrap ( ) ;
1559+
1560+ nodes[ 1 ] . onion_messenger . send_onion_message (
1561+ ParsedOnionMessageContents :: < Infallible > :: Offers ( OffersMessage :: StaticInvoice ( valid_static_invoice. clone ( ) ) ) ,
1562+ MessageSendInstructions :: WithoutReplyPath { destination : Destination :: BlindedPath ( invreq_reply_path. clone ( ) ) }
1563+ ) . unwrap ( ) ;
1564+ let static_invoice_om = nodes[ 1 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 0 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1565+ nodes[ 0 ] . onion_messenger . handle_onion_message ( nodes[ 1 ] . node . get_our_node_id ( ) , & static_invoice_om) ;
1566+ let async_pmts_msgs = AsyncPaymentsMessageHandler :: release_pending_messages ( nodes[ 0 ] . node ) ;
1567+ assert ! ( !async_pmts_msgs. is_empty( ) ) ;
1568+ assert ! ( async_pmts_msgs. into_iter( ) . all( |( msg, _) | matches!( msg, AsyncPaymentsMessage :: HeldHtlcAvailable ( _) ) ) ) ;
1569+
1570+ // Receiving a duplicate invoice will have no effect.
1571+ nodes[ 1 ] . onion_messenger . send_onion_message (
1572+ ParsedOnionMessageContents :: < Infallible > :: Offers ( OffersMessage :: StaticInvoice ( valid_static_invoice) ) ,
1573+ MessageSendInstructions :: WithoutReplyPath { destination : Destination :: BlindedPath ( invreq_reply_path) }
1574+ ) . unwrap ( ) ;
1575+ let dup_static_invoice_om = nodes[ 1 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 0 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1576+ nodes[ 0 ] . onion_messenger . handle_onion_message ( nodes[ 1 ] . node . get_our_node_id ( ) , & dup_static_invoice_om) ;
1577+ let async_pmts_msgs = AsyncPaymentsMessageHandler :: release_pending_messages ( nodes[ 0 ] . node ) ;
1578+ assert ! ( async_pmts_msgs. is_empty( ) ) ;
1579+ }
1580+
1581+ #[ test]
1582+ #[ cfg( async_payments) ]
1583+ fn pays_static_invoice ( ) {
1584+ // Test that we support the async payments flow up to and including sending the actual payment.
1585+ // Async receive is not yet supported so we don't complete the payment yet.
1586+ let secp_ctx = Secp256k1 :: new ( ) ;
1587+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
1588+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
1589+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
1590+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
1591+ create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 0 ) ;
1592+ create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
1593+
1594+ let blinded_paths_to_always_online_node = nodes[ 1 ] . message_router . create_blinded_paths (
1595+ nodes[ 1 ] . node . get_our_node_id ( ) ,
1596+ MessageContext :: Offers ( OffersContext :: InvoiceRequest { nonce : Nonce ( [ 42 ; 16 ] ) } ) ,
1597+ Vec :: new ( ) , & secp_ctx
1598+ ) . unwrap ( ) ;
1599+ let ( offer_builder, offer_nonce) = nodes[ 2 ] . node . create_async_receive_offer_builder ( blinded_paths_to_always_online_node) . unwrap ( ) ;
1600+ let offer = offer_builder. build ( ) . unwrap ( ) ;
1601+ let amt_msat = 5000 ;
1602+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
1603+ let static_invoice = nodes[ 2 ] . node . create_static_invoice_builder (
1604+ & offer, offer_nonce, None
1605+ ) . unwrap ( ) . build_and_sign ( & secp_ctx) . unwrap ( ) ;
1606+
1607+ nodes[ 0 ] . node . pay_for_offer ( & offer, None , Some ( amt_msat) , None , payment_id, Retry :: Attempts ( 0 ) , None ) . unwrap ( ) ;
1608+
1609+ // Don't forward the invreq since we don't support retrieving the static invoice from the
1610+ // recipient's LSP yet, instead manually construct the response.
1611+ let invreq_om = nodes[ 0 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 1 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1612+ let invreq_reply_path = extract_invoice_request_reply_path ( & nodes[ 1 ] , & invreq_om) ;
1613+
1614+ nodes[ 1 ] . onion_messenger . send_onion_message (
1615+ ParsedOnionMessageContents :: < Infallible > :: Offers ( OffersMessage :: StaticInvoice ( static_invoice) ) ,
1616+ MessageSendInstructions :: WithoutReplyPath { destination : Destination :: BlindedPath ( invreq_reply_path) }
1617+ ) . unwrap ( ) ;
1618+ let static_invoice_om = nodes[ 1 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 0 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1619+ nodes[ 0 ] . onion_messenger . handle_onion_message ( nodes[ 1 ] . node . get_our_node_id ( ) , & static_invoice_om) ;
1620+ let mut async_pmts_msgs = AsyncPaymentsMessageHandler :: release_pending_messages ( nodes[ 0 ] . node ) ;
1621+ assert ! ( !async_pmts_msgs. is_empty( ) ) ;
1622+ assert ! ( async_pmts_msgs. iter( ) . all( |( msg, _) | matches!( msg, AsyncPaymentsMessage :: HeldHtlcAvailable ( _) ) ) ) ;
1623+
1624+ // Manually send the message and context releasing the HTLC since the recipient doesn't support
1625+ // responding themselves yet.
1626+ let held_htlc_avail_reply_path = match async_pmts_msgs. pop ( ) . unwrap ( ) . 1 {
1627+ MessageSendInstructions :: WithSpecifiedReplyPath { reply_path, .. } => reply_path,
1628+ _ => panic ! ( )
1629+ } ;
1630+ nodes[ 2 ] . onion_messenger . send_onion_message (
1631+ ParsedOnionMessageContents :: < Infallible > :: AsyncPayments (
1632+ AsyncPaymentsMessage :: ReleaseHeldHtlc ( ReleaseHeldHtlc { } ) ,
1633+ ) ,
1634+ MessageSendInstructions :: WithoutReplyPath {
1635+ destination : Destination :: BlindedPath ( held_htlc_avail_reply_path)
1636+ }
1637+ ) . unwrap ( ) ;
1638+
1639+ let release_held_htlc_om = nodes[ 2 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 0 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1640+ nodes[ 0 ] . onion_messenger . handle_onion_message ( nodes[ 2 ] . node . get_our_node_id ( ) , & release_held_htlc_om) ;
1641+
1642+ // Check that we've queued the HTLCs of the async keysend payment.
1643+ let htlc_updates = get_htlc_update_msgs ! ( nodes[ 0 ] , nodes[ 1 ] . node. get_our_node_id( ) ) ;
1644+ assert_eq ! ( htlc_updates. update_add_htlcs. len( ) , 1 ) ;
1645+ check_added_monitors ! ( nodes[ 0 ] , 1 ) ;
1646+
1647+ // Receiving a duplicate release_htlc message doesn't result in duplicate payment.
1648+ nodes[ 0 ] . onion_messenger . handle_onion_message ( nodes[ 2 ] . node . get_our_node_id ( ) , & release_held_htlc_om) ;
1649+ assert ! ( nodes[ 0 ] . node. get_and_clear_pending_msg_events( ) . is_empty( ) ) ;
1650+ }
1651+
14191652fn secret_from_hex ( hex : & str ) -> SecretKey {
14201653 SecretKey :: from_slice ( & <Vec < u8 > >:: from_hex ( hex) . unwrap ( ) ) . unwrap ( )
14211654}
0 commit comments