@@ -1142,6 +1142,8 @@ fn calculate_amount_to_forward_per_htlc(
1142
1142
mod tests {
1143
1143
1144
1144
use super :: * ;
1145
+ use chrono:: TimeZone ;
1146
+ use chrono:: Utc ;
1145
1147
use proptest:: prelude:: * ;
1146
1148
1147
1149
const MAX_VALUE_MSAT : u64 = 21_000_000_0000_0000_000 ;
@@ -1230,4 +1232,337 @@ mod tests {
1230
1232
]
1231
1233
) ;
1232
1234
}
1235
+
1236
+ #[ test]
1237
+ fn test_jit_channel_state_mpp ( ) {
1238
+ let payment_size_msat = Some ( 500_000_000 ) ;
1239
+ let opening_fee_params = OpeningFeeParams {
1240
+ min_fee_msat : 10_000_000 ,
1241
+ proportional : 10_000 ,
1242
+ valid_until : Utc . timestamp_opt ( 3000 , 0 ) . unwrap ( ) ,
1243
+ min_lifetime : 4032 ,
1244
+ max_client_to_self_delay : 2016 ,
1245
+ min_payment_size_msat : 10_000_000 ,
1246
+ max_payment_size_msat : 1_000_000_000 ,
1247
+ promise : "ignore" . to_string ( ) ,
1248
+ } ;
1249
+ let mut state = OutboundJITChannelState :: new ( ) ;
1250
+ // Intercepts the first HTLC of a multipart payment A.
1251
+ {
1252
+ let ( new_state, action) = state
1253
+ . htlc_intercepted (
1254
+ & opening_fee_params,
1255
+ & payment_size_msat,
1256
+ InterceptedHTLC {
1257
+ intercept_id : InterceptId ( [ 0 ; 32 ] ) ,
1258
+ expected_outbound_amount_msat : 200_000_000 ,
1259
+ payment_hash : PaymentHash ( [ 100 ; 32 ] ) ,
1260
+ } ,
1261
+ )
1262
+ . unwrap ( ) ;
1263
+ assert ! ( matches!( new_state, OutboundJITChannelState :: PendingInitialPayment { .. } ) ) ;
1264
+ assert ! ( action. is_none( ) ) ;
1265
+ state = new_state;
1266
+ }
1267
+ // Intercepts the first HTLC of a different multipart payment B.
1268
+ {
1269
+ let ( new_state, action) = state
1270
+ . htlc_intercepted (
1271
+ & opening_fee_params,
1272
+ & payment_size_msat,
1273
+ InterceptedHTLC {
1274
+ intercept_id : InterceptId ( [ 1 ; 32 ] ) ,
1275
+ expected_outbound_amount_msat : 1_000_000 ,
1276
+ payment_hash : PaymentHash ( [ 101 ; 32 ] ) ,
1277
+ } ,
1278
+ )
1279
+ . unwrap ( ) ;
1280
+ assert ! ( matches!( new_state, OutboundJITChannelState :: PendingInitialPayment { .. } ) ) ;
1281
+ assert ! ( action. is_none( ) ) ;
1282
+ state = new_state;
1283
+ }
1284
+ // Intercepts the second HTLC of multipart payment A, completing the expected payment and
1285
+ // opening the channel.
1286
+ {
1287
+ let ( new_state, action) = state
1288
+ . htlc_intercepted (
1289
+ & opening_fee_params,
1290
+ & payment_size_msat,
1291
+ InterceptedHTLC {
1292
+ intercept_id : InterceptId ( [ 2 ; 32 ] ) ,
1293
+ expected_outbound_amount_msat : 300_000_000 ,
1294
+ payment_hash : PaymentHash ( [ 100 ; 32 ] ) ,
1295
+ } ,
1296
+ )
1297
+ . unwrap ( ) ;
1298
+ assert ! ( matches!( new_state, OutboundJITChannelState :: PendingChannelOpen { .. } ) ) ;
1299
+ assert ! ( matches!( action, Some ( HTLCInterceptedAction :: OpenChannel ( _) ) ) ) ;
1300
+ state = new_state;
1301
+ }
1302
+ // Channel opens, becomes ready, and multipart payment A gets forwarded.
1303
+ {
1304
+ let ( new_state, ForwardPaymentAction ( channel_id, payment) ) =
1305
+ state. channel_ready ( ChannelId ( [ 200 ; 32 ] ) ) . unwrap ( ) ;
1306
+ assert_eq ! ( channel_id, ChannelId ( [ 200 ; 32 ] ) ) ;
1307
+ assert_eq ! ( payment. opening_fee_msat, 10_000_000 ) ;
1308
+ assert_eq ! (
1309
+ payment. htlcs,
1310
+ vec![
1311
+ InterceptedHTLC {
1312
+ intercept_id: InterceptId ( [ 0 ; 32 ] ) ,
1313
+ expected_outbound_amount_msat: 200_000_000 ,
1314
+ payment_hash: PaymentHash ( [ 100 ; 32 ] ) ,
1315
+ } ,
1316
+ InterceptedHTLC {
1317
+ intercept_id: InterceptId ( [ 2 ; 32 ] ) ,
1318
+ expected_outbound_amount_msat: 300_000_000 ,
1319
+ payment_hash: PaymentHash ( [ 100 ; 32 ] ) ,
1320
+ } ,
1321
+ ]
1322
+ ) ;
1323
+ state = new_state;
1324
+ }
1325
+ // Intercepts the first HTLC of a different payment C.
1326
+ {
1327
+ let ( new_state, action) = state
1328
+ . htlc_intercepted (
1329
+ & opening_fee_params,
1330
+ & payment_size_msat,
1331
+ InterceptedHTLC {
1332
+ intercept_id : InterceptId ( [ 3 ; 32 ] ) ,
1333
+ expected_outbound_amount_msat : 2_000_000 ,
1334
+ payment_hash : PaymentHash ( [ 102 ; 32 ] ) ,
1335
+ } ,
1336
+ )
1337
+ . unwrap ( ) ;
1338
+ assert ! ( matches!( new_state, OutboundJITChannelState :: PendingPaymentForward { .. } ) ) ;
1339
+ assert ! ( action. is_none( ) ) ;
1340
+ state = new_state;
1341
+ }
1342
+ // Payment A fails.
1343
+ {
1344
+ let ( new_state, action) = state. htlc_handling_failed ( ) . unwrap ( ) ;
1345
+ assert ! ( matches!( new_state, OutboundJITChannelState :: PendingPayment { .. } ) ) ;
1346
+ // No payments have received sufficient HTLCs yet.
1347
+ assert ! ( action. is_none( ) ) ;
1348
+ state = new_state;
1349
+ }
1350
+ // Additional HTLC of payment B arrives, completing the expectd payment.
1351
+ {
1352
+ let ( new_state, action) = state
1353
+ . htlc_intercepted (
1354
+ & opening_fee_params,
1355
+ & payment_size_msat,
1356
+ InterceptedHTLC {
1357
+ intercept_id : InterceptId ( [ 4 ; 32 ] ) ,
1358
+ expected_outbound_amount_msat : 500_000_000 ,
1359
+ payment_hash : PaymentHash ( [ 101 ; 32 ] ) ,
1360
+ } ,
1361
+ )
1362
+ . unwrap ( ) ;
1363
+ assert ! ( matches!( new_state, OutboundJITChannelState :: PendingPaymentForward { .. } ) ) ;
1364
+ match action {
1365
+ Some ( HTLCInterceptedAction :: ForwardPayment ( channel_id, payment) ) => {
1366
+ assert_eq ! ( channel_id, ChannelId ( [ 200 ; 32 ] ) ) ;
1367
+ assert_eq ! ( payment. opening_fee_msat, 10_000_000 ) ;
1368
+ assert_eq ! (
1369
+ payment. htlcs,
1370
+ vec![
1371
+ InterceptedHTLC {
1372
+ intercept_id: InterceptId ( [ 1 ; 32 ] ) ,
1373
+ expected_outbound_amount_msat: 1_000_000 ,
1374
+ payment_hash: PaymentHash ( [ 101 ; 32 ] ) ,
1375
+ } ,
1376
+ InterceptedHTLC {
1377
+ intercept_id: InterceptId ( [ 4 ; 32 ] ) ,
1378
+ expected_outbound_amount_msat: 500_000_000 ,
1379
+ payment_hash: PaymentHash ( [ 101 ; 32 ] ) ,
1380
+ } ,
1381
+ ]
1382
+ ) ;
1383
+ } ,
1384
+ _ => panic ! ( "Unexpected action when intercepted HTLC." ) ,
1385
+ }
1386
+ state = new_state;
1387
+ }
1388
+ // Payment completes, queued payments get forwarded.
1389
+ {
1390
+ let ( new_state, action) = state. payment_forwarded ( ) . unwrap ( ) ;
1391
+ assert ! ( matches!( new_state, OutboundJITChannelState :: PaymentForwarded { .. } ) ) ;
1392
+ match action {
1393
+ Some ( ForwardHTLCsAction ( channel_id, htlcs) ) => {
1394
+ assert_eq ! ( channel_id, ChannelId ( [ 200 ; 32 ] ) ) ;
1395
+ assert_eq ! (
1396
+ htlcs,
1397
+ vec![ InterceptedHTLC {
1398
+ intercept_id: InterceptId ( [ 3 ; 32 ] ) ,
1399
+ expected_outbound_amount_msat: 2_000_000 ,
1400
+ payment_hash: PaymentHash ( [ 102 ; 32 ] ) ,
1401
+ } ]
1402
+ ) ;
1403
+ } ,
1404
+ _ => panic ! ( "Unexpected action when forwarded payment." ) ,
1405
+ }
1406
+ state = new_state;
1407
+ }
1408
+ // Any new HTLC gets automatically forwarded.
1409
+ {
1410
+ let ( new_state, action) = state
1411
+ . htlc_intercepted (
1412
+ & opening_fee_params,
1413
+ & payment_size_msat,
1414
+ InterceptedHTLC {
1415
+ intercept_id : InterceptId ( [ 5 ; 32 ] ) ,
1416
+ expected_outbound_amount_msat : 200_000_000 ,
1417
+ payment_hash : PaymentHash ( [ 103 ; 32 ] ) ,
1418
+ } ,
1419
+ )
1420
+ . unwrap ( ) ;
1421
+ assert ! ( matches!( new_state, OutboundJITChannelState :: PaymentForwarded { .. } ) ) ;
1422
+ assert ! (
1423
+ matches!( action, Some ( HTLCInterceptedAction :: ForwardHTLC ( channel_id) ) if channel_id == ChannelId ( [ 200 ; 32 ] ) )
1424
+ ) ;
1425
+ }
1426
+ }
1427
+
1428
+ #[ test]
1429
+ fn test_jit_channel_state_no_mpp ( ) {
1430
+ let payment_size_msat = None ;
1431
+ let opening_fee_params = OpeningFeeParams {
1432
+ min_fee_msat : 10_000_000 ,
1433
+ proportional : 10_000 ,
1434
+ valid_until : Utc . timestamp_opt ( 3000 , 0 ) . unwrap ( ) ,
1435
+ min_lifetime : 4032 ,
1436
+ max_client_to_self_delay : 2016 ,
1437
+ min_payment_size_msat : 10_000_000 ,
1438
+ max_payment_size_msat : 1_000_000_000 ,
1439
+ promise : "ignore" . to_string ( ) ,
1440
+ } ;
1441
+ let mut state = OutboundJITChannelState :: new ( ) ;
1442
+ // Intercepts payment A, opening the channel.
1443
+ {
1444
+ let ( new_state, action) = state
1445
+ . htlc_intercepted (
1446
+ & opening_fee_params,
1447
+ & payment_size_msat,
1448
+ InterceptedHTLC {
1449
+ intercept_id : InterceptId ( [ 0 ; 32 ] ) ,
1450
+ expected_outbound_amount_msat : 500_000_000 ,
1451
+ payment_hash : PaymentHash ( [ 100 ; 32 ] ) ,
1452
+ } ,
1453
+ )
1454
+ . unwrap ( ) ;
1455
+ assert ! ( matches!( new_state, OutboundJITChannelState :: PendingChannelOpen { .. } ) ) ;
1456
+ assert ! ( matches!( action, Some ( HTLCInterceptedAction :: OpenChannel ( _) ) ) ) ;
1457
+ state = new_state;
1458
+ }
1459
+ // Intercepts payment B.
1460
+ {
1461
+ let ( new_state, action) = state
1462
+ . htlc_intercepted (
1463
+ & opening_fee_params,
1464
+ & payment_size_msat,
1465
+ InterceptedHTLC {
1466
+ intercept_id : InterceptId ( [ 1 ; 32 ] ) ,
1467
+ expected_outbound_amount_msat : 600_000_000 ,
1468
+ payment_hash : PaymentHash ( [ 101 ; 32 ] ) ,
1469
+ } ,
1470
+ )
1471
+ . unwrap ( ) ;
1472
+ assert ! ( matches!( new_state, OutboundJITChannelState :: PendingChannelOpen { .. } ) ) ;
1473
+ assert ! ( action. is_none( ) ) ;
1474
+ state = new_state;
1475
+ }
1476
+ // Channel opens, becomes ready, and payment A gets forwarded.
1477
+ {
1478
+ let ( new_state, ForwardPaymentAction ( channel_id, payment) ) =
1479
+ state. channel_ready ( ChannelId ( [ 200 ; 32 ] ) ) . unwrap ( ) ;
1480
+ assert_eq ! ( channel_id, ChannelId ( [ 200 ; 32 ] ) ) ;
1481
+ assert_eq ! ( payment. opening_fee_msat, 10_000_000 ) ;
1482
+ assert_eq ! (
1483
+ payment. htlcs,
1484
+ vec![ InterceptedHTLC {
1485
+ intercept_id: InterceptId ( [ 0 ; 32 ] ) ,
1486
+ expected_outbound_amount_msat: 500_000_000 ,
1487
+ payment_hash: PaymentHash ( [ 100 ; 32 ] ) ,
1488
+ } , ]
1489
+ ) ;
1490
+ state = new_state;
1491
+ }
1492
+ // Intercepts payment C.
1493
+ {
1494
+ let ( new_state, action) = state
1495
+ . htlc_intercepted (
1496
+ & opening_fee_params,
1497
+ & payment_size_msat,
1498
+ InterceptedHTLC {
1499
+ intercept_id : InterceptId ( [ 2 ; 32 ] ) ,
1500
+ expected_outbound_amount_msat : 500_000_000 ,
1501
+ payment_hash : PaymentHash ( [ 102 ; 32 ] ) ,
1502
+ } ,
1503
+ )
1504
+ . unwrap ( ) ;
1505
+ assert ! ( matches!( new_state, OutboundJITChannelState :: PendingPaymentForward { .. } ) ) ;
1506
+ assert ! ( action. is_none( ) ) ;
1507
+ state = new_state;
1508
+ }
1509
+ // Payment A fails, and payment B is forwarded.
1510
+ {
1511
+ let ( new_state, action) = state. htlc_handling_failed ( ) . unwrap ( ) ;
1512
+ assert ! ( matches!( new_state, OutboundJITChannelState :: PendingPaymentForward { .. } ) ) ;
1513
+ match action {
1514
+ Some ( ForwardPaymentAction ( channel_id, payment) ) => {
1515
+ assert_eq ! ( channel_id, ChannelId ( [ 200 ; 32 ] ) ) ;
1516
+ assert_eq ! (
1517
+ payment. htlcs,
1518
+ vec![ InterceptedHTLC {
1519
+ intercept_id: InterceptId ( [ 1 ; 32 ] ) ,
1520
+ expected_outbound_amount_msat: 600_000_000 ,
1521
+ payment_hash: PaymentHash ( [ 101 ; 32 ] ) ,
1522
+ } , ]
1523
+ ) ;
1524
+ } ,
1525
+ _ => panic ! ( "Unexpected action when HTLC handling failed." ) ,
1526
+ }
1527
+ state = new_state;
1528
+ }
1529
+ // Payment completes, queued payments get forwarded.
1530
+ {
1531
+ let ( new_state, action) = state. payment_forwarded ( ) . unwrap ( ) ;
1532
+ assert ! ( matches!( new_state, OutboundJITChannelState :: PaymentForwarded { .. } ) ) ;
1533
+ match action {
1534
+ Some ( ForwardHTLCsAction ( channel_id, htlcs) ) => {
1535
+ assert_eq ! ( channel_id, ChannelId ( [ 200 ; 32 ] ) ) ;
1536
+ assert_eq ! (
1537
+ htlcs,
1538
+ vec![ InterceptedHTLC {
1539
+ intercept_id: InterceptId ( [ 2 ; 32 ] ) ,
1540
+ expected_outbound_amount_msat: 500_000_000 ,
1541
+ payment_hash: PaymentHash ( [ 102 ; 32 ] ) ,
1542
+ } ]
1543
+ ) ;
1544
+ } ,
1545
+ _ => panic ! ( "Unexpected action when forwarded payment." ) ,
1546
+ }
1547
+ state = new_state;
1548
+ }
1549
+ // Any new HTLC gets automatically forwarded.
1550
+ {
1551
+ let ( new_state, action) = state
1552
+ . htlc_intercepted (
1553
+ & opening_fee_params,
1554
+ & payment_size_msat,
1555
+ InterceptedHTLC {
1556
+ intercept_id : InterceptId ( [ 3 ; 32 ] ) ,
1557
+ expected_outbound_amount_msat : 200_000_000 ,
1558
+ payment_hash : PaymentHash ( [ 103 ; 32 ] ) ,
1559
+ } ,
1560
+ )
1561
+ . unwrap ( ) ;
1562
+ assert ! ( matches!( new_state, OutboundJITChannelState :: PaymentForwarded { .. } ) ) ;
1563
+ assert ! (
1564
+ matches!( action, Some ( HTLCInterceptedAction :: ForwardHTLC ( channel_id) ) if channel_id == ChannelId ( [ 200 ; 32 ] ) )
1565
+ ) ;
1566
+ }
1567
+ }
1233
1568
}
0 commit comments