Skip to content

Commit 3db0553

Browse files
committed
Don't abandon payments for duplicate invoices
When making an outbound BOLT12 payment, multiple invoices may be received for the same payment id. Instead of abandoning the payment when a duplicate invoice received, simply ignore it without responding with an InvoiceError. This prevents abandoning in-progress payments and sending unnecessary onion messages.
1 parent 8233689 commit 3db0553

File tree

3 files changed

+49
-46
lines changed

3 files changed

+49
-46
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10845,43 +10845,51 @@ where
1084510845
&self.logger, None, None, Some(invoice.payment_hash()),
1084610846
);
1084710847

10848-
let result = {
10849-
let features = self.bolt12_invoice_features();
10850-
if invoice.invoice_features().requires_unknown_bits_from(&features) {
10851-
Err(InvoiceError::from(Bolt12SemanticError::UnknownRequiredFeatures))
10852-
} else if self.default_configuration.manually_handle_bolt12_invoices {
10853-
let event = Event::InvoiceReceived {
10854-
payment_id, invoice, context, responder,
10855-
};
10856-
self.pending_events.lock().unwrap().push_back((event, None));
10857-
return ResponseInstruction::NoResponse;
10858-
} else {
10859-
self.send_payment_for_verified_bolt12_invoice(&invoice, payment_id)
10860-
.map_err(|e| {
10861-
log_trace!(logger, "Failed paying invoice: {:?}", e);
10862-
InvoiceError::from_string(format!("{:?}", e))
10863-
})
10864-
}
10865-
};
10848+
let features = self.bolt12_invoice_features();
10849+
if invoice.invoice_features().requires_unknown_bits_from(&features) {
10850+
log_trace!(
10851+
logger, "Invoice requires unknown features: {:?}",
10852+
invoice.invoice_features(),
10853+
);
10854+
abandon_if_payment(context);
1086610855

10867-
match result {
10868-
Ok(_) => ResponseInstruction::NoResponse,
10869-
Err(err) => match responder {
10870-
Some(responder) => {
10871-
abandon_if_payment(context);
10872-
responder.respond(OffersMessage::InvoiceError(err))
10873-
},
10856+
let error = InvoiceError::from(Bolt12SemanticError::UnknownRequiredFeatures);
10857+
let response = match responder {
10858+
Some(responder) => responder.respond(OffersMessage::InvoiceError(error)),
1087410859
None => {
10875-
abandon_if_payment(context);
10876-
log_trace!(
10877-
logger,
10878-
"An error response was generated, but there is no reply_path specified \
10879-
for sending the response. Error: {}",
10880-
err
10881-
);
10882-
return ResponseInstruction::NoResponse;
10860+
log_trace!(logger, "No reply path to send error: {:?}", error);
10861+
ResponseInstruction::NoResponse
1088310862
},
10863+
};
10864+
return response;
10865+
}
10866+
10867+
if self.default_configuration.manually_handle_bolt12_invoices {
10868+
let event = Event::InvoiceReceived {
10869+
payment_id, invoice, context, responder,
10870+
};
10871+
self.pending_events.lock().unwrap().push_back((event, None));
10872+
return ResponseInstruction::NoResponse;
10873+
}
10874+
10875+
match self.send_payment_for_verified_bolt12_invoice(&invoice, payment_id) {
10876+
// Payments with SendingFailed error will already have been abandoned.
10877+
Err(Bolt12PaymentError::SendingFailed(e)) => {
10878+
log_trace!(logger, "Failed paying invoice: {:?}", e);
10879+
10880+
let err = InvoiceError::from_string(format!("{:?}", e));
10881+
match responder {
10882+
Some(responder) => responder.respond(OffersMessage::InvoiceError(err)),
10883+
None => {
10884+
log_trace!(logger, "No reply path to send error: {:?}", err);
10885+
ResponseInstruction::NoResponse
10886+
},
10887+
}
1088410888
},
10889+
// Otherwise, don't abandon unknown, pending, or successful payments.
10890+
Err(Bolt12PaymentError::UnexpectedInvoice)
10891+
| Err(Bolt12PaymentError::DuplicateInvoice)
10892+
| Ok(()) => ResponseInstruction::NoResponse,
1088510893
}
1088610894
},
1088710895
#[cfg(async_payments)]

lightning/src/ln/max_payment_path_len_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ fn bolt12_invoice_too_large_blinded_paths() {
388388
let invoice_om = nodes[1].onion_messenger.next_onion_message_for_peer(nodes[0].node.get_our_node_id()).unwrap();
389389
nodes[0].onion_messenger.handle_onion_message(&nodes[1].node.get_our_node_id(), &invoice_om);
390390
// TODO: assert on the invoice error once we support replying to invoice OMs with failure info
391-
nodes[0].logger.assert_log_contains("lightning::ln::channelmanager", "Failed paying invoice: SendingFailed(OnionPacketSizeExceeded)", 1);
391+
nodes[0].logger.assert_log_contains("lightning::ln::channelmanager", "Failed paying invoice: OnionPacketSizeExceeded", 1);
392392

393393
let events = nodes[0].node.get_and_clear_pending_events();
394394
assert_eq!(events.len(), 1);

lightning/src/ln/offers_tests.rs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2027,16 +2027,13 @@ fn fails_paying_invoice_more_than_once() {
20272027
let onion_message = charlie.onion_messenger.next_onion_message_for_peer(david_id).unwrap();
20282028
david.onion_messenger.handle_onion_message(&charlie_id, &onion_message);
20292029

2030-
// David pays the first invoice
2030+
// David initiates paying the first invoice
20312031
let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext {});
20322032
let (invoice1, _) = extract_invoice(david, &onion_message);
20332033

20342034
route_bolt12_payment(david, &[charlie, bob, alice], &invoice1);
20352035
expect_recent_payment!(david, RecentPaymentDetails::Pending, payment_id);
20362036

2037-
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context);
2038-
expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
2039-
20402037
disconnect_peers(alice, &[charlie]);
20412038

20422039
// Alice sends the second invoice
@@ -2054,13 +2051,11 @@ fn fails_paying_invoice_more_than_once() {
20542051
let (invoice2, _) = extract_invoice(david, &onion_message);
20552052
assert_eq!(invoice1.payer_metadata(), invoice2.payer_metadata());
20562053

2057-
// David sends an error instead of paying the second invoice
2058-
let onion_message = david.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
2059-
bob.onion_messenger.handle_onion_message(&david_id, &onion_message);
2060-
2061-
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
2062-
alice.onion_messenger.handle_onion_message(&bob_id, &onion_message);
2054+
// David doesn't initiate paying the second invoice
2055+
assert!(david.onion_messenger.next_onion_message_for_peer(bob_id).is_none());
2056+
assert!(david.node.get_and_clear_pending_msg_events().is_empty());
20632057

2064-
let invoice_error = extract_invoice_error(alice, &onion_message);
2065-
assert_eq!(invoice_error, InvoiceError::from_string("DuplicateInvoice".to_string()));
2058+
// Complete paying the first invoice
2059+
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context);
2060+
expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
20662061
}

0 commit comments

Comments
 (0)