@@ -37,8 +37,17 @@ use crate::util::ser::WithoutLength;
3737use crate :: util:: test_utils;
3838use lightning_invoice:: RawBolt11Invoice ;
3939#[ cfg( async_payments) ] use {
40+ crate :: blinded_path:: message:: { BlindedMessagePath , MessageContext , OffersContext } ,
4041 crate :: ln:: inbound_payment,
42+ crate :: ln:: msgs:: OnionMessageHandler ,
43+ crate :: onion_message:: async_payments:: { AsyncPaymentsMessage , AsyncPaymentsMessageHandler , ReleaseHeldHtlc } ,
44+ crate :: onion_message:: messenger:: { Destination , MessageSendInstructions , MessageRouter , PeeledOnion } ,
45+ crate :: onion_message:: offers:: OffersMessage ,
46+ crate :: onion_message:: packet:: ParsedOnionMessageContents ,
47+ crate :: types:: features:: Bolt12InvoiceFeatures ,
4148 crate :: types:: payment:: PaymentPreimage ,
49+
50+ core:: convert:: Infallible ,
4251} ;
4352
4453fn blinded_payment_path (
@@ -109,6 +118,25 @@ pub fn get_blinded_route_parameters(
109118 )
110119}
111120
121+ #[ cfg( async_payments) ]
122+ fn extract_invoice_request_reply_path < ' a , ' b , ' c > (
123+ invreq_recipient : & Node < ' a , ' b , ' c > , message : & msgs:: OnionMessage
124+ ) -> BlindedMessagePath {
125+ match invreq_recipient. onion_messenger . peel_onion_message ( message) {
126+ Ok ( PeeledOnion :: Receive ( invreq, context, reply_path) ) => {
127+ assert ! (
128+ matches!( invreq, ParsedOnionMessageContents :: Offers ( OffersMessage :: InvoiceRequest ( _) ) )
129+ ) ;
130+ assert ! (
131+ matches!( context, Some ( MessageContext :: Offers ( OffersContext :: InvoiceRequest { .. } ) ) )
132+ ) ;
133+ reply_path. unwrap ( )
134+ } ,
135+ Ok ( PeeledOnion :: Forward ( _, _) ) => panic ! ( "Unexpected onion message forward" ) ,
136+ Err ( e) => panic ! ( "Failed to process onion message {:?}" , e) ,
137+ }
138+ }
139+
112140#[ test]
113141fn one_hop_blinded_path ( ) {
114142 do_one_hop_blinded_path ( true ) ;
@@ -1512,6 +1540,210 @@ fn fails_receive_tlvs_authentication() {
15121540 ) ;
15131541}
15141542
1543+ #[ test]
1544+ #[ cfg( async_payments) ]
1545+ fn static_invoice_unknown_required_features ( ) {
1546+ // Test that we will fail to pay a static invoice with unsupported required features.
1547+ let secp_ctx = Secp256k1 :: new ( ) ;
1548+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
1549+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
1550+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
1551+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
1552+ create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 0 ) ;
1553+ create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
1554+
1555+ let blinded_paths_to_always_online_node = nodes[ 1 ] . message_router . create_blinded_paths (
1556+ nodes[ 1 ] . node . get_our_node_id ( ) ,
1557+ MessageContext :: Offers ( OffersContext :: InvoiceRequest { nonce : Nonce ( [ 42 ; 16 ] ) } ) ,
1558+ Vec :: new ( ) , & secp_ctx
1559+ ) . unwrap ( ) ;
1560+ let ( offer_builder, nonce) =
1561+ nodes[ 2 ] . node . create_async_receive_offer_builder ( blinded_paths_to_always_online_node) . unwrap ( ) ;
1562+ let offer = offer_builder. build ( ) . unwrap ( ) ;
1563+ let static_invoice_unknown_req_features = nodes[ 2 ] . node . create_static_invoice_builder (
1564+ & offer, nonce, None
1565+ )
1566+ . unwrap ( )
1567+ . features_unchecked ( Bolt12InvoiceFeatures :: unknown ( ) )
1568+ . build_and_sign ( & secp_ctx) . unwrap ( ) ;
1569+
1570+ let amt_msat = 5000 ;
1571+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
1572+ nodes[ 0 ] . node . pay_for_offer ( & offer, None , Some ( amt_msat) , None , payment_id, Retry :: Attempts ( 0 ) , None ) . unwrap ( ) ;
1573+
1574+ // Don't forward the invreq since we don't support retrieving the static invoice from the
1575+ // recipient's LSP yet, instead manually construct the response.
1576+ let invreq_om = nodes[ 0 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 1 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1577+ let invreq_reply_path = extract_invoice_request_reply_path ( & nodes[ 1 ] , & invreq_om) ;
1578+ nodes[ 1 ] . onion_messenger . send_onion_message (
1579+ ParsedOnionMessageContents :: < Infallible > :: Offers ( OffersMessage :: StaticInvoice ( static_invoice_unknown_req_features) ) ,
1580+ MessageSendInstructions :: WithoutReplyPath { destination : Destination :: BlindedPath ( invreq_reply_path) }
1581+ ) . unwrap ( ) ;
1582+
1583+ let static_invoice_om = nodes[ 1 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 0 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1584+ nodes[ 0 ] . onion_messenger . handle_onion_message ( nodes[ 1 ] . node . get_our_node_id ( ) , & static_invoice_om) ;
1585+ let events = nodes[ 0 ] . node . get_and_clear_pending_events ( ) ;
1586+ assert_eq ! ( events. len( ) , 1 ) ;
1587+ match events[ 0 ] {
1588+ Event :: PaymentFailed { payment_hash, payment_id : ev_payment_id, reason } => {
1589+ assert_eq ! ( payment_hash, None ) ;
1590+ assert_eq ! ( payment_id, ev_payment_id) ;
1591+ assert_eq ! ( reason, Some ( PaymentFailureReason :: UnknownRequiredFeatures ) ) ;
1592+ } ,
1593+ _ => panic ! ( )
1594+ }
1595+ }
1596+
1597+ #[ test]
1598+ #[ cfg( async_payments) ]
1599+ fn ignore_unexpected_static_invoice ( ) {
1600+ // Test that we'll ignore unexpected static invoices, invoices that don't match our invoice
1601+ // request, and duplicate invoices.
1602+ let secp_ctx = Secp256k1 :: new ( ) ;
1603+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
1604+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
1605+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
1606+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
1607+ create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 0 ) ;
1608+ create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
1609+
1610+ // Initiate payment to the sender's intended offer.
1611+ let blinded_paths_to_always_online_node = nodes[ 1 ] . message_router . create_blinded_paths (
1612+ nodes[ 1 ] . node . get_our_node_id ( ) ,
1613+ MessageContext :: Offers ( OffersContext :: InvoiceRequest { nonce : Nonce ( [ 42 ; 16 ] ) } ) ,
1614+ Vec :: new ( ) , & secp_ctx
1615+ ) . unwrap ( ) ;
1616+ let ( offer_builder, offer_nonce) = nodes[ 2 ] . node . create_async_receive_offer_builder ( blinded_paths_to_always_online_node. clone ( ) ) . unwrap ( ) ;
1617+ let offer = offer_builder. build ( ) . unwrap ( ) ;
1618+ let amt_msat = 5000 ;
1619+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
1620+ nodes[ 0 ] . node . pay_for_offer ( & offer, None , Some ( amt_msat) , None , payment_id, Retry :: Attempts ( 0 ) , None ) . unwrap ( ) ;
1621+
1622+ // Don't forward the invreq since we don't support retrieving the static invoice from the
1623+ // recipient's LSP yet, instead manually construct the responses below.
1624+ let invreq_om = nodes[ 0 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 1 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1625+ let invreq_reply_path = extract_invoice_request_reply_path ( & nodes[ 1 ] , & invreq_om) ;
1626+
1627+ // Create a static invoice to be sent over the reply path containing the original payment_id, but
1628+ // the static invoice corresponds to a different offer than was originally paid.
1629+ let unexpected_static_invoice = {
1630+ let ( offer_builder, nonce) = nodes[ 2 ] . node . create_async_receive_offer_builder ( blinded_paths_to_always_online_node) . unwrap ( ) ;
1631+ let sender_unintended_offer = offer_builder. build ( ) . unwrap ( ) ;
1632+
1633+ nodes[ 2 ] . node . create_static_invoice_builder (
1634+ & sender_unintended_offer, nonce, None
1635+ ) . unwrap ( ) . build_and_sign ( & secp_ctx) . unwrap ( )
1636+ } ;
1637+
1638+ // Check that we'll ignore the unexpected static invoice.
1639+ nodes[ 1 ] . onion_messenger . send_onion_message (
1640+ ParsedOnionMessageContents :: < Infallible > :: Offers ( OffersMessage :: StaticInvoice ( unexpected_static_invoice) ) ,
1641+ MessageSendInstructions :: WithoutReplyPath { destination : Destination :: BlindedPath ( invreq_reply_path. clone ( ) ) }
1642+ ) . unwrap ( ) ;
1643+ let unexpected_static_invoice_om = nodes[ 1 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 0 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1644+ nodes[ 0 ] . onion_messenger . handle_onion_message ( nodes[ 1 ] . node . get_our_node_id ( ) , & unexpected_static_invoice_om) ;
1645+ let async_pmts_msgs = AsyncPaymentsMessageHandler :: release_pending_messages ( nodes[ 0 ] . node ) ;
1646+ assert ! ( async_pmts_msgs. is_empty( ) ) ;
1647+ assert ! ( nodes[ 0 ] . node. get_and_clear_pending_events( ) . is_empty( ) ) ;
1648+
1649+ // A valid static invoice corresponding to the correct offer will succeed and cause us to send a
1650+ // held_htlc_available onion message.
1651+ let valid_static_invoice = nodes[ 2 ] . node . create_static_invoice_builder (
1652+ & offer, offer_nonce, None
1653+ ) . unwrap ( ) . build_and_sign ( & secp_ctx) . unwrap ( ) ;
1654+
1655+ nodes[ 1 ] . onion_messenger . send_onion_message (
1656+ ParsedOnionMessageContents :: < Infallible > :: Offers ( OffersMessage :: StaticInvoice ( valid_static_invoice. clone ( ) ) ) ,
1657+ MessageSendInstructions :: WithoutReplyPath { destination : Destination :: BlindedPath ( invreq_reply_path. clone ( ) ) }
1658+ ) . unwrap ( ) ;
1659+ let static_invoice_om = nodes[ 1 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 0 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1660+ nodes[ 0 ] . onion_messenger . handle_onion_message ( nodes[ 1 ] . node . get_our_node_id ( ) , & static_invoice_om) ;
1661+ let async_pmts_msgs = AsyncPaymentsMessageHandler :: release_pending_messages ( nodes[ 0 ] . node ) ;
1662+ assert ! ( !async_pmts_msgs. is_empty( ) ) ;
1663+ assert ! ( async_pmts_msgs. into_iter( ) . all( |( msg, _) | matches!( msg, AsyncPaymentsMessage :: HeldHtlcAvailable ( _) ) ) ) ;
1664+
1665+ // Receiving a duplicate invoice will have no effect.
1666+ nodes[ 1 ] . onion_messenger . send_onion_message (
1667+ ParsedOnionMessageContents :: < Infallible > :: Offers ( OffersMessage :: StaticInvoice ( valid_static_invoice) ) ,
1668+ MessageSendInstructions :: WithoutReplyPath { destination : Destination :: BlindedPath ( invreq_reply_path) }
1669+ ) . unwrap ( ) ;
1670+ let dup_static_invoice_om = nodes[ 1 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 0 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1671+ nodes[ 0 ] . onion_messenger . handle_onion_message ( nodes[ 1 ] . node . get_our_node_id ( ) , & dup_static_invoice_om) ;
1672+ let async_pmts_msgs = AsyncPaymentsMessageHandler :: release_pending_messages ( nodes[ 0 ] . node ) ;
1673+ assert ! ( async_pmts_msgs. is_empty( ) ) ;
1674+ }
1675+
1676+ #[ test]
1677+ #[ cfg( async_payments) ]
1678+ fn pays_static_invoice ( ) {
1679+ // Test that we support the async payments flow up to and including sending the actual payment.
1680+ // Async receive is not yet supported so we don't complete the payment yet.
1681+ let secp_ctx = Secp256k1 :: new ( ) ;
1682+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
1683+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
1684+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
1685+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
1686+ create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 0 ) ;
1687+ create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
1688+
1689+ let blinded_paths_to_always_online_node = nodes[ 1 ] . message_router . create_blinded_paths (
1690+ nodes[ 1 ] . node . get_our_node_id ( ) ,
1691+ MessageContext :: Offers ( OffersContext :: InvoiceRequest { nonce : Nonce ( [ 42 ; 16 ] ) } ) ,
1692+ Vec :: new ( ) , & secp_ctx
1693+ ) . unwrap ( ) ;
1694+ let ( offer_builder, offer_nonce) = nodes[ 2 ] . node . create_async_receive_offer_builder ( blinded_paths_to_always_online_node) . unwrap ( ) ;
1695+ let offer = offer_builder. build ( ) . unwrap ( ) ;
1696+ let amt_msat = 5000 ;
1697+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
1698+ let static_invoice = nodes[ 2 ] . node . create_static_invoice_builder (
1699+ & offer, offer_nonce, None
1700+ ) . unwrap ( ) . build_and_sign ( & secp_ctx) . unwrap ( ) ;
1701+
1702+ nodes[ 0 ] . node . pay_for_offer ( & offer, None , Some ( amt_msat) , None , payment_id, Retry :: Attempts ( 0 ) , None ) . unwrap ( ) ;
1703+
1704+ // Don't forward the invreq since we don't support retrieving the static invoice from the
1705+ // recipient's LSP yet, instead manually construct the response.
1706+ let invreq_om = nodes[ 0 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 1 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1707+ let invreq_reply_path = extract_invoice_request_reply_path ( & nodes[ 1 ] , & invreq_om) ;
1708+
1709+ nodes[ 1 ] . onion_messenger . send_onion_message (
1710+ ParsedOnionMessageContents :: < Infallible > :: Offers ( OffersMessage :: StaticInvoice ( static_invoice) ) ,
1711+ MessageSendInstructions :: WithoutReplyPath { destination : Destination :: BlindedPath ( invreq_reply_path) }
1712+ ) . unwrap ( ) ;
1713+ let static_invoice_om = nodes[ 1 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 0 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1714+ nodes[ 0 ] . onion_messenger . handle_onion_message ( nodes[ 1 ] . node . get_our_node_id ( ) , & static_invoice_om) ;
1715+ let mut async_pmts_msgs = AsyncPaymentsMessageHandler :: release_pending_messages ( nodes[ 0 ] . node ) ;
1716+ assert ! ( !async_pmts_msgs. is_empty( ) ) ;
1717+ assert ! ( async_pmts_msgs. iter( ) . all( |( msg, _) | matches!( msg, AsyncPaymentsMessage :: HeldHtlcAvailable ( _) ) ) ) ;
1718+
1719+ // Manually send the message and context releasing the HTLC since the recipient doesn't support
1720+ // responding themselves yet.
1721+ let held_htlc_avail_reply_path = match async_pmts_msgs. pop ( ) . unwrap ( ) . 1 {
1722+ MessageSendInstructions :: WithSpecifiedReplyPath { reply_path, .. } => reply_path,
1723+ _ => panic ! ( )
1724+ } ;
1725+ nodes[ 2 ] . onion_messenger . send_onion_message (
1726+ ParsedOnionMessageContents :: < Infallible > :: AsyncPayments (
1727+ AsyncPaymentsMessage :: ReleaseHeldHtlc ( ReleaseHeldHtlc { } ) ,
1728+ ) ,
1729+ MessageSendInstructions :: WithoutReplyPath {
1730+ destination : Destination :: BlindedPath ( held_htlc_avail_reply_path)
1731+ }
1732+ ) . unwrap ( ) ;
1733+
1734+ let release_held_htlc_om = nodes[ 2 ] . onion_messenger . next_onion_message_for_peer ( nodes[ 0 ] . node . get_our_node_id ( ) ) . unwrap ( ) ;
1735+ nodes[ 0 ] . onion_messenger . handle_onion_message ( nodes[ 2 ] . node . get_our_node_id ( ) , & release_held_htlc_om) ;
1736+
1737+ // Check that we've queued the HTLCs of the async keysend payment.
1738+ let htlc_updates = get_htlc_update_msgs ! ( nodes[ 0 ] , nodes[ 1 ] . node. get_our_node_id( ) ) ;
1739+ assert_eq ! ( htlc_updates. update_add_htlcs. len( ) , 1 ) ;
1740+ check_added_monitors ! ( nodes[ 0 ] , 1 ) ;
1741+
1742+ // Receiving a duplicate release_htlc message doesn't result in duplicate payment.
1743+ nodes[ 0 ] . onion_messenger . handle_onion_message ( nodes[ 2 ] . node . get_our_node_id ( ) , & release_held_htlc_om) ;
1744+ assert ! ( nodes[ 0 ] . node. get_and_clear_pending_msg_events( ) . is_empty( ) ) ;
1745+ }
1746+
15151747fn secret_from_hex ( hex : & str ) -> SecretKey {
15161748 SecretKey :: from_slice ( & <Vec < u8 > >:: from_hex ( hex) . unwrap ( ) ) . unwrap ( )
15171749}
0 commit comments