@@ -484,11 +484,57 @@ trait SimNetwork: Send + Sync {
484
484
fn list_nodes ( & self ) -> Result < Vec < NodeInfo > , LightningError > ;
485
485
}
486
486
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
+
487
533
/// A wrapper struct used to implement the LightningNode trait (can be thought of as "the" lightning node). Passes
488
534
/// all functionality through to a coordinating simulation network. This implementation contains both the [`SimNetwork`]
489
535
/// implementation that will allow us to dispatch payments and a read-only NetworkGraph that is used for pathfinding.
490
536
/// 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 > {
492
538
info : NodeInfo ,
493
539
/// The underlying execution network that will be responsible for dispatching payments.
494
540
network : Arc < Mutex < T > > ,
@@ -499,15 +545,18 @@ struct SimNode<'a, T: SimNetwork> {
499
545
/// Probabilistic scorer used to rank paths through the network for routing. This is reused across
500
546
/// multiple payments to maintain scoring state.
501
547
scorer : ProbabilisticScorer < Arc < NetworkGraph < & ' a WrappedLog > > , & ' a WrappedLog > ,
548
+ /// The pathfinder implementation to use for finding routes
549
+ pathfinder : P ,
502
550
}
503
551
504
- impl < ' a , T : SimNetwork > SimNode < ' a , T > {
552
+ impl < ' a , T : SimNetwork , P : PathFinder < ' a > > SimNode < ' a , T , P > {
505
553
/// Creates a new simulation node that refers to the high level network coordinator provided to process payments
506
554
/// on its behalf. The pathfinding graph is provided separately so that each node can handle its own pathfinding.
507
555
pub fn new (
508
556
pubkey : PublicKey ,
509
557
payment_network : Arc < Mutex < T > > ,
510
558
pathfinding_graph : Arc < NetworkGraph < & ' a WrappedLog > > ,
559
+ pathfinder : P ,
511
560
) -> Self {
512
561
// Initialize the probabilistic scorer with default parameters for learning from payment
513
562
// history. These parameters control how much successful/failed payments affect routing
@@ -524,6 +573,7 @@ impl<'a, T: SimNetwork> SimNode<'a, T> {
524
573
in_flight : HashMap :: new ( ) ,
525
574
pathfinding_graph,
526
575
scorer,
576
+ pathfinder,
527
577
}
528
578
}
529
579
}
@@ -541,39 +591,8 @@ fn node_info(pubkey: PublicKey) -> NodeInfo {
541
591
}
542
592
}
543
593
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
-
575
594
#[ async_trait]
576
- impl < T : SimNetwork > LightningNode for SimNode < ' _ , T > {
595
+ impl < ' a , T : SimNetwork , P : PathFinder < ' a > > LightningNode for SimNode < ' a , T , P > {
577
596
fn get_info ( & self ) -> & NodeInfo {
578
597
& self . info
579
598
}
@@ -589,8 +608,24 @@ impl<T: SimNetwork> LightningNode for SimNode<'_, T> {
589
608
dest : PublicKey ,
590
609
amount_msat : u64 ,
591
610
) -> 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.
594
629
let ( sender, receiver) = channel ( ) ;
595
630
let preimage = PaymentPreimage ( rand:: random ( ) ) ;
596
631
let payment_hash = preimage. into ( ) ;
@@ -607,36 +642,13 @@ impl<T: SimNetwork> LightningNode for SimNode<'_, T> {
607
642
} ,
608
643
}
609
644
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
+ ) ;
640
652
641
653
Ok ( payment_hash)
642
654
}
@@ -975,19 +987,24 @@ impl SimGraph {
975
987
}
976
988
977
989
/// 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 > (
979
991
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
+ {
982
998
let mut nodes: HashMap < PublicKey , Arc < Mutex < dyn LightningNode > > > = HashMap :: new ( ) ;
983
-
999
+
984
1000
for pk in graph. lock ( ) . await . nodes . keys ( ) {
985
1001
nodes. insert (
986
1002
* pk,
987
1003
Arc :: new ( Mutex :: new ( SimNode :: new (
988
1004
* pk,
989
1005
graph. clone ( ) ,
990
1006
routing_graph. clone ( ) ,
1007
+ pathfinder. clone ( ) ,
991
1008
) ) ) ,
992
1009
) ;
993
1010
}
@@ -1864,7 +1881,7 @@ mod tests {
1864
1881
1865
1882
// Create a simulated node for the first channel in our network.
1866
1883
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 ) ;
1868
1885
1869
1886
// Prime mock to return node info from lookup and assert that we get the pubkey we're expecting.
1870
1887
let lookup_pk = channels[ 3 ] . node_1 . policy . pubkey ;
@@ -1955,6 +1972,7 @@ mod tests {
1955
1972
routing_graph : Arc < NetworkGraph < & ' a WrappedLog > > ,
1956
1973
scorer : ProbabilisticScorer < Arc < NetworkGraph < & ' a WrappedLog > > , & ' a WrappedLog > ,
1957
1974
shutdown : ( Trigger , Listener ) ,
1975
+ pathfinder : DefaultPathFinder ,
1958
1976
}
1959
1977
1960
1978
impl DispatchPaymentTestKit < ' _ > {
@@ -2003,6 +2021,7 @@ mod tests {
2003
2021
routing_graph,
2004
2022
scorer,
2005
2023
shutdown : shutdown_clone,
2024
+ pathfinder : DefaultPathFinder ,
2006
2025
} ;
2007
2026
2008
2027
// Assert that our channel balance is all on the side of the channel opener when we start up.
@@ -2045,8 +2064,13 @@ mod tests {
2045
2064
dest : PublicKey ,
2046
2065
amt : u64 ,
2047
2066
) -> ( 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 ( ) ;
2050
2074
2051
2075
let ( sender, receiver) = oneshot:: channel ( ) ;
2052
2076
self . graph
0 commit comments