@@ -824,7 +824,7 @@ async fn handle_intercepted_htlc(
824
824
shutdown_listener : Listener ,
825
825
) -> Result < Result < CustomRecords , ForwardingError > , CriticalError > {
826
826
if interceptors. is_empty ( ) {
827
- return Ok ( Ok ( HashMap :: new ( ) ) ) ;
827
+ return Ok ( Ok ( request . incoming_custom_records ) ) ;
828
828
}
829
829
830
830
let mut attached_custom_records: CustomRecords = HashMap :: new ( ) ;
@@ -925,6 +925,9 @@ pub struct SimGraph {
925
925
/// trigger a shutdown signal to other interceptors.
926
926
interceptors : Vec < Arc < dyn Interceptor > > ,
927
927
928
+ /// Custom records that will be added to the first outgoing HTLC in a payment.
929
+ default_custom_records : CustomRecords ,
930
+
928
931
/// Shutdown signal that can be used to trigger a shutdown if a critical error occurs. Listener
929
932
/// can be used to listen for shutdown signals coming from upstream.
930
933
shutdown_signal : ( Trigger , Listener ) ,
@@ -936,6 +939,7 @@ impl SimGraph {
936
939
graph_channels : Vec < SimulatedChannel > ,
937
940
tasks : TaskTracker ,
938
941
interceptors : Vec < Arc < dyn Interceptor > > ,
942
+ default_custom_records : CustomRecords ,
939
943
shutdown_signal : ( Trigger , Listener ) ,
940
944
) -> Result < Self , SimulationError > {
941
945
let mut nodes: HashMap < PublicKey , Vec < u64 > > = HashMap :: new ( ) ;
@@ -971,6 +975,7 @@ impl SimGraph {
971
975
channels : Arc :: new ( Mutex :: new ( channels) ) ,
972
976
tasks,
973
977
interceptors,
978
+ default_custom_records,
974
979
shutdown_signal,
975
980
} )
976
981
}
@@ -1091,15 +1096,16 @@ impl SimNetwork for SimGraph {
1091
1096
} ,
1092
1097
} ;
1093
1098
1094
- self . tasks . spawn ( propagate_payment (
1095
- self . channels . clone ( ) ,
1099
+ self . tasks . spawn ( propagate_payment ( PropagatePaymentRequest {
1100
+ nodes : Arc :: clone ( & self . channels ) ,
1096
1101
source,
1097
- path. clone ( ) ,
1102
+ route : path. clone ( ) ,
1098
1103
payment_hash,
1099
1104
sender,
1100
- self . interceptors . clone ( ) ,
1101
- self . shutdown_signal . clone ( ) ,
1102
- ) ) ;
1105
+ interceptors : self . interceptors . clone ( ) ,
1106
+ custom_records : self . default_custom_records . clone ( ) ,
1107
+ shutdown_signal : self . shutdown_signal . clone ( ) ,
1108
+ } ) ) ;
1103
1109
}
1104
1110
1105
1111
/// lookup_node fetches a node's information and channel capacities.
@@ -1149,13 +1155,14 @@ async fn add_htlcs(
1149
1155
route : Path ,
1150
1156
payment_hash : PaymentHash ,
1151
1157
interceptors : Vec < Arc < dyn Interceptor > > ,
1158
+ custom_records : CustomRecords ,
1152
1159
shutdown_listener : Listener ,
1153
1160
) -> Result < Result < ( ) , ( Option < usize > , ForwardingError ) > , CriticalError > {
1154
1161
let mut outgoing_node = source;
1155
1162
let mut outgoing_amount = route. fee_msat ( ) + route. final_value_msat ( ) ;
1156
1163
let mut outgoing_cltv = route. hops . iter ( ) . map ( |hop| hop. cltv_expiry_delta ) . sum ( ) ;
1157
1164
1158
- let mut incoming_custom_records = HashMap :: new ( ) ;
1165
+ let mut incoming_custom_records = custom_records ;
1159
1166
1160
1167
// Tracks the hop index that we need to remove htlcs from on payment completion (both success and failure).
1161
1168
// Given a payment from A to C, over the route A -- B -- C, this index has the following meanings:
@@ -1237,7 +1244,7 @@ async fn add_htlcs(
1237
1244
forwarding_node : hop. pubkey ,
1238
1245
payment_hash,
1239
1246
incoming_htlc : incoming_htlc. clone ( ) ,
1240
- incoming_custom_records : incoming_custom_records . clone ( ) ,
1247
+ incoming_custom_records,
1241
1248
outgoing_channel_id : next_scid,
1242
1249
incoming_amount_msat : outgoing_amount,
1243
1250
outgoing_amount_msat : outgoing_amount - hop. fee_msat ,
@@ -1338,43 +1345,47 @@ async fn remove_htlcs(
1338
1345
Ok ( ( ) )
1339
1346
}
1340
1347
1341
- /// Finds a payment path from the source to destination nodes provided, and propagates the appropriate htlcs through
1342
- /// the simulated network, notifying the sender channel provided of the payment outcome. If a critical error occurs,
1343
- /// ie a breakdown of our state machine, it will still notify the payment outcome and will use the shutdown trigger
1344
- /// to signal that we should exit.
1345
- async fn propagate_payment (
1348
+ struct PropagatePaymentRequest {
1346
1349
nodes : Arc < Mutex < HashMap < ShortChannelID , SimulatedChannel > > > ,
1347
1350
source : PublicKey ,
1348
1351
route : Path ,
1349
1352
payment_hash : PaymentHash ,
1350
1353
sender : Sender < Result < PaymentResult , LightningError > > ,
1351
1354
interceptors : Vec < Arc < dyn Interceptor > > ,
1355
+ custom_records : CustomRecords ,
1352
1356
shutdown_signal : ( Trigger , Listener ) ,
1353
- ) {
1357
+ }
1358
+
1359
+ /// Finds a payment path from the source to destination nodes provided, and propagates the appropriate htlcs through
1360
+ /// the simulated network, notifying the sender channel provided of the payment outcome. If a critical error occurs,
1361
+ /// ie a breakdown of our state machine, it will still notify the payment outcome and will use the shutdown trigger
1362
+ /// to signal that we should exit.
1363
+ async fn propagate_payment ( request : PropagatePaymentRequest ) {
1354
1364
let notify_result = match add_htlcs (
1355
- nodes. clone ( ) ,
1356
- source,
1357
- route. clone ( ) ,
1358
- payment_hash,
1359
- interceptors. clone ( ) ,
1360
- shutdown_signal. 1 ,
1365
+ request. nodes . clone ( ) ,
1366
+ request. source ,
1367
+ request. route . clone ( ) ,
1368
+ request. payment_hash ,
1369
+ request. interceptors . clone ( ) ,
1370
+ request. custom_records ,
1371
+ request. shutdown_signal . 1 ,
1361
1372
)
1362
1373
. await
1363
1374
{
1364
1375
Ok ( Ok ( _) ) => {
1365
1376
// If we successfully added the htlc, go ahead and remove all the htlcs in the route with successful resolution.
1366
1377
if let Err ( e) = remove_htlcs (
1367
- nodes,
1368
- route. hops . len ( ) - 1 ,
1369
- source,
1370
- route,
1371
- payment_hash,
1378
+ request . nodes ,
1379
+ request . route . hops . len ( ) - 1 ,
1380
+ request . source ,
1381
+ request . route ,
1382
+ request . payment_hash ,
1372
1383
true ,
1373
- interceptors,
1384
+ request . interceptors ,
1374
1385
)
1375
1386
. await
1376
1387
{
1377
- shutdown_signal. 0 . trigger ( ) ;
1388
+ request . shutdown_signal . 0 . trigger ( ) ;
1378
1389
log:: error!( "Could not remove htlcs from channel: {e}." ) ;
1379
1390
}
1380
1391
PaymentResult {
@@ -1387,35 +1398,35 @@ async fn propagate_payment(
1387
1398
// state. It's possible that we failed with the very first add, and then we don't need to clean anything up.
1388
1399
if let Some ( resolution_idx) = fail_idx {
1389
1400
if remove_htlcs (
1390
- nodes,
1401
+ request . nodes ,
1391
1402
resolution_idx,
1392
- source,
1393
- route,
1394
- payment_hash,
1403
+ request . source ,
1404
+ request . route ,
1405
+ request . payment_hash ,
1395
1406
false ,
1396
- interceptors,
1407
+ request . interceptors ,
1397
1408
)
1398
1409
. await
1399
1410
. is_err ( )
1400
1411
{
1401
- shutdown_signal. 0 . trigger ( ) ;
1412
+ request . shutdown_signal . 0 . trigger ( ) ;
1402
1413
}
1403
1414
}
1404
1415
1405
1416
log:: debug!(
1406
1417
"Forwarding failure for simulated payment {}: {fwd_err}" ,
1407
- hex:: encode( payment_hash. 0 )
1418
+ hex:: encode( request . payment_hash. 0 )
1408
1419
) ;
1409
1420
PaymentResult {
1410
1421
htlc_count : 0 ,
1411
1422
payment_outcome : PaymentOutcome :: Unknown ,
1412
1423
}
1413
1424
} ,
1414
1425
Err ( critical_err) => {
1415
- shutdown_signal. 0 . trigger ( ) ;
1426
+ request . shutdown_signal . 0 . trigger ( ) ;
1416
1427
log:: debug!(
1417
1428
"Critical error in simulated payment {}: {critical_err}" ,
1418
- hex:: encode( payment_hash. 0 )
1429
+ hex:: encode( request . payment_hash. 0 )
1419
1430
) ;
1420
1431
PaymentResult {
1421
1432
htlc_count : 0 ,
@@ -1424,7 +1435,7 @@ async fn propagate_payment(
1424
1435
} ,
1425
1436
} ;
1426
1437
1427
- if let Err ( e) = sender. send ( Ok ( notify_result) ) {
1438
+ if let Err ( e) = request . sender . send ( Ok ( notify_result) ) {
1428
1439
log:: error!( "Could not notify payment result: {:?}." , e) ;
1429
1440
}
1430
1441
}
@@ -1962,7 +1973,11 @@ mod tests {
1962
1973
/// Alice (100) --- (0) Bob (100) --- (0) Carol (100) --- (0) Dave
1963
1974
///
1964
1975
/// The nodes pubkeys in this chain of channels are provided in-order for easy access.
1965
- async fn new ( capacity : u64 , interceptors : Vec < Arc < dyn Interceptor > > ) -> Self {
1976
+ async fn new (
1977
+ capacity : u64 ,
1978
+ interceptors : Vec < Arc < dyn Interceptor > > ,
1979
+ custom_records : CustomRecords ,
1980
+ ) -> Self {
1966
1981
let shutdown_signal = triggered:: trigger ( ) ;
1967
1982
let channels = create_simulated_channels ( 3 , capacity) ;
1968
1983
let routing_graph = Arc :: new (
@@ -1989,6 +2004,7 @@ mod tests {
1989
2004
channels. clone ( ) ,
1990
2005
TaskTracker :: new ( ) ,
1991
2006
interceptors,
2007
+ custom_records,
1992
2008
shutdown_signal,
1993
2009
)
1994
2010
. expect ( "could not create test graph" ) ,
@@ -2069,7 +2085,8 @@ mod tests {
2069
2085
#[ tokio:: test]
2070
2086
async fn test_successful_dispatch ( ) {
2071
2087
let chan_capacity = 500_000_000 ;
2072
- let mut test_kit = DispatchPaymentTestKit :: new ( chan_capacity, vec ! [ ] ) . await ;
2088
+ let mut test_kit =
2089
+ DispatchPaymentTestKit :: new ( chan_capacity, vec ! [ ] , CustomRecords :: default ( ) ) . await ;
2073
2090
2074
2091
// Send a payment that should succeed from Alice -> Dave.
2075
2092
let mut amt = 20_000 ;
@@ -2134,7 +2151,8 @@ mod tests {
2134
2151
#[ tokio:: test]
2135
2152
async fn test_successful_multi_hop ( ) {
2136
2153
let chan_capacity = 500_000_000 ;
2137
- let mut test_kit = DispatchPaymentTestKit :: new ( chan_capacity, vec ! [ ] ) . await ;
2154
+ let mut test_kit =
2155
+ DispatchPaymentTestKit :: new ( chan_capacity, vec ! [ ] , CustomRecords :: default ( ) ) . await ;
2138
2156
2139
2157
// Send a payment that should succeed from Alice -> Dave.
2140
2158
let amt = 20_000 ;
@@ -2164,7 +2182,8 @@ mod tests {
2164
2182
#[ tokio:: test]
2165
2183
async fn test_single_hop_payments ( ) {
2166
2184
let chan_capacity = 500_000_000 ;
2167
- let mut test_kit = DispatchPaymentTestKit :: new ( chan_capacity, vec ! [ ] ) . await ;
2185
+ let mut test_kit =
2186
+ DispatchPaymentTestKit :: new ( chan_capacity, vec ! [ ] , CustomRecords :: default ( ) ) . await ;
2168
2187
2169
2188
// Send a single hop payment from Alice -> Bob, it will succeed because Alice has all the liquidity.
2170
2189
let amt = 150_000 ;
@@ -2196,7 +2215,8 @@ mod tests {
2196
2215
#[ tokio:: test]
2197
2216
async fn test_multi_hop_faiulre ( ) {
2198
2217
let chan_capacity = 500_000_000 ;
2199
- let mut test_kit = DispatchPaymentTestKit :: new ( chan_capacity, vec ! [ ] ) . await ;
2218
+ let mut test_kit =
2219
+ DispatchPaymentTestKit :: new ( chan_capacity, vec ! [ ] , CustomRecords :: default ( ) ) . await ;
2200
2220
2201
2221
// Drain liquidity between Bob and Carol to force failures on Bob's outgoing linke.
2202
2222
test_kit
@@ -2242,7 +2262,7 @@ mod tests {
2242
2262
channel_id : ShortChannelID ( 0 ) ,
2243
2263
index : 0 ,
2244
2264
} ,
2245
- incoming_custom_records : CustomRecords :: new ( ) ,
2265
+ incoming_custom_records : CustomRecords :: default ( ) ,
2246
2266
outgoing_channel_id : None ,
2247
2267
incoming_amount_msat : 0 ,
2248
2268
outgoing_amount_msat : 0 ,
@@ -2404,7 +2424,8 @@ mod tests {
2404
2424
. returning ( |_| Ok ( ( ) ) ) ;
2405
2425
2406
2426
let mock_1 = Arc :: new ( mock_interceptor_1) ;
2407
- let mut test_kit = DispatchPaymentTestKit :: new ( 500_000_000 , vec ! [ mock_1] ) . await ;
2427
+ let mut test_kit =
2428
+ DispatchPaymentTestKit :: new ( 500_000_000 , vec ! [ mock_1] , CustomRecords :: default ( ) ) . await ;
2408
2429
let ( _, result) = test_kit
2409
2430
. send_test_payment ( test_kit. nodes [ 0 ] , test_kit. nodes [ 3 ] , 150_000_000 )
2410
2431
. await ;
@@ -2425,4 +2446,40 @@ mod tests {
2425
2446
test_kit. graph . tasks . close ( ) ;
2426
2447
test_kit. graph . tasks . wait ( ) . await ;
2427
2448
}
2449
+
2450
+ /// Tests custom records set for interceptors in multi-hop payment.
2451
+ #[ tokio:: test]
2452
+ async fn test_custom_records ( ) {
2453
+ let custom_records = HashMap :: from ( [ ( 1000 , vec ! [ 1 ] ) ] ) ;
2454
+
2455
+ let mut mock_interceptor_1 = MockTestInterceptor :: new ( ) ;
2456
+ let custom_records_clone = custom_records. clone ( ) ;
2457
+ mock_interceptor_1
2458
+ . expect_intercept_htlc ( )
2459
+ . withf ( move |req : & InterceptRequest | {
2460
+ // Check custom records passed to interceptor are the default ones set.
2461
+ req. incoming_custom_records == custom_records_clone
2462
+ } )
2463
+ . returning ( |_| Ok ( Ok ( CustomRecords :: default ( ) ) ) ) ; // Set empty records for 2nd hop.
2464
+ mock_interceptor_1
2465
+ . expect_notify_resolution ( )
2466
+ . returning ( |_| Ok ( ( ) ) )
2467
+ . times ( 2 ) ;
2468
+
2469
+ mock_interceptor_1
2470
+ . expect_intercept_htlc ( )
2471
+ . withf ( move |req : & InterceptRequest | {
2472
+ // On this 2nd hop, the custom records should be empty.
2473
+ req. incoming_custom_records == CustomRecords :: default ( )
2474
+ } )
2475
+ . returning ( |_| Ok ( Ok ( CustomRecords :: default ( ) ) ) ) ;
2476
+
2477
+ let chan_capacity = 500_000_000 ;
2478
+ let mock_1: Arc < dyn Interceptor > = Arc :: new ( mock_interceptor_1) ;
2479
+ let mut test_kit =
2480
+ DispatchPaymentTestKit :: new ( chan_capacity, vec ! [ mock_1] , custom_records) . await ;
2481
+ let _ = test_kit
2482
+ . send_test_payment ( test_kit. nodes [ 0 ] , test_kit. nodes [ 2 ] , 150_000 )
2483
+ . await ;
2484
+ }
2428
2485
}
0 commit comments