Skip to content

Commit ffca644

Browse files
committed
WIP: Functional tests for BOLT 12 Offers payment flow
ChannelManager provides utilities to create offers and refunds along with utilities to initiate and request payment for them, respectively. It also manages the payment flow via implementing OffersMessageHandler. Test that functionality, including the resulting event generation.
1 parent 76486bb commit ffca644

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

lightning/src/ln/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ mod monitor_tests;
7373
#[cfg(test)]
7474
#[allow(unused_mut)]
7575
mod shutdown_tests;
76+
#[cfg(test)]
77+
#[allow(unused_mut)]
78+
mod offers_tests;
7679

7780
pub use self::peer_channel_encryptor::LN_MAX_MSG_LEN;
7881

lightning/src/ln/offers_tests.rs

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
10+
//! Functional tests for the BOLT 12 Offers payment flow.
11+
//!
12+
//! [`ChannelManager`] provides utilities to create [`Offer`]s and [`Refund`]s along with utilities
13+
//! to initiate and request payment for them, respectively. It also manages the payment flow via
14+
//! implementing [`OffersMessageHandler`]. This module tests that functionality, including the
15+
//! resulting [`Event`] generation.
16+
17+
use crate::events::{Event, MessageSendEventsProvider, PaymentPurpose};
18+
use crate::ln::PaymentHash;
19+
use crate::ln::channelmanager::{PaymentId, RecentPaymentDetails, Retry};
20+
use crate::ln::functional_test_utils::*;
21+
use crate::ln::msgs::OnionMessageHandler;
22+
23+
use crate::prelude::*;
24+
25+
macro_rules! expect_recent_payment {
26+
($node: expr, $payment_state: path, $payment_id: expr) => {
27+
match $node.node.list_recent_payments().first() {
28+
Some(&$payment_state { payment_id: actual_payment_id, .. }) => {
29+
assert_eq!($payment_id, actual_payment_id);
30+
},
31+
Some(_) => panic!("Unexpected recent payment state"),
32+
None => panic!("No recent payments"),
33+
}
34+
}
35+
}
36+
37+
macro_rules! route_payment {
38+
($node: expr, $path: expr, $amount_msats: expr) => {
39+
{
40+
// Monitor added when handling the invoice onion message.
41+
check_added_monitors($node, 1);
42+
43+
// Use a fake payment_hash and bypass checking for the PaymentClaimable event since the
44+
// invoice contains the payment_hash but it was encrypted inside an onion message.
45+
let fake_payment_hash = PaymentHash([0; 32]);
46+
47+
let mut events = $node.node.get_and_clear_pending_msg_events();
48+
assert_eq!(events.len(), 1);
49+
let ev = remove_first_msg_event_to_node(&$path[0].node.get_our_node_id(), &mut events);
50+
51+
do_pass_along_path(
52+
$node, $path, $amount_msats, fake_payment_hash, None, ev, false, false, None
53+
);
54+
}
55+
}
56+
}
57+
58+
macro_rules! claim_payment {
59+
($node: expr, $path: expr) => {
60+
{
61+
let recipient = &$path[$path.len() - 1];
62+
match get_event!(recipient, Event::PaymentClaimable) {
63+
Event::PaymentClaimable {
64+
purpose: PaymentPurpose::InvoicePayment {
65+
payment_preimage: Some(payment_preimage), ..
66+
}, ..
67+
} => claim_payment($node, $path, payment_preimage),
68+
_ => panic!(),
69+
};
70+
}
71+
}
72+
}
73+
74+
#[test]
75+
fn pays_for_offer() {
76+
let chanmon_cfgs = create_chanmon_cfgs(2);
77+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
78+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
79+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
80+
81+
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
82+
83+
let alice = &nodes[0];
84+
let alice_id = alice.node.get_our_node_id();
85+
let bob = &nodes[1];
86+
let bob_id = bob.node.get_our_node_id();
87+
88+
let offer = alice.node.create_offer_builder("coffee".to_string()).build().unwrap();
89+
90+
let payment_id = PaymentId([1; 32]);
91+
bob.node.pay_for_offer(
92+
&offer, None, Some(10_000_000), None, payment_id, Retry::Attempts(0), None
93+
).unwrap();
94+
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
95+
96+
let invoice_request = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
97+
alice.onion_messenger.handle_onion_message(&bob_id, &invoice_request);
98+
99+
let invoice = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
100+
bob.onion_messenger.handle_onion_message(&alice_id, &invoice);
101+
102+
route_payment!(bob, &[alice], 10_000_000);
103+
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
104+
105+
claim_payment!(bob, &[alice]);
106+
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
107+
}

0 commit comments

Comments
 (0)