Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 47 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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 |
/// | `tor_config` | None |
///
Expand Down Expand Up @@ -189,6 +189,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<AnchorChannelsConfig>,
/// 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,
Expand Down Expand Up @@ -216,6 +222,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(),
tor_config: None,
route_parameters: None,
node_alias: None,
Expand Down Expand Up @@ -638,6 +645,43 @@ 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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than duplicating the object here, can't we reuse LDK's Retry?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No we can't re-export it to the bindings because of the Duration inside.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, then it would be likely preferable to expose a remote type for Duration, too, rather than duplicating a lot of code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we also cannot because Retry is a tuple enum Retry::Attempts(u64) which isn't supported by uniffi. Would need to be like Retry::Attempts { attmepts: u64 } for us to be able to.

I still changed to re-export duration so it is less duplication

Copy link
Collaborator

@tnull tnull Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we also cannot because Retry is a tuple enum Retry::Attempts(u64) which isn't supported by uniffi.

They are supported by now, but only in proc macros (see https://mozilla.github.io/uniffi-rs/0.29/udl/enumerations.html). Given it's a remote type you'll want to do something like we did with ClosureReason here:

#[uniffi::remote(Enum)]

I.e., use Retry in the normal Rust code, and then in src/ffi/types.rs only duplicate the upstream enum with the #[uniffi::remote(Enum)] tag. I think that could work (note that the duplicated code doesn't actually be imported anywhere separately, and will just be used by the bindings generator).

/// 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 after which we stop retrying.
timeout: Duration,
},
}

impl Default for PaymentRetryStrategy {
fn default() -> Self {
Self::Timeout { timeout: Duration::from_secs(DEFAULT_PAYMENT_RETRY_TIMEOUT_SECS) }
}
}

impl From<PaymentRetryStrategy> for Retry {
fn from(value: PaymentRetryStrategy) -> Self {
match value {
PaymentRetryStrategy::Attempts { max_attempts } => Retry::Attempts(max_attempts),
PaymentRetryStrategy::Timeout { timeout } => Retry::Timeout(timeout),
}
}
}

#[cfg(test)]
mod tests {
use std::str::FromStr;
Expand Down
8 changes: 4 additions & 4 deletions src/payment/bolt11.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
9 changes: 4 additions & 5 deletions src/payment/bolt12.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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};
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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();

Expand Down
6 changes: 3 additions & 3 deletions src/payment/spontaneous.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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);
Expand Down
Loading