@@ -1178,6 +1178,265 @@ func makeAttemptInfo(total, amtForwarded int) HTLCAttemptInfo {
1178
1178
}
1179
1179
}
1180
1180
1181
+ // lastHopArgs is a helper struct that holds the arguments for the last hop
1182
+ // when creating an attempt with a route with a single hop (last hop).
1183
+ type lastHopArgs struct {
1184
+ amt lnwire.MilliSatoshi
1185
+ total lnwire.MilliSatoshi
1186
+ mpp * record.MPP
1187
+ encrypted []byte
1188
+ }
1189
+
1190
+ // makeLastHopAttemptInfo creates an HTLCAttemptInfo with a route with a single
1191
+ // hop (last hop).
1192
+ func makeLastHopAttemptInfo (id uint64 , args lastHopArgs ) HTLCAttemptInfo {
1193
+ lastHop := & route.Hop {
1194
+ PubKeyBytes : vertex ,
1195
+ ChannelID : 1 ,
1196
+ AmtToForward : args .amt ,
1197
+ MPP : args .mpp ,
1198
+ EncryptedData : args .encrypted ,
1199
+ TotalAmtMsat : args .total ,
1200
+ }
1201
+
1202
+ return HTLCAttemptInfo {
1203
+ AttemptID : id ,
1204
+ Route : route.Route {
1205
+ SourcePubKey : vertex ,
1206
+ TotalAmount : args .amt ,
1207
+ Hops : []* route.Hop {lastHop },
1208
+ },
1209
+ }
1210
+ }
1211
+
1212
+ // makePayment creates an MPPayment with set of attempts.
1213
+ func makePayment (total lnwire.MilliSatoshi ,
1214
+ attempts ... HTLCAttempt ) * MPPayment {
1215
+
1216
+ return & MPPayment {
1217
+ Info : & PaymentCreationInfo {
1218
+ Value : total ,
1219
+ },
1220
+ HTLCs : attempts ,
1221
+ }
1222
+ }
1223
+
1224
+ // TestVerifyAttemptNonMPPAmountMismatch tests that we return an error if the
1225
+ // attempted amount doesn't match the payment amount.
1226
+ func TestVerifyAttemptNonMPPAmountMismatch (t * testing.T ) {
1227
+ t .Parallel ()
1228
+
1229
+ payment := makePayment (1000 )
1230
+ attempt := makeLastHopAttemptInfo (1 , lastHopArgs {amt : 900 })
1231
+
1232
+ require .ErrorIs (t , verifyAttempt (payment , & attempt ), ErrValueMismatch )
1233
+ }
1234
+
1235
+ // TestVerifyAttemptNonMPPSuccess tests that we don't return an error if the
1236
+ // attempted amount matches the payment amount.
1237
+ func TestVerifyAttemptNonMPPSuccess (t * testing.T ) {
1238
+ t .Parallel ()
1239
+
1240
+ payment := makePayment (1200 )
1241
+ attempt := makeLastHopAttemptInfo (1 , lastHopArgs {amt : 1200 })
1242
+
1243
+ require .NoError (t , verifyAttempt (payment , & attempt ))
1244
+ }
1245
+
1246
+ // TestVerifyAttemptMPPTransitionErrors tests cases where we cannot transition
1247
+ // from a non-MPP payment to an MPP payment or vice versa.
1248
+ func TestVerifyAttemptMPPTransitionErrors (t * testing.T ) {
1249
+ t .Parallel ()
1250
+
1251
+ total := lnwire .MilliSatoshi (2000 )
1252
+ mpp := record .NewMPP (total , testHash )
1253
+
1254
+ paymentWithMPP := makePayment (
1255
+ total ,
1256
+ HTLCAttempt {
1257
+ HTLCAttemptInfo : makeLastHopAttemptInfo (
1258
+ 1 ,
1259
+ lastHopArgs {amt : 1000 , mpp : mpp },
1260
+ ),
1261
+ },
1262
+ )
1263
+ nonMPP := makeLastHopAttemptInfo (2 , lastHopArgs {amt : 1000 })
1264
+ require .ErrorIs (t , verifyAttempt (paymentWithMPP , & nonMPP ), ErrMPPayment )
1265
+
1266
+ paymentWithNonMPP := makePayment (
1267
+ total ,
1268
+ HTLCAttempt {
1269
+ HTLCAttemptInfo : makeLastHopAttemptInfo (
1270
+ 1 ,
1271
+ lastHopArgs {amt : total },
1272
+ ),
1273
+ },
1274
+ )
1275
+ mppAttempt := makeLastHopAttemptInfo (
1276
+ 2 , lastHopArgs {amt : 1000 , mpp : mpp },
1277
+ )
1278
+ require .ErrorIs (
1279
+ t ,
1280
+ verifyAttempt (paymentWithNonMPP , & mppAttempt ),
1281
+ ErrNonMPPayment ,
1282
+ )
1283
+ }
1284
+
1285
+ // TestVerifyAttemptMPPOptionMismatch tests that we return an error if the
1286
+ // MPP options don't match the payment options.
1287
+ func TestVerifyAttemptMPPOptionMismatch (t * testing.T ) {
1288
+ t .Parallel ()
1289
+
1290
+ total := lnwire .MilliSatoshi (3000 )
1291
+ goodMPP := record .NewMPP (total , testHash )
1292
+ payment := makePayment (
1293
+ total ,
1294
+ HTLCAttempt {
1295
+ HTLCAttemptInfo : makeLastHopAttemptInfo (
1296
+ 1 ,
1297
+ lastHopArgs {amt : 1500 , mpp : goodMPP },
1298
+ ),
1299
+ },
1300
+ )
1301
+
1302
+ badAddr := record .NewMPP (total , rev )
1303
+ attemptBadAddr := makeLastHopAttemptInfo (
1304
+ 2 ,
1305
+ lastHopArgs {amt : 1500 , mpp : badAddr },
1306
+ )
1307
+ require .ErrorIs (
1308
+ t ,
1309
+ verifyAttempt (payment , & attemptBadAddr ),
1310
+ ErrMPPPaymentAddrMismatch ,
1311
+ )
1312
+
1313
+ badTotal := record .NewMPP (total - 1 , testHash )
1314
+ attemptBadTotal := makeLastHopAttemptInfo (
1315
+ 3 ,
1316
+ lastHopArgs {amt : 1500 , mpp : badTotal },
1317
+ )
1318
+ require .ErrorIs (
1319
+ t ,
1320
+ verifyAttempt (payment , & attemptBadTotal ),
1321
+ ErrMPPTotalAmountMismatch ,
1322
+ )
1323
+
1324
+ matching := makeLastHopAttemptInfo (
1325
+ 4 ,
1326
+ lastHopArgs {amt : 1500 , mpp : record .NewMPP (total , testHash )},
1327
+ )
1328
+ require .NoError (t , verifyAttempt (payment , & matching ))
1329
+ }
1330
+
1331
+ // TestVerifyAttemptBlindedValidation tests that we return an error if we try
1332
+ // to register an MPP attempt for a blinded payment.
1333
+ func TestVerifyAttemptBlindedValidation (t * testing.T ) {
1334
+ t .Parallel ()
1335
+
1336
+ total := lnwire .MilliSatoshi (5000 )
1337
+
1338
+ // Payment with a blinded attempt.
1339
+ existing := makeLastHopAttemptInfo (
1340
+ 1 ,
1341
+ lastHopArgs {amt : 2500 , total : total , encrypted : []byte {1 }},
1342
+ )
1343
+ payment := makePayment (
1344
+ total ,
1345
+ HTLCAttempt {HTLCAttemptInfo : existing },
1346
+ )
1347
+
1348
+ // Attempt with a normal MPP record should fail because a payment
1349
+ // cannot have a mix of blinded and non-blinded attempts.
1350
+ goodMPP := makeLastHopAttemptInfo (
1351
+ 2 ,
1352
+ lastHopArgs {amt : 2500 , mpp : record .NewMPP (total , testHash )},
1353
+ )
1354
+ require .ErrorIs (
1355
+ t , verifyAttempt (payment , & goodMPP ),
1356
+ ErrMixedBlindedAndNonBlindedPayments ,
1357
+ )
1358
+
1359
+ blindedMPP := makeLastHopAttemptInfo (
1360
+ 2 ,
1361
+ lastHopArgs {
1362
+ amt : 2500 ,
1363
+ total : total ,
1364
+ mpp : record .NewMPP (total , testHash ),
1365
+ encrypted : []byte {2 },
1366
+ },
1367
+ )
1368
+ require .ErrorIs (
1369
+ t ,
1370
+ verifyAttempt (payment , & blindedMPP ),
1371
+ ErrMPPRecordInBlindedPayment ,
1372
+ )
1373
+
1374
+ mismatchedTotal := makeLastHopAttemptInfo (
1375
+ 3 ,
1376
+ lastHopArgs {amt : 2500 , total : total + 1 , encrypted : []byte {3 }},
1377
+ )
1378
+ require .ErrorIs (
1379
+ t ,
1380
+ verifyAttempt (payment , & mismatchedTotal ),
1381
+ ErrBlindedPaymentTotalAmountMismatch ,
1382
+ )
1383
+
1384
+ matching := makeLastHopAttemptInfo (
1385
+ 4 ,
1386
+ lastHopArgs {amt : 2500 , total : total , encrypted : []byte {4 }},
1387
+ )
1388
+ require .NoError (t , verifyAttempt (payment , & matching ))
1389
+ }
1390
+
1391
+ // TestVerifyAttemptBlindedMixedWithNonBlinded tests that we return an error if
1392
+ // we try to register a non-MPP attempt for a blinded payment.
1393
+ func TestVerifyAttemptBlindedMixedWithNonBlinded (t * testing.T ) {
1394
+ t .Parallel ()
1395
+
1396
+ total := lnwire .MilliSatoshi (4000 )
1397
+
1398
+ // Payment with a blinded attempt.
1399
+ existing := makeLastHopAttemptInfo (
1400
+ 1 ,
1401
+ lastHopArgs {amt : 2000 , total : total , encrypted : []byte {1 }},
1402
+ )
1403
+ payment := makePayment (
1404
+ total ,
1405
+ HTLCAttempt {HTLCAttemptInfo : existing },
1406
+ )
1407
+
1408
+ partial := makeLastHopAttemptInfo (2 , lastHopArgs {amt : 2000 })
1409
+ require .ErrorIs (
1410
+ t ,
1411
+ verifyAttempt (payment , & partial ),
1412
+ ErrMixedBlindedAndNonBlindedPayments ,
1413
+ )
1414
+
1415
+ full := makeLastHopAttemptInfo (3 , lastHopArgs {amt : total })
1416
+ require .ErrorIs (
1417
+ t ,
1418
+ verifyAttempt (payment , & full ),
1419
+ ErrMixedBlindedAndNonBlindedPayments ,
1420
+ )
1421
+ }
1422
+
1423
+ // TestVerifyAttemptAmountExceedsTotal tests that we return an error if the
1424
+ // attempted amount exceeds the payment amount.
1425
+ func TestVerifyAttemptAmountExceedsTotal (t * testing.T ) {
1426
+ t .Parallel ()
1427
+
1428
+ total := lnwire .MilliSatoshi (1000 )
1429
+ mpp := record .NewMPP (total , testHash )
1430
+ existing := makeLastHopAttemptInfo (1 , lastHopArgs {amt : 800 , mpp : mpp })
1431
+ payment := makePayment (
1432
+ total ,
1433
+ HTLCAttempt {HTLCAttemptInfo : existing },
1434
+ )
1435
+
1436
+ attempt := makeLastHopAttemptInfo (2 , lastHopArgs {amt : 300 , mpp : mpp })
1437
+ require .ErrorIs (t , verifyAttempt (payment , & attempt ), ErrValueExceedsAmt )
1438
+ }
1439
+
1181
1440
// TestEmptyRoutesGenerateSphinxPacket tests that the generateSphinxPacket
1182
1441
// function is able to gracefully handle being passed a nil set of hops for the
1183
1442
// route by the caller.
0 commit comments