Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 45 additions & 21 deletions l402/client_interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,31 +388,55 @@ func (i *ClientInterceptor) payL402Token(ctx context.Context, md *metadata.MD) (
}

// Pay invoice now and wait for the result to arrive or the main context
// being canceled.
// being canceled. The payment is dispatched through lnd's router
// subserver (SendPaymentV2), as the legacy SendPaymentSync RPC that
// was used previously is deprecated and payments made through it can
// fail to initiate on recent lnd versions.
payCtx, cancel := context.WithTimeout(ctx, PaymentTimeout)
defer cancel()
respChan := i.lnd.Client.PayInvoice(
payCtx, invoiceStr, i.maxFee, nil,
payStatusChan, payErrChan, err := i.lnd.Router.SendPayment(
payCtx, lndclient.SendPaymentRequest{
Invoice: invoiceStr,
MaxFee: i.maxFee,
Timeout: PaymentTimeout,
},
)
select {
case result := <-respChan:
if result.Err != nil {
return nil, result.Err
if err != nil {
return nil, fmt.Errorf("unable to dispatch payment: %v", err)
}

for {
select {
case result := <-payStatusChan:
switch result.State {
// The payment was successful, we have all the
// information we need and can return the fully paid
// token.
case lnrpc.Payment_SUCCEEDED:
token.Preimage = result.Preimage
token.AmountPaid = result.Value
token.RoutingFeePaid = result.Fee
return token, i.store.StoreToken(token)

case lnrpc.Payment_FAILED:
return nil, fmt.Errorf("payment failed: %v",
result.FailureReason)

// Any other state means the payment is still in
// flight, we keep waiting for a terminal update.
}

case err := <-payErrChan:
return nil, fmt.Errorf("error paying invoice: %v", err)

case <-payCtx.Done():
return nil, fmt.Errorf("payment timed out. try again "+
"to track payment. %s", manualRetryHint)

case <-ctx.Done():
return nil, fmt.Errorf("parent context canceled. try "+
"again to track payment. %s", manualRetryHint)
}
token.Preimage = result.Preimage
token.AmountPaid = lnwire.NewMSatFromSatoshis(result.PaidAmt)
token.RoutingFeePaid = lnwire.NewMSatFromSatoshis(
result.PaidFee,
)
return token, i.store.StoreToken(token)

case <-payCtx.Done():
return nil, fmt.Errorf("payment timed out. try again to track "+
"payment. %s", manualRetryHint)

case <-ctx.Done():
return nil, fmt.Errorf("parent context canceled. try again to"+
"track payment. %s", manualRetryHint)
}
}

Expand Down
26 changes: 14 additions & 12 deletions l402/client_interceptor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type interceptTestCase struct {
resetCb func(addL402 bool)
expectLndCall bool
expectSecondLndCall bool
sendPaymentCb func(*testing.T, test.PaymentChannelMessage)
sendPaymentCb func(*testing.T, test.RouterPaymentChannelMessage)
trackPaymentCb func(*testing.T, test.TrackPaymentMessage)
expectToken bool
expectInterceptErr string
Expand Down Expand Up @@ -103,17 +103,18 @@ var (
},
expectLndCall: true,
sendPaymentCb: func(t *testing.T,
msg test.PaymentChannelMessage) {
msg test.RouterPaymentChannelMessage) {

require.Len(t, callMD, 0)

// The next call to the "backend" shouldn't return an
// error.
resetBackend(nil, []string{})
msg.Done <- lndclient.PaymentResult{
msg.Updates <- lndclient.PaymentStatus{
State: lnrpc.Payment_SUCCEEDED,
Preimage: paidPreimage,
PaidAmt: 123,
PaidFee: 345,
Value: 123_000,
Fee: 345_000,
}
},
trackPaymentCb: func(t *testing.T,
Expand Down Expand Up @@ -149,7 +150,7 @@ var (
},
expectLndCall: true,
sendPaymentCb: func(t *testing.T,
msg test.PaymentChannelMessage) {
msg test.RouterPaymentChannelMessage) {

t.Fatal("didn't expect call to sendPayment")
},
Expand Down Expand Up @@ -181,17 +182,18 @@ var (
expectLndCall: true,
expectSecondLndCall: true,
sendPaymentCb: func(t *testing.T,
msg test.PaymentChannelMessage) {
msg test.RouterPaymentChannelMessage) {

require.Len(t, callMD, 0)

// The next call to the "backend" shouldn't return an
// error.
resetBackend(nil, []string{})
msg.Done <- lndclient.PaymentResult{
msg.Updates <- lndclient.PaymentStatus{
State: lnrpc.Payment_SUCCEEDED,
Preimage: paidPreimage,
PaidAmt: 123,
PaidFee: 345,
Value: 123_000,
Fee: 345_000,
}
},
trackPaymentCb: func(t *testing.T,
Expand Down Expand Up @@ -361,7 +363,7 @@ func testInterceptor(t *testing.T, tc interceptTestCase, addL402 bool,
// Simulate payment related calls to lnd, if there are any expected.
if tc.expectLndCall {
select {
case payment := <-lnd.SendPaymentChannel:
case payment := <-lnd.RouterSendPaymentChannel:
tc.sendPaymentCb(t, payment)

case track := <-lnd.TrackPaymentChannel:
Expand All @@ -373,7 +375,7 @@ func testInterceptor(t *testing.T, tc interceptTestCase, addL402 bool,
}
if tc.expectSecondLndCall {
select {
case payment := <-lnd.SendPaymentChannel:
case payment := <-lnd.RouterSendPaymentChannel:
tc.sendPaymentCb(t, payment)

case track := <-lnd.TrackPaymentChannel:
Expand Down