1717use core:: time:: Duration ;
1818use crate :: blinded_path:: BlindedPath ;
1919use crate :: events:: { Event , MessageSendEventsProvider , PaymentPurpose } ;
20- use crate :: ln:: channelmanager:: { PaymentId , RecentPaymentDetails , Retry } ;
20+ use crate :: ln:: channelmanager:: { PaymentId , RecentPaymentDetails , Retry , self } ;
2121use crate :: ln:: functional_test_utils:: * ;
22- use crate :: ln:: msgs:: { OnionMessage , OnionMessageHandler } ;
22+ use crate :: ln:: msgs:: { ChannelMessageHandler , Init , OnionMessage , OnionMessageHandler } ;
2323use crate :: offers:: invoice:: Bolt12Invoice ;
2424use crate :: offers:: invoice_request:: InvoiceRequest ;
2525use crate :: onion_message:: { OffersMessage , ParsedOnionMessageContents , PeeledOnion } ;
@@ -38,6 +38,36 @@ macro_rules! expect_recent_payment {
3838 }
3939}
4040
41+ fn connect_peers < ' a , ' b , ' c > ( node_a : & Node < ' a , ' b , ' c > , node_b : & Node < ' a , ' b , ' c > ) {
42+ let node_id_a = node_a. node . get_our_node_id ( ) ;
43+ let node_id_b = node_b. node . get_our_node_id ( ) ;
44+
45+ let init_a = Init {
46+ features : node_a. init_features ( & node_id_b) ,
47+ networks : None ,
48+ remote_network_address : None ,
49+ } ;
50+ let init_b = Init {
51+ features : node_b. init_features ( & node_id_a) ,
52+ networks : None ,
53+ remote_network_address : None ,
54+ } ;
55+
56+ node_a. node . peer_connected ( & node_id_b, & init_b, true ) . unwrap ( ) ;
57+ node_b. node . peer_connected ( & node_id_a, & init_a, false ) . unwrap ( ) ;
58+ node_a. onion_messenger . peer_connected ( & node_id_b, & init_b, true ) . unwrap ( ) ;
59+ node_b. onion_messenger . peer_connected ( & node_id_a, & init_a, false ) . unwrap ( ) ;
60+ }
61+
62+ fn disconnect_peers < ' a , ' b , ' c > ( node_a : & Node < ' a , ' b , ' c > , peers : & [ & Node < ' a , ' b , ' c > ] ) {
63+ for node_b in peers {
64+ node_a. node . peer_disconnected ( & node_b. node . get_our_node_id ( ) ) ;
65+ node_b. node . peer_disconnected ( & node_a. node . get_our_node_id ( ) ) ;
66+ node_a. onion_messenger . peer_disconnected ( & node_b. node . get_our_node_id ( ) ) ;
67+ node_b. onion_messenger . peer_disconnected ( & node_a. node . get_our_node_id ( ) ) ;
68+ }
69+ }
70+
4171fn route_bolt12_payment < ' a , ' b , ' c > (
4272 node : & Node < ' a , ' b , ' c > , path : & [ & Node < ' a , ' b , ' c > ] , invoice : & Bolt12Invoice
4373) {
@@ -101,6 +131,175 @@ fn extract_invoice<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, message: &OnionMessage)
101131 }
102132}
103133
134+ /// Checks that an offer can be paid through blinded paths and that ephemeral pubkeys are used
135+ /// rather than exposing a node's pubkey.
136+ #[ test]
137+ fn creates_and_pays_for_offer_using_two_hop_blinded_path ( ) {
138+ let mut accept_forward_cfg = test_default_channel_config ( ) ;
139+ accept_forward_cfg. accept_forwards_to_priv_channels = true ;
140+
141+ let mut features = channelmanager:: provided_init_features ( & accept_forward_cfg) ;
142+ features. set_onion_messages_optional ( ) ;
143+ features. set_route_blinding_optional ( ) ;
144+
145+ let chanmon_cfgs = create_chanmon_cfgs ( 6 ) ;
146+ let node_cfgs = create_node_cfgs ( 6 , & chanmon_cfgs) ;
147+
148+ * node_cfgs[ 1 ] . override_init_features . borrow_mut ( ) = Some ( features) ;
149+
150+ let node_chanmgrs = create_node_chanmgrs (
151+ 6 , & node_cfgs, & [ None , Some ( accept_forward_cfg) , None , None , None , None ]
152+ ) ;
153+ let nodes = create_network ( 6 , & node_cfgs, & node_chanmgrs) ;
154+
155+ create_unannounced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 10_000_000 , 1_000_000_000 ) ;
156+ create_unannounced_chan_between_nodes_with_value ( & nodes, 2 , 3 , 10_000_000 , 1_000_000_000 ) ;
157+ create_announced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 10_000_000 , 1_000_000_000 ) ;
158+ create_announced_chan_between_nodes_with_value ( & nodes, 1 , 4 , 10_000_000 , 1_000_000_000 ) ;
159+ create_announced_chan_between_nodes_with_value ( & nodes, 1 , 5 , 10_000_000 , 1_000_000_000 ) ;
160+ create_announced_chan_between_nodes_with_value ( & nodes, 2 , 4 , 10_000_000 , 1_000_000_000 ) ;
161+ create_announced_chan_between_nodes_with_value ( & nodes, 2 , 5 , 10_000_000 , 1_000_000_000 ) ;
162+
163+ let ( alice, bob, charlie, david) = ( & nodes[ 0 ] , & nodes[ 1 ] , & nodes[ 2 ] , & nodes[ 3 ] ) ;
164+ let alice_id = alice. node . get_our_node_id ( ) ;
165+ let bob_id = bob. node . get_our_node_id ( ) ;
166+ let charlie_id = charlie. node . get_our_node_id ( ) ;
167+ let david_id = david. node . get_our_node_id ( ) ;
168+
169+ disconnect_peers ( alice, & [ charlie, david, & nodes[ 4 ] , & nodes[ 5 ] ] ) ;
170+ disconnect_peers ( david, & [ bob, & nodes[ 4 ] , & nodes[ 5 ] ] ) ;
171+
172+ let offer = alice. node
173+ . create_offer_builder ( "coffee" . to_string ( ) ) . unwrap ( )
174+ . amount_msats ( 10_000_000 )
175+ . build ( ) . unwrap ( ) ;
176+ assert_ne ! ( offer. signing_pubkey( ) , alice_id) ;
177+ assert ! ( !offer. paths( ) . is_empty( ) ) ;
178+ for path in offer. paths ( ) {
179+ assert_eq ! ( path. introduction_node_id, bob_id) ;
180+ }
181+
182+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
183+ david. node . pay_for_offer ( & offer, None , None , None , payment_id, Retry :: Attempts ( 0 ) , None )
184+ . unwrap ( ) ;
185+ expect_recent_payment ! ( david, RecentPaymentDetails :: AwaitingInvoice , payment_id) ;
186+
187+ connect_peers ( david, bob) ;
188+
189+ let onion_message = david. onion_messenger . next_onion_message_for_peer ( bob_id) . unwrap ( ) ;
190+ bob. onion_messenger . handle_onion_message ( & david_id, & onion_message) ;
191+
192+ connect_peers ( alice, charlie) ;
193+
194+ let onion_message = bob. onion_messenger . next_onion_message_for_peer ( alice_id) . unwrap ( ) ;
195+ alice. onion_messenger . handle_onion_message ( & bob_id, & onion_message) ;
196+
197+ let ( invoice_request, reply_path) = extract_invoice_request ( alice, & onion_message) ;
198+ assert_eq ! ( invoice_request. amount_msats( ) , None ) ;
199+ assert_ne ! ( invoice_request. payer_id( ) , david_id) ;
200+ assert_eq ! ( reply_path. unwrap( ) . introduction_node_id, charlie_id) ;
201+
202+ let onion_message = alice. onion_messenger . next_onion_message_for_peer ( charlie_id) . unwrap ( ) ;
203+ charlie. onion_messenger . handle_onion_message ( & alice_id, & onion_message) ;
204+
205+ let onion_message = charlie. onion_messenger . next_onion_message_for_peer ( david_id) . unwrap ( ) ;
206+ david. onion_messenger . handle_onion_message ( & charlie_id, & onion_message) ;
207+
208+ let invoice = extract_invoice ( david, & onion_message) ;
209+ assert_eq ! ( invoice. amount_msats( ) , 10_000_000 ) ;
210+ assert_ne ! ( invoice. signing_pubkey( ) , alice_id) ;
211+ assert ! ( !invoice. payment_paths( ) . is_empty( ) ) ;
212+ for ( _, path) in invoice. payment_paths ( ) {
213+ assert_eq ! ( path. introduction_node_id, bob_id) ;
214+ }
215+
216+ route_bolt12_payment ( david, & [ charlie, bob, alice] , & invoice) ;
217+ expect_recent_payment ! ( david, RecentPaymentDetails :: Pending , payment_id) ;
218+
219+ claim_bolt12_payment ( david, & [ charlie, bob, alice] ) ;
220+ expect_recent_payment ! ( david, RecentPaymentDetails :: Fulfilled , payment_id) ;
221+ }
222+
223+ /// Checks that a refund can be paid through blinded paths and that ephemeral pubkeys are used
224+ /// rather than exposing a node's pubkey.
225+ #[ test]
226+ fn creates_and_pays_for_refund_using_two_hop_blinded_path ( ) {
227+ let mut accept_forward_cfg = test_default_channel_config ( ) ;
228+ accept_forward_cfg. accept_forwards_to_priv_channels = true ;
229+
230+ let mut features = channelmanager:: provided_init_features ( & accept_forward_cfg) ;
231+ features. set_onion_messages_optional ( ) ;
232+ features. set_route_blinding_optional ( ) ;
233+
234+ let chanmon_cfgs = create_chanmon_cfgs ( 6 ) ;
235+ let node_cfgs = create_node_cfgs ( 6 , & chanmon_cfgs) ;
236+
237+ * node_cfgs[ 1 ] . override_init_features . borrow_mut ( ) = Some ( features) ;
238+
239+ let node_chanmgrs = create_node_chanmgrs (
240+ 6 , & node_cfgs, & [ None , Some ( accept_forward_cfg) , None , None , None , None ]
241+ ) ;
242+ let nodes = create_network ( 6 , & node_cfgs, & node_chanmgrs) ;
243+
244+ create_unannounced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 10_000_000 , 1_000_000_000 ) ;
245+ create_unannounced_chan_between_nodes_with_value ( & nodes, 2 , 3 , 10_000_000 , 1_000_000_000 ) ;
246+ create_announced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 10_000_000 , 1_000_000_000 ) ;
247+ create_announced_chan_between_nodes_with_value ( & nodes, 1 , 4 , 10_000_000 , 1_000_000_000 ) ;
248+ create_announced_chan_between_nodes_with_value ( & nodes, 1 , 5 , 10_000_000 , 1_000_000_000 ) ;
249+ create_announced_chan_between_nodes_with_value ( & nodes, 2 , 4 , 10_000_000 , 1_000_000_000 ) ;
250+ create_announced_chan_between_nodes_with_value ( & nodes, 2 , 5 , 10_000_000 , 1_000_000_000 ) ;
251+
252+ let ( alice, bob, charlie, david) = ( & nodes[ 0 ] , & nodes[ 1 ] , & nodes[ 2 ] , & nodes[ 3 ] ) ;
253+ let alice_id = alice. node . get_our_node_id ( ) ;
254+ let bob_id = bob. node . get_our_node_id ( ) ;
255+ let charlie_id = charlie. node . get_our_node_id ( ) ;
256+ let david_id = david. node . get_our_node_id ( ) ;
257+
258+ disconnect_peers ( alice, & [ charlie, david, & nodes[ 4 ] , & nodes[ 5 ] ] ) ;
259+ disconnect_peers ( david, & [ bob, & nodes[ 4 ] , & nodes[ 5 ] ] ) ;
260+
261+ let absolute_expiry = Duration :: from_secs ( u64:: MAX ) ;
262+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
263+ let refund = david. node
264+ . create_refund_builder (
265+ "refund" . to_string ( ) , 10_000_000 , absolute_expiry, payment_id, Retry :: Attempts ( 0 ) , None
266+ )
267+ . unwrap ( )
268+ . build ( ) . unwrap ( ) ;
269+ assert_eq ! ( refund. amount_msats( ) , 10_000_000 ) ;
270+ assert_eq ! ( refund. absolute_expiry( ) , Some ( absolute_expiry) ) ;
271+ assert_ne ! ( refund. payer_id( ) , david_id) ;
272+ assert ! ( !refund. paths( ) . is_empty( ) ) ;
273+ for path in refund. paths ( ) {
274+ assert_eq ! ( path. introduction_node_id, charlie_id) ;
275+ }
276+ expect_recent_payment ! ( david, RecentPaymentDetails :: AwaitingInvoice , payment_id) ;
277+
278+ alice. node . request_refund_payment ( & refund) . unwrap ( ) ;
279+
280+ connect_peers ( alice, charlie) ;
281+
282+ let onion_message = alice. onion_messenger . next_onion_message_for_peer ( charlie_id) . unwrap ( ) ;
283+ charlie. onion_messenger . handle_onion_message ( & alice_id, & onion_message) ;
284+
285+ let onion_message = charlie. onion_messenger . next_onion_message_for_peer ( david_id) . unwrap ( ) ;
286+ david. onion_messenger . handle_onion_message ( & charlie_id, & onion_message) ;
287+
288+ let invoice = extract_invoice ( david, & onion_message) ;
289+ assert_eq ! ( invoice. amount_msats( ) , 10_000_000 ) ;
290+ assert_ne ! ( invoice. signing_pubkey( ) , alice_id) ;
291+ assert ! ( !invoice. payment_paths( ) . is_empty( ) ) ;
292+ for ( _, path) in invoice. payment_paths ( ) {
293+ assert_eq ! ( path. introduction_node_id, bob_id) ;
294+ }
295+
296+ route_bolt12_payment ( david, & [ charlie, bob, alice] , & invoice) ;
297+ expect_recent_payment ! ( david, RecentPaymentDetails :: Pending , payment_id) ;
298+
299+ claim_bolt12_payment ( david, & [ charlie, bob, alice] ) ;
300+ expect_recent_payment ! ( david, RecentPaymentDetails :: Fulfilled , payment_id) ;
301+ }
302+
104303/// Checks that an offer can be paid through a one-hop blinded path and that ephemeral pubkeys are
105304/// used rather than exposing a node's pubkey. However, the node's pubkey is still used as the
106305/// introduction node of the blinded path.
0 commit comments