Skip to content

Commit 115fab4

Browse files
committed
Add InvoiceRequest and Context Fields in AwaitingInvoice
- Introduced `InvoiceRequest` and `context` fields in the `AwaitingInvoice` to enable recreation of the `InvoiceRequest` message for retries if the corresponding invoice is not received in time. - Added `awaiting_invoice` flag to track pending outbound payments with invoice requests without locking the `pending_outbound_payment` mutex.
1 parent a76ec06 commit 115fab4

File tree

3 files changed

+75
-32
lines changed

3 files changed

+75
-32
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3049,7 +3049,7 @@ where
30493049

30503050
outbound_scid_aliases: Mutex::new(new_hash_set()),
30513051
pending_inbound_payments: Mutex::new(new_hash_map()),
3052-
pending_outbound_payments: OutboundPayments::new(),
3052+
pending_outbound_payments: OutboundPayments::new(new_hash_map()),
30533053
forward_htlcs: Mutex::new(new_hash_map()),
30543054
decode_update_add_htlcs: Mutex::new(new_hash_map()),
30553055
claimable_payments: Mutex::new(ClaimablePayments { claimable_payments: new_hash_map(), pending_claiming_payments: new_hash_map() }),
@@ -8882,7 +8882,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
88828882

88838883
let nonce = Nonce::from_entropy_source(entropy);
88848884
let context = OffersContext::OutboundPayment { payment_id, nonce };
8885-
let path = $self.create_blinded_paths_using_absolute_expiry(context, Some(absolute_expiry))
8885+
let path = $self.create_blinded_paths_using_absolute_expiry(context.clone(), Some(absolute_expiry))
88868886
.and_then(|paths| paths.into_iter().next().ok_or(()))
88878887
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
88888888

@@ -8898,7 +8898,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
88988898
let expiration = StaleExpiration::AbsoluteTimeout(absolute_expiry);
88998899
$self.pending_outbound_payments
89008900
.add_new_awaiting_invoice(
8901-
payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
8901+
payment_id, expiration, retry_strategy, max_total_routing_fee_msat, None, context
89028902
)
89038903
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
89048904

@@ -9017,15 +9017,16 @@ where
90179017
let invoice_request = builder.build_and_sign()?;
90189018

90199019
let context = OffersContext::OutboundPayment { payment_id, nonce };
9020-
let reply_paths = self.create_blinded_paths(context)
9020+
let reply_paths = self.create_blinded_paths(context.clone())
90219021
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
90229022

90239023
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
90249024

90259025
let expiration = StaleExpiration::TimerTicks(1);
90269026
self.pending_outbound_payments
90279027
.add_new_awaiting_invoice(
9028-
payment_id, expiration, retry_strategy, max_total_routing_fee_msat
9028+
payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
9029+
Some(invoice_request.clone()), context,
90299030
)
90309031
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
90319032

@@ -12090,10 +12091,7 @@ where
1209012091
}
1209112092
pending_outbound_payments = Some(outbounds);
1209212093
}
12093-
let pending_outbounds = OutboundPayments {
12094-
pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()),
12095-
retry_lock: Mutex::new(())
12096-
};
12094+
let pending_outbounds = OutboundPayments::new(pending_outbound_payments.unwrap());
1209712095

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

lightning/src/ln/outbound_payment.rs

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use bitcoin::hashes::Hash;
1313
use bitcoin::hashes::sha256::Hash as Sha256;
1414
use bitcoin::secp256k1::{self, Secp256k1, SecretKey};
1515

16+
use crate::blinded_path::message::OffersContext;
1617
use crate::blinded_path::{IntroductionNode, NodeIdLookUp};
1718
use crate::blinded_path::payment::advance_path_by_one;
1819
use crate::events::{self, PaymentFailureReason};
@@ -22,6 +23,7 @@ use crate::ln::channelmanager::{EventCompletionAction, HTLCSource, PaymentId};
2223
use crate::ln::onion_utils;
2324
use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason};
2425
use crate::offers::invoice::Bolt12Invoice;
26+
use crate::offers::invoice_request::InvoiceRequest;
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,8 @@ pub(crate) enum PendingOutboundPayment {
5457
expiration: StaleExpiration,
5558
retry_strategy: Retry,
5659
max_total_routing_fee_msat: Option<u64>,
60+
invoice_request: Option<InvoiceRequest>,
61+
context: OffersContext,
5762
},
5863
InvoiceReceived {
5964
payment_hash: PaymentHash,
@@ -684,13 +689,19 @@ pub(super) struct SendAlongPathArgs<'a> {
684689

685690
pub(super) struct OutboundPayments {
686691
pub(super) pending_outbound_payments: Mutex<HashMap<PaymentId, PendingOutboundPayment>>,
687-
pub(super) retry_lock: Mutex<()>,
692+
awaiting_invoice: AtomicBool,
693+
retry_lock: Mutex<()>,
688694
}
689695

690696
impl OutboundPayments {
691-
pub(super) fn new() -> Self {
697+
pub(super) fn new(pending_outbound_payments: HashMap<PaymentId, PendingOutboundPayment>) -> Self {
698+
let has_invoice_requests = pending_outbound_payments.values().any(|payment| {
699+
matches!(payment, PendingOutboundPayment::AwaitingInvoice { invoice_request: Some(_), .. })
700+
});
701+
692702
Self {
693-
pending_outbound_payments: Mutex::new(new_hash_map()),
703+
pending_outbound_payments: Mutex::new(pending_outbound_payments),
704+
awaiting_invoice: AtomicBool::new(has_invoice_requests),
694705
retry_lock: Mutex::new(()),
695706
}
696707
}
@@ -1351,7 +1362,8 @@ impl OutboundPayments {
13511362

13521363
pub(super) fn add_new_awaiting_invoice(
13531364
&self, payment_id: PaymentId, expiration: StaleExpiration, retry_strategy: Retry,
1354-
max_total_routing_fee_msat: Option<u64>
1365+
max_total_routing_fee_msat: Option<u64>, invoice_request: Option<InvoiceRequest>,
1366+
context: OffersContext,
13551367
) -> Result<(), ()> {
13561368
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
13571369
match pending_outbounds.entry(payment_id) {
@@ -1361,7 +1373,10 @@ impl OutboundPayments {
13611373
expiration,
13621374
retry_strategy,
13631375
max_total_routing_fee_msat,
1376+
invoice_request,
1377+
context,
13641378
});
1379+
self.awaiting_invoice.store(true, Ordering::Release);
13651380

13661381
Ok(())
13671382
},
@@ -1827,6 +1842,25 @@ impl OutboundPayments {
18271842
pub fn clear_pending_payments(&self) {
18281843
self.pending_outbound_payments.lock().unwrap().clear()
18291844
}
1845+
1846+
pub fn release_invoice_request_awaiting_invoice(&self) -> Vec<(OffersContext, InvoiceRequest)> {
1847+
if !self.awaiting_invoice.load(Ordering::Acquire) {
1848+
return vec![];
1849+
}
1850+
1851+
let mut pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
1852+
let invoice_requests = pending_outbound_payments.iter_mut()
1853+
.filter_map(|(_, payment)| match payment {
1854+
PendingOutboundPayment::AwaitingInvoice { invoice_request, context, ..} => {
1855+
invoice_request.take().map(|req| (context.clone(), req))
1856+
}
1857+
_ => None,
1858+
})
1859+
.collect();
1860+
1861+
self.awaiting_invoice.store(false, Ordering::Release);
1862+
invoice_requests
1863+
}
18301864
}
18311865

18321866
/// Returns whether a payment with the given [`PaymentHash`] and [`PaymentId`] is, in fact, a
@@ -1882,6 +1916,8 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
18821916
(0, expiration, required),
18831917
(2, retry_strategy, required),
18841918
(4, max_total_routing_fee_msat, option),
1919+
(5, invoice_request, option),
1920+
(6, context, required),
18851921
},
18861922
(7, InvoiceReceived) => {
18871923
(0, payment_hash, required),
@@ -1897,6 +1933,7 @@ mod tests {
18971933

18981934
use core::time::Duration;
18991935

1936+
use crate::blinded_path::message::OffersContext;
19001937
use crate::blinded_path::EmptyNodeIdLookUp;
19011938
use crate::events::{Event, PathFailure, PaymentFailureReason};
19021939
use crate::ln::types::PaymentHash;
@@ -1912,6 +1949,7 @@ mod tests {
19121949
use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters};
19131950
use crate::sync::{Arc, Mutex, RwLock};
19141951
use crate::util::errors::APIError;
1952+
use crate::util::hash_tables::new_hash_map;
19151953
use crate::util::test_utils;
19161954

19171955
use alloc::collections::VecDeque;
@@ -1946,7 +1984,7 @@ mod tests {
19461984
}
19471985
#[cfg(feature = "std")]
19481986
fn do_fails_paying_after_expiration(on_retry: bool) {
1949-
let outbound_payments = OutboundPayments::new();
1987+
let outbound_payments = OutboundPayments::new(new_hash_map());
19501988
let logger = test_utils::TestLogger::new();
19511989
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
19521990
let scorer = RwLock::new(test_utils::TestScorer::new());
@@ -1990,7 +2028,7 @@ mod tests {
19902028
do_find_route_error(true);
19912029
}
19922030
fn do_find_route_error(on_retry: bool) {
1993-
let outbound_payments = OutboundPayments::new();
2031+
let outbound_payments = OutboundPayments::new(new_hash_map());
19942032
let logger = test_utils::TestLogger::new();
19952033
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
19962034
let scorer = RwLock::new(test_utils::TestScorer::new());
@@ -2029,7 +2067,7 @@ mod tests {
20292067

20302068
#[test]
20312069
fn initial_send_payment_path_failed_evs() {
2032-
let outbound_payments = OutboundPayments::new();
2070+
let outbound_payments = OutboundPayments::new(new_hash_map());
20332071
let logger = test_utils::TestLogger::new();
20342072
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
20352073
let scorer = RwLock::new(test_utils::TestScorer::new());
@@ -2111,7 +2149,7 @@ mod tests {
21112149
#[test]
21122150
fn removes_stale_awaiting_invoice_using_absolute_timeout() {
21132151
let pending_events = Mutex::new(VecDeque::new());
2114-
let outbound_payments = OutboundPayments::new();
2152+
let outbound_payments = OutboundPayments::new(new_hash_map());
21152153
let payment_id = PaymentId([0; 32]);
21162154
let absolute_expiry = 100;
21172155
let tick_interval = 10;
@@ -2120,7 +2158,7 @@ mod tests {
21202158
assert!(!outbound_payments.has_pending_payments());
21212159
assert!(
21222160
outbound_payments.add_new_awaiting_invoice(
2123-
payment_id, expiration, Retry::Attempts(0), None
2161+
payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
21242162
).is_ok()
21252163
);
21262164
assert!(outbound_payments.has_pending_payments());
@@ -2146,30 +2184,30 @@ mod tests {
21462184

21472185
assert!(
21482186
outbound_payments.add_new_awaiting_invoice(
2149-
payment_id, expiration, Retry::Attempts(0), None
2187+
payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
21502188
).is_ok()
21512189
);
21522190
assert!(outbound_payments.has_pending_payments());
21532191

21542192
assert!(
21552193
outbound_payments.add_new_awaiting_invoice(
2156-
payment_id, expiration, Retry::Attempts(0), None
2194+
payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
21572195
).is_err()
21582196
);
21592197
}
21602198

21612199
#[test]
21622200
fn removes_stale_awaiting_invoice_using_timer_ticks() {
21632201
let pending_events = Mutex::new(VecDeque::new());
2164-
let outbound_payments = OutboundPayments::new();
2202+
let outbound_payments = OutboundPayments::new(new_hash_map());
21652203
let payment_id = PaymentId([0; 32]);
21662204
let timer_ticks = 3;
21672205
let expiration = StaleExpiration::TimerTicks(timer_ticks);
21682206

21692207
assert!(!outbound_payments.has_pending_payments());
21702208
assert!(
21712209
outbound_payments.add_new_awaiting_invoice(
2172-
payment_id, expiration, Retry::Attempts(0), None
2210+
payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
21732211
).is_ok()
21742212
);
21752213
assert!(outbound_payments.has_pending_payments());
@@ -2195,29 +2233,29 @@ mod tests {
21952233

21962234
assert!(
21972235
outbound_payments.add_new_awaiting_invoice(
2198-
payment_id, expiration, Retry::Attempts(0), None
2236+
payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
21992237
).is_ok()
22002238
);
22012239
assert!(outbound_payments.has_pending_payments());
22022240

22032241
assert!(
22042242
outbound_payments.add_new_awaiting_invoice(
2205-
payment_id, expiration, Retry::Attempts(0), None
2243+
payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
22062244
).is_err()
22072245
);
22082246
}
22092247

22102248
#[test]
22112249
fn removes_abandoned_awaiting_invoice() {
22122250
let pending_events = Mutex::new(VecDeque::new());
2213-
let outbound_payments = OutboundPayments::new();
2251+
let outbound_payments = OutboundPayments::new(new_hash_map());
22142252
let payment_id = PaymentId([0; 32]);
22152253
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
22162254

22172255
assert!(!outbound_payments.has_pending_payments());
22182256
assert!(
22192257
outbound_payments.add_new_awaiting_invoice(
2220-
payment_id, expiration, Retry::Attempts(0), None
2258+
payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
22212259
).is_ok()
22222260
);
22232261
assert!(outbound_payments.has_pending_payments());
@@ -2245,13 +2283,13 @@ mod tests {
22452283
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
22462284

22472285
let pending_events = Mutex::new(VecDeque::new());
2248-
let outbound_payments = OutboundPayments::new();
2286+
let outbound_payments = OutboundPayments::new(new_hash_map());
22492287
let payment_id = PaymentId([0; 32]);
22502288
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
22512289

22522290
assert!(
22532291
outbound_payments.add_new_awaiting_invoice(
2254-
payment_id, expiration, Retry::Attempts(0), None
2292+
payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
22552293
).is_ok()
22562294
);
22572295
assert!(outbound_payments.has_pending_payments());
@@ -2298,7 +2336,7 @@ mod tests {
22982336
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
22992337

23002338
let pending_events = Mutex::new(VecDeque::new());
2301-
let outbound_payments = OutboundPayments::new();
2339+
let outbound_payments = OutboundPayments::new(new_hash_map());
23022340
let payment_id = PaymentId([0; 32]);
23032341
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
23042342

@@ -2315,7 +2353,7 @@ mod tests {
23152353
assert!(
23162354
outbound_payments.add_new_awaiting_invoice(
23172355
payment_id, expiration, Retry::Attempts(0),
2318-
Some(invoice.amount_msats() / 100 + 50_000)
2356+
Some(invoice.amount_msats() / 100 + 50_000), None, OffersContext::Unknown {}
23192357
).is_ok()
23202358
);
23212359
assert!(outbound_payments.has_pending_payments());
@@ -2359,7 +2397,7 @@ mod tests {
23592397
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
23602398

23612399
let pending_events = Mutex::new(VecDeque::new());
2362-
let outbound_payments = OutboundPayments::new();
2400+
let outbound_payments = OutboundPayments::new(new_hash_map());
23632401
let payment_id = PaymentId([0; 32]);
23642402
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
23652403

@@ -2415,7 +2453,7 @@ mod tests {
24152453

24162454
assert!(
24172455
outbound_payments.add_new_awaiting_invoice(
2418-
payment_id, expiration, Retry::Attempts(0), Some(1234)
2456+
payment_id, expiration, Retry::Attempts(0), Some(1234), None, OffersContext::Unknown {}
24192457
).is_ok()
24202458
);
24212459
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
@@ -1038,6 +1038,13 @@ impl Writeable for InvoiceRequestContents {
10381038
}
10391039
}
10401040

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

0 commit comments

Comments
 (0)