mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-04 01:36:24 +01:00
itest: refactor testMaxHtlcPathfind
This commit is contained in:
parent
ec3e1561b6
commit
2a7830d442
3 changed files with 74 additions and 141 deletions
|
@ -251,4 +251,8 @@ var allTestCasesTemp = []*lntemp.TestCase{
|
||||||
Name: "wumbo channels",
|
Name: "wumbo channels",
|
||||||
TestFunc: testWumboChannels,
|
TestFunc: testWumboChannels,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "max htlc pathfind",
|
||||||
|
TestFunc: testMaxHtlcPathfind,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
package itest
|
package itest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||||
"github.com/lightningnetwork/lnd/lntest"
|
"github.com/lightningnetwork/lnd/lntemp"
|
||||||
|
"github.com/lightningnetwork/lnd/lntemp/node"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -16,118 +14,69 @@ import (
|
||||||
// channel where we have already reached the limit of the number of htlcs that
|
// channel where we have already reached the limit of the number of htlcs that
|
||||||
// we may add to the remote party's commitment. This test asserts that we do
|
// we may add to the remote party's commitment. This test asserts that we do
|
||||||
// not attempt to use the full channel at all in our pathfinding.
|
// not attempt to use the full channel at all in our pathfinding.
|
||||||
func testMaxHtlcPathfind(net *lntest.NetworkHarness, t *harnessTest) {
|
func testMaxHtlcPathfind(ht *lntemp.HarnessTest) {
|
||||||
ctxb := context.Background()
|
|
||||||
|
|
||||||
// Setup a channel between Alice and Bob where Alice will only allow
|
// Setup a channel between Alice and Bob where Alice will only allow
|
||||||
// Bob to add a maximum of 5 htlcs to her commitment.
|
// Bob to add a maximum of 5 htlcs to her commitment.
|
||||||
maxHtlcs := 5
|
maxHtlcs := 5
|
||||||
|
|
||||||
chanPoint := openChannelAndAssert(
|
alice, bob := ht.Alice, ht.Bob
|
||||||
t, net, net.Alice, net.Bob,
|
chanPoint := ht.OpenChannel(
|
||||||
lntest.OpenChannelParams{
|
alice, bob, lntemp.OpenChannelParams{
|
||||||
Amt: 1000000,
|
Amt: 1000000,
|
||||||
PushAmt: 800000,
|
PushAmt: 800000,
|
||||||
RemoteMaxHtlcs: uint16(maxHtlcs),
|
RemoteMaxHtlcs: uint16(maxHtlcs),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// Wait for Alice and Bob to receive the channel edge from the
|
|
||||||
// funding manager.
|
|
||||||
err := net.Alice.WaitForNetworkChannelOpen(chanPoint)
|
|
||||||
require.NoError(t.t, err, "alice does not have open channel")
|
|
||||||
|
|
||||||
err = net.Bob.WaitForNetworkChannelOpen(chanPoint)
|
|
||||||
require.NoError(t.t, err, "bob does not have open channel")
|
|
||||||
|
|
||||||
// Alice and bob should have one channel open with each other now.
|
// Alice and bob should have one channel open with each other now.
|
||||||
assertNodeNumChannels(t, net.Alice, 1)
|
ht.AssertNodeNumChannels(alice, 1)
|
||||||
assertNodeNumChannels(t, net.Bob, 1)
|
ht.AssertNodeNumChannels(bob, 1)
|
||||||
|
|
||||||
// Send our maximum number of htlcs from Bob -> Alice so that we get
|
// Send our maximum number of htlcs from Bob -> Alice so that we get
|
||||||
// to a point where Alice won't accept any more htlcs on the channel.
|
// to a point where Alice won't accept any more htlcs on the channel.
|
||||||
subscriptions := make([]*holdSubscription, maxHtlcs)
|
subscriptions := make([]*holdSubscription, maxHtlcs)
|
||||||
cancelCtxs := make([]func(), maxHtlcs)
|
|
||||||
|
|
||||||
for i := 0; i < maxHtlcs; i++ {
|
for i := 0; i < maxHtlcs; i++ {
|
||||||
subCtx, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
subscriptions[i] = acceptHoldInvoice(ht, i, bob, alice)
|
||||||
cancelCtxs[i] = cancel
|
|
||||||
|
|
||||||
subscriptions[i] = acceptHoldInvoice(
|
|
||||||
subCtx, t.t, i, net.Bob, net.Alice,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel all of our subscriptions on exit.
|
ht.AssertNumActiveHtlcs(alice, maxHtlcs)
|
||||||
defer func() {
|
ht.AssertNumActiveHtlcs(bob, maxHtlcs)
|
||||||
for _, cancel := range cancelCtxs {
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = assertNumActiveHtlcs([]*lntest.HarnessNode{
|
|
||||||
net.Alice, net.Bob,
|
|
||||||
}, maxHtlcs)
|
|
||||||
require.NoError(t.t, err, "htlcs not active")
|
|
||||||
|
|
||||||
// Now we send a payment from Alice -> Bob to sanity check that our
|
// Now we send a payment from Alice -> Bob to sanity check that our
|
||||||
// commitment limit is not applied in the opposite direction.
|
// commitment limit is not applied in the opposite direction.
|
||||||
subCtx, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
aliceBobSub := acceptHoldInvoice(ht, maxHtlcs, alice, bob)
|
||||||
defer cancel()
|
ht.AssertNumActiveHtlcs(alice, maxHtlcs+1)
|
||||||
aliceBobSub := acceptHoldInvoice(
|
ht.AssertNumActiveHtlcs(bob, maxHtlcs+1)
|
||||||
subCtx, t.t, maxHtlcs, net.Alice, net.Bob,
|
|
||||||
)
|
|
||||||
err = assertNumActiveHtlcs([]*lntest.HarnessNode{
|
|
||||||
net.Alice, net.Bob,
|
|
||||||
}, maxHtlcs+1)
|
|
||||||
require.NoError(t.t, err, "htlcs not active")
|
|
||||||
|
|
||||||
// Now, we're going to try to send another payment from Bob -> Alice.
|
// Now, we're going to try to send another payment from Bob -> Alice.
|
||||||
// We've hit our max remote htlcs, so we expect this payment to spin
|
// We've hit our max remote htlcs, so we expect this payment to spin
|
||||||
// out dramatically with pathfinding.
|
// out dramatically with pathfinding.
|
||||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
sendReq := &routerrpc.SendPaymentRequest{
|
||||||
payment, err := net.Bob.RouterClient.SendPaymentV2(
|
|
||||||
ctxt, &routerrpc.SendPaymentRequest{
|
|
||||||
Amt: 1000,
|
Amt: 1000,
|
||||||
Dest: net.Alice.PubKey[:],
|
Dest: alice.PubKey[:],
|
||||||
TimeoutSeconds: 60,
|
TimeoutSeconds: 60,
|
||||||
FeeLimitSat: 1000000,
|
FeeLimitSat: 1000000,
|
||||||
MaxParts: 10,
|
MaxParts: 10,
|
||||||
Amp: true,
|
Amp: true,
|
||||||
},
|
}
|
||||||
)
|
ht.SendPaymentAndAssertStatus(bob, sendReq, lnrpc.Payment_FAILED)
|
||||||
require.NoError(t.t, err, "send payment failed")
|
|
||||||
|
|
||||||
update, err := payment.Recv()
|
|
||||||
require.NoError(t.t, err, "no payment in flight update")
|
|
||||||
require.Equal(t.t, lnrpc.Payment_IN_FLIGHT, update.Status,
|
|
||||||
"payment not inflight")
|
|
||||||
|
|
||||||
update, err = payment.Recv()
|
|
||||||
require.NoError(t.t, err, "no payment failed update")
|
|
||||||
require.Equal(t.t, lnrpc.Payment_FAILED, update.Status)
|
|
||||||
require.Len(t.t, update.Htlcs, 0, "expected no htlcs dispatched")
|
|
||||||
|
|
||||||
// Now that we're done, we cancel all our pending htlcs so that we
|
// Now that we're done, we cancel all our pending htlcs so that we
|
||||||
// can cleanup the channel with a coop close.
|
// can cleanup the channel with a coop close.
|
||||||
for _, sub := range subscriptions {
|
for _, sub := range subscriptions {
|
||||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
sub.cancel(ht)
|
||||||
sub.cancel(ctxt, t.t)
|
|
||||||
}
|
}
|
||||||
|
aliceBobSub.cancel(ht)
|
||||||
|
|
||||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
ht.AssertNumActiveHtlcs(alice, 0)
|
||||||
aliceBobSub.cancel(ctxt, t.t)
|
ht.AssertNumActiveHtlcs(bob, 0)
|
||||||
|
|
||||||
err = assertNumActiveHtlcs([]*lntest.HarnessNode{
|
ht.CloseChannel(alice, chanPoint)
|
||||||
net.Alice, net.Bob,
|
|
||||||
}, 0)
|
|
||||||
require.NoError(t.t, err, "expected all htlcs canceled")
|
|
||||||
|
|
||||||
closeChannelAndAssert(t, net, net.Alice, chanPoint, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type holdSubscription struct {
|
type holdSubscription struct {
|
||||||
recipient invoicesrpc.InvoicesClient
|
recipient *node.HarnessNode
|
||||||
hash lntypes.Hash
|
hash lntypes.Hash
|
||||||
invSubscription invoicesrpc.Invoices_SubscribeSingleInvoiceClient
|
invSubscription invoicesrpc.Invoices_SubscribeSingleInvoiceClient
|
||||||
paymentSubscription routerrpc.Router_SendPaymentV2Client
|
paymentSubscription routerrpc.Router_SendPaymentV2Client
|
||||||
|
@ -135,89 +84,73 @@ type holdSubscription struct {
|
||||||
|
|
||||||
// cancel updates a hold invoice to cancel from the recipient and consumes
|
// cancel updates a hold invoice to cancel from the recipient and consumes
|
||||||
// updates from the payer until it has reached a final, failed state.
|
// updates from the payer until it has reached a final, failed state.
|
||||||
func (h *holdSubscription) cancel(ctx context.Context, t *testing.T) {
|
func (h *holdSubscription) cancel(ht *lntemp.HarnessTest) {
|
||||||
_, err := h.recipient.CancelInvoice(ctx, &invoicesrpc.CancelInvoiceMsg{
|
h.recipient.RPC.CancelInvoice(h.hash[:])
|
||||||
PaymentHash: h.hash[:],
|
|
||||||
})
|
|
||||||
require.NoError(t, err, "invoice cancel failed")
|
|
||||||
|
|
||||||
invUpdate, err := h.invSubscription.Recv()
|
invUpdate := ht.ReceiveSingleInvoice(h.invSubscription)
|
||||||
require.NoError(t, err, "cancel invoice subscribe failed")
|
require.Equal(ht, lnrpc.Invoice_CANCELED, invUpdate.State,
|
||||||
require.Equal(t, lnrpc.Invoice_CANCELED, invUpdate.State,
|
|
||||||
"expected invoice canceled")
|
"expected invoice canceled")
|
||||||
|
|
||||||
// We expect one in flight update when our htlc is canceled back, and
|
// We expect one in flight update when our htlc is canceled back, and
|
||||||
// another when we fail the payment as a whole.
|
// another when we fail the payment as a whole.
|
||||||
payUpdate, err := h.paymentSubscription.Recv()
|
payUpdate := ht.AssertPaymentStatusFromStream(
|
||||||
require.NoError(t, err, "cancel payment subscribe failed")
|
h.paymentSubscription, lnrpc.Payment_IN_FLIGHT,
|
||||||
require.Len(t, payUpdate.Htlcs, 1)
|
)
|
||||||
require.Equal(t, lnrpc.Payment_IN_FLIGHT, payUpdate.Status)
|
require.Len(ht, payUpdate.Htlcs, 1)
|
||||||
|
|
||||||
payUpdate, err = h.paymentSubscription.Recv()
|
payUpdate = ht.AssertPaymentStatusFromStream(
|
||||||
require.NoError(t, err, "cancel payment subscribe failed")
|
h.paymentSubscription, lnrpc.Payment_FAILED,
|
||||||
require.Equal(t, lnrpc.Payment_FAILED, payUpdate.Status,
|
)
|
||||||
|
require.Equal(ht, lnrpc.Payment_FAILED, payUpdate.Status,
|
||||||
"expected payment failed")
|
"expected payment failed")
|
||||||
require.Equal(t, lnrpc.PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS,
|
require.Equal(ht, lnrpc.PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS, //nolint:lll
|
||||||
payUpdate.FailureReason, "expected unknown details")
|
payUpdate.FailureReason, "expected unknown details")
|
||||||
}
|
}
|
||||||
|
|
||||||
// acceptHoldInvoice adds a hold invoice to the recipient node, pays it from
|
// acceptHoldInvoice adds a hold invoice to the recipient node, pays it from
|
||||||
// the sender and asserts that we have reached the accepted state where htlcs
|
// the sender and asserts that we have reached the accepted state where htlcs
|
||||||
// are locked in for the payment.
|
// are locked in for the payment.
|
||||||
func acceptHoldInvoice(ctx context.Context, t *testing.T, idx int, sender,
|
func acceptHoldInvoice(ht *lntemp.HarnessTest, idx int, sender,
|
||||||
receiver *lntest.HarnessNode) *holdSubscription {
|
receiver *node.HarnessNode) *holdSubscription {
|
||||||
|
|
||||||
hash := [lntypes.HashSize]byte{byte(idx + 1)}
|
hash := [lntypes.HashSize]byte{byte(idx + 1)}
|
||||||
|
|
||||||
invoice, err := receiver.AddHoldInvoice(
|
req := &invoicesrpc.AddHoldInvoiceRequest{
|
||||||
ctx, &invoicesrpc.AddHoldInvoiceRequest{
|
|
||||||
ValueMsat: 10000,
|
ValueMsat: 10000,
|
||||||
Hash: hash[:],
|
Hash: hash[:],
|
||||||
},
|
}
|
||||||
)
|
invoice := receiver.RPC.AddHoldInvoice(req)
|
||||||
require.NoError(t, err, "couldn't add invoice")
|
|
||||||
|
|
||||||
invStream, err := receiver.InvoicesClient.SubscribeSingleInvoice(
|
invStream := receiver.RPC.SubscribeSingleInvoice(hash[:])
|
||||||
ctx, &invoicesrpc.SubscribeSingleInvoiceRequest{
|
inv := ht.ReceiveSingleInvoice(invStream)
|
||||||
RHash: hash[:],
|
require.Equal(ht, lnrpc.Invoice_OPEN, inv.State, "expect open")
|
||||||
},
|
|
||||||
)
|
|
||||||
require.NoError(t, err, "could not subscribe to invoice")
|
|
||||||
|
|
||||||
inv, err := invStream.Recv()
|
sendReq := &routerrpc.SendPaymentRequest{
|
||||||
require.NoError(t, err, "invoice open stream failed")
|
|
||||||
require.Equal(t, lnrpc.Invoice_OPEN, inv.State,
|
|
||||||
"expected open")
|
|
||||||
|
|
||||||
payStream, err := sender.RouterClient.SendPaymentV2(
|
|
||||||
ctx, &routerrpc.SendPaymentRequest{
|
|
||||||
PaymentRequest: invoice.PaymentRequest,
|
PaymentRequest: invoice.PaymentRequest,
|
||||||
TimeoutSeconds: 60,
|
TimeoutSeconds: 60,
|
||||||
FeeLimitSat: 1000000,
|
FeeLimitSat: 1000000,
|
||||||
},
|
}
|
||||||
)
|
payStream := sender.RPC.SendPayment(sendReq)
|
||||||
require.NoError(t, err, "send payment failed")
|
|
||||||
|
|
||||||
// Finally, assert that we progress to an accepted state. We expect
|
// Finally, assert that we progress to an accepted state. We expect
|
||||||
// the payer to get one update for the creation of the payment, and
|
// the payer to get one update for the creation of the payment, and
|
||||||
// another when a htlc is dispatched.
|
// another when a htlc is dispatched.
|
||||||
payment, err := payStream.Recv()
|
payment := ht.AssertPaymentStatusFromStream(
|
||||||
require.NoError(t, err, "payment in flight stream failed")
|
payStream, lnrpc.Payment_IN_FLIGHT,
|
||||||
require.Equal(t, lnrpc.Payment_IN_FLIGHT, payment.Status)
|
)
|
||||||
require.Len(t, payment.Htlcs, 0)
|
require.Empty(ht, payment.Htlcs)
|
||||||
|
|
||||||
payment, err = payStream.Recv()
|
payment = ht.AssertPaymentStatusFromStream(
|
||||||
require.NoError(t, err, "payment in flight stream failed")
|
payStream, lnrpc.Payment_IN_FLIGHT,
|
||||||
require.Equal(t, lnrpc.Payment_IN_FLIGHT, payment.Status)
|
)
|
||||||
require.Len(t, payment.Htlcs, 1)
|
require.Len(ht, payment.Htlcs, 1)
|
||||||
|
|
||||||
inv, err = invStream.Recv()
|
inv = ht.ReceiveSingleInvoice(invStream)
|
||||||
require.NoError(t, err, "invoice accepted stream failed")
|
require.Equal(ht, lnrpc.Invoice_ACCEPTED, inv.State,
|
||||||
require.Equal(t, lnrpc.Invoice_ACCEPTED, inv.State,
|
"expected accepted")
|
||||||
"expected accepted invoice")
|
|
||||||
|
|
||||||
return &holdSubscription{
|
return &holdSubscription{
|
||||||
recipient: receiver.InvoicesClient,
|
recipient: receiver,
|
||||||
hash: hash,
|
hash: hash,
|
||||||
invSubscription: invStream,
|
invSubscription: invStream,
|
||||||
paymentSubscription: payStream,
|
paymentSubscription: payStream,
|
||||||
|
|
|
@ -194,10 +194,6 @@ var allTestCases = []*testCase{
|
||||||
name: "wallet import pubkey",
|
name: "wallet import pubkey",
|
||||||
test: testWalletImportPubKey,
|
test: testWalletImportPubKey,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "max htlc pathfind",
|
|
||||||
test: testMaxHtlcPathfind,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "rpc middleware interceptor",
|
name: "rpc middleware interceptor",
|
||||||
test: testRPCMiddlewareInterceptor,
|
test: testRPCMiddlewareInterceptor,
|
||||||
|
|
Loading…
Add table
Reference in a new issue