Skip to content

Commit 3c6f16b

Browse files
committed
paymentsdb: add more unit tests to increase coverage
We add a couple of additional tests to increase the unit test coverage of the sql store but also the kv store. We only create db agnostic unit tests so both backends are tested effectively.
1 parent 7d89e21 commit 3c6f16b

File tree

1 file changed

+294
-9
lines changed

1 file changed

+294
-9
lines changed

payments/db/payment_test.go

Lines changed: 294 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,23 +82,24 @@ var (
8282
SourcePubKey: vertex,
8383
Hops: []*route.Hop{
8484
{
85-
PubKeyBytes: vertex,
86-
ChannelID: 9876,
87-
OutgoingTimeLock: 120,
88-
AmtToForward: 900,
89-
EncryptedData: []byte{1, 3, 3},
90-
BlindingPoint: pub,
85+
PubKeyBytes: vertex,
86+
EncryptedData: []byte{1, 3, 3},
87+
BlindingPoint: pub,
9188
},
9289
{
9390
PubKeyBytes: vertex,
9491
EncryptedData: []byte{3, 2, 1},
9592
},
9693
{
94+
// Final hop must have AmtToForward,
95+
// OutgoingTimeLock, and TotalAmtMsat per
96+
// BOLT spec. We use the correct values here
97+
// although it is not tested in this test.
9798
PubKeyBytes: vertex,
98-
Metadata: []byte{4, 5, 6},
99-
AmtToForward: 500,
99+
EncryptedData: []byte{2, 2, 2},
100+
AmtToForward: 1000,
100101
OutgoingTimeLock: 100,
101-
TotalAmtMsat: 500,
102+
TotalAmtMsat: 1000,
102103
},
103104
},
104105
}
@@ -3046,3 +3047,287 @@ func TestRouteFirstHopData(t *testing.T) {
30463047
htlc.Route.FirstHopWireCustomRecords[typeIdx2],
30473048
)
30483049
}
3050+
3051+
// TestRegisterAttemptWithAMP tests that AMP data is correctly stored and
3052+
// retrieved on route hops.
3053+
func TestRegisterAttemptWithAMP(t *testing.T) {
3054+
t.Parallel()
3055+
3056+
ctx := t.Context()
3057+
3058+
paymentDB, _ := NewTestDB(t)
3059+
3060+
preimg := genPreimage(t)
3061+
rhash := sha256.Sum256(preimg[:])
3062+
info := genPaymentCreationInfo(t, rhash)
3063+
3064+
// Init payment.
3065+
err := paymentDB.InitPayment(ctx, info.PaymentIdentifier, info)
3066+
require.NoError(t, err)
3067+
3068+
// Create a basic attempt, then modify the route to include AMP data.
3069+
// This bypasses the route validation in NewHtlcAttempt.
3070+
attempt := genAttemptWithHash(t, 0, genSessionKey(t), rhash)
3071+
3072+
// Add AMP data to the final hop.
3073+
rootShare := [32]byte{1, 2, 3, 4}
3074+
setID := [32]byte{5, 6, 7, 8}
3075+
childIndex := uint32(42)
3076+
3077+
finalHopIdx := len(attempt.Route.Hops) - 1
3078+
attempt.Route.Hops[finalHopIdx].AMP = record.NewAMP(
3079+
rootShare, setID, childIndex,
3080+
)
3081+
3082+
_, err = paymentDB.RegisterAttempt(ctx, info.PaymentIdentifier, attempt)
3083+
require.NoError(t, err)
3084+
3085+
// Fetch the payment and verify AMP data was stored.
3086+
payment, err := paymentDB.FetchPayment(ctx, info.PaymentIdentifier)
3087+
require.NoError(t, err)
3088+
3089+
require.Len(t, payment.HTLCs, 1)
3090+
htlc := payment.HTLCs[0]
3091+
3092+
// Verify the AMP data on the final hop matches what we set.
3093+
finalHop := htlc.Route.Hops[finalHopIdx]
3094+
require.NotNil(t, finalHop.AMP)
3095+
require.Equal(t, rootShare, finalHop.AMP.RootShare())
3096+
require.Equal(t, setID, finalHop.AMP.SetID())
3097+
require.Equal(t, childIndex, finalHop.AMP.ChildIndex())
3098+
}
3099+
3100+
// TestRegisterAttemptWithBlindedRoute tests that blinded route data
3101+
// (EncryptedData, BlindingPoint, TotalAmtMsat) is correctly stored and
3102+
// retrieved.
3103+
func TestRegisterAttemptWithBlindedRoute(t *testing.T) {
3104+
t.Parallel()
3105+
3106+
ctx := t.Context()
3107+
3108+
paymentDB, _ := NewTestDB(t)
3109+
3110+
preimg := genPreimage(t)
3111+
rhash := sha256.Sum256(preimg[:])
3112+
3113+
// Create payment info with amount matching
3114+
// testBlindedRoute.TotalAmount.
3115+
info := &PaymentCreationInfo{
3116+
PaymentIdentifier: rhash,
3117+
Value: testBlindedRoute.TotalAmount,
3118+
CreationTime: time.Unix(time.Now().Unix(), 0),
3119+
PaymentRequest: []byte("blinded"),
3120+
}
3121+
3122+
// Init payment.
3123+
err := paymentDB.InitPayment(ctx, info.PaymentIdentifier, info)
3124+
require.NoError(t, err)
3125+
3126+
// Create a basic attempt, then replace the route with testBlindedRoute.
3127+
// This bypasses the route validation in NewHtlcAttempt.
3128+
attempt := genAttemptWithHash(t, 0, genSessionKey(t), rhash)
3129+
3130+
// Replace with testBlindedRoute which has the correct blinded route
3131+
// structure.
3132+
attempt.Route = testBlindedRoute
3133+
3134+
_, err = paymentDB.RegisterAttempt(ctx, info.PaymentIdentifier, attempt)
3135+
require.NoError(t, err)
3136+
3137+
// Fetch the payment and verify blinded route data was stored.
3138+
payment, err := paymentDB.FetchPayment(ctx, info.PaymentIdentifier)
3139+
require.NoError(t, err)
3140+
3141+
require.Len(t, payment.HTLCs, 1)
3142+
htlc := payment.HTLCs[0]
3143+
3144+
// Verify the blinded route data.
3145+
require.Len(t, htlc.Route.Hops, 3)
3146+
3147+
// First hop (introduction point) should have BlindingPoint and
3148+
// EncryptedData.
3149+
hop0 := htlc.Route.Hops[0]
3150+
require.Equal(t, []byte{1, 3, 3}, hop0.EncryptedData)
3151+
require.NotNil(t, hop0.BlindingPoint)
3152+
require.True(t, hop0.BlindingPoint.IsEqual(pub))
3153+
3154+
// Second hop (intermediate) should have only EncryptedData.
3155+
hop1 := htlc.Route.Hops[1]
3156+
require.Equal(t, []byte{3, 2, 1}, hop1.EncryptedData)
3157+
require.Nil(t, hop1.BlindingPoint)
3158+
3159+
// Third hop (final) should have EncryptedData, AmtToForward,
3160+
// OutgoingTimeLock, and TotalAmtMsat.
3161+
hop2 := htlc.Route.Hops[2]
3162+
require.Equal(t, []byte{2, 2, 2}, hop2.EncryptedData)
3163+
require.Equal(t, lnwire.MilliSatoshi(1000), hop2.AmtToForward)
3164+
require.Equal(t, uint32(100), hop2.OutgoingTimeLock)
3165+
require.Equal(t, lnwire.MilliSatoshi(1000), hop2.TotalAmtMsat)
3166+
}
3167+
3168+
// TestFailAttemptWithoutMessage tests that FailAttempt works correctly when
3169+
// no failure message is provided.
3170+
func TestFailAttemptWithoutMessage(t *testing.T) {
3171+
t.Parallel()
3172+
3173+
ctx := t.Context()
3174+
3175+
paymentDB, _ := NewTestDB(t)
3176+
3177+
preimg := genPreimage(t)
3178+
rhash := sha256.Sum256(preimg[:])
3179+
info := genPaymentCreationInfo(t, rhash)
3180+
3181+
// Init payment.
3182+
err := paymentDB.InitPayment(ctx, info.PaymentIdentifier, info)
3183+
require.NoError(t, err)
3184+
3185+
// Register an attempt.
3186+
attempt := genAttemptWithHash(t, 0, genSessionKey(t), rhash)
3187+
3188+
_, err = paymentDB.RegisterAttempt(ctx, info.PaymentIdentifier, attempt)
3189+
require.NoError(t, err)
3190+
3191+
// Fail the attempt without a failure message (nil Message).
3192+
failInfo := &HTLCFailInfo{
3193+
Reason: HTLCFailUnreadable,
3194+
FailureSourceIndex: 2,
3195+
Message: nil, // No message.
3196+
}
3197+
3198+
payment, err := paymentDB.FailAttempt(
3199+
ctx, info.PaymentIdentifier, attempt.AttemptID, failInfo,
3200+
)
3201+
require.NoError(t, err)
3202+
require.NotNil(t, payment)
3203+
3204+
// Verify the attempt was failed.
3205+
require.Len(t, payment.HTLCs, 1)
3206+
htlc := payment.HTLCs[0]
3207+
require.NotNil(t, htlc.Failure)
3208+
require.Equal(t, HTLCFailUnreadable, htlc.Failure.Reason)
3209+
require.Equal(t, uint32(2), htlc.Failure.FailureSourceIndex)
3210+
require.Nil(t, htlc.Failure.Message)
3211+
}
3212+
3213+
// TestFailAttemptWithMessage tests that FailAttempt correctly stores and
3214+
// retrieves a failure message.
3215+
func TestFailAttemptWithMessage(t *testing.T) {
3216+
t.Parallel()
3217+
3218+
ctx := t.Context()
3219+
3220+
paymentDB, _ := NewTestDB(t)
3221+
3222+
preimg := genPreimage(t)
3223+
rhash := sha256.Sum256(preimg[:])
3224+
info := genPaymentCreationInfo(t, rhash)
3225+
3226+
// Init payment.
3227+
err := paymentDB.InitPayment(ctx, info.PaymentIdentifier, info)
3228+
require.NoError(t, err)
3229+
3230+
// Register an attempt.
3231+
attempt := genAttemptWithHash(t, 0, genSessionKey(t), rhash)
3232+
3233+
_, err = paymentDB.RegisterAttempt(ctx, info.PaymentIdentifier, attempt)
3234+
require.NoError(t, err)
3235+
3236+
// Create a failure message.
3237+
failureMsg := lnwire.NewTemporaryChannelFailure(nil)
3238+
3239+
// Fail the attempt with a failure message.
3240+
failInfo := &HTLCFailInfo{
3241+
Reason: HTLCFailUnreadable,
3242+
FailureSourceIndex: 1,
3243+
Message: failureMsg,
3244+
}
3245+
3246+
payment, err := paymentDB.FailAttempt(
3247+
ctx, info.PaymentIdentifier, attempt.AttemptID, failInfo,
3248+
)
3249+
require.NoError(t, err)
3250+
require.NotNil(t, payment)
3251+
3252+
// Verify the attempt was failed.
3253+
require.Len(t, payment.HTLCs, 1)
3254+
htlc := payment.HTLCs[0]
3255+
require.NotNil(t, htlc.Failure)
3256+
require.Equal(t, HTLCFailUnreadable, htlc.Failure.Reason)
3257+
}
3258+
3259+
// TestFailAttemptOnSucceededPayment tests that FailAttempt returns an error
3260+
// when trying to fail an attempt on an already succeeded payment.
3261+
func TestFailAttemptOnSucceededPayment(t *testing.T) {
3262+
t.Parallel()
3263+
3264+
ctx := t.Context()
3265+
3266+
paymentDB, _ := NewTestDB(t)
3267+
3268+
preimg := genPreimage(t)
3269+
rhash := sha256.Sum256(preimg[:])
3270+
info := genPaymentCreationInfo(t, rhash)
3271+
3272+
// Init payment.
3273+
err := paymentDB.InitPayment(ctx, info.PaymentIdentifier, info)
3274+
require.NoError(t, err)
3275+
3276+
// Register an attempt.
3277+
attempt := genAttemptWithHash(t, 0, genSessionKey(t), rhash)
3278+
3279+
_, err = paymentDB.RegisterAttempt(ctx, info.PaymentIdentifier, attempt)
3280+
require.NoError(t, err)
3281+
3282+
// Settle the attempt, which makes the payment succeed.
3283+
_, err = paymentDB.SettleAttempt(
3284+
ctx, info.PaymentIdentifier, attempt.AttemptID,
3285+
&HTLCSettleInfo{Preimage: preimg},
3286+
)
3287+
require.NoError(t, err)
3288+
3289+
// Now try to fail the same attempt - this should fail because the
3290+
// payment is already succeeded.
3291+
failInfo := &HTLCFailInfo{
3292+
Reason: HTLCFailUnreadable,
3293+
}
3294+
3295+
_, err = paymentDB.FailAttempt(
3296+
ctx, info.PaymentIdentifier, attempt.AttemptID, failInfo,
3297+
)
3298+
require.Error(t, err)
3299+
require.ErrorIs(t, err, ErrPaymentAlreadySucceeded)
3300+
}
3301+
3302+
// TestFetchPaymentWithNoAttempts tests that FetchPayment correctly returns a
3303+
// payment that has been initialized but has no HTLC attempts yet. This tests
3304+
// the early return path in batchLoadPaymentDetailsData when there are no
3305+
// attempts.
3306+
func TestFetchPaymentWithNoAttempts(t *testing.T) {
3307+
t.Parallel()
3308+
3309+
ctx := t.Context()
3310+
3311+
paymentDB, _ := NewTestDB(t)
3312+
3313+
preimg := genPreimage(t)
3314+
rhash := sha256.Sum256(preimg[:])
3315+
info := genPaymentCreationInfo(t, rhash)
3316+
3317+
// Init payment but don't register any attempts.
3318+
err := paymentDB.InitPayment(ctx, info.PaymentIdentifier, info)
3319+
require.NoError(t, err)
3320+
3321+
// Fetch the payment - it should have no HTLCs.
3322+
payment, err := paymentDB.FetchPayment(ctx, info.PaymentIdentifier)
3323+
require.NoError(t, err)
3324+
require.NotNil(t, payment)
3325+
3326+
// Verify the payment has no HTLCs.
3327+
require.Empty(t, payment.HTLCs)
3328+
3329+
// Verify the payment info is correct.
3330+
require.Equal(t, info.PaymentIdentifier, payment.Info.PaymentIdentifier)
3331+
require.Equal(t, info.Value, payment.Info.Value)
3332+
require.Equal(t, StatusInitiated, payment.Status)
3333+
}

0 commit comments

Comments
 (0)