Skip to content

Commit 8592ce2

Browse files
committed
Implement Scoring for DefaultPathfinder
1 parent 2274fe1 commit 8592ce2

File tree

3 files changed

+314
-272
lines changed

3 files changed

+314
-272
lines changed

sim-cli/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ async fn main() -> anyhow::Result<()> {
3838
let tasks = TaskTracker::new();
3939

4040
// Create the pathfinder instance
41-
let pathfinder = DefaultPathFinder;
41+
let pathfinder = DefaultPathFinder::default();
4242

4343
let (sim, validated_activities) = if sim_params.sim_network.is_empty() {
4444
create_simulation(&cli, &sim_params, tasks.clone()).await?

sim-cli/src/parsing.rs

Lines changed: 10 additions & 234 deletions
Original file line numberDiff line numberDiff line change
@@ -243,28 +243,21 @@ struct NodeMapping {
243243
}
244244

245245
pub async fn create_simulation_with_network<P: PathFinder + Clone + 'static>(
246-
cli: &Cli,
246+
cfg: SimulationCfg,
247247
sim_params: &SimParams,
248248
clock: Arc<SimulationClock>,
249249
tasks: TaskTracker,
250250
interceptors: Vec<Arc<dyn Interceptor>>,
251251
custom_records: CustomRecords,
252-
<<<<<<< HEAD
253-
<<<<<<< HEAD
252+
pathfinder: P,
254253
) -> Result<
255254
(
256255
Simulation<SimulationClock>,
257256
Vec<ActivityDefinition>,
258-
HashMap<PublicKey, Arc<Mutex<SimNode<SimGraph, SimulationClock>>>>,
257+
HashMap<PublicKey, Arc<Mutex<dyn LightningNode>>>,
259258
),
260259
anyhow::Error,
261260
> {
262-
=======
263-
=======
264-
>>>>>>> 079041b (Fix formatting)
265-
pathfinder: P,
266-
) -> Result<(Simulation<SimulationClock>, Vec<ActivityDefinition>), anyhow::Error> {
267-
let cfg: SimulationCfg = SimulationCfg::try_from(cli)?;
268261
let SimParams {
269262
nodes: _,
270263
sim_network,
@@ -308,14 +301,17 @@ pub async fn create_simulation_with_network<P: PathFinder + Clone + 'static>(
308301
.map_err(|e| SimulationError::SimulatedNetworkError(format!("{:?}", e)))?,
309302
);
310303

311-
<<<<<<< HEAD
312-
<<<<<<< HEAD
313-
<<<<<<< HEAD
314304
// We want the full set of nodes in our graph to return to the caller so that they can take
315305
// custom actions on the simulated network. For the nodes we'll pass our simulation, cast them
316306
// to a dyn trait and exclude any nodes that shouldn't be included in random activity
317307
// generation.
318-
let nodes = ln_node_from_graph(simulation_graph.clone(), routing_graph, clock.clone()).await?;
308+
let nodes = ln_node_from_graph(
309+
simulation_graph.clone(),
310+
routing_graph,
311+
clock.clone(),
312+
pathfinder,
313+
)
314+
.await?;
319315
let mut nodes_dyn: HashMap<_, Arc<Mutex<dyn LightningNode>>> = nodes
320316
.iter()
321317
.map(|(pk, node)| (*pk, Arc::clone(node) as Arc<Mutex<dyn LightningNode>>))
@@ -324,13 +320,6 @@ pub async fn create_simulation_with_network<P: PathFinder + Clone + 'static>(
324320
nodes_dyn.remove(pk);
325321
}
326322

327-
=======
328-
=======
329-
>>>>>>> 079041b (Fix formatting)
330-
// Pass the pathfinder to ln_node_from_graph
331-
=======
332-
>>>>>>> 4a0f276 (Remove LDK specific scoring from PathFinder trait)
333-
let nodes = ln_node_from_graph(simulation_graph.clone(), routing_graph, pathfinder).await;
334323
let validated_activities =
335324
get_validated_activities(&nodes_dyn, nodes_info, sim_params.activity.clone()).await?;
336325

@@ -645,216 +634,3 @@ pub async fn get_validated_activities(
645634

646635
validate_activities(activity.to_vec(), activity_validation_params).await
647636
}
648-
649-
#[cfg(test)]
650-
mod tests {
651-
use super::*;
652-
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
653-
use lightning::routing::gossip::NetworkGraph;
654-
use lightning::routing::router::{find_route, PaymentParameters, Route, RouteParameters};
655-
use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringDecayParameters};
656-
use rand::RngCore;
657-
use simln_lib::clock::SystemClock;
658-
use simln_lib::sim_node::{
659-
ln_node_from_graph, populate_network_graph, PathFinder, SimGraph, WrappedLog,
660-
};
661-
use simln_lib::SimulationError;
662-
use std::sync::Arc;
663-
use tokio::sync::Mutex;
664-
use tokio_util::task::TaskTracker;
665-
666-
/// Gets a key pair generated in a pseudorandom way.
667-
fn get_random_keypair() -> (SecretKey, PublicKey) {
668-
let secp = Secp256k1::new();
669-
let mut rng = rand::thread_rng();
670-
let mut bytes = [0u8; 32];
671-
rng.fill_bytes(&mut bytes);
672-
let secret_key = SecretKey::from_slice(&bytes).expect("Failed to create secret key");
673-
let public_key = PublicKey::from_secret_key(&secp, &secret_key);
674-
(secret_key, public_key)
675-
}
676-
677-
/// Helper function to create simulated channels for testing.
678-
fn create_simulated_channels(num_channels: usize, capacity_msat: u64) -> Vec<SimulatedChannel> {
679-
let mut channels = Vec::new();
680-
for i in 0..num_channels {
681-
let (_node1_sk, node1_pubkey) = get_random_keypair();
682-
let (_node2_sk, node2_pubkey) = get_random_keypair();
683-
684-
let channel = SimulatedChannel::new(
685-
capacity_msat,
686-
ShortChannelID::from(i as u64),
687-
ChannelPolicy {
688-
pubkey: node1_pubkey,
689-
max_htlc_count: 483,
690-
max_in_flight_msat: capacity_msat / 2,
691-
min_htlc_size_msat: 1000,
692-
max_htlc_size_msat: capacity_msat / 2,
693-
cltv_expiry_delta: 144,
694-
base_fee: 1000,
695-
fee_rate_prop: 100,
696-
},
697-
ChannelPolicy {
698-
pubkey: node2_pubkey,
699-
max_htlc_count: 483,
700-
max_in_flight_msat: capacity_msat / 2,
701-
min_htlc_size_msat: 1000,
702-
max_htlc_size_msat: capacity_msat / 2,
703-
cltv_expiry_delta: 144,
704-
base_fee: 1000,
705-
fee_rate_prop: 100,
706-
},
707-
);
708-
channels.push(channel);
709-
}
710-
channels
711-
}
712-
713-
/// A pathfinder that always fails to find a path.
714-
#[derive(Clone)]
715-
pub struct AlwaysFailPathFinder;
716-
717-
impl PathFinder for AlwaysFailPathFinder {
718-
fn find_route(
719-
&self,
720-
_source: &PublicKey,
721-
_dest: PublicKey,
722-
_amount_msat: u64,
723-
_pathfinding_graph: &NetworkGraph<&'static WrappedLog>,
724-
) -> Result<Route, SimulationError> {
725-
Err(SimulationError::SimulatedNetworkError(
726-
"No route found".to_string(),
727-
))
728-
}
729-
}
730-
731-
/// A pathfinder that only returns single-hop paths.
732-
#[derive(Clone)]
733-
pub struct SingleHopOnlyPathFinder;
734-
735-
impl PathFinder for SingleHopOnlyPathFinder {
736-
fn find_route(
737-
&self,
738-
source: &PublicKey,
739-
dest: PublicKey,
740-
amount_msat: u64,
741-
pathfinding_graph: &NetworkGraph<&'static WrappedLog>,
742-
) -> Result<Route, SimulationError> {
743-
let scorer = ProbabilisticScorer::new(
744-
ProbabilisticScoringDecayParameters::default(),
745-
pathfinding_graph,
746-
&WrappedLog {},
747-
);
748-
749-
// Try to find a route - if it fails or has more than one hop, return an error.
750-
match find_route(
751-
source,
752-
&RouteParameters {
753-
payment_params: PaymentParameters::from_node_id(dest, 0)
754-
.with_max_total_cltv_expiry_delta(u32::MAX)
755-
.with_max_path_count(1)
756-
.with_max_channel_saturation_power_of_half(1),
757-
final_value_msat: amount_msat,
758-
max_total_routing_fee_msat: None,
759-
},
760-
pathfinding_graph,
761-
None,
762-
&WrappedLog {},
763-
&scorer,
764-
&Default::default(),
765-
&[0; 32],
766-
) {
767-
Ok(route) => {
768-
// Only allow single-hop routes.
769-
if route.paths.len() == 1 && route.paths[0].hops.len() == 1 {
770-
Ok(route)
771-
} else {
772-
Err(SimulationError::SimulatedNetworkError(
773-
"Only single-hop routes allowed".to_string(),
774-
))
775-
}
776-
},
777-
Err(e) => Err(SimulationError::SimulatedNetworkError(e.err)),
778-
}
779-
}
780-
}
781-
782-
#[tokio::test]
783-
async fn test_always_fail_pathfinder() {
784-
let channels = create_simulated_channels(3, 1_000_000_000);
785-
let routing_graph =
786-
Arc::new(populate_network_graph(channels.clone(), Arc::new(SystemClock {})).unwrap());
787-
788-
let pathfinder = AlwaysFailPathFinder;
789-
let source = channels[0].get_node_1_pubkey();
790-
let dest = channels[2].get_node_2_pubkey();
791-
792-
let result = pathfinder.find_route(&source, dest, 100_000, &routing_graph);
793-
794-
// Should always fail.
795-
assert!(result.is_err());
796-
}
797-
798-
#[tokio::test]
799-
async fn test_single_hop_only_pathfinder() {
800-
let channels = create_simulated_channels(3, 1_000_000_000);
801-
let routing_graph =
802-
Arc::new(populate_network_graph(channels.clone(), Arc::new(SystemClock {})).unwrap());
803-
804-
let pathfinder = SingleHopOnlyPathFinder;
805-
let source = channels[0].get_node_1_pubkey();
806-
807-
// Test direct connection (should work).
808-
let direct_dest = channels[0].get_node_2_pubkey();
809-
let result = pathfinder.find_route(&source, direct_dest, 100_000, &routing_graph);
810-
811-
if result.is_ok() {
812-
let route = result.unwrap();
813-
assert_eq!(route.paths[0].hops.len(), 1); // Only one hop
814-
}
815-
816-
// Test indirect connection (should fail).
817-
let indirect_dest = channels[2].get_node_2_pubkey();
818-
let _result = pathfinder.find_route(&source, indirect_dest, 100_000, &routing_graph);
819-
820-
// May fail because no direct route exists.
821-
// (depends on your test network topology)
822-
}
823-
824-
/// Test that different pathfinders produce different behavior in payments.
825-
#[tokio::test]
826-
async fn test_pathfinder_affects_payment_behavior() {
827-
let channels = create_simulated_channels(3, 1_000_000_000);
828-
let (shutdown_trigger, shutdown_listener) = triggered::trigger();
829-
let sim_graph = Arc::new(Mutex::new(
830-
SimGraph::new(
831-
channels.clone(),
832-
TaskTracker::new(),
833-
Vec::new(),
834-
HashMap::new(), // Empty custom records
835-
(shutdown_trigger.clone(), shutdown_listener.clone()),
836-
)
837-
.unwrap(),
838-
));
839-
let routing_graph =
840-
Arc::new(populate_network_graph(channels.clone(), Arc::new(SystemClock {})).unwrap());
841-
842-
// Create nodes with different pathfinders.
843-
let nodes_default = ln_node_from_graph(
844-
sim_graph.clone(),
845-
routing_graph.clone(),
846-
simln_lib::sim_node::DefaultPathFinder,
847-
)
848-
.await;
849-
850-
let nodes_fail = ln_node_from_graph(
851-
sim_graph.clone(),
852-
routing_graph.clone(),
853-
AlwaysFailPathFinder,
854-
)
855-
.await;
856-
857-
// Both should create the same structure.
858-
assert_eq!(nodes_default.len(), nodes_fail.len());
859-
}
860-
}

0 commit comments

Comments
 (0)