Skip to content

Commit a9bb2e9

Browse files
authored
Merge pull request #156 from tnull/2023-08-refuse-large-probes
Skip channels used for probing based on available liquidity
2 parents 5b7019e + 758f91b commit a9bb2e9

File tree

2 files changed

+51
-16
lines changed

2 files changed

+51
-16
lines changed

bindings/ldk_node.udl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ dictionary Config {
1111
u64 onchain_wallet_sync_interval_secs = 80;
1212
u64 wallet_sync_interval_secs = 30;
1313
u64 fee_rate_cache_update_interval_secs = 600;
14-
LogLevel log_level = "Debug";
1514
sequence<PublicKey> trusted_peers_0conf = [];
15+
u64 probing_liquidity_limit_multiplier = 3;
16+
LogLevel log_level = "Debug";
1617
};
1718

1819
interface Builder {

src/lib.rs

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ use bitcoin::{Address, Txid};
149149

150150
use rand::Rng;
151151

152+
use std::collections::HashMap;
152153
use std::default::Default;
153154
use std::net::ToSocketAddrs;
154155
use std::sync::{Arc, Mutex, RwLock};
@@ -164,6 +165,7 @@ const DEFAULT_CLTV_EXPIRY_DELTA: u32 = 144;
164165
const DEFAULT_BDK_WALLET_SYNC_INTERVAL_SECS: u64 = 80;
165166
const DEFAULT_LDK_WALLET_SYNC_INTERVAL_SECS: u64 = 30;
166167
const DEFAULT_FEE_RATE_CACHE_UPDATE_INTERVAL_SECS: u64 = 60 * 10;
168+
const DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER: u64 = 3;
167169
const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Debug;
168170

169171
// The 'stop gap' parameter used by BDK's wallet sync. This seems to configure the threshold
@@ -210,6 +212,7 @@ const WALLET_KEYS_SEED_LEN: usize = 64;
210212
/// | `wallet_sync_interval_secs` | 30 |
211213
/// | `fee_rate_cache_update_interval_secs` | 600 |
212214
/// | `trusted_peers_0conf` | [] |
215+
/// | `probing_liquidity_limit_multiplier` | 3 |
213216
/// | `log_level` | Debug |
214217
///
215218
pub struct Config {
@@ -243,6 +246,11 @@ pub struct Config {
243246
/// funding transaction ends up never being confirmed on-chain. Zero-confirmation channels
244247
/// should therefore only be accepted from trusted peers.
245248
pub trusted_peers_0conf: Vec<PublicKey>,
249+
/// The liquidity factor by which we filter the outgoing channels used for sending probes.
250+
///
251+
/// Channels with available liquidity less than the required amount times this value won't be
252+
/// used to send pre-flight probes.
253+
pub probing_liquidity_limit_multiplier: u64,
246254
/// The level at which we log messages.
247255
///
248256
/// Any messages below this level will be excluded from the logs.
@@ -261,6 +269,7 @@ impl Default for Config {
261269
wallet_sync_interval_secs: DEFAULT_LDK_WALLET_SYNC_INTERVAL_SECS,
262270
fee_rate_cache_update_interval_secs: DEFAULT_FEE_RATE_CACHE_UPDATE_INTERVAL_SECS,
263271
trusted_peers_0conf: Vec::new(),
272+
probing_liquidity_limit_multiplier: DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER,
264273
log_level: DEFAULT_LOG_LEVEL,
265274
}
266275
}
@@ -1302,10 +1311,14 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
13021311
/// This may be used to send "pre-flight" probes, i.e., to train our scorer before conducting
13031312
/// the actual payment. Note this is only useful if there likely is sufficient time for the
13041313
/// probe to settle before sending out the actual payment, e.g., when waiting for user
1305-
/// confirmation in a wallet UI. Otherwise, there is a chance the probe could take up some
1306-
/// liquidity needed to complete the actual payment. Users should therefore be cautious and
1307-
/// might avoid sending probes if liquidity is scarce and/or they don't expect the probe to
1308-
/// return before they send the payment.
1314+
/// confirmation in a wallet UI.
1315+
///
1316+
/// Otherwise, there is a chance the probe could take up some liquidity needed to complete the
1317+
/// actual payment. Users should therefore be cautious and might avoid sending probes if
1318+
/// liquidity is scarce and/or they don't expect the probe to return before they send the
1319+
/// payment. To mitigate this issue, channels with available liquidity less than the required
1320+
/// amount times [`Config::probing_liquidity_limit_multiplier`] won't be used to send
1321+
/// pre-flight probes.
13091322
pub fn send_payment_probe(&self, invoice: &Bolt11Invoice) -> Result<(), Error> {
13101323
let rt_lock = self.runtime.read().unwrap();
13111324
if rt_lock.is_none() {
@@ -1343,10 +1356,14 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
13431356
/// This may be used to send "pre-flight" probes, i.e., to train our scorer before conducting
13441357
/// the actual payment. Note this is only useful if there likely is sufficient time for the
13451358
/// probe to settle before sending out the actual payment, e.g., when waiting for user
1346-
/// confirmation in a wallet UI. Otherwise, there is a chance the probe could take up some
1347-
/// liquidity needed to complete the actual payment. Users should therefore be cautious and
1348-
/// might avoid sending probes if liquidity is scarce and/or they don't expect the probe to
1349-
/// return before they send the payment.
1359+
/// confirmation in a wallet UI.
1360+
///
1361+
/// Otherwise, there is a chance the probe could take up some liquidity needed to complete the
1362+
/// actual payment. Users should therefore be cautious and might avoid sending probes if
1363+
/// liquidity is scarce and/or they don't expect the probe to return before they send the
1364+
/// payment. To mitigate this issue, channels with available liquidity less than the required
1365+
/// amount times [`Config::probing_liquidity_limit_multiplier`] won't be used to send
1366+
/// pre-flight probes.
13501367
pub fn send_spontaneous_payment_probe(
13511368
&self, amount_msat: u64, node_id: PublicKey,
13521369
) -> Result<(), Error> {
@@ -1365,22 +1382,19 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
13651382

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

13711389
let route = self
13721390
.router
1373-
.find_route(
1374-
&payer,
1375-
&route_params,
1376-
Some(&first_hops.iter().collect::<Vec<_>>()),
1377-
inflight_htlcs,
1378-
)
1391+
.find_route(&payer, &route_params, Some(&first_hops), inflight_htlcs)
13791392
.map_err(|e| {
13801393
log_error!(self.logger, "Failed to find path for payment probe: {:?}", e);
13811394
Error::ProbeSendingFailed
13821395
})?;
13831396

1397+
let mut used_liquidity_map = HashMap::with_capacity(first_hops.len());
13841398
for path in route.paths {
13851399
if path.hops.len() + path.blinded_tail.as_ref().map_or(0, |t| t.hops.len()) < 2 {
13861400
log_debug!(
@@ -1390,6 +1404,26 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
13901404
continue;
13911405
}
13921406

1407+
if let Some(first_path_hop) = path.hops.first() {
1408+
if let Some(first_hop) = first_hops.iter().find(|h| {
1409+
h.get_outbound_payment_scid() == Some(first_path_hop.short_channel_id)
1410+
}) {
1411+
let path_value = path.final_value_msat() + path.fee_msat();
1412+
let used_liquidity =
1413+
used_liquidity_map.entry(first_path_hop.short_channel_id).or_insert(0);
1414+
1415+
if first_hop.next_outbound_htlc_limit_msat
1416+
< (*used_liquidity + path_value)
1417+
* self.config.probing_liquidity_limit_multiplier
1418+
{
1419+
log_debug!(self.logger, "Skipped sending payment probe to avoid putting channel {} under the liquidity limit.", first_path_hop.short_channel_id);
1420+
continue;
1421+
} else {
1422+
*used_liquidity += path_value;
1423+
}
1424+
}
1425+
}
1426+
13931427
self.channel_manager.send_probe(path).map_err(|e| {
13941428
log_error!(self.logger, "Failed to send payment probe: {:?}", e);
13951429
Error::ProbeSendingFailed

0 commit comments

Comments
 (0)