itest: refactor testMaxHtlcPathfind

This commit is contained in:
yyforyongyu 2022-08-05 18:22:19 +08:00
parent ec3e1561b6
commit 2a7830d442
No known key found for this signature in database
GPG key ID: 9BCD95C4FF296868
3 changed files with 74 additions and 141 deletions

View file

@ -251,4 +251,8 @@ var allTestCasesTemp = []*lntemp.TestCase{
Name: "wumbo channels", Name: "wumbo channels",
TestFunc: testWumboChannels, TestFunc: testWumboChannels,
}, },
{
Name: "max htlc pathfind",
TestFunc: testMaxHtlcPathfind,
},
} }

View file

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

View file

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