Skip to content

Commit dce1f3f

Browse files
committed
simln-lib/feat: Surface send_to_route for SimGraph
1 parent 466fea5 commit dce1f3f

File tree

1 file changed

+80
-3
lines changed

1 file changed

+80
-3
lines changed

simln-lib/src/sim_node.rs

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ impl SimulatedChannel {
467467

468468
/// SimNetwork represents a high level network coordinator that is responsible for the task of actually propagating
469469
/// payments through the simulated network.
470-
trait SimNetwork: Send + Sync {
470+
pub trait SimNetwork: Send + Sync {
471471
/// Sends payments over the route provided through the network, reporting the final payment outcome to the sender
472472
/// channel provided.
473473
fn dispatch_payment(
@@ -488,7 +488,7 @@ trait SimNetwork: Send + Sync {
488488
/// all functionality through to a coordinating simulation network. This implementation contains both the [`SimNetwork`]
489489
/// implementation that will allow us to dispatch payments and a read-only NetworkGraph that is used for pathfinding.
490490
/// 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> {
491+
pub struct SimNode<'a, T: SimNetwork> {
492492
info: NodeInfo,
493493
/// The underlying execution network that will be responsible for dispatching payments.
494494
network: Arc<Mutex<T>>,
@@ -526,6 +526,37 @@ impl<'a, T: SimNetwork> SimNode<'a, T> {
526526
scorer,
527527
}
528528
}
529+
530+
/// Dispatches a payment to a specified route.
531+
/// The [`lightning::routing::router::build_route_from_hops`] function can be used to build the route to be passed here.
532+
///
533+
/// **Note:** The payment hash passed in here should be used in track_payment to track the payment outcome.
534+
pub async fn send_to_route(
535+
&mut self,
536+
route: Route,
537+
payment_hash: PaymentHash,
538+
) -> Result<(), LightningError> {
539+
let (sender, receiver) = channel();
540+
541+
// Check for payment hash collision, failing the payment if we happen to repeat one.
542+
match self.in_flight.entry(payment_hash) {
543+
Entry::Occupied(_) => {
544+
return Err(LightningError::SendPaymentError(
545+
"payment hash exists".to_string(),
546+
));
547+
},
548+
Entry::Vacant(vacant) => {
549+
vacant.insert(receiver);
550+
},
551+
}
552+
553+
self.network
554+
.lock()
555+
.await
556+
.dispatch_payment(self.info.pubkey, route, payment_hash, sender);
557+
558+
Ok(())
559+
}
529560
}
530561

531562
/// Produces the node info for a mocked node, filling in the features that the simulator requires.
@@ -642,7 +673,8 @@ impl<T: SimNetwork> LightningNode for SimNode<'_, T> {
642673
}
643674

644675
/// track_payment blocks until a payment outcome is returned for the payment hash provided, or the shutdown listener
645-
/// provided is triggered. This call will fail if the hash provided was not obtained by calling send_payment first.
676+
/// provided is triggered. This call will fail if the hash provided was not obtained from send_payment or passed
677+
/// into send_to_route first.
646678
async fn track_payment(
647679
&mut self,
648680
hash: &PaymentHash,
@@ -1470,6 +1502,7 @@ mod tests {
14701502
use super::*;
14711503
use crate::clock::SystemClock;
14721504
use crate::test_utils::get_random_keypair;
1505+
use lightning::routing::router::build_route_from_hops;
14731506
use lightning::routing::router::Route;
14741507
use mockall::mock;
14751508
use ntest::assert_true;
@@ -2244,6 +2277,50 @@ mod tests {
22442277
test_kit.graph.tasks.wait().await;
22452278
}
22462279

2280+
#[tokio::test]
2281+
async fn test_send_and_track_payment_to_route() {
2282+
let chan_capacity = 500_000_000;
2283+
let test_kit =
2284+
DispatchPaymentTestKit::new(chan_capacity, vec![], CustomRecords::default()).await;
2285+
2286+
let mut node = SimNode::new(
2287+
test_kit.nodes[0],
2288+
Arc::new(Mutex::new(test_kit.graph)),
2289+
test_kit.routing_graph.clone(),
2290+
);
2291+
2292+
let route = build_route_from_hops(
2293+
&test_kit.nodes[0],
2294+
&[test_kit.nodes[1], test_kit.nodes[2], test_kit.nodes[3]],
2295+
&RouteParameters {
2296+
payment_params: PaymentParameters::from_node_id(*test_kit.nodes.last().unwrap(), 0)
2297+
.with_max_total_cltv_expiry_delta(u32::MAX)
2298+
// TODO: set non-zero value to support MPP.
2299+
.with_max_path_count(1)
2300+
// Allow sending htlcs up to 50% of the channel's capacity.
2301+
.with_max_channel_saturation_power_of_half(1),
2302+
final_value_msat: 20_000,
2303+
max_total_routing_fee_msat: None,
2304+
},
2305+
&test_kit.routing_graph,
2306+
&WrappedLog {},
2307+
&[0; 32],
2308+
)
2309+
.unwrap();
2310+
2311+
let preimage = PaymentPreimage(rand::random());
2312+
let payment_hash = preimage.into();
2313+
node.send_to_route(route, payment_hash).await.unwrap();
2314+
2315+
let (_, shutdown_listener) = triggered::trigger();
2316+
let result = node
2317+
.track_payment(&payment_hash, shutdown_listener)
2318+
.await
2319+
.unwrap();
2320+
2321+
assert!(matches!(result.payment_outcome, PaymentOutcome::Success));
2322+
}
2323+
22472324
fn create_intercept_request(shutdown_listener: Listener) -> InterceptRequest {
22482325
let (_, pubkey) = get_random_keypair();
22492326
InterceptRequest {

0 commit comments

Comments
 (0)