Skip to content

Commit a871058

Browse files
committed
Functional tests for offers with blinded paths
1 parent 067a22c commit a871058

File tree

1 file changed

+201
-2
lines changed

1 file changed

+201
-2
lines changed

lightning/src/ln/offers_tests.rs

Lines changed: 201 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
use core::time::Duration;
1818
use crate::blinded_path::BlindedPath;
1919
use crate::events::{Event, MessageSendEventsProvider, PaymentPurpose};
20-
use crate::ln::channelmanager::{PaymentId, RecentPaymentDetails, Retry};
20+
use crate::ln::channelmanager::{PaymentId, RecentPaymentDetails, Retry, self};
2121
use crate::ln::functional_test_utils::*;
22-
use crate::ln::msgs::{OnionMessage, OnionMessageHandler};
22+
use crate::ln::msgs::{ChannelMessageHandler, Init, OnionMessage, OnionMessageHandler};
2323
use crate::offers::invoice::Bolt12Invoice;
2424
use crate::offers::invoice_request::InvoiceRequest;
2525
use 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+
4171
fn 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

Comments
 (0)