diff --git a/src/config.rs b/src/config.rs index 96a6f49d9..7f96af03e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,6 +13,7 @@ use std::time::Duration; use bitcoin::secp256k1::PublicKey; use bitcoin::Network; use lightning::ln::msgs::SocketAddress; +use lightning::ln::outbound_payment::Retry; use lightning::routing::gossip::NodeAlias; use lightning::routing::router::RouteParametersConfig; use lightning::util::config::{ @@ -28,6 +29,7 @@ const DEFAULT_LDK_WALLET_SYNC_INTERVAL_SECS: u64 = 30; const DEFAULT_FEE_RATE_CACHE_UPDATE_INTERVAL_SECS: u64 = 60 * 10; const DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER: u64 = 3; const DEFAULT_ANCHOR_PER_CHANNEL_RESERVE_SATS: u64 = 25_000; +const DEFAULT_PAYMENT_RETRY_TIMEOUT_SECS: u64 = 10; // The default timeout after which we abort a wallet syncing operation. const DEFAULT_BDK_WALLET_SYNC_TIMEOUT_SECS: u64 = 60; @@ -63,9 +65,6 @@ pub(crate) const BDK_CLIENT_STOP_GAP: usize = 20; // The number of concurrent requests made against the API provider. pub(crate) const BDK_CLIENT_CONCURRENCY: usize = 4; -// The timeout after which we abandon retrying failed payments. -pub(crate) const LDK_PAYMENT_RETRY_TIMEOUT: Duration = Duration::from_secs(10); - // The time in-between peer reconnection attempts. pub(crate) const PEER_RECONNECTION_INTERVAL: Duration = Duration::from_secs(60); @@ -131,6 +130,7 @@ pub(crate) const LNURL_AUTH_TIMEOUT_SECS: u64 = 15; /// | `probing_liquidity_limit_multiplier` | 3 | /// | `log_level` | Debug | /// | `anchor_channels_config` | Some(..) | +/// | `payment_retry_strategy` | Timeout(10s) | /// | `route_parameters` | None | /// /// See [`AnchorChannelsConfig`] and [`RouteParametersConfig`] for more information regarding their @@ -188,6 +188,12 @@ pub struct Config { /// closure. We *will* however still try to get the Anchor spending transactions confirmed /// on-chain with the funds available. pub anchor_channels_config: Option, + /// The strategy used when retrying failed payments. + /// + /// When a payment fails to route, LDK will automatically retry according to this strategy. + /// + /// See [`PaymentRetryStrategy`] for available options. + pub payment_retry_strategy: PaymentRetryStrategy, /// Configuration options for payment routing and pathfinding. /// /// Setting the [`RouteParametersConfig`] provides flexibility to customize how payments are routed, @@ -208,6 +214,7 @@ impl Default for Config { trusted_peers_0conf: Vec::new(), probing_liquidity_limit_multiplier: DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER, anchor_channels_config: Some(AnchorChannelsConfig::default()), + payment_retry_strategy: PaymentRetryStrategy::default(), route_parameters: None, node_alias: None, } @@ -619,6 +626,45 @@ pub enum AsyncPaymentsRole { Server, } +/// Strategies available to retry payment path failures. +/// +/// See [`Retry`] for details. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))] +pub enum PaymentRetryStrategy { + /// Max number of attempts to retry payment. + /// + /// Please refer to [`Retry`] for further details. + Attempts { + /// The maximum number of payment attempts. + max_attempts: u32, + }, + /// Time elapsed before abandoning retries for a payment. + /// + /// Please refer to [`Retry`] for further details. + Timeout { + /// The timeout in seconds after which we stop retrying. + timeout_secs: u64, + }, +} + +impl Default for PaymentRetryStrategy { + fn default() -> Self { + Self::Timeout { timeout_secs: DEFAULT_PAYMENT_RETRY_TIMEOUT_SECS } + } +} + +impl From for Retry { + fn from(value: PaymentRetryStrategy) -> Self { + match value { + PaymentRetryStrategy::Attempts { max_attempts } => Retry::Attempts(max_attempts), + PaymentRetryStrategy::Timeout { timeout_secs } => { + Retry::Timeout(Duration::from_secs(timeout_secs)) + }, + } + } +} + #[cfg(test)] mod tests { use std::str::FromStr; diff --git a/src/payment/bolt11.rs b/src/payment/bolt11.rs index f2857e814..e7c2b0682 100644 --- a/src/payment/bolt11.rs +++ b/src/payment/bolt11.rs @@ -16,14 +16,14 @@ use bitcoin::hashes::Hash; use lightning::ln::channelmanager::{ Bolt11InvoiceParameters, OptionalBolt11PaymentParams, PaymentId, }; -use lightning::ln::outbound_payment::{Bolt11PaymentError, Retry, RetryableSendFailure}; +use lightning::ln::outbound_payment::{Bolt11PaymentError, RetryableSendFailure}; use lightning::routing::router::{PaymentParameters, RouteParameters, RouteParametersConfig}; use lightning_invoice::{ Bolt11Invoice as LdkBolt11Invoice, Bolt11InvoiceDescription as LdkBolt11InvoiceDescription, }; use lightning_types::payment::{PaymentHash, PaymentPreimage}; -use crate::config::{Config, LDK_PAYMENT_RETRY_TIMEOUT}; +use crate::config::Config; use crate::connection::ConnectionManager; use crate::data_store::DataStoreUpdateResult; use crate::error::Error; @@ -259,7 +259,7 @@ impl Bolt11Payment { let route_params_config = route_parameters.or(self.config.route_parameters).unwrap_or_default(); - let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT); + let retry_strategy = self.config.payment_retry_strategy.into(); let payment_secret = Some(*invoice.payment_secret()); let optional_params = OptionalBolt11PaymentParams { @@ -369,7 +369,7 @@ impl Bolt11Payment { let route_params_config = route_parameters.or(self.config.route_parameters).unwrap_or_default(); - let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT); + let retry_strategy = self.config.payment_retry_strategy.into(); let payment_secret = Some(*invoice.payment_secret()); let optional_params = OptionalBolt11PaymentParams { diff --git a/src/payment/bolt12.rs b/src/payment/bolt12.rs index 980e20696..ac1f1a671 100644 --- a/src/payment/bolt12.rs +++ b/src/payment/bolt12.rs @@ -15,7 +15,6 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use lightning::blinded_path::message::BlindedMessagePath; use lightning::ln::channelmanager::{OptionalOfferPaymentParams, PaymentId}; -use lightning::ln::outbound_payment::Retry; use lightning::offers::offer::{Amount, Offer as LdkOffer, OfferFromHrn, Quantity}; use lightning::offers::parse::Bolt12SemanticError; use lightning::routing::router::RouteParametersConfig; @@ -24,7 +23,7 @@ use lightning::sign::EntropySource; use lightning::util::ser::{Readable, Writeable}; use lightning_types::string::UntrustedString; -use crate::config::{AsyncPaymentsRole, Config, LDK_PAYMENT_RETRY_TIMEOUT}; +use crate::config::{AsyncPaymentsRole, Config}; use crate::error::Error; use crate::ffi::{maybe_deref, maybe_wrap}; use crate::logger::{log_error, log_info, LdkLogger, Logger}; @@ -96,7 +95,7 @@ impl Bolt12Payment { let offer = maybe_deref(offer); let payment_id = PaymentId(self.keys_manager.get_secure_random_bytes()); - let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT); + let retry_strategy = self.config.payment_retry_strategy.into(); let route_parameters = route_parameters.or(self.config.route_parameters).unwrap_or_default(); @@ -269,7 +268,7 @@ impl Bolt12Payment { let offer = maybe_deref(offer); let payment_id = PaymentId(self.keys_manager.get_secure_random_bytes()); - let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT); + let retry_strategy = self.config.payment_retry_strategy.into(); let route_parameters = route_parameters.or(self.config.route_parameters).unwrap_or_default(); @@ -475,7 +474,7 @@ impl Bolt12Payment { let absolute_expiry = (SystemTime::now() + Duration::from_secs(expiry_secs as u64)) .duration_since(UNIX_EPOCH) .unwrap(); - let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT); + let retry_strategy = self.config.payment_retry_strategy.into(); let route_parameters = route_parameters.or(self.config.route_parameters).unwrap_or_default(); diff --git a/src/payment/spontaneous.rs b/src/payment/spontaneous.rs index 74fa84c0e..3fc19034d 100644 --- a/src/payment/spontaneous.rs +++ b/src/payment/spontaneous.rs @@ -12,13 +12,13 @@ use std::sync::{Arc, RwLock}; use bitcoin::secp256k1::PublicKey; use lightning::ln::channelmanager::PaymentId; use lightning::ln::outbound_payment::{ - RecipientCustomTlvs, RecipientOnionFields, Retry, RetryableSendFailure, + RecipientCustomTlvs, RecipientOnionFields, RetryableSendFailure, }; use lightning::routing::router::{PaymentParameters, RouteParameters, RouteParametersConfig}; use lightning::sign::EntropySource; use lightning_types::payment::{PaymentHash, PaymentPreimage}; -use crate::config::{Config, LDK_PAYMENT_RETRY_TIMEOUT}; +use crate::config::Config; use crate::error::Error; use crate::logger::{log_error, log_info, LdkLogger, Logger}; use crate::payment::store::{PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus}; @@ -113,7 +113,7 @@ impl SpontaneousPayment { recipient_fields, PaymentId(payment_hash.0), route_params, - Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT), + self.config.payment_retry_strategy.into(), ) { Ok(_hash) => { log_info!(self.logger, "Initiated sending {}msat to {}.", amount_msat, node_id);