Skip to content

Commit 04842b1

Browse files
committed
sim_node/feat: Implement Scoring for DefaultPathfinder
1 parent e5b94fa commit 04842b1

File tree

3 files changed

+302
-296
lines changed

3 files changed

+302
-296
lines changed

sim-cli/src/main.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ use simln_lib::{
1212
use simple_logger::SimpleLogger;
1313
use tokio_util::task::TaskTracker;
1414

15-
// Import the pathfinder types
16-
use simln_lib::sim_node::DefaultPathFinder;
17-
1815
#[tokio::main]
1916
async fn main() -> anyhow::Result<()> {
2017
// Enable tracing if building in developer mode.
@@ -37,9 +34,6 @@ async fn main() -> anyhow::Result<()> {
3734

3835
let tasks = TaskTracker::new();
3936

40-
// Create the pathfinder instance
41-
let pathfinder = DefaultPathFinder;
42-
4337
let (sim, validated_activities) = if sim_params.sim_network.is_empty() {
4438
create_simulation(&cli, &sim_params, tasks.clone()).await?
4539
} else {
@@ -58,7 +52,6 @@ async fn main() -> anyhow::Result<()> {
5852
tasks.clone(),
5953
interceptors,
6054
CustomRecords::default(),
61-
pathfinder,
6255
)
6356
.await?;
6457
(sim, validated_activities)

sim-cli/src/parsing.rs

Lines changed: 14 additions & 234 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use log::LevelFilter;
55
use serde::{Deserialize, Serialize};
66
use simln_lib::clock::SimulationClock;
77
use 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

1212
use 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

Comments
 (0)