diff --git a/lightning-dns-resolver/src/lib.rs b/lightning-dns-resolver/src/lib.rs index 8f855cb5fb7..a0ab7483f26 100644 --- a/lightning-dns-resolver/src/lib.rs +++ b/lightning-dns-resolver/src/lib.rs @@ -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(); diff --git a/lightning/src/ln/async_payments_tests.rs b/lightning/src/ln/async_payments_tests.rs index 8d18938272c..ed9b4b88ec5 100644 --- a/lightning/src/ln/async_payments_tests.rs +++ b/lightning/src/ln/async_payments_tests.rs @@ -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( 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; diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 5342cc649d3..fd630065586 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -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; @@ -2096,6 +2096,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(channel_manager: T) -> Result<(), Bolt12SemanticError> { /// # let channel_manager = channel_manager.get_cm(); @@ -2143,15 +2144,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( /// # channel_manager: T, offer: &Offer, quantity: Option, amount_msats: Option, -/// # payer_note: Option, retry: Retry, max_total_routing_fee_msat: Option +/// # payer_note: Option, 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), @@ -2199,16 +2201,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( /// # channel_manager: T, amount_msats: u64, absolute_expiry: Duration, retry: Retry, -/// # max_total_routing_fee_msat: Option +/// # 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 @@ -9983,7 +9986,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 + 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; @@ -10008,7 +10011,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)?; @@ -10184,7 +10187,7 @@ where pub fn pay_for_offer( &self, offer: &Offer, quantity: Option, amount_msats: Option, payer_note: Option, payment_id: PaymentId, retry_strategy: Retry, - max_total_routing_fee_msat: Option + 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); @@ -10195,7 +10198,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) @@ -10428,14 +10431,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, + retry_strategy: Retry, route_params_config: RouteParametersConfig, dns_resolvers: Vec, ) -> 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))) diff --git a/lightning/src/ln/max_payment_path_len_tests.rs b/lightning/src/ln/max_payment_path_len_tests.rs index 98c3a062bc4..fb1004062a0 100644 --- a/lightning/src/ln/max_payment_path_len_tests.rs +++ b/lightning/src/ln/max_payment_path_len_tests.rs @@ -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); diff --git a/lightning/src/ln/offers_tests.rs b/lightning/src/ln/offers_tests.rs index 803441f09fe..1a7a63a0681 100644 --- a/lightning/src/ln/offers_tests.rs +++ b/lightning/src/ln/offers_tests.rs @@ -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(); diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index eb57c8a94c4..f56795cf56a 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -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, + 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, + route_params_config: RouteParametersConfig, retryable_invoice_request: Option }, // 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, + // 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, 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, retryable_invoice_request: Option + route_params_config: RouteParametersConfig, retryable_invoice_request: Option ) -> 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()); diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 59e4dc7decd..017496f26cb 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -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, + + /// 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)] diff --git a/lightning/src/util/ser_macros.rs b/lightning/src/util/ser_macros.rs index e30f793d4d6..9d9e01b99d7 100644 --- a/lightning/src/util/ser_macros.rs +++ b/lightning/src/util/ser_macros.rs @@ -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() {