@@ -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