@@ -1041,9 +1041,11 @@ impl UtxoLookup for UtxoValidator {
1041
1041
1042
1042
#[ cfg( test) ]
1043
1043
mod tests {
1044
- use crate :: test_utils:: get_random_keypair;
1045
-
1046
1044
use super :: * ;
1045
+ use crate :: test_utils:: get_random_keypair;
1046
+ use bitcoin:: secp256k1:: PublicKey ;
1047
+ use lightning:: routing:: router:: Route ;
1048
+ use mockall:: mock;
1047
1049
1048
1050
/// Creates a test channel policy with its maximum HTLC size set to half of the in flight limit of the channel.
1049
1051
/// The minimum HTLC size is hardcoded to 2 so that we can fall beneath this value with a 1 msat htlc.
@@ -1061,6 +1063,58 @@ mod tests {
1061
1063
}
1062
1064
}
1063
1065
1066
+ /// Creates a set of n simulated channels connected in a chain of channels, where the short channel ID of each
1067
+ /// channel is its index in the chain of channels and all capacity is on the side of the node that opened the
1068
+ /// channel.
1069
+ ///
1070
+ /// For example if n = 3 it will produce: node_1 -- node_2 -- node_3 -- node_4, connected by channels.
1071
+ fn create_simulated_channels ( n : u64 , capacity_msat : u64 ) -> Vec < SimulatedChannel > {
1072
+ let mut channels: Vec < SimulatedChannel > = vec ! [ ] ;
1073
+ let ( _, first_node) = get_random_keypair ( ) ;
1074
+
1075
+ // Create channels in a ring so that we'll get long payment paths.
1076
+ let mut node_1 = first_node;
1077
+ for i in 0 ..n {
1078
+ // Generate a new random node pubkey.
1079
+ let ( _, node_2) = get_random_keypair ( ) ;
1080
+
1081
+ let node_1_to_2 = ChannelPolicy {
1082
+ pubkey : node_1,
1083
+ max_htlc_count : 483 ,
1084
+ max_in_flight_msat : capacity_msat / 2 ,
1085
+ min_htlc_size_msat : 1 ,
1086
+ max_htlc_size_msat : capacity_msat / 2 ,
1087
+ cltv_expiry_delta : 40 ,
1088
+ base_fee : 1000 * i,
1089
+ fee_rate_prop : 1500 * i,
1090
+ } ;
1091
+
1092
+ let node_2_to_1 = ChannelPolicy {
1093
+ pubkey : node_2,
1094
+ max_htlc_count : 483 ,
1095
+ max_in_flight_msat : capacity_msat / 2 ,
1096
+ min_htlc_size_msat : 1 ,
1097
+ max_htlc_size_msat : capacity_msat / 2 ,
1098
+ cltv_expiry_delta : 40 + 10 * i as u32 ,
1099
+ base_fee : 2000 * i,
1100
+ fee_rate_prop : i,
1101
+ } ;
1102
+
1103
+ channels. push ( SimulatedChannel {
1104
+ capacity_msat,
1105
+ // Unique channel ID per link.
1106
+ short_channel_id : ShortChannelID :: from ( i) ,
1107
+ node_1 : ChannelState :: new ( node_1_to_2, capacity_msat) ,
1108
+ node_2 : ChannelState :: new ( node_2_to_1, 0 ) ,
1109
+ } ) ;
1110
+
1111
+ // Progress source ID to create a chain of nodes.
1112
+ node_1 = node_2;
1113
+ }
1114
+
1115
+ channels
1116
+ }
1117
+
1064
1118
macro_rules! assert_channel_balances {
1065
1119
( $channel_state: expr, $local_balance: expr, $in_flight_len: expr, $in_flight_total: expr) => {
1066
1120
assert_eq!( $channel_state. local_balance_msat, $local_balance) ;
@@ -1354,4 +1408,102 @@ mod tests {
1354
1408
Err ( ForwardingError :: NodeNotFound ( _) )
1355
1409
) ) ;
1356
1410
}
1411
+
1412
+ mock ! {
1413
+ Network { }
1414
+
1415
+ #[ async_trait]
1416
+ impl SimNetwork for Network {
1417
+ fn dispatch_payment(
1418
+ & mut self ,
1419
+ source: PublicKey ,
1420
+ route: Route ,
1421
+ payment_hash: PaymentHash ,
1422
+ sender: Sender <Result <PaymentResult , LightningError >>,
1423
+ ) ;
1424
+
1425
+ async fn lookup_node( & self , node: & PublicKey ) -> Result <( NodeInfo , Vec <u64 >) , LightningError >;
1426
+ }
1427
+ }
1428
+
1429
+ /// Tests the functionality of a `SimNode`, mocking out the `SimNetwork` that is responsible for payment
1430
+ /// propagation to isolate testing to just the implementation of `LightningNode`.
1431
+ #[ tokio:: test]
1432
+ async fn test_simulated_node ( ) {
1433
+ // Mock out our network and create a routing graph with 5 hops.
1434
+ let mock = MockNetwork :: new ( ) ;
1435
+ let sim_network = Arc :: new ( Mutex :: new ( mock) ) ;
1436
+ let channels = create_simulated_channels ( 5 , 300000000 ) ;
1437
+ let graph = populate_network_graph ( channels. clone ( ) ) . unwrap ( ) ;
1438
+
1439
+ // Create a simulated node for the first channel in our network.
1440
+ let pk = channels[ 0 ] . node_1 . policy . pubkey ;
1441
+ let mut node = SimNode :: new ( pk, sim_network. clone ( ) , Arc :: new ( graph) ) ;
1442
+
1443
+ // Prime mock to return node info from lookup and assert that we get the pubkey we're expecting.
1444
+ let lookup_pk = channels[ 3 ] . node_1 . policy . pubkey ;
1445
+ sim_network
1446
+ . lock ( )
1447
+ . await
1448
+ . expect_lookup_node ( )
1449
+ . returning ( move |_| Ok ( ( node_info ( lookup_pk) , vec ! [ 1 , 2 , 3 ] ) ) ) ;
1450
+
1451
+ // Assert that we get three channels from the mock.
1452
+ let node_info = node. get_node_info ( & lookup_pk) . await . unwrap ( ) ;
1453
+ assert_eq ! ( lookup_pk, node_info. pubkey) ;
1454
+ assert_eq ! ( node. list_channels( ) . await . unwrap( ) . len( ) , 3 ) ;
1455
+
1456
+ // Next, we're going to test handling of in-flight payments. To do this, we'll mock out calls to our dispatch
1457
+ // function to send different results depending on the destination.
1458
+ let dest_1 = channels[ 2 ] . node_1 . policy . pubkey ;
1459
+ let dest_2 = channels[ 4 ] . node_1 . policy . pubkey ;
1460
+
1461
+ sim_network
1462
+ . lock ( )
1463
+ . await
1464
+ . expect_dispatch_payment ( )
1465
+ . returning (
1466
+ move |_, route : Route , _, sender : Sender < Result < PaymentResult , LightningError > > | {
1467
+ // If we've reached dispatch, we must have at least one path, grab the last hop to match the
1468
+ // receiver.
1469
+ let receiver = route. paths [ 0 ] . hops . last ( ) . unwrap ( ) . pubkey ;
1470
+ let result = if receiver == dest_1 {
1471
+ PaymentResult {
1472
+ htlc_count : 2 ,
1473
+ payment_outcome : PaymentOutcome :: Success ,
1474
+ }
1475
+ } else if receiver == dest_2 {
1476
+ PaymentResult {
1477
+ htlc_count : 0 ,
1478
+ payment_outcome : PaymentOutcome :: InsufficientBalance ,
1479
+ }
1480
+ } else {
1481
+ panic ! ( "unknown mocked receiver" ) ;
1482
+ } ;
1483
+
1484
+ let _ = sender. send ( Ok ( result) ) . unwrap ( ) ;
1485
+ } ,
1486
+ ) ;
1487
+
1488
+ // Dispatch payments to different destinations and assert that our track payment results are as expected.
1489
+ let hash_1 = node. send_payment ( dest_1, 10_000 ) . await . unwrap ( ) ;
1490
+ let hash_2 = node. send_payment ( dest_2, 15_000 ) . await . unwrap ( ) ;
1491
+
1492
+ let ( _, shutdown_listener) = triggered:: trigger ( ) ;
1493
+
1494
+ let result_1 = node
1495
+ . track_payment ( hash_1, shutdown_listener. clone ( ) )
1496
+ . await
1497
+ . unwrap ( ) ;
1498
+ assert ! ( matches!( result_1. payment_outcome, PaymentOutcome :: Success ) ) ;
1499
+
1500
+ let result_2 = node
1501
+ . track_payment ( hash_2, shutdown_listener. clone ( ) )
1502
+ . await
1503
+ . unwrap ( ) ;
1504
+ assert ! ( matches!(
1505
+ result_2. payment_outcome,
1506
+ PaymentOutcome :: InsufficientBalance
1507
+ ) ) ;
1508
+ }
1357
1509
}
0 commit comments