@@ -5,8 +5,8 @@ use log::LevelFilter;
55use serde:: { Deserialize , Serialize } ;
66use simln_lib:: clock:: SimulationClock ;
77use simln_lib:: sim_node:: {
8- ln_node_from_graph, populate_network_graph, ChannelPolicy , CustomRecords , Interceptor ,
9- PathFinder , SimGraph , SimulatedChannel ,
8+ ln_node_from_graph, populate_network_graph, ChannelPolicy , CustomRecords , DefaultPathFinder ,
9+ Interceptor , SimGraph , SimulatedChannel ,
1010} ;
1111
1212use simln_lib:: {
@@ -259,12 +259,11 @@ pub async fn create_simulation_with_network(
259259 tasks : TaskTracker ,
260260 interceptors : Vec < Arc < dyn Interceptor > > ,
261261 custom_records : CustomRecords ,
262- pathfinder : P ,
263262) -> Result <
264263 (
265264 Simulation < SimulationClock > ,
266265 Vec < ActivityDefinition > ,
267- HashMap < PublicKey , Arc < Mutex < SimNode < SimGraph , SimulationClock > > > > ,
266+ HashMap < PublicKey , Arc < Mutex < dyn LightningNode > > > ,
268267 ) ,
269268 anyhow:: Error ,
270269> {
@@ -310,11 +309,21 @@ pub async fn create_simulation_with_network(
310309 populate_network_graph ( channels, clock. clone ( ) )
311310 . map_err ( |e| SimulationError :: SimulatedNetworkError ( format ! ( "{:?}" , e) ) ) ?,
312311 ) ;
312+
313+ // Create the pathfinder instance
314+ let pathfinder = DefaultPathFinder :: new ( routing_graph. clone ( ) ) ;
315+
313316 // We want the full set of nodes in our graph to return to the caller so that they can take
314317 // custom actions on the simulated network. For the nodes we'll pass our simulation, cast them
315318 // to a dyn trait and exclude any nodes that shouldn't be included in random activity
316319 // generation.
317- let nodes = ln_node_from_graph ( simulation_graph. clone ( ) , routing_graph, clock. clone ( ) , pathfinder) . await ?;
320+ let nodes = ln_node_from_graph (
321+ simulation_graph. clone ( ) ,
322+ routing_graph,
323+ clock. clone ( ) ,
324+ pathfinder,
325+ )
326+ . await ?;
318327 let mut nodes_dyn: HashMap < _ , Arc < Mutex < dyn LightningNode > > > = nodes
319328 . iter ( )
320329 . map ( |( pk, node) | ( * pk, Arc :: clone ( node) as Arc < Mutex < dyn LightningNode > > ) )
@@ -636,232 +645,3 @@ pub async fn get_validated_activities(
636645
637646 validate_activities ( activity. to_vec ( ) , activity_validation_params) . await
638647}
639-
640- #[ cfg( test) ]
641- mod tests {
642- use super :: * ;
643- use bitcoin:: secp256k1:: { PublicKey , Secp256k1 , SecretKey } ;
644- use lightning:: routing:: gossip:: NetworkGraph ;
645- use lightning:: routing:: router:: { find_route, PaymentParameters , Route , RouteParameters } ;
646- use lightning:: routing:: scoring:: { ProbabilisticScorer , ProbabilisticScoringDecayParameters } ;
647- use rand:: RngCore ;
648- use simln_lib:: clock:: SystemClock ;
649- use simln_lib:: sim_node:: {
650- ln_node_from_graph, populate_network_graph, PathFinder , SimGraph , WrappedLog ,
651- } ;
652- use simln_lib:: SimulationError ;
653- use std:: sync:: Arc ;
654- use tokio:: sync:: Mutex ;
655- use tokio_util:: task:: TaskTracker ;
656-
657- /// Gets a key pair generated in a pseudorandom way.
658- fn get_random_keypair ( ) -> ( SecretKey , PublicKey ) {
659- let secp = Secp256k1 :: new ( ) ;
660- let mut rng = rand:: thread_rng ( ) ;
661- let mut bytes = [ 0u8 ; 32 ] ;
662- rng. fill_bytes ( & mut bytes) ;
663- let secret_key = SecretKey :: from_slice ( & bytes) . expect ( "Failed to create secret key" ) ;
664- let public_key = PublicKey :: from_secret_key ( & secp, & secret_key) ;
665- ( secret_key, public_key)
666- }
667-
668- /// Helper function to create simulated channels for testing
669- fn create_simulated_channels ( num_channels : usize , capacity_msat : u64 ) -> Vec < SimulatedChannel > {
670- let mut channels = Vec :: new ( ) ;
671- for i in 0 ..num_channels {
672- let ( _node1_sk, node1_pubkey) = get_random_keypair ( ) ;
673- let ( _node2_sk, node2_pubkey) = get_random_keypair ( ) ;
674-
675- let channel = SimulatedChannel :: new (
676- capacity_msat,
677- ShortChannelID :: from ( i as u64 ) ,
678- ChannelPolicy {
679- pubkey : node1_pubkey,
680- alias : "" . to_string ( ) ,
681- max_htlc_count : 483 ,
682- max_in_flight_msat : capacity_msat / 2 ,
683- min_htlc_size_msat : 1000 ,
684- max_htlc_size_msat : capacity_msat / 2 ,
685- cltv_expiry_delta : 144 ,
686- base_fee : 1000 ,
687- fee_rate_prop : 100 ,
688- } ,
689- ChannelPolicy {
690- pubkey : node2_pubkey,
691- alias : "" . to_string ( ) ,
692- max_htlc_count : 483 ,
693- max_in_flight_msat : capacity_msat / 2 ,
694- min_htlc_size_msat : 1000 ,
695- max_htlc_size_msat : capacity_msat / 2 ,
696- cltv_expiry_delta : 144 ,
697- base_fee : 1000 ,
698- fee_rate_prop : 100 ,
699- } ,
700- ) ;
701- channels. push ( channel) ;
702- }
703- channels
704- }
705-
706- /// A pathfinder that always fails to find a path
707- #[ derive( Clone ) ]
708- pub struct AlwaysFailPathFinder ;
709-
710- impl < ' a > PathFinder < ' a > for AlwaysFailPathFinder {
711- fn find_route (
712- & self ,
713- _source : & PublicKey ,
714- _dest : PublicKey ,
715- _amount_msat : u64 ,
716- _pathfinding_graph : & NetworkGraph < & ' a WrappedLog > ,
717- _scorer : & ProbabilisticScorer < Arc < NetworkGraph < & ' a WrappedLog > > , & ' a WrappedLog > ,
718- ) -> Result < Route , SimulationError > {
719- Err ( SimulationError :: SimulatedNetworkError (
720- "No route found" . to_string ( ) ,
721- ) )
722- }
723- }
724-
725- /// A pathfinder that only returns single-hop paths
726- #[ derive( Clone ) ]
727- pub struct SingleHopOnlyPathFinder ;
728-
729- impl < ' a > PathFinder < ' a > for SingleHopOnlyPathFinder {
730- fn find_route (
731- & self ,
732- source : & PublicKey ,
733- dest : PublicKey ,
734- amount_msat : u64 ,
735- pathfinding_graph : & NetworkGraph < & ' a WrappedLog > ,
736- scorer : & ProbabilisticScorer < Arc < NetworkGraph < & ' a WrappedLog > > , & ' a WrappedLog > ,
737- ) -> Result < Route , SimulationError > {
738- // Try to find a direct route only (single hop)
739- let route_params = RouteParameters {
740- payment_params : PaymentParameters :: from_node_id ( dest, 0 )
741- . with_max_total_cltv_expiry_delta ( u32:: MAX )
742- . with_max_path_count ( 1 )
743- . with_max_channel_saturation_power_of_half ( 1 ) ,
744- final_value_msat : amount_msat,
745- max_total_routing_fee_msat : None ,
746- } ;
747-
748- // Try to find a route - if it fails or has more than one hop, return an error
749- match find_route (
750- source,
751- & route_params,
752- pathfinding_graph,
753- None ,
754- & WrappedLog { } ,
755- scorer,
756- & Default :: default ( ) ,
757- & [ 0 ; 32 ] ,
758- ) {
759- Ok ( route) => {
760- // Check if the route has exactly one hop
761- if route. paths . len ( ) == 1 && route. paths [ 0 ] . hops . len ( ) == 1 {
762- Ok ( route)
763- } else {
764- Err ( SimulationError :: SimulatedNetworkError (
765- "No direct route found" . to_string ( ) ,
766- ) )
767- }
768- } ,
769- Err ( e) => Err ( SimulationError :: SimulatedNetworkError ( e. err ) ) ,
770- }
771- }
772- }
773-
774- #[ tokio:: test]
775- async fn test_always_fail_pathfinder ( ) {
776- let channels = create_simulated_channels ( 3 , 1_000_000_000 ) ;
777- let routing_graph =
778- Arc :: new ( populate_network_graph ( channels. clone ( ) , Arc :: new ( SystemClock { } ) ) . unwrap ( ) ) ;
779-
780- let pathfinder = AlwaysFailPathFinder ;
781- let source = channels[ 0 ] . get_node_1_pubkey ( ) ;
782- let dest = channels[ 2 ] . get_node_2_pubkey ( ) ;
783-
784- let scorer = ProbabilisticScorer :: new (
785- ProbabilisticScoringDecayParameters :: default ( ) ,
786- routing_graph. clone ( ) ,
787- & WrappedLog { } ,
788- ) ;
789-
790- let result = pathfinder. find_route ( & source, dest, 100_000 , & routing_graph, ) ;
791-
792- // Should always fail
793- assert ! ( result. is_err( ) ) ;
794- }
795-
796- #[ tokio:: test]
797- async fn test_single_hop_only_pathfinder ( ) {
798- let channels = create_simulated_channels ( 3 , 1_000_000_000 ) ;
799- let routing_graph =
800- Arc :: new ( populate_network_graph ( channels. clone ( ) , Arc :: new ( SystemClock { } ) ) . unwrap ( ) ) ;
801-
802- let pathfinder = SingleHopOnlyPathFinder ;
803- let source = channels[ 0 ] . get_node_1_pubkey ( ) ;
804-
805- let scorer = ProbabilisticScorer :: new (
806- ProbabilisticScoringDecayParameters :: default ( ) ,
807- routing_graph. clone ( ) ,
808- & WrappedLog { } ,
809- ) ;
810-
811- // Test direct connection (should work)
812- let direct_dest = channels[ 0 ] . get_node_2_pubkey ( ) ;
813- let result = pathfinder. find_route ( & source, direct_dest, 100_000 , & routing_graph, ) ;
814-
815- if result. is_ok ( ) {
816- let route = result. unwrap ( ) ;
817- assert_eq ! ( route. paths[ 0 ] . hops. len( ) , 1 ) ; // Only one hop
818- }
819-
820- // Test indirect connection (should fail)
821- let indirect_dest = channels[ 2 ] . get_node_2_pubkey ( ) ;
822- let _result =
823- pathfinder. find_route ( & source, indirect_dest, 100_000 , & routing_graph, ) ;
824-
825- // May fail because no direct route exists
826- // (depends on your test network topology)
827- }
828-
829- /// Test that different pathfinders produce different behavior in payments
830- #[ tokio:: test]
831- async fn test_pathfinder_affects_payment_behavior ( ) {
832- let channels = create_simulated_channels ( 3 , 1_000_000_000 ) ;
833- let ( shutdown_trigger, shutdown_listener) = triggered:: trigger ( ) ;
834- let sim_graph = Arc :: new ( Mutex :: new (
835- SimGraph :: new (
836- channels. clone ( ) ,
837- TaskTracker :: new ( ) ,
838- Vec :: new ( ) ,
839- HashMap :: new ( ) , // Empty custom records
840- ( shutdown_trigger. clone ( ) , shutdown_listener. clone ( ) ) ,
841- )
842- . unwrap ( ) ,
843- ) ) ;
844- let routing_graph =
845- Arc :: new ( populate_network_graph ( channels. clone ( ) , Arc :: new ( SystemClock { } ) ) . unwrap ( ) ) ;
846-
847- // Create nodes with different pathfinders
848- let nodes_default = ln_node_from_graph (
849- sim_graph. clone ( ) ,
850- routing_graph. clone ( ) ,
851- SystemClock { } ,
852- simln_lib:: sim_node:: DefaultPathFinder ,
853- )
854- . await ;
855-
856- let nodes_fail = ln_node_from_graph (
857- sim_graph. clone ( ) ,
858- routing_graph. clone ( ) ,
859- SystemClock { } ,
860- AlwaysFailPathFinder ,
861- )
862- . await ;
863-
864- // Both should create the same structure
865- assert_eq ! ( nodes_default. len( ) , nodes_fail. len( ) ) ;
866- }
867- }
0 commit comments