@@ -37,8 +37,17 @@ use crate::util::ser::WithoutLength;
37
37
use crate :: util:: test_utils;
38
38
use lightning_invoice:: RawBolt11Invoice ;
39
39
#[ cfg( async_payments) ] use {
40
+ crate :: blinded_path:: message:: { BlindedMessagePath , MessageContext , OffersContext } ,
40
41
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 ,
41
48
crate :: types:: payment:: PaymentPreimage ,
49
+
50
+ core:: convert:: Infallible ,
42
51
} ;
43
52
44
53
fn blinded_payment_path (
@@ -109,6 +118,25 @@ pub fn get_blinded_route_parameters(
109
118
)
110
119
}
111
120
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
+
112
140
#[ test]
113
141
fn one_hop_blinded_path ( ) {
114
142
do_one_hop_blinded_path ( true ) ;
@@ -1512,6 +1540,210 @@ fn fails_receive_tlvs_authentication() {
1512
1540
) ;
1513
1541
}
1514
1542
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
+
1515
1747
fn secret_from_hex ( hex : & str ) -> SecretKey {
1516
1748
SecretKey :: from_slice ( & <Vec < u8 > >:: from_hex ( hex) . unwrap ( ) ) . unwrap ( )
1517
1749
}
0 commit comments