@@ -2,8 +2,10 @@ package itest
2
2
3
3
import (
4
4
"encoding/hex"
5
+ "time"
5
6
6
7
"github.com/btcsuite/btcd/btcutil"
8
+ "github.com/lightningnetwork/lnd/chainreg"
7
9
"github.com/lightningnetwork/lnd/lnrpc"
8
10
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
9
11
"github.com/lightningnetwork/lnd/lntest"
@@ -148,3 +150,96 @@ func testTrackPaymentsCompatible(ht *lntest.HarnessTest) {
148
150
require .NoError (ht , err , "unable to get payment update" )
149
151
require .Equal (ht , lnrpc .Payment_SUCCEEDED , payment3 .Status )
150
152
}
153
+
154
+ // testSendToRouteFailPaymentNotification tests that when we are failing a
155
+ // payment via SendToRouteV2 we only receive one failure notification, also
156
+ // making sure the failure reason is not overwritten in our db.
157
+ func testSendToRouteFailPaymentNotification (ht * lntest.HarnessTest ) {
158
+ // Create a simple network with Alice->Bob.
159
+ _ , nodes := ht .CreateSimpleNetwork (
160
+ [][]string {nil , nil },
161
+ lntest.OpenChannelParams {Amt : 100_000 },
162
+ )
163
+ alice , bob := nodes [0 ], nodes [1 ]
164
+
165
+ // Make Bob create an invoice for Alice to pay.
166
+ _ , rHashes , _ := ht .CreatePayReqs (bob , paymentAmt , 1 )
167
+
168
+ rHash := rHashes [0 ]
169
+
170
+ // We create a dummy payment address so that the payment fails at
171
+ // the first hop.
172
+ payAddr := make ([]byte , 32 )
173
+
174
+ // Subscribe to all the payments because otherwise we would not be able
175
+ // to verify if two failure notifications are received. The single
176
+ // subscriber is deleted after receiving the first failure notification.
177
+ tracker := alice .RPC .TrackPayments (& routerrpc.TrackPaymentsRequest {
178
+ NoInflightUpdates : true ,
179
+ })
180
+
181
+ bobPubKey , err := hex .DecodeString (bob .PubKeyStr )
182
+ require .NoError (ht , err , "unable to decode bob's pubkey" )
183
+
184
+ // Build a route for the specified hops.
185
+ r := alice .RPC .BuildRoute (& routerrpc.BuildRouteRequest {
186
+ AmtMsat : int64 (paymentAmt * 1000 ),
187
+ FinalCltvDelta : chainreg .DefaultBitcoinTimeLockDelta ,
188
+ HopPubkeys : [][]byte {bobPubKey },
189
+ }).Route
190
+
191
+ // Set the MPP records to indicate this is a payment shard.
192
+ hop := r .Hops [len (r .Hops )- 1 ]
193
+ hop .MppRecord = & lnrpc.MPPRecord {
194
+ PaymentAddr : payAddr ,
195
+ TotalAmtMsat : int64 (paymentAmt * 1000 ),
196
+ }
197
+
198
+ // Send the only shard.
199
+ sendReq := & routerrpc.SendToRouteRequest {
200
+ PaymentHash : rHash ,
201
+ Route : r ,
202
+ }
203
+
204
+ // We launch the payment and check the payment stream to verify that
205
+ // we are receiving exactly one failure notification.
206
+ respChan := make (chan * lnrpc.HTLCAttempt , 1 )
207
+ go func () {
208
+ attempt := alice .RPC .SendToRouteV2 (sendReq )
209
+ respChan <- attempt
210
+ }()
211
+
212
+ // We excpect the attempt and the payment to fail.
213
+ select {
214
+ case attempt := <- respChan :
215
+ require .Equal (ht , attempt .Status , lnrpc .HTLCAttempt_FAILED )
216
+
217
+ case <- time .After (defaultTimeout ):
218
+ ht .Fatal ("timeout waiting for SendToRouteV2 response" )
219
+ }
220
+
221
+ // Assert the first track payment update should be a failed update.
222
+ update , err := tracker .Recv ()
223
+ require .NoError (ht , err , "unable to receive payment update" )
224
+ require .Equal (ht , lnrpc .Payment_FAILED , update .Status )
225
+
226
+ // Now check no other notifications come in.
227
+ notifChan := make (chan * lnrpc.Payment , 1 )
228
+ go func () {
229
+ notif , err := tracker .Recv ()
230
+ if err != nil {
231
+ return
232
+ }
233
+
234
+ notifChan <- notif
235
+ }()
236
+
237
+ // We do not expect any other notifications.
238
+ select {
239
+ case unexpectedNotif := <- notifChan :
240
+ ht .Fatalf ("received unexpected notification: %v" ,
241
+ unexpectedNotif )
242
+
243
+ case <- time .After (100 * time .Millisecond ):
244
+ }
245
+ }
0 commit comments