Skip to content

Commit

Permalink
Skip channels used for probing based on available liquidity
Browse files Browse the repository at this point in the history
As pre-flight probes might take up some of the available liquidity, we
here introduce that channels whose available liquidity is less than the
required amount times `Config::probing_liquidity_limit_multiplier` won't
be used to send pre-flight probes.
  • Loading branch information
tnull committed Aug 16, 2023
1 parent 5b7019e commit 9c92be3
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 8 deletions.
3 changes: 2 additions & 1 deletion bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ dictionary Config {
u64 onchain_wallet_sync_interval_secs = 80;
u64 wallet_sync_interval_secs = 30;
u64 fee_rate_cache_update_interval_secs = 600;
LogLevel log_level = "Debug";
sequence<PublicKey> trusted_peers_0conf = [];
u64 probing_liquidity_limit_multiplier = 3;
LogLevel log_level = "Debug";
};

interface Builder {
Expand Down
38 changes: 31 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ const DEFAULT_CLTV_EXPIRY_DELTA: u32 = 144;
const DEFAULT_BDK_WALLET_SYNC_INTERVAL_SECS: u64 = 80;
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_LOG_LEVEL: LogLevel = LogLevel::Debug;

// The 'stop gap' parameter used by BDK's wallet sync. This seems to configure the threshold
Expand Down Expand Up @@ -210,6 +211,7 @@ const WALLET_KEYS_SEED_LEN: usize = 64;
/// | `wallet_sync_interval_secs` | 30 |
/// | `fee_rate_cache_update_interval_secs` | 600 |
/// | `trusted_peers_0conf` | [] |
/// | `probing_liquidity_limit_multiplier` | 3 |
/// | `log_level` | Debug |
///
pub struct Config {
Expand Down Expand Up @@ -243,6 +245,8 @@ pub struct Config {
/// funding transaction ends up never being confirmed on-chain. Zero-confirmation channels
/// should therefore only be accepted from trusted peers.
pub trusted_peers_0conf: Vec<PublicKey>,
/// The liquidity factor by which we filter the outgoing channels used for sending probes.
pub probing_liquidity_limit_multiplier: u64,
/// The level at which we log messages.
///
/// Any messages below this level will be excluded from the logs.
Expand All @@ -261,6 +265,7 @@ impl Default for Config {
wallet_sync_interval_secs: DEFAULT_LDK_WALLET_SYNC_INTERVAL_SECS,
fee_rate_cache_update_interval_secs: DEFAULT_FEE_RATE_CACHE_UPDATE_INTERVAL_SECS,
trusted_peers_0conf: Vec::new(),
probing_liquidity_limit_multiplier: DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER,
log_level: DEFAULT_LOG_LEVEL,
}
}
Expand Down Expand Up @@ -1306,6 +1311,10 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
/// liquidity needed to complete the actual payment. Users should therefore be cautious and
/// might avoid sending probes if liquidity is scarce and/or they don't expect the probe to
/// return before they send the payment.
///
/// To mitigate this issue, channels whose available liquidity is less than the required amount
/// times [`Config::probing_liquidity_limit_multiplier`] won't be used to send pre-flight
/// probes.
pub fn send_payment_probe(&self, invoice: &Bolt11Invoice) -> Result<(), Error> {
let rt_lock = self.runtime.read().unwrap();
if rt_lock.is_none() {
Expand Down Expand Up @@ -1347,6 +1356,10 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
/// liquidity needed to complete the actual payment. Users should therefore be cautious and
/// might avoid sending probes if liquidity is scarce and/or they don't expect the probe to
/// return before they send the payment.
///
/// To mitigate this issue, channels whose available liquidity is less than the required amount
/// times [`Config::probing_liquidity_limit_multiplier`] won't be used to send pre-flight
/// probes.
pub fn send_spontaneous_payment_probe(
&self, amount_msat: u64, node_id: PublicKey,
) -> Result<(), Error> {
Expand All @@ -1365,17 +1378,13 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {

fn send_payment_probe_internal(&self, route_params: RouteParameters) -> Result<(), Error> {
let payer = self.channel_manager.get_our_node_id();
let first_hops = self.channel_manager.list_usable_channels();
let usable_channels = self.channel_manager.list_usable_channels();
let first_hops = usable_channels.iter().collect::<Vec<_>>();
let inflight_htlcs = self.channel_manager.compute_inflight_htlcs();

let route = self
.router
.find_route(
&payer,
&route_params,
Some(&first_hops.iter().collect::<Vec<_>>()),
inflight_htlcs,
)
.find_route(&payer, &route_params, Some(&first_hops), inflight_htlcs)
.map_err(|e| {
log_error!(self.logger, "Failed to find path for payment probe: {:?}", e);
Error::ProbeSendingFailed
Expand All @@ -1390,6 +1399,21 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
continue;
}

if let Some(first_route_hop) = path.hops.first() {
if let Some(first_hop) = first_hops
.iter()
.find(|h| h.short_channel_id == Some(first_route_hop.short_channel_id))
{
let path_value = path.final_value_msat() + path.fee_msat();
if first_hop.next_outbound_htlc_limit_msat
< path_value * self.config.probing_liquidity_limit_multiplier
{
log_debug!(self.logger, "Skipped sending payment probe to avoid putting channel {} under the liquidity limit.", first_route_hop.short_channel_id);
continue;
}
}
}

self.channel_manager.send_probe(path).map_err(|e| {
log_error!(self.logger, "Failed to send payment probe: {:?}", e);
Error::ProbeSendingFailed
Expand Down

0 comments on commit 9c92be3

Please sign in to comment.