Skip to content

Commit a727bcf

Browse files
randomloginclaude
andcommitted
Fix probing budget accounting and remove Probe::Destination
The locked_msat budget tracking was broken for Destination probes: send_spontaneous_preflight_probes only returns (PaymentHash, PaymentId) without exposing the actual paths or per-hop amounts. This meant we locked amount_msat at send time but released amount+fees per path in ProbeSuccessful/ProbeFailed events, causing a systematic mismatch. Fix by removing Probe::Destination entirely. Strategies now return a fully constructed Path, and run_prober always uses send_probe(path), locking and releasing the same path.hops.sum(fee_msat) on both sides. HighDegreeStrategy now calls Router::find_route directly and applies the liquidity-limit check itself, mirroring send_preflight_probes. Other fixes in this commit: - Fix RandomStrategy fee calculation: compute proportional fees on the forwarded amount (delivery + downstream fees), not just delivery - Fix HighDegreeStrategy doc - Fix random_range overflow when max - min == u64::MAX - Add doc warning about scorer_channel_liquidity being O(scorer size) - Make probing module public, import objects directly in builder.rs - Reorder EventHandler fields (prober after om_mailbox) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c470da6 commit a727bcf

File tree

7 files changed

+257
-162
lines changed

7 files changed

+257
-162
lines changed

src/builder.rs

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
7575
use crate::message_handler::NodeCustomMessageHandler;
7676
use crate::payment::asynchronous::om_mailbox::OnionMessageMailbox;
7777
use crate::peer_store::PeerStore;
78-
use crate::probing;
78+
use crate::probing::{
79+
HighDegreeStrategy, Prober, ProbingConfig, ProbingStrategy, ProbingStrategyKind, RandomStrategy,
80+
};
7981
use crate::runtime::{Runtime, RuntimeSpawner};
8082
use crate::tx_broadcaster::TransactionBroadcaster;
8183
use crate::types::{
@@ -284,7 +286,7 @@ pub struct NodeBuilder {
284286
runtime_handle: Option<tokio::runtime::Handle>,
285287
pathfinding_scores_sync_config: Option<PathfindingScoresSyncConfig>,
286288
recovery_mode: bool,
287-
probing_config: Option<probing::ProbingConfig>,
289+
probing_config: Option<ProbingConfig>,
288290
}
289291

290292
impl NodeBuilder {
@@ -623,7 +625,7 @@ impl NodeBuilder {
623625

624626
/// Configures background probing.
625627
///
626-
/// Use [`probing::ProbingConfig`] to build the configuration:
628+
/// Use [`ProbingConfig`] to build the configuration:
627629
/// ```ignore
628630
/// use ldk_node::probing::ProbingConfig;
629631
///
@@ -633,7 +635,7 @@ impl NodeBuilder {
633635
/// .build()
634636
/// );
635637
/// ```
636-
pub fn set_probing_config(&mut self, config: probing::ProbingConfig) -> &mut Self {
638+
pub fn set_probing_config(&mut self, config: ProbingConfig) -> &mut Self {
637639
self.probing_config = Some(config);
638640
self
639641
}
@@ -1108,8 +1110,8 @@ impl ArcedNodeBuilder {
11081110

11091111
/// Configures background probing.
11101112
///
1111-
/// See [`probing::ProbingConfig`] for details.
1112-
pub fn set_probing_config(&self, config: probing::ProbingConfig) {
1113+
/// See [`ProbingConfig`] for details.
1114+
pub fn set_probing_config(&self, config: ProbingConfig) {
11131115
self.inner.write().unwrap().set_probing_config(config);
11141116
}
11151117

@@ -1256,9 +1258,9 @@ fn build_with_store_internal(
12561258
gossip_source_config: Option<&GossipSourceConfig>,
12571259
liquidity_source_config: Option<&LiquiditySourceConfig>,
12581260
pathfinding_scores_sync_config: Option<&PathfindingScoresSyncConfig>,
1259-
probing_config: Option<&probing::ProbingConfig>,
1260-
async_payments_role: Option<AsyncPaymentsRole>, recovery_mode: bool, seed_bytes: [u8; 64],
1261-
runtime: Arc<Runtime>, logger: Arc<Logger>, kv_store: Arc<DynStore>,
1261+
probing_config: Option<&ProbingConfig>, async_payments_role: Option<AsyncPaymentsRole>,
1262+
recovery_mode: bool, seed_bytes: [u8; 64], runtime: Arc<Runtime>, logger: Arc<Logger>,
1263+
kv_store: Arc<DynStore>,
12621264
) -> Result<Node, BuildError> {
12631265
optionally_install_rustls_cryptoprovider();
12641266

@@ -2002,33 +2004,33 @@ fn build_with_store_internal(
20022004
}
20032005

20042006
let prober = probing_config.map(|probing_cfg| {
2005-
let strategy: Arc<dyn probing::ProbingStrategy> = match &probing_cfg.kind {
2006-
probing::ProbingStrategyKind::HighDegree { top_node_count } => {
2007-
Arc::new(probing::HighDegreeStrategy::new(
2007+
let strategy: Arc<dyn ProbingStrategy> = match &probing_cfg.kind {
2008+
ProbingStrategyKind::HighDegree { top_node_count } => {
2009+
Arc::new(HighDegreeStrategy::new(
20082010
Arc::clone(&network_graph),
2011+
Arc::clone(&channel_manager),
2012+
Arc::clone(&router),
20092013
*top_node_count,
20102014
MIN_PROBE_AMOUNT_MSAT,
20112015
DEFAULT_MAX_PROBE_AMOUNT_MSAT,
20122016
probing_cfg.cooldown,
2017+
config.probing_liquidity_limit_multiplier,
20132018
))
20142019
},
2015-
probing::ProbingStrategyKind::Random { max_hops } => {
2016-
Arc::new(probing::RandomStrategy::new(
2017-
Arc::clone(&network_graph),
2018-
Arc::clone(&channel_manager),
2019-
*max_hops,
2020-
MIN_PROBE_AMOUNT_MSAT,
2021-
DEFAULT_MAX_PROBE_AMOUNT_MSAT,
2022-
))
2023-
},
2024-
probing::ProbingStrategyKind::Custom(s) => Arc::clone(s),
2020+
ProbingStrategyKind::Random { max_hops } => Arc::new(RandomStrategy::new(
2021+
Arc::clone(&network_graph),
2022+
Arc::clone(&channel_manager),
2023+
*max_hops,
2024+
MIN_PROBE_AMOUNT_MSAT,
2025+
DEFAULT_MAX_PROBE_AMOUNT_MSAT,
2026+
)),
2027+
ProbingStrategyKind::Custom(s) => Arc::clone(s),
20252028
};
2026-
Arc::new(probing::Prober {
2029+
Arc::new(Prober {
20272030
channel_manager: Arc::clone(&channel_manager),
20282031
logger: Arc::clone(&logger),
20292032
strategy,
20302033
interval: probing_cfg.interval,
2031-
liquidity_limit_multiplier: Some(config.probing_liquidity_limit_multiplier),
20322034
max_locked_msat: probing_cfg.max_locked_msat,
20332035
locked_msat: Arc::new(AtomicU64::new(0)),
20342036
})

src/event.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -510,13 +510,13 @@ where
510510
payment_store: Arc<PaymentStore>,
511511
peer_store: Arc<PeerStore<L>>,
512512
keys_manager: Arc<KeysManager>,
513-
runtime: Arc<Runtime>,
514-
logger: L,
515-
config: Arc<Config>,
516513
static_invoice_store: Option<StaticInvoiceStore>,
517514
onion_messenger: Arc<OnionMessenger>,
518515
om_mailbox: Option<Arc<OnionMessageMailbox>>,
519516
prober: Option<Arc<Prober>>,
517+
runtime: Arc<Runtime>,
518+
logger: L,
519+
config: Arc<Config>,
520520
}
521521

522522
impl<L: Deref + Clone + Sync + Send + 'static> EventHandler<L>
@@ -532,7 +532,7 @@ where
532532
payment_store: Arc<PaymentStore>, peer_store: Arc<PeerStore<L>>,
533533
keys_manager: Arc<KeysManager>, static_invoice_store: Option<StaticInvoiceStore>,
534534
onion_messenger: Arc<OnionMessenger>, om_mailbox: Option<Arc<OnionMessageMailbox>>,
535-
runtime: Arc<Runtime>, logger: L, config: Arc<Config>, prober: Option<Arc<Prober>>,
535+
prober: Option<Arc<Prober>>, runtime: Arc<Runtime>, logger: L, config: Arc<Config>,
536536
) -> Self {
537537
Self {
538538
event_queue,
@@ -546,13 +546,13 @@ where
546546
payment_store,
547547
peer_store,
548548
keys_manager,
549-
logger,
550-
runtime,
551-
config,
552549
static_invoice_store,
553550
onion_messenger,
554551
om_mailbox,
555552
prober,
553+
runtime,
554+
logger,
555+
config,
556556
}
557557
}
558558

src/lib.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ pub mod logger;
101101
mod message_handler;
102102
pub mod payment;
103103
mod peer_store;
104-
mod probing;
104+
pub mod probing;
105105
mod runtime;
106106
mod scoring;
107107
mod tx_broadcaster;
@@ -172,10 +172,7 @@ use payment::{
172172
UnifiedPayment,
173173
};
174174
use peer_store::{PeerInfo, PeerStore};
175-
pub use probing::{
176-
HighDegreeStrategy, Probe, Prober, ProbingConfig, ProbingConfigBuilder, ProbingStrategy,
177-
RandomStrategy,
178-
};
175+
use probing::{run_prober, Prober};
179176
use runtime::Runtime;
180177
pub use tokio;
181178
use types::{
@@ -245,7 +242,7 @@ pub struct Node {
245242
om_mailbox: Option<Arc<OnionMessageMailbox>>,
246243
async_payments_role: Option<AsyncPaymentsRole>,
247244
hrn_resolver: Arc<HRNResolver>,
248-
prober: Option<Arc<probing::Prober>>,
245+
prober: Option<Arc<Prober>>,
249246
#[cfg(cycle_tests)]
250247
_leak_checker: LeakChecker,
251248
}
@@ -597,16 +594,16 @@ impl Node {
597594
static_invoice_store,
598595
Arc::clone(&self.onion_messenger),
599596
self.om_mailbox.clone(),
597+
self.prober.clone(),
600598
Arc::clone(&self.runtime),
601599
Arc::clone(&self.logger),
602600
Arc::clone(&self.config),
603-
self.prober.clone(),
604601
));
605602

606603
if let Some(prober) = self.prober.clone() {
607604
let stop_rx = self.stop_sender.subscribe();
608605
self.runtime.spawn_cancellable_background_task(async move {
609-
probing::run_prober(prober, stop_rx).await;
606+
run_prober(prober, stop_rx).await;
610607
});
611608
}
612609

@@ -1090,8 +1087,9 @@ impl Node {
10901087
/// Returns the scorer's estimated `(min, max)` liquidity range for the given channel in the
10911088
/// direction toward `target`, or `None` if the scorer has no data for that channel.
10921089
///
1093-
/// Works by serializing the `CombinedScorer` (which writes `local_only_scorer`) and
1094-
/// deserializing it as a plain `ProbabilisticScorer` to call `estimated_channel_liquidity_range`.
1090+
/// **Warning:** This is expensive — O(scorer size) per call. It works by serializing the
1091+
/// entire `CombinedScorer` and deserializing it as a plain `ProbabilisticScorer` to access
1092+
/// `estimated_channel_liquidity_range`. Intended for testing and debugging, not hot paths.
10951093
pub fn scorer_channel_liquidity(&self, scid: u64, target: PublicKey) -> Option<(u64, u64)> {
10961094
use lightning::routing::scoring::{
10971095
ProbabilisticScorer, ProbabilisticScoringDecayParameters,

0 commit comments

Comments
 (0)