2021-03-25 03:53:40 +01:00
|
|
|
package itest
|
|
|
|
|
|
|
|
import (
|
2021-10-14 18:46:53 +02:00
|
|
|
"encoding/hex"
|
2021-05-06 18:17:34 +02:00
|
|
|
"testing"
|
2021-03-25 03:53:40 +01:00
|
|
|
"time"
|
|
|
|
|
2022-02-23 14:48:00 +01:00
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
2021-03-25 03:53:40 +01:00
|
|
|
"github.com/lightningnetwork/lnd/amp"
|
2021-03-31 13:26:15 +02:00
|
|
|
"github.com/lightningnetwork/lnd/chainreg"
|
2021-03-25 03:53:40 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
2021-10-14 18:46:53 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
2021-03-25 03:53:40 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
2022-08-12 11:03:44 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lntest"
|
|
|
|
"github.com/lightningnetwork/lnd/lntest/node"
|
2021-03-25 03:53:40 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
2021-05-06 18:19:40 +02:00
|
|
|
// testSendPaymentAMPInvoice tests that we can send an AMP payment to a
|
|
|
|
// specified AMP invoice using SendPaymentV2.
|
2022-08-12 11:03:44 +02:00
|
|
|
func testSendPaymentAMPInvoice(ht *lntest.HarnessTest) {
|
2022-08-10 22:10:40 +02:00
|
|
|
succeed := ht.Run("native payaddr", func(t *testing.T) {
|
|
|
|
tt := ht.Subtest(t)
|
|
|
|
testSendPaymentAMPInvoiceCase(tt, false)
|
2021-05-27 03:10:47 +02:00
|
|
|
})
|
2022-08-10 22:10:40 +02:00
|
|
|
|
|
|
|
// Abort the test if failed.
|
|
|
|
if !succeed {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ht.Run("external payaddr", func(t *testing.T) {
|
|
|
|
tt := ht.Subtest(t)
|
|
|
|
testSendPaymentAMPInvoiceCase(tt, true)
|
2021-05-27 03:10:47 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-08-12 11:03:44 +02:00
|
|
|
func testSendPaymentAMPInvoiceCase(ht *lntest.HarnessTest,
|
2021-05-27 03:10:47 +02:00
|
|
|
useExternalPayAddr bool) {
|
|
|
|
|
2022-08-10 22:10:40 +02:00
|
|
|
mts := newMppTestScenario(ht)
|
2021-05-06 18:19:40 +02:00
|
|
|
|
2021-09-14 15:01:28 +02:00
|
|
|
// Subscribe to bob's invoices. Do this early in the test to make sure
|
|
|
|
// that the subscription has actually been completed when we add an
|
|
|
|
// invoice. Otherwise the notification will be missed.
|
|
|
|
req := &lnrpc.InvoiceSubscription{}
|
2022-08-10 22:10:40 +02:00
|
|
|
bobInvoiceSubscription := mts.bob.RPC.SubscribeInvoices(req)
|
2021-09-14 15:01:28 +02:00
|
|
|
|
2021-05-06 18:19:40 +02:00
|
|
|
const paymentAmt = btcutil.Amount(300000)
|
|
|
|
|
|
|
|
// Set up a network with three different paths Alice <-> Bob. Channel
|
|
|
|
// capacities are set such that the payment can only succeed if (at
|
|
|
|
// least) three paths are used.
|
|
|
|
//
|
|
|
|
// _ Eve _
|
|
|
|
// / \
|
|
|
|
// Alice -- Carol ---- Bob
|
|
|
|
// \ /
|
|
|
|
// \__ Dave ____/
|
|
|
|
//
|
2022-08-10 22:10:40 +02:00
|
|
|
mppReq := &mppOpenChannelRequest{
|
2023-11-05 11:33:22 +01:00
|
|
|
amtAliceCarol: 285000,
|
|
|
|
amtAliceDave: 155000,
|
|
|
|
amtCarolBob: 200000,
|
|
|
|
amtCarolEve: 155000,
|
|
|
|
amtDaveBob: 155000,
|
|
|
|
amtEveBob: 155000,
|
2022-08-10 22:10:40 +02:00
|
|
|
}
|
|
|
|
mts.openChannels(mppReq)
|
|
|
|
chanPointAliceDave := mts.channelPoints[1]
|
|
|
|
chanPointDaveBob := mts.channelPoints[4]
|
2021-05-06 18:19:40 +02:00
|
|
|
|
2022-08-10 22:10:40 +02:00
|
|
|
invoice := &lnrpc.Invoice{
|
2021-05-06 18:19:40 +02:00
|
|
|
Value: int64(paymentAmt),
|
|
|
|
IsAmp: true,
|
2022-08-10 22:10:40 +02:00
|
|
|
}
|
|
|
|
addInvoiceResp := mts.bob.RPC.AddInvoice(invoice)
|
2021-05-06 18:19:40 +02:00
|
|
|
|
|
|
|
// Ensure we get a notification of the invoice being added by Bob.
|
2022-08-10 22:10:40 +02:00
|
|
|
rpcInvoice := ht.ReceiveInvoiceUpdate(bobInvoiceSubscription)
|
2021-05-06 18:19:40 +02:00
|
|
|
|
2022-10-25 18:55:26 +02:00
|
|
|
require.False(ht, rpcInvoice.Settled)
|
2022-08-10 22:10:40 +02:00
|
|
|
require.Equal(ht, lnrpc.Invoice_OPEN, rpcInvoice.State)
|
|
|
|
require.Equal(ht, int64(0), rpcInvoice.AmtPaidSat)
|
|
|
|
require.Equal(ht, int64(0), rpcInvoice.AmtPaidMsat)
|
|
|
|
require.Equal(ht, 0, len(rpcInvoice.Htlcs))
|
2021-05-06 18:19:40 +02:00
|
|
|
|
|
|
|
// Increase Dave's fee to make the test deterministic. Otherwise it
|
|
|
|
// would be unpredictable whether pathfinding would go through Charlie
|
|
|
|
// or Dave for the first shard.
|
2024-02-20 14:30:28 +01:00
|
|
|
expectedPolicy := &lnrpc.RoutingPolicy{
|
|
|
|
FeeBaseMsat: 500_000,
|
|
|
|
FeeRateMilliMsat: int64(0.001 * 1_000_000),
|
|
|
|
TimeLockDelta: 40,
|
|
|
|
MinHtlc: 1000, // default value
|
|
|
|
MaxHtlcMsat: 133_650_000,
|
|
|
|
}
|
|
|
|
mts.dave.UpdateGlobalPolicy(expectedPolicy)
|
2022-08-10 22:10:40 +02:00
|
|
|
|
|
|
|
// Make sure Alice has heard it for both Dave's channels.
|
|
|
|
ht.AssertChannelPolicyUpdate(
|
|
|
|
mts.alice, mts.dave, expectedPolicy, chanPointAliceDave, false,
|
|
|
|
)
|
|
|
|
ht.AssertChannelPolicyUpdate(
|
|
|
|
mts.alice, mts.dave, expectedPolicy, chanPointDaveBob, false,
|
2021-05-06 18:19:40 +02:00
|
|
|
)
|
|
|
|
|
2021-05-27 03:10:47 +02:00
|
|
|
// Generate an external payment address when attempting to pseudo-reuse
|
|
|
|
// an AMP invoice. When using an external payment address, we'll also
|
|
|
|
// expect an extra invoice to appear in the ListInvoices response, since
|
|
|
|
// a new invoice will be JIT inserted under a different payment address
|
|
|
|
// than the one in the invoice.
|
2024-02-03 13:09:11 +01:00
|
|
|
//
|
|
|
|
// NOTE: This will only work when the peer has spontaneous AMP payments
|
|
|
|
// enabled otherwise no invoice under a different payment_addr will be
|
|
|
|
// found.
|
2021-05-27 03:10:47 +02:00
|
|
|
var (
|
|
|
|
expNumInvoices = 1
|
|
|
|
externalPayAddr []byte
|
|
|
|
)
|
|
|
|
if useExternalPayAddr {
|
|
|
|
expNumInvoices = 2
|
2022-08-10 22:10:40 +02:00
|
|
|
externalPayAddr = ht.Random32Bytes()
|
2021-05-27 03:10:47 +02:00
|
|
|
}
|
|
|
|
|
2022-08-10 22:10:40 +02:00
|
|
|
sendReq := &routerrpc.SendPaymentRequest{
|
|
|
|
PaymentRequest: addInvoiceResp.PaymentRequest,
|
|
|
|
PaymentAddr: externalPayAddr,
|
|
|
|
TimeoutSeconds: 60,
|
|
|
|
FeeLimitMsat: noFeeLimitMsat,
|
|
|
|
}
|
|
|
|
payment := ht.SendPaymentAssertSettled(mts.alice, sendReq)
|
2021-05-06 18:19:40 +02:00
|
|
|
|
|
|
|
// Check that Alice split the payment in at least three shards. Because
|
|
|
|
// the hand-off of the htlc to the link is asynchronous (via a mailbox),
|
|
|
|
// there is some non-determinism in the process. Depending on whether
|
|
|
|
// the new pathfinding round is started before or after the htlc is
|
|
|
|
// locked into the channel, different sharding may occur. Therefore we
|
|
|
|
// can only check if the number of shards isn't below the theoretical
|
|
|
|
// minimum.
|
|
|
|
succeeded := 0
|
|
|
|
for _, htlc := range payment.Htlcs {
|
|
|
|
if htlc.Status == lnrpc.HTLCAttempt_SUCCEEDED {
|
|
|
|
succeeded++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const minExpectedShards = 3
|
2022-08-10 22:10:40 +02:00
|
|
|
require.GreaterOrEqual(ht, succeeded, minExpectedShards,
|
|
|
|
"expected num of shards not reached")
|
2021-05-06 18:19:40 +02:00
|
|
|
|
2021-05-27 03:10:47 +02:00
|
|
|
// When an external payment address is supplied, we'll get an extra
|
|
|
|
// notification for the JIT inserted invoice, since it differs from the
|
|
|
|
// original.
|
|
|
|
if useExternalPayAddr {
|
2022-08-10 22:10:40 +02:00
|
|
|
ht.ReceiveInvoiceUpdate(bobInvoiceSubscription)
|
2021-05-27 03:10:47 +02:00
|
|
|
}
|
|
|
|
|
2021-05-06 18:19:40 +02:00
|
|
|
// There should now be a settle event for the invoice.
|
2022-08-10 22:10:40 +02:00
|
|
|
rpcInvoice = ht.ReceiveInvoiceUpdate(bobInvoiceSubscription)
|
2021-05-06 18:19:40 +02:00
|
|
|
|
|
|
|
// Also fetch Bob's invoice from ListInvoices and assert it is equal to
|
2022-01-13 17:29:43 +01:00
|
|
|
// the one received via the subscription.
|
2022-08-10 22:10:40 +02:00
|
|
|
invoices := ht.AssertNumInvoices(mts.bob, expNumInvoices)
|
2022-08-10 23:06:35 +02:00
|
|
|
ht.AssertInvoiceEqual(rpcInvoice, invoices[expNumInvoices-1])
|
2021-05-06 18:19:40 +02:00
|
|
|
|
|
|
|
// Assert that the invoice is settled for the total payment amount and
|
|
|
|
// has the correct payment address.
|
2022-10-25 18:55:26 +02:00
|
|
|
require.True(ht, rpcInvoice.Settled)
|
2022-08-10 22:10:40 +02:00
|
|
|
require.Equal(ht, lnrpc.Invoice_SETTLED, rpcInvoice.State)
|
|
|
|
require.Equal(ht, int64(paymentAmt), rpcInvoice.AmtPaidSat)
|
|
|
|
require.Equal(ht, int64(paymentAmt*1000), rpcInvoice.AmtPaidMsat)
|
2021-05-06 18:19:40 +02:00
|
|
|
|
|
|
|
// Finally, assert that the same set id is recorded for each htlc, and
|
|
|
|
// that the preimage hash pair is valid.
|
|
|
|
var setID []byte
|
2022-08-10 22:10:40 +02:00
|
|
|
require.Equal(ht, succeeded, len(rpcInvoice.Htlcs))
|
2021-05-06 18:19:40 +02:00
|
|
|
for _, htlc := range rpcInvoice.Htlcs {
|
2022-08-10 22:10:40 +02:00
|
|
|
require.NotNil(ht, htlc.Amp)
|
2021-05-06 18:19:40 +02:00
|
|
|
if setID == nil {
|
|
|
|
setID = make([]byte, 32)
|
|
|
|
copy(setID, htlc.Amp.SetId)
|
|
|
|
}
|
2022-08-10 22:10:40 +02:00
|
|
|
require.Equal(ht, setID, htlc.Amp.SetId)
|
2021-05-06 18:19:40 +02:00
|
|
|
|
|
|
|
// Parse the child hash and child preimage, and assert they are
|
|
|
|
// well-formed.
|
|
|
|
childHash, err := lntypes.MakeHash(htlc.Amp.Hash)
|
2022-08-10 22:10:40 +02:00
|
|
|
require.NoError(ht, err)
|
2021-05-06 18:19:40 +02:00
|
|
|
childPreimage, err := lntypes.MakePreimage(htlc.Amp.Preimage)
|
2022-08-10 22:10:40 +02:00
|
|
|
require.NoError(ht, err)
|
2021-05-06 18:19:40 +02:00
|
|
|
|
|
|
|
// Assert that the preimage actually matches the hashes.
|
|
|
|
validPreimage := childPreimage.Matches(childHash)
|
2022-08-10 22:10:40 +02:00
|
|
|
require.True(ht, validPreimage)
|
2021-05-06 18:19:40 +02:00
|
|
|
}
|
2021-05-27 03:10:47 +02:00
|
|
|
|
2021-10-14 18:46:53 +02:00
|
|
|
// The set ID we extract above should be shown in the final settled
|
|
|
|
// state.
|
|
|
|
ampState := rpcInvoice.AmpInvoiceState[hex.EncodeToString(setID)]
|
2022-08-10 22:10:40 +02:00
|
|
|
require.Equal(ht, lnrpc.InvoiceHTLCState_SETTLED, ampState.State)
|
|
|
|
|
|
|
|
// Finally, close all channels.
|
|
|
|
mts.closeChannels()
|
2021-10-14 18:46:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// testSendPaymentAMPInvoiceRepeat tests that it's possible to pay an AMP
|
|
|
|
// invoice multiple times by having the client generate a new setID each time.
|
2022-08-12 11:03:44 +02:00
|
|
|
func testSendPaymentAMPInvoiceRepeat(ht *lntest.HarnessTest) {
|
2021-10-14 18:46:53 +02:00
|
|
|
// In this basic test, we'll only need two nodes as we want to
|
|
|
|
// primarily test the recurring payment feature. So we'll re-use the
|
2022-08-10 21:02:44 +02:00
|
|
|
carol := ht.NewNode("Carol", nil)
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
// Send Carol enough coins to be able to open a channel to Dave.
|
2022-08-10 21:02:44 +02:00
|
|
|
ht.FundCoins(btcutil.SatoshiPerBitcoin, carol)
|
2021-10-14 18:46:53 +02:00
|
|
|
|
2022-08-10 21:02:44 +02:00
|
|
|
dave := ht.NewNode("Dave", nil)
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
// Set up an invoice subscription so we can be notified when Dave
|
|
|
|
// receives his repeated payments.
|
|
|
|
req := &lnrpc.InvoiceSubscription{}
|
2022-08-10 21:02:44 +02:00
|
|
|
invSubscription := dave.RPC.SubscribeInvoices(req)
|
|
|
|
|
|
|
|
// Before we start the test, we'll ensure both sides are connected to
|
|
|
|
// the funding flow can properly be executed.
|
|
|
|
ht.EnsureConnected(carol, dave)
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
// Establish a channel between Carol and Dave.
|
|
|
|
chanAmt := btcutil.Amount(100_000)
|
2022-08-10 21:02:44 +02:00
|
|
|
ht.OpenChannel(
|
2022-08-12 11:03:44 +02:00
|
|
|
carol, dave, lntest.OpenChannelParams{Amt: chanAmt},
|
2021-10-14 18:46:53 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Create an AMP invoice of a trivial amount, that we'll pay repeatedly
|
|
|
|
// in this integration test.
|
|
|
|
paymentAmt := 10000
|
2022-08-10 21:02:44 +02:00
|
|
|
invoice := &lnrpc.Invoice{
|
2021-10-14 18:46:53 +02:00
|
|
|
Value: int64(paymentAmt),
|
|
|
|
IsAmp: true,
|
2022-08-10 21:02:44 +02:00
|
|
|
}
|
|
|
|
addInvoiceResp := dave.RPC.AddInvoice(invoice)
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
// We should get an initial notification that the HTLC has been added.
|
2022-08-10 21:02:44 +02:00
|
|
|
rpcInvoice := ht.ReceiveInvoiceUpdate(invSubscription)
|
2022-10-25 18:55:26 +02:00
|
|
|
require.False(ht, rpcInvoice.Settled)
|
2022-08-10 21:02:44 +02:00
|
|
|
require.Equal(ht, lnrpc.Invoice_OPEN, rpcInvoice.State)
|
|
|
|
require.Equal(ht, int64(0), rpcInvoice.AmtPaidSat)
|
|
|
|
require.Equal(ht, int64(0), rpcInvoice.AmtPaidMsat)
|
|
|
|
require.Equal(ht, 0, len(rpcInvoice.Htlcs))
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
// Now we'll use Carol to pay the invoice that Dave created.
|
2022-08-10 21:02:44 +02:00
|
|
|
ht.CompletePaymentRequests(
|
|
|
|
carol, []string{addInvoiceResp.PaymentRequest},
|
2021-10-14 18:46:53 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Dave should get a notification that the invoice has been settled.
|
2022-08-10 21:02:44 +02:00
|
|
|
invoiceNtfn := ht.ReceiveInvoiceUpdate(invSubscription)
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
// The notification should signal that the invoice is now settled, and
|
|
|
|
// should also include the set ID, and show the proper amount paid.
|
2022-10-25 18:55:26 +02:00
|
|
|
require.True(ht, invoiceNtfn.Settled)
|
2022-08-10 21:02:44 +02:00
|
|
|
require.Equal(ht, lnrpc.Invoice_SETTLED, invoiceNtfn.State)
|
|
|
|
require.Equal(ht, paymentAmt, int(invoiceNtfn.AmtPaidSat))
|
|
|
|
require.Equal(ht, 1, len(invoiceNtfn.AmpInvoiceState))
|
2021-10-14 18:46:53 +02:00
|
|
|
var firstSetID []byte
|
|
|
|
for setIDStr, ampState := range invoiceNtfn.AmpInvoiceState {
|
|
|
|
firstSetID, _ = hex.DecodeString(setIDStr)
|
2022-08-10 21:02:44 +02:00
|
|
|
require.Equal(ht, lnrpc.InvoiceHTLCState_SETTLED,
|
|
|
|
ampState.State)
|
2021-10-14 18:46:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Pay the invoice again, we should get another notification that Dave
|
|
|
|
// has received another payment.
|
2022-08-10 21:02:44 +02:00
|
|
|
ht.CompletePaymentRequests(
|
|
|
|
carol, []string{addInvoiceResp.PaymentRequest},
|
2021-10-14 18:46:53 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Dave should get another notification.
|
2022-08-10 21:02:44 +02:00
|
|
|
invoiceNtfn = ht.ReceiveInvoiceUpdate(invSubscription)
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
// The invoice should still be shown as settled, and also include the
|
|
|
|
// information about this newly generated setID, showing 2x the amount
|
|
|
|
// paid.
|
2022-10-25 18:55:26 +02:00
|
|
|
require.True(ht, invoiceNtfn.Settled)
|
2022-08-10 21:02:44 +02:00
|
|
|
require.Equal(ht, paymentAmt*2, int(invoiceNtfn.AmtPaidSat))
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
var secondSetID []byte
|
|
|
|
for setIDStr, ampState := range invoiceNtfn.AmpInvoiceState {
|
|
|
|
secondSetID, _ = hex.DecodeString(setIDStr)
|
2022-08-10 21:02:44 +02:00
|
|
|
require.Equal(ht, lnrpc.InvoiceHTLCState_SETTLED,
|
|
|
|
ampState.State)
|
2021-10-14 18:46:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// The returned invoice should only include a single HTLC since we
|
|
|
|
// return the "projected" sub-invoice for a given setID.
|
2022-08-10 21:02:44 +02:00
|
|
|
require.Equal(ht, 1, len(invoiceNtfn.Htlcs))
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
// However the AMP state index should show that there've been two
|
|
|
|
// repeated payments to this invoice so far.
|
2022-08-10 21:02:44 +02:00
|
|
|
require.Equal(ht, 2, len(invoiceNtfn.AmpInvoiceState))
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
// Now we'll look up the invoice using the new LookupInvoice2 RPC call
|
|
|
|
// by the set ID of each of the invoices.
|
2022-08-10 21:02:44 +02:00
|
|
|
msg := &invoicesrpc.LookupInvoiceMsg{
|
2021-10-14 18:46:53 +02:00
|
|
|
InvoiceRef: &invoicesrpc.LookupInvoiceMsg_SetId{
|
|
|
|
SetId: firstSetID,
|
|
|
|
},
|
|
|
|
LookupModifier: invoicesrpc.LookupModifier_HTLC_SET_ONLY,
|
2022-08-10 21:02:44 +02:00
|
|
|
}
|
|
|
|
subInvoice1 := dave.RPC.LookupInvoiceV2(msg)
|
|
|
|
msg = &invoicesrpc.LookupInvoiceMsg{
|
2021-10-14 18:46:53 +02:00
|
|
|
InvoiceRef: &invoicesrpc.LookupInvoiceMsg_SetId{
|
|
|
|
SetId: secondSetID,
|
|
|
|
},
|
|
|
|
LookupModifier: invoicesrpc.LookupModifier_HTLC_SET_ONLY,
|
2022-08-10 21:02:44 +02:00
|
|
|
}
|
|
|
|
subInvoice2 := dave.RPC.LookupInvoiceV2(msg)
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
// Each invoice should only show a single HTLC present, as we passed
|
|
|
|
// the HTLC set only modifier.
|
2022-08-10 21:02:44 +02:00
|
|
|
require.Equal(ht, 1, len(subInvoice1.Htlcs))
|
|
|
|
require.Equal(ht, 1, len(subInvoice2.Htlcs))
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
// If we look up the same invoice, by its payment address, but now with
|
|
|
|
// the HTLC blank modifier, then none of them should be returned.
|
2022-08-10 21:02:44 +02:00
|
|
|
msg = &invoicesrpc.LookupInvoiceMsg{
|
2021-10-14 18:46:53 +02:00
|
|
|
InvoiceRef: &invoicesrpc.LookupInvoiceMsg_PaymentAddr{
|
|
|
|
PaymentAddr: addInvoiceResp.PaymentAddr,
|
|
|
|
},
|
|
|
|
LookupModifier: invoicesrpc.LookupModifier_HTLC_SET_BLANK,
|
2022-08-10 21:02:44 +02:00
|
|
|
}
|
|
|
|
rootInvoice := dave.RPC.LookupInvoiceV2(msg)
|
|
|
|
require.Equal(ht, 0, len(rootInvoice.Htlcs))
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
// If we look up the same invoice, by its payment address, but without
|
|
|
|
// that modified, then we should get all the relevant HTLCs.
|
2022-08-10 21:02:44 +02:00
|
|
|
msg = &invoicesrpc.LookupInvoiceMsg{
|
|
|
|
InvoiceRef: &invoicesrpc.LookupInvoiceMsg_PaymentAddr{
|
|
|
|
PaymentAddr: addInvoiceResp.PaymentAddr,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
rootInvoice = dave.RPC.LookupInvoiceV2(msg)
|
|
|
|
require.Equal(ht, 2, len(rootInvoice.Htlcs))
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
// Finally, we'll test that if we subscribe for notifications of
|
|
|
|
// settled invoices, we get a backlog, which includes the invoice we
|
|
|
|
// settled last (since you can only fetch from index 1 onwards), and
|
|
|
|
// only the relevant set of HTLCs.
|
|
|
|
req = &lnrpc.InvoiceSubscription{
|
|
|
|
SettleIndex: 1,
|
|
|
|
}
|
2022-08-10 21:02:44 +02:00
|
|
|
invSub2 := dave.RPC.SubscribeInvoices(req)
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
// The first invoice we get back should match the state of the invoice
|
|
|
|
// after our second payment: amt updated, but only a single HTLC shown
|
|
|
|
// through.
|
2022-08-10 21:02:44 +02:00
|
|
|
backlogInv := ht.ReceiveInvoiceUpdate(invSub2)
|
|
|
|
require.Equal(ht, 1, len(backlogInv.Htlcs))
|
|
|
|
require.Equal(ht, 2, len(backlogInv.AmpInvoiceState))
|
2022-10-25 18:55:26 +02:00
|
|
|
require.True(ht, backlogInv.Settled)
|
2022-08-10 21:02:44 +02:00
|
|
|
require.Equal(ht, paymentAmt*2, int(backlogInv.AmtPaidSat))
|
2021-05-06 18:19:40 +02:00
|
|
|
}
|
|
|
|
|
2021-03-31 13:26:15 +02:00
|
|
|
// testSendPaymentAMP tests that we can send an AMP payment to a specified
|
|
|
|
// destination using SendPaymentV2.
|
2022-08-12 11:03:44 +02:00
|
|
|
func testSendPaymentAMP(ht *lntest.HarnessTest) {
|
2022-08-10 22:38:11 +02:00
|
|
|
mts := newMppTestScenario(ht)
|
2021-03-31 13:26:15 +02:00
|
|
|
const paymentAmt = btcutil.Amount(300000)
|
|
|
|
|
|
|
|
// Set up a network with three different paths Alice <-> Bob. Channel
|
|
|
|
// capacities are set such that the payment can only succeed if (at
|
|
|
|
// least) three paths are used.
|
|
|
|
//
|
|
|
|
// _ Eve _
|
|
|
|
// / \
|
|
|
|
// Alice -- Carol ---- Bob
|
|
|
|
// \ /
|
|
|
|
// \__ Dave ____/
|
|
|
|
//
|
2022-08-10 22:38:11 +02:00
|
|
|
mppReq := &mppOpenChannelRequest{
|
2023-11-05 11:33:22 +01:00
|
|
|
amtAliceCarol: 285000,
|
|
|
|
amtAliceDave: 155000,
|
|
|
|
amtCarolBob: 200000,
|
|
|
|
amtCarolEve: 155000,
|
|
|
|
amtDaveBob: 155000,
|
|
|
|
amtEveBob: 155000,
|
2022-08-10 22:38:11 +02:00
|
|
|
}
|
|
|
|
mts.openChannels(mppReq)
|
|
|
|
chanPointAliceDave := mts.channelPoints[1]
|
2021-03-31 13:26:15 +02:00
|
|
|
|
2024-02-20 14:30:28 +01:00
|
|
|
// Increase Dave's fee to make the test deterministic. Otherwise, it
|
2021-03-31 13:26:15 +02:00
|
|
|
// would be unpredictable whether pathfinding would go through Charlie
|
|
|
|
// or Dave for the first shard.
|
2024-02-20 14:30:28 +01:00
|
|
|
expectedPolicy := &lnrpc.RoutingPolicy{
|
|
|
|
FeeBaseMsat: 500_000,
|
|
|
|
FeeRateMilliMsat: int64(0.001 * 1_000_000),
|
|
|
|
TimeLockDelta: 40,
|
|
|
|
MinHtlc: 1000, // default value
|
|
|
|
MaxHtlcMsat: 133_650_000,
|
|
|
|
}
|
|
|
|
mts.dave.UpdateGlobalPolicy(expectedPolicy)
|
2021-03-31 13:26:15 +02:00
|
|
|
|
2022-08-10 22:38:11 +02:00
|
|
|
// Make sure Alice has heard it.
|
|
|
|
ht.AssertChannelPolicyUpdate(
|
|
|
|
mts.alice, mts.dave, expectedPolicy, chanPointAliceDave, false,
|
2021-03-31 13:26:15 +02:00
|
|
|
)
|
|
|
|
|
2022-08-10 22:38:11 +02:00
|
|
|
sendReq := &routerrpc.SendPaymentRequest{
|
|
|
|
Dest: mts.bob.PubKey[:],
|
|
|
|
Amt: int64(paymentAmt),
|
|
|
|
FinalCltvDelta: chainreg.DefaultBitcoinTimeLockDelta,
|
|
|
|
TimeoutSeconds: 60,
|
|
|
|
FeeLimitMsat: noFeeLimitMsat,
|
|
|
|
Amp: true,
|
|
|
|
}
|
|
|
|
payment := ht.SendPaymentAssertSettled(mts.alice, sendReq)
|
|
|
|
|
2021-03-31 13:26:15 +02:00
|
|
|
// Check that Alice split the payment in at least three shards. Because
|
|
|
|
// the hand-off of the htlc to the link is asynchronous (via a mailbox),
|
|
|
|
// there is some non-determinism in the process. Depending on whether
|
|
|
|
// the new pathfinding round is started before or after the htlc is
|
|
|
|
// locked into the channel, different sharding may occur. Therefore we
|
|
|
|
// can only check if the number of shards isn't below the theoretical
|
|
|
|
// minimum.
|
|
|
|
succeeded := 0
|
|
|
|
for _, htlc := range payment.Htlcs {
|
|
|
|
if htlc.Status == lnrpc.HTLCAttempt_SUCCEEDED {
|
|
|
|
succeeded++
|
|
|
|
}
|
2024-01-26 21:21:43 +01:00
|
|
|
|
|
|
|
// When an AMP record is expected, it will only be seen on the
|
|
|
|
// last hop of a route. So we make sure it isn't set on any of
|
|
|
|
// the hops except the last one
|
|
|
|
for _, hop := range htlc.Route.Hops[:len(htlc.Route.Hops)-1] {
|
|
|
|
require.Nil(ht, hop.AmpRecord)
|
|
|
|
}
|
|
|
|
|
|
|
|
lastHop := htlc.Route.Hops[len(htlc.Route.Hops)-1]
|
|
|
|
require.NotNil(ht, lastHop.AmpRecord)
|
2021-03-31 13:26:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const minExpectedShards = 3
|
2022-08-10 22:38:11 +02:00
|
|
|
require.GreaterOrEqual(ht, succeeded, minExpectedShards,
|
|
|
|
"expected num of shards not reached")
|
2021-03-31 13:26:15 +02:00
|
|
|
|
2022-08-10 22:38:11 +02:00
|
|
|
// Fetch Bob's invoices. There should only be one invoice.
|
|
|
|
invoices := ht.AssertNumInvoices(mts.bob, 1)
|
|
|
|
rpcInvoice := invoices[0]
|
2021-03-31 13:26:15 +02:00
|
|
|
|
|
|
|
// Assert that the invoice is settled for the total payment amount and
|
|
|
|
// has the correct payment address.
|
2022-10-25 18:55:26 +02:00
|
|
|
require.True(ht, rpcInvoice.Settled)
|
2022-08-10 22:38:11 +02:00
|
|
|
require.Equal(ht, lnrpc.Invoice_SETTLED, rpcInvoice.State)
|
|
|
|
require.Equal(ht, int64(paymentAmt), rpcInvoice.AmtPaidSat)
|
|
|
|
require.Equal(ht, int64(paymentAmt*1000), rpcInvoice.AmtPaidMsat)
|
2021-03-31 13:26:15 +02:00
|
|
|
|
|
|
|
// Finally, assert that the same set id is recorded for each htlc, and
|
|
|
|
// that the preimage hash pair is valid.
|
|
|
|
var setID []byte
|
2022-08-10 22:38:11 +02:00
|
|
|
require.Equal(ht, succeeded, len(rpcInvoice.Htlcs))
|
2021-03-31 13:26:15 +02:00
|
|
|
for _, htlc := range rpcInvoice.Htlcs {
|
2022-08-10 22:38:11 +02:00
|
|
|
require.NotNil(ht, htlc.Amp)
|
2021-03-31 13:26:15 +02:00
|
|
|
if setID == nil {
|
|
|
|
setID = make([]byte, 32)
|
|
|
|
copy(setID, htlc.Amp.SetId)
|
|
|
|
}
|
2022-08-10 22:38:11 +02:00
|
|
|
require.Equal(ht, setID, htlc.Amp.SetId)
|
2021-03-31 13:26:15 +02:00
|
|
|
|
|
|
|
// Parse the child hash and child preimage, and assert they are
|
|
|
|
// well-formed.
|
|
|
|
childHash, err := lntypes.MakeHash(htlc.Amp.Hash)
|
2022-08-10 22:38:11 +02:00
|
|
|
require.NoError(ht, err)
|
2021-03-31 13:26:15 +02:00
|
|
|
childPreimage, err := lntypes.MakePreimage(htlc.Amp.Preimage)
|
2022-08-10 22:38:11 +02:00
|
|
|
require.NoError(ht, err)
|
2021-03-31 13:26:15 +02:00
|
|
|
|
|
|
|
// Assert that the preimage actually matches the hashes.
|
|
|
|
validPreimage := childPreimage.Matches(childHash)
|
2022-08-10 22:38:11 +02:00
|
|
|
require.True(ht, validPreimage)
|
2021-03-31 13:26:15 +02:00
|
|
|
}
|
2021-10-14 18:46:53 +02:00
|
|
|
|
|
|
|
// The set ID we extract above should be shown in the final settled
|
|
|
|
// state.
|
|
|
|
ampState := rpcInvoice.AmpInvoiceState[hex.EncodeToString(setID)]
|
2022-08-10 22:38:11 +02:00
|
|
|
require.Equal(ht, lnrpc.InvoiceHTLCState_SETTLED, ampState.State)
|
|
|
|
|
|
|
|
// Finally, close all channels.
|
|
|
|
mts.closeChannels()
|
2021-03-31 13:26:15 +02:00
|
|
|
}
|
|
|
|
|
2022-08-12 11:03:44 +02:00
|
|
|
func testSendToRouteAMP(ht *lntest.HarnessTest) {
|
2022-08-10 23:06:35 +02:00
|
|
|
mts := newMppTestScenario(ht)
|
2021-03-25 03:53:40 +01:00
|
|
|
const (
|
|
|
|
paymentAmt = btcutil.Amount(300000)
|
|
|
|
numShards = 3
|
|
|
|
shardAmt = paymentAmt / numShards
|
|
|
|
chanAmt = shardAmt * 3 / 2
|
|
|
|
)
|
|
|
|
|
2022-08-10 23:06:35 +02:00
|
|
|
// Subscribe to bob's invoices.
|
|
|
|
req := &lnrpc.InvoiceSubscription{}
|
|
|
|
bobInvoiceSubscription := mts.bob.RPC.SubscribeInvoices(req)
|
|
|
|
|
2021-03-25 03:53:40 +01:00
|
|
|
// Set up a network with three different paths Alice <-> Bob.
|
|
|
|
// _ Eve _
|
|
|
|
// / \
|
|
|
|
// Alice -- Carol ---- Bob
|
|
|
|
// \ /
|
|
|
|
// \__ Dave ____/
|
2024-02-20 14:30:28 +01:00
|
|
|
//
|
2022-08-10 23:06:35 +02:00
|
|
|
mppReq := &mppOpenChannelRequest{
|
|
|
|
// Since the channel Alice-> Carol will have to carry two
|
|
|
|
// shards, we make it larger.
|
|
|
|
amtAliceCarol: chanAmt + shardAmt,
|
|
|
|
amtAliceDave: chanAmt,
|
|
|
|
amtCarolBob: chanAmt,
|
|
|
|
amtCarolEve: chanAmt,
|
|
|
|
amtDaveBob: chanAmt,
|
|
|
|
amtEveBob: chanAmt,
|
|
|
|
}
|
|
|
|
mts.openChannels(mppReq)
|
2021-05-06 18:17:34 +02:00
|
|
|
|
2021-03-25 03:53:40 +01:00
|
|
|
// We'll send shards along three routes from Alice.
|
2022-08-10 23:06:35 +02:00
|
|
|
sendRoutes := [numShards][]*node.HarnessNode{
|
|
|
|
{mts.carol, mts.bob},
|
|
|
|
{mts.dave, mts.bob},
|
|
|
|
{mts.carol, mts.eve, mts.bob},
|
2021-03-25 03:53:40 +01:00
|
|
|
}
|
|
|
|
|
2022-08-10 23:06:35 +02:00
|
|
|
payAddr := ht.Random32Bytes()
|
|
|
|
setID := ht.Random32Bytes()
|
2021-03-25 03:53:40 +01:00
|
|
|
|
|
|
|
var sharer amp.Sharer
|
2022-08-10 23:06:35 +02:00
|
|
|
sharer, err := amp.NewSeedSharer()
|
|
|
|
require.NoError(ht, err)
|
2021-03-25 03:53:40 +01:00
|
|
|
|
|
|
|
childPreimages := make(map[lntypes.Preimage]uint32)
|
|
|
|
responses := make(chan *lnrpc.HTLCAttempt, len(sendRoutes))
|
2021-05-06 18:17:34 +02:00
|
|
|
|
|
|
|
// Define a closure for sending each of the three shards.
|
2022-08-10 23:06:35 +02:00
|
|
|
sendShard := func(i int, hops []*node.HarnessNode) {
|
2021-03-25 03:53:40 +01:00
|
|
|
// Build a route for the specified hops.
|
2022-08-10 23:06:35 +02:00
|
|
|
r := mts.buildRoute(shardAmt, mts.alice, hops)
|
2021-03-25 03:53:40 +01:00
|
|
|
|
|
|
|
// Set the MPP records to indicate this is a payment shard.
|
|
|
|
hop := r.Hops[len(r.Hops)-1]
|
|
|
|
hop.TlvPayload = true
|
|
|
|
hop.MppRecord = &lnrpc.MPPRecord{
|
|
|
|
PaymentAddr: payAddr,
|
|
|
|
TotalAmtMsat: int64(paymentAmt * 1000),
|
|
|
|
}
|
|
|
|
|
|
|
|
var child *amp.Child
|
|
|
|
if i < len(sendRoutes)-1 {
|
|
|
|
var left amp.Sharer
|
|
|
|
left, sharer, err = sharer.Split()
|
2022-08-10 23:06:35 +02:00
|
|
|
require.NoError(ht, err)
|
2021-03-25 03:53:40 +01:00
|
|
|
|
|
|
|
child = left.Child(uint32(i))
|
|
|
|
} else {
|
|
|
|
child = sharer.Child(uint32(i))
|
|
|
|
}
|
|
|
|
childPreimages[child.Preimage] = child.Index
|
|
|
|
|
|
|
|
hop.AmpRecord = &lnrpc.AMPRecord{
|
|
|
|
RootShare: child.Share[:],
|
|
|
|
SetId: setID,
|
|
|
|
ChildIndex: child.Index,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send the shard.
|
|
|
|
sendReq := &routerrpc.SendToRouteRequest{
|
|
|
|
PaymentHash: child.Hash[:],
|
|
|
|
Route: r,
|
|
|
|
}
|
|
|
|
|
2022-08-10 23:06:35 +02:00
|
|
|
// We'll send all shards in their own goroutine, since
|
|
|
|
// SendToRoute will block as long as the payment is in flight.
|
2021-03-25 03:53:40 +01:00
|
|
|
go func() {
|
2022-08-10 23:06:35 +02:00
|
|
|
resp := mts.alice.RPC.SendToRouteV2(sendReq)
|
2021-03-25 03:53:40 +01:00
|
|
|
responses <- resp
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2021-05-06 18:17:34 +02:00
|
|
|
// Send the first shard, this cause Bob to JIT add an invoice.
|
|
|
|
sendShard(0, sendRoutes[0])
|
|
|
|
|
|
|
|
// Ensure we get a notification of the invoice being added by Bob.
|
|
|
|
rpcInvoice, err := bobInvoiceSubscription.Recv()
|
2022-08-10 23:06:35 +02:00
|
|
|
require.NoError(ht, err)
|
2021-05-06 18:17:34 +02:00
|
|
|
|
2022-10-25 18:55:26 +02:00
|
|
|
require.False(ht, rpcInvoice.Settled)
|
2022-08-10 23:06:35 +02:00
|
|
|
require.Equal(ht, lnrpc.Invoice_OPEN, rpcInvoice.State)
|
|
|
|
require.Equal(ht, int64(0), rpcInvoice.AmtPaidSat)
|
|
|
|
require.Equal(ht, int64(0), rpcInvoice.AmtPaidMsat)
|
|
|
|
require.Equal(ht, payAddr, rpcInvoice.PaymentAddr)
|
2021-05-06 18:17:34 +02:00
|
|
|
|
2022-08-10 23:06:35 +02:00
|
|
|
require.Equal(ht, 0, len(rpcInvoice.Htlcs))
|
2021-05-06 18:17:34 +02:00
|
|
|
|
|
|
|
sendShard(1, sendRoutes[1])
|
|
|
|
sendShard(2, sendRoutes[2])
|
|
|
|
|
2021-03-25 03:53:40 +01:00
|
|
|
// Assert that all of the child preimages are unique.
|
2022-08-10 23:06:35 +02:00
|
|
|
require.Equal(ht, len(sendRoutes), len(childPreimages))
|
2021-03-25 03:53:40 +01:00
|
|
|
|
|
|
|
// Make a copy of the childPreimages map for validating the resulting
|
|
|
|
// invoice.
|
|
|
|
childPreimagesCopy := make(map[lntypes.Preimage]uint32)
|
|
|
|
for preimage, childIndex := range childPreimages {
|
|
|
|
childPreimagesCopy[preimage] = childIndex
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for all responses to be back, and check that they all
|
|
|
|
// succeeded.
|
2022-08-10 23:06:35 +02:00
|
|
|
timer := time.After(defaultTimeout)
|
2021-03-25 03:53:40 +01:00
|
|
|
for range sendRoutes {
|
|
|
|
var resp *lnrpc.HTLCAttempt
|
|
|
|
select {
|
|
|
|
case resp = <-responses:
|
2022-08-10 23:06:35 +02:00
|
|
|
case <-timer:
|
|
|
|
require.Fail(ht, "response not received")
|
2021-03-25 03:53:40 +01:00
|
|
|
}
|
|
|
|
|
2022-08-10 23:06:35 +02:00
|
|
|
require.Nil(ht, resp.Failure, "received payment failure")
|
2021-03-25 03:53:40 +01:00
|
|
|
|
|
|
|
preimage, err := lntypes.MakePreimage(resp.Preimage)
|
2022-08-10 23:06:35 +02:00
|
|
|
require.NoError(ht, err)
|
2021-03-25 03:53:40 +01:00
|
|
|
|
|
|
|
// Assert that the response includes one of our child preimages.
|
|
|
|
_, ok := childPreimages[preimage]
|
2022-08-10 23:06:35 +02:00
|
|
|
require.True(ht, ok)
|
2021-03-25 03:53:40 +01:00
|
|
|
|
|
|
|
// Remove this preimage from out set so that we ensure all
|
|
|
|
// responses have a unique child preimage.
|
|
|
|
delete(childPreimages, preimage)
|
|
|
|
}
|
|
|
|
childPreimages = childPreimagesCopy
|
|
|
|
|
2021-05-06 18:17:34 +02:00
|
|
|
// There should now be a settle event for the invoice.
|
|
|
|
rpcInvoice, err = bobInvoiceSubscription.Recv()
|
2022-08-10 23:06:35 +02:00
|
|
|
require.NoError(ht, err)
|
2021-05-06 18:17:34 +02:00
|
|
|
|
|
|
|
// Also fetch Bob's invoice from ListInvoices and assert it is equal to
|
2022-01-13 17:29:43 +01:00
|
|
|
// the one received via the subscription.
|
2022-08-10 23:06:35 +02:00
|
|
|
invoices := ht.AssertNumInvoices(mts.bob, 1)
|
|
|
|
ht.AssertInvoiceEqual(rpcInvoice, invoices[0])
|
2021-03-25 03:53:40 +01:00
|
|
|
|
|
|
|
// Assert that the invoice is settled for the total payment amount and
|
|
|
|
// has the correct payment address.
|
2022-10-25 18:55:26 +02:00
|
|
|
require.True(ht, rpcInvoice.Settled)
|
2022-08-10 23:06:35 +02:00
|
|
|
require.Equal(ht, lnrpc.Invoice_SETTLED, rpcInvoice.State)
|
|
|
|
require.Equal(ht, int64(paymentAmt), rpcInvoice.AmtPaidSat)
|
|
|
|
require.Equal(ht, int64(paymentAmt*1000), rpcInvoice.AmtPaidMsat)
|
|
|
|
require.Equal(ht, payAddr, rpcInvoice.PaymentAddr)
|
2021-03-25 03:53:40 +01:00
|
|
|
|
|
|
|
// Finally, assert that the proper set id is recorded for each htlc, and
|
|
|
|
// that the preimage hash pair is valid.
|
2022-08-10 23:06:35 +02:00
|
|
|
require.Equal(ht, numShards, len(rpcInvoice.Htlcs))
|
2021-03-25 03:53:40 +01:00
|
|
|
for _, htlc := range rpcInvoice.Htlcs {
|
2022-08-10 23:06:35 +02:00
|
|
|
require.NotNil(ht, htlc.Amp)
|
|
|
|
require.Equal(ht, setID, htlc.Amp.SetId)
|
2021-03-25 03:53:40 +01:00
|
|
|
|
|
|
|
// Parse the child hash and child preimage, and assert they are
|
|
|
|
// well-formed.
|
|
|
|
childHash, err := lntypes.MakeHash(htlc.Amp.Hash)
|
2022-08-10 23:06:35 +02:00
|
|
|
require.NoError(ht, err)
|
2021-03-25 03:53:40 +01:00
|
|
|
childPreimage, err := lntypes.MakePreimage(htlc.Amp.Preimage)
|
2022-08-10 23:06:35 +02:00
|
|
|
require.NoError(ht, err)
|
2021-03-25 03:53:40 +01:00
|
|
|
|
|
|
|
// Assert that the preimage actually matches the hashes.
|
|
|
|
validPreimage := childPreimage.Matches(childHash)
|
2022-08-10 23:06:35 +02:00
|
|
|
require.True(ht, validPreimage)
|
2021-03-25 03:53:40 +01:00
|
|
|
|
|
|
|
// Assert that the HTLC includes one of our child preimages.
|
|
|
|
childIndex, ok := childPreimages[childPreimage]
|
2022-08-10 23:06:35 +02:00
|
|
|
require.True(ht, ok)
|
2021-03-25 03:53:40 +01:00
|
|
|
|
|
|
|
// Assert that the correct child index is reflected.
|
2022-08-10 23:06:35 +02:00
|
|
|
require.Equal(ht, childIndex, htlc.Amp.ChildIndex)
|
2021-03-25 03:53:40 +01:00
|
|
|
|
|
|
|
// Remove this preimage from our set so that we ensure all HTLCs
|
|
|
|
// have a unique child preimage.
|
|
|
|
delete(childPreimages, childPreimage)
|
|
|
|
}
|
2021-05-06 18:17:34 +02:00
|
|
|
|
2022-08-10 23:06:35 +02:00
|
|
|
// Finally, close all channels.
|
|
|
|
mts.closeChannels()
|
2021-05-06 18:17:34 +02:00
|
|
|
}
|