@@ -467,7 +467,7 @@ impl SimulatedChannel {
467
467
468
468
/// SimNetwork represents a high level network coordinator that is responsible for the task of actually propagating
469
469
/// payments through the simulated network.
470
- trait SimNetwork : Send + Sync {
470
+ pub trait SimNetwork : Send + Sync {
471
471
/// Sends payments over the route provided through the network, reporting the final payment outcome to the sender
472
472
/// channel provided.
473
473
fn dispatch_payment (
@@ -488,7 +488,7 @@ trait SimNetwork: Send + Sync {
488
488
/// all functionality through to a coordinating simulation network. This implementation contains both the [`SimNetwork`]
489
489
/// implementation that will allow us to dispatch payments and a read-only NetworkGraph that is used for pathfinding.
490
490
/// 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 > {
492
492
info : NodeInfo ,
493
493
/// The underlying execution network that will be responsible for dispatching payments.
494
494
network : Arc < Mutex < T > > ,
@@ -526,6 +526,37 @@ impl<'a, T: SimNetwork> SimNode<'a, T> {
526
526
scorer,
527
527
}
528
528
}
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
+ }
529
560
}
530
561
531
562
/// 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> {
642
673
}
643
674
644
675
/// 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.
646
678
async fn track_payment (
647
679
& mut self ,
648
680
hash : & PaymentHash ,
@@ -1470,6 +1502,7 @@ mod tests {
1470
1502
use super :: * ;
1471
1503
use crate :: clock:: SystemClock ;
1472
1504
use crate :: test_utils:: get_random_keypair;
1505
+ use lightning:: routing:: router:: build_route_from_hops;
1473
1506
use lightning:: routing:: router:: Route ;
1474
1507
use mockall:: mock;
1475
1508
use ntest:: assert_true;
@@ -2244,6 +2277,50 @@ mod tests {
2244
2277
test_kit. graph . tasks . wait ( ) . await ;
2245
2278
}
2246
2279
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
+
2247
2324
fn create_intercept_request ( shutdown_listener : Listener ) -> InterceptRequest {
2248
2325
let ( _, pubkey) = get_random_keypair ( ) ;
2249
2326
InterceptRequest {
0 commit comments