@@ -484,11 +484,57 @@ trait SimNetwork: Send + Sync {
484484 fn list_nodes ( & self ) -> Result < Vec < NodeInfo > , LightningError > ;
485485}
486486
487+ /// A trait for custom pathfinding implementations.
488+ pub trait PathFinder < ' a > : Send + Sync {
489+ fn find_route (
490+ & self ,
491+ source : & PublicKey ,
492+ dest : PublicKey ,
493+ amount_msat : u64 ,
494+ pathfinding_graph : & NetworkGraph < & ' a WrappedLog > ,
495+ scorer : & ProbabilisticScorer < Arc < NetworkGraph < & ' a WrappedLog > > , & ' a WrappedLog > ,
496+ ) -> Result < Route , SimulationError > ;
497+ }
498+
499+ /// Default pathfinder that uses LDK's pathfinding algorithm.
500+ #[ derive( Clone ) ]
501+ pub struct DefaultPathFinder ;
502+
503+ impl < ' a > PathFinder < ' a > for DefaultPathFinder {
504+ fn find_route (
505+ & self ,
506+ source : & PublicKey ,
507+ dest : PublicKey ,
508+ amount_msat : u64 ,
509+ pathfinding_graph : & NetworkGraph < & ' a WrappedLog > ,
510+ scorer : & ProbabilisticScorer < Arc < NetworkGraph < & ' a WrappedLog > > , & ' a WrappedLog > ,
511+ ) -> Result < Route , SimulationError > {
512+ find_route (
513+ source,
514+ & RouteParameters {
515+ payment_params : PaymentParameters :: from_node_id ( dest, 0 )
516+ . with_max_total_cltv_expiry_delta ( u32:: MAX )
517+ . with_max_path_count ( 1 )
518+ . with_max_channel_saturation_power_of_half ( 1 ) ,
519+ final_value_msat : amount_msat,
520+ max_total_routing_fee_msat : None ,
521+ } ,
522+ pathfinding_graph,
523+ None ,
524+ & WrappedLog { } ,
525+ scorer,
526+ & Default :: default ( ) ,
527+ & [ 0 ; 32 ] ,
528+ )
529+ . map_err ( |e| SimulationError :: SimulatedNetworkError ( e. err ) )
530+ }
531+ }
532+
487533/// A wrapper struct used to implement the LightningNode trait (can be thought of as "the" lightning node). Passes
488534/// all functionality through to a coordinating simulation network. This implementation contains both the [`SimNetwork`]
489535/// implementation that will allow us to dispatch payments and a read-only NetworkGraph that is used for pathfinding.
490536/// While these two could be combined, we re-use the LDK-native struct to allow re-use of their pathfinding logic.
491- struct SimNode < ' a , T : SimNetwork > {
537+ struct SimNode < ' a , T : SimNetwork , P : PathFinder < ' a > = DefaultPathFinder > {
492538 info : NodeInfo ,
493539 /// The underlying execution network that will be responsible for dispatching payments.
494540 network : Arc < Mutex < T > > ,
@@ -499,15 +545,18 @@ struct SimNode<'a, T: SimNetwork> {
499545 /// Probabilistic scorer used to rank paths through the network for routing. This is reused across
500546 /// multiple payments to maintain scoring state.
501547 scorer : ProbabilisticScorer < Arc < NetworkGraph < & ' a WrappedLog > > , & ' a WrappedLog > ,
548+ /// The pathfinder implementation to use for finding routes
549+ pathfinder : P ,
502550}
503551
504- impl < ' a , T : SimNetwork > SimNode < ' a , T > {
552+ impl < ' a , T : SimNetwork , P : PathFinder < ' a > > SimNode < ' a , T , P > {
505553 /// Creates a new simulation node that refers to the high level network coordinator provided to process payments
506554 /// on its behalf. The pathfinding graph is provided separately so that each node can handle its own pathfinding.
507555 pub fn new (
508556 pubkey : PublicKey ,
509557 payment_network : Arc < Mutex < T > > ,
510558 pathfinding_graph : Arc < NetworkGraph < & ' a WrappedLog > > ,
559+ pathfinder : P ,
511560 ) -> Self {
512561 // Initialize the probabilistic scorer with default parameters for learning from payment
513562 // history. These parameters control how much successful/failed payments affect routing
@@ -524,6 +573,7 @@ impl<'a, T: SimNetwork> SimNode<'a, T> {
524573 in_flight : HashMap :: new ( ) ,
525574 pathfinding_graph,
526575 scorer,
576+ pathfinder,
527577 }
528578 }
529579}
@@ -541,39 +591,8 @@ fn node_info(pubkey: PublicKey) -> NodeInfo {
541591 }
542592}
543593
544- /// Uses LDK's pathfinding algorithm with default parameters to find a path from source to destination, with no
545- /// restrictions on fee budget.
546- fn find_payment_route < ' a > (
547- source : & PublicKey ,
548- dest : PublicKey ,
549- amount_msat : u64 ,
550- pathfinding_graph : & NetworkGraph < & ' a WrappedLog > ,
551- scorer : & ProbabilisticScorer < Arc < NetworkGraph < & ' a WrappedLog > > , & ' a WrappedLog > ,
552- ) -> Result < Route , SimulationError > {
553- find_route (
554- source,
555- & RouteParameters {
556- payment_params : PaymentParameters :: from_node_id ( dest, 0 )
557- . with_max_total_cltv_expiry_delta ( u32:: MAX )
558- // TODO: set non-zero value to support MPP.
559- . with_max_path_count ( 1 )
560- // Allow sending htlcs up to 50% of the channel's capacity.
561- . with_max_channel_saturation_power_of_half ( 1 ) ,
562- final_value_msat : amount_msat,
563- max_total_routing_fee_msat : None ,
564- } ,
565- pathfinding_graph,
566- None ,
567- & WrappedLog { } ,
568- scorer,
569- & Default :: default ( ) ,
570- & [ 0 ; 32 ] ,
571- )
572- . map_err ( |e| SimulationError :: SimulatedNetworkError ( e. err ) )
573- }
574-
575594#[ async_trait]
576- impl < T : SimNetwork > LightningNode for SimNode < ' _ , T > {
595+ impl < ' a , T : SimNetwork , P : PathFinder < ' a > > LightningNode for SimNode < ' a , T , P > {
577596 fn get_info ( & self ) -> & NodeInfo {
578597 & self . info
579598 }
@@ -589,8 +608,24 @@ impl<T: SimNetwork> LightningNode for SimNode<'_, T> {
589608 dest : PublicKey ,
590609 amount_msat : u64 ,
591610 ) -> Result < PaymentHash , LightningError > {
592- // Create a sender and receiver pair that will be used to report the results of the payment and add them to
593- // our internal tracking state along with the chosen payment hash.
611+ // Use the stored scorer when finding a route
612+ let route = match self . pathfinder . find_route (
613+ & self . info . pubkey ,
614+ dest,
615+ amount_msat,
616+ & self . pathfinding_graph ,
617+ & self . scorer ,
618+ ) {
619+ Ok ( route) => route,
620+ Err ( e) => {
621+ log:: warn!( "No route found: {e}" ) ;
622+ return Err ( LightningError :: SendPaymentError ( format ! (
623+ "No route found: {e}"
624+ ) ) ) ;
625+ } ,
626+ } ;
627+
628+ // Create a channel to receive the payment result.
594629 let ( sender, receiver) = channel ( ) ;
595630 let preimage = PaymentPreimage ( rand:: random ( ) ) ;
596631 let payment_hash = preimage. into ( ) ;
@@ -607,36 +642,13 @@ impl<T: SimNetwork> LightningNode for SimNode<'_, T> {
607642 } ,
608643 }
609644
610- // Use the stored scorer when finding a route
611- let route = match find_payment_route (
612- & self . info . pubkey ,
613- dest,
614- amount_msat,
615- & self . pathfinding_graph ,
616- & self . scorer ,
617- ) {
618- Ok ( path) => path,
619- // In the case that we can't find a route for the payment, we still report a successful payment *api call*
620- // and report RouteNotFound to the tracking channel. This mimics the behavior of real nodes.
621- Err ( e) => {
622- log:: trace!( "Could not find path for payment: {:?}." , e) ;
623-
624- if let Err ( e) = sender. send ( Ok ( PaymentResult {
625- htlc_count : 0 ,
626- payment_outcome : PaymentOutcome :: RouteNotFound ,
627- } ) ) {
628- log:: error!( "Could not send payment result: {:?}." , e) ;
629- }
630-
631- return Ok ( payment_hash) ;
632- } ,
633- } ;
634-
635- // If we did successfully obtain a route, dispatch the payment through the network and then report success.
636- self . network
637- . lock ( )
638- . await
639- . dispatch_payment ( self . info . pubkey , route, payment_hash, sender) ;
645+ // Dispatch the payment through the network
646+ self . network . lock ( ) . await . dispatch_payment (
647+ self . info . pubkey ,
648+ route,
649+ payment_hash,
650+ sender,
651+ ) ;
640652
641653 Ok ( payment_hash)
642654 }
@@ -975,19 +987,24 @@ impl SimGraph {
975987}
976988
977989/// Produces a map of node public key to lightning node implementation to be used for simulations.
978- pub async fn ln_node_from_graph (
990+ pub async fn ln_node_from_graph < P > (
979991 graph : Arc < Mutex < SimGraph > > ,
980- routing_graph : Arc < NetworkGraph < & ' _ WrappedLog > > ,
981- ) -> HashMap < PublicKey , Arc < Mutex < dyn LightningNode + ' _ > > > {
992+ routing_graph : Arc < NetworkGraph < & ' static WrappedLog > > ,
993+ pathfinder : P ,
994+ ) -> HashMap < PublicKey , Arc < Mutex < dyn LightningNode > > >
995+ where
996+ P : for < ' a > PathFinder < ' a > + Clone + ' static ,
997+ {
982998 let mut nodes: HashMap < PublicKey , Arc < Mutex < dyn LightningNode > > > = HashMap :: new ( ) ;
983-
999+
9841000 for pk in graph. lock ( ) . await . nodes . keys ( ) {
9851001 nodes. insert (
9861002 * pk,
9871003 Arc :: new ( Mutex :: new ( SimNode :: new (
9881004 * pk,
9891005 graph. clone ( ) ,
9901006 routing_graph. clone ( ) ,
1007+ pathfinder. clone ( ) ,
9911008 ) ) ) ,
9921009 ) ;
9931010 }
@@ -1864,7 +1881,7 @@ mod tests {
18641881
18651882 // Create a simulated node for the first channel in our network.
18661883 let pk = channels[ 0 ] . node_1 . policy . pubkey ;
1867- let mut node = SimNode :: new ( pk, sim_network. clone ( ) , Arc :: new ( graph) ) ;
1884+ let mut node = SimNode :: new ( pk, sim_network. clone ( ) , Arc :: new ( graph) , DefaultPathFinder ) ;
18681885
18691886 // Prime mock to return node info from lookup and assert that we get the pubkey we're expecting.
18701887 let lookup_pk = channels[ 3 ] . node_1 . policy . pubkey ;
@@ -1955,6 +1972,7 @@ mod tests {
19551972 routing_graph : Arc < NetworkGraph < & ' a WrappedLog > > ,
19561973 scorer : ProbabilisticScorer < Arc < NetworkGraph < & ' a WrappedLog > > , & ' a WrappedLog > ,
19571974 shutdown : ( Trigger , Listener ) ,
1975+ pathfinder : DefaultPathFinder ,
19581976 }
19591977
19601978 impl DispatchPaymentTestKit < ' _ > {
@@ -2003,6 +2021,7 @@ mod tests {
20032021 routing_graph,
20042022 scorer,
20052023 shutdown : shutdown_clone,
2024+ pathfinder : DefaultPathFinder ,
20062025 } ;
20072026
20082027 // Assert that our channel balance is all on the side of the channel opener when we start up.
@@ -2045,8 +2064,13 @@ mod tests {
20452064 dest : PublicKey ,
20462065 amt : u64 ,
20472066 ) -> ( Route , Result < PaymentResult , LightningError > ) {
2048- let route =
2049- find_payment_route ( & source, dest, amt, & self . routing_graph , & self . scorer ) . unwrap ( ) ;
2067+ let route = self . pathfinder . find_route (
2068+ & source,
2069+ dest,
2070+ amt,
2071+ & self . routing_graph ,
2072+ & self . scorer ,
2073+ ) . unwrap ( ) ;
20502074
20512075 let ( sender, receiver) = oneshot:: channel ( ) ;
20522076 self . graph
0 commit comments