@@ -9,8 +9,9 @@ use std::collections::HashMap;
99use std:: convert:: TryInto ;
1010use std:: default:: Default ;
1111use std:: path:: PathBuf ;
12+ use std:: sync:: atomic:: AtomicU64 ;
1213use std:: sync:: { Arc , Mutex , Once , RwLock } ;
13- use std:: time:: SystemTime ;
14+ use std:: time:: { Duration , SystemTime } ;
1415use std:: { fmt, fs} ;
1516
1617use bdk_wallet:: template:: Bip84 ;
@@ -47,6 +48,7 @@ use crate::config::{
4748 default_user_config, may_announce_channel, AnnounceError , AsyncPaymentsRole ,
4849 BitcoindRestClientConfig , Config , ElectrumSyncConfig , EsploraSyncConfig ,
4950 DEFAULT_ESPLORA_SERVER_URL , DEFAULT_LOG_FILENAME , DEFAULT_LOG_LEVEL ,
51+ DEFAULT_MAX_PROBE_LOCKED_MSAT , DEFAULT_PROBING_INTERVAL_SECS , MIN_PROBE_AMOUNT_MSAT ,
5052} ;
5153use crate :: connection:: ConnectionManager ;
5254use crate :: entropy:: NodeEntropy ;
@@ -72,6 +74,8 @@ use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
7274use crate :: message_handler:: NodeCustomMessageHandler ;
7375use crate :: payment:: asynchronous:: om_mailbox:: OnionMessageMailbox ;
7476use crate :: peer_store:: PeerStore ;
77+ use crate :: probing;
78+ use crate :: probing:: ProbingStrategy ;
7579use crate :: runtime:: { Runtime , RuntimeSpawner } ;
7680use crate :: tx_broadcaster:: TransactionBroadcaster ;
7781use crate :: types:: {
@@ -150,6 +154,37 @@ impl std::fmt::Debug for LogWriterConfig {
150154 }
151155}
152156
157+ enum ProbingStrategyKind {
158+ HighDegree { top_n : usize } ,
159+ Random { max_hops : usize } ,
160+ Custom ( Arc < dyn probing:: ProbingStrategy > ) ,
161+ }
162+
163+ struct ProbingStrategyConfig {
164+ kind : ProbingStrategyKind ,
165+ interval : Duration ,
166+ max_locked_msat : u64 ,
167+ }
168+
169+ impl fmt:: Debug for ProbingStrategyConfig {
170+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
171+ let kind_str = match & self . kind {
172+ ProbingStrategyKind :: HighDegree { top_n } => {
173+ format ! ( "HighDegree {{ top_n: {} }}" , top_n)
174+ } ,
175+ ProbingStrategyKind :: Random { max_hops } => {
176+ format ! ( "Random {{ max_hops: {} }}" , max_hops)
177+ } ,
178+ ProbingStrategyKind :: Custom ( _) => "Custom(<probing strategy>)" . to_string ( ) ,
179+ } ;
180+ f. debug_struct ( "ProbingStrategyConfig" )
181+ . field ( "kind" , & kind_str)
182+ . field ( "interval" , & self . interval )
183+ . field ( "max_locked_msat" , & self . max_locked_msat )
184+ . finish ( )
185+ }
186+ }
187+
153188/// An error encountered during building a [`Node`].
154189///
155190/// [`Node`]: crate::Node
@@ -245,6 +280,7 @@ pub struct NodeBuilder {
245280 runtime_handle : Option < tokio:: runtime:: Handle > ,
246281 pathfinding_scores_sync_config : Option < PathfindingScoresSyncConfig > ,
247282 recovery_mode : bool ,
283+ probing_strategy : Option < ProbingStrategyConfig > ,
248284}
249285
250286impl NodeBuilder {
@@ -273,6 +309,7 @@ impl NodeBuilder {
273309 async_payments_role : None ,
274310 pathfinding_scores_sync_config,
275311 recovery_mode,
312+ probing_strategy : None ,
276313 }
277314 }
278315
@@ -557,6 +594,64 @@ impl NodeBuilder {
557594 self
558595 }
559596
597+ /// Configures background probing toward the highest-degree nodes in the network graph.
598+ ///
599+ /// `top_n` controls how many of the most-connected nodes are cycled through.
600+ pub fn set_high_degree_probing_strategy ( & mut self , top_n : usize ) -> & mut Self {
601+ let kind = ProbingStrategyKind :: HighDegree { top_n } ;
602+ self . probing_strategy = Some ( self . make_probing_config ( kind) ) ;
603+ self
604+ }
605+
606+ /// Configures background probing via random graph walks of up to `max_hops` hops.
607+ pub fn set_random_probing_strategy ( & mut self , max_hops : usize ) -> & mut Self {
608+ let kind = ProbingStrategyKind :: Random { max_hops } ;
609+ self . probing_strategy = Some ( self . make_probing_config ( kind) ) ;
610+ self
611+ }
612+
613+ /// Configures a custom probing strategy for background channel probing.
614+ ///
615+ /// When set, the node will periodically call [`ProbingStrategy::next_probe`] and dispatch the
616+ /// returned probe via the channel manager.
617+ pub fn set_probing_strategy (
618+ & mut self , strategy : Arc < dyn probing:: ProbingStrategy > ,
619+ ) -> & mut Self {
620+ let kind = ProbingStrategyKind :: Custom ( strategy) ;
621+ self . probing_strategy = Some ( self . make_probing_config ( kind) ) ;
622+ self
623+ }
624+
625+ /// Overrides the interval between probe attempts. Only has effect if a probing strategy is set.
626+ pub fn set_probing_interval ( & mut self , interval : Duration ) -> & mut Self {
627+ if let Some ( cfg) = & mut self . probing_strategy {
628+ cfg. interval = interval;
629+ }
630+ self
631+ }
632+
633+ /// Overrides the maximum millisatoshis that may be locked in in-flight probes at any time.
634+ /// Only has effect if a probing strategy is set.
635+ pub fn set_max_probe_locked_msat ( & mut self , max_msat : u64 ) -> & mut Self {
636+ if let Some ( cfg) = & mut self . probing_strategy {
637+ cfg. max_locked_msat = max_msat;
638+ }
639+ self
640+ }
641+
642+ fn make_probing_config ( & self , kind : ProbingStrategyKind ) -> ProbingStrategyConfig {
643+ let existing = self . probing_strategy . as_ref ( ) ;
644+ ProbingStrategyConfig {
645+ kind,
646+ interval : existing
647+ . map ( |c| c. interval )
648+ . unwrap_or ( Duration :: from_secs ( DEFAULT_PROBING_INTERVAL_SECS ) ) ,
649+ max_locked_msat : existing
650+ . map ( |c| c. max_locked_msat )
651+ . unwrap_or ( DEFAULT_MAX_PROBE_LOCKED_MSAT ) ,
652+ }
653+ }
654+
560655 /// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
561656 /// previously configured.
562657 pub fn build ( & self , node_entropy : NodeEntropy ) -> Result < Node , BuildError > {
@@ -697,6 +792,7 @@ impl NodeBuilder {
697792 runtime,
698793 logger,
699794 Arc :: new ( DynStoreWrapper ( kv_store) ) ,
795+ self . probing_strategy . as_ref ( ) ,
700796 )
701797 }
702798}
@@ -942,6 +1038,11 @@ impl ArcedNodeBuilder {
9421038 self . inner . write ( ) . unwrap ( ) . set_wallet_recovery_mode ( ) ;
9431039 }
9441040
1041+ /// Configures a probing strategy for background channel probing.
1042+ pub fn set_probing_strategy ( & self , strategy : Arc < dyn probing:: ProbingStrategy > ) {
1043+ self . inner . write ( ) . unwrap ( ) . set_probing_strategy ( strategy) ;
1044+ }
1045+
9451046 /// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
9461047 /// previously configured.
9471048 pub fn build ( & self , node_entropy : Arc < NodeEntropy > ) -> Result < Arc < Node > , BuildError > {
@@ -1058,6 +1159,7 @@ fn build_with_store_internal(
10581159 pathfinding_scores_sync_config : Option < & PathfindingScoresSyncConfig > ,
10591160 async_payments_role : Option < AsyncPaymentsRole > , recovery_mode : bool , seed_bytes : [ u8 ; 64 ] ,
10601161 runtime : Arc < Runtime > , logger : Arc < Logger > , kv_store : Arc < DynStore > ,
1162+ probing_config : Option < & ProbingStrategyConfig > ,
10611163) -> Result < Node , BuildError > {
10621164 optionally_install_rustls_cryptoprovider ( ) ;
10631165
@@ -1783,6 +1885,36 @@ fn build_with_store_internal(
17831885 _leak_checker. 0 . push ( Arc :: downgrade ( & wallet) as Weak < dyn Any + Send + Sync > ) ;
17841886 }
17851887
1888+ let prober = probing_config. map ( |config| {
1889+ let strategy: Arc < dyn probing:: ProbingStrategy > = match & config. kind {
1890+ ProbingStrategyKind :: HighDegree { top_n } => {
1891+ Arc :: new ( probing:: HighDegreeStrategy :: new (
1892+ network_graph. clone ( ) ,
1893+ * top_n,
1894+ MIN_PROBE_AMOUNT_MSAT ,
1895+ config. max_locked_msat ,
1896+ ) )
1897+ } ,
1898+ ProbingStrategyKind :: Random { max_hops } => Arc :: new ( probing:: RandomStrategy :: new (
1899+ network_graph. clone ( ) ,
1900+ channel_manager. clone ( ) ,
1901+ * max_hops,
1902+ MIN_PROBE_AMOUNT_MSAT ,
1903+ config. max_locked_msat ,
1904+ ) ) ,
1905+ ProbingStrategyKind :: Custom ( s) => s. clone ( ) ,
1906+ } ;
1907+ Arc :: new ( probing:: Prober {
1908+ channel_manager : channel_manager. clone ( ) ,
1909+ logger : logger. clone ( ) ,
1910+ strategy,
1911+ interval : config. interval ,
1912+ liquidity_limit_multiplier : None ,
1913+ max_locked_msat : config. max_locked_msat ,
1914+ locked_msat : Arc :: new ( AtomicU64 :: new ( 0 ) ) ,
1915+ } )
1916+ } ) ;
1917+
17861918 Ok ( Node {
17871919 runtime,
17881920 stop_sender,
@@ -1815,6 +1947,7 @@ fn build_with_store_internal(
18151947 om_mailbox,
18161948 async_payments_role,
18171949 hrn_resolver,
1950+ prober,
18181951 #[ cfg( cycle_tests) ]
18191952 _leak_checker,
18201953 } )
0 commit comments