Move reasonable-default arguments out of `pay_for_offer`.
What changed, and why it matters
This commit is a routine API cleanup, not a security fix. It groups several rarely-used arguments of the `pay_for_offer` and `pay_for_offer_from_human_readable_name` functions into a new `OptionalOfferPaymentParams` struct with sensible defaults. Existing callers are updated to use the new, simpler call pattern. There is no change to payment logic, validation, or security behavior.
No security action required. Developers consuming this library should update their `pay_for_offer` calls to the new signature and can rely on `OptionalOfferPaymentParams::default()` for typical use cases.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors ChannelManager::pay_for_offer and ChannelManager::pay_for_offer_from_human_readable_name by moving quantity, payer_note, route_params_config, and retry_strategy into a new OptionalOfferPaymentParams struct that implements Default. The default values match prior common usage (Retry::Timeout(2s) with std, Retry::Attempts(3) without, and RouteParametersConfig::default()). pay_for_offer now automatically supplies quantity = Some(1) when the offer expects a quantity; a new pay_for_offer_with_quantity method is added for explicit quantity selection. All test and DNS resolver callers are updated to the new signature. No cryptographic, routing, or payment-state logic is altered.
Changed components
lightning/src/ln/channelmanager.rslightning/src/ln/async_payments_tests.rslightning/src/ln/offers_tests.rslightning/src/ln/max_payment_path_len_tests.rslightning-dns-resolver/src/lib.rsInspect captured patch +157 / −180
diff --git a/lightning-dns-resolver/src/lib.rs b/lightning-dns-resolver/src/lib.rs
index 3d8e1b3..426f418 100644
--- a/lightning-dns-resolver/src/lib.rs
+++ b/lightning-dns-resolver/src/lib.rs
@@ -164,7 +164,7 @@ mod test {
};
use lightning::blinded_path::NodeIdLookUp;
use lightning::events::{Event, PaymentPurpose};
- use lightning::ln::channelmanager::{PaymentId, Retry};
+ use lightning::ln::channelmanager::{OptionalOfferPaymentParams, PaymentId};
use lightning::ln::functional_test_utils::*;
use lightning::ln::msgs::{
BaseMessageHandler, ChannelMessageHandler, Init, OnionMessageHandler,
@@ -175,7 +175,6 @@ mod test {
use lightning::onion_message::messenger::{
AOnionMessenger, Destination, MessageRouter, OnionMessagePath, OnionMessenger,
};
- use lightning::routing::router::RouteParametersConfig;
use lightning::sign::{KeysManager, NodeSigner, ReceiveAuthKey, Recipient};
use lightning::types::features::InitFeatures;
use lightning::types::payment::PaymentHash;
@@ -380,23 +379,18 @@ mod test {
async fn pay_offer_flow<'a, 'b, 'c>(
nodes: &[Node<'a, 'b, 'c>], resolver_messenger: &impl AOnionMessenger,
resolver_id: PublicKey, payer_id: PublicKey, payee_id: PublicKey, offer: Offer,
- name: HumanReadableName, amt: u64, payment_id: PaymentId, payer_note: Option<String>,
- retry: Retry, params: RouteParametersConfig, resolvers: Vec<Destination>,
+ name: HumanReadableName, payment_id: PaymentId, payer_note: Option<String>,
+ resolvers: Vec<Destination>,
) {
// Override contents to offer provided
let proof_override = &nodes[0].node.testing_dnssec_proof_offer_resolution_override;
proof_override.lock().unwrap().insert(name.clone(), offer);
+ let amt = 42_000;
+ let mut opts = OptionalOfferPaymentParams::default();
+ opts.payer_note = payer_note.clone();
nodes[0]
.node
- .pay_for_offer_from_human_readable_name(
- name,
- amt,
- payment_id,
- payer_note.clone(),
- retry,
- params,
- resolvers,
- )
+ .pay_for_offer_from_human_readable_name(name, amt, payment_id, opts, resolvers)
.unwrap();
let query = nodes[0].onion_messenger.next_onion_message_for_peer(resolver_id).unwrap();
@@ -493,9 +487,6 @@ mod test {
let bs_offer = nodes[1].node.create_offer_builder().unwrap().build().unwrap();
let resolvers = vec![Destination::Node(resolver_id)];
- let retry = Retry::Attempts(0);
- let amt = 42_000;
- let params = RouteParametersConfig::default();
pay_offer_flow(
&nodes,
@@ -505,11 +496,8 @@ mod test {
payee_id,
bs_offer.clone(),
name.clone(),
- amt,
PaymentId([42; 32]),
None,
- retry,
- params,
resolvers.clone(),
)
.await;
@@ -523,11 +511,8 @@ mod test {
payee_id,
bs_offer,
name,
- amt,
PaymentId([21; 32]),
Some("foo".into()),
- retry,
- params,
resolvers,
)
.await;
diff --git a/lightning/src/ln/async_payments_tests.rs b/lightning/src/ln/async_payments_tests.rs
index 1fa1b77..6bdc311 100644
--- a/lightning/src/ln/async_payments_tests.rs
+++ b/lightning/src/ln/async_payments_tests.rs
@@ -15,7 +15,9 @@ use crate::events::{
Event, HTLCHandlingFailureType, PaidBolt12Invoice, PaymentFailureReason, PaymentPurpose,
};
use crate::ln::blinded_payment_tests::{fail_blinded_htlc_backwards, get_blinded_route_parameters};
-use crate::ln::channelmanager::{Bolt12PaymentError, PaymentId, RecipientOnionFields};
+use crate::ln::channelmanager::{
+ Bolt12PaymentError, OptionalOfferPaymentParams, PaymentId, RecipientOnionFields,
+};
use crate::ln::functional_test_utils::*;
use crate::ln::inbound_payment;
use crate::ln::msgs;
@@ -49,7 +51,7 @@ use crate::onion_message::messenger::{
use crate::onion_message::offers::OffersMessage;
use crate::onion_message::packet::ParsedOnionMessageContents;
use crate::prelude::*;
-use crate::routing::router::{Payee, PaymentParameters, RouteParametersConfig};
+use crate::routing::router::{Payee, PaymentParameters};
use crate::sign::NodeSigner;
use crate::sync::Mutex;
use crate::types::features::Bolt12InvoiceFeatures;
@@ -498,11 +500,7 @@ fn static_invoice_unknown_required_features() {
// unknown required features.
let amt_msat = 5000;
let payment_id = PaymentId([1; 32]);
- let params = RouteParametersConfig::default();
- nodes[0]
- .node
- .pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), params)
- .unwrap();
+ nodes[0].node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
// Don't forward the invreq since the invoice was created outside of the normal flow, instead
// manually construct the response.
@@ -574,11 +572,7 @@ fn ignore_unexpected_static_invoice() {
let amt_msat = 5000;
let payment_id = PaymentId([1; 32]);
- let params = RouteParametersConfig::default();
- nodes[0]
- .node
- .pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), params)
- .unwrap();
+ nodes[0].node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
let invreq_om = nodes[0]
.onion_messenger
@@ -707,11 +701,7 @@ fn ignore_duplicate_invoice() {
let offer = async_recipient.node.get_async_receive_offer().unwrap();
let amt_msat = 5000;
let payment_id = PaymentId([1; 32]);
- let params = RouteParametersConfig::default();
- sender
- .node
- .pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), params)
- .unwrap();
+ sender.node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
let sender_node_id = sender.node.get_our_node_id();
let always_online_node_id = always_online_node.node.get_our_node_id();
@@ -809,10 +799,7 @@ fn ignore_duplicate_invoice() {
// Now handle case where the sender pays regular invoice and ignores static invoice.
let payment_id = PaymentId([2; 32]);
- sender
- .node
- .pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), params)
- .unwrap();
+ sender.node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
let invreq_om =
sender.onion_messenger.next_onion_message_for_peer(always_online_node_id).unwrap();
@@ -918,11 +905,7 @@ fn async_receive_flow_success() {
let offer = nodes[2].node.get_async_receive_offer().unwrap();
let amt_msat = 5000;
let payment_id = PaymentId([1; 32]);
- let params = RouteParametersConfig::default();
- nodes[0]
- .node
- .pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), params)
- .unwrap();
+ nodes[0].node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
let release_held_htlc_om = pass_async_payments_oms(
static_invoice.clone(),
&nodes[0],
@@ -982,11 +965,7 @@ fn expired_static_invoice_fail() {
let amt_msat = 5000;
let payment_id = PaymentId([1; 32]);
- let params = RouteParametersConfig::default();
- nodes[0]
- .node
- .pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), params)
- .unwrap();
+ nodes[0].node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
let invreq_om = nodes[0]
.onion_messenger
@@ -1069,11 +1048,7 @@ fn timeout_unreleased_payment() {
let amt_msat = 5000;
let payment_id = PaymentId([1; 32]);
- let params = RouteParametersConfig::default();
- sender
- .node
- .pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), params)
- .unwrap();
+ sender.node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
let invreq_om =
sender.onion_messenger.next_onion_message_for_peer(server.node.get_our_node_id()).unwrap();
@@ -1166,11 +1141,7 @@ fn async_receive_mpp() {
let amt_msat = 15_000_000;
let payment_id = PaymentId([1; 32]);
- let params = RouteParametersConfig::default();
- nodes[0]
- .node
- .pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(1), params)
- .unwrap();
+ nodes[0].node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
let release_held_htlc_om_3_0 = pass_async_payments_oms(
static_invoice,
&nodes[0],
@@ -1268,11 +1239,7 @@ fn amount_doesnt_match_invreq() {
let amt_msat = 5000;
let payment_id = PaymentId([1; 32]);
- let params = RouteParametersConfig::default();
- nodes[0]
- .node
- .pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(1), params)
- .unwrap();
+ nodes[0].node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
let release_held_htlc_om_3_0 = pass_async_payments_oms(
static_invoice,
&nodes[0],
@@ -1515,11 +1482,7 @@ fn invalid_async_receive_with_retry<F1, F2>(
let static_invoice = invoice_flow_res.invoice;
let offer = nodes[2].node.get_async_receive_offer().unwrap();
- let params = RouteParametersConfig::default();
- nodes[0]
- .node
- .pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(2), params)
- .unwrap();
+ nodes[0].node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
let release_held_htlc_om_2_0 = pass_async_payments_oms(
static_invoice,
&nodes[0],
@@ -1613,11 +1576,7 @@ fn expired_static_invoice_message_path() {
let amt_msat = 5000;
let payment_id = PaymentId([1; 32]);
- let params = RouteParametersConfig::default();
- nodes[0]
- .node
- .pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(1), params)
- .unwrap();
+ nodes[0].node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
// While the invoice is unexpired, respond with release_held_htlc.
let (held_htlc_available_om, _release_held_htlc_om) = pass_async_payments_oms(
@@ -1730,11 +1689,9 @@ fn expired_static_invoice_payment_path() {
let amt_msat = 5000;
let payment_id = PaymentId([1; 32]);
- let params = RouteParametersConfig::default();
- nodes[0]
- .node
- .pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), params)
- .unwrap();
+ let mut params: OptionalOfferPaymentParams = Default::default();
+ params.retry_strategy = Retry::Attempts(0);
+ nodes[0].node.pay_for_offer(&offer, Some(amt_msat), payment_id, params).unwrap();
let release_held_htlc_om = pass_async_payments_oms(
static_invoice,
&nodes[0],
@@ -2178,11 +2135,7 @@ fn refresh_static_invoices_for_used_offers() {
let offer = recipient.node.get_async_receive_offer().unwrap();
let amt_msat = 5000;
let payment_id = PaymentId([1; 32]);
- let params = RouteParametersConfig::default();
- sender
- .node
- .pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), params)
- .unwrap();
+ sender.node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
let release_held_htlc_om = pass_async_payments_oms(
updated_invoice.clone(),
@@ -2514,11 +2467,7 @@ fn invoice_server_is_not_channel_peer() {
let offer = recipient.node.get_async_receive_offer().unwrap();
let amt_msat = 5000;
let payment_id = PaymentId([1; 32]);
- let params = RouteParametersConfig::default();
- sender
- .node
- .pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), params)
- .unwrap();
+ sender.node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
// Do the held_htlc_available --> release_held_htlc dance.
let release_held_htlc_om = pass_async_payments_oms(
@@ -2580,11 +2529,7 @@ fn invoice_request_forwarded_to_async_recipient() {
let offer = async_recipient.node.get_async_receive_offer().unwrap();
let amt_msat = 5000;
let payment_id = PaymentId([1; 32]);
- let params = RouteParametersConfig::default();
- sender
- .node
- .pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), params)
- .unwrap();
+ sender.node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
let sender_node_id = sender.node.get_our_node_id();
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index f991d22..fa739ed 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -696,6 +696,42 @@ impl Readable for InterceptId {
}
}
+/// Optional arguments to [`ChannelManager::pay_for_offer`]
+#[cfg_attr(
+ feature = "dnssec",
+ doc = "and [`ChannelManager::pay_for_offer_from_human_readable_name`]"
+)]
+/// .
+///
+/// These fields will often not need to be set, and the provided [`Self::default`] can be used.
+pub struct OptionalOfferPaymentParams {
+ /// A note that is communicated to the recipient about this payment via
+ /// [`InvoiceRequest::payer_note`].
+ pub payer_note: Option<String>,
+ /// Pathfinding options which tweak how the path is constructed to the recipient.
+ pub route_params_config: RouteParametersConfig,
+ /// The number of tries or time during which we'll retry this payment if some paths to the
+ /// recipient fail.
+ ///
+ /// Once the retry limit is reached, further path failures will not be retried and the payment
+ /// will ultimately fail once all pending paths have failed (generating an
+ /// [`Event::PaymentFailed`]).
+ pub retry_strategy: Retry,
+}
+
+impl Default for OptionalOfferPaymentParams {
+ fn default() -> Self {
+ Self {
+ payer_note: None,
+ route_params_config: Default::default(),
+ #[cfg(feature = "std")]
+ retry_strategy: Retry::Timeout(core::time::Duration::from_secs(2)),
+ #[cfg(not(feature = "std"))]
+ retry_strategy: Retry::Attempts(3),
+ }
+ }
+}
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
/// Uniquely describes an HTLC by its source. Just the guaranteed-unique subset of [`HTLCSource`].
pub(crate) enum SentHTLCId {
@@ -2286,18 +2322,16 @@ where
///
/// ```
/// # use lightning::events::{Event, EventsProvider};
-/// # use lightning::ln::channelmanager::{AChannelManager, PaymentId, RecentPaymentDetails, Retry};
+/// # use lightning::ln::channelmanager::{AChannelManager, PaymentId, RecentPaymentDetails};
/// # use lightning::offers::offer::Offer;
-/// # use lightning::routing::router::RouteParametersConfig;
/// #
/// # fn example<T: AChannelManager>(
-/// # channel_manager: T, offer: &Offer, quantity: Option<u64>, amount_msats: Option<u64>,
-/// # payer_note: Option<String>, retry: Retry, route_params_config: RouteParametersConfig
+/// # channel_manager: T, offer: &Offer, amount_msats: Option<u64>,
/// # ) {
/// # let channel_manager = channel_manager.get_cm();
/// let payment_id = PaymentId([42; 32]);
/// match channel_manager.pay_for_offer(
-/// offer, quantity, amount_msats, payer_note, payment_id, retry, route_params_config
+/// offer, amount_msats, payment_id, Default::default(),
/// ) {
/// Ok(()) => println!("Requesting invoice for offer"),
/// Err(e) => println!("Unable to request invoice for offer: {:?}", e),
@@ -5115,6 +5149,9 @@ where
/// Sends a payment to the route found using the provided [`RouteParameters`], retrying failed
/// payment paths based on the provided `Retry`.
///
+ /// You should likely prefer [`Self::pay_for_bolt11_invoice`] or [`Self::pay_for_offer`] in
+ /// general, however this method may allow for slightly more customization.
+ ///
/// May generate [`UpdateHTLCs`] message(s) event on success, which should be relayed (e.g. via
/// [`PeerManager::process_events`]).
///
@@ -12247,16 +12284,9 @@ where
///
/// Uses [`InvoiceRequestBuilder`] such that the [`InvoiceRequest`] it builds is recognized by
/// the [`ChannelManager`] when handling a [`Bolt12Invoice`] message in response to the request.
- /// The optional parameters are used in the builder, if `Some`:
- /// - `quantity` for [`InvoiceRequest::quantity`] which must be set if
- /// [`Offer::expects_quantity`] is `true`.
- /// - `amount_msats` if overpaying what is required for the given `quantity` is desired, and
- /// - `payer_note` for [`InvoiceRequest::payer_note`].
///
- /// # Custom Routing Parameters
- ///
- /// Users can customize routing parameters via [`RouteParametersConfig`].
- /// To use default settings, call the function with [`RouteParametersConfig::default`].
+ /// `amount_msats` allows you to overpay what is required to satisfy the offer, or may be
+ /// required if the offer does not require a specific amount.
///
/// # Payment
///
@@ -12289,17 +12319,14 @@ where
/// request.
///
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
- /// [`InvoiceRequest::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity
- /// [`InvoiceRequest::payer_note`]: crate::offers::invoice_request::InvoiceRequest::payer_note
/// [`InvoiceRequestBuilder`]: crate::offers::invoice_request::InvoiceRequestBuilder
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
/// [`BlindedMessagePath`]: crate::blinded_path::message::BlindedMessagePath
/// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths
/// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments
pub fn pay_for_offer(
- &self, offer: &Offer, quantity: Option<u64>, amount_msats: Option<u64>,
- payer_note: Option<String>, payment_id: PaymentId, retry_strategy: Retry,
- route_params_config: RouteParametersConfig,
+ &self, offer: &Offer, amount_msats: Option<u64>, payment_id: PaymentId,
+ optional_params: OptionalOfferPaymentParams,
) -> Result<(), Bolt12SemanticError> {
let create_pending_payment_fn = |invoice_request: &InvoiceRequest, nonce| {
let expiration = StaleExpiration::TimerTicks(1);
@@ -12312,8 +12339,8 @@ where
.add_new_awaiting_invoice(
payment_id,
expiration,
- retry_strategy,
- route_params_config,
+ optional_params.retry_strategy,
+ optional_params.route_params_config,
Some(retryable_invoice_request),
)
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)
@@ -12321,9 +12348,57 @@ where
self.pay_for_offer_intern(
offer,
- quantity,
+ if offer.expects_quantity() { Some(1) } else { None },
amount_msats,
- payer_note,
+ optional_params.payer_note,
+ payment_id,
+ None,
+ create_pending_payment_fn,
+ )
+ }
+
+ /// Pays for an [`Offer`] using the given parameters, including a `quantity`, by creating an
+ /// [`InvoiceRequest`] and enqueuing it to be sent via an onion message. [`ChannelManager`] will
+ /// pay the actual [`Bolt12Invoice`] once it is received.
+ ///
+ /// This method is identical to [`Self::pay_for_offer`] with the one exception that it allows
+ /// you to specify the [`InvoiceRequest::quantity`]. We expect this to be rather seldomly used,
+ /// as the "quantity" feature of offers doesn't line up with common payment flows today.
+ ///
+ /// This method is otherwise identical to [`Self::pay_for_offer`] but will additionally fail if
+ /// the provided `quantity` does not meet the requirements described by
+ /// [`Offer::supported_quantity`].
+ ///
+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
+ /// [`InvoiceRequest::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity
+ pub fn pay_for_offer_with_quantity(
+ &self, offer: &Offer, amount_msats: Option<u64>, payment_id: PaymentId,
+ optional_params: OptionalOfferPaymentParams, quantity: u64,
+ ) -> Result<(), Bolt12SemanticError> {
+ let create_pending_payment_fn = |invoice_request: &InvoiceRequest, nonce| {
+ let expiration = StaleExpiration::TimerTicks(1);
+ let retryable_invoice_request = RetryableInvoiceRequest {
+ invoice_request: invoice_request.clone(),
+ nonce,
+ needs_retry: true,
+ };
+ self.pending_outbound_payments
+ .add_new_awaiting_invoice(
+ payment_id,
+ expiration,
+ optional_params.retry_strategy,
+ optional_params.route_params_config,
+ Some(retryable_invoice_request),
+ )
+ .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)
+ };
+
+ self.pay_for_offer_intern(
+ offer,
+ Some(quantity),
+ amount_msats,
+ optional_params.payer_note,
payment_id,
None,
create_pending_payment_fn,
@@ -12431,11 +12506,6 @@ where
/// implementing [`DNSResolverMessageHandler`]) directly to look up a URI and then delegate to
/// your normal URI handling.
///
- /// # Custom Routing Parameters
- ///
- /// Users can customize routing parameters via [`RouteParametersConfig`].
- /// To use default settings, call the function with [`RouteParametersConfig::default`].
- ///
/// # Payment
///
/// The provided `payment_id` is used to ensure that only one invoice is paid for the request
@@ -12460,8 +12530,7 @@ where
///
/// # Errors
///
- /// Errors if:
- /// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
+ /// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link.
///
/// [BIP 353]: https://github.com/bitcoin/bips/blob/master/bip-0353.mediawiki
/// [bLIP 32]: https://github.com/lightning/blips/blob/master/blip-0032.md
@@ -12475,8 +12544,7 @@ where
#[cfg(feature = "dnssec")]
pub fn pay_for_offer_from_human_readable_name(
&self, name: HumanReadableName, amount_msats: u64, payment_id: PaymentId,
- payer_note: Option<String>, retry_strategy: Retry,
- route_params_config: RouteParametersConfig, dns_resolvers: Vec<Destination>,
+ optional_params: OptionalOfferPaymentParams, dns_resolvers: Vec<Destination>,
) -> Result<(), ()> {
let (onion_message, context) =
self.flow.hrn_resolver.resolve_name(payment_id, name, &*self.entropy_source)?;
@@ -12485,10 +12553,10 @@ where
self.pending_outbound_payments.add_new_awaiting_offer(
payment_id,
expiration,
- retry_strategy,
- route_params_config,
+ optional_params.retry_strategy,
+ optional_params.route_params_config,
amount_msats,
- payer_note,
+ optional_params.payer_note,
)?;
self.flow
diff --git a/lightning/src/ln/max_payment_path_len_tests.rs b/lightning/src/ln/max_payment_path_len_tests.rs
index 177050b..8425e19 100644
--- a/lightning/src/ln/max_payment_path_len_tests.rs
+++ b/lightning/src/ln/max_payment_path_len_tests.rs
@@ -17,7 +17,7 @@ use crate::blinded_path::payment::{
use crate::blinded_path::BlindedHop;
use crate::events::Event;
use crate::ln::blinded_payment_tests::get_blinded_route_parameters;
-use crate::ln::channelmanager::PaymentId;
+use crate::ln::channelmanager::{OptionalOfferPaymentParams, PaymentId};
use crate::ln::functional_test_utils::*;
use crate::ln::msgs;
use crate::ln::msgs::{BaseMessageHandler, OnionMessageHandler};
@@ -27,7 +27,7 @@ use crate::ln::outbound_payment::{RecipientOnionFields, Retry, RetryableSendFail
use crate::offers::nonce::Nonce;
use crate::prelude::*;
use crate::routing::router::{
- PaymentParameters, RouteParameters, RouteParametersConfig, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
+ PaymentParameters, RouteParameters, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
};
use crate::sign::NodeSigner;
use crate::types::features::BlindedHopFeatures;
@@ -519,10 +519,9 @@ fn bolt12_invoice_too_large_blinded_paths() {
let offer = nodes[1].node.create_offer_builder().unwrap().build().unwrap();
let payment_id = PaymentId([1; 32]);
- let route_config = RouteParametersConfig::default();
nodes[0]
.node
- .pay_for_offer(&offer, None, Some(5000), None, payment_id, Retry::Attempts(0), route_config)
+ .pay_for_offer(&offer, Some(5000), payment_id, OptionalOfferPaymentParams::default())
.unwrap();
let invreq_om = nodes[0]
.onion_messenger
diff --git a/lightning/src/ln/offers_tests.rs b/lightning/src/ln/offers_tests.rs
index e2bdfc4..b7d64df 100644
--- a/lightning/src/ln/offers_tests.rs
+++ b/lightning/src/ln/offers_tests.rs
@@ -473,7 +473,7 @@ fn check_dummy_hop_pattern_in_offer() {
}
let payment_id = PaymentId([1; 32]);
- bob.node.pay_for_offer(&compact_offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()).unwrap();
+ bob.node.pay_for_offer(&compact_offer, None, payment_id, Default::default()).unwrap();
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
let (invoice_request, reply_path) = extract_invoice_request(alice, &onion_message);
@@ -495,7 +495,7 @@ fn check_dummy_hop_pattern_in_offer() {
assert!(padded_offer.paths().iter().all(|path| path.blinded_hops().len() == PADDED_PATH_LENGTH));
let payment_id = PaymentId([2; 32]);
- bob.node.pay_for_offer(&padded_offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()).unwrap();
+ bob.node.pay_for_offer(&padded_offer, None, payment_id, Default::default()).unwrap();
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
let (invoice_request, reply_path) = extract_invoice_request(alice, &onion_message);
@@ -662,8 +662,7 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() {
}
let payment_id = PaymentId([1; 32]);
- david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
- .unwrap();
+ david.node.pay_for_offer(&offer, None, payment_id, Default::default()).unwrap();
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
connect_peers(david, bob);
@@ -828,7 +827,7 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() {
}
let payment_id = PaymentId([1; 32]);
- bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()).unwrap();
+ bob.node.pay_for_offer(&offer, None, payment_id, Default::default()).unwrap();
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
@@ -949,7 +948,7 @@ fn pays_for_offer_without_blinded_paths() {
assert!(offer.paths().is_empty());
let payment_id = PaymentId([1; 32]);
- bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()).unwrap();
+ bob.node.pay_for_offer(&offer, None, payment_id, Default::default()).unwrap();
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
@@ -1076,8 +1075,7 @@ fn send_invoice_requests_with_distinct_reply_path() {
}
let payment_id = PaymentId([1; 32]);
- david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
- .unwrap();
+ david.node.pay_for_offer(&offer, None, payment_id, Default::default()).unwrap();
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
connect_peers(david, bob);
@@ -1210,7 +1208,7 @@ fn creates_and_pays_for_offer_with_retry() {
assert!(check_compact_path_introduction_node(&path, bob, alice_id));
}
let payment_id = PaymentId([1; 32]);
- bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()).unwrap();
+ bob.node.pay_for_offer(&offer, None, payment_id, Default::default()).unwrap();
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
let _lost_onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
@@ -1282,7 +1280,7 @@ fn pays_bolt12_invoice_asynchronously() {
.build().unwrap();
let payment_id = PaymentId([1; 32]);
- bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()).unwrap();
+ bob.node.pay_for_offer(&offer, None, payment_id, Default::default()).unwrap();
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
@@ -1379,7 +1377,7 @@ fn creates_offer_with_blinded_path_using_unannounced_introduction_node() {
}
let payment_id = PaymentId([1; 32]);
- bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()).unwrap();
+ bob.node.pay_for_offer(&offer, None, payment_id, Default::default()).unwrap();
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
@@ -1520,8 +1518,7 @@ fn fails_authentication_when_handling_invoice_request() {
// Send the invoice request directly to Alice instead of using a blinded path.
let payment_id = PaymentId([1; 32]);
- david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
- .unwrap();
+ david.node.pay_for_offer(&offer, None, payment_id, Default::default()).unwrap();
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
connect_peers(david, alice);
@@ -1546,8 +1543,7 @@ fn fails_authentication_when_handling_invoice_request() {
// Send the invoice request to Alice using an invalid blinded path.
let payment_id = PaymentId([2; 32]);
- david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
- .unwrap();
+ david.node.pay_for_offer(&offer, None, payment_id, Default::default()).unwrap();
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
match &mut david.node.flow.pending_offers_messages.lock().unwrap().first_mut().unwrap().1 {
@@ -1623,8 +1619,7 @@ fn fails_authentication_when_handling_invoice_for_offer() {
// Initiate an invoice request, but abandon tracking it.
let payment_id = PaymentId([1; 32]);
- david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
- .unwrap();
+ david.node.pay_for_offer(&offer, None, payment_id, Default::default()).unwrap();
david.node.abandon_payment(payment_id);
get_event!(david, Event::PaymentFailed);
@@ -1640,8 +1635,7 @@ fn fails_authentication_when_handling_invoice_for_offer() {
};
let payment_id = PaymentId([2; 32]);
- david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
- .unwrap();
+ david.node.pay_for_offer(&offer, None, payment_id, Default::default()).unwrap();
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
// Swap out the reply path to force authentication to fail when handling the invoice since it
@@ -1825,7 +1819,7 @@ fn fails_creating_or_paying_for_offer_without_connected_peers() {
let payment_id = PaymentId([1; 32]);
- match david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()) {
+ match david.node.pay_for_offer(&offer, None, payment_id, Default::default()) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths),
}
@@ -1836,12 +1830,7 @@ fn fails_creating_or_paying_for_offer_without_connected_peers() {
args.send_channel_ready = (true, true);
reconnect_nodes(args);
- assert!(
- david.node.pay_for_offer(
- &offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()
- ).is_ok()
- );
-
+ assert!(david.node.pay_for_offer(&offer, None, payment_id, Default::default()).is_ok());
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
}
@@ -1928,8 +1917,7 @@ fn fails_creating_invoice_request_for_unsupported_chain() {
.chain(Network::Signet)
.build().unwrap();
- let payment_id = PaymentId([1; 32]);
- match bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()) {
+ match bob.node.pay_for_offer(&offer, None, PaymentId([1; 32]), Default::default()) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::UnsupportedChain),
}
@@ -1986,9 +1974,7 @@ fn fails_creating_invoice_request_without_blinded_reply_path() {
.amount_msats(10_000_000)
.build().unwrap();
- let payment_id = PaymentId([1; 32]);
-
- match david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()) {
+ match david.node.pay_for_offer(&offer, None, PaymentId([1; 32]), Default::default()) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths),
}
@@ -2021,14 +2007,10 @@ fn fails_creating_invoice_request_with_duplicate_payment_id() {
.build().unwrap();
let payment_id = PaymentId([1; 32]);
- assert!(
- david.node.pay_for_offer(
- &offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()
- ).is_ok()
- );
+ assert!(david.node.pay_for_offer( &offer, None, payment_id, Default::default()).is_ok());
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
- match david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()) {
+ match david.node.pay_for_offer(&offer, None, payment_id, Default::default()) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::DuplicatePaymentId),
}
@@ -2107,8 +2089,7 @@ fn fails_sending_invoice_without_blinded_payment_paths_for_offer() {
.build().unwrap();
let payment_id = PaymentId([1; 32]);
- david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
- .unwrap();
+ david.node.pay_for_offer(&offer, None, payment_id, Default::default()).unwrap();
connect_peers(david, bob);
@@ -2316,8 +2297,7 @@ fn fails_paying_invoice_with_unknown_required_features() {
.build().unwrap();
let payment_id = PaymentId([1; 32]);
- david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
- .unwrap();
+ david.node.pay_for_offer(&offer, None, payment_id, Default::default()).unwrap();
connect_peers(david, bob);
@@ -2393,7 +2373,7 @@ fn rejects_keysend_to_non_static_invoice_path() {
let offer = nodes[1].node.create_offer_builder().unwrap().build().unwrap();
let amt_msat = 5000;
let payment_id = PaymentId([1; 32]);
- nodes[0].node.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(1), RouteParametersConfig::default()).unwrap();
+ nodes[0].node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
let invreq_om = nodes[0].onion_messenger.next_onion_message_for_peer(nodes[1].node.get_our_node_id()).unwrap();
nodes[1].onion_messenger.handle_onion_message(nodes[0].node.get_our_node_id(), &invreq_om);
let invoice_om = nodes[1].onion_messenger.next_onion_message_for_peer(nodes[0].node.get_our_node_id()).unwrap();
@@ -2478,7 +2458,7 @@ fn no_double_pay_with_stale_channelmanager() {
assert!(offer.paths().is_empty());
let payment_id = PaymentId([1; 32]);
- nodes[0].node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()).unwrap();
+ nodes[0].node.pay_for_offer(&offer, None, payment_id, Default::default()).unwrap();
expect_recent_payment!(nodes[0], RecentPaymentDetails::AwaitingInvoice, payment_id);
let invreq_om = nodes[0].onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.