Skip to content

Commit

Permalink
f MPP test
Browse files Browse the repository at this point in the history
  • Loading branch information
valentinewallace committed Sep 16, 2024
1 parent ae76024 commit cd5709f
Showing 1 changed file with 31 additions and 14 deletions.
45 changes: 31 additions & 14 deletions lightning/src/ln/offers_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2259,13 +2259,13 @@ fn fails_paying_invoice_with_unknown_required_features() {
fn no_double_pay_with_stale_channelmanager() {
// This tests the following bug:
// - An outbound payment is AwaitingInvoice
// - We receive an invoice and lock the HTLCs into the relevant ChannelMonitor
// - The monitor is successfully persisted, but the ChannelManager fails to persist, so the
// - We receive an invoice and lock the HTLCs into the relevant ChannelMonitors
// - The monitors are successfully persisted, but the ChannelManager fails to persist, so the
// payment remains AwaitingInvoice
// - We restart, causing the channel to close due to a stale ChannelManager
// - We restart, causing the channels to close due to a stale ChannelManager
// - We receive a duplicate invoice, and attempt to pay it again due to the payment still being
// AwaitingInvoice in the stale ChannelManager
// After the fix for this, we will notice that the payment is already locked into the monitor on
// After the fix for this, we will notice that the payment is already locked into the monitors on
// startup and transition the incorrectly-AwaitingInvoice payment to Retryable, which prevents
// double-paying on duplicate invoice receipt.
let chanmon_cfgs = create_chanmon_cfgs(2);
Expand All @@ -2275,15 +2275,17 @@ fn no_double_pay_with_stale_channelmanager() {
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let alice_deserialized;
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
let chan_id = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000).2;
let chan_id_0 = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000).2;
let chan_id_1 = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000).2;

let alice_id = nodes[0].node.get_our_node_id();
let bob_id = nodes[1].node.get_our_node_id();

let amt_msat = nodes[0].node.list_usable_channels()[0].next_outbound_htlc_limit_msat + 1; // Force MPP
let offer = nodes[1].node
.create_offer_builder(None).unwrap()
.clear_paths()
.amount_msats(10_000_000)
.amount_msats(amt_msat)
.build().unwrap();
assert_eq!(offer.signing_pubkey(), Some(bob_id));
assert!(offer.paths().is_empty());
Expand All @@ -2300,9 +2302,23 @@ fn no_double_pay_with_stale_channelmanager() {

let invoice_om = nodes[1].onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
nodes[0].onion_messenger.handle_onion_message(bob_id, &invoice_om);
let payment_hash = extract_invoice(&nodes[0], &invoice_om).0.payment_hash();

let expected_route: &[&[&Node]] = &[&[&nodes[1]], &[&nodes[1]]];
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 2);
check_added_monitors!(nodes[0], 2);

let ev = remove_first_msg_event_to_node(&bob_id, &mut events);
let args = PassAlongPathArgs::new(&nodes[0], expected_route[0], amt_msat, payment_hash, ev)
.without_clearing_recipient_events();
do_pass_along_path(args);

let ev = remove_first_msg_event_to_node(&bob_id, &mut events);
let args = PassAlongPathArgs::new(&nodes[0], expected_route[0], amt_msat, payment_hash, ev)
.without_clearing_recipient_events();
do_pass_along_path(args);

let (invoice, _) = extract_invoice(&nodes[0], &invoice_om);
route_bolt12_payment(&nodes[0], &[&nodes[1]], &invoice);
expect_recent_payment!(nodes[0], RecentPaymentDetails::Pending, payment_id);
match get_event!(nodes[1], Event::PaymentClaimable) {
Event::PaymentClaimable { .. } => {},
Expand All @@ -2311,16 +2327,17 @@ fn no_double_pay_with_stale_channelmanager() {

// Reload with the stale manager and check that receiving the invoice again won't result in a
// duplicate payment attempt.
let monitor = get_monitor!(nodes[0], chan_id).encode();
reload_node!(nodes[0], &alice_chan_manager_serialized, &[&monitor], persister, chain_monitor, alice_deserialized);
// The stale manager results in closing the channel.
check_closed_event!(nodes[0], 1, ClosureReason::OutdatedChannelManager, [bob_id], 10_000_000);
check_added_monitors!(nodes[0], 1);
let monitor_0 = get_monitor!(nodes[0], chan_id_0).encode();
let monitor_1 = get_monitor!(nodes[0], chan_id_1).encode();
reload_node!(nodes[0], &alice_chan_manager_serialized, &[&monitor_0, &monitor_1], persister, chain_monitor, alice_deserialized);
// The stale manager results in closing the channels.
check_closed_event!(nodes[0], 2, ClosureReason::OutdatedChannelManager, [bob_id, bob_id], 10_000_000);
check_added_monitors!(nodes[0], 2);

// Alice receives a duplicate invoice, but the payment should be transitioned to Retryable by now.
nodes[0].onion_messenger.handle_onion_message(bob_id, &invoice_om);
// Previously, Alice would've attempted to pay the invoice a 2nd time. In this test case, this 2nd
// attempt would have resulted in a PaymentFailed event here, since the only channnel between
// attempt would have resulted in a PaymentFailed event here, since the only channnels between
// Alice and Bob is closed. Since no 2nd attempt should be made, check that no events are
// generated in response to the duplicate invoice.
assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
Expand Down

0 comments on commit cd5709f

Please sign in to comment.