Skip to content
This repository was archived by the owner on Jan 6, 2025. It is now read-only.

Commit 97f5282

Browse files
committed
Channel state unit test
1 parent bdae99f commit 97f5282

File tree

1 file changed

+335
-0
lines changed

1 file changed

+335
-0
lines changed

src/lsps2/service.rs

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,6 +1142,8 @@ fn calculate_amount_to_forward_per_htlc(
11421142
mod tests {
11431143

11441144
use super::*;
1145+
use chrono::TimeZone;
1146+
use chrono::Utc;
11451147
use proptest::prelude::*;
11461148

11471149
const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
@@ -1230,4 +1232,337 @@ mod tests {
12301232
]
12311233
);
12321234
}
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+
}
12331568
}

0 commit comments

Comments
 (0)