Skip to content

Commit bf7a0d6

Browse files
committed
Introduce RetryableInvoiceRequest in AwaitingInvoice
1. To enable the retry of the Invoice Request message, it's necessary to store the essential data required to recreate the message. 2. A new struct is introduced to manage this data, ensuring the InvoiceRequest message can be reliably recreated for retries. 3. The addition of an `awaiting_invoice` flag allows tracking of retryable invoice requests, preventing the need to lock the `pending_outbound_payment` mutex.
1 parent 680d399 commit bf7a0d6

File tree

3 files changed

+91
-31
lines changed

3 files changed

+91
-31
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING};
6060
use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
6161
#[cfg(test)]
6262
use crate::ln::outbound_payment;
63-
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration};
63+
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, RetryableInvoiceRequest, SendAlongPathArgs, StaleExpiration};
6464
use crate::ln::wire::Encode;
6565
use crate::offers::invoice::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice};
6666
use crate::offers::invoice_error::InvoiceError;
@@ -3070,7 +3070,7 @@ where
30703070

30713071
outbound_scid_aliases: Mutex::new(new_hash_set()),
30723072
pending_inbound_payments: Mutex::new(new_hash_map()),
3073-
pending_outbound_payments: OutboundPayments::new(),
3073+
pending_outbound_payments: OutboundPayments::new(new_hash_map()),
30743074
forward_htlcs: Mutex::new(new_hash_map()),
30753075
decode_update_add_htlcs: Mutex::new(new_hash_map()),
30763076
claimable_payments: Mutex::new(ClaimablePayments { claimable_payments: new_hash_map(), pending_claiming_payments: new_hash_map() }),
@@ -8929,7 +8929,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
89298929
let expiration = StaleExpiration::AbsoluteTimeout(absolute_expiry);
89308930
$self.pending_outbound_payments
89318931
.add_new_awaiting_invoice(
8932-
payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
8932+
payment_id, expiration, retry_strategy, max_total_routing_fee_msat, None,
89338933
)
89348934
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
89358935

@@ -9055,9 +9055,14 @@ where
90559055
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
90569056

90579057
let expiration = StaleExpiration::TimerTicks(1);
9058+
let retryable_invoice_request = RetryableInvoiceRequest {
9059+
invoice_request: invoice_request.clone(),
9060+
nonce,
9061+
};
90589062
self.pending_outbound_payments
90599063
.add_new_awaiting_invoice(
9060-
payment_id, expiration, retry_strategy, max_total_routing_fee_msat
9064+
payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
9065+
Some(retryable_invoice_request)
90619066
)
90629067
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
90639068

@@ -12139,10 +12144,7 @@ where
1213912144
}
1214012145
pending_outbound_payments = Some(outbounds);
1214112146
}
12142-
let pending_outbounds = OutboundPayments {
12143-
pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()),
12144-
retry_lock: Mutex::new(())
12145-
};
12147+
let pending_outbounds = OutboundPayments::new(pending_outbound_payments.unwrap());
1214612148

1214712149
// We have to replay (or skip, if they were completed after we wrote the `ChannelManager`)
1214812150
// each `ChannelMonitorUpdate` in `in_flight_monitor_updates`. After doing so, we have to

lightning/src/ln/outbound_payment.rs

Lines changed: 74 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ use crate::ln::features::Bolt12InvoiceFeatures;
2222
use crate::ln::onion_utils;
2323
use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason};
2424
use crate::offers::invoice::Bolt12Invoice;
25+
use crate::offers::invoice_request::InvoiceRequest;
26+
use crate::offers::nonce::Nonce;
2527
use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router};
2628
use crate::sign::{EntropySource, NodeSigner, Recipient};
2729
use crate::util::errors::APIError;
@@ -33,6 +35,7 @@ use crate::util::ser::ReadableArgs;
3335

3436
use core::fmt::{self, Display, Formatter};
3537
use core::ops::Deref;
38+
use core::sync::atomic::{AtomicBool, Ordering};
3639
use core::time::Duration;
3740

3841
use crate::prelude::*;
@@ -54,6 +57,7 @@ pub(crate) enum PendingOutboundPayment {
5457
expiration: StaleExpiration,
5558
retry_strategy: Retry,
5659
max_total_routing_fee_msat: Option<u64>,
60+
retryable_invoice_request: Option<RetryableInvoiceRequest>
5761
},
5862
InvoiceReceived {
5963
payment_hash: PaymentHash,
@@ -101,6 +105,16 @@ pub(crate) enum PendingOutboundPayment {
101105
},
102106
}
103107

108+
pub(crate) struct RetryableInvoiceRequest {
109+
pub(crate) invoice_request: InvoiceRequest,
110+
pub(crate) nonce: Nonce,
111+
}
112+
113+
impl_writeable_tlv_based!(RetryableInvoiceRequest, {
114+
(0, invoice_request, required),
115+
(2, nonce, required),
116+
});
117+
104118
impl PendingOutboundPayment {
105119
fn increment_attempts(&mut self) {
106120
if let PendingOutboundPayment::Retryable { attempts, .. } = self {
@@ -684,13 +698,19 @@ pub(super) struct SendAlongPathArgs<'a> {
684698

685699
pub(super) struct OutboundPayments {
686700
pub(super) pending_outbound_payments: Mutex<HashMap<PaymentId, PendingOutboundPayment>>,
687-
pub(super) retry_lock: Mutex<()>,
701+
awaiting_invoice: AtomicBool,
702+
retry_lock: Mutex<()>,
688703
}
689704

690705
impl OutboundPayments {
691-
pub(super) fn new() -> Self {
706+
pub(super) fn new(pending_outbound_payments: HashMap<PaymentId, PendingOutboundPayment>) -> Self {
707+
let has_invoice_requests = pending_outbound_payments.values().any(|payment| {
708+
matches!(payment, PendingOutboundPayment::AwaitingInvoice { retryable_invoice_request: Some(_), .. })
709+
});
710+
692711
Self {
693-
pending_outbound_payments: Mutex::new(new_hash_map()),
712+
pending_outbound_payments: Mutex::new(pending_outbound_payments),
713+
awaiting_invoice: AtomicBool::new(has_invoice_requests),
694714
retry_lock: Mutex::new(()),
695715
}
696716
}
@@ -1411,16 +1431,20 @@ impl OutboundPayments {
14111431

14121432
pub(super) fn add_new_awaiting_invoice(
14131433
&self, payment_id: PaymentId, expiration: StaleExpiration, retry_strategy: Retry,
1414-
max_total_routing_fee_msat: Option<u64>
1434+
max_total_routing_fee_msat: Option<u64>, retryable_invoice_request: Option<RetryableInvoiceRequest>
14151435
) -> Result<(), ()> {
14161436
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
14171437
match pending_outbounds.entry(payment_id) {
14181438
hash_map::Entry::Occupied(_) => Err(()),
14191439
hash_map::Entry::Vacant(entry) => {
1440+
retryable_invoice_request.as_ref().map(|_|
1441+
self.awaiting_invoice.store(true, Ordering::Release)
1442+
);
14201443
entry.insert(PendingOutboundPayment::AwaitingInvoice {
14211444
expiration,
14221445
retry_strategy,
14231446
max_total_routing_fee_msat,
1447+
retryable_invoice_request,
14241448
});
14251449

14261450
Ok(())
@@ -1892,6 +1916,31 @@ impl OutboundPayments {
18921916
pub fn clear_pending_payments(&self) {
18931917
self.pending_outbound_payments.lock().unwrap().clear()
18941918
}
1919+
1920+
pub fn release_invoice_requests_awaiting_invoice(&self) -> Vec<(PaymentId, RetryableInvoiceRequest)> {
1921+
if !self.awaiting_invoice.load(Ordering::Acquire) {
1922+
return vec![];
1923+
}
1924+
1925+
let mut pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
1926+
let invoice_requests = pending_outbound_payments
1927+
.iter_mut()
1928+
.filter_map(|(payment_id, payment)| {
1929+
if let PendingOutboundPayment::AwaitingInvoice {
1930+
retryable_invoice_request, ..
1931+
} = payment {
1932+
retryable_invoice_request.take().map(|retryable_invoice_request| {
1933+
(*payment_id, retryable_invoice_request)
1934+
})
1935+
} else {
1936+
None
1937+
}
1938+
})
1939+
.collect();
1940+
1941+
self.awaiting_invoice.store(false, Ordering::Release);
1942+
invoice_requests
1943+
}
18951944
}
18961945

18971946
/// Returns whether a payment with the given [`PaymentHash`] and [`PaymentId`] is, in fact, a
@@ -1947,6 +1996,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
19471996
(0, expiration, required),
19481997
(2, retry_strategy, required),
19491998
(4, max_total_routing_fee_msat, option),
1999+
(5, retryable_invoice_request, option),
19502000
},
19512001
(7, InvoiceReceived) => {
19522002
(0, payment_hash, required),
@@ -1977,6 +2027,7 @@ mod tests {
19772027
use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters};
19782028
use crate::sync::{Arc, Mutex, RwLock};
19792029
use crate::util::errors::APIError;
2030+
use crate::util::hash_tables::new_hash_map;
19802031
use crate::util::test_utils;
19812032

19822033
use alloc::collections::VecDeque;
@@ -2011,7 +2062,7 @@ mod tests {
20112062
}
20122063
#[cfg(feature = "std")]
20132064
fn do_fails_paying_after_expiration(on_retry: bool) {
2014-
let outbound_payments = OutboundPayments::new();
2065+
let outbound_payments = OutboundPayments::new(new_hash_map());
20152066
let logger = test_utils::TestLogger::new();
20162067
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
20172068
let scorer = RwLock::new(test_utils::TestScorer::new());
@@ -2055,7 +2106,7 @@ mod tests {
20552106
do_find_route_error(true);
20562107
}
20572108
fn do_find_route_error(on_retry: bool) {
2058-
let outbound_payments = OutboundPayments::new();
2109+
let outbound_payments = OutboundPayments::new(new_hash_map());
20592110
let logger = test_utils::TestLogger::new();
20602111
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
20612112
let scorer = RwLock::new(test_utils::TestScorer::new());
@@ -2094,7 +2145,7 @@ mod tests {
20942145

20952146
#[test]
20962147
fn initial_send_payment_path_failed_evs() {
2097-
let outbound_payments = OutboundPayments::new();
2148+
let outbound_payments = OutboundPayments::new(new_hash_map());
20982149
let logger = test_utils::TestLogger::new();
20992150
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
21002151
let scorer = RwLock::new(test_utils::TestScorer::new());
@@ -2176,7 +2227,7 @@ mod tests {
21762227
#[test]
21772228
fn removes_stale_awaiting_invoice_using_absolute_timeout() {
21782229
let pending_events = Mutex::new(VecDeque::new());
2179-
let outbound_payments = OutboundPayments::new();
2230+
let outbound_payments = OutboundPayments::new(new_hash_map());
21802231
let payment_id = PaymentId([0; 32]);
21812232
let absolute_expiry = 100;
21822233
let tick_interval = 10;
@@ -2185,7 +2236,7 @@ mod tests {
21852236
assert!(!outbound_payments.has_pending_payments());
21862237
assert!(
21872238
outbound_payments.add_new_awaiting_invoice(
2188-
payment_id, expiration, Retry::Attempts(0), None
2239+
payment_id, expiration, Retry::Attempts(0), None, None,
21892240
).is_ok()
21902241
);
21912242
assert!(outbound_payments.has_pending_payments());
@@ -2215,30 +2266,30 @@ mod tests {
22152266

22162267
assert!(
22172268
outbound_payments.add_new_awaiting_invoice(
2218-
payment_id, expiration, Retry::Attempts(0), None
2269+
payment_id, expiration, Retry::Attempts(0), None, None,
22192270
).is_ok()
22202271
);
22212272
assert!(outbound_payments.has_pending_payments());
22222273

22232274
assert!(
22242275
outbound_payments.add_new_awaiting_invoice(
2225-
payment_id, expiration, Retry::Attempts(0), None
2276+
payment_id, expiration, Retry::Attempts(0), None, None,
22262277
).is_err()
22272278
);
22282279
}
22292280

22302281
#[test]
22312282
fn removes_stale_awaiting_invoice_using_timer_ticks() {
22322283
let pending_events = Mutex::new(VecDeque::new());
2233-
let outbound_payments = OutboundPayments::new();
2284+
let outbound_payments = OutboundPayments::new(new_hash_map());
22342285
let payment_id = PaymentId([0; 32]);
22352286
let timer_ticks = 3;
22362287
let expiration = StaleExpiration::TimerTicks(timer_ticks);
22372288

22382289
assert!(!outbound_payments.has_pending_payments());
22392290
assert!(
22402291
outbound_payments.add_new_awaiting_invoice(
2241-
payment_id, expiration, Retry::Attempts(0), None
2292+
payment_id, expiration, Retry::Attempts(0), None, None,
22422293
).is_ok()
22432294
);
22442295
assert!(outbound_payments.has_pending_payments());
@@ -2268,29 +2319,29 @@ mod tests {
22682319

22692320
assert!(
22702321
outbound_payments.add_new_awaiting_invoice(
2271-
payment_id, expiration, Retry::Attempts(0), None
2322+
payment_id, expiration, Retry::Attempts(0), None, None,
22722323
).is_ok()
22732324
);
22742325
assert!(outbound_payments.has_pending_payments());
22752326

22762327
assert!(
22772328
outbound_payments.add_new_awaiting_invoice(
2278-
payment_id, expiration, Retry::Attempts(0), None
2329+
payment_id, expiration, Retry::Attempts(0), None, None,
22792330
).is_err()
22802331
);
22812332
}
22822333

22832334
#[test]
22842335
fn removes_abandoned_awaiting_invoice() {
22852336
let pending_events = Mutex::new(VecDeque::new());
2286-
let outbound_payments = OutboundPayments::new();
2337+
let outbound_payments = OutboundPayments::new(new_hash_map());
22872338
let payment_id = PaymentId([0; 32]);
22882339
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
22892340

22902341
assert!(!outbound_payments.has_pending_payments());
22912342
assert!(
22922343
outbound_payments.add_new_awaiting_invoice(
2293-
payment_id, expiration, Retry::Attempts(0), None
2344+
payment_id, expiration, Retry::Attempts(0), None, None,
22942345
).is_ok()
22952346
);
22962347
assert!(outbound_payments.has_pending_payments());
@@ -2320,13 +2371,13 @@ mod tests {
23202371
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
23212372

23222373
let pending_events = Mutex::new(VecDeque::new());
2323-
let outbound_payments = OutboundPayments::new();
2374+
let outbound_payments = OutboundPayments::new(new_hash_map());
23242375
let payment_id = PaymentId([0; 32]);
23252376
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
23262377

23272378
assert!(
23282379
outbound_payments.add_new_awaiting_invoice(
2329-
payment_id, expiration, Retry::Attempts(0), None
2380+
payment_id, expiration, Retry::Attempts(0), None, None,
23302381
).is_ok()
23312382
);
23322383
assert!(outbound_payments.has_pending_payments());
@@ -2373,7 +2424,7 @@ mod tests {
23732424
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
23742425

23752426
let pending_events = Mutex::new(VecDeque::new());
2376-
let outbound_payments = OutboundPayments::new();
2427+
let outbound_payments = OutboundPayments::new(new_hash_map());
23772428
let payment_id = PaymentId([0; 32]);
23782429
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
23792430

@@ -2390,7 +2441,7 @@ mod tests {
23902441
assert!(
23912442
outbound_payments.add_new_awaiting_invoice(
23922443
payment_id, expiration, Retry::Attempts(0),
2393-
Some(invoice.amount_msats() / 100 + 50_000)
2444+
Some(invoice.amount_msats() / 100 + 50_000), None,
23942445
).is_ok()
23952446
);
23962447
assert!(outbound_payments.has_pending_payments());
@@ -2434,7 +2485,7 @@ mod tests {
24342485
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
24352486

24362487
let pending_events = Mutex::new(VecDeque::new());
2437-
let outbound_payments = OutboundPayments::new();
2488+
let outbound_payments = OutboundPayments::new(new_hash_map());
24382489
let payment_id = PaymentId([0; 32]);
24392490
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
24402491

@@ -2490,7 +2541,7 @@ mod tests {
24902541

24912542
assert!(
24922543
outbound_payments.add_new_awaiting_invoice(
2493-
payment_id, expiration, Retry::Attempts(0), Some(1234)
2544+
payment_id, expiration, Retry::Attempts(0), Some(1234), None,
24942545
).is_ok()
24952546
);
24962547
assert!(outbound_payments.has_pending_payments());

lightning/src/offers/invoice_request.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,13 @@ impl Writeable for InvoiceRequestContents {
10391039
}
10401040
}
10411041

1042+
impl Readable for InvoiceRequest {
1043+
fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
1044+
let bytes: WithoutLength<Vec<u8>> = Readable::read(reader)?;
1045+
Self::try_from(bytes.0).map_err(|_| DecodeError::InvalidValue)
1046+
}
1047+
}
1048+
10421049
/// Valid type range for invoice_request TLV records.
10431050
pub(super) const INVOICE_REQUEST_TYPES: core::ops::Range<u64> = 80..160;
10441051

0 commit comments

Comments
 (0)