Skip to content

Commit 10426ad

Browse files
committed
Change probing builder for uniffi bindings
Strategy constructors (high_degree/random_walk/custom) moved from ProbingConfig to ProbingConfigBuilder, so they live on the builder rather than on the thing being built. ProbingConfigBuilder setters switched from consuming `self -> Self` to `&mut self -> &mut Self`, matching NodeBuilder. `build` now takes `&self`. Existing fluent call sites still compile unchanged. Removed the flat new_high_degree/new_random_walk UniFFI constructors on ProbingConfig that replicated the builder wiring. Bindings now go through ArcedProbingConfigBuilder (exposed as ProbingConfigBuilder via UDL), which wraps ProbingConfigBuilder in an RwLock for the Arc semantics UniFFI requires — mirroring ArcedNodeBuilder. AI-assisted (Claude Code).
1 parent 56b5b0f commit 10426ad

File tree

5 files changed

+122
-98
lines changed

5 files changed

+122
-98
lines changed

bindings/ldk_node.udl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ typedef interface NodeEntropy;
1515

1616
typedef interface ProbingConfig;
1717

18+
typedef interface ProbingConfigBuilder;
19+
1820
typedef enum WordCount;
1921

2022
[Remote]

src/builder.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -625,16 +625,18 @@ impl NodeBuilder {
625625

626626
/// Configures background probing.
627627
///
628-
/// Use [`ProbingConfig`] to build the configuration:
628+
/// Use [`ProbingConfigBuilder`] to build the configuration:
629629
/// ```ignore
630-
/// use ldk_node::probing::ProbingConfig;
630+
/// use ldk_node::probing::ProbingConfigBuilder;
631631
///
632632
/// builder.set_probing_config(
633-
/// ProbingConfig::high_degree(100)
633+
/// ProbingConfigBuilder::high_degree(100)
634634
/// .interval(Duration::from_secs(30))
635635
/// .build()
636636
/// );
637637
/// ```
638+
///
639+
/// [`ProbingConfigBuilder`]: crate::probing::ProbingConfigBuilder
638640
pub fn set_probing_config(&mut self, config: ProbingConfig) -> &mut Self {
639641
self.probing_config = Some(config);
640642
self

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ use payment::{
174174
UnifiedPayment,
175175
};
176176
use peer_store::{PeerInfo, PeerStore};
177+
#[cfg(feature = "uniffi")]
178+
pub use probing::ArcedProbingConfigBuilder as ProbingConfigBuilder;
177179
use probing::{run_prober, Prober};
178180
use runtime::Runtime;
179181
pub use tokio;

src/probing.rs

Lines changed: 110 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
use std::collections::HashMap;
1111
use std::fmt;
1212
use std::sync::atomic::{AtomicU64, Ordering};
13+
#[cfg(feature = "uniffi")]
14+
use std::sync::RwLock;
1315
use std::sync::{Arc, Mutex};
1416
use std::time::{Duration, Instant};
1517

@@ -40,23 +42,20 @@ pub(crate) enum ProbingStrategyKind {
4042

4143
/// Configuration for the background probing subsystem.
4244
///
43-
/// Use the constructor methods [`high_degree`], [`random_walk`], or [`custom`] to start
44-
/// building, then chain optional setters and call [`build`].
45+
/// Construct via [`ProbingConfigBuilder`]. Pick a strategy with
46+
/// [`ProbingConfigBuilder::high_degree`], [`ProbingConfigBuilder::random_walk`], or
47+
/// [`ProbingConfigBuilder::custom`], chain optional setters, and finalize with
48+
/// [`ProbingConfigBuilder::build`].
4549
///
4650
/// # Example
4751
/// ```ignore
48-
/// let config = ProbingConfig::high_degree(100)
52+
/// let config = ProbingConfigBuilder::high_degree(100)
4953
/// .interval(Duration::from_secs(30))
5054
/// .max_locked_msat(500_000)
5155
/// .diversity_penalty_msat(250)
5256
/// .build();
5357
/// builder.set_probing_config(config);
5458
/// ```
55-
///
56-
/// [`high_degree`]: Self::high_degree
57-
/// [`random_walk`]: Self::random_walk
58-
/// [`custom`]: Self::custom
59-
/// [`build`]: ProbingConfigBuilder::build
6059
#[derive(Clone)]
6160
#[cfg_attr(feature = "uniffi", derive(uniffi::Object))]
6261
pub struct ProbingConfig {
@@ -88,86 +87,14 @@ impl fmt::Debug for ProbingConfig {
8887
}
8988
}
9089

91-
impl ProbingConfig {
92-
/// Start building a config that probes toward the highest-degree nodes in the graph.
93-
///
94-
/// `top_node_count` controls how many of the most-connected nodes are cycled through.
95-
pub fn high_degree(top_node_count: usize) -> ProbingConfigBuilder {
96-
ProbingConfigBuilder::new(ProbingStrategyKind::HighDegree { top_node_count })
97-
}
98-
99-
/// Start building a config that probes via random graph walks.
100-
///
101-
/// `max_hops` is the upper bound on the number of hops in a randomly constructed path.
102-
pub fn random_walk(max_hops: usize) -> ProbingConfigBuilder {
103-
ProbingConfigBuilder::new(ProbingStrategyKind::Random { max_hops })
104-
}
105-
106-
/// Start building a config with a custom [`ProbingStrategy`] implementation.
107-
pub fn custom(strategy: Arc<dyn ProbingStrategy>) -> ProbingConfigBuilder {
108-
ProbingConfigBuilder::new(ProbingStrategyKind::Custom(strategy))
109-
}
110-
}
111-
112-
#[cfg(feature = "uniffi")]
113-
#[uniffi::export]
114-
impl ProbingConfig {
115-
/// Creates a probing config that probes toward the highest-degree nodes in the graph.
116-
///
117-
/// `top_node_count` controls how many of the most-connected nodes are cycled through.
118-
/// All other parameters are optional and fall back to sensible defaults when `None`.
119-
#[uniffi::constructor]
120-
pub fn new_high_degree(
121-
top_node_count: u64, interval_secs: Option<u64>, max_locked_msat: Option<u64>,
122-
diversity_penalty_msat: Option<u64>, cooldown_secs: Option<u64>,
123-
) -> Self {
124-
let mut builder = Self::high_degree(top_node_count as usize);
125-
if let Some(secs) = interval_secs {
126-
builder = builder.interval(Duration::from_secs(secs));
127-
}
128-
if let Some(msat) = max_locked_msat {
129-
builder = builder.max_locked_msat(msat);
130-
}
131-
if let Some(penalty) = diversity_penalty_msat {
132-
builder = builder.diversity_penalty_msat(penalty);
133-
}
134-
if let Some(secs) = cooldown_secs {
135-
builder = builder.cooldown(Duration::from_secs(secs));
136-
}
137-
builder.build()
138-
}
139-
140-
/// Creates a probing config that probes via random graph walks.
141-
///
142-
/// `max_hops` is the upper bound on the number of hops in a randomly constructed path.
143-
/// All other parameters are optional and fall back to sensible defaults when `None`.
144-
#[uniffi::constructor]
145-
pub fn new_random_walk(
146-
max_hops: u64, interval_secs: Option<u64>, max_locked_msat: Option<u64>,
147-
diversity_penalty_msat: Option<u64>, cooldown_secs: Option<u64>,
148-
) -> Self {
149-
let mut builder = Self::random_walk(max_hops as usize);
150-
if let Some(secs) = interval_secs {
151-
builder = builder.interval(Duration::from_secs(secs));
152-
}
153-
if let Some(msat) = max_locked_msat {
154-
builder = builder.max_locked_msat(msat);
155-
}
156-
if let Some(penalty) = diversity_penalty_msat {
157-
builder = builder.diversity_penalty_msat(penalty);
158-
}
159-
if let Some(secs) = cooldown_secs {
160-
builder = builder.cooldown(Duration::from_secs(secs));
161-
}
162-
builder.build()
163-
}
164-
}
165-
16690
/// Builder for [`ProbingConfig`].
16791
///
168-
/// Created via [`ProbingConfig::high_degree`], [`ProbingConfig::random_walk`], or
169-
/// [`ProbingConfig::custom`]. Call [`build`] to finalize.
92+
/// Pick a strategy with [`high_degree`], [`random_walk`], or [`custom`], chain optional
93+
/// setters, and call [`build`] to finalize.
17094
///
95+
/// [`high_degree`]: Self::high_degree
96+
/// [`random_walk`]: Self::random_walk
97+
/// [`custom`]: Self::custom
17198
/// [`build`]: Self::build
17299
pub struct ProbingConfigBuilder {
173100
kind: ProbingStrategyKind,
@@ -178,7 +105,7 @@ pub struct ProbingConfigBuilder {
178105
}
179106

180107
impl ProbingConfigBuilder {
181-
fn new(kind: ProbingStrategyKind) -> Self {
108+
fn with_kind(kind: ProbingStrategyKind) -> Self {
182109
Self {
183110
kind,
184111
interval: Duration::from_secs(DEFAULT_PROBING_INTERVAL_SECS),
@@ -188,18 +115,37 @@ impl ProbingConfigBuilder {
188115
}
189116
}
190117

118+
/// Start building a config that probes toward the highest-degree nodes in the graph.
119+
///
120+
/// `top_node_count` controls how many of the most-connected nodes are cycled through.
121+
pub fn high_degree(top_node_count: usize) -> Self {
122+
Self::with_kind(ProbingStrategyKind::HighDegree { top_node_count })
123+
}
124+
125+
/// Start building a config that probes via random graph walks.
126+
///
127+
/// `max_hops` is the upper bound on the number of hops in a randomly constructed path.
128+
pub fn random_walk(max_hops: usize) -> Self {
129+
Self::with_kind(ProbingStrategyKind::Random { max_hops })
130+
}
131+
132+
/// Start building a config with a custom [`ProbingStrategy`] implementation.
133+
pub fn custom(strategy: Arc<dyn ProbingStrategy>) -> Self {
134+
Self::with_kind(ProbingStrategyKind::Custom(strategy))
135+
}
136+
191137
/// Overrides the interval between probe attempts.
192138
///
193139
/// Defaults to 10 seconds.
194-
pub fn interval(mut self, interval: Duration) -> Self {
140+
pub fn interval(&mut self, interval: Duration) -> &mut Self {
195141
self.interval = interval;
196142
self
197143
}
198144

199145
/// Overrides the maximum millisatoshis that may be locked in in-flight probes at any time.
200146
///
201147
/// Defaults to 100 000 000 msat (100k sats).
202-
pub fn max_locked_msat(mut self, max_msat: u64) -> Self {
148+
pub fn max_locked_msat(&mut self, max_msat: u64) -> &mut Self {
203149
self.max_locked_msat = max_msat;
204150
self
205151
}
@@ -215,23 +161,23 @@ impl ProbingConfigBuilder {
215161
/// (e.g., [`RandomStrategy`]) bypass the scorer entirely.
216162
///
217163
/// If unset, LDK's default of `0` (no penalty) is used.
218-
pub fn diversity_penalty_msat(mut self, penalty_msat: u64) -> Self {
164+
pub fn diversity_penalty_msat(&mut self, penalty_msat: u64) -> &mut Self {
219165
self.diversity_penalty_msat = Some(penalty_msat);
220166
self
221167
}
222168

223169
/// Sets how long a probed node stays ineligible before being probed again.
224170
///
225171
/// Only applies to [`HighDegreeStrategy`]. Defaults to 1 hour.
226-
pub fn cooldown(mut self, cooldown: Duration) -> Self {
172+
pub fn cooldown(&mut self, cooldown: Duration) -> &mut Self {
227173
self.cooldown = cooldown;
228174
self
229175
}
230176

231177
/// Builds the [`ProbingConfig`].
232-
pub fn build(self) -> ProbingConfig {
178+
pub fn build(&self) -> ProbingConfig {
233179
ProbingConfig {
234-
kind: self.kind,
180+
kind: self.kind.clone(),
235181
interval: self.interval,
236182
max_locked_msat: self.max_locked_msat,
237183
diversity_penalty_msat: self.diversity_penalty_msat,
@@ -240,6 +186,78 @@ impl ProbingConfigBuilder {
240186
}
241187
}
242188

189+
/// A UniFFI-compatible wrapper around [`ProbingConfigBuilder`] that uses interior mutability
190+
/// so it can be shared behind an `Arc` as required by the FFI object model.
191+
///
192+
/// Obtain one via the constructors [`new_high_degree`] or [`new_random_walk`], configure it
193+
/// with the `set_*` methods, then call [`build`] to produce a [`ProbingConfig`].
194+
///
195+
/// [`new_high_degree`]: Self::new_high_degree
196+
/// [`new_random_walk`]: Self::new_random_walk
197+
/// [`build`]: Self::build
198+
#[cfg(feature = "uniffi")]
199+
#[derive(uniffi::Object)]
200+
pub struct ArcedProbingConfigBuilder {
201+
inner: RwLock<ProbingConfigBuilder>,
202+
}
203+
204+
#[cfg(feature = "uniffi")]
205+
#[uniffi::export]
206+
impl ArcedProbingConfigBuilder {
207+
/// Creates a builder configured to probe toward the highest-degree nodes in the graph.
208+
///
209+
/// `top_node_count` controls how many of the most-connected nodes are cycled through.
210+
#[uniffi::constructor]
211+
pub fn new_high_degree(top_node_count: u64) -> Arc<Self> {
212+
Arc::new(Self {
213+
inner: RwLock::new(ProbingConfigBuilder::high_degree(top_node_count as usize)),
214+
})
215+
}
216+
217+
/// Creates a builder configured to probe via random graph walks.
218+
///
219+
/// `max_hops` is the upper bound on the number of hops in a randomly constructed path.
220+
#[uniffi::constructor]
221+
pub fn new_random_walk(max_hops: u64) -> Arc<Self> {
222+
Arc::new(Self { inner: RwLock::new(ProbingConfigBuilder::random_walk(max_hops as usize)) })
223+
}
224+
225+
/// Overrides the interval between probe attempts. Defaults to 10 seconds.
226+
pub fn set_interval(&self, secs: u64) {
227+
self.inner.write().unwrap().interval(Duration::from_secs(secs));
228+
}
229+
230+
/// Overrides the maximum millisatoshis that may be locked in in-flight probes at any time.
231+
///
232+
/// Defaults to 100 000 000 msat (100k sats).
233+
pub fn set_max_locked_msat(&self, max_msat: u64) {
234+
self.inner.write().unwrap().max_locked_msat(max_msat);
235+
}
236+
237+
/// Sets the probing diversity penalty applied by the probabilistic scorer.
238+
///
239+
/// When set, the scorer will penalize channels that have been recently probed,
240+
/// encouraging path diversity during background probing. The penalty decays
241+
/// quadratically over 24 hours.
242+
///
243+
/// If unset, LDK's default of `0` (no penalty) is used.
244+
pub fn set_diversity_penalty_msat(&self, penalty_msat: u64) {
245+
self.inner.write().unwrap().diversity_penalty_msat(penalty_msat);
246+
}
247+
248+
/// Sets how long a probed node stays ineligible before being probed again.
249+
///
250+
/// Only applies to the high-degree strategy. Defaults to 1 hour.
251+
pub fn set_cooldown(&self, secs: u64) {
252+
self.inner.write().unwrap().cooldown(Duration::from_secs(secs));
253+
}
254+
255+
/// Builds the [`ProbingConfig`].
256+
pub fn build(&self) -> Arc<ProbingConfig> {
257+
Arc::new(self.inner.read().unwrap().build())
258+
}
259+
}
260+
243261
/// Strategy can be used for determining the next target and amount for probing.
244262
pub trait ProbingStrategy: Send + Sync + 'static {
245263
/// Returns the next probe path to run, or `None` to skip this tick.

tests/probing_tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use common::{
2121
};
2222

2323
use ldk_node::bitcoin::Amount;
24-
use ldk_node::probing::{ProbingConfig, ProbingStrategy};
24+
use ldk_node::probing::{ProbingConfigBuilder, ProbingStrategy};
2525
use ldk_node::Event;
2626

2727
use lightning::routing::router::Path;
@@ -124,7 +124,7 @@ async fn probe_budget_increments_and_decrements() {
124124
let mut config_a = random_config(false);
125125
let strategy = FixedPathStrategy::new();
126126
config_a.probing = Some(
127-
ProbingConfig::custom(strategy.clone())
127+
ProbingConfigBuilder::custom(strategy.clone())
128128
.interval(Duration::from_millis(PROBING_INTERVAL_MILLISECONDS))
129129
.max_locked_msat(10 * PROBE_AMOUNT_MSAT)
130130
.build(),
@@ -210,7 +210,7 @@ async fn exhausted_probe_budget_blocks_new_probes() {
210210
let mut config_a = random_config(false);
211211
let strategy = FixedPathStrategy::new();
212212
config_a.probing = Some(
213-
ProbingConfig::custom(strategy.clone())
213+
ProbingConfigBuilder::custom(strategy.clone())
214214
.interval(Duration::from_millis(PROBING_INTERVAL_MILLISECONDS))
215215
.max_locked_msat(10 * PROBE_AMOUNT_MSAT)
216216
.build(),

0 commit comments

Comments
 (0)