@@ -1178,6 +1178,265 @@ func makeAttemptInfo(total, amtForwarded int) HTLCAttemptInfo {
11781178 }
11791179}
11801180
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+
11811440// TestEmptyRoutesGenerateSphinxPacket tests that the generateSphinxPacket
11821441// function is able to gracefully handle being passed a nil set of hops for the
11831442// route by the caller.
0 commit comments