Merge pull request #3342 from shaavan/i3262

Introduce RouteParametersConfig
This commit is contained in:
Matt Corallo 2025-02-25 15:12:49 +00:00 committed by GitHub
commit ece4ab7cb7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 323 additions and 98 deletions

View file

@ -170,6 +170,7 @@ mod test {
use lightning::onion_message::messenger::{
AOnionMessenger, Destination, MessageRouter, OnionMessagePath, OnionMessenger,
};
use lightning::routing::router::RouteParametersConfig;
use lightning::sign::{KeysManager, NodeSigner, Recipient};
use lightning::types::features::InitFeatures;
use lightning::types::payment::PaymentHash;
@ -405,7 +406,14 @@ mod test {
let amt = 42_000;
nodes[0]
.node
.pay_for_offer_from_human_readable_name(name, amt, payment_id, retry, None, resolvers)
.pay_for_offer_from_human_readable_name(
name,
amt,
payment_id,
retry,
RouteParametersConfig::default(),
resolvers,
)
.unwrap();
let query = nodes[0].onion_messenger.next_onion_message_for_peer(resolver_id).unwrap();

View file

@ -33,7 +33,7 @@ use crate::onion_message::messenger::{Destination, MessageRouter, MessageSendIns
use crate::onion_message::offers::OffersMessage;
use crate::onion_message::packet::ParsedOnionMessageContents;
use crate::prelude::*;
use crate::routing::router::{Payee, PaymentParameters};
use crate::routing::router::{Payee, PaymentParameters, RouteParametersConfig};
use crate::sign::NodeSigner;
use crate::sync::Mutex;
use crate::types::features::Bolt12InvoiceFeatures;
@ -238,7 +238,15 @@ fn static_invoice_unknown_required_features() {
let payment_id = PaymentId([1; 32]);
nodes[0]
.node
.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), None)
.pay_for_offer(
&offer,
None,
Some(amt_msat),
None,
payment_id,
Retry::Attempts(0),
RouteParametersConfig::default(),
)
.unwrap();
// Don't forward the invreq since we don't support retrieving the static invoice from the
@ -298,7 +306,15 @@ fn ignore_unexpected_static_invoice() {
let payment_id = PaymentId([1; 32]);
nodes[0]
.node
.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), None)
.pay_for_offer(
&offer,
None,
Some(amt_msat),
None,
payment_id,
Retry::Attempts(0),
RouteParametersConfig::default(),
)
.unwrap();
// Don't forward the invreq since we don't support retrieving the static invoice from the
@ -415,7 +431,15 @@ fn async_receive_flow_success() {
let payment_id = PaymentId([1; 32]);
nodes[0]
.node
.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), None)
.pay_for_offer(
&offer,
None,
Some(amt_msat),
None,
payment_id,
Retry::Attempts(0),
RouteParametersConfig::default(),
)
.unwrap();
let release_held_htlc_om =
pass_async_payments_oms(static_invoice, &nodes[0], &nodes[1], &nodes[2]).1;
@ -463,7 +487,15 @@ fn expired_static_invoice_fail() {
let payment_id = PaymentId([1; 32]);
nodes[0]
.node
.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), None)
.pay_for_offer(
&offer,
None,
Some(amt_msat),
None,
payment_id,
Retry::Attempts(0),
RouteParametersConfig::default(),
)
.unwrap();
let invreq_om = nodes[0]
@ -546,7 +578,15 @@ fn async_receive_mpp() {
let payment_id = PaymentId([1; 32]);
nodes[0]
.node
.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(1), None)
.pay_for_offer(
&offer,
None,
Some(amt_msat),
None,
payment_id,
Retry::Attempts(1),
RouteParametersConfig::default(),
)
.unwrap();
let release_held_htlc_om_3_0 =
pass_async_payments_oms(static_invoice, &nodes[0], &nodes[1], &nodes[3]).1;
@ -630,7 +670,15 @@ fn amount_doesnt_match_invreq() {
let payment_id = PaymentId([1; 32]);
nodes[0]
.node
.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(1), None)
.pay_for_offer(
&offer,
None,
Some(amt_msat),
None,
payment_id,
Retry::Attempts(1),
RouteParametersConfig::default(),
)
.unwrap();
let release_held_htlc_om_3_0 =
pass_async_payments_oms(static_invoice, &nodes[0], &nodes[1], &nodes[3]).1;
@ -859,7 +907,15 @@ fn invalid_async_receive_with_retry<F1, F2>(
nodes[0]
.node
.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(2), None)
.pay_for_offer(
&offer,
None,
Some(amt_msat),
None,
payment_id,
Retry::Attempts(2),
RouteParametersConfig::default(),
)
.unwrap();
let release_held_htlc_om_2_0 =
pass_async_payments_oms(static_invoice, &nodes[0], &nodes[1], &nodes[2]).1;
@ -948,7 +1004,15 @@ fn expired_static_invoice_message_path() {
let payment_id = PaymentId([1; 32]);
nodes[0]
.node
.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(1), None)
.pay_for_offer(
&offer,
None,
Some(amt_msat),
None,
payment_id,
Retry::Attempts(1),
RouteParametersConfig::default(),
)
.unwrap();
// While the invoice is unexpired, respond with release_held_htlc.
@ -1052,7 +1116,15 @@ fn expired_static_invoice_payment_path() {
let payment_id = PaymentId([1; 32]);
nodes[0]
.node
.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), None)
.pay_for_offer(
&offer,
None,
Some(amt_msat),
None,
payment_id,
Retry::Attempts(0),
RouteParametersConfig::default(),
)
.unwrap();
let release_held_htlc_om =
pass_async_payments_oms(static_invoice, &nodes[0], &nodes[1], &nodes[2]).1;

View file

@ -57,7 +57,7 @@ use crate::types::features::{Bolt12InvoiceFeatures, ChannelFeatures, ChannelType
use crate::types::features::Bolt11InvoiceFeatures;
#[cfg(trampoline)]
use crate::routing::gossip::NodeId;
use crate::routing::router::{BlindedTail, FixedRouter, InFlightHtlcs, Path, Payee, PaymentParameters, Route, RouteParameters, Router};
use crate::routing::router::{BlindedTail, FixedRouter, InFlightHtlcs, Path, Payee, PaymentParameters, Route, RouteParameters, RouteParametersConfig, Router};
use crate::ln::onion_payment::{check_incoming_htlc_cltv, create_recv_pending_htlc_info, create_fwd_pending_htlc_info, decode_incoming_update_add_htlc_onion, InboundHTLCErr, NextPacketDetails};
use crate::ln::msgs;
use crate::ln::onion_utils;
@ -2105,6 +2105,7 @@ where
/// # use lightning::events::{Event, EventsProvider, PaymentPurpose};
/// # use lightning::ln::channelmanager::AChannelManager;
/// # use lightning::offers::parse::Bolt12SemanticError;
/// # use lightning::routing::router::RouteParametersConfig;
/// #
/// # fn example<T: AChannelManager>(channel_manager: T) -> Result<(), Bolt12SemanticError> {
/// # let channel_manager = channel_manager.get_cm();
@ -2152,15 +2153,16 @@ where
/// # use lightning::events::{Event, EventsProvider};
/// # use lightning::ln::channelmanager::{AChannelManager, PaymentId, RecentPaymentDetails, Retry};
/// # 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, max_total_routing_fee_msat: Option<u64>
/// # payer_note: Option<String>, retry: Retry, route_params_config: RouteParametersConfig
/// # ) {
/// # 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, max_total_routing_fee_msat
/// offer, quantity, amount_msats, payer_note, payment_id, retry, route_params_config
/// ) {
/// Ok(()) => println!("Requesting invoice for offer"),
/// Err(e) => println!("Unable to request invoice for offer: {:?}", e),
@ -2208,16 +2210,17 @@ where
/// # use lightning::events::{Event, EventsProvider};
/// # use lightning::ln::channelmanager::{AChannelManager, PaymentId, RecentPaymentDetails, Retry};
/// # use lightning::offers::parse::Bolt12SemanticError;
/// # use lightning::routing::router::RouteParametersConfig;
/// #
/// # fn example<T: AChannelManager>(
/// # channel_manager: T, amount_msats: u64, absolute_expiry: Duration, retry: Retry,
/// # max_total_routing_fee_msat: Option<u64>
/// # route_params_config: RouteParametersConfig
/// # ) -> Result<(), Bolt12SemanticError> {
/// # let channel_manager = channel_manager.get_cm();
/// let payment_id = PaymentId([42; 32]);
/// let refund = channel_manager
/// .create_refund_builder(
/// amount_msats, absolute_expiry, payment_id, retry, max_total_routing_fee_msat
/// amount_msats, absolute_expiry, payment_id, retry, route_params_config
/// )?
/// # ;
/// # // Needed for compiling for c_bindings
@ -10161,7 +10164,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
/// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments
pub fn create_refund_builder(
&$self, amount_msats: u64, absolute_expiry: Duration, payment_id: PaymentId,
retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>
retry_strategy: Retry, route_params_config: RouteParametersConfig
) -> Result<$builder, Bolt12SemanticError> {
let node_id = $self.get_our_node_id();
let expanded_key = &$self.inbound_payment_key;
@ -10186,7 +10189,7 @@ 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, None,
payment_id, expiration, retry_strategy, route_params_config, None,
)
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
@ -10362,7 +10365,7 @@ where
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,
max_total_routing_fee_msat: Option<u64>
route_params_config: RouteParametersConfig
) -> Result<(), Bolt12SemanticError> {
self.pay_for_offer_intern(offer, quantity, amount_msats, payer_note, payment_id, None, |invoice_request, nonce| {
let expiration = StaleExpiration::TimerTicks(1);
@ -10373,7 +10376,7 @@ where
};
self.pending_outbound_payments
.add_new_awaiting_invoice(
payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
payment_id, expiration, retry_strategy, route_params_config,
Some(retryable_invoice_request)
)
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)
@ -10606,14 +10609,14 @@ where
#[cfg(feature = "dnssec")]
pub fn pay_for_offer_from_human_readable_name(
&self, name: HumanReadableName, amount_msats: u64, payment_id: PaymentId,
retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>,
retry_strategy: Retry, route_params_config: RouteParametersConfig,
dns_resolvers: Vec<Destination>,
) -> Result<(), ()> {
let (onion_message, context) =
self.hrn_resolver.resolve_name(payment_id, name, &*self.entropy_source)?;
let reply_paths = self.create_blinded_paths(MessageContext::DNSResolver(context))?;
let expiration = StaleExpiration::TimerTicks(1);
self.pending_outbound_payments.add_new_awaiting_offer(payment_id, expiration, retry_strategy, max_total_routing_fee_msat, amount_msats)?;
self.pending_outbound_payments.add_new_awaiting_offer(payment_id, expiration, retry_strategy, route_params_config, amount_msats)?;
let message_params = dns_resolvers
.iter()
.flat_map(|destination| reply_paths.iter().map(move |path| (path, destination)))

View file

@ -26,7 +26,7 @@ use crate::ln::onion_utils::MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY;
use crate::ln::outbound_payment::{RecipientOnionFields, Retry, RetryableSendFailure};
use crate::offers::nonce::Nonce;
use crate::prelude::*;
use crate::routing::router::{DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, PaymentParameters, RouteParameters};
use crate::routing::router::{PaymentParameters, RouteParameters, RouteParametersConfig, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA};
use crate::sign::NodeSigner;
use crate::util::errors::APIError;
use crate::util::ser::Writeable;
@ -393,7 +393,7 @@ fn bolt12_invoice_too_large_blinded_paths() {
let offer = nodes[1].node.create_offer_builder(None).unwrap().build().unwrap();
let payment_id = PaymentId([1; 32]);
nodes[0].node.pay_for_offer(&offer, None, Some(5000), None, payment_id, Retry::Attempts(0), None).unwrap();
nodes[0].node.pay_for_offer(&offer, None, Some(5000), None, payment_id, Retry::Attempts(0), RouteParametersConfig::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);

View file

@ -62,7 +62,7 @@ use crate::onion_message::messenger::{Destination, PeeledOnion, MessageSendInstr
use crate::onion_message::offers::OffersMessage;
use crate::onion_message::packet::ParsedOnionMessageContents;
use crate::routing::gossip::{NodeAlias, NodeId};
use crate::routing::router::{PaymentParameters, RouteParameters};
use crate::routing::router::{PaymentParameters, RouteParameters, RouteParametersConfig};
use crate::sign::{NodeSigner, Recipient};
use crate::util::ser::Writeable;
@ -454,7 +454,7 @@ fn creates_short_lived_refund() {
let absolute_expiry = bob.node.duration_since_epoch() + MAX_SHORT_LIVED_RELATIVE_EXPIRY;
let payment_id = PaymentId([1; 32]);
let refund = bob.node
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap()
.build().unwrap();
assert_eq!(refund.absolute_expiry(), Some(absolute_expiry));
@ -483,7 +483,7 @@ fn creates_long_lived_refund() {
+ Duration::from_secs(1);
let payment_id = PaymentId([1; 32]);
let refund = bob.node
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap()
.build().unwrap();
assert_eq!(refund.absolute_expiry(), Some(absolute_expiry));
@ -543,7 +543,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), None)
david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap();
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
@ -642,7 +642,7 @@ fn creates_and_pays_for_refund_using_two_hop_blinded_path() {
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
let refund = david.node
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap()
.build().unwrap();
assert_eq!(refund.amount_msats(), 10_000_000);
@ -712,7 +712,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), None).unwrap();
bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()).unwrap();
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
@ -771,7 +771,7 @@ fn creates_and_pays_for_refund_using_one_hop_blinded_path() {
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
let refund = bob.node
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap()
.build().unwrap();
assert_eq!(refund.amount_msats(), 10_000_000);
@ -833,7 +833,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), None).unwrap();
bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()).unwrap();
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
@ -880,7 +880,7 @@ fn pays_for_refund_without_blinded_paths() {
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
let refund = bob.node
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap()
.clear_paths()
.build().unwrap();
@ -960,7 +960,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), None)
david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap();
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
connect_peers(david, bob);
@ -1035,7 +1035,7 @@ fn send_invoice_for_refund_with_distinct_reply_path() {
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
let refund = alice.node
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap()
.build().unwrap();
assert_ne!(refund.payer_signing_pubkey(), alice_id);
@ -1094,7 +1094,7 @@ fn creates_and_pays_for_offer_with_retry() {
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();
bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::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();
@ -1166,7 +1166,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), None).unwrap();
bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()).unwrap();
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
@ -1256,7 +1256,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), None).unwrap();
bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()).unwrap();
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
@ -1314,7 +1314,7 @@ fn creates_refund_with_blinded_path_using_unannounced_introduction_node() {
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
let refund = bob.node
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap()
.build().unwrap();
assert_ne!(refund.payer_signing_pubkey(), bob_id);
@ -1397,7 +1397,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), None)
david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap();
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
@ -1423,7 +1423,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), None)
david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap();
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
@ -1500,7 +1500,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), None)
david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap();
david.node.abandon_payment(payment_id);
get_event!(david, Event::PaymentFailed);
@ -1517,7 +1517,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), None)
david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap();
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
@ -1597,7 +1597,7 @@ fn fails_authentication_when_handling_invoice_for_refund() {
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
let refund = david.node
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap()
.build().unwrap();
assert_ne!(refund.payer_signing_pubkey(), david_id);
@ -1631,7 +1631,7 @@ fn fails_authentication_when_handling_invoice_for_refund() {
let invalid_path = refund.paths().first().unwrap().clone();
let payment_id = PaymentId([2; 32]);
let refund = david.node
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap()
.build().unwrap();
assert_ne!(refund.payer_signing_pubkey(), david_id);
@ -1701,7 +1701,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), None) {
match david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths),
}
@ -1714,7 +1714,7 @@ fn fails_creating_or_paying_for_offer_without_connected_peers() {
assert!(
david.node.pay_for_offer(
&offer, None, None, None, payment_id, Retry::Attempts(0), None
&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()
).is_ok()
);
@ -1758,7 +1758,7 @@ fn fails_creating_refund_or_sending_invoice_without_connected_peers() {
let absolute_expiry = david.node.duration_since_epoch() + MAX_SHORT_LIVED_RELATIVE_EXPIRY;
let payment_id = PaymentId([1; 32]);
match david.node.create_refund_builder(
10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None
10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default()
) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths),
@ -1769,7 +1769,7 @@ fn fails_creating_refund_or_sending_invoice_without_connected_peers() {
reconnect_nodes(args);
let refund = david.node
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap()
.build().unwrap();
@ -1805,7 +1805,7 @@ fn fails_creating_invoice_request_for_unsupported_chain() {
.build().unwrap();
let payment_id = PaymentId([1; 32]);
match bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None) {
match bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::UnsupportedChain),
}
@ -1827,7 +1827,7 @@ fn fails_sending_invoice_with_unsupported_chain_for_refund() {
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
let refund = bob.node
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap()
.chain(Network::Signet)
.build().unwrap();
@ -1864,7 +1864,7 @@ fn fails_creating_invoice_request_without_blinded_reply_path() {
let payment_id = PaymentId([1; 32]);
match david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None) {
match david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths),
}
@ -1899,12 +1899,12 @@ fn fails_creating_invoice_request_with_duplicate_payment_id() {
let payment_id = PaymentId([1; 32]);
assert!(
david.node.pay_for_offer(
&offer, None, None, None, payment_id, Retry::Attempts(0), None
&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::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), None) {
match david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default()) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::DuplicatePaymentId),
}
@ -1925,13 +1925,13 @@ fn fails_creating_refund_with_duplicate_payment_id() {
let payment_id = PaymentId([1; 32]);
assert!(
nodes[0].node.create_refund_builder(
10_000, absolute_expiry, payment_id, Retry::Attempts(0), None
10_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default()
).is_ok()
);
expect_recent_payment!(nodes[0], RecentPaymentDetails::AwaitingInvoice, payment_id);
match nodes[0].node.create_refund_builder(
10_000, absolute_expiry, payment_id, Retry::Attempts(0), None
10_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default()
) {
Ok(_) => panic!("Expected error"),
Err(e) => assert_eq!(e, Bolt12SemanticError::DuplicatePaymentId),
@ -1983,7 +1983,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), None)
david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap();
connect_peers(david, bob);
@ -2051,7 +2051,7 @@ fn fails_sending_invoice_without_blinded_payment_paths_for_refund() {
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
let refund = david.node
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap()
.build().unwrap();
@ -2100,7 +2100,7 @@ fn fails_paying_invoice_more_than_once() {
let absolute_expiry = Duration::from_secs(u64::MAX);
let payment_id = PaymentId([1; 32]);
let refund = david.node
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap()
.build().unwrap();
expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
@ -2192,7 +2192,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), None)
david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
.unwrap();
connect_peers(david, bob);
@ -2269,7 +2269,7 @@ fn rejects_keysend_to_non_static_invoice_path() {
let offer = nodes[1].node.create_offer_builder(None).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), None).unwrap();
nodes[0].node.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(1), RouteParametersConfig::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();
@ -2354,7 +2354,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), None).unwrap();
nodes[0].node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), RouteParametersConfig::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();

View file

@ -24,7 +24,7 @@ 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::routing::router::{BlindedTail, InFlightHtlcs, RouteParametersConfig, Path, PaymentParameters, Route, RouteParameters, Router};
use crate::sign::{EntropySource, NodeSigner, Recipient};
use crate::util::errors::APIError;
use crate::util::logger::Logger;
@ -62,7 +62,7 @@ pub(crate) enum PendingOutboundPayment {
AwaitingOffer {
expiration: StaleExpiration,
retry_strategy: Retry,
max_total_routing_fee_msat: Option<u64>,
route_params_config: RouteParametersConfig,
/// Human Readable Names-originated payments should always specify an explicit amount to
/// send up-front, which we track here and enforce once we receive the offer.
amount_msats: u64,
@ -70,7 +70,7 @@ pub(crate) enum PendingOutboundPayment {
AwaitingInvoice {
expiration: StaleExpiration,
retry_strategy: Retry,
max_total_routing_fee_msat: Option<u64>,
route_params_config: RouteParametersConfig,
retryable_invoice_request: Option<RetryableInvoiceRequest>
},
// This state will never be persisted to disk because we transition from `AwaitingInvoice` to
@ -79,9 +79,10 @@ pub(crate) enum PendingOutboundPayment {
InvoiceReceived {
payment_hash: PaymentHash,
retry_strategy: Retry,
// Note this field is currently just replicated from AwaitingInvoice but not actually
// used anywhere.
max_total_routing_fee_msat: Option<u64>,
// Currently unused, but replicated from `AwaitingInvoice` to avoid potential
// race conditions where this field might be missing upon reload. It may be required
// for future retries.
route_params_config: RouteParametersConfig,
},
// This state applies when we are paying an often-offline recipient and another node on the
// network served us a static invoice on the recipient's behalf in response to our invoice
@ -862,19 +863,19 @@ impl OutboundPayments {
SP: Fn(SendAlongPathArgs) -> Result<(), APIError>,
{
let payment_hash = invoice.payment_hash();
let max_total_routing_fee_msat;
let params_config;
let retry_strategy;
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
hash_map::Entry::Occupied(entry) => match entry.get() {
PendingOutboundPayment::AwaitingInvoice {
retry_strategy: retry, max_total_routing_fee_msat: max_total_fee, ..
retry_strategy: retry, route_params_config, ..
} => {
retry_strategy = *retry;
max_total_routing_fee_msat = *max_total_fee;
params_config = *route_params_config;
*entry.into_mut() = PendingOutboundPayment::InvoiceReceived {
payment_hash,
retry_strategy: *retry,
max_total_routing_fee_msat,
route_params_config: *route_params_config,
};
},
_ => return Err(Bolt12PaymentError::DuplicateInvoice),
@ -890,9 +891,10 @@ impl OutboundPayments {
}
let mut route_params = RouteParameters::from_payment_params_and_value(
PaymentParameters::from_bolt12_invoice(&invoice), invoice.amount_msats()
PaymentParameters::from_bolt12_invoice(&invoice)
.with_user_config_ignoring_fee_limit(params_config), invoice.amount_msats()
);
if let Some(max_fee_msat) = max_total_routing_fee_msat {
if let Some(max_fee_msat) = params_config.max_total_routing_fee_msat {
route_params.max_total_routing_fee_msat = Some(max_fee_msat);
}
self.send_payment_for_bolt12_invoice_internal(
@ -1035,7 +1037,7 @@ impl OutboundPayments {
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
hash_map::Entry::Occupied(mut entry) => match entry.get_mut() {
PendingOutboundPayment::AwaitingInvoice {
retry_strategy, retryable_invoice_request, max_total_routing_fee_msat, ..
retry_strategy, retryable_invoice_request, route_params_config, ..
} => {
let invreq = &retryable_invoice_request
.as_ref()
@ -1065,9 +1067,10 @@ impl OutboundPayments {
};
let keysend_preimage = PaymentPreimage(entropy_source.get_secure_random_bytes());
let payment_hash = PaymentHash(Sha256::hash(&keysend_preimage.0).to_byte_array());
let pay_params = PaymentParameters::from_static_invoice(invoice);
let pay_params = PaymentParameters::from_static_invoice(invoice)
.with_user_config_ignoring_fee_limit(*route_params_config);
let mut route_params = RouteParameters::from_payment_params_and_value(pay_params, amount_msat);
route_params.max_total_routing_fee_msat = *max_total_routing_fee_msat;
route_params.max_total_routing_fee_msat = route_params_config.max_total_routing_fee_msat;
if let Err(()) = onion_utils::set_max_path_length(
&mut route_params, &RecipientOnionFields::spontaneous_empty(), Some(keysend_preimage),
@ -1710,7 +1713,7 @@ impl OutboundPayments {
#[cfg(feature = "dnssec")]
pub(super) fn add_new_awaiting_offer(
&self, payment_id: PaymentId, expiration: StaleExpiration, retry_strategy: Retry,
max_total_routing_fee_msat: Option<u64>, amount_msats: u64,
route_params_config: RouteParametersConfig, amount_msats: u64,
) -> Result<(), ()> {
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
match pending_outbounds.entry(payment_id) {
@ -1719,7 +1722,7 @@ impl OutboundPayments {
entry.insert(PendingOutboundPayment::AwaitingOffer {
expiration,
retry_strategy,
max_total_routing_fee_msat,
route_params_config,
amount_msats,
});
@ -1746,12 +1749,12 @@ impl OutboundPayments {
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
hash_map::Entry::Occupied(entry) => match entry.get() {
PendingOutboundPayment::AwaitingOffer {
expiration, retry_strategy, max_total_routing_fee_msat, ..
expiration, retry_strategy, route_params_config, ..
} => {
let mut new_val = PendingOutboundPayment::AwaitingInvoice {
expiration: *expiration,
retry_strategy: *retry_strategy,
max_total_routing_fee_msat: *max_total_routing_fee_msat,
route_params_config: *route_params_config,
retryable_invoice_request,
};
core::mem::swap(&mut new_val, entry.into_mut());
@ -1765,7 +1768,7 @@ 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>, retryable_invoice_request: Option<RetryableInvoiceRequest>
route_params_config: RouteParametersConfig, retryable_invoice_request: Option<RetryableInvoiceRequest>
) -> Result<(), ()> {
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
match pending_outbounds.entry(payment_id) {
@ -1777,7 +1780,7 @@ impl OutboundPayments {
entry.insert(PendingOutboundPayment::AwaitingInvoice {
expiration,
retry_strategy,
max_total_routing_fee_msat,
route_params_config,
retryable_invoice_request,
});
@ -2411,13 +2414,35 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
(5, AwaitingInvoice) => {
(0, expiration, required),
(2, retry_strategy, required),
(4, max_total_routing_fee_msat, option),
(4, _max_total_routing_fee_msat, (legacy, u64,
|us: &PendingOutboundPayment| match us {
PendingOutboundPayment::AwaitingInvoice { route_params_config, .. } => route_params_config.max_total_routing_fee_msat,
_ => None,
}
)),
(5, retryable_invoice_request, option),
(7, route_params_config, (default_value, (
_max_total_routing_fee_msat.map_or(
RouteParametersConfig::default(),
|fee_msat| RouteParametersConfig::default().with_max_total_routing_fee_msat(fee_msat)
)
))),
},
(7, InvoiceReceived) => {
(0, payment_hash, required),
(2, retry_strategy, required),
(4, max_total_routing_fee_msat, option),
(4, _max_total_routing_fee_msat, (legacy, u64,
|us: &PendingOutboundPayment| match us {
PendingOutboundPayment::InvoiceReceived { route_params_config, .. } => route_params_config.max_total_routing_fee_msat,
_ => None,
}
)),
(5, route_params_config, (default_value, (
_max_total_routing_fee_msat.map_or(
RouteParametersConfig::default(),
|fee_msat| RouteParametersConfig::default().with_max_total_routing_fee_msat(fee_msat)
)
))),
},
// Added in 0.1. Prior versions will drop these outbounds on downgrade, which is safe because no
// HTLCs are in-flight.
@ -2433,7 +2458,18 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
(11, AwaitingOffer) => {
(0, expiration, required),
(2, retry_strategy, required),
(4, max_total_routing_fee_msat, option),
(4, _max_total_routing_fee_msat, (legacy, u64,
|us: &PendingOutboundPayment| match us {
PendingOutboundPayment::AwaitingOffer { route_params_config, .. } => route_params_config.max_total_routing_fee_msat,
_ => None,
}
)),
(5, route_params_config, (default_value, (
_max_total_routing_fee_msat.map_or(
RouteParametersConfig::default(),
|fee_msat| RouteParametersConfig::default().with_max_total_routing_fee_msat(fee_msat)
)
))),
(6, amount_msats, required),
},
);
@ -2459,7 +2495,7 @@ mod tests {
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::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters, RouteParametersConfig};
use crate::sync::{Arc, Mutex, RwLock};
use crate::util::errors::APIError;
use crate::util::hash_tables::new_hash_map;
@ -2670,7 +2706,7 @@ mod tests {
assert!(!outbound_payments.has_pending_payments());
assert!(
outbound_payments.add_new_awaiting_invoice(
payment_id, expiration, Retry::Attempts(0), None, None,
payment_id, expiration, Retry::Attempts(0), RouteParametersConfig::default(), None,
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@ -2700,14 +2736,14 @@ mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
payment_id, expiration, Retry::Attempts(0), None, None,
payment_id, expiration, Retry::Attempts(0), RouteParametersConfig::default(), None,
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
assert!(
outbound_payments.add_new_awaiting_invoice(
payment_id, expiration, Retry::Attempts(0), None, None,
payment_id, expiration, Retry::Attempts(0), RouteParametersConfig::default(), None,
).is_err()
);
}
@ -2723,7 +2759,7 @@ mod tests {
assert!(!outbound_payments.has_pending_payments());
assert!(
outbound_payments.add_new_awaiting_invoice(
payment_id, expiration, Retry::Attempts(0), None, None,
payment_id, expiration, Retry::Attempts(0), RouteParametersConfig::default(), None,
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@ -2753,14 +2789,14 @@ mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
payment_id, expiration, Retry::Attempts(0), None, None,
payment_id, expiration, Retry::Attempts(0), RouteParametersConfig::default(), None,
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
assert!(
outbound_payments.add_new_awaiting_invoice(
payment_id, expiration, Retry::Attempts(0), None, None,
payment_id, expiration, Retry::Attempts(0), RouteParametersConfig::default(), None,
).is_err()
);
}
@ -2775,7 +2811,7 @@ mod tests {
assert!(!outbound_payments.has_pending_payments());
assert!(
outbound_payments.add_new_awaiting_invoice(
payment_id, expiration, Retry::Attempts(0), None, None,
payment_id, expiration, Retry::Attempts(0), RouteParametersConfig::default(), None,
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@ -2813,7 +2849,7 @@ mod tests {
assert!(
outbound_payments.add_new_awaiting_invoice(
payment_id, expiration, Retry::Attempts(0), None, None,
payment_id, expiration, Retry::Attempts(0), RouteParametersConfig::default(), None,
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@ -2874,10 +2910,12 @@ mod tests {
.build().unwrap()
.sign(recipient_sign).unwrap();
let route_params_config = RouteParametersConfig::default().with_max_total_routing_fee_msat(invoice.amount_msats() / 100 + 50_000);
assert!(
outbound_payments.add_new_awaiting_invoice(
payment_id, expiration, Retry::Attempts(0),
Some(invoice.amount_msats() / 100 + 50_000), None,
route_params_config, None,
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
@ -2974,9 +3012,11 @@ mod tests {
assert!(!outbound_payments.has_pending_payments());
assert!(pending_events.lock().unwrap().is_empty());
let route_params_config = RouteParametersConfig::default().with_max_total_routing_fee_msat(1234);
assert!(
outbound_payments.add_new_awaiting_invoice(
payment_id, expiration, Retry::Attempts(0), Some(1234), None,
payment_id, expiration, Retry::Attempts(0), route_params_config, None,
).is_ok()
);
assert!(outbound_payments.has_pending_payments());

View file

@ -943,6 +943,21 @@ impl PaymentParameters {
}
}
/// Updates the parameters with the given route parameters configuration.
///
/// Note:
/// We *do not* apply `max_total_routing_fee_msat` here, as it is unique to each route.
/// Instead, we apply only the parameters that are common across multiple route-finding sessions
/// for a payment across retries.
pub(crate) fn with_user_config_ignoring_fee_limit(self, params_config: RouteParametersConfig) -> Self {
Self {
max_total_cltv_expiry_delta: params_config.max_total_cltv_expiry_delta,
max_path_count: params_config.max_path_count,
max_channel_saturation_power_of_half: params_config.max_channel_saturation_power_of_half,
..self
}
}
/// Includes the payee's features. Errors if the parameters were not initialized with
/// [`PaymentParameters::from_bolt12_invoice`].
///
@ -1030,6 +1045,93 @@ impl PaymentParameters {
}
}
/// A struct for configuring parameters for routing the payment.
#[derive(Clone, Copy)]
pub struct RouteParametersConfig {
/// The maximum total fees, in millisatoshi, that may accrue during route finding.
///
/// This limit also applies to the total fees that may arise while retrying failed payment
/// paths.
///
/// Note that values below a few sats may result in some paths being spuriously ignored.
///
/// Defaults to 1% of the payment amount + 50 sats
pub max_total_routing_fee_msat: Option<u64>,
/// The maximum total CLTV delta we accept for the route.
/// Defaults to [`DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA`].
pub max_total_cltv_expiry_delta: u32,
/// The maximum number of paths that may be used by (MPP) payments.
/// Defaults to [`DEFAULT_MAX_PATH_COUNT`].
pub max_path_count: u8,
/// Selects the maximum share of a channel's total capacity which will be sent over a channel,
/// as a power of 1/2. A higher value prefers to send the payment using more MPP parts whereas
/// a lower value prefers to send larger MPP parts, potentially saturating channels and
/// increasing failure probability for those paths.
///
/// Note that this restriction will be relaxed during pathfinding after paths which meet this
/// restriction have been found. While paths which meet this criteria will be searched for, it
/// is ultimately up to the scorer to select them over other paths.
///
/// A value of 0 will allow payments up to and including a channel's total announced usable
/// capacity, a value of one will only use up to half its capacity, two 1/4, etc.
///
/// Default value: 2
pub max_channel_saturation_power_of_half: u8,
}
impl_writeable_tlv_based!(RouteParametersConfig, {
(1, max_total_routing_fee_msat, option),
(3, max_total_cltv_expiry_delta, required),
(5, max_path_count, required),
(7, max_channel_saturation_power_of_half, required),
});
impl RouteParametersConfig {
/// Set the maximum total fees, in millisatoshi, that may accrue during route finding.
///
/// This is not exported to bindings users since bindings don't support move semantics
pub fn with_max_total_routing_fee_msat(self, fee_msat: u64) -> Self {
Self { max_total_routing_fee_msat: Some(fee_msat), ..self }
}
/// Includes a limit for the total CLTV expiry delta which is considered during routing
///
/// This is not exported to bindings users since bindings don't support move semantics
pub fn with_max_total_cltv_expiry_delta(self, max_total_cltv_expiry_delta: u32) -> Self {
Self { max_total_cltv_expiry_delta, ..self }
}
/// Includes a limit for the maximum number of payment paths that may be used.
///
/// This is not exported to bindings users since bindings don't support move semantics
pub fn with_max_path_count(self, max_path_count: u8) -> Self {
Self { max_path_count, ..self }
}
/// Includes a limit for the maximum share of a channel's total capacity that can be sent over, as
/// a power of 1/2. See [`PaymentParameters::max_channel_saturation_power_of_half`].
///
/// This is not exported to bindings users since bindings don't support move semantics
pub fn with_max_channel_saturation_power_of_half(self, max_channel_saturation_power_of_half: u8) -> Self {
Self { max_channel_saturation_power_of_half, ..self }
}
}
impl Default for RouteParametersConfig {
/// Initates an new set of route parameter configs with default parameters.
fn default() -> Self {
Self {
max_total_routing_fee_msat: None,
max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
max_path_count: DEFAULT_MAX_PATH_COUNT,
max_channel_saturation_power_of_half: DEFAULT_MAX_CHANNEL_SATURATION_POW_HALF,
}
}
}
/// The recipient of a payment, differing based on whether they've hidden their identity with route
/// blinding.
#[derive(Clone, Debug, Hash, PartialEq, Eq)]

View file

@ -40,7 +40,7 @@ macro_rules! _encode_tlv {
}
};
($stream: expr, $optional_type: expr, $optional_field: expr, (legacy, $fieldty: ty, $write: expr) $(, $self: ident)?) => {
$crate::_encode_tlv!($stream, $optional_type, $write($($self)?), option);
$crate::_encode_tlv!($stream, $optional_type, { let value: Option<$fieldty> = $write($($self)?); value }, option);
};
($stream: expr, $type: expr, $field: expr, optional_vec $(, $self: ident)?) => {
if !$field.is_empty() {