Skip to content

Commit 21ec608

Browse files
committed
Make InvoiceReceived event generation idempotent
Ensures `InvoiceReceived` events are not generated multiple times when `manually_handle_bolt12_invoice` is enabled. Duplicate events for the same invoice could cause confusion—this change introduces an idempotency check to prevent that.
1 parent 5bc9ffa commit 21ec608

File tree

2 files changed

+69
-27
lines changed

2 files changed

+69
-27
lines changed

lightning/src/ln/channelmanager.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -4824,7 +4824,8 @@ where
48244824
invoice, payment_id, &self.router, self.list_usable_channels(), features,
48254825
|| self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, &self,
48264826
&self.secp_ctx, best_block_height, &self.logger, &self.pending_events,
4827-
|args| self.send_payment_along_path(args)
4827+
|args| self.send_payment_along_path(args),
4828+
self.default_configuration.manually_handle_bolt12_invoices
48284829
)
48294830
}
48304831

@@ -12477,6 +12478,11 @@ where
1247712478
);
1247812479

1247912480
if self.default_configuration.manually_handle_bolt12_invoices {
12481+
// Update the corresponding entry in `PendingOutboundPayment` for this invoice.
12482+
// This ensures that event generation remains idempotent in case we receive
12483+
// the same invoice multiple times.
12484+
self.pending_outbound_payments.mark_invoice_received(&invoice, payment_id).ok()?;
12485+
1248012486
let event = Event::InvoiceReceived {
1248112487
payment_id, invoice, context, responder,
1248212488
};

lightning/src/ln/outbound_payment.rs

+62-26
Original file line numberDiff line numberDiff line change
@@ -851,7 +851,7 @@ impl OutboundPayments {
851851
entropy_source: &ES, node_signer: &NS, node_id_lookup: &NL,
852852
secp_ctx: &Secp256k1<secp256k1::All>, best_block_height: u32, logger: &L,
853853
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
854-
send_payment_along_path: SP,
854+
send_payment_along_path: SP, with_manual_handling: bool
855855
) -> Result<(), Bolt12PaymentError>
856856
where
857857
R::Target: Router,
@@ -862,26 +862,15 @@ impl OutboundPayments {
862862
IH: Fn() -> InFlightHtlcs,
863863
SP: Fn(SendAlongPathArgs) -> Result<(), APIError>,
864864
{
865-
let payment_hash = invoice.payment_hash();
866-
let params_config;
867-
let retry_strategy;
868-
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
869-
hash_map::Entry::Occupied(entry) => match entry.get() {
870-
PendingOutboundPayment::AwaitingInvoice {
871-
retry_strategy: retry, route_params_config, ..
872-
} => {
873-
retry_strategy = *retry;
874-
params_config = *route_params_config;
875-
*entry.into_mut() = PendingOutboundPayment::InvoiceReceived {
876-
payment_hash,
877-
retry_strategy: *retry,
878-
route_params_config: *route_params_config,
879-
};
880-
},
881-
_ => return Err(Bolt12PaymentError::DuplicateInvoice),
882-
},
883-
hash_map::Entry::Vacant(_) => return Err(Bolt12PaymentError::UnexpectedInvoice),
884-
}
865+
// When manual invoice handling is enabled, the corresponding `PendingOutboundPayment` entry
866+
// is already updated at the time the invoice is received. This ensures that `InvoiceReceived`
867+
// event generation remains idempotent, even if the same invoice is received again before the
868+
// event is handled by the user.
869+
let (payment_hash, retry_strategy, params_config) = if with_manual_handling {
870+
self.received_invoice_details(invoice, payment_id)?
871+
} else {
872+
self.mark_invoice_received_and_get_details(invoice, payment_id)?
873+
};
885874

886875
if invoice.invoice_features().requires_unknown_bits_from(&features) {
887876
self.abandon_payment(
@@ -1789,6 +1778,53 @@ impl OutboundPayments {
17891778
}
17901779
}
17911780

1781+
pub(super) fn mark_invoice_received(
1782+
&self, invoice: &Bolt12Invoice, payment_id: PaymentId
1783+
) -> Result<(), Bolt12PaymentError> {
1784+
self.mark_invoice_received_and_get_details(invoice, payment_id).map(|_| ())
1785+
}
1786+
1787+
fn mark_invoice_received_and_get_details(
1788+
&self, invoice: &Bolt12Invoice, payment_id: PaymentId
1789+
) -> Result<(PaymentHash, Retry, RouteParametersConfig), Bolt12PaymentError> {
1790+
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
1791+
hash_map::Entry::Occupied(entry) => match entry.get() {
1792+
PendingOutboundPayment::AwaitingInvoice {
1793+
retry_strategy: retry, route_params_config, ..
1794+
} => {
1795+
let payment_hash = invoice.payment_hash();
1796+
let retry = *retry;
1797+
let config = *route_params_config;
1798+
*entry.into_mut() = PendingOutboundPayment::InvoiceReceived {
1799+
payment_hash,
1800+
retry_strategy: retry,
1801+
route_params_config: config,
1802+
};
1803+
1804+
Ok((payment_hash, retry, config))
1805+
},
1806+
_ => Err(Bolt12PaymentError::DuplicateInvoice),
1807+
},
1808+
hash_map::Entry::Vacant(_) => Err(Bolt12PaymentError::UnexpectedInvoice),
1809+
}
1810+
}
1811+
1812+
fn received_invoice_details(
1813+
&self, invoice: &Bolt12Invoice, payment_id: PaymentId,
1814+
) -> Result<(PaymentHash, Retry, RouteParametersConfig), Bolt12PaymentError> {
1815+
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
1816+
hash_map::Entry::Occupied(entry) => match entry.get() {
1817+
PendingOutboundPayment::InvoiceReceived {
1818+
retry_strategy, route_params_config, ..
1819+
} => {
1820+
Ok((invoice.payment_hash(), *retry_strategy, *route_params_config))
1821+
},
1822+
_ => Err(Bolt12PaymentError::DuplicateInvoice),
1823+
},
1824+
hash_map::Entry::Vacant(_) => Err(Bolt12PaymentError::UnexpectedInvoice),
1825+
}
1826+
}
1827+
17921828
fn pay_route_internal<NS: Deref, F>(
17931829
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields,
17941830
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
@@ -2868,7 +2904,7 @@ mod tests {
28682904
outbound_payments.send_payment_for_bolt12_invoice(
28692905
&invoice, payment_id, &&router, vec![], Bolt12InvoiceFeatures::empty(),
28702906
|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, &EmptyNodeIdLookUp {},
2871-
&secp_ctx, 0, &&logger, &pending_events, |_| panic!()
2907+
&secp_ctx, 0, &&logger, &pending_events, |_| panic!(), false
28722908
),
28732909
Err(Bolt12PaymentError::SendingFailed(RetryableSendFailure::PaymentExpired)),
28742910
);
@@ -2930,7 +2966,7 @@ mod tests {
29302966
outbound_payments.send_payment_for_bolt12_invoice(
29312967
&invoice, payment_id, &&router, vec![], Bolt12InvoiceFeatures::empty(),
29322968
|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, &EmptyNodeIdLookUp {},
2933-
&secp_ctx, 0, &&logger, &pending_events, |_| panic!()
2969+
&secp_ctx, 0, &&logger, &pending_events, |_| panic!(), false
29342970
),
29352971
Err(Bolt12PaymentError::SendingFailed(RetryableSendFailure::RouteNotFound)),
29362972
);
@@ -3005,7 +3041,7 @@ mod tests {
30053041
outbound_payments.send_payment_for_bolt12_invoice(
30063042
&invoice, payment_id, &&router, vec![], Bolt12InvoiceFeatures::empty(),
30073043
|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, &EmptyNodeIdLookUp {},
3008-
&secp_ctx, 0, &&logger, &pending_events, |_| panic!()
3044+
&secp_ctx, 0, &&logger, &pending_events, |_| panic!(), false
30093045
),
30103046
Err(Bolt12PaymentError::UnexpectedInvoice),
30113047
);
@@ -3025,7 +3061,7 @@ mod tests {
30253061
outbound_payments.send_payment_for_bolt12_invoice(
30263062
&invoice, payment_id, &&router, vec![], Bolt12InvoiceFeatures::empty(),
30273063
|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, &EmptyNodeIdLookUp {},
3028-
&secp_ctx, 0, &&logger, &pending_events, |_| Ok(())
3064+
&secp_ctx, 0, &&logger, &pending_events, |_| Ok(()), false
30293065
),
30303066
Ok(()),
30313067
);
@@ -3036,7 +3072,7 @@ mod tests {
30363072
outbound_payments.send_payment_for_bolt12_invoice(
30373073
&invoice, payment_id, &&router, vec![], Bolt12InvoiceFeatures::empty(),
30383074
|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, &EmptyNodeIdLookUp {},
3039-
&secp_ctx, 0, &&logger, &pending_events, |_| panic!()
3075+
&secp_ctx, 0, &&logger, &pending_events, |_| panic!(), false
30403076
),
30413077
Err(Bolt12PaymentError::DuplicateInvoice),
30423078
);

0 commit comments

Comments
 (0)