Skip to content

Commit 17d80f9

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 db905e8 commit 17d80f9

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
@@ -61,7 +61,7 @@ use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING};
6161
use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
6262
#[cfg(test)]
6363
use crate::ln::outbound_payment;
64-
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration};
64+
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, RetryableInvoiceRequest, SendAlongPathArgs, StaleExpiration};
6565
use crate::ln::wire::Encode;
6666
use crate::offers::invoice::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice};
6767
use crate::offers::invoice_error::InvoiceError;
@@ -3105,7 +3105,7 @@ where
31053105

31063106
outbound_scid_aliases: Mutex::new(new_hash_set()),
31073107
pending_inbound_payments: Mutex::new(new_hash_map()),
3108-
pending_outbound_payments: OutboundPayments::new(),
3108+
pending_outbound_payments: OutboundPayments::new(new_hash_map()),
31093109
forward_htlcs: Mutex::new(new_hash_map()),
31103110
decode_update_add_htlcs: Mutex::new(new_hash_map()),
31113111
claimable_payments: Mutex::new(ClaimablePayments { claimable_payments: new_hash_map(), pending_claiming_payments: new_hash_map() }),
@@ -9005,7 +9005,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
90059005
let expiration = StaleExpiration::AbsoluteTimeout(absolute_expiry);
90069006
$self.pending_outbound_payments
90079007
.add_new_awaiting_invoice(
9008-
payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
9008+
payment_id, expiration, retry_strategy, max_total_routing_fee_msat, None,
90099009
)
90109010
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
90119011

@@ -9131,9 +9131,14 @@ where
91319131
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
91329132

91339133
let expiration = StaleExpiration::TimerTicks(1);
9134+
let retryable_invoice_request = RetryableInvoiceRequest {
9135+
invoice_request: invoice_request.clone(),
9136+
nonce,
9137+
};
91349138
self.pending_outbound_payments
91359139
.add_new_awaiting_invoice(
9136-
payment_id, expiration, retry_strategy, max_total_routing_fee_msat
9140+
payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
9141+
Some(retryable_invoice_request)
91379142
)
91389143
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
91399144

@@ -12227,10 +12232,7 @@ where
1222712232
}
1222812233
pending_outbound_payments = Some(outbounds);
1222912234
}
12230-
let pending_outbounds = OutboundPayments {
12231-
pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()),
12232-
retry_lock: Mutex::new(())
12233-
};
12235+
let pending_outbounds = OutboundPayments::new(pending_outbound_payments.unwrap());
1223412236

1223512237
// We have to replay (or skip, if they were completed after we wrote the `ChannelManager`)
1223612238
// 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;
@@ -32,6 +34,7 @@ use crate::util::ser::ReadableArgs;
3234

3335
use core::fmt::{self, Display, Formatter};
3436
use core::ops::Deref;
37+
use core::sync::atomic::{AtomicBool, Ordering};
3538
use core::time::Duration;
3639

3740
use crate::prelude::*;
@@ -53,6 +56,7 @@ pub(crate) enum PendingOutboundPayment {
5356
expiration: StaleExpiration,
5457
retry_strategy: Retry,
5558
max_total_routing_fee_msat: Option<u64>,
59+
retryable_invoice_request: Option<RetryableInvoiceRequest>
5660
},
5761
InvoiceReceived {
5862
payment_hash: PaymentHash,
@@ -100,6 +104,16 @@ pub(crate) enum PendingOutboundPayment {
100104
},
101105
}
102106

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

667681
pub(super) struct OutboundPayments {
668682
pub(super) pending_outbound_payments: Mutex<HashMap<PaymentId, PendingOutboundPayment>>,
669-
pub(super) retry_lock: Mutex<()>,
683+
awaiting_invoice: AtomicBool,
684+
retry_lock: Mutex<()>,
670685
}
671686

672687
impl OutboundPayments {
673-
pub(super) fn new() -> Self {
688+
pub(super) fn new(pending_outbound_payments: HashMap<PaymentId, PendingOutboundPayment>) -> Self {
689+
let has_invoice_requests = pending_outbound_payments.values().any(|payment| {
690+
matches!(payment, PendingOutboundPayment::AwaitingInvoice { retryable_invoice_request: Some(_), .. })
691+
});
692+
674693
Self {
675-
pending_outbound_payments: Mutex::new(new_hash_map()),
694+
pending_outbound_payments: Mutex::new(pending_outbound_payments),
695+
awaiting_invoice: AtomicBool::new(has_invoice_requests),
676696
retry_lock: Mutex::new(()),
677697
}
678698
}
@@ -1393,16 +1413,20 @@ impl OutboundPayments {
13931413

13941414
pub(super) fn add_new_awaiting_invoice(
13951415
&self, payment_id: PaymentId, expiration: StaleExpiration, retry_strategy: Retry,
1396-
max_total_routing_fee_msat: Option<u64>
1416+
max_total_routing_fee_msat: Option<u64>, retryable_invoice_request: Option<RetryableInvoiceRequest>
13971417
) -> Result<(), ()> {
13981418
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
13991419
match pending_outbounds.entry(payment_id) {
14001420
hash_map::Entry::Occupied(_) => Err(()),
14011421
hash_map::Entry::Vacant(entry) => {
1422+
if retryable_invoice_request.is_some() {
1423+
self.awaiting_invoice.store(true, Ordering::Release);
1424+
}
14021425
entry.insert(PendingOutboundPayment::AwaitingInvoice {
14031426
expiration,
14041427
retry_strategy,
14051428
max_total_routing_fee_msat,
1429+
retryable_invoice_request,
14061430
});
14071431

14081432
Ok(())
@@ -1874,6 +1898,31 @@ impl OutboundPayments {
18741898
pub fn clear_pending_payments(&self) {
18751899
self.pending_outbound_payments.lock().unwrap().clear()
18761900
}
1901+
1902+
pub fn release_invoice_requests_awaiting_invoice(&self) -> Vec<(PaymentId, RetryableInvoiceRequest)> {
1903+
if !self.awaiting_invoice.load(Ordering::Acquire) {
1904+
return vec![];
1905+
}
1906+
1907+
let mut pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
1908+
let invoice_requests = pending_outbound_payments
1909+
.iter_mut()
1910+
.filter_map(|(payment_id, payment)| {
1911+
if let PendingOutboundPayment::AwaitingInvoice {
1912+
retryable_invoice_request, ..
1913+
} = payment {
1914+
retryable_invoice_request.take().map(|retryable_invoice_request| {
1915+
(*payment_id, retryable_invoice_request)
1916+
})
1917+
} else {
1918+
None
1919+
}
1920+
})
1921+
.collect();
1922+
1923+
self.awaiting_invoice.store(false, Ordering::Release);
1924+
invoice_requests
1925+
}
18771926
}
18781927

18791928
/// Returns whether a payment with the given [`PaymentHash`] and [`PaymentId`] is, in fact, a
@@ -1929,6 +1978,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
19291978
(0, expiration, required),
19301979
(2, retry_strategy, required),
19311980
(4, max_total_routing_fee_msat, option),
1981+
(5, retryable_invoice_request, option),
19321982
},
19331983
(7, InvoiceReceived) => {
19341984
(0, payment_hash, required),
@@ -1959,6 +2009,7 @@ mod tests {
19592009
use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters};
19602010
use crate::sync::{Arc, Mutex, RwLock};
19612011
use crate::util::errors::APIError;
2012+
use crate::util::hash_tables::new_hash_map;
19622013
use crate::util::test_utils;
19632014

19642015
use alloc::collections::VecDeque;
@@ -1993,7 +2044,7 @@ mod tests {
19932044
}
19942045
#[cfg(feature = "std")]
19952046
fn do_fails_paying_after_expiration(on_retry: bool) {
1996-
let outbound_payments = OutboundPayments::new();
2047+
let outbound_payments = OutboundPayments::new(new_hash_map());
19972048
let logger = test_utils::TestLogger::new();
19982049
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
19992050
let scorer = RwLock::new(test_utils::TestScorer::new());
@@ -2037,7 +2088,7 @@ mod tests {
20372088
do_find_route_error(true);
20382089
}
20392090
fn do_find_route_error(on_retry: bool) {
2040-
let outbound_payments = OutboundPayments::new();
2091+
let outbound_payments = OutboundPayments::new(new_hash_map());
20412092
let logger = test_utils::TestLogger::new();
20422093
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
20432094
let scorer = RwLock::new(test_utils::TestScorer::new());
@@ -2076,7 +2127,7 @@ mod tests {
20762127

20772128
#[test]
20782129
fn initial_send_payment_path_failed_evs() {
2079-
let outbound_payments = OutboundPayments::new();
2130+
let outbound_payments = OutboundPayments::new(new_hash_map());
20802131
let logger = test_utils::TestLogger::new();
20812132
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
20822133
let scorer = RwLock::new(test_utils::TestScorer::new());
@@ -2158,7 +2209,7 @@ mod tests {
21582209
#[test]
21592210
fn removes_stale_awaiting_invoice_using_absolute_timeout() {
21602211
let pending_events = Mutex::new(VecDeque::new());
2161-
let outbound_payments = OutboundPayments::new();
2212+
let outbound_payments = OutboundPayments::new(new_hash_map());
21622213
let payment_id = PaymentId([0; 32]);
21632214
let absolute_expiry = 100;
21642215
let tick_interval = 10;
@@ -2167,7 +2218,7 @@ mod tests {
21672218
assert!(!outbound_payments.has_pending_payments());
21682219
assert!(
21692220
outbound_payments.add_new_awaiting_invoice(
2170-
payment_id, expiration, Retry::Attempts(0), None
2221+
payment_id, expiration, Retry::Attempts(0), None, None,
21712222
).is_ok()
21722223
);
21732224
assert!(outbound_payments.has_pending_payments());
@@ -2197,30 +2248,30 @@ mod tests {
21972248

21982249
assert!(
21992250
outbound_payments.add_new_awaiting_invoice(
2200-
payment_id, expiration, Retry::Attempts(0), None
2251+
payment_id, expiration, Retry::Attempts(0), None, None,
22012252
).is_ok()
22022253
);
22032254
assert!(outbound_payments.has_pending_payments());
22042255

22052256
assert!(
22062257
outbound_payments.add_new_awaiting_invoice(
2207-
payment_id, expiration, Retry::Attempts(0), None
2258+
payment_id, expiration, Retry::Attempts(0), None, None,
22082259
).is_err()
22092260
);
22102261
}
22112262

22122263
#[test]
22132264
fn removes_stale_awaiting_invoice_using_timer_ticks() {
22142265
let pending_events = Mutex::new(VecDeque::new());
2215-
let outbound_payments = OutboundPayments::new();
2266+
let outbound_payments = OutboundPayments::new(new_hash_map());
22162267
let payment_id = PaymentId([0; 32]);
22172268
let timer_ticks = 3;
22182269
let expiration = StaleExpiration::TimerTicks(timer_ticks);
22192270

22202271
assert!(!outbound_payments.has_pending_payments());
22212272
assert!(
22222273
outbound_payments.add_new_awaiting_invoice(
2223-
payment_id, expiration, Retry::Attempts(0), None
2274+
payment_id, expiration, Retry::Attempts(0), None, None,
22242275
).is_ok()
22252276
);
22262277
assert!(outbound_payments.has_pending_payments());
@@ -2250,29 +2301,29 @@ mod tests {
22502301

22512302
assert!(
22522303
outbound_payments.add_new_awaiting_invoice(
2253-
payment_id, expiration, Retry::Attempts(0), None
2304+
payment_id, expiration, Retry::Attempts(0), None, None,
22542305
).is_ok()
22552306
);
22562307
assert!(outbound_payments.has_pending_payments());
22572308

22582309
assert!(
22592310
outbound_payments.add_new_awaiting_invoice(
2260-
payment_id, expiration, Retry::Attempts(0), None
2311+
payment_id, expiration, Retry::Attempts(0), None, None,
22612312
).is_err()
22622313
);
22632314
}
22642315

22652316
#[test]
22662317
fn removes_abandoned_awaiting_invoice() {
22672318
let pending_events = Mutex::new(VecDeque::new());
2268-
let outbound_payments = OutboundPayments::new();
2319+
let outbound_payments = OutboundPayments::new(new_hash_map());
22692320
let payment_id = PaymentId([0; 32]);
22702321
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
22712322

22722323
assert!(!outbound_payments.has_pending_payments());
22732324
assert!(
22742325
outbound_payments.add_new_awaiting_invoice(
2275-
payment_id, expiration, Retry::Attempts(0), None
2326+
payment_id, expiration, Retry::Attempts(0), None, None,
22762327
).is_ok()
22772328
);
22782329
assert!(outbound_payments.has_pending_payments());
@@ -2302,13 +2353,13 @@ mod tests {
23022353
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
23032354

23042355
let pending_events = Mutex::new(VecDeque::new());
2305-
let outbound_payments = OutboundPayments::new();
2356+
let outbound_payments = OutboundPayments::new(new_hash_map());
23062357
let payment_id = PaymentId([0; 32]);
23072358
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
23082359

23092360
assert!(
23102361
outbound_payments.add_new_awaiting_invoice(
2311-
payment_id, expiration, Retry::Attempts(0), None
2362+
payment_id, expiration, Retry::Attempts(0), None, None,
23122363
).is_ok()
23132364
);
23142365
assert!(outbound_payments.has_pending_payments());
@@ -2355,7 +2406,7 @@ mod tests {
23552406
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
23562407

23572408
let pending_events = Mutex::new(VecDeque::new());
2358-
let outbound_payments = OutboundPayments::new();
2409+
let outbound_payments = OutboundPayments::new(new_hash_map());
23592410
let payment_id = PaymentId([0; 32]);
23602411
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
23612412

@@ -2372,7 +2423,7 @@ mod tests {
23722423
assert!(
23732424
outbound_payments.add_new_awaiting_invoice(
23742425
payment_id, expiration, Retry::Attempts(0),
2375-
Some(invoice.amount_msats() / 100 + 50_000)
2426+
Some(invoice.amount_msats() / 100 + 50_000), None,
23762427
).is_ok()
23772428
);
23782429
assert!(outbound_payments.has_pending_payments());
@@ -2416,7 +2467,7 @@ mod tests {
24162467
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
24172468

24182469
let pending_events = Mutex::new(VecDeque::new());
2419-
let outbound_payments = OutboundPayments::new();
2470+
let outbound_payments = OutboundPayments::new(new_hash_map());
24202471
let payment_id = PaymentId([0; 32]);
24212472
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
24222473

@@ -2472,7 +2523,7 @@ mod tests {
24722523

24732524
assert!(
24742525
outbound_payments.add_new_awaiting_invoice(
2475-
payment_id, expiration, Retry::Attempts(0), Some(1234)
2526+
payment_id, expiration, Retry::Attempts(0), Some(1234), None,
24762527
).is_ok()
24772528
);
24782529
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)