2021-06-28 22:50:20 +02:00
|
|
|
package itest
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-07-23 07:37:34 +02:00
|
|
|
"encoding/hex"
|
2021-06-28 22:50:20 +02:00
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"strings"
|
|
|
|
|
2022-02-23 14:48:00 +01:00
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
2021-06-28 22:50:20 +02:00
|
|
|
"github.com/btcsuite/btcwallet/wallet"
|
|
|
|
"github.com/lightningnetwork/lnd/chainreg"
|
|
|
|
"github.com/lightningnetwork/lnd/funding"
|
2021-09-23 21:40:37 +02:00
|
|
|
"github.com/lightningnetwork/lnd/input"
|
2021-06-28 22:50:20 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lncfg"
|
|
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
2022-08-02 18:25:19 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
2021-06-28 22:50:20 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
|
|
|
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
2022-08-02 14:56:13 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lntemp"
|
2022-08-03 18:34:01 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lntemp/node"
|
2021-06-28 22:50:20 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lntest"
|
|
|
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
|
|
|
"github.com/lightningnetwork/lnd/lnwallet"
|
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
2022-08-02 15:15:49 +02:00
|
|
|
// testDisconnectingTargetPeer performs a test which disconnects Alice-peer
|
|
|
|
// from Bob-peer and then re-connects them again. We expect Alice to be able to
|
2021-06-28 22:50:20 +02:00
|
|
|
// disconnect at any point.
|
2022-08-02 15:15:49 +02:00
|
|
|
//
|
|
|
|
// TODO(yy): move to lnd_network_test.
|
|
|
|
func testDisconnectingTargetPeer(ht *lntemp.HarnessTest) {
|
2021-06-28 22:50:20 +02:00
|
|
|
// We'll start both nodes with a high backoff so that they don't
|
|
|
|
// reconnect automatically during our test.
|
|
|
|
args := []string{
|
|
|
|
"--minbackoff=1m",
|
|
|
|
"--maxbackoff=1m",
|
|
|
|
}
|
|
|
|
|
2022-08-02 15:15:49 +02:00
|
|
|
alice, bob := ht.Alice, ht.Bob
|
|
|
|
ht.RestartNodeWithExtraArgs(alice, args)
|
|
|
|
ht.RestartNodeWithExtraArgs(bob, args)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Start by connecting Alice and Bob with no channels.
|
2022-08-02 15:15:49 +02:00
|
|
|
ht.EnsureConnected(alice, bob)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
chanAmt := funding.MaxBtcFundingAmount
|
|
|
|
pushAmt := btcutil.Amount(0)
|
|
|
|
|
|
|
|
// Create a new channel that requires 1 confs before it's considered
|
|
|
|
// open, then broadcast the funding transaction
|
|
|
|
const numConfs = 1
|
2022-08-02 15:15:49 +02:00
|
|
|
p := lntemp.OpenChannelParams{
|
|
|
|
Amt: chanAmt,
|
|
|
|
PushAmt: pushAmt,
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
2022-08-02 15:15:49 +02:00
|
|
|
stream := ht.OpenChannelAssertPending(alice, bob, p)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// At this point, the channel's funding transaction will have been
|
|
|
|
// broadcast, but not confirmed. Alice and Bob's nodes should reflect
|
|
|
|
// this when queried via RPC.
|
2022-08-02 15:15:49 +02:00
|
|
|
ht.AssertNumPendingOpenChannels(alice, 1)
|
|
|
|
ht.AssertNumPendingOpenChannels(bob, 1)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-02 15:15:49 +02:00
|
|
|
// Disconnect Alice-peer from Bob-peer should have no error.
|
|
|
|
ht.DisconnectNodes(alice, bob)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Assert that the connection was torn down.
|
2022-08-02 15:15:49 +02:00
|
|
|
ht.AssertNotConnected(alice, bob)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Mine a block, then wait for Alice's node to notify us that the
|
2022-08-02 15:15:49 +02:00
|
|
|
// channel has been opened.
|
|
|
|
ht.MineBlocksAndAssertNumTxes(numConfs, 1)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-02 15:15:49 +02:00
|
|
|
// At this point, the channel should be fully opened and there should
|
|
|
|
// be no pending channels remaining for either node.
|
|
|
|
ht.AssertNumPendingOpenChannels(alice, 0)
|
|
|
|
ht.AssertNumPendingOpenChannels(bob, 0)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Reconnect the nodes so that the channel can become active.
|
2022-08-02 15:15:49 +02:00
|
|
|
ht.ConnectNodes(alice, bob)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-02 15:15:49 +02:00
|
|
|
// The channel should be listed in the peer information returned by
|
|
|
|
// both peers.
|
|
|
|
chanPoint := ht.WaitForChannelOpenEvent(stream)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Check both nodes to ensure that the channel is ready for operation.
|
2022-08-02 15:15:49 +02:00
|
|
|
ht.AssertChannelExists(alice, chanPoint)
|
|
|
|
ht.AssertChannelExists(bob, chanPoint)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-02 15:15:49 +02:00
|
|
|
// Disconnect Alice-peer from Bob-peer should have no error.
|
|
|
|
ht.DisconnectNodes(alice, bob)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Check existing connection.
|
2022-08-02 15:15:49 +02:00
|
|
|
ht.AssertNotConnected(alice, bob)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Reconnect both nodes before force closing the channel.
|
2022-08-02 15:15:49 +02:00
|
|
|
ht.ConnectNodes(alice, bob)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-02 15:15:49 +02:00
|
|
|
// Finally, immediately close the channel. This function will also
|
|
|
|
// block until the channel is closed and will additionally assert the
|
|
|
|
// relevant channel closing post conditions.
|
|
|
|
ht.ForceCloseChannel(alice, chanPoint)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-02 15:15:49 +02:00
|
|
|
// Disconnect Alice-peer from Bob-peer should have no error.
|
|
|
|
ht.DisconnectNodes(alice, bob)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2021-07-17 11:49:06 +02:00
|
|
|
// Check that the nodes not connected.
|
2022-08-02 15:15:49 +02:00
|
|
|
ht.AssertNotConnected(alice, bob)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Finally, re-connect both nodes.
|
2022-08-02 15:15:49 +02:00
|
|
|
ht.ConnectNodes(alice, bob)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Check existing connection.
|
2022-08-02 15:15:49 +02:00
|
|
|
ht.AssertConnected(alice, bob)
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
|
|
|
|
2022-08-02 18:25:19 +02:00
|
|
|
// testSphinxReplayPersistence verifies that replayed onion packets are
|
|
|
|
// rejected by a remote peer after a restart. We use a combination of unsafe
|
|
|
|
// configuration arguments to force Carol to replay the same sphinx packet
|
|
|
|
// after reconnecting to Dave, and compare the returned failure message with
|
|
|
|
// what we expect for replayed onion packets.
|
|
|
|
func testSphinxReplayPersistence(ht *lntemp.HarnessTest) {
|
|
|
|
// Open a channel with 100k satoshis between Carol and Dave with Carol
|
|
|
|
// being the sole funder of the channel.
|
2021-06-28 22:50:20 +02:00
|
|
|
chanAmt := btcutil.Amount(100000)
|
|
|
|
|
|
|
|
// First, we'll create Dave, the receiver, and start him in hodl mode.
|
2022-08-02 18:25:19 +02:00
|
|
|
dave := ht.NewNode("Dave", []string{"--hodl.exit-settle"})
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Next, we'll create Carol and establish a channel to from her to
|
|
|
|
// Dave. Carol is started in both unsafe-replay which will cause her to
|
|
|
|
// replay any pending Adds held in memory upon reconnection.
|
2022-08-02 18:25:19 +02:00
|
|
|
carol := ht.NewNode("Carol", []string{"--unsafe-replay"})
|
|
|
|
ht.FundCoins(btcutil.SatoshiPerBitcoin, carol)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-02 18:25:19 +02:00
|
|
|
ht.ConnectNodes(carol, dave)
|
|
|
|
chanPoint := ht.OpenChannel(
|
|
|
|
carol, dave, lntemp.OpenChannelParams{
|
2021-06-28 22:50:20 +02:00
|
|
|
Amt: chanAmt,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
// Next, we'll create Fred who is going to initiate the payment and
|
|
|
|
// establish a channel to from him to Carol. We can't perform this test
|
|
|
|
// by paying from Carol directly to Dave, because the '--unsafe-replay'
|
|
|
|
// setup doesn't apply to locally added htlcs. In that case, the
|
|
|
|
// mailbox, that is responsible for generating the replay, is bypassed.
|
2022-08-02 18:25:19 +02:00
|
|
|
fred := ht.NewNode("Fred", nil)
|
|
|
|
ht.FundCoins(btcutil.SatoshiPerBitcoin, fred)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-02 18:25:19 +02:00
|
|
|
ht.ConnectNodes(fred, carol)
|
|
|
|
chanPointFC := ht.OpenChannel(
|
|
|
|
fred, carol, lntemp.OpenChannelParams{
|
2021-06-28 22:50:20 +02:00
|
|
|
Amt: chanAmt,
|
|
|
|
},
|
|
|
|
)
|
2022-08-02 18:25:19 +02:00
|
|
|
defer ht.CloseChannel(fred, chanPointFC)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Now that the channel is open, create an invoice for Dave which
|
|
|
|
// expects a payment of 1000 satoshis from Carol paid via a particular
|
|
|
|
// preimage.
|
|
|
|
const paymentAmt = 1000
|
2022-08-02 18:25:19 +02:00
|
|
|
preimage := ht.Random32Bytes()
|
2021-06-28 22:50:20 +02:00
|
|
|
invoice := &lnrpc.Invoice{
|
|
|
|
Memo: "testing",
|
|
|
|
RPreimage: preimage,
|
|
|
|
Value: paymentAmt,
|
|
|
|
}
|
2022-08-02 18:25:19 +02:00
|
|
|
invoiceResp := dave.RPC.AddInvoice(invoice)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Wait for all channels to be recognized and advertized.
|
2022-08-02 18:25:19 +02:00
|
|
|
ht.AssertTopologyChannelOpen(carol, chanPoint)
|
|
|
|
ht.AssertTopologyChannelOpen(dave, chanPoint)
|
|
|
|
ht.AssertTopologyChannelOpen(carol, chanPointFC)
|
|
|
|
ht.AssertTopologyChannelOpen(fred, chanPointFC)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// With the invoice for Dave added, send a payment from Fred paying
|
|
|
|
// to the above generated invoice.
|
2022-08-02 18:25:19 +02:00
|
|
|
req := &routerrpc.SendPaymentRequest{
|
|
|
|
PaymentRequest: invoiceResp.PaymentRequest,
|
|
|
|
TimeoutSeconds: 60,
|
|
|
|
FeeLimitMsat: noFeeLimitMsat,
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
2022-08-02 18:25:19 +02:00
|
|
|
payStream := fred.RPC.SendPayment(req)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Dave's invoice should not be marked as settled.
|
2022-08-02 18:25:19 +02:00
|
|
|
msg := &invoicesrpc.LookupInvoiceMsg{
|
|
|
|
InvoiceRef: &invoicesrpc.LookupInvoiceMsg_PaymentAddr{
|
|
|
|
PaymentAddr: invoiceResp.PaymentAddr,
|
|
|
|
},
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
2022-08-02 18:25:19 +02:00
|
|
|
dbInvoice := dave.RPC.LookupInvoiceV2(msg)
|
|
|
|
require.NotEqual(ht, lnrpc.InvoiceHTLCState_SETTLED, dbInvoice.State,
|
|
|
|
"dave's invoice should not be marked as settled")
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// With the payment sent but hedl, all balance related stats should not
|
|
|
|
// have changed.
|
2022-08-02 18:25:19 +02:00
|
|
|
ht.AssertAmountPaid("carol => dave", carol, chanPoint, 0, 0)
|
|
|
|
ht.AssertAmountPaid("dave <= carol", dave, chanPoint, 0, 0)
|
|
|
|
|
|
|
|
// Before we restart Dave, make sure both Carol and Dave have added the
|
|
|
|
// HTLC.
|
|
|
|
ht.AssertNumActiveHtlcs(carol, 2)
|
|
|
|
ht.AssertNumActiveHtlcs(dave, 1)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// With the first payment sent, restart dave to make sure he is
|
|
|
|
// persisting the information required to detect replayed sphinx
|
|
|
|
// packets.
|
2022-08-02 18:25:19 +02:00
|
|
|
ht.RestartNode(dave)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Carol should retransmit the Add hedl in her mailbox on startup. Dave
|
|
|
|
// should not accept the replayed Add, and actually fail back the
|
|
|
|
// pending payment. Even though he still holds the original settle, if
|
|
|
|
// he does fail, it is almost certainly caused by the sphinx replay
|
|
|
|
// protection, as it is the only validation we do in hodl mode.
|
2022-08-02 18:25:19 +02:00
|
|
|
//
|
2021-06-28 22:50:20 +02:00
|
|
|
// Assert that Fred receives the expected failure after Carol sent a
|
|
|
|
// duplicate packet that fails due to sphinx replay detection.
|
2022-08-02 18:25:19 +02:00
|
|
|
ht.AssertPaymentStatusFromStream(payStream, lnrpc.Payment_FAILED)
|
|
|
|
ht.AssertLastHTLCError(fred, lnrpc.Failure_INVALID_ONION_KEY)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Since the payment failed, the balance should still be left
|
|
|
|
// unaltered.
|
2022-08-02 18:25:19 +02:00
|
|
|
ht.AssertAmountPaid("carol => dave", carol, chanPoint, 0, 0)
|
|
|
|
ht.AssertAmountPaid("dave <= carol", dave, chanPoint, 0, 0)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Cleanup by mining the force close and sweep transaction.
|
2022-08-02 18:25:19 +02:00
|
|
|
ht.ForceCloseChannel(carol, chanPoint)
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// testListChannels checks that the response from ListChannels is correct. It
|
|
|
|
// tests the values in all ChannelConstraints are returned as expected. Once
|
2022-04-07 22:27:26 +02:00
|
|
|
// ListChannels becomes mature, a test against all fields in ListChannels
|
|
|
|
// should be performed.
|
2022-08-02 19:00:20 +02:00
|
|
|
func testListChannels(ht *lntemp.HarnessTest) {
|
2021-06-28 22:50:20 +02:00
|
|
|
const aliceRemoteMaxHtlcs = 50
|
|
|
|
const bobRemoteMaxHtlcs = 100
|
|
|
|
|
2022-08-02 19:00:20 +02:00
|
|
|
// Get the standby nodes and open a channel between them.
|
|
|
|
alice, bob := ht.Alice, ht.Bob
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-02 19:00:20 +02:00
|
|
|
args := []string{fmt.Sprintf(
|
|
|
|
"--default-remote-max-htlcs=%v",
|
|
|
|
bobRemoteMaxHtlcs,
|
|
|
|
)}
|
|
|
|
ht.RestartNodeWithExtraArgs(bob, args)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Connect Alice to Bob.
|
2022-08-02 19:00:20 +02:00
|
|
|
ht.EnsureConnected(alice, bob)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Open a channel with 100k satoshis between Alice and Bob with Alice
|
2022-04-07 22:27:26 +02:00
|
|
|
// being the sole funder of the channel. The minial HTLC amount is set
|
|
|
|
// to 4200 msats.
|
2021-06-28 22:50:20 +02:00
|
|
|
const customizedMinHtlc = 4200
|
|
|
|
|
|
|
|
chanAmt := btcutil.Amount(100000)
|
2022-05-14 19:50:30 +02:00
|
|
|
pushAmt := btcutil.Amount(1000)
|
2022-08-02 19:00:20 +02:00
|
|
|
p := lntemp.OpenChannelParams{
|
|
|
|
Amt: chanAmt,
|
|
|
|
PushAmt: pushAmt,
|
|
|
|
MinHtlc: customizedMinHtlc,
|
|
|
|
RemoteMaxHtlcs: aliceRemoteMaxHtlcs,
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
2022-08-02 19:00:20 +02:00
|
|
|
chanPoint := ht.OpenChannel(alice, bob, p)
|
|
|
|
defer ht.CloseChannel(alice, chanPoint)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Alice should have one channel opened with Bob.
|
2022-08-02 19:00:20 +02:00
|
|
|
ht.AssertNodeNumChannels(alice, 1)
|
2021-06-28 22:50:20 +02:00
|
|
|
// Bob should have one channel opened with Alice.
|
2022-08-02 19:00:20 +02:00
|
|
|
ht.AssertNodeNumChannels(bob, 1)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Check the returned response is correct.
|
2022-08-02 19:00:20 +02:00
|
|
|
aliceChannel := ht.QueryChannelByChanPoint(alice, chanPoint)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-04-07 22:27:26 +02:00
|
|
|
// Since Alice is the initiator, she pays the commit fee.
|
2022-05-14 19:50:30 +02:00
|
|
|
aliceBalance := int64(chanAmt) - aliceChannel.CommitFee - int64(pushAmt)
|
2022-04-07 22:27:26 +02:00
|
|
|
|
|
|
|
// Check the balance related fields are correct.
|
2022-08-02 19:00:20 +02:00
|
|
|
require.Equal(ht, aliceBalance, aliceChannel.LocalBalance)
|
|
|
|
require.EqualValues(ht, pushAmt, aliceChannel.RemoteBalance)
|
|
|
|
require.EqualValues(ht, pushAmt, aliceChannel.PushAmountSat)
|
2022-04-07 22:27:26 +02:00
|
|
|
|
2021-09-23 21:40:37 +02:00
|
|
|
// Calculate the dust limit we'll use for the test.
|
|
|
|
dustLimit := lnwallet.DustLimitForSize(input.UnknownWitnessSize)
|
|
|
|
|
2022-04-07 22:27:26 +02:00
|
|
|
// defaultConstraints is a ChannelConstraints with default values. It
|
|
|
|
// is used to test against Alice's local channel constraints.
|
2021-06-28 22:50:20 +02:00
|
|
|
defaultConstraints := &lnrpc.ChannelConstraints{
|
|
|
|
CsvDelay: 4,
|
|
|
|
ChanReserveSat: 1000,
|
2021-09-23 21:40:37 +02:00
|
|
|
DustLimitSat: uint64(dustLimit),
|
2021-06-28 22:50:20 +02:00
|
|
|
MaxPendingAmtMsat: 99000000,
|
|
|
|
MinHtlcMsat: 1,
|
|
|
|
MaxAcceptedHtlcs: bobRemoteMaxHtlcs,
|
|
|
|
}
|
|
|
|
assertChannelConstraintsEqual(
|
2022-08-02 19:00:20 +02:00
|
|
|
ht, defaultConstraints, aliceChannel.LocalConstraints,
|
2021-06-28 22:50:20 +02:00
|
|
|
)
|
|
|
|
|
2022-08-02 19:00:20 +02:00
|
|
|
// customizedConstraints is a ChannelConstraints with customized
|
|
|
|
// values. Ideally, all these values can be passed in when creating the
|
|
|
|
// channel. Currently, only the MinHtlcMsat is customized. It is used
|
|
|
|
// to check against Alice's remote channel constratins.
|
2021-06-28 22:50:20 +02:00
|
|
|
customizedConstraints := &lnrpc.ChannelConstraints{
|
|
|
|
CsvDelay: 4,
|
|
|
|
ChanReserveSat: 1000,
|
2021-09-23 21:40:37 +02:00
|
|
|
DustLimitSat: uint64(dustLimit),
|
2021-06-28 22:50:20 +02:00
|
|
|
MaxPendingAmtMsat: 99000000,
|
|
|
|
MinHtlcMsat: customizedMinHtlc,
|
|
|
|
MaxAcceptedHtlcs: aliceRemoteMaxHtlcs,
|
|
|
|
}
|
|
|
|
assertChannelConstraintsEqual(
|
2022-08-02 19:00:20 +02:00
|
|
|
ht, customizedConstraints, aliceChannel.RemoteConstraints,
|
2021-06-28 22:50:20 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Get the ListChannel response for Bob.
|
2022-08-02 19:00:20 +02:00
|
|
|
bobChannel := ht.QueryChannelByChanPoint(bob, chanPoint)
|
|
|
|
require.Equal(ht, aliceChannel.ChannelPoint, bobChannel.ChannelPoint,
|
|
|
|
"Bob's channel point mismatched")
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-04-07 22:27:26 +02:00
|
|
|
// Check the balance related fields are correct.
|
2022-08-02 19:00:20 +02:00
|
|
|
require.Equal(ht, aliceBalance, bobChannel.RemoteBalance)
|
|
|
|
require.EqualValues(ht, pushAmt, bobChannel.LocalBalance)
|
|
|
|
require.EqualValues(ht, pushAmt, bobChannel.PushAmountSat)
|
2022-04-07 22:27:26 +02:00
|
|
|
|
|
|
|
// Check channel constraints match. Alice's local channel constraint
|
|
|
|
// should be equal to Bob's remote channel constraint, and her remote
|
|
|
|
// one should be equal to Bob's local one.
|
2021-06-28 22:50:20 +02:00
|
|
|
assertChannelConstraintsEqual(
|
2022-08-02 19:00:20 +02:00
|
|
|
ht, aliceChannel.LocalConstraints, bobChannel.RemoteConstraints,
|
2021-06-28 22:50:20 +02:00
|
|
|
)
|
|
|
|
assertChannelConstraintsEqual(
|
2022-08-02 19:00:20 +02:00
|
|
|
ht, aliceChannel.RemoteConstraints, bobChannel.LocalConstraints,
|
2021-06-28 22:50:20 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// testMaxPendingChannels checks that error is returned from remote peer if
|
|
|
|
// max pending channel number was exceeded and that '--maxpendingchannels' flag
|
|
|
|
// exists and works properly.
|
2022-08-03 15:29:25 +02:00
|
|
|
func testMaxPendingChannels(ht *lntemp.HarnessTest) {
|
2021-06-28 22:50:20 +02:00
|
|
|
maxPendingChannels := lncfg.DefaultMaxPendingChannels + 1
|
|
|
|
amount := funding.MaxBtcFundingAmount
|
|
|
|
|
|
|
|
// Create a new node (Carol) with greater number of max pending
|
|
|
|
// channels.
|
|
|
|
args := []string{
|
|
|
|
fmt.Sprintf("--maxpendingchannels=%v", maxPendingChannels),
|
|
|
|
}
|
2022-08-03 15:29:25 +02:00
|
|
|
carol := ht.NewNode("Carol", args)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-03 15:29:25 +02:00
|
|
|
alice := ht.Alice
|
|
|
|
ht.ConnectNodes(alice, carol)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
carolBalance := btcutil.Amount(maxPendingChannels) * amount
|
2022-08-03 15:29:25 +02:00
|
|
|
ht.FundCoins(carolBalance, carol)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Send open channel requests without generating new blocks thereby
|
|
|
|
// increasing pool of pending channels. Then check that we can't open
|
|
|
|
// the channel if the number of pending channels exceed max value.
|
2022-08-03 15:29:25 +02:00
|
|
|
openStreams := make(
|
|
|
|
[]lnrpc.Lightning_OpenChannelClient, maxPendingChannels,
|
|
|
|
)
|
2021-06-28 22:50:20 +02:00
|
|
|
for i := 0; i < maxPendingChannels; i++ {
|
2022-08-03 15:29:25 +02:00
|
|
|
stream := ht.OpenChannelAssertPending(
|
|
|
|
alice, carol, lntemp.OpenChannelParams{
|
2021-06-28 22:50:20 +02:00
|
|
|
Amt: amount,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
openStreams[i] = stream
|
|
|
|
}
|
|
|
|
|
|
|
|
// Carol exhausted available amount of pending channels, next open
|
|
|
|
// channel request should cause ErrorGeneric to be sent back to Alice.
|
2022-08-03 15:29:25 +02:00
|
|
|
ht.OpenChannelAssertErr(
|
|
|
|
alice, carol, lntemp.OpenChannelParams{
|
2021-06-28 22:50:20 +02:00
|
|
|
Amt: amount,
|
2022-08-03 15:29:25 +02:00
|
|
|
}, lnwire.ErrMaxPendingChannels,
|
2021-06-28 22:50:20 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// For now our channels are in pending state, in order to not interfere
|
|
|
|
// with other tests we should clean up - complete opening of the
|
|
|
|
// channel and then close it.
|
|
|
|
|
2022-08-03 15:29:25 +02:00
|
|
|
// Mine 6 blocks, then wait for node's to notify us that the channel
|
|
|
|
// has been opened. The funding transactions should be found within the
|
2021-06-28 22:50:20 +02:00
|
|
|
// first newly mined block. 6 blocks make sure the funding transaction
|
|
|
|
// has enough confirmations to be announced publicly.
|
2022-08-03 15:29:25 +02:00
|
|
|
block := ht.MineBlocksAndAssertNumTxes(6, maxPendingChannels)[0]
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
chanPoints := make([]*lnrpc.ChannelPoint, maxPendingChannels)
|
|
|
|
for i, stream := range openStreams {
|
2022-08-03 15:29:25 +02:00
|
|
|
fundingChanPoint := ht.WaitForChannelOpenEvent(stream)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-03 15:29:25 +02:00
|
|
|
fundingTxID := ht.GetChanPointFundingTxid(fundingChanPoint)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Ensure that the funding transaction enters a block, and is
|
|
|
|
// properly advertised by Alice.
|
2022-08-03 15:29:25 +02:00
|
|
|
ht.Miner.AssertTxInBlock(block, fundingTxID)
|
|
|
|
ht.AssertTopologyChannelOpen(alice, fundingChanPoint)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// The channel should be listed in the peer information
|
|
|
|
// returned by both peers.
|
2022-08-03 15:29:25 +02:00
|
|
|
ht.AssertChannelExists(alice, fundingChanPoint)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
chanPoints[i] = fundingChanPoint
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next, close the channel between Alice and Carol, asserting that the
|
|
|
|
// channel has been properly closed on-chain.
|
|
|
|
for _, chanPoint := range chanPoints {
|
2022-08-03 15:29:25 +02:00
|
|
|
ht.CloseChannel(alice, chanPoint)
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-03 18:34:01 +02:00
|
|
|
// testGarbageCollectLinkNodes tests that we properly garbage collect link
|
|
|
|
// nodes from the database and the set of persistent connections within the
|
|
|
|
// server.
|
|
|
|
func testGarbageCollectLinkNodes(ht *lntemp.HarnessTest) {
|
|
|
|
const chanAmt = 1000000
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-03 18:34:01 +02:00
|
|
|
alice, bob := ht.Alice, ht.Bob
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Open a channel between Alice and Bob which will later be
|
|
|
|
// cooperatively closed.
|
2022-08-03 18:34:01 +02:00
|
|
|
coopChanPoint := ht.OpenChannel(
|
|
|
|
alice, bob, lntemp.OpenChannelParams{
|
2021-06-28 22:50:20 +02:00
|
|
|
Amt: chanAmt,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
// Create Carol's node and connect Alice to her.
|
2022-08-03 18:34:01 +02:00
|
|
|
carol := ht.NewNode("Carol", nil)
|
|
|
|
ht.ConnectNodes(alice, carol)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Open a channel between Alice and Carol which will later be force
|
|
|
|
// closed.
|
2022-08-03 18:34:01 +02:00
|
|
|
forceCloseChanPoint := ht.OpenChannel(
|
|
|
|
alice, carol, lntemp.OpenChannelParams{
|
2021-06-28 22:50:20 +02:00
|
|
|
Amt: chanAmt,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
// Now, create Dave's a node and also open a channel between Alice and
|
|
|
|
// him. This link will serve as the only persistent link throughout
|
|
|
|
// restarts in this test.
|
2022-08-03 18:34:01 +02:00
|
|
|
dave := ht.NewNode("Dave", nil)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-03 18:34:01 +02:00
|
|
|
ht.ConnectNodes(alice, dave)
|
|
|
|
persistentChanPoint := ht.OpenChannel(
|
|
|
|
alice, dave, lntemp.OpenChannelParams{
|
2021-06-28 22:50:20 +02:00
|
|
|
Amt: chanAmt,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
// Restart both Bob and Carol to ensure Alice is able to reconnect to
|
|
|
|
// them.
|
2022-08-03 18:34:01 +02:00
|
|
|
ht.RestartNode(bob)
|
|
|
|
ht.RestartNode(carol)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-03 18:34:01 +02:00
|
|
|
ht.AssertConnected(alice, bob)
|
|
|
|
ht.AssertConnected(alice, carol)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// We'll also restart Alice to ensure she can reconnect to her peers
|
|
|
|
// with open channels.
|
2022-08-03 18:34:01 +02:00
|
|
|
ht.RestartNode(alice)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-03 18:34:01 +02:00
|
|
|
ht.AssertConnected(alice, bob)
|
|
|
|
ht.AssertConnected(alice, carol)
|
|
|
|
ht.AssertConnected(alice, dave)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// testReconnection is a helper closure that restarts the nodes at both
|
|
|
|
// ends of a channel to ensure they do not reconnect after restarting.
|
|
|
|
// When restarting Alice, we'll first need to ensure she has
|
|
|
|
// reestablished her connection with Dave, as they still have an open
|
|
|
|
// channel together.
|
2022-08-03 18:34:01 +02:00
|
|
|
testReconnection := func(node *node.HarnessNode) {
|
2021-06-28 22:50:20 +02:00
|
|
|
// Restart both nodes, to trigger the pruning logic.
|
2022-08-03 18:34:01 +02:00
|
|
|
ht.RestartNode(node)
|
|
|
|
ht.RestartNode(alice)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Now restart both nodes and make sure they don't reconnect.
|
2022-08-03 18:34:01 +02:00
|
|
|
ht.RestartNode(node)
|
|
|
|
ht.AssertNotConnected(alice, node)
|
|
|
|
|
|
|
|
ht.RestartNode(alice)
|
|
|
|
ht.AssertConnected(alice, dave)
|
|
|
|
ht.AssertNotConnected(alice, node)
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now, we'll close the channel between Alice and Bob and ensure there
|
|
|
|
// is no reconnection logic between the both once the channel is fully
|
|
|
|
// closed.
|
2022-08-03 18:34:01 +02:00
|
|
|
ht.CloseChannel(alice, coopChanPoint)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-03 18:34:01 +02:00
|
|
|
testReconnection(bob)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// We'll do the same with Alice and Carol, but this time we'll force
|
|
|
|
// close the channel instead.
|
2022-08-03 18:34:01 +02:00
|
|
|
ht.ForceCloseChannel(alice, forceCloseChanPoint)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// We'll need to mine some blocks in order to mark the channel fully
|
|
|
|
// closed.
|
2022-08-03 18:34:01 +02:00
|
|
|
ht.MineBlocks(
|
|
|
|
chainreg.DefaultBitcoinTimeLockDelta - defaultCSV,
|
|
|
|
)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Before we test reconnection, we'll ensure that the channel has been
|
|
|
|
// fully cleaned up for both Carol and Alice.
|
2022-08-03 18:34:01 +02:00
|
|
|
ht.AssertNumPendingForceClose(alice, 0)
|
|
|
|
ht.AssertNumPendingForceClose(carol, 0)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
testReconnection(carol)
|
|
|
|
|
|
|
|
// Finally, we'll ensure that Bob and Carol no longer show in Alice's
|
|
|
|
// channel graph.
|
2022-08-03 18:34:01 +02:00
|
|
|
req := &lnrpc.ChannelGraphRequest{IncludeUnannounced: true}
|
|
|
|
channelGraph := alice.RPC.DescribeGraph(req)
|
|
|
|
require.NotContains(ht, channelGraph.Nodes, bob.PubKeyStr,
|
|
|
|
"did not expect to find bob in the channel graph, but did")
|
|
|
|
require.NotContains(ht, channelGraph.Nodes, carol.PubKeyStr,
|
|
|
|
"did not expect to find carol in the channel graph, but did")
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Now that the test is done, we can also close the persistent link.
|
2022-08-03 18:34:01 +02:00
|
|
|
ht.CloseChannel(alice, persistentChanPoint)
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// testRejectHTLC tests that a node can be created with the flag --rejecthtlc.
|
|
|
|
// This means that the node will reject all forwarded HTLCs but can still
|
|
|
|
// accept direct HTLCs as well as send HTLCs.
|
2022-08-03 18:37:47 +02:00
|
|
|
func testRejectHTLC(ht *lntemp.HarnessTest) {
|
2021-06-28 22:50:20 +02:00
|
|
|
// RejectHTLC
|
|
|
|
// Alice ------> Carol ------> Bob
|
|
|
|
//
|
|
|
|
const chanAmt = btcutil.Amount(1000000)
|
2022-08-03 18:37:47 +02:00
|
|
|
alice, bob := ht.Alice, ht.Bob
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Create Carol with reject htlc flag.
|
2022-08-03 18:37:47 +02:00
|
|
|
carol := ht.NewNode("Carol", []string{"--rejecthtlc"})
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Connect Alice to Carol.
|
2022-08-03 18:37:47 +02:00
|
|
|
ht.ConnectNodes(alice, carol)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Connect Carol to Bob.
|
2022-08-03 18:37:47 +02:00
|
|
|
ht.ConnectNodes(carol, bob)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Send coins to Carol.
|
2022-08-03 18:37:47 +02:00
|
|
|
ht.FundCoins(btcutil.SatoshiPerBitcoin, carol)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Open a channel between Alice and Carol.
|
2022-08-03 18:37:47 +02:00
|
|
|
chanPointAlice := ht.OpenChannel(
|
|
|
|
alice, carol, lntemp.OpenChannelParams{
|
2021-06-28 22:50:20 +02:00
|
|
|
Amt: chanAmt,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
// Open a channel between Carol and Bob.
|
2022-08-03 18:37:47 +02:00
|
|
|
chanPointCarol := ht.OpenChannel(
|
|
|
|
carol, bob, lntemp.OpenChannelParams{
|
2021-06-28 22:50:20 +02:00
|
|
|
Amt: chanAmt,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
// Channel should be ready for payments.
|
|
|
|
const payAmt = 100
|
|
|
|
|
|
|
|
// Create an invoice from Carol of 100 satoshis.
|
|
|
|
// We expect Alice to be able to pay this invoice.
|
|
|
|
carolInvoice := &lnrpc.Invoice{
|
|
|
|
Memo: "testing - alice should pay carol",
|
2022-08-03 18:37:47 +02:00
|
|
|
RPreimage: ht.Random32Bytes(),
|
2021-06-28 22:50:20 +02:00
|
|
|
Value: payAmt,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Carol adds the invoice to her database.
|
2022-08-03 18:37:47 +02:00
|
|
|
resp := carol.RPC.AddInvoice(carolInvoice)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Alice pays Carols invoice.
|
2022-08-03 18:37:47 +02:00
|
|
|
ht.CompletePaymentRequests(alice, []string{resp.PaymentRequest})
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Create an invoice from Bob of 100 satoshis.
|
|
|
|
// We expect Carol to be able to pay this invoice.
|
|
|
|
bobInvoice := &lnrpc.Invoice{
|
|
|
|
Memo: "testing - carol should pay bob",
|
2022-08-03 18:37:47 +02:00
|
|
|
RPreimage: ht.Random32Bytes(),
|
2021-06-28 22:50:20 +02:00
|
|
|
Value: payAmt,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bob adds the invoice to his database.
|
2022-08-03 18:37:47 +02:00
|
|
|
resp = bob.RPC.AddInvoice(bobInvoice)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Carol pays Bobs invoice.
|
2022-08-03 18:37:47 +02:00
|
|
|
ht.CompletePaymentRequests(carol, []string{resp.PaymentRequest})
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Create an invoice from Bob of 100 satoshis.
|
|
|
|
// Alice attempts to pay Bob but this should fail, since we are
|
|
|
|
// using Carol as a hop and her node will reject onward HTLCs.
|
|
|
|
bobInvoice = &lnrpc.Invoice{
|
|
|
|
Memo: "testing - alice tries to pay bob",
|
2022-08-03 18:37:47 +02:00
|
|
|
RPreimage: ht.Random32Bytes(),
|
2021-06-28 22:50:20 +02:00
|
|
|
Value: payAmt,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bob adds the invoice to his database.
|
2022-08-03 18:37:47 +02:00
|
|
|
resp = bob.RPC.AddInvoice(bobInvoice)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-03 18:37:47 +02:00
|
|
|
// Alice attempts to pay Bobs invoice. This payment should be rejected
|
|
|
|
// since we are using Carol as an intermediary hop, Carol is running
|
|
|
|
// lnd with --rejecthtlc.
|
|
|
|
paymentReq := &routerrpc.SendPaymentRequest{
|
|
|
|
PaymentRequest: resp.PaymentRequest,
|
|
|
|
TimeoutSeconds: 60,
|
|
|
|
FeeLimitMsat: noFeeLimitMsat,
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
2022-08-03 18:37:47 +02:00
|
|
|
payStream := alice.RPC.SendPayment(paymentReq)
|
|
|
|
ht.AssertPaymentStatusFromStream(payStream, lnrpc.Payment_FAILED)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-03 18:37:47 +02:00
|
|
|
ht.AssertLastHTLCError(alice, lnrpc.Failure_CHANNEL_DISABLED)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Close all channels.
|
2022-08-03 18:37:47 +02:00
|
|
|
ht.CloseChannel(alice, chanPointAlice)
|
|
|
|
ht.CloseChannel(carol, chanPointCarol)
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func testNodeSignVerify(net *lntest.NetworkHarness, t *harnessTest) {
|
|
|
|
ctxb := context.Background()
|
|
|
|
|
|
|
|
chanAmt := funding.MaxBtcFundingAmount
|
|
|
|
pushAmt := btcutil.Amount(100000)
|
|
|
|
|
|
|
|
// Create a channel between alice and bob.
|
|
|
|
aliceBobCh := openChannelAndAssert(
|
2021-08-19 14:12:50 +02:00
|
|
|
t, net, net.Alice, net.Bob,
|
2021-06-28 22:50:20 +02:00
|
|
|
lntest.OpenChannelParams{
|
|
|
|
Amt: chanAmt,
|
|
|
|
PushAmt: pushAmt,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
aliceMsg := []byte("alice msg")
|
|
|
|
|
|
|
|
// alice signs "alice msg" and sends her signature to bob.
|
|
|
|
sigReq := &lnrpc.SignMessageRequest{Msg: aliceMsg}
|
2021-08-19 14:12:50 +02:00
|
|
|
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
2021-06-28 22:50:20 +02:00
|
|
|
sigResp, err := net.Alice.SignMessage(ctxt, sigReq)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("SignMessage rpc call failed: %v", err)
|
|
|
|
}
|
|
|
|
aliceSig := sigResp.Signature
|
|
|
|
|
|
|
|
// bob verifying alice's signature should succeed since alice and bob are
|
|
|
|
// connected.
|
|
|
|
verifyReq := &lnrpc.VerifyMessageRequest{Msg: aliceMsg, Signature: aliceSig}
|
|
|
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
|
|
verifyResp, err := net.Bob.VerifyMessage(ctxt, verifyReq)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("VerifyMessage failed: %v", err)
|
|
|
|
}
|
|
|
|
if !verifyResp.Valid {
|
|
|
|
t.Fatalf("alice's signature didn't validate")
|
|
|
|
}
|
|
|
|
if verifyResp.Pubkey != net.Alice.PubKeyStr {
|
|
|
|
t.Fatalf("alice's signature doesn't contain alice's pubkey.")
|
|
|
|
}
|
|
|
|
|
|
|
|
// carol is a new node that is unconnected to alice or bob.
|
|
|
|
carol := net.NewNode(t.t, "Carol", nil)
|
|
|
|
defer shutdownAndAssert(net, t, carol)
|
|
|
|
|
|
|
|
carolMsg := []byte("carol msg")
|
|
|
|
|
|
|
|
// carol signs "carol msg" and sends her signature to bob.
|
|
|
|
sigReq = &lnrpc.SignMessageRequest{Msg: carolMsg}
|
|
|
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
|
|
sigResp, err = carol.SignMessage(ctxt, sigReq)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("SignMessage rpc call failed: %v", err)
|
|
|
|
}
|
|
|
|
carolSig := sigResp.Signature
|
|
|
|
|
|
|
|
// bob verifying carol's signature should fail since they are not connected.
|
|
|
|
verifyReq = &lnrpc.VerifyMessageRequest{Msg: carolMsg, Signature: carolSig}
|
|
|
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
|
|
verifyResp, err = net.Bob.VerifyMessage(ctxt, verifyReq)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("VerifyMessage failed: %v", err)
|
|
|
|
}
|
|
|
|
if verifyResp.Valid {
|
|
|
|
t.Fatalf("carol's signature should not be valid")
|
|
|
|
}
|
|
|
|
if verifyResp.Pubkey != carol.PubKeyStr {
|
|
|
|
t.Fatalf("carol's signature doesn't contain her pubkey")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close the channel between alice and bob.
|
2021-08-11 06:29:40 +02:00
|
|
|
closeChannelAndAssert(t, net, net.Alice, aliceBobCh, false)
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// testAbandonChannel abandones a channel and asserts that it is no
|
|
|
|
// longer open and not in one of the pending closure states. It also
|
|
|
|
// verifies that the abandoned channel is reported as closed with close
|
|
|
|
// type 'abandoned'.
|
|
|
|
func testAbandonChannel(net *lntest.NetworkHarness, t *harnessTest) {
|
|
|
|
ctxb := context.Background()
|
|
|
|
|
|
|
|
// First establish a channel between Alice and Bob.
|
|
|
|
channelParam := lntest.OpenChannelParams{
|
|
|
|
Amt: funding.MaxBtcFundingAmount,
|
|
|
|
PushAmt: btcutil.Amount(100000),
|
|
|
|
}
|
|
|
|
|
|
|
|
chanPoint := openChannelAndAssert(
|
2021-08-19 14:12:50 +02:00
|
|
|
t, net, net.Alice, net.Bob, channelParam,
|
2021-06-28 22:50:20 +02:00
|
|
|
)
|
|
|
|
txid, err := lnrpc.GetChanPointFundingTxid(chanPoint)
|
2021-07-13 18:12:36 +02:00
|
|
|
require.NoError(t.t, err, "alice bob get channel funding txid")
|
2021-06-28 22:50:20 +02:00
|
|
|
chanPointStr := fmt.Sprintf("%v:%v", txid, chanPoint.OutputIndex)
|
|
|
|
|
|
|
|
// Wait for channel to be confirmed open.
|
2021-09-14 12:40:02 +02:00
|
|
|
err = net.Alice.WaitForNetworkChannelOpen(chanPoint)
|
2021-07-13 18:12:36 +02:00
|
|
|
require.NoError(t.t, err, "alice wait for network channel open")
|
2021-09-14 12:40:02 +02:00
|
|
|
err = net.Bob.WaitForNetworkChannelOpen(chanPoint)
|
2021-07-13 18:12:36 +02:00
|
|
|
require.NoError(t.t, err, "bob wait for network channel open")
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Now that the channel is open, we'll obtain its channel ID real quick
|
|
|
|
// so we can use it to query the graph below.
|
|
|
|
listReq := &lnrpc.ListChannelsRequest{}
|
2021-09-14 12:40:02 +02:00
|
|
|
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
2021-06-28 22:50:20 +02:00
|
|
|
aliceChannelList, err := net.Alice.ListChannels(ctxt, listReq)
|
2021-07-13 18:12:36 +02:00
|
|
|
require.NoError(t.t, err)
|
2021-06-28 22:50:20 +02:00
|
|
|
var chanID uint64
|
|
|
|
for _, channel := range aliceChannelList.Channels {
|
|
|
|
if channel.ChannelPoint == chanPointStr {
|
|
|
|
chanID = channel.ChanId
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-13 18:12:36 +02:00
|
|
|
require.NotZero(t.t, chanID, "unable to find channel")
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// To make sure the channel is removed from the backup file as well when
|
|
|
|
// being abandoned, grab a backup snapshot so we can compare it with the
|
|
|
|
// later state.
|
|
|
|
bkupBefore, err := ioutil.ReadFile(net.Alice.ChanBackupPath())
|
2021-07-13 18:12:36 +02:00
|
|
|
require.NoError(t.t, err, "channel backup before abandoning channel")
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Send request to abandon channel.
|
|
|
|
abandonChannelRequest := &lnrpc.AbandonChannelRequest{
|
|
|
|
ChannelPoint: chanPoint,
|
|
|
|
}
|
|
|
|
|
|
|
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
|
|
_, err = net.Alice.AbandonChannel(ctxt, abandonChannelRequest)
|
2021-07-13 18:12:36 +02:00
|
|
|
require.NoError(t.t, err, "abandon channel")
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Assert that channel in no longer open.
|
|
|
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
|
|
aliceChannelList, err = net.Alice.ListChannels(ctxt, listReq)
|
2021-07-13 18:12:36 +02:00
|
|
|
require.NoError(t.t, err, "list channels")
|
|
|
|
require.Zero(t.t, len(aliceChannelList.Channels), "alice open channels")
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Assert that channel is not pending closure.
|
|
|
|
pendingReq := &lnrpc.PendingChannelsRequest{}
|
|
|
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
|
|
alicePendingList, err := net.Alice.PendingChannels(ctxt, pendingReq)
|
2021-07-13 18:12:36 +02:00
|
|
|
require.NoError(t.t, err, "alice list pending channels")
|
|
|
|
require.Zero(
|
|
|
|
t.t, len(alicePendingList.PendingClosingChannels), //nolint:staticcheck
|
|
|
|
"alice pending channels",
|
|
|
|
)
|
|
|
|
require.Zero(
|
|
|
|
t.t, len(alicePendingList.PendingForceClosingChannels),
|
|
|
|
"alice pending force close channels",
|
|
|
|
)
|
|
|
|
require.Zero(
|
|
|
|
t.t, len(alicePendingList.WaitingCloseChannels),
|
|
|
|
"alice waiting close channels",
|
|
|
|
)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Assert that channel is listed as abandoned.
|
|
|
|
closedReq := &lnrpc.ClosedChannelsRequest{
|
|
|
|
Abandoned: true,
|
|
|
|
}
|
|
|
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
|
|
aliceClosedList, err := net.Alice.ClosedChannels(ctxt, closedReq)
|
2021-07-13 18:12:36 +02:00
|
|
|
require.NoError(t.t, err, "alice list closed channels")
|
|
|
|
require.Len(t.t, aliceClosedList.Channels, 1, "alice closed channels")
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Ensure that the channel can no longer be found in the channel graph.
|
2021-07-13 18:12:36 +02:00
|
|
|
err = wait.NoError(func() error {
|
|
|
|
_, err := net.Alice.GetChanInfo(ctxb, &lnrpc.ChanInfoRequest{
|
|
|
|
ChanId: chanID,
|
|
|
|
})
|
|
|
|
if err == nil {
|
|
|
|
return fmt.Errorf("expected error but got nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !strings.Contains(err.Error(), "marked as zombie") {
|
|
|
|
return fmt.Errorf("expected error to contain '%s' but "+
|
|
|
|
"was '%v'", "marked as zombie", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}, defaultTimeout)
|
|
|
|
require.NoError(t.t, err, "marked as zombie")
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Make sure the channel is no longer in the channel backup list.
|
2021-07-13 18:12:36 +02:00
|
|
|
err = wait.NoError(func() error {
|
2021-06-28 22:50:20 +02:00
|
|
|
bkupAfter, err := ioutil.ReadFile(net.Alice.ChanBackupPath())
|
|
|
|
if err != nil {
|
2021-07-13 18:12:36 +02:00
|
|
|
return fmt.Errorf("could not get channel backup "+
|
|
|
|
"before abandoning channel: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(bkupAfter) >= len(bkupBefore) {
|
|
|
|
return fmt.Errorf("expected backups after to be less "+
|
|
|
|
"than %d but was %d", bkupBefore, bkupAfter)
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
|
|
|
|
2021-07-13 18:12:36 +02:00
|
|
|
return nil
|
2021-06-28 22:50:20 +02:00
|
|
|
}, defaultTimeout)
|
2021-07-13 18:12:36 +02:00
|
|
|
require.NoError(t.t, err, "channel removed from backup file")
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Calling AbandonChannel again, should result in no new errors, as the
|
|
|
|
// channel has already been removed.
|
|
|
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
|
|
_, err = net.Alice.AbandonChannel(ctxt, abandonChannelRequest)
|
2021-07-13 18:12:36 +02:00
|
|
|
require.NoError(t.t, err, "abandon channel second time")
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Now that we're done with the test, the channel can be closed. This
|
|
|
|
// is necessary to avoid unexpected outcomes of other tests that use
|
|
|
|
// Bob's lnd instance.
|
2021-08-11 06:29:40 +02:00
|
|
|
closeChannelAndAssert(t, net, net.Bob, chanPoint, true)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Cleanup by mining the force close and sweep transaction.
|
|
|
|
cleanupForceClose(t, net, net.Bob, chanPoint)
|
|
|
|
}
|
|
|
|
|
|
|
|
// testSweepAllCoins tests that we're able to properly sweep all coins from the
|
|
|
|
// wallet into a single target address at the specified fee rate.
|
2022-08-02 14:56:13 +02:00
|
|
|
//
|
|
|
|
// TODO(yy): expand this test to also use P2TR.
|
|
|
|
func testSweepAllCoins(ht *lntemp.HarnessTest) {
|
2021-06-28 22:50:20 +02:00
|
|
|
// First, we'll make a new node, ainz who'll we'll use to test wallet
|
|
|
|
// sweeping.
|
2022-08-02 14:56:13 +02:00
|
|
|
//
|
|
|
|
// NOTE: we won't use standby nodes here since the test will change
|
|
|
|
// each of the node's wallet state.
|
|
|
|
ainz := ht.NewNode("Ainz", nil)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Next, we'll give Ainz exactly 2 utxos of 1 BTC each, with one of
|
|
|
|
// them being p2wkh and the other being a n2wpkh address.
|
2022-08-02 14:56:13 +02:00
|
|
|
ht.FundCoins(btcutil.SatoshiPerBitcoin, ainz)
|
|
|
|
ht.FundCoinsNP2WKH(btcutil.SatoshiPerBitcoin, ainz)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Ensure that we can't send coins to our own Pubkey.
|
2022-08-02 14:56:13 +02:00
|
|
|
info := ainz.RPC.GetInfo()
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Create a label that we will used to label the transaction with.
|
|
|
|
sendCoinsLabel := "send all coins"
|
|
|
|
|
|
|
|
sweepReq := &lnrpc.SendCoinsRequest{
|
|
|
|
Addr: info.IdentityPubkey,
|
|
|
|
SendAll: true,
|
|
|
|
Label: sendCoinsLabel,
|
|
|
|
}
|
2022-08-02 14:56:13 +02:00
|
|
|
ainz.RPC.SendCoinsAssertErr(sweepReq)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-02 14:56:13 +02:00
|
|
|
// Ensure that we can't send coins to another user's Pubkey.
|
|
|
|
info = ht.Alice.RPC.GetInfo()
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
sweepReq = &lnrpc.SendCoinsRequest{
|
|
|
|
Addr: info.IdentityPubkey,
|
|
|
|
SendAll: true,
|
|
|
|
Label: sendCoinsLabel,
|
|
|
|
}
|
2022-08-02 14:56:13 +02:00
|
|
|
ainz.RPC.SendCoinsAssertErr(sweepReq)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// With the two coins above mined, we'll now instruct ainz to sweep all
|
2022-08-02 14:56:13 +02:00
|
|
|
// the coins to an external address not under its control. We will
|
|
|
|
// first attempt to send the coins to addresses that are not compatible
|
|
|
|
// with the current network. This is to test that the wallet will
|
|
|
|
// prevent any onchain transactions to addresses that are not on the
|
2021-06-28 22:50:20 +02:00
|
|
|
// same network as the user.
|
|
|
|
|
|
|
|
// Send coins to a testnet3 address.
|
|
|
|
sweepReq = &lnrpc.SendCoinsRequest{
|
|
|
|
Addr: "tb1qfc8fusa98jx8uvnhzavxccqlzvg749tvjw82tg",
|
|
|
|
SendAll: true,
|
|
|
|
Label: sendCoinsLabel,
|
|
|
|
}
|
2022-08-02 14:56:13 +02:00
|
|
|
ainz.RPC.SendCoinsAssertErr(sweepReq)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Send coins to a mainnet address.
|
|
|
|
sweepReq = &lnrpc.SendCoinsRequest{
|
|
|
|
Addr: "1MPaXKp5HhsLNjVSqaL7fChE3TVyrTMRT3",
|
|
|
|
SendAll: true,
|
|
|
|
Label: sendCoinsLabel,
|
|
|
|
}
|
2022-08-02 14:56:13 +02:00
|
|
|
ainz.RPC.SendCoinsAssertErr(sweepReq)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Send coins to a compatible address.
|
2022-08-02 14:56:13 +02:00
|
|
|
minerAddr := ht.Miner.NewMinerAddress()
|
2021-06-28 22:50:20 +02:00
|
|
|
sweepReq = &lnrpc.SendCoinsRequest{
|
|
|
|
Addr: minerAddr.String(),
|
|
|
|
SendAll: true,
|
|
|
|
Label: sendCoinsLabel,
|
|
|
|
}
|
2022-08-02 14:56:13 +02:00
|
|
|
ainz.RPC.SendCoins(sweepReq)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// We'll mine a block which should include the sweep transaction we
|
|
|
|
// generated above.
|
2022-08-02 14:56:13 +02:00
|
|
|
block := ht.MineBlocksAndAssertNumTxes(1, 1)[0]
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// The sweep transaction should have exactly two inputs as we only had
|
|
|
|
// two UTXOs in the wallet.
|
|
|
|
sweepTx := block.Transactions[1]
|
2022-08-02 14:56:13 +02:00
|
|
|
require.Len(ht, sweepTx.TxIn, 2, "expected 2 inputs")
|
|
|
|
|
|
|
|
// assertTxLabel is a helper function which finds a target tx in our
|
|
|
|
// set of transactions and checks that it has the desired label.
|
2022-11-10 07:47:48 +01:00
|
|
|
assertTxLabel := func(targetTx, label string) error {
|
2022-08-02 14:56:13 +02:00
|
|
|
// List all transactions relevant to our wallet, and find the
|
|
|
|
// tx so that we can check the correct label has been set.
|
|
|
|
txResp := ainz.RPC.GetTransactions()
|
|
|
|
|
2022-11-10 07:47:48 +01:00
|
|
|
var target *lnrpc.Transaction
|
|
|
|
|
|
|
|
// First we need to find the target tx.
|
2022-08-02 14:56:13 +02:00
|
|
|
for _, txn := range txResp.Transactions {
|
|
|
|
if txn.TxHash == targetTx {
|
2022-11-10 07:47:48 +01:00
|
|
|
target = txn
|
2022-08-02 14:56:13 +02:00
|
|
|
}
|
|
|
|
}
|
2022-11-10 07:47:48 +01:00
|
|
|
|
|
|
|
// If we cannot find it, return an error.
|
|
|
|
if target == nil {
|
|
|
|
return fmt.Errorf("target tx %v not found", targetTx)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, check the labels are matched.
|
|
|
|
if target.Label == label {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Errorf("labels not match, want: "+
|
|
|
|
"%v, got %v", label, target.Label)
|
|
|
|
}
|
|
|
|
|
|
|
|
// waitTxLabel waits until the desired tx label is found or timeout.
|
|
|
|
waitTxLabel := func(targetTx, label string) {
|
|
|
|
err := wait.NoError(func() error {
|
|
|
|
return assertTxLabel(targetTx, label)
|
|
|
|
}, defaultTimeout)
|
|
|
|
|
|
|
|
require.NoError(ht, err, "timeout assertTxLabel")
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
sweepTxStr := sweepTx.TxHash().String()
|
2022-11-10 07:47:48 +01:00
|
|
|
waitTxLabel(sweepTxStr, sendCoinsLabel)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-08-02 14:56:13 +02:00
|
|
|
// While we are looking at labels, we test our label transaction
|
|
|
|
// command to make sure it is behaving as expected. First, we try to
|
|
|
|
// label our transaction with an empty label, and check that we fail as
|
|
|
|
// expected.
|
2021-06-28 22:50:20 +02:00
|
|
|
sweepHash := sweepTx.TxHash()
|
2022-08-02 14:56:13 +02:00
|
|
|
req := &walletrpc.LabelTransactionRequest{
|
|
|
|
Txid: sweepHash[:],
|
|
|
|
Label: "",
|
|
|
|
Overwrite: false,
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
2022-08-02 14:56:13 +02:00
|
|
|
err := ainz.RPC.LabelTransactionAssertErr(req)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Our error will be wrapped in a rpc error, so we check that it
|
|
|
|
// contains the error we expect.
|
|
|
|
errZeroLabel := "cannot label transaction with empty label"
|
2022-08-02 14:56:13 +02:00
|
|
|
require.Contains(ht, err.Error(), errZeroLabel,
|
|
|
|
"expected: zero label errorv")
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Next, we try to relabel our transaction without setting the overwrite
|
|
|
|
// boolean. We expect this to fail, because the wallet requires setting
|
|
|
|
// of this param to prevent accidental overwrite of labels.
|
2022-08-02 14:56:13 +02:00
|
|
|
req = &walletrpc.LabelTransactionRequest{
|
|
|
|
Txid: sweepHash[:],
|
|
|
|
Label: "label that will not work",
|
|
|
|
Overwrite: false,
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
2022-08-02 14:56:13 +02:00
|
|
|
err = ainz.RPC.LabelTransactionAssertErr(req)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Our error will be wrapped in a rpc error, so we check that it
|
|
|
|
// contains the error we expect.
|
2022-08-02 14:56:13 +02:00
|
|
|
require.Contains(ht, err.Error(), wallet.ErrTxLabelExists.Error())
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Finally, we overwrite our label with a new label, which should not
|
|
|
|
// fail.
|
|
|
|
newLabel := "new sweep tx label"
|
2022-08-02 14:56:13 +02:00
|
|
|
req = &walletrpc.LabelTransactionRequest{
|
|
|
|
Txid: sweepHash[:],
|
|
|
|
Label: newLabel,
|
|
|
|
Overwrite: true,
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
2022-08-02 14:56:13 +02:00
|
|
|
ainz.RPC.LabelTransaction(req)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
2022-11-10 07:47:48 +01:00
|
|
|
waitTxLabel(sweepTxStr, newLabel)
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// Finally, Ainz should now have no coins at all within his wallet.
|
2022-08-02 14:56:13 +02:00
|
|
|
resp := ainz.RPC.WalletBalance()
|
|
|
|
require.Zero(ht, resp.ConfirmedBalance, "wrong confirmed balance")
|
|
|
|
require.Zero(ht, resp.UnconfirmedBalance, "wrong unconfirmed balance")
|
2021-06-28 22:50:20 +02:00
|
|
|
|
|
|
|
// If we try again, but this time specifying an amount, then the call
|
|
|
|
// should fail.
|
|
|
|
sweepReq.Amount = 10000
|
2022-08-02 14:56:13 +02:00
|
|
|
ainz.RPC.SendCoinsAssertErr(sweepReq)
|
2021-06-28 22:50:20 +02:00
|
|
|
}
|
2022-07-23 07:37:34 +02:00
|
|
|
|
|
|
|
// testListAddresses tests that we get all the addresses and their
|
|
|
|
// corresponding balance correctly.
|
|
|
|
func testListAddresses(net *lntest.NetworkHarness, t *harnessTest) {
|
|
|
|
ctxb := context.Background()
|
|
|
|
|
|
|
|
// First, we'll make a new node - Alice, which will be generating
|
|
|
|
// new addresses.
|
|
|
|
alice := net.NewNode(t.t, "Alice", nil)
|
|
|
|
defer shutdownAndAssert(net, t, alice)
|
|
|
|
|
|
|
|
// Next, we'll give Alice exactly 1 utxo of 1 BTC.
|
|
|
|
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, alice)
|
|
|
|
|
|
|
|
type addressDetails struct {
|
|
|
|
Balance int64
|
|
|
|
Type walletrpc.AddressType
|
|
|
|
}
|
|
|
|
|
|
|
|
// A map of generated address and its balance.
|
|
|
|
generatedAddr := make(map[string]addressDetails)
|
|
|
|
|
|
|
|
// Create an address generated from internal keys.
|
|
|
|
keyLoc := &walletrpc.KeyReq{KeyFamily: 123}
|
|
|
|
keyDesc, err := alice.WalletKitClient.DeriveNextKey(ctxb, keyLoc)
|
|
|
|
require.NoError(t.t, err)
|
|
|
|
|
|
|
|
// Hex Encode the public key.
|
|
|
|
pubkeyString := hex.EncodeToString(keyDesc.RawKeyBytes)
|
|
|
|
|
|
|
|
// Create a p2tr address.
|
|
|
|
resp, err := alice.NewAddress(ctxb, &lnrpc.NewAddressRequest{
|
|
|
|
Type: lnrpc.AddressType_TAPROOT_PUBKEY,
|
|
|
|
})
|
|
|
|
require.NoError(t.t, err)
|
|
|
|
generatedAddr[resp.Address] = addressDetails{
|
|
|
|
Balance: 200_000,
|
|
|
|
Type: walletrpc.AddressType_TAPROOT_PUBKEY,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a p2wkh address.
|
|
|
|
resp, err = alice.NewAddress(ctxb, &lnrpc.NewAddressRequest{
|
|
|
|
Type: lnrpc.AddressType_WITNESS_PUBKEY_HASH,
|
|
|
|
})
|
|
|
|
require.NoError(t.t, err)
|
|
|
|
generatedAddr[resp.Address] = addressDetails{
|
|
|
|
Balance: 300_000,
|
|
|
|
Type: walletrpc.AddressType_WITNESS_PUBKEY_HASH,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a np2wkh address.
|
|
|
|
resp, err = alice.NewAddress(ctxb, &lnrpc.NewAddressRequest{
|
|
|
|
Type: lnrpc.AddressType_NESTED_PUBKEY_HASH,
|
|
|
|
})
|
|
|
|
require.NoError(t.t, err)
|
|
|
|
generatedAddr[resp.Address] = addressDetails{
|
|
|
|
Balance: 400_000,
|
|
|
|
Type: walletrpc.AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH,
|
|
|
|
}
|
|
|
|
|
|
|
|
for addr, addressDetail := range generatedAddr {
|
|
|
|
_, err := alice.SendCoins(ctxb, &lnrpc.SendCoinsRequest{
|
|
|
|
Addr: addr,
|
|
|
|
Amount: addressDetail.Balance,
|
|
|
|
SpendUnconfirmed: true,
|
|
|
|
})
|
|
|
|
require.NoError(t.t, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
mineBlocks(t, net, 1, 3)
|
|
|
|
|
|
|
|
// Get all the accounts except LND's custom accounts.
|
|
|
|
addressLists, err := alice.WalletKitClient.ListAddresses(
|
|
|
|
ctxb, &walletrpc.ListAddressesRequest{},
|
|
|
|
)
|
|
|
|
require.NoError(t.t, err)
|
|
|
|
|
|
|
|
foundAddresses := 0
|
|
|
|
for _, addressList := range addressLists.AccountWithAddresses {
|
|
|
|
addresses := addressList.Addresses
|
|
|
|
derivationPath, err := parseDerivationPath(
|
|
|
|
addressList.DerivationPath,
|
|
|
|
)
|
|
|
|
require.NoError(t.t, err)
|
|
|
|
|
|
|
|
// Should not get an account with KeyFamily - 123.
|
|
|
|
require.NotEqual(
|
|
|
|
t.t, uint32(keyLoc.KeyFamily), derivationPath[2],
|
|
|
|
)
|
|
|
|
|
|
|
|
for _, address := range addresses {
|
|
|
|
if _, ok := generatedAddr[address.Address]; ok {
|
|
|
|
addrDetails := generatedAddr[address.Address]
|
|
|
|
require.Equal(
|
|
|
|
t.t, addrDetails.Balance,
|
|
|
|
address.Balance,
|
|
|
|
)
|
|
|
|
require.Equal(
|
|
|
|
t.t, addrDetails.Type,
|
|
|
|
addressList.AddressType,
|
|
|
|
)
|
|
|
|
foundAddresses++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
require.Equal(t.t, len(generatedAddr), foundAddresses)
|
|
|
|
foundAddresses = 0
|
|
|
|
|
|
|
|
// Get all the accounts (including LND's custom accounts).
|
|
|
|
addressLists, err = alice.WalletKitClient.ListAddresses(
|
|
|
|
ctxb, &walletrpc.ListAddressesRequest{
|
|
|
|
ShowCustomAccounts: true,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
require.NoError(t.t, err)
|
|
|
|
|
|
|
|
for _, addressList := range addressLists.AccountWithAddresses {
|
|
|
|
addresses := addressList.Addresses
|
|
|
|
derivationPath, err := parseDerivationPath(
|
|
|
|
addressList.DerivationPath,
|
|
|
|
)
|
|
|
|
require.NoError(t.t, err)
|
|
|
|
|
|
|
|
for _, address := range addresses {
|
|
|
|
// Check if the KeyFamily in derivation path is 123.
|
|
|
|
if uint32(keyLoc.KeyFamily) == derivationPath[2] {
|
|
|
|
// For LND's custom accounts, the address
|
|
|
|
// represents the public key.
|
|
|
|
pubkey := address.Address
|
|
|
|
require.Equal(t.t, pubkeyString, pubkey)
|
|
|
|
} else if _, ok := generatedAddr[address.Address]; ok {
|
|
|
|
addrDetails := generatedAddr[address.Address]
|
|
|
|
require.Equal(
|
|
|
|
t.t, addrDetails.Balance,
|
|
|
|
address.Balance,
|
|
|
|
)
|
|
|
|
require.Equal(
|
|
|
|
t.t, addrDetails.Type,
|
|
|
|
addressList.AddressType,
|
|
|
|
)
|
|
|
|
foundAddresses++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
require.Equal(t.t, len(generatedAddr), foundAddresses)
|
|
|
|
}
|
2022-08-02 19:00:20 +02:00
|
|
|
|
|
|
|
func assertChannelConstraintsEqual(ht *lntemp.HarnessTest,
|
|
|
|
want, got *lnrpc.ChannelConstraints) {
|
|
|
|
|
|
|
|
require.Equal(ht, want.CsvDelay, got.CsvDelay, "CsvDelay mismatched")
|
|
|
|
|
|
|
|
require.Equal(ht, want.ChanReserveSat, got.ChanReserveSat,
|
|
|
|
"ChanReserveSat mismatched")
|
|
|
|
|
|
|
|
require.Equal(ht, want.DustLimitSat, got.DustLimitSat,
|
|
|
|
"DustLimitSat mismatched")
|
|
|
|
|
|
|
|
require.Equal(ht, want.MaxPendingAmtMsat, got.MaxPendingAmtMsat,
|
|
|
|
"MaxPendingAmtMsat mismatched")
|
|
|
|
|
|
|
|
require.Equal(ht, want.MinHtlcMsat, got.MinHtlcMsat,
|
|
|
|
"MinHtlcMsat mismatched")
|
|
|
|
|
|
|
|
require.Equal(ht, want.MaxAcceptedHtlcs, got.MaxAcceptedHtlcs,
|
|
|
|
"MaxAcceptedHtlcs mismatched")
|
|
|
|
}
|