Skip to content

Commit f65d20d

Browse files
committed
Remove LDK specific scoring from PathFinder trait
1 parent 75cb6f4 commit f65d20d

File tree

2 files changed

+80
-91
lines changed

2 files changed

+80
-91
lines changed

sim-cli/src/parsing.rs

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ struct NodeMapping {
225225
alias_node_map: HashMap<String, NodeInfo>,
226226
}
227227

228-
pub async fn create_simulation_with_network<P: for<'a> PathFinder<'a> + Clone + 'static>(
228+
pub async fn create_simulation_with_network<P: PathFinder + Clone + 'static>(
229229
cli: &Cli,
230230
sim_params: &SimParams,
231231
tasks: TaskTracker,
@@ -661,62 +661,63 @@ mod tests {
661661
#[derive(Clone)]
662662
pub struct AlwaysFailPathFinder;
663663

664-
impl<'a> PathFinder<'a> for AlwaysFailPathFinder {
664+
impl PathFinder for AlwaysFailPathFinder {
665665
fn find_route(
666666
&self,
667667
_source: &PublicKey,
668668
_dest: PublicKey,
669669
_amount_msat: u64,
670-
_pathfinding_graph: &NetworkGraph<&'a WrappedLog>,
671-
_scorer: &ProbabilisticScorer<Arc<NetworkGraph<&'a WrappedLog>>, &'a WrappedLog>,
670+
_pathfinding_graph: &NetworkGraph<&'static WrappedLog>,
672671
) -> Result<Route, SimulationError> {
673672
Err(SimulationError::SimulatedNetworkError(
674673
"No route found".to_string(),
675674
))
676675
}
677676
}
678677

679-
/// A pathfinder that only returns single-hop paths
678+
/// A pathfinder that only returns single-hop paths.
680679
#[derive(Clone)]
681680
pub struct SingleHopOnlyPathFinder;
682681

683-
impl<'a> PathFinder<'a> for SingleHopOnlyPathFinder {
682+
impl PathFinder for SingleHopOnlyPathFinder {
684683
fn find_route(
685684
&self,
686685
source: &PublicKey,
687686
dest: PublicKey,
688687
amount_msat: u64,
689-
pathfinding_graph: &NetworkGraph<&'a WrappedLog>,
690-
scorer: &ProbabilisticScorer<Arc<NetworkGraph<&'a WrappedLog>>, &'a WrappedLog>,
688+
pathfinding_graph: &NetworkGraph<&'static WrappedLog>,
691689
) -> Result<Route, SimulationError> {
692-
// Try to find a direct route only (single hop)
693-
let route_params = RouteParameters {
694-
payment_params: PaymentParameters::from_node_id(dest, 0)
695-
.with_max_total_cltv_expiry_delta(u32::MAX)
696-
.with_max_path_count(1)
697-
.with_max_channel_saturation_power_of_half(1),
698-
final_value_msat: amount_msat,
699-
max_total_routing_fee_msat: None,
700-
};
701-
702-
// Try to find a route - if it fails or has more than one hop, return an error
690+
let scorer = ProbabilisticScorer::new(
691+
ProbabilisticScoringDecayParameters::default(),
692+
pathfinding_graph,
693+
&WrappedLog {},
694+
);
695+
696+
// Try to find a route - if it fails or has more than one hop, return an error.
703697
match find_route(
704698
source,
705-
&route_params,
699+
&RouteParameters {
700+
payment_params: PaymentParameters::from_node_id(dest, 0)
701+
.with_max_total_cltv_expiry_delta(u32::MAX)
702+
.with_max_path_count(1)
703+
.with_max_channel_saturation_power_of_half(1),
704+
final_value_msat: amount_msat,
705+
max_total_routing_fee_msat: None,
706+
},
706707
pathfinding_graph,
707708
None,
708709
&WrappedLog {},
709-
scorer,
710+
&scorer,
710711
&Default::default(),
711712
&[0; 32],
712713
) {
713714
Ok(route) => {
714-
// Check if the route has exactly one hop
715+
// Only allow single-hop routes.
715716
if route.paths.len() == 1 && route.paths[0].hops.len() == 1 {
716717
Ok(route)
717718
} else {
718719
Err(SimulationError::SimulatedNetworkError(
719-
"No direct route found".to_string(),
720+
"Only single-hop routes allowed".to_string(),
720721
))
721722
}
722723
},
@@ -735,13 +736,7 @@ mod tests {
735736
let source = channels[0].get_node_1_pubkey();
736737
let dest = channels[2].get_node_2_pubkey();
737738

738-
let scorer = ProbabilisticScorer::new(
739-
ProbabilisticScoringDecayParameters::default(),
740-
routing_graph.clone(),
741-
&WrappedLog {},
742-
);
743-
744-
let result = pathfinder.find_route(&source, dest, 100_000, &routing_graph, &scorer);
739+
let result = pathfinder.find_route(&source, dest, 100_000, &routing_graph);
745740

746741
// Should always fail
747742
assert!(result.is_err());
@@ -756,15 +751,9 @@ mod tests {
756751
let pathfinder = SingleHopOnlyPathFinder;
757752
let source = channels[0].get_node_1_pubkey();
758753

759-
let scorer = ProbabilisticScorer::new(
760-
ProbabilisticScoringDecayParameters::default(),
761-
routing_graph.clone(),
762-
&WrappedLog {},
763-
);
764-
765754
// Test direct connection (should work)
766755
let direct_dest = channels[0].get_node_2_pubkey();
767-
let result = pathfinder.find_route(&source, direct_dest, 100_000, &routing_graph, &scorer);
756+
let result = pathfinder.find_route(&source, direct_dest, 100_000, &routing_graph);
768757

769758
if result.is_ok() {
770759
let route = result.unwrap();
@@ -773,8 +762,7 @@ mod tests {
773762

774763
// Test indirect connection (should fail)
775764
let indirect_dest = channels[2].get_node_2_pubkey();
776-
let _result =
777-
pathfinder.find_route(&source, indirect_dest, 100_000, &routing_graph, &scorer);
765+
let _result = pathfinder.find_route(&source, indirect_dest, 100_000, &routing_graph);
778766

779767
// May fail because no direct route exists
780768
// (depends on your test network topology)

simln-lib/src/sim_node.rs

Lines changed: 53 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ impl SimulatedChannel {
477477

478478
/// SimNetwork represents a high level network coordinator that is responsible for the task of actually propagating
479479
/// payments through the simulated network.
480-
trait SimNetwork: Send + Sync {
480+
pub trait SimNetwork: Send + Sync {
481481
/// Sends payments over the route provided through the network, reporting the final payment outcome to the sender
482482
/// channel provided.
483483
fn dispatch_payment(
@@ -495,30 +495,53 @@ trait SimNetwork: Send + Sync {
495495
}
496496

497497
/// A trait for custom pathfinding implementations.
498-
pub trait PathFinder<'a>: Send + Sync {
498+
/// Finds a route from the source node to the destination node for the specified amount.
499+
///
500+
/// # Arguments
501+
/// * `source` - The public key of the node initiating the payment.
502+
/// * `dest` - The public key of the destination node to receive the payment.
503+
/// * `amount_msat` - The amount to send in millisatoshis.
504+
/// * `pathfinding_graph` - The network graph containing channel topology and routing information.
505+
///
506+
/// # Returns
507+
/// Returns a `Route` containing the payment path, or a `SimulationError` if no route is found.
508+
509+
pub trait PathFinder: Send + Sync + Clone {
499510
fn find_route(
500511
&self,
501512
source: &PublicKey,
502513
dest: PublicKey,
503514
amount_msat: u64,
504-
pathfinding_graph: &NetworkGraph<&'a WrappedLog>,
505-
scorer: &ProbabilisticScorer<Arc<NetworkGraph<&'a WrappedLog>>, &'a WrappedLog>,
515+
pathfinding_graph: &NetworkGraph<&'static WrappedLog>,
506516
) -> Result<Route, SimulationError>;
507517
}
508518

509-
/// Default pathfinder that uses LDK's pathfinding algorithm.
519+
/// The default pathfinding implementation that uses LDK's built-in pathfinding algorithm.
510520
#[derive(Clone)]
511521
pub struct DefaultPathFinder;
512522

513-
impl<'a> PathFinder<'a> for DefaultPathFinder {
523+
impl DefaultPathFinder {
524+
pub fn new() -> Self {
525+
Self
526+
}
527+
}
528+
529+
impl PathFinder for DefaultPathFinder {
514530
fn find_route(
515531
&self,
516532
source: &PublicKey,
517533
dest: PublicKey,
518534
amount_msat: u64,
519-
pathfinding_graph: &NetworkGraph<&'a WrappedLog>,
520-
scorer: &ProbabilisticScorer<Arc<NetworkGraph<&'a WrappedLog>>, &'a WrappedLog>,
535+
pathfinding_graph: &NetworkGraph<&'static WrappedLog>,
521536
) -> Result<Route, SimulationError> {
537+
let scorer_graph = NetworkGraph::new(bitcoin::Network::Regtest, &WrappedLog {});
538+
let scorer = ProbabilisticScorer::new(
539+
ProbabilisticScoringDecayParameters::default(),
540+
Arc::new(scorer_graph),
541+
&WrappedLog {},
542+
);
543+
544+
// Call LDK's find_route with the scorer (LDK-specific requirement)
522545
find_route(
523546
source,
524547
&RouteParameters {
@@ -529,10 +552,10 @@ impl<'a> PathFinder<'a> for DefaultPathFinder {
529552
final_value_msat: amount_msat,
530553
max_total_routing_fee_msat: None,
531554
},
532-
pathfinding_graph,
555+
pathfinding_graph, // This is the real network graph used for pathfinding
533556
None,
534557
&WrappedLog {},
535-
scorer,
558+
&scorer, // LDK requires a scorer, so we provide a simple one
536559
&Default::default(),
537560
&[0; 32],
538561
)
@@ -544,45 +567,32 @@ impl<'a> PathFinder<'a> for DefaultPathFinder {
544567
/// all functionality through to a coordinating simulation network. This implementation contains both the [`SimNetwork`]
545568
/// implementation that will allow us to dispatch payments and a read-only NetworkGraph that is used for pathfinding.
546569
/// While these two could be combined, we re-use the LDK-native struct to allow re-use of their pathfinding logic.
547-
struct SimNode<'a, T: SimNetwork, P: PathFinder<'a> = DefaultPathFinder> {
570+
pub struct SimNode<T: SimNetwork, P: PathFinder = DefaultPathFinder> {
548571
info: NodeInfo,
549572
/// The underlying execution network that will be responsible for dispatching payments.
550573
network: Arc<Mutex<T>>,
551574
/// Tracks the channel that will provide updates for payments by hash.
552575
in_flight: HashMap<PaymentHash, Receiver<Result<PaymentResult, LightningError>>>,
553576
/// A read-only graph used for pathfinding.
554-
pathfinding_graph: Arc<NetworkGraph<&'a WrappedLog>>,
555-
/// Probabilistic scorer used to rank paths through the network for routing. This is reused across
556-
/// multiple payments to maintain scoring state.
557-
scorer: ProbabilisticScorer<Arc<NetworkGraph<&'a WrappedLog>>, &'a WrappedLog>,
577+
pathfinding_graph: Arc<NetworkGraph<&'static WrappedLog>>,
558578
/// The pathfinder implementation to use for finding routes
559579
pathfinder: P,
560580
}
561581

562-
impl<'a, T: SimNetwork, P: PathFinder<'a>> SimNode<'a, T, P> {
582+
impl<T: SimNetwork, P: PathFinder> SimNode<T, P> {
563583
/// Creates a new simulation node that refers to the high level network coordinator provided to process payments
564584
/// on its behalf. The pathfinding graph is provided separately so that each node can handle its own pathfinding.
565585
pub fn new(
566586
pubkey: PublicKey,
567587
payment_network: Arc<Mutex<T>>,
568-
pathfinding_graph: Arc<NetworkGraph<&'a WrappedLog>>,
588+
pathfinding_graph: Arc<NetworkGraph<&'static WrappedLog>>,
569589
pathfinder: P,
570590
) -> Self {
571-
// Initialize the probabilistic scorer with default parameters for learning from payment
572-
// history. These parameters control how much successful/failed payments affect routing
573-
// scores and how quickly these scores decay over time.
574-
let scorer = ProbabilisticScorer::new(
575-
ProbabilisticScoringDecayParameters::default(),
576-
pathfinding_graph.clone(),
577-
&WrappedLog {},
578-
);
579-
580591
SimNode {
581592
info: node_info(pubkey),
582593
network: payment_network,
583594
in_flight: HashMap::new(),
584595
pathfinding_graph,
585-
scorer,
586596
pathfinder,
587597
}
588598
}
@@ -602,7 +612,7 @@ fn node_info(pubkey: PublicKey) -> NodeInfo {
602612
}
603613

604614
#[async_trait]
605-
impl<'a, T: SimNetwork, P: PathFinder<'a>> LightningNode for SimNode<'a, T, P> {
615+
impl<T: SimNetwork, P: PathFinder> LightningNode for SimNode<T, P> {
606616
fn get_info(&self) -> &NodeInfo {
607617
&self.info
608618
}
@@ -624,7 +634,6 @@ impl<'a, T: SimNetwork, P: PathFinder<'a>> LightningNode for SimNode<'a, T, P> {
624634
dest,
625635
amount_msat,
626636
&self.pathfinding_graph,
627-
&self.scorer,
628637
) {
629638
Ok(route) => route,
630639
Err(e) => {
@@ -1001,7 +1010,7 @@ pub async fn ln_node_from_graph<P>(
10011010
pathfinder: P,
10021011
) -> HashMap<PublicKey, Arc<Mutex<dyn LightningNode>>>
10031012
where
1004-
P: for<'a> PathFinder<'a> + Clone + 'static,
1013+
P: PathFinder + 'static,
10051014
{
10061015
let mut nodes: HashMap<PublicKey, Arc<Mutex<dyn LightningNode>>> = HashMap::new();
10071016

@@ -1499,7 +1508,6 @@ mod tests {
14991508
use mockall::mock;
15001509
use ntest::assert_true;
15011510
use std::time::Duration;
1502-
use tokio::sync::oneshot;
15031511
use tokio::time::{self, timeout};
15041512

15051513
/// Creates a test channel policy with its maximum HTLC size set to half of the in flight limit of the channel.
@@ -1889,7 +1897,12 @@ mod tests {
18891897

18901898
// Create a simulated node for the first channel in our network.
18911899
let pk = channels[0].node_1.policy.pubkey;
1892-
let mut node = SimNode::new(pk, sim_network.clone(), Arc::new(graph), DefaultPathFinder);
1900+
let mut node = SimNode::new(
1901+
pk,
1902+
sim_network.clone(),
1903+
Arc::new(graph),
1904+
DefaultPathFinder::new(),
1905+
);
18931906

18941907
// Prime mock to return node info from lookup and assert that we get the pubkey we're expecting.
18951908
let lookup_pk = channels[3].node_1.policy.pubkey;
@@ -1974,16 +1987,15 @@ mod tests {
19741987
}
19751988

19761989
/// Contains elements required to test dispatch_payment functionality.
1977-
struct DispatchPaymentTestKit<'a> {
1990+
struct DispatchPaymentTestKit {
19781991
graph: SimGraph,
19791992
nodes: Vec<PublicKey>,
1980-
routing_graph: Arc<NetworkGraph<&'a WrappedLog>>,
1981-
scorer: ProbabilisticScorer<Arc<NetworkGraph<&'a WrappedLog>>, &'a WrappedLog>,
1993+
routing_graph: Arc<NetworkGraph<&'static WrappedLog>>,
19821994
shutdown: (Trigger, Listener),
19831995
pathfinder: DefaultPathFinder,
19841996
}
19851997

1986-
impl DispatchPaymentTestKit<'_> {
1998+
impl DispatchPaymentTestKit {
19871999
/// Creates a test graph with a set of nodes connected by three channels, with all the capacity of the channel
19882000
/// on the side of the first node. For example, if called with capacity = 100 it will set up the following
19892001
/// network:
@@ -2001,12 +2013,6 @@ mod tests {
20012013
populate_network_graph(channels.clone(), Arc::new(SystemClock {})).unwrap(),
20022014
);
20032015

2004-
let scorer = ProbabilisticScorer::new(
2005-
ProbabilisticScoringDecayParameters::default(),
2006-
routing_graph.clone(),
2007-
&WrappedLog {},
2008-
);
2009-
20102016
// Collect pubkeys in-order, pushing the last node on separately because they don't have an outgoing
20112017
// channel (they are not node_1 in any channel, only node_2).
20122018
let mut nodes = channels
@@ -2027,9 +2033,8 @@ mod tests {
20272033
.expect("could not create test graph"),
20282034
nodes,
20292035
routing_graph,
2030-
scorer,
20312036
shutdown: shutdown_clone,
2032-
pathfinder: DefaultPathFinder,
2037+
pathfinder: DefaultPathFinder::new(),
20332038
};
20342039

20352040
// Assert that our channel balance is all on the side of the channel opener when we start up.
@@ -2074,18 +2079,14 @@ mod tests {
20742079
) -> (Route, Result<PaymentResult, LightningError>) {
20752080
let route = self
20762081
.pathfinder
2077-
.find_route(&source, dest, amt, &self.routing_graph, &self.scorer)
2082+
.find_route(&source, dest, amt, &self.routing_graph)
20782083
.unwrap();
2084+
let (sender, receiver) = tokio::sync::oneshot::channel();
20792085

2080-
let (sender, receiver) = oneshot::channel();
20812086
self.graph
2082-
.dispatch_payment(source, route.clone(), PaymentHash([1; 32]), sender);
2083-
2084-
let payment_result = timeout(Duration::from_millis(10), receiver).await;
2085-
// Assert that we receive from the channel or fail.
2086-
assert!(payment_result.is_ok());
2087+
.dispatch_payment(source, route.clone(), PaymentHash([0; 32]), sender);
20872088

2088-
(route, payment_result.unwrap().unwrap())
2089+
(route, receiver.await.unwrap())
20892090
}
20902091

20912092
// Sets the balance on the channel to the tuple provided, used to arrange liquidity for testing.

0 commit comments

Comments
 (0)