Skip to content

Commit cfdd217

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 cfdd217

File tree

2 files changed

+77
-26
lines changed

2 files changed

+77
-26
lines changed

lightning/src/ln/channelmanager.rs

+21-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,25 @@ 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+
match self.pending_outbound_payments.pending_outbound_payments.lock().unwrap().entry(payment_id) {
12485+
hash_map::Entry::Occupied(entry) => match entry.get() {
12486+
PendingOutboundPayment::AwaitingInvoice {
12487+
retry_strategy, route_params_config, ..
12488+
} => {
12489+
*entry.into_mut() = PendingOutboundPayment::InvoiceReceived {
12490+
payment_hash: invoice.payment_hash(),
12491+
retry_strategy: *retry_strategy,
12492+
route_params_config: *route_params_config,
12493+
};
12494+
},
12495+
_ => return None
12496+
},
12497+
hash_map::Entry::Vacant(_) => return None,
12498+
}
12499+
1248012500
let event = Event::InvoiceReceived {
1248112501
payment_id, invoice, context, responder,
1248212502
};

lightning/src/ln/outbound_payment.rs

+56-25
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,
@@ -863,25 +863,8 @@ impl OutboundPayments {
863863
SP: Fn(SendAlongPathArgs) -> Result<(), APIError>,
864864
{
865865
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-
}
866+
let (retry_strategy, params_config) = self
867+
.mark_invoice_received(payment_id, payment_hash, with_manual_handling)?;
885868

886869
if invoice.invoice_features().requires_unknown_bits_from(&features) {
887870
self.abandon_payment(
@@ -1789,6 +1772,54 @@ impl OutboundPayments {
17891772
}
17901773
}
17911774

1775+
fn mark_invoice_received(
1776+
&self, payment_id: PaymentId, payment_hash: PaymentHash, with_manual_handling: bool
1777+
) -> Result<(Retry, RouteParametersConfig), Bolt12PaymentError> {
1778+
let retry_strategy;
1779+
let params_config;
1780+
1781+
if with_manual_handling {
1782+
// When manual invoice handling is enabled, the corresponding `PendingOutboundPayment` entry
1783+
// is already updated at the time the invoice is received. This ensures that `InvoiceReceived`
1784+
// event generation remains idempotent, even if the same invoice is received again before the
1785+
// current one is handled by the user.
1786+
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
1787+
hash_map::Entry::Occupied(entry) => match entry.get() {
1788+
PendingOutboundPayment::InvoiceReceived {
1789+
retry_strategy: retry, route_params_config, ..
1790+
} => {
1791+
retry_strategy = *retry;
1792+
params_config = *route_params_config;
1793+
},
1794+
_ => return Err(Bolt12PaymentError::DuplicateInvoice),
1795+
},
1796+
hash_map::Entry::Vacant(_) => return Err(Bolt12PaymentError::UnexpectedInvoice),
1797+
}
1798+
} else {
1799+
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
1800+
hash_map::Entry::Occupied(entry) => match entry.get() {
1801+
PendingOutboundPayment::AwaitingInvoice {
1802+
retry_strategy: retry, route_params_config, ..
1803+
} => {
1804+
retry_strategy = *retry;
1805+
params_config = *route_params_config;
1806+
1807+
*entry.into_mut() = PendingOutboundPayment::InvoiceReceived {
1808+
payment_hash,
1809+
retry_strategy: *retry,
1810+
route_params_config: *route_params_config,
1811+
};
1812+
},
1813+
_ => return Err(Bolt12PaymentError::DuplicateInvoice),
1814+
},
1815+
hash_map::Entry::Vacant(_) => return Err(Bolt12PaymentError::UnexpectedInvoice),
1816+
}
1817+
}
1818+
1819+
Ok((retry_strategy, params_config))
1820+
1821+
}
1822+
17921823
fn pay_route_internal<NS: Deref, F>(
17931824
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields,
17941825
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
@@ -2868,7 +2899,7 @@ mod tests {
28682899
outbound_payments.send_payment_for_bolt12_invoice(
28692900
&invoice, payment_id, &&router, vec![], Bolt12InvoiceFeatures::empty(),
28702901
|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, &EmptyNodeIdLookUp {},
2871-
&secp_ctx, 0, &&logger, &pending_events, |_| panic!()
2902+
&secp_ctx, 0, &&logger, &pending_events, |_| panic!(), false
28722903
),
28732904
Err(Bolt12PaymentError::SendingFailed(RetryableSendFailure::PaymentExpired)),
28742905
);
@@ -2930,7 +2961,7 @@ mod tests {
29302961
outbound_payments.send_payment_for_bolt12_invoice(
29312962
&invoice, payment_id, &&router, vec![], Bolt12InvoiceFeatures::empty(),
29322963
|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, &EmptyNodeIdLookUp {},
2933-
&secp_ctx, 0, &&logger, &pending_events, |_| panic!()
2964+
&secp_ctx, 0, &&logger, &pending_events, |_| panic!(), false
29342965
),
29352966
Err(Bolt12PaymentError::SendingFailed(RetryableSendFailure::RouteNotFound)),
29362967
);
@@ -3005,7 +3036,7 @@ mod tests {
30053036
outbound_payments.send_payment_for_bolt12_invoice(
30063037
&invoice, payment_id, &&router, vec![], Bolt12InvoiceFeatures::empty(),
30073038
|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, &EmptyNodeIdLookUp {},
3008-
&secp_ctx, 0, &&logger, &pending_events, |_| panic!()
3039+
&secp_ctx, 0, &&logger, &pending_events, |_| panic!(), false
30093040
),
30103041
Err(Bolt12PaymentError::UnexpectedInvoice),
30113042
);
@@ -3025,7 +3056,7 @@ mod tests {
30253056
outbound_payments.send_payment_for_bolt12_invoice(
30263057
&invoice, payment_id, &&router, vec![], Bolt12InvoiceFeatures::empty(),
30273058
|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, &EmptyNodeIdLookUp {},
3028-
&secp_ctx, 0, &&logger, &pending_events, |_| Ok(())
3059+
&secp_ctx, 0, &&logger, &pending_events, |_| Ok(()), false
30293060
),
30303061
Ok(()),
30313062
);
@@ -3036,7 +3067,7 @@ mod tests {
30363067
outbound_payments.send_payment_for_bolt12_invoice(
30373068
&invoice, payment_id, &&router, vec![], Bolt12InvoiceFeatures::empty(),
30383069
|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, &EmptyNodeIdLookUp {},
3039-
&secp_ctx, 0, &&logger, &pending_events, |_| panic!()
3070+
&secp_ctx, 0, &&logger, &pending_events, |_| panic!(), false
30403071
),
30413072
Err(Bolt12PaymentError::DuplicateInvoice),
30423073
);

0 commit comments

Comments
 (0)