lnd/itest/lnd_max_htlcs_test.go

167 lines
5.4 KiB
Go
Raw Normal View History

package itest
import (
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lntest/node"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/stretchr/testify/require"
)
// testMaxHtlcPathfind tests the case where we try to send a payment over a
// 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
// not attempt to use the full channel at all in our pathfinding.
func testMaxHtlcPathfind(ht *lntest.HarnessTest) {
// Setup a channel between Alice and Bob where Alice will only allow
// Bob to add a maximum of 5 htlcs to her commitment.
maxHtlcs := 5
2022-08-05 12:22:19 +02:00
alice, bob := ht.Alice, ht.Bob
// Restart nodes with the new flag so they understand the new payment
// status.
ht.RestartNodeWithExtraArgs(alice, []string{
"--routerrpc.usestatusinitiated",
})
ht.RestartNodeWithExtraArgs(bob, []string{
"--routerrpc.usestatusinitiated",
})
ht.EnsureConnected(alice, bob)
2022-08-05 12:22:19 +02:00
chanPoint := ht.OpenChannel(
alice, bob, lntest.OpenChannelParams{
Amt: 1000000,
PushAmt: 800000,
RemoteMaxHtlcs: uint16(maxHtlcs),
},
)
// Alice and bob should have one channel open with each other now.
2022-08-05 12:22:19 +02:00
ht.AssertNodeNumChannels(alice, 1)
ht.AssertNodeNumChannels(bob, 1)
// 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.
subscriptions := make([]*holdSubscription, maxHtlcs)
for i := 0; i < maxHtlcs; i++ {
2022-08-05 12:22:19 +02:00
subscriptions[i] = acceptHoldInvoice(ht, i, bob, alice)
}
2022-08-05 12:22:19 +02:00
ht.AssertNumActiveHtlcs(alice, maxHtlcs)
ht.AssertNumActiveHtlcs(bob, maxHtlcs)
// Now we send a payment from Alice -> Bob to sanity check that our
// commitment limit is not applied in the opposite direction.
2022-08-05 12:22:19 +02:00
aliceBobSub := acceptHoldInvoice(ht, maxHtlcs, alice, bob)
ht.AssertNumActiveHtlcs(alice, maxHtlcs+1)
ht.AssertNumActiveHtlcs(bob, maxHtlcs+1)
// 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
// out dramatically with pathfinding.
2022-08-05 12:22:19 +02:00
sendReq := &routerrpc.SendPaymentRequest{
Amt: 1000,
Dest: alice.PubKey[:],
TimeoutSeconds: 60,
FeeLimitSat: 1000000,
MaxParts: 10,
Amp: true,
}
ht.SendPaymentAndAssertStatus(bob, sendReq, lnrpc.Payment_FAILED)
// Now that we're done, we cancel all our pending htlcs so that we
// can cleanup the channel with a coop close.
for _, sub := range subscriptions {
2022-08-05 12:22:19 +02:00
sub.cancel(ht)
}
2022-08-05 12:22:19 +02:00
aliceBobSub.cancel(ht)
2022-08-05 12:22:19 +02:00
ht.AssertNumActiveHtlcs(alice, 0)
ht.AssertNumActiveHtlcs(bob, 0)
2022-08-05 12:22:19 +02:00
ht.CloseChannel(alice, chanPoint)
}
type holdSubscription struct {
2022-08-05 12:22:19 +02:00
recipient *node.HarnessNode
hash lntypes.Hash
invSubscription invoicesrpc.Invoices_SubscribeSingleInvoiceClient
paymentSubscription routerrpc.Router_SendPaymentV2Client
}
// cancel updates a hold invoice to cancel from the recipient and consumes
// updates from the payer until it has reached a final, failed state.
func (h *holdSubscription) cancel(ht *lntest.HarnessTest) {
2022-08-05 12:22:19 +02:00
h.recipient.RPC.CancelInvoice(h.hash[:])
invUpdate := ht.ReceiveSingleInvoice(h.invSubscription)
require.Equal(ht, lnrpc.Invoice_CANCELED, invUpdate.State,
"expected invoice canceled")
// We expect one in flight update when our htlc is canceled back, and
// another when we fail the payment as a whole.
2022-08-05 12:22:19 +02:00
payUpdate := ht.AssertPaymentStatusFromStream(
h.paymentSubscription, lnrpc.Payment_IN_FLIGHT,
)
require.Len(ht, payUpdate.Htlcs, 1)
payUpdate = ht.AssertPaymentStatusFromStream(
h.paymentSubscription, lnrpc.Payment_FAILED,
)
require.Equal(ht, lnrpc.Payment_FAILED, payUpdate.Status,
"expected payment failed")
2022-08-05 12:22:19 +02:00
require.Equal(ht, lnrpc.PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS, //nolint:lll
payUpdate.FailureReason, "expected unknown details")
}
// 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
// are locked in for the payment.
func acceptHoldInvoice(ht *lntest.HarnessTest, idx int, sender,
2022-08-05 12:22:19 +02:00
receiver *node.HarnessNode) *holdSubscription {
hash := [lntypes.HashSize]byte{byte(idx + 1)}
2022-08-05 12:22:19 +02:00
req := &invoicesrpc.AddHoldInvoiceRequest{
ValueMsat: 10000,
Hash: hash[:],
}
invoice := receiver.RPC.AddHoldInvoice(req)
2022-08-05 12:22:19 +02:00
invStream := receiver.RPC.SubscribeSingleInvoice(hash[:])
ht.AssertInvoiceState(invStream, lnrpc.Invoice_OPEN)
2022-08-05 12:22:19 +02:00
sendReq := &routerrpc.SendPaymentRequest{
PaymentRequest: invoice.PaymentRequest,
TimeoutSeconds: 60,
FeeLimitSat: 1000000,
}
payStream := sender.RPC.SendPayment(sendReq)
// Finally, assert that we progress to an accepted state. We expect
// the payer to get one update for the creation of the payment, and
// another when a htlc is dispatched.
2022-08-05 12:22:19 +02:00
payment := ht.AssertPaymentStatusFromStream(
payStream, lnrpc.Payment_INITIATED,
2022-08-05 12:22:19 +02:00
)
require.Empty(ht, payment.Htlcs)
2022-08-05 12:22:19 +02:00
payment = ht.AssertPaymentStatusFromStream(
payStream, lnrpc.Payment_IN_FLIGHT,
)
require.Len(ht, payment.Htlcs, 1)
ht.AssertInvoiceState(invStream, lnrpc.Invoice_ACCEPTED)
return &holdSubscription{
2022-08-05 12:22:19 +02:00
recipient: receiver,
hash: hash,
invSubscription: invStream,
paymentSubscription: payStream,
}
}