Skip to content

Commit b010e95

Browse files
committed
itest: add itest to test duplicate failure notification
1 parent 9e683a3 commit b010e95

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed

itest/list_on_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,10 @@ var allTestCases = []*lntest.TestCase{
378378
Name: "sendtoroute multi path payment",
379379
TestFunc: testSendToRouteMultiPath,
380380
},
381+
{
382+
Name: "send to route fail payment notification",
383+
TestFunc: testSendToRouteFailPaymentNotification,
384+
},
381385
{
382386
Name: "send multi path payment",
383387
TestFunc: testSendMultiPathPayment,

itest/lnd_trackpayments_test.go

+95
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package itest
22

33
import (
44
"encoding/hex"
5+
"time"
56

67
"github.com/btcsuite/btcd/btcutil"
8+
"github.com/lightningnetwork/lnd/chainreg"
79
"github.com/lightningnetwork/lnd/lnrpc"
810
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
911
"github.com/lightningnetwork/lnd/lntest"
@@ -148,3 +150,96 @@ func testTrackPaymentsCompatible(ht *lntest.HarnessTest) {
148150
require.NoError(ht, err, "unable to get payment update")
149151
require.Equal(ht, lnrpc.Payment_SUCCEEDED, payment3.Status)
150152
}
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

Comments
 (0)