rust-lightning
rust-lightning copied to clipboard
Introduce Retry InvoiceRequest Flow
resolves #2836
Description:
- Add functionality to handle retrying the sending of
invoice_requestmessages on new reply_paths that are still awaiting invoices.
Changes:
- Introduced invoice_request as an optional field in the
PendingOutboundPayments::AwaitingInvoicevariant to accommodate instances without invoice requests. - Refactored logic from
pay_for_offerto create invoice request messages into a separate function for reuse with retry message flow. - Implemented the
retry_tick_occurredfunction in ChannelManager to handle generating invoice request messages for AwaitingInvoice payments and enqueueing them. - Added
retry_tick_occurredtoln_background_processorwith a timer duration of 5 seconds for timely retries without overwhelming the system with too many onion messages.
Updated from pr3010.01 to pr3010.02 (diff):
Changes:
- Introduce
Readable impl for InvoiceRequest - Restructure commits.
Updated from pr3010.02 to pr3010.03 (diff):
Updates:
- Rebase on main, to resolve merge conflicts.
- Fix ci
Codecov Report
Attention: Patch coverage is 95.09202% with 8 lines in your changes missing coverage. Please review.
Project coverage is 89.64%. Comparing base (
db905e8) to head (b1cd887). Report is 5 commits behind head on main.
Additional details and impacted files
@@ Coverage Diff @@
## main #3010 +/- ##
==========================================
- Coverage 89.64% 89.64% -0.01%
==========================================
Files 126 126
Lines 102251 102383 +132
Branches 102251 102383 +132
==========================================
+ Hits 91666 91781 +115
- Misses 7863 7874 +11
- Partials 2722 2728 +6
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
Updated from pr3010.04 to pr3010.05 (diff):
Addressed @TheBlueMatt comments
Updates:
- Removed the secondary timer in ln_background_processor.
- Moved the
retry_tick_occurredto a private function, and renamed it toretry_invoice_request_messages. - Decrease the
FRESHNESS_TIMERduration, while introducing a call counter withintimer_tick_occurred. This is done so thatretry_invoice_request_messagescan be called faster than the rest of the code without introducing a secondary timer. - Update the test appropriately.
Updated from pr3010.05 to pr3010.06 (diff):
Update:
- Rebase on main.
- To keep the code consistent with the main, now the
create_invoice_reqeust_message, andretry_invoice_request_messagesfunctions returnResult<(), Bolt12SemanticError>
Updated from pr3010.06 to pr3010.07 (diff):
Update:
- Replaced using
unsafewith using internalMutexvariable for tracking call counts.
I don't buy that we need a new counter. There's a lot of things that hang on the timer rate, but this is a great opportunity to redefine the constants in seconds and multiply out a rate. Looking at all the things we do in the timer tick, as far as I can tell the only one that doesn't have a counter and a limit constant already is timer_check_closing_negotiation_process, and it should be easy to add there.
Hi Matt! Thanks a lot for the suggestion!
I think this will definitely be a more maintainable way to handle this change than introducing a counter! Worked out your suggestion in a separate PR since they were quite different from this one: #3065 Let me know if the approach seems sound!
Thanks a lot!
Updated from pr3010.08 to pr3010.09 (diff): Addressed @TheBlueMatt's comment
Update:
- Rebase on main
- Introduced a new approach for retrying invoice request messages.
Details:
- This approach triggers
InvoiceRequestretries when anyChannelMessageHandler's handler is invoked by any message from any peer. - It ensures that if an
InvoiceRequestmessage fails to send on the first attempt due to network disconnection, it will retry as soon as we are back online and receive messages from our peer. - It guarantees that we only retry once for a given
PendingOutboundPayment. - This approach allows retries independent of a sub-minute timer.
Updated from pr3010.09 to pr3010.10 (diff): Addressed @TheBlueMatt comments
Changes:
- Update function name from
retry_invoice_requests->handle_message_received, so that it's more explicit about its calling behavior than specific functionality. - Move the function call to
do_handle_message_without_peer_lockso that it can be triggered when any message from any peer for any handler is received. - Updated the code to generate
awaiting_invoice_flagfrom pending_outbound_payments instead of serialising it.
@TheBlueMatt A gentle ping! :)
Updated from pr3010.11 to pr3010.12 (diff): Addressed @TheBlueMatt comments
Changes:
- Updated the
handle_message_receiveddocumentation to be more generic, removing references to BOLT12. - Removed the return
Resulttype forhandle_message_receivedas the result was unused in the codebase. - Changed the TLV number of
invoice_requestto odd so that downgrade support is maintained.
Updated from pr3010.12 to pr3010.13 (diff): Addressed @TheBlueMatt comments
Updates:
- Change the memory ordering from
SeqCsttoAcquireandRelease. - Moved the storing of
awaiting_invoice_flagfrompay_for_offertoadd_new_awaiting_invoice.
Updated from pr3010.13 to pr3010.14 (diff):
Updates:
- Rebase on main, to fix ci.
- Update
get_invoice_request_awaiting_invoiceto return vector of (payment_id, invoice_request). This is done so that channelmanager'shandle_message_receivedcan recreate the message context using the payment_id when retrying the invoice_request message.
Updated from pr3010.14 to pr3010.15 (diff): Addressed @jkczyz comments
Updates:
- Refactor the Outbound Payment Constructor to make the lock, and flag private members.
- Moved the create_invoice_request_message below pay_for_offer for more logical ordering.
- Introduce a log message in handle_message_received, and replace
returnwithcontinueto not quit the function on the first failure. - Update the test. Introduce the
with_retryas a separate test instead of modifying the current one.
Updated from pr3010.15 to pr3010.16 (diff): Addressed @jkczyz comments
- Calculate
reply_pathprior to locking thepending_offers_messagemutex inhandle_message_received.
Updated from pr3010.16 to pr3010.17 (diff): Addressed @jkczyz comments
Updates:
- Renames function and variable name to be more apt to their usage.
- Update test to remove improve comment naming and remove redundant checks.
- Used
new_hash_map()instead ofHashMap::new().
Updated from pr3010.17 to pr3010.18 (diff): Addressed @jkczyz comments
Changes:
- Squashed commits together.
- Some code cleanups.
Updated from pr3010.18 to pr3010.19 (diff): Addressed @jkczyz comments
Changes:
With the merge of #3139, the Invoice Request message now needs both the Invoice Request as well as the original context to authenticate the corresponding Invoice. So post-rebase, this leads to some significant changes to this PR.
- Updated the
AwaitingInvoiceto contain both the InvoiceRequest and context. - Update
release_invoice_request_awaiting_invoiceto return the Vec<(OffersContext, InvoiceRequest)>, instead of Vec<(PaymentId, InvoiceRequest)>.
Range Diff
git range-diff 6035c83a1d797133b723a084abe6a55bfb4b9f62..0f1c4920e157bf4c67ac8f02b32e25a8ee5f855f a76ec06d23801191f12688245584e12057bd3b10..45ecb2a46e4b982877a8eb791ffb6bb768544c3e
1: 2da46cb0 ! 1: 115fab41 Add InvoiceRequest field and awaiting_invoice_flag
@@ Metadata
Author: shaavan <[email protected]>
## Commit message ##
- Add InvoiceRequest field and awaiting_invoice_flag
+ Add InvoiceRequest and Context Fields in AwaitingInvoice
- - Introduce InvoiceRequest as a field in AwaitingInvoice.
- - Use this field to recreate InvoiceRequest messages for
- retrying payments still awaiting an invoice.
- - Introduce awaiting_invoice_flag to track if there are any
- PendingOutboundPayments::AwaitingInvoice with the corresponding
- InvoiceRequest.
- - This flag helps determine the state of pending_outbound_payments
- without explicitly locking its mutex.
+ - 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.
## lightning/src/ln/channelmanager.rs ##
@@ lightning/src/ln/channelmanager.rs: where
@@ lightning/src/ln/channelmanager.rs: where
forward_htlcs: Mutex::new(new_hash_map()),
decode_update_add_htlcs: Mutex::new(new_hash_map()),
claimable_payments: Mutex::new(ClaimablePayments { claimable_payments: new_hash_map(), pending_claiming_payments: new_hash_map() }),
+@@ lightning/src/ln/channelmanager.rs: macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
+
+ let nonce = Nonce::from_entropy_source(entropy);
+ let context = OffersContext::OutboundPayment { payment_id, nonce };
+- let path = $self.create_blinded_paths_using_absolute_expiry(context, Some(absolute_expiry))
++ let path = $self.create_blinded_paths_using_absolute_expiry(context.clone(), Some(absolute_expiry))
+ .and_then(|paths| paths.into_iter().next().ok_or(()))
+ .map_err(|_| Bolt12SemanticError::MissingPaths)?;
+
@@ lightning/src/ln/channelmanager.rs: macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
let expiration = StaleExpiration::AbsoluteTimeout(absolute_expiry);
$self.pending_outbound_payments
.add_new_awaiting_invoice(
- payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
-+ payment_id, expiration, retry_strategy, max_total_routing_fee_msat, None
++ payment_id, expiration, retry_strategy, max_total_routing_fee_msat, None, context
)
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
+@@ lightning/src/ln/channelmanager.rs: where
+ let invoice_request = builder.build_and_sign()?;
+
+ let context = OffersContext::OutboundPayment { payment_id, nonce };
+- let reply_paths = self.create_blinded_paths(context)
++ let reply_paths = self.create_blinded_paths(context.clone())
+ .map_err(|_| Bolt12SemanticError::MissingPaths)?;
+
+ let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
@@ lightning/src/ln/channelmanager.rs: where
let expiration = StaleExpiration::TimerTicks(1);
self.pending_outbound_payments
.add_new_awaiting_invoice(
- payment_id, expiration, retry_strategy, max_total_routing_fee_msat
+ payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
-+ Some(invoice_request.clone())
++ Some(invoice_request.clone()), context,
)
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
@@ lightning/src/ln/channelmanager.rs: where
// each `ChannelMonitorUpdate` in `in_flight_monitor_updates`. After doing so, we have to
## lightning/src/ln/outbound_payment.rs ##
+@@ lightning/src/ln/outbound_payment.rs: use bitcoin::hashes::Hash;
+ use bitcoin::hashes::sha256::Hash as Sha256;
+ use bitcoin::secp256k1::{self, Secp256k1, SecretKey};
+
++use crate::blinded_path::message::OffersContext;
+ use crate::blinded_path::{IntroductionNode, NodeIdLookUp};
+ use crate::blinded_path::payment::advance_path_by_one;
+ use crate::events::{self, PaymentFailureReason};
@@ lightning/src/ln/outbound_payment.rs: use crate::ln::channelmanager::{EventCompletionAction, HTLCSource, PaymentId};
use crate::ln::onion_utils;
use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason};
@@ lightning/src/ln/outbound_payment.rs: pub(crate) enum PendingOutboundPayment {
retry_strategy: Retry,
max_total_routing_fee_msat: Option<u64>,
+ invoice_request: Option<InvoiceRequest>,
++ context: OffersContext,
},
InvoiceReceived {
payment_hash: PaymentHash,
@@ lightning/src/ln/outbound_payment.rs: impl OutboundPayments {
pub(super) fn add_new_awaiting_invoice(
&self, payment_id: PaymentId, expiration: StaleExpiration, retry_strategy: Retry,
- max_total_routing_fee_msat: Option<u64>
-+ max_total_routing_fee_msat: Option<u64>, invoice_request: Option<InvoiceRequest>
++ max_total_routing_fee_msat: Option<u64>, invoice_request: Option<InvoiceRequest>,
++ context: OffersContext,
) -> Result<(), ()> {
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
match pending_outbounds.entry(payment_id) {
@@ lightning/src/ln/outbound_payment.rs: impl OutboundPayments {
retry_strategy,
max_total_routing_fee_msat,
+ invoice_request,
++ context,
});
+ self.awaiting_invoice.store(true, Ordering::Release);
@@ lightning/src/ln/outbound_payment.rs: impl OutboundPayments {
self.pending_outbound_payments.lock().unwrap().clear()
}
+
-+ pub fn release_invoice_request_awaiting_invoice(&self) -> Vec<(PaymentId, InvoiceRequest)> {
++ pub fn release_invoice_request_awaiting_invoice(&self) -> Vec<(OffersContext, InvoiceRequest)> {
+ if !self.awaiting_invoice.load(Ordering::Acquire) {
+ return vec![];
+ }
-+
++
+ let mut pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
+ let invoice_requests = pending_outbound_payments.iter_mut()
-+ .filter_map(|(payment_id, payment)| match payment {
-+ PendingOutboundPayment::AwaitingInvoice { invoice_request, .. } => {
-+ invoice_request.take().map(|req| (*payment_id, req))
++ .filter_map(|(_, payment)| match payment {
++ PendingOutboundPayment::AwaitingInvoice { invoice_request, context, ..} => {
++ invoice_request.take().map(|req| (context.clone(), req))
+ }
+ _ => None,
+ })
+ .collect();
-+
++
+ self.awaiting_invoice.store(false, Ordering::Release);
+ invoice_requests
+ }
@@ lightning/src/ln/outbound_payment.rs: impl_writeable_tlv_based_enum_upgradable!(
(2, retry_strategy, required),
(4, max_total_routing_fee_msat, option),
+ (5, invoice_request, option),
++ (6, context, required),
},
(7, InvoiceReceived) => {
(0, payment_hash, required),
@@ lightning/src/ln/outbound_payment.rs: mod tests {
+
+ use core::time::Duration;
+
++ use crate::blinded_path::message::OffersContext;
+ use crate::blinded_path::EmptyNodeIdLookUp;
+ use crate::events::{Event, PathFailure, PaymentFailureReason};
+ use crate::ln::types::PaymentHash;
+@@ lightning/src/ln/outbound_payment.rs: mod tests {
use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters};
use crate::sync::{Arc, Mutex, RwLock};
use crate::util::errors::APIError;
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None
++ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None
++ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None
++ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
).is_err()
);
}
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None
++ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None
++ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None
++ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
).is_err()
);
}
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None
++ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None
++ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@@ lightning/src/ln/outbound_payment.rs: mod tests {
outbound_payments.add_new_awaiting_invoice(
payment_id, expiration, Retry::Attempts(0),
- Some(invoice.amount_msats() / 100 + 50_000)
-+ Some(invoice.amount_msats() / 100 + 50_000), None
++ Some(invoice.amount_msats() / 100 + 50_000), None, OffersContext::Unknown {}
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), Some(1234)
-+ payment_id, expiration, Retry::Attempts(0), Some(1234), None
++ payment_id, expiration, Retry::Attempts(0), Some(1234), None, OffersContext::Unknown {}
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
2: 522b2a6e < -: -------- Add create_invoice_request_messages function
-: -------- > 2: 261268bd Introduce enqueue_invoice_request Function
3: ce851f1c ! 3: dba6d91b Add handle_message_received function in ChannelMessageHandler
@@ Metadata
Author: shaavan <[email protected]>
## Commit message ##
- Add handle_message_received function in ChannelMessageHandler
+ Introduce handle_message_received in ChannelMessageHandler
- - Handle behavior triggered when any message from any peer is received.
- - Manage retry mechanism for InvoiceRequest messages in ChannelManager
- related to PendingOutboundPayments.
- - Retrieve the InvoiceRequest, use a new reply path, and enqueue the
- messages for retry.
- - Ensure retries of InvoiceRequest messages if initial attempts fail
- due to connection loss, retrying when back online and receiving messages
- from peers.
+ - Introduce the `handle_message_received` function to manage the
+ behavior when a message is received from any peer.
+ - This function is used within `ChannelManager` to retry `InvoiceRequest`
+ messages if we haven't received the corresponding invoice yet.
+ - This change makes the offer communication more robust against sudden
+ connection drops where the initial attempt to send the message might
+ have failed.
## lightning-net-tokio/src/lib.rs ##
@@ lightning-net-tokio/src/lib.rs: mod tests {
@@ lightning/src/ln/channelmanager.rs: where
}
+
+ fn handle_message_received(&self) {
-+ let invoice_requests = self.pending_outbound_payments.release_invoice_request_awaiting_invoice();
-+ if invoice_requests.is_empty() {
-+ return;
-+ }
-+
-+ let invoice_requests_reply_paths = invoice_requests
-+ .into_iter()
-+ .filter_map(|(payment_id, invoice_request)| {
-+ let context = OffersContext::OutboundPayment { payment_id };
-+ match self.create_blinded_path(context) {
-+ Ok(reply_path) => Some((invoice_request, reply_path)),
++ for (context, invoice_request) in self
++ .pending_outbound_payments
++ .release_invoice_request_awaiting_invoice()
++ {
++ match self.create_blinded_paths(context) {
++ Ok(reply_paths) => match self.enqueue_invoice_request(invoice_request, reply_paths) {
++ Ok(_) => {}
+ Err(_) => {
-+ log_info!(
-+ self.logger,
-+ "Retry failed for invoice request with payment id: {}. \
++ log_info!(self.logger, "Retry failed for an invoice request.");
++ }
++ },
++ Err(_) => {
++ log_info!(self.logger,
++ "Retry failed for an invoice request. \
+ Reason: router could not find a blinded path to include as the reply path",
-+ payment_id
-+ );
-+ None
-+ },
++ );
+ }
-+ });
-+
-+ let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
-+ for (invoice_request, reply_path) in invoice_requests_reply_paths {
-+ if let Ok(messages) = self.create_invoice_request_messages(
-+ invoice_request, reply_path
-+ ) {
-+ pending_offers_messages.extend(messages);
+ }
+ }
+ }
4: 0f1c4920 ! 4: 45ecb2a4 Introduce handle_message_received test
@@ lightning/src/ln/offers_tests.rs: fn creates_and_pays_for_offer_using_one_hop_bl
+ None => {},
+ };
+
-+ let invoice = extract_invoice(bob, &onion_message);
++ let (invoice, _) = extract_invoice(bob, &onion_message);
+ assert_eq!(invoice.amount_msats(), 10_000_000);
+ assert_ne!(invoice.signing_pubkey(), alice_id);
+ assert!(!invoice.payment_paths().is_empty());
Updated from pr3010.19 to pr3010.20 (diff): Addressed @jkczyz comments
Changes:
- Include
Nonceas the field inAwaitingInvoiceinstead ofContext, sinceNonceis sufficient for context recreation, and safeguards against potentially passing the wrong context toAwaitingInvoice.
Updated from pr3010.20 to pr3010.21 (diff): Addressed @jkczyz comments
Changes:
- Removed redundant clone.
- Make nonce and Option, instead of required to support downgrading.
Updated from pr3010.21 to pr3010.22 (diff): Addressed @jkczyz comments
Changes:
- Fixed tlv number of nonce, to follow the "it's okay to be odd" rule.
- Fixed release pending onion message to avoid unnecessary panic.
Updated from pr3010.22 to pr3010.23 (diff):
Changes:
- Rebase on main to resolve merge conflicts.
- Squash fixup commit
- No changes to the approach in this version.
Range-Diff:
1: 115fab41 ! 1: 8abd02fd Add InvoiceRequest and Context Fields in AwaitingInvoice
@@ Metadata
Author: shaavan <[email protected]>
## Commit message ##
- Add InvoiceRequest and Context Fields in AwaitingInvoice
+ Add InvoiceRequest and Nonce Fields in AwaitingInvoice
- - Introduced `InvoiceRequest` and `context` fields in the `AwaitingInvoice`
+ - Introduced `InvoiceRequest` and `nonce` 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
@@ lightning/src/ln/channelmanager.rs: where
@@ lightning/src/ln/channelmanager.rs: macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
let nonce = Nonce::from_entropy_source(entropy);
- let context = OffersContext::OutboundPayment { payment_id, nonce };
+ let context = OffersContext::OutboundPayment { payment_id, nonce, hmac: None };
- let path = $self.create_blinded_paths_using_absolute_expiry(context, Some(absolute_expiry))
+ let path = $self.create_blinded_paths_using_absolute_expiry(context.clone(), Some(absolute_expiry))
.and_then(|paths| paths.into_iter().next().ok_or(()))
@@ lightning/src/ln/channelmanager.rs: macro_rules! create_refund_builder { ($self:
$self.pending_outbound_payments
.add_new_awaiting_invoice(
- payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
-+ payment_id, expiration, retry_strategy, max_total_routing_fee_msat, None, context
++ payment_id, expiration, retry_strategy, max_total_routing_fee_msat, None, nonce
)
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
-@@ lightning/src/ln/channelmanager.rs: where
- let invoice_request = builder.build_and_sign()?;
-
- let context = OffersContext::OutboundPayment { payment_id, nonce };
-- let reply_paths = self.create_blinded_paths(context)
-+ let reply_paths = self.create_blinded_paths(context.clone())
- .map_err(|_| Bolt12SemanticError::MissingPaths)?;
-
- let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
@@ lightning/src/ln/channelmanager.rs: where
let expiration = StaleExpiration::TimerTicks(1);
self.pending_outbound_payments
.add_new_awaiting_invoice(
- payment_id, expiration, retry_strategy, max_total_routing_fee_msat
+ payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
-+ Some(invoice_request.clone()), context,
++ Some(invoice_request.clone()), nonce,
)
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
@@ lightning/src/ln/outbound_payment.rs: use bitcoin::hashes::Hash;
+use crate::blinded_path::message::OffersContext;
use crate::blinded_path::{IntroductionNode, NodeIdLookUp};
- use crate::blinded_path::payment::advance_path_by_one;
use crate::events::{self, PaymentFailureReason};
-@@ lightning/src/ln/outbound_payment.rs: use crate::ln::channelmanager::{EventCompletionAction, HTLCSource, PaymentId};
+ use crate::ln::types::{PaymentHash, PaymentPreimage, PaymentSecret};
+@@ lightning/src/ln/outbound_payment.rs: use crate::ln::features::Bolt12InvoiceFeatures;
use crate::ln::onion_utils;
use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason};
use crate::offers::invoice::Bolt12Invoice;
+use crate::offers::invoice_request::InvoiceRequest;
++use crate::offers::nonce::Nonce;
use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router};
use crate::sign::{EntropySource, NodeSigner, Recipient};
use crate::util::errors::APIError;
@@ lightning/src/ln/outbound_payment.rs: pub(crate) enum PendingOutboundPayment {
retry_strategy: Retry,
max_total_routing_fee_msat: Option<u64>,
+ invoice_request: Option<InvoiceRequest>,
-+ context: OffersContext,
++ nonce: Option<Nonce>,
},
InvoiceReceived {
payment_hash: PaymentHash,
@@ lightning/src/ln/outbound_payment.rs: impl OutboundPayments {
&self, payment_id: PaymentId, expiration: StaleExpiration, retry_strategy: Retry,
- max_total_routing_fee_msat: Option<u64>
+ max_total_routing_fee_msat: Option<u64>, invoice_request: Option<InvoiceRequest>,
-+ context: OffersContext,
++ nonce: Nonce,
) -> Result<(), ()> {
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
match pending_outbounds.entry(payment_id) {
@@ lightning/src/ln/outbound_payment.rs: impl OutboundPayments {
retry_strategy,
max_total_routing_fee_msat,
+ invoice_request,
-+ context,
++ nonce: Some(nonce),
});
+ self.awaiting_invoice.store(true, Ordering::Release);
@@ lightning/src/ln/outbound_payment.rs: impl OutboundPayments {
+ }
+
+ let mut pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
-+ let invoice_requests = pending_outbound_payments.iter_mut()
-+ .filter_map(|(_, payment)| match payment {
-+ PendingOutboundPayment::AwaitingInvoice { invoice_request, context, ..} => {
-+ invoice_request.take().map(|req| (context.clone(), req))
++ let invoice_requests = pending_outbound_payments
++ .iter_mut()
++ .filter_map(|(payment_id, payment)| {
++ if let PendingOutboundPayment::AwaitingInvoice {
++ invoice_request, nonce: Some(nonce), ..
++ } = payment {
++ let context = OffersContext::OutboundPayment {
++ payment_id: *payment_id,
++ nonce: *nonce,
++ // TODO: hmac to be incorporated.
++ hmac: None,
++ };
++ invoice_request.take().map(|req| (context, req))
++ } else {
++ None
+ }
-+ _ => None,
+ })
+ .collect();
+
@@ lightning/src/ln/outbound_payment.rs: impl_writeable_tlv_based_enum_upgradable!(
(2, retry_strategy, required),
(4, max_total_routing_fee_msat, option),
+ (5, invoice_request, option),
-+ (6, context, required),
++ (7, nonce, option),
},
(7, InvoiceReceived) => {
(0, payment_hash, required),
@@ lightning/src/ln/outbound_payment.rs: mod tests {
-
- use core::time::Duration;
-
-+ use crate::blinded_path::message::OffersContext;
- use crate::blinded_path::EmptyNodeIdLookUp;
- use crate::events::{Event, PathFailure, PaymentFailureReason};
- use crate::ln::types::PaymentHash;
-@@ lightning/src/ln/outbound_payment.rs: mod tests {
+ use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, Retry, RetryableSendFailure, StaleExpiration};
+ #[cfg(feature = "std")]
+ use crate::offers::invoice::DEFAULT_RELATIVE_EXPIRY;
++ use crate::offers::nonce::Nonce;
+ use crate::offers::offer::OfferBuilder;
+ use crate::offers::test_utils::*;
+ use crate::routing::gossip::NetworkGraph;
use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters};
use crate::sync::{Arc, Mutex, RwLock};
use crate::util::errors::APIError;
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
++ payment_id, expiration, Retry::Attempts(0), None, None, Nonce::try_from(&[0u8; 16][..]).unwrap()
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
++ payment_id, expiration, Retry::Attempts(0), None, None, Nonce::try_from(&[0u8; 16][..]).unwrap()
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
++ payment_id, expiration, Retry::Attempts(0), None, None, Nonce::try_from(&[0u8; 16][..]).unwrap()
).is_err()
);
}
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
++ payment_id, expiration, Retry::Attempts(0), None, None, Nonce::try_from(&[0u8; 16][..]).unwrap()
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
++ payment_id, expiration, Retry::Attempts(0), None, None, Nonce::try_from(&[0u8; 16][..]).unwrap()
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
++ payment_id, expiration, Retry::Attempts(0), None, None, Nonce::try_from(&[0u8; 16][..]).unwrap()
).is_err()
);
}
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
++ payment_id, expiration, Retry::Attempts(0), None, None, Nonce::try_from(&[0u8; 16][..]).unwrap()
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), None
-+ payment_id, expiration, Retry::Attempts(0), None, None, OffersContext::Unknown {}
++ payment_id, expiration, Retry::Attempts(0), None, None, Nonce::try_from(&[0u8; 16][..]).unwrap()
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@@ lightning/src/ln/outbound_payment.rs: mod tests {
outbound_payments.add_new_awaiting_invoice(
payment_id, expiration, Retry::Attempts(0),
- Some(invoice.amount_msats() / 100 + 50_000)
-+ Some(invoice.amount_msats() / 100 + 50_000), None, OffersContext::Unknown {}
++ Some(invoice.amount_msats() / 100 + 50_000), None, Nonce::try_from(&[0u8; 16][..]).unwrap()
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@@ lightning/src/ln/outbound_payment.rs: mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0), Some(1234)
-+ payment_id, expiration, Retry::Attempts(0), Some(1234), None, OffersContext::Unknown {}
++ payment_id, expiration, Retry::Attempts(0), Some(1234), None, Nonce::try_from(&[0u8; 16][..]).unwrap()
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
2: bf1d03b9 < -: -------- f: Include Nonce in AwaitingInvoice for context recreation
3: 9b1e056c ! 2: 1cc365ce Introduce enqueue_invoice_request Function
@@ Commit message
## lightning/src/ln/channelmanager.rs ##
@@ lightning/src/ln/channelmanager.rs: use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutb
use crate::ln::wire::Encode;
- use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice};
+ use crate::offers::invoice::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice};
use crate::offers::invoice_error::InvoiceError;
-use crate::offers::invoice_request::{DerivedPayerId, InvoiceRequestBuilder};
+use crate::offers::invoice_request::{DerivedPayerId, InvoiceRequest, InvoiceRequestBuilder};
@@ lightning/src/ln/channelmanager.rs: where
+ fn enqueue_invoice_request(
+ &self,
+ invoice_request: InvoiceRequest,
-+ reply_paths: Vec<BlindedPath>,
++ reply_paths: Vec<BlindedMessagePath>,
+ ) -> Result<(), Bolt12SemanticError> {
let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
- if !offer.paths().is_empty() {
4: e9fbabc9 = 3: 8a17319a Introduce handle_message_received in ChannelMessageHandler
5: 656dd95d ! 4: 6a9e9134 Introduce handle_message_received test
@@ lightning/src/ln/offers_tests.rs: fn creates_and_pays_for_offer_using_one_hop_bl
+ assert_ne!(offer.signing_pubkey(), Some(alice_id));
+ assert!(!offer.paths().is_empty());
+ for path in offer.paths() {
-+ assert_eq!(path.introduction_node, IntroductionNode::NodeId(alice_id));
++ assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(alice_id));
+ }
+ let payment_id = PaymentId([1; 32]);
+ bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None).unwrap();
@@ lightning/src/ln/offers_tests.rs: fn creates_and_pays_for_offer_using_one_hop_bl
+ });
+ assert_eq!(invoice_request.amount_msats(), None);
+ assert_ne!(invoice_request.payer_id(), bob_id);
-+ assert_eq!(reply_path.introduction_node, IntroductionNode::NodeId(bob_id));
++ assert_eq!(reply_path.introduction_node(), &IntroductionNode::NodeId(bob_id));
+ let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
+ bob.onion_messenger.handle_onion_message(&alice_id, &onion_message);
+
@@ lightning/src/ln/offers_tests.rs: fn creates_and_pays_for_offer_using_one_hop_bl
+ assert_eq!(invoice.amount_msats(), 10_000_000);
+ assert_ne!(invoice.signing_pubkey(), alice_id);
+ assert!(!invoice.payment_paths().is_empty());
-+ for (_, path) in invoice.payment_paths() {
-+ assert_eq!(path.introduction_node, IntroductionNode::NodeId(alice_id));
++ for path in invoice.payment_paths() {
++ assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(alice_id));
+ }
+ route_bolt12_payment(bob, &[alice], &invoice);
+ expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
Updated from pr3010.23 to pr3010.24 (diff): Addressed @vincenzopalazzo, and @jkczyz comments
Changes:
- Introduce RetryableInvoiceRequest struct, which contains all the data required to recreate InvoiceRequest Messages.
- Upgrade logging level from
infotowarnfor invoice request retry failure case, and improve logging.
Updated from pr3010.24 to pr3010.25 (diff): Addressed @jkczyz comments
Changes:
- Remove a redundant
clone. - Rename
handle_message_received->message_received, and update docs to align with the function's purpose. - Move the
message_receivedcall tohandle_messageso that it is called before the "message handling" for any kind of message received. - Simplify a check in test.