package itest

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/btcsuite/btcd/btcec/v2"
	"github.com/btcsuite/btcd/btcutil"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/txscript"
	"github.com/btcsuite/btcd/wire"
	"github.com/lightningnetwork/lnd/chainreg"
	"github.com/lightningnetwork/lnd/fn"
	"github.com/lightningnetwork/lnd/funding"
	"github.com/lightningnetwork/lnd/input"
	"github.com/lightningnetwork/lnd/labels"
	"github.com/lightningnetwork/lnd/lnrpc"
	"github.com/lightningnetwork/lnd/lnrpc/signrpc"
	"github.com/lightningnetwork/lnd/lntest"
	"github.com/lightningnetwork/lnd/lntest/node"
	"github.com/lightningnetwork/lnd/lnwallet/chainfee"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/stretchr/testify/require"
)

// testBasicChannelFunding performs a test exercising expected behavior from a
// basic funding workflow. The test creates a new channel between Alice and
// Bob, then immediately closes the channel after asserting some expected post
// conditions. Finally, the chain itself is checked to ensure the closing
// transaction was mined.
func testBasicChannelFunding(ht *lntest.HarnessTest) {
	// Run through the test with combinations of all the different
	// commitment types.
	allTypes := []lnrpc.CommitmentType{
		lnrpc.CommitmentType_STATIC_REMOTE_KEY,
		lnrpc.CommitmentType_ANCHORS,
		lnrpc.CommitmentType_SIMPLE_TAPROOT,
	}

	// testFunding is a function closure that takes Carol and Dave's
	// commitment types and test the funding flow.
	testFunding := func(ht *lntest.HarnessTest, carolCommitType,
		daveCommitType lnrpc.CommitmentType) {

		// Based on the current tweak variable for Carol, we'll
		// preferentially signal the legacy commitment format.  We do
		// the same for Dave shortly below.
		carolArgs := lntest.NodeArgsForCommitType(carolCommitType)
		carol := ht.NewNode("Carol", carolArgs)

		// Each time, we'll send Carol a new set of coins in order to
		// fund the channel.
		ht.FundCoins(btcutil.SatoshiPerBitcoin, carol)

		daveArgs := lntest.NodeArgsForCommitType(daveCommitType)
		dave := ht.NewNode("Dave", daveArgs)

		// Before we start the test, we'll ensure both sides are
		// connected to the funding flow can properly be executed.
		ht.EnsureConnected(carol, dave)

		var privateChan bool

		// If this is to be a taproot channel type, then it needs to be
		// private, otherwise it'll be rejected by Dave.
		//
		// TODO(roasbeef): lift after gossip 1.75
		if carolCommitType == lnrpc.CommitmentType_SIMPLE_TAPROOT {
			privateChan = true
		}

		// If carol wants taproot, but dave wants something
		// else, then we'll assert that the channel negotiation
		// attempt fails.
		if carolCommitType == lnrpc.CommitmentType_SIMPLE_TAPROOT &&
			daveCommitType != lnrpc.CommitmentType_SIMPLE_TAPROOT {

			expectedErr := fmt.Errorf("requested channel type " +
				"not supported")
			amt := funding.MaxBtcFundingAmount
			ht.OpenChannelAssertErr(
				carol, dave, lntest.OpenChannelParams{
					Private:        privateChan,
					Amt:            amt,
					CommitmentType: carolCommitType,
				}, expectedErr,
			)

			return
		}

		carolChan, daveChan, closeChan := basicChannelFundingTest(
			ht, carol, dave, nil, privateChan, &carolCommitType,
		)

		// Both nodes should report the same commitment
		// type.
		chansCommitType := carolChan.CommitmentType
		require.Equal(ht, chansCommitType, daveChan.CommitmentType,
			"commit types don't match")

		// Now check that the commitment type reported by both nodes is
		// what we expect. It will be the minimum of the two nodes'
		// preference, in the order Legacy, Tweakless, Anchors.
		expType := carolCommitType

		switch daveCommitType {
		// Dave supports taproot, type will be what Carol supports.
		case lnrpc.CommitmentType_SIMPLE_TAPROOT:

		// Dave supports anchors, type will be what Carol supports.
		case lnrpc.CommitmentType_ANCHORS:
			// However if Alice wants taproot chans, then we
			// downgrade to anchors as this is still using implicit
			// negotiation.
			if expType == lnrpc.CommitmentType_SIMPLE_TAPROOT {
				expType = lnrpc.CommitmentType_ANCHORS
			}

		// Dave only supports tweakless, channel will be downgraded to
		// this type if Carol supports anchors.
		case lnrpc.CommitmentType_STATIC_REMOTE_KEY:
			switch expType {
			case lnrpc.CommitmentType_ANCHORS:
				expType = lnrpc.CommitmentType_STATIC_REMOTE_KEY
			case lnrpc.CommitmentType_SIMPLE_TAPROOT:
				expType = lnrpc.CommitmentType_STATIC_REMOTE_KEY
			}

		// Dave only supports legacy type, channel will be downgraded
		// to this type.
		case lnrpc.CommitmentType_LEGACY:
			expType = lnrpc.CommitmentType_LEGACY

		default:
			ht.Fatalf("invalid commit type %v", daveCommitType)
		}

		// Check that the signalled type matches what we expect.
		switch {
		case expType == lnrpc.CommitmentType_ANCHORS &&
			chansCommitType == lnrpc.CommitmentType_ANCHORS:

		case expType == lnrpc.CommitmentType_STATIC_REMOTE_KEY &&
			chansCommitType == lnrpc.CommitmentType_STATIC_REMOTE_KEY: //nolint:lll

		case expType == lnrpc.CommitmentType_LEGACY &&
			chansCommitType == lnrpc.CommitmentType_LEGACY:

		case expType == lnrpc.CommitmentType_SIMPLE_TAPROOT &&
			chansCommitType == lnrpc.CommitmentType_SIMPLE_TAPROOT:

		default:
			ht.Fatalf("expected nodes to signal "+
				"commit type %v, instead got "+
				"%v", expType, chansCommitType)
		}

		// As we've concluded this sub-test case we'll now close out
		// the channel for both sides.
		closeChan()
	}

test:
	// We'll test all possible combinations of the feature bit presence
	// that both nodes can signal for this new channel type. We'll make a
	// new Carol+Dave for each test instance as well.
	for _, carolCommitType := range allTypes {
		for _, daveCommitType := range allTypes {
			cc := carolCommitType
			dc := daveCommitType

			testName := fmt.Sprintf(
				"carol_commit=%v,dave_commit=%v", cc, dc,
			)

			success := ht.Run(testName, func(t *testing.T) {
				st := ht.Subtest(t)
				testFunding(st, cc, dc)
			})

			if !success {
				break test
			}
		}
	}
}

// basicChannelFundingTest is a sub-test of the main testBasicChannelFunding
// test. Given two nodes: Alice and Bob, it'll assert proper channel creation,
// then return a function closure that should be called to assert proper
// channel closure.
func basicChannelFundingTest(ht *lntest.HarnessTest,
	alice, bob *node.HarnessNode, fundingShim *lnrpc.FundingShim,
	privateChan bool, commitType *lnrpc.CommitmentType) (*lnrpc.Channel,
	*lnrpc.Channel, func()) {

	chanAmt := funding.MaxBtcFundingAmount
	pushAmt := btcutil.Amount(100000)
	satPerVbyte := btcutil.Amount(1)

	// Record nodes' channel balance before testing.
	aliceChannelBalance := alice.RPC.ChannelBalance()
	bobChannelBalance := bob.RPC.ChannelBalance()

	// Creates a helper closure to be used below which asserts the proper
	// response to a channel balance RPC.
	checkChannelBalance := func(node *node.HarnessNode,
		oldChannelBalance *lnrpc.ChannelBalanceResponse,
		local, remote btcutil.Amount) {

		newResp := oldChannelBalance

		newResp.LocalBalance.Sat += uint64(local)
		newResp.LocalBalance.Msat += uint64(
			lnwire.NewMSatFromSatoshis(local),
		)
		newResp.RemoteBalance.Sat += uint64(remote)
		newResp.RemoteBalance.Msat += uint64(
			lnwire.NewMSatFromSatoshis(remote),
		)

		// Deprecated fields.
		newResp.Balance += int64(local)
		ht.AssertChannelBalanceResp(node, newResp)
	}

	// For taproot channels, the only way we can negotiate is using the
	// explicit commitment type. This allows us to continue supporting the
	// existing min version comparison for implicit negotiation.
	var commitTypeParam lnrpc.CommitmentType
	if commitType != nil &&
		*commitType == lnrpc.CommitmentType_SIMPLE_TAPROOT {

		commitTypeParam = *commitType
	}

	// First establish a channel with a capacity of 0.5 BTC between Alice
	// and Bob with Alice pushing 100k satoshis to Bob's side during
	// funding. This function will block until the channel itself is fully
	// open or an error occurs in the funding process. A series of
	// assertions will be executed to ensure the funding process completed
	// successfully.
	chanPoint := ht.OpenChannel(alice, bob, lntest.OpenChannelParams{
		Private:        privateChan,
		Amt:            chanAmt,
		PushAmt:        pushAmt,
		FundingShim:    fundingShim,
		SatPerVByte:    satPerVbyte,
		CommitmentType: commitTypeParam,
	})

	cType := ht.GetChannelCommitType(alice, chanPoint)

	// With the channel open, ensure that the amount specified above has
	// properly been pushed to Bob.
	aliceLocalBalance := chanAmt - pushAmt - lntest.CalcStaticFee(cType, 0)
	checkChannelBalance(
		alice, aliceChannelBalance, aliceLocalBalance, pushAmt,
	)
	checkChannelBalance(
		bob, bobChannelBalance, pushAmt, aliceLocalBalance,
	)

	aliceChannel := ht.GetChannelByChanPoint(alice, chanPoint)
	bobChannel := ht.GetChannelByChanPoint(bob, chanPoint)

	closeChan := func() {
		// 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.CloseChannel(alice, chanPoint)
	}

	return aliceChannel, bobChannel, closeChan
}

// testUnconfirmedChannelFunding tests that our unconfirmed change outputs can
// be used to fund channels.
func testUnconfirmedChannelFunding(ht *lntest.HarnessTest) {
	const (
		chanAmt = funding.MaxBtcFundingAmount
		pushAmt = btcutil.Amount(100000)
	)

	// We'll start off by creating a node for Carol.
	carol := ht.NewNode("Carol", nil)

	alice := ht.Alice

	// We'll send her some unconfirmed funds.
	ht.FundCoinsUnconfirmed(2*chanAmt, carol)

	// For neutrino backend, we will confirm the coins sent above and let
	// Carol send all her funds to herself to create unconfirmed output.
	if ht.IsNeutrinoBackend() {
		// Confirm the above coins.
		ht.MineBlocksAndAssertNumTxes(1, 1)

		// Create a new address and send to herself.
		resp := carol.RPC.NewAddress(&lnrpc.NewAddressRequest{
			Type: lnrpc.AddressType_TAPROOT_PUBKEY,
		})

		// Once sent, Carol would have one unconfirmed UTXO.
		carol.RPC.SendCoins(&lnrpc.SendCoinsRequest{
			Addr:    resp.Address,
			SendAll: true,
		})
	}

	// Now, we'll connect her to Alice so that they can open a channel
	// together. The funding flow should select Carol's unconfirmed output
	// as she doesn't have any other funds since it's a new node.
	ht.ConnectNodes(carol, alice)

	chanOpenUpdate := ht.OpenChannelAssertStream(
		carol, alice, lntest.OpenChannelParams{
			Amt:              chanAmt,
			PushAmt:          pushAmt,
			SpendUnconfirmed: true,
		},
	)

	// Creates a helper closure to be used below which asserts the proper
	// response to a channel balance RPC.
	checkChannelBalance := func(node *node.HarnessNode,
		local, remote, pendingLocal, pendingRemote btcutil.Amount) {

		expectedResponse := &lnrpc.ChannelBalanceResponse{
			LocalBalance: &lnrpc.Amount{
				Sat: uint64(local),
				Msat: uint64(lnwire.NewMSatFromSatoshis(
					local,
				)),
			},
			RemoteBalance: &lnrpc.Amount{
				Sat: uint64(remote),
				Msat: uint64(lnwire.NewMSatFromSatoshis(
					remote,
				)),
			},
			PendingOpenLocalBalance: &lnrpc.Amount{
				Sat: uint64(pendingLocal),
				Msat: uint64(lnwire.NewMSatFromSatoshis(
					pendingLocal,
				)),
			},
			PendingOpenRemoteBalance: &lnrpc.Amount{
				Sat: uint64(pendingRemote),
				Msat: uint64(lnwire.NewMSatFromSatoshis(
					pendingRemote,
				)),
			},
			UnsettledLocalBalance:  &lnrpc.Amount{},
			UnsettledRemoteBalance: &lnrpc.Amount{},
			// Deprecated fields.
			Balance:            int64(local),
			PendingOpenBalance: int64(pendingLocal),
		}
		ht.AssertChannelBalanceResp(node, expectedResponse)
	}

	// As the channel is pending open, it's expected Carol has both zero
	// local and remote balances, and pending local/remote should not be
	// zero.
	//
	// Note that atm we haven't obtained the chanPoint yet, so we use the
	// type directly.
	cType := lnrpc.CommitmentType_STATIC_REMOTE_KEY
	carolLocalBalance := chanAmt - pushAmt - lntest.CalcStaticFee(cType, 0)
	checkChannelBalance(carol, 0, 0, carolLocalBalance, pushAmt)

	// For Alice, her local/remote balances should be zero, and the
	// local/remote balances are the mirror of Carol's.
	checkChannelBalance(alice, 0, 0, pushAmt, carolLocalBalance)

	// Confirm the channel and wait for it to be recognized by both
	// parties. For neutrino backend, the funding transaction should be
	// mined. Otherwise, two transactions should be mined, the unconfirmed
	// spend and the funding tx.
	ht.MineBlocksAndAssertNumTxes(6, 2)

	chanPoint := ht.WaitForChannelOpenEvent(chanOpenUpdate)

	// With the channel open, we'll check the balances on each side of the
	// channel as a sanity check to ensure things worked out as intended.
	checkChannelBalance(carol, carolLocalBalance, pushAmt, 0, 0)
	checkChannelBalance(alice, pushAmt, carolLocalBalance, 0, 0)

	// TODO(yy): remove the sleep once the following bug is fixed.
	//
	// We may get the error `unable to gracefully close channel while peer
	// is offline (try force closing it instead): channel link not found`.
	// This happens because the channel link hasn't been added yet but we
	// now proceed to closing the channel. We may need to revisit how the
	// channel open event is created and make sure the event is only sent
	// after all relevant states have been updated.
	time.Sleep(2 * time.Second)

	// Now that we're done with the test, the channel can be closed.
	ht.CloseChannel(carol, chanPoint)
}

// testChannelFundingInputTypes tests that any type of supported input type can
// be used to fund channels.
func testChannelFundingInputTypes(ht *lntest.HarnessTest) {
	// We'll start off by creating a node for Carol.
	carol := ht.NewNode("Carol", nil)

	// Now, we'll connect her to Alice so that they can open a
	// channel together.
	ht.ConnectNodes(carol, ht.Alice)

	runChannelFundingInputTypes(ht, ht.Alice, carol)
}

// runChannelFundingInputTypes tests that any type of supported input type can
// be used to fund channels.
func runChannelFundingInputTypes(ht *lntest.HarnessTest, alice,
	carol *node.HarnessNode) {

	const (
		chanAmt  = funding.MaxBtcFundingAmount
		burnAddr = "bcrt1qxsnqpdc842lu8c0xlllgvejt6rhy49u6fmpgyz"
	)

	fundMixed := func(amt btcutil.Amount, target *node.HarnessNode) {
		ht.FundCoins(amt/5, target)
		ht.FundCoins(amt/5, target)
		ht.FundCoinsP2TR(amt/5, target)
		ht.FundCoinsP2TR(amt/5, target)
		ht.FundCoinsP2TR(amt/5, target)
	}
	fundMultipleP2TR := func(amt btcutil.Amount, target *node.HarnessNode) {
		ht.FundCoinsP2TR(amt/4, target)
		ht.FundCoinsP2TR(amt/4, target)
		ht.FundCoinsP2TR(amt/4, target)
		ht.FundCoinsP2TR(amt/4, target)
	}
	fundWithTypes := []func(amt btcutil.Amount, target *node.HarnessNode){
		ht.FundCoins, ht.FundCoinsNP2WKH, ht.FundCoinsP2TR, fundMixed,
		fundMultipleP2TR,
	}

	// Creates a helper closure to be used below which asserts the
	// proper response to a channel balance RPC.
	checkChannelBalance := func(node *node.HarnessNode, local,
		remote, pendingLocal, pendingRemote btcutil.Amount) {

		expectedResponse := &lnrpc.ChannelBalanceResponse{
			LocalBalance: &lnrpc.Amount{
				Sat:  uint64(local),
				Msat: uint64(lnwire.NewMSatFromSatoshis(local)),
			},
			RemoteBalance: &lnrpc.Amount{
				Sat: uint64(remote),
				Msat: uint64(lnwire.NewMSatFromSatoshis(
					remote,
				)),
			},
			PendingOpenLocalBalance: &lnrpc.Amount{
				Sat: uint64(pendingLocal),
				Msat: uint64(lnwire.NewMSatFromSatoshis(
					pendingLocal,
				)),
			},
			PendingOpenRemoteBalance: &lnrpc.Amount{
				Sat: uint64(pendingRemote),
				Msat: uint64(lnwire.NewMSatFromSatoshis(
					pendingRemote,
				)),
			},
			UnsettledLocalBalance:  &lnrpc.Amount{},
			UnsettledRemoteBalance: &lnrpc.Amount{},
			// Deprecated fields.
			Balance:            int64(local),
			PendingOpenBalance: int64(pendingLocal),
		}
		ht.AssertChannelBalanceResp(node, expectedResponse)
	}

	for _, funder := range fundWithTypes {
		// We'll send her some confirmed funds. We send 10% more than
		// we need to account for fees.
		funder((chanAmt*11)/10, carol)

		chanOpenUpdate := ht.OpenChannelAssertStream(
			carol, alice, lntest.OpenChannelParams{
				Amt: chanAmt,
			},
		)

		// As the channel is pending open, it's expected Carol has both
		// zero local and remote balances, and pending local/remote
		// should not be zero.
		//
		// Note that atm we haven't obtained the chanPoint yet, so we
		// use the type directly.
		cType := lnrpc.CommitmentType_STATIC_REMOTE_KEY
		carolLocalBalance := chanAmt - lntest.CalcStaticFee(cType, 0)
		checkChannelBalance(carol, 0, 0, carolLocalBalance, 0)

		// For Alice, her local/remote balances should be zero, and the
		// local/remote balances are the mirror of Carol's.
		checkChannelBalance(alice, 0, 0, 0, carolLocalBalance)

		// Confirm the channel and wait for it to be recognized by both
		// parties. Two transactions should be mined, the unconfirmed
		// spend and the funding tx.
		ht.MineBlocksAndAssertNumTxes(1, 1)
		chanPoint := ht.WaitForChannelOpenEvent(chanOpenUpdate)

		// With the channel open, we'll check the balances on each side
		// of the channel as a sanity check to ensure things worked out
		// as intended.
		checkChannelBalance(carol, carolLocalBalance, 0, 0, 0)
		checkChannelBalance(alice, 0, carolLocalBalance, 0, 0)

		// TODO(yy): remove the sleep once the following bug is fixed.
		//
		// We may get the error `unable to gracefully close channel
		// while peer is offline (try force closing it instead):
		// channel link not found`. This happens because the channel
		// link hasn't been added yet but we now proceed to closing the
		// channel. We may need to revisit how the channel open event
		// is created and make sure the event is only sent after all
		// relevant states have been updated.
		time.Sleep(2 * time.Second)

		// Now that we're done with the test, the channel can be closed.
		ht.CloseChannel(carol, chanPoint)

		// Empty out the wallet so there aren't any lingering coins.
		sendAllCoinsConfirm(ht, carol, burnAddr)
	}
}

// sendAllCoinsConfirm sends all coins of the node's wallet to the given address
// and awaits one confirmation.
func sendAllCoinsConfirm(ht *lntest.HarnessTest, node *node.HarnessNode,
	addr string) {

	sweepReq := &lnrpc.SendCoinsRequest{
		Addr:       addr,
		SendAll:    true,
		TargetConf: 6,
	}
	node.RPC.SendCoins(sweepReq)
	ht.MineBlocksAndAssertNumTxes(1, 1)
}

// testExternalFundingChanPoint tests that we're able to carry out a normal
// channel funding workflow given a channel point that was constructed outside
// the main daemon.
func testExternalFundingChanPoint(ht *lntest.HarnessTest) {
	runExternalFundingScriptEnforced(ht)
	runExternalFundingTaproot(ht)
}

// runExternalFundingChanPoint runs the actual test that tests we're able to
// carry out a normal channel funding workflow given a channel point that was
// constructed outside the main daemon for the script enforced channel type.
func runExternalFundingScriptEnforced(ht *lntest.HarnessTest) {
	// First, we'll create two new nodes that we'll use to open channel
	// between for this test.
	carol := ht.NewNode("carol", nil)
	dave := ht.NewNode("dave", nil)
	commitmentType := lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE

	// Carol will be funding the channel, so we'll send some coins over to
	// her and ensure they have enough confirmations before we proceed.
	ht.FundCoins(btcutil.SatoshiPerBitcoin, carol)

	// Before we start the test, we'll ensure both sides are connected to
	// the funding flow can properly be executed.
	ht.EnsureConnected(carol, dave)

	// At this point, we're ready to simulate our external channel funding
	// flow. To start with, we'll create a pending channel with a shim for
	// a transaction that will never be published.
	const thawHeight uint32 = 10
	const chanSize = funding.MaxBtcFundingAmount
	fundingShim1, chanPoint1 := deriveFundingShim(
		ht, carol, dave, chanSize, thawHeight, false, commitmentType,
	)
	ht.OpenChannelAssertPending(
		carol, dave, lntest.OpenChannelParams{
			Amt:         chanSize,
			FundingShim: fundingShim1,
		},
	)
	ht.AssertNodesNumPendingOpenChannels(carol, dave, 1)

	// That channel is now pending forever and normally would saturate the
	// max pending channel limit for both nodes. But because the channel is
	// externally funded, we should still be able to open another one. Let's
	// do exactly that now. For this one we publish the transaction so we
	// can mine it later.
	fundingShim2, chanPoint2 := deriveFundingShim(
		ht, carol, dave, chanSize, thawHeight, true, commitmentType,
	)

	// At this point, we'll now carry out the normal basic channel funding
	// test as everything should now proceed as normal (a regular channel
	// funding flow).
	carolChan, daveChan, _ := basicChannelFundingTest(
		ht, carol, dave, fundingShim2, false, nil,
	)

	// Both channels should be marked as frozen with the proper thaw
	// height.
	require.Equal(ht, thawHeight, carolChan.ThawHeight,
		"thaw height unmatched")
	require.Equal(ht, thawHeight, daveChan.ThawHeight,
		"thaw height unmatched")

	// Next, to make sure the channel functions as normal, we'll make some
	// payments within the channel.
	payAmt := btcutil.Amount(100000)
	invoice := &lnrpc.Invoice{
		Memo:  "new chans",
		Value: int64(payAmt),
	}
	resp := dave.RPC.AddInvoice(invoice)
	ht.CompletePaymentRequests(carol, []string{resp.PaymentRequest})

	// Now that the channels are open, and we've confirmed that they're
	// operational, we'll now ensure that the channels are frozen as
	// intended (if requested).
	//
	// First, we'll try to close the channel as Carol, the initiator. This
	// should fail as a frozen channel only allows the responder to
	// initiate a channel close.
	err := ht.CloseChannelAssertErr(carol, chanPoint2, false)
	require.Contains(ht, err.Error(), "cannot co-op close frozen channel")

	// Before Dave closes the channel, he needs to check the invoice is
	// settled to avoid an error saying cannot close channel due to active
	// HTLCs.
	ht.AssertInvoiceSettled(dave, resp.PaymentAddr)

	// TODO(yy): remove the sleep once the following bug is fixed.
	// When the invoice is reported settled, the commitment dance is not
	// yet finished, which can cause an error when closing the channel,
	// saying there's active HTLCs. We need to investigate this issue and
	// reverse the order to, first finish the commitment dance, then report
	// the invoice as settled.
	time.Sleep(2 * time.Second)

	// Next we'll try but this time with Dave (the responder) as the
	// initiator. This time the channel should be closed as normal.
	ht.CloseChannel(dave, chanPoint2)

	// As a last step, we check if we still have the pending channel
	// hanging around because we never published the funding TX.
	ht.AssertNodesNumPendingOpenChannels(carol, dave, 1)

	// Let's make sure we can abandon it.
	carol.RPC.AbandonChannel(&lnrpc.AbandonChannelRequest{
		ChannelPoint:           chanPoint1,
		PendingFundingShimOnly: true,
	})
	dave.RPC.AbandonChannel(&lnrpc.AbandonChannelRequest{
		ChannelPoint:           chanPoint1,
		PendingFundingShimOnly: true,
	})

	// It should now not appear in the pending channels anymore.
	ht.AssertNodesNumPendingOpenChannels(carol, dave, 0)
}

// runExternalFundingTaproot runs the actual test that tests we're able to carry
// out a normal channel funding workflow given a channel point that was
// constructed outside the main daemon for the taproot channel type.
func runExternalFundingTaproot(ht *lntest.HarnessTest) {
	// First, we'll create two new nodes that we'll use to open channel
	// between for this test.
	commitmentType := lnrpc.CommitmentType_SIMPLE_TAPROOT
	args := lntest.NodeArgsForCommitType(commitmentType)
	carol := ht.NewNode("carol", args)

	// We'll attempt two channels, so Dave will need to accept two pending
	// ones.
	dave := ht.NewNode("dave", append(args, "--maxpendingchannels=2"))

	// Carol will be funding the channel, so we'll send some coins over to
	// her and ensure they have enough confirmations before we proceed.
	ht.FundCoins(btcutil.SatoshiPerBitcoin, carol)

	// Before we start the test, we'll ensure both sides are connected to
	// the funding flow can properly be executed.
	ht.EnsureConnected(carol, dave)

	// At this point, we're ready to simulate our external channel funding
	// flow. To start with, we'll create a pending channel with a shim for
	// a transaction that will never be published.
	const thawHeight uint32 = 10
	const chanSize = funding.MaxBtcFundingAmount
	fundingShim1, chanPoint1 := deriveFundingShim(
		ht, carol, dave, chanSize, thawHeight, false, commitmentType,
	)
	ht.OpenChannelAssertPending(carol, dave, lntest.OpenChannelParams{
		Amt:            chanSize,
		FundingShim:    fundingShim1,
		CommitmentType: commitmentType,
		Private:        true,
	})
	ht.AssertNodesNumPendingOpenChannels(carol, dave, 1)

	// That channel is now pending forever and normally would saturate the
	// max pending channel limit for both nodes. But because the channel is
	// externally funded, we should still be able to open another one. Let's
	// do exactly that now. For this one we publish the transaction so we
	// can mine it later.
	fundingShim2, chanPoint2 := deriveFundingShim(
		ht, carol, dave, chanSize, thawHeight, true, commitmentType,
	)

	// At this point, we'll now carry out the normal basic channel funding
	// test as everything should now proceed as normal (a regular channel
	// funding flow).
	carolChan, daveChan, _ := basicChannelFundingTest(
		ht, carol, dave, fundingShim2, true, &commitmentType,
	)

	// The itest harness doesn't mine blocks for private channels, so we
	// want to make sure the channel with the published and mined
	// transaction leaves the pending state.
	ht.MineBlocks(6)

	rpcChanPointToStr := func(cp *lnrpc.ChannelPoint) string {
		txid, err := chainhash.NewHash(cp.GetFundingTxidBytes())
		require.NoError(ht, err)
		return fmt.Sprintf("%v:%d", txid.String(), cp.OutputIndex)
	}

	pendingCarol := carol.RPC.PendingChannels().PendingOpenChannels
	require.Len(ht, pendingCarol, 1)
	require.Equal(
		ht, rpcChanPointToStr(chanPoint1),
		pendingCarol[0].Channel.ChannelPoint,
	)
	openCarol := carol.RPC.ListChannels(&lnrpc.ListChannelsRequest{
		ActiveOnly:  true,
		PrivateOnly: true,
	})
	require.Len(ht, openCarol.Channels, 1)
	require.Equal(
		ht, rpcChanPointToStr(chanPoint2),
		openCarol.Channels[0].ChannelPoint,
	)

	pendingDave := dave.RPC.PendingChannels().PendingOpenChannels
	require.Len(ht, pendingDave, 1)
	require.Equal(
		ht, rpcChanPointToStr(chanPoint1),
		pendingDave[0].Channel.ChannelPoint,
	)

	openDave := dave.RPC.ListChannels(&lnrpc.ListChannelsRequest{
		ActiveOnly:  true,
		PrivateOnly: true,
	})
	require.Len(ht, openDave.Channels, 1)
	require.Equal(
		ht, rpcChanPointToStr(chanPoint2),
		openDave.Channels[0].ChannelPoint,
	)

	// Both channels should be marked as frozen with the proper thaw height.
	require.EqualValues(ht, thawHeight, carolChan.ThawHeight, "thaw height")
	require.EqualValues(ht, thawHeight, daveChan.ThawHeight, "thaw height")

	// Next, to make sure the channel functions as normal, we'll make some
	// payments within the channel.
	payAmt := btcutil.Amount(100000)
	invoice := &lnrpc.Invoice{
		Memo:  "new chans",
		Value: int64(payAmt),
	}
	resp := dave.RPC.AddInvoice(invoice)
	ht.CompletePaymentRequests(carol, []string{resp.PaymentRequest})

	// Now that the channels are open, and we've confirmed that they're
	// operational, we'll now ensure that the channels are frozen as
	// intended (if requested).
	//
	// First, we'll try to close the channel as Carol, the initiator. This
	// should fail as a frozen channel only allows the responder to
	// initiate a channel close.
	err := ht.CloseChannelAssertErr(carol, chanPoint2, false)
	require.Contains(ht, err.Error(), "cannot co-op close frozen channel")

	// Before Dave closes the channel, he needs to check the invoice is
	// settled to avoid an error saying cannot close channel due to active
	// HTLCs.
	ht.AssertInvoiceSettled(dave, resp.PaymentAddr)

	// TODO(yy): remove the sleep once the following bug is fixed.
	// When the invoice is reported settled, the commitment dance is not
	// yet finished, which can cause an error when closing the channel,
	// saying there's active HTLCs. We need to investigate this issue and
	// reverse the order to, first finish the commitment dance, then report
	// the invoice as settled.
	time.Sleep(2 * time.Second)

	// Next we'll try but this time with Dave (the responder) as the
	// initiator. This time the channel should be closed as normal.
	ht.CloseChannel(dave, chanPoint2)

	// Let's make sure we can abandon it.
	carol.RPC.AbandonChannel(&lnrpc.AbandonChannelRequest{
		ChannelPoint:           chanPoint1,
		PendingFundingShimOnly: true,
	})
	dave.RPC.AbandonChannel(&lnrpc.AbandonChannelRequest{
		ChannelPoint:           chanPoint1,
		PendingFundingShimOnly: true,
	})

	// It should now not appear in the pending channels anymore.
	ht.AssertNodesNumPendingOpenChannels(carol, dave, 0)
}

// testFundingPersistence is intended to ensure that the Funding Manager
// persists the state of new channels prior to broadcasting the channel's
// funding transaction. This ensures that the daemon maintains an up-to-date
// representation of channels if the system is restarted or disconnected.
// testFundingPersistence mirrors testBasicChannelFunding, but adds restarts
// and checks for the state of channels with unconfirmed funding transactions.
func testChannelFundingPersistence(ht *lntest.HarnessTest) {
	chanAmt := funding.MaxBtcFundingAmount
	pushAmt := btcutil.Amount(0)

	// As we need to create a channel that requires more than 1
	// confirmation before it's open, with the current set of defaults,
	// we'll need to create a new node instance.
	const numConfs = 5
	carolArgs := []string{
		fmt.Sprintf("--bitcoin.defaultchanconfs=%v", numConfs),
	}
	carol := ht.NewNode("Carol", carolArgs)

	alice := ht.Alice
	ht.ConnectNodes(alice, carol)

	// Create a new channel that requires 5 confs before it's considered
	// open, then broadcast the funding transaction
	param := lntest.OpenChannelParams{
		Amt:     chanAmt,
		PushAmt: pushAmt,
	}
	update := ht.OpenChannelAssertPending(alice, carol, param)

	// 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.
	ht.AssertNumPendingOpenChannels(alice, 1)
	ht.AssertNumPendingOpenChannels(carol, 1)

	// Restart both nodes to test that the appropriate state has been
	// persisted and that both nodes recover gracefully.
	ht.RestartNode(alice)
	ht.RestartNode(carol)

	fundingTxID, err := chainhash.NewHash(update.Txid)
	require.NoError(ht, err, "unable to convert funding txid "+
		"into chainhash.Hash")

	// Mine a block, then wait for Alice's node to notify us that the
	// channel has been opened. The funding transaction should be found
	// within the newly mined block.
	block := ht.MineBlocksAndAssertNumTxes(1, 1)[0]
	ht.AssertTxInBlock(block, *fundingTxID)

	// Get the height that our transaction confirmed at.
	height := int32(ht.CurrentHeight())

	// Restart both nodes to test that the appropriate state has been
	// persisted and that both nodes recover gracefully.
	ht.RestartNode(alice)
	ht.RestartNode(carol)

	// The following block ensures that after both nodes have restarted,
	// they have reconnected before the execution of the next test.
	ht.EnsureConnected(alice, carol)

	// Next, mine enough blocks s.t the channel will open with a single
	// additional block mined.
	ht.MineBlocks(3)

	// Assert that our wallet has our opening transaction with a label
	// that does not have a channel ID set yet, because we have not
	// reached our required confirmations.
	tx := ht.AssertTxAtHeight(alice, height, fundingTxID)

	// At this stage, we expect the transaction to be labelled, but not with
	// our channel ID because our transaction has not yet confirmed.
	label := labels.MakeLabel(labels.LabelTypeChannelOpen, nil)
	require.Equal(ht, label, tx.Label, "open channel label wrong")

	// Both nodes should still show a single channel as pending.
	ht.AssertNumPendingOpenChannels(alice, 1)
	ht.AssertNumPendingOpenChannels(carol, 1)

	// Finally, mine the last block which should mark the channel as open.
	ht.MineBlocks(1)

	// 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(carol, 0)

	// The channel should be listed in the peer information returned by
	// both peers.
	chanPoint := lntest.ChanPointFromPendingUpdate(update)

	// Re-lookup our transaction in the block that it confirmed in.
	tx = ht.AssertTxAtHeight(alice, height, fundingTxID)

	// Check both nodes to ensure that the channel is ready for operation.
	chanAlice := ht.AssertChannelExists(alice, chanPoint)
	ht.AssertChannelExists(carol, chanPoint)

	// Make sure Alice and Carol have seen the channel in their network
	// topology.
	ht.AssertTopologyChannelOpen(alice, chanPoint)
	ht.AssertTopologyChannelOpen(carol, chanPoint)

	// Create an additional check for our channel assertion that will
	// check that our label is as expected.
	shortChanID := lnwire.NewShortChanIDFromInt(chanAlice.ChanId)
	label = labels.MakeLabel(labels.LabelTypeChannelOpen, &shortChanID)
	require.Equal(ht, label, tx.Label, "open channel label not updated")

	// 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.CloseChannel(alice, chanPoint)
}

// testBatchChanFunding makes sure multiple channels can be opened in one batch
// transaction in an atomic way.
func testBatchChanFunding(ht *lntest.HarnessTest) {
	// First, we'll create two new nodes that we'll use to open channels
	// to during this test. Carol has a high minimum funding amount that
	// we'll use to trigger an error during the batch channel open.
	carol := ht.NewNode("carol", []string{"--minchansize=200000"})
	dave := ht.NewNode("dave", nil)

	// Next we create a node that will receive a zero-conf channel open from
	// Alice. We'll create the node with the required parameters.
	scidAliasArgs := []string{
		"--protocol.option-scid-alias",
		"--protocol.zero-conf",
		"--protocol.anchors",
	}
	eve := ht.NewNode("eve", scidAliasArgs)

	alice, bob := ht.Alice, ht.Bob
	ht.RestartNodeWithExtraArgs(alice, scidAliasArgs)

	// Before we start the test, we'll ensure Alice is connected to Carol
	// and Dave, so she can open channels to both of them (and Bob).
	ht.EnsureConnected(alice, bob)
	ht.EnsureConnected(alice, carol)
	ht.EnsureConnected(alice, dave)
	ht.EnsureConnected(alice, eve)

	expectedFeeRate := chainfee.SatPerKWeight(2500)

	// We verify that the channel opening uses the correct fee rate.
	ht.SetFeeEstimateWithConf(expectedFeeRate, 3)

	// Let's create our batch TX request. This first one should fail as we
	// open a channel to Carol that is too small for her min chan size.
	batchReq := &lnrpc.BatchOpenChannelRequest{
		TargetConf: 3,
		MinConfs:   1,
		Channels: []*lnrpc.BatchOpenChannel{{
			NodePubkey:         bob.PubKey[:],
			LocalFundingAmount: 100_000,
			BaseFee:            1337,
			UseBaseFee:         true,
		}, {
			NodePubkey:         carol.PubKey[:],
			LocalFundingAmount: 100_000,
			FeeRate:            1337,
			UseFeeRate:         true,
		}, {
			NodePubkey:         dave.PubKey[:],
			LocalFundingAmount: 100_000,
			BaseFee:            1337,
			UseBaseFee:         true,
			FeeRate:            1337,
			UseFeeRate:         true,
		}, {
			NodePubkey:         eve.PubKey[:],
			LocalFundingAmount: 100_000,
			Private:            true,
			ZeroConf:           true,
			CommitmentType:     lnrpc.CommitmentType_ANCHORS,
		}},
	}

	// Check that batch opening fails due to the minchansize requirement.
	err := alice.RPC.BatchOpenChannelAssertErr(batchReq)
	require.Contains(ht, err.Error(), "initial negotiation failed")

	// Let's fix the minimum amount for Alice now and try again.
	batchReq.Channels[1].LocalFundingAmount = 200_000

	// Set up a ChannelAcceptor for Eve to accept a zero-conf opening from
	// Alice.
	acceptStream, cancel := eve.RPC.ChannelAcceptor()
	go acceptChannel(ht.T, true, acceptStream)

	// Batch-open all channels.
	batchResp := alice.RPC.BatchOpenChannel(batchReq)
	require.Len(ht, batchResp.PendingChannels, 4)

	txHash, err := chainhash.NewHash(batchResp.PendingChannels[0].Txid)
	require.NoError(ht, err)

	// Remove the ChannelAcceptor.
	cancel()

	chanPoint1 := &lnrpc.ChannelPoint{
		FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
			FundingTxidBytes: batchResp.PendingChannels[0].Txid,
		},
		OutputIndex: batchResp.PendingChannels[0].OutputIndex,
	}
	chanPoint2 := &lnrpc.ChannelPoint{
		FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
			FundingTxidBytes: batchResp.PendingChannels[1].Txid,
		},
		OutputIndex: batchResp.PendingChannels[1].OutputIndex,
	}
	chanPoint3 := &lnrpc.ChannelPoint{
		FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
			FundingTxidBytes: batchResp.PendingChannels[2].Txid,
		},
		OutputIndex: batchResp.PendingChannels[2].OutputIndex,
	}
	chanPoint4 := &lnrpc.ChannelPoint{
		FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
			FundingTxidBytes: batchResp.PendingChannels[3].Txid,
		},
		OutputIndex: batchResp.PendingChannels[3].OutputIndex,
	}

	// Ensure that Alice can send funds to Eve via the zero-conf channel
	// before the batch transaction was mined.
	ht.AssertTopologyChannelOpen(alice, chanPoint4)
	eveInvoiceParams := &lnrpc.Invoice{
		Value:   int64(10_000),
		Private: true,
	}
	eveInvoiceResp := eve.RPC.AddInvoice(eveInvoiceParams)
	ht.CompletePaymentRequests(
		alice, []string{eveInvoiceResp.PaymentRequest},
	)

	// Mine the batch transaction and check the network topology.
	block := ht.MineBlocksAndAssertNumTxes(6, 1)[0]
	ht.AssertTxInBlock(block, *txHash)
	ht.AssertTopologyChannelOpen(alice, chanPoint1)
	ht.AssertTopologyChannelOpen(alice, chanPoint2)
	ht.AssertTopologyChannelOpen(alice, chanPoint3)

	// Check if the change type from the batch_open_channel funding is P2TR.
	rawTx := ht.GetRawTransaction(*txHash)
	require.Len(ht, rawTx.MsgTx().TxOut, 5)

	// Check the fee rate of the batch-opening transaction. We expect slight
	// inaccuracies because of the DER signature fee estimation.
	openingFeeRate := ht.CalculateTxFeeRate(rawTx.MsgTx())
	require.InEpsilonf(ht, uint64(expectedFeeRate), uint64(openingFeeRate),
		0.01, "want %v, got %v", expectedFeeRate, openingFeeRate)

	// For calculating the change output index we use the formula for the
	// sum of consecutive of integers (n(n+1)/2). All the channel point
	// indexes are known, so we just calculate the difference to get the
	// change output index.
	// Example: Batch outputs = 4, sum_consecutive_ints(4) = 10
	// Subtract all other output indices to get the change index:
	// 10 - 0 - 1 - 2 - 3 = 4
	changeIndex := uint32(10) - (chanPoint1.OutputIndex +
		chanPoint2.OutputIndex + chanPoint3.OutputIndex +
		chanPoint4.OutputIndex)
	ht.AssertOutputScriptClass(
		rawTx, changeIndex, txscript.WitnessV1TaprootTy,
	)

	// With the channel open, ensure that it is counted towards Alice's
	// total channel balance.
	balRes := alice.RPC.ChannelBalance()
	require.NotEqual(ht, int64(0), balRes.LocalBalance.Sat)

	// Next, to make sure the channel functions as normal, we'll make some
	// payments within the channel.
	payAmt := btcutil.Amount(100000)
	invoice := &lnrpc.Invoice{
		Memo:  "new chans",
		Value: int64(payAmt),
	}
	resp := carol.RPC.AddInvoice(invoice)
	ht.CompletePaymentRequests(alice, []string{resp.PaymentRequest})

	// Confirm that Alice's channel partners see here initial fee settings.
	ensurePolicy(
		ht, alice, bob, chanPoint1,
		lnwire.MilliSatoshi(batchReq.Channels[0].BaseFee),
		chainreg.DefaultBitcoinFeeRate,
	)
	ensurePolicy(
		ht, alice, carol, chanPoint2,
		chainreg.DefaultBitcoinBaseFeeMSat,
		lnwire.MilliSatoshi(batchReq.Channels[1].FeeRate),
	)
	ensurePolicy(
		ht, alice, dave, chanPoint3,
		lnwire.MilliSatoshi(batchReq.Channels[2].BaseFee),
		lnwire.MilliSatoshi(batchReq.Channels[2].FeeRate),
	)
	ensurePolicy(
		ht, alice, eve, chanPoint4,
		chainreg.DefaultBitcoinBaseFeeMSat,
		chainreg.DefaultBitcoinFeeRate,
	)

	// To conclude, we'll close the newly created channel between Carol and
	// Dave. This function will also block until the channel is closed and
	// will additionally assert the relevant channel closing post
	// conditions.
	ht.CloseChannel(alice, chanPoint1)
	ht.CloseChannel(alice, chanPoint2)
	ht.CloseChannel(alice, chanPoint3)
	ht.CloseChannel(alice, chanPoint4)
}

// ensurePolicy ensures that the peer sees alice's channel fee settings.
func ensurePolicy(ht *lntest.HarnessTest, alice, peer *node.HarnessNode,
	chanPoint *lnrpc.ChannelPoint, expectedBaseFee lnwire.MilliSatoshi,
	expectedFeeRate lnwire.MilliSatoshi) {

	channel := ht.AssertChannelExists(peer, chanPoint)
	policy, err := peer.RPC.LN.GetChanInfo(
		context.Background(), &lnrpc.ChanInfoRequest{
			ChanId: channel.ChanId,
		},
	)
	require.NoError(ht, err)
	alicePolicy := policy.Node1Policy
	if alice.PubKeyStr == policy.Node2Pub {
		alicePolicy = policy.Node2Policy
	}
	require.EqualValues(ht, expectedBaseFee, alicePolicy.FeeBaseMsat)
	require.EqualValues(ht, expectedFeeRate, alicePolicy.FeeRateMilliMsat)
}

// deriveFundingShim creates a channel funding shim by deriving the necessary
// keys on both sides.
func deriveFundingShim(ht *lntest.HarnessTest, carol, dave *node.HarnessNode,
	chanSize btcutil.Amount, thawHeight uint32, publish bool,
	commitType lnrpc.CommitmentType) (*lnrpc.FundingShim,
	*lnrpc.ChannelPoint) {

	keyLoc := &signrpc.KeyLocator{KeyFamily: 9999}
	carolFundingKey := carol.RPC.DeriveKey(keyLoc)
	daveFundingKey := dave.RPC.DeriveKey(keyLoc)

	// Now that we have the multi-sig keys for each party, we can manually
	// construct the funding transaction. We'll instruct the backend to
	// immediately create and broadcast a transaction paying out an exact
	// amount. Normally this would reside in the mempool, but we just
	// confirm it now for simplicity.
	var (
		fundingOutput *wire.TxOut
		musig2        bool
		err           error
	)
	if commitType == lnrpc.CommitmentType_SIMPLE_TAPROOT {
		var carolKey, daveKey *btcec.PublicKey
		carolKey, err = btcec.ParsePubKey(carolFundingKey.RawKeyBytes)
		require.NoError(ht, err)
		daveKey, err = btcec.ParsePubKey(daveFundingKey.RawKeyBytes)
		require.NoError(ht, err)

		_, fundingOutput, err = input.GenTaprootFundingScript(
			carolKey, daveKey, int64(chanSize),
			fn.None[chainhash.Hash](),
		)
		require.NoError(ht, err)

		musig2 = true
	} else {
		_, fundingOutput, err = input.GenFundingPkScript(
			carolFundingKey.RawKeyBytes, daveFundingKey.RawKeyBytes,
			int64(chanSize),
		)
		require.NoError(ht, err)
	}

	var txid *chainhash.Hash
	targetOutputs := []*wire.TxOut{fundingOutput}
	if publish {
		txid = ht.SendOutputsWithoutChange(targetOutputs, 5)
	} else {
		tx := ht.CreateTransaction(targetOutputs, 5)

		txHash := tx.TxHash()
		txid = &txHash
	}

	// At this point, we can being our external channel funding workflow.
	// We'll start by generating a pending channel ID externally that will
	// be used to track this new funding type.
	pendingChanID := ht.Random32Bytes()

	// Now that we have the pending channel ID, Dave (our responder) will
	// register the intent to receive a new channel funding workflow using
	// the pending channel ID.
	chanPoint := &lnrpc.ChannelPoint{
		FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
			FundingTxidBytes: txid[:],
		},
	}
	chanPointShim := &lnrpc.ChanPointShim{
		Amt:       int64(chanSize),
		ChanPoint: chanPoint,
		LocalKey: &lnrpc.KeyDescriptor{
			RawKeyBytes: daveFundingKey.RawKeyBytes,
			KeyLoc: &lnrpc.KeyLocator{
				KeyFamily: daveFundingKey.KeyLoc.KeyFamily,
				KeyIndex:  daveFundingKey.KeyLoc.KeyIndex,
			},
		},
		RemoteKey:     carolFundingKey.RawKeyBytes,
		PendingChanId: pendingChanID,
		ThawHeight:    thawHeight,
		Musig2:        musig2,
	}
	fundingShim := &lnrpc.FundingShim{
		Shim: &lnrpc.FundingShim_ChanPointShim{
			ChanPointShim: chanPointShim,
		},
	}
	dave.RPC.FundingStateStep(&lnrpc.FundingTransitionMsg{
		Trigger: &lnrpc.FundingTransitionMsg_ShimRegister{
			ShimRegister: fundingShim,
		},
	})

	// If we attempt to register the same shim (has the same pending chan
	// ID), then we should get an error.
	dave.RPC.FundingStateStepAssertErr(&lnrpc.FundingTransitionMsg{
		Trigger: &lnrpc.FundingTransitionMsg_ShimRegister{
			ShimRegister: fundingShim,
		},
	})

	// We'll take the chan point shim we just registered for Dave (the
	// responder), and swap the local/remote keys before we feed it in as
	// Carol's funding shim as the initiator.
	fundingShim.GetChanPointShim().LocalKey = &lnrpc.KeyDescriptor{
		RawKeyBytes: carolFundingKey.RawKeyBytes,
		KeyLoc: &lnrpc.KeyLocator{
			KeyFamily: carolFundingKey.KeyLoc.KeyFamily,
			KeyIndex:  carolFundingKey.KeyLoc.KeyIndex,
		},
	}
	fundingShim.GetChanPointShim().RemoteKey = daveFundingKey.RawKeyBytes

	return fundingShim, chanPoint
}

// testChannelFundingWithUnstableUtxos tests channel openings with restricted
// utxo selection. Internal wallet utxos might be restricted due to another
// subsystems still using it therefore it would be unsecure to use them for
// channel openings. This test focuses on unconfirmed utxos which are still
// being used by the sweeper subsystem hence should only be used when confirmed.
func testChannelFundingWithUnstableUtxos(ht *lntest.HarnessTest) {
	// Select funding amt below wumbo size because we later use fundMax to
	// open a channel with the total balance.
	fundingAmt := btcutil.Amount(3_000_000)

	// We use STATIC_REMOTE_KEY channels because anchor sweeps would
	// interfere and create additional utxos.
	// Although its the current default we explicitly signal it.
	cType := lnrpc.CommitmentType_STATIC_REMOTE_KEY

	// First, we'll create two new nodes that we'll use to open channel
	// between for this test.
	carol := ht.NewNode("carol", nil)
	// We'll attempt at max 2 pending channels, so Dave will need to accept
	// two pending ones.
	dave := ht.NewNode("dave", []string{
		"--maxpendingchannels=2",
	})
	ht.EnsureConnected(carol, dave)

	// Fund Carol's wallet with a confirmed utxo.
	ht.FundCoins(fundingAmt, carol)

	// Now spend the coins to create an unconfirmed transaction. This is
	// necessary to test also the neutrino behaviour. For neutrino nodes
	// only unconfirmed transactions originating from this node will be
	// recognized as unconfirmed.
	req := &lnrpc.NewAddressRequest{Type: AddrTypeTaprootPubkey}
	resp := carol.RPC.NewAddress(req)

	sendCoinsResp := carol.RPC.SendCoins(&lnrpc.SendCoinsRequest{
		Addr:        resp.Address,
		SendAll:     true,
		SatPerVbyte: 1,
	})

	walletUtxo := ht.AssertNumUTXOsUnconfirmed(carol, 1)[0]
	require.EqualValues(ht, sendCoinsResp.Txid, walletUtxo.Outpoint.TxidStr)

	// We will attempt to open 2 channels at a time.
	chanSize := btcutil.Amount(walletUtxo.AmountSat / 3)

	// Open a channel to dave with an unconfirmed utxo. Although this utxo
	// is unconfirmed it can be used to open a channel because it did not
	// originated from the sweeper subsystem.
	update := ht.OpenChannelAssertPending(carol, dave,
		lntest.OpenChannelParams{
			Amt:              chanSize,
			SpendUnconfirmed: true,
			CommitmentType:   cType,
		})
	chanPoint1 := lntest.ChanPointFromPendingUpdate(update)

	// Verify that both nodes know about the channel.
	ht.AssertNumPendingOpenChannels(carol, 1)
	ht.AssertNumPendingOpenChannels(dave, 1)

	// We open another channel on the fly, funds are unconfirmed but because
	// the tx was not created by the sweeper we can use it and open another
	// channel. This is a common use case when opening zeroconf channels,
	// so unconfirmed utxos originated from prior channel opening are safe
	// to use because channel opening should not be RBFed, at least not for
	// now.
	update = ht.OpenChannelAssertPending(carol, dave,
		lntest.OpenChannelParams{
			Amt:              chanSize,
			SpendUnconfirmed: true,
			CommitmentType:   cType,
		})

	chanPoint2 := lntest.ChanPointFromPendingUpdate(update)

	ht.AssertNumPendingOpenChannels(carol, 2)
	ht.AssertNumPendingOpenChannels(dave, 2)

	// We expect the initial funding tx to confirm and also the two
	// unconfirmed channel openings.
	ht.MineBlocksAndAssertNumTxes(1, 3)

	// Now we create an unconfirmed utxo which originated from the sweeper
	// subsystem and hence is not safe to use for channel openings. We do
	// that by dave force-closing the channel. Which let's carol sweep its
	// to_remote output which is not encumbered by any relative locktime.
	ht.CloseChannelAssertPending(dave, chanPoint2, true)

	// Mine the force close commitment transaction.
	ht.MineBlocksAndAssertNumTxes(1, 1)

	// Make sure Carol sees her to_remote output from the force close tx.
	ht.AssertNumPendingSweeps(carol, 1)

	// Mine one block to trigger the sweep transaction.
	ht.MineEmptyBlocks(1)

	// We need to wait for carol initiating the sweep of the to_remote
	// output of chanPoint2.
	utxo := ht.AssertNumUTXOsUnconfirmed(carol, 1)[0]

	// We now try to open channel using the unconfirmed utxo.
	fundingUtxo := utxo

	// Now try to open the channel with this utxo and expect an error.
	expectedErr := fmt.Errorf("outpoint already spent or "+
		"locked by another subsystem: %s:%d",
		fundingUtxo.Outpoint.TxidStr,
		fundingUtxo.Outpoint.OutputIndex)

	ht.OpenChannelAssertErr(carol, dave,
		lntest.OpenChannelParams{
			FundMax:          true,
			SpendUnconfirmed: true,
			Outpoints: []*lnrpc.OutPoint{
				fundingUtxo.Outpoint,
			},
		}, expectedErr)

	// The channel opening failed because the utxo was unconfirmed and
	// originated from the sweeper subsystem. Now we confirm the
	// to_remote sweep and expect the channel opening to work.
	ht.MineBlocksAndAssertNumTxes(1, 1)

	// Try opening the channel with the same utxo (now confirmed) again.
	update = ht.OpenChannelAssertPending(carol, dave,
		lntest.OpenChannelParams{
			FundMax:          true,
			SpendUnconfirmed: true,
			Outpoints: []*lnrpc.OutPoint{
				fundingUtxo.Outpoint,
			},
		})

	chanPoint3 := lntest.ChanPointFromPendingUpdate(update)
	ht.AssertNumPendingOpenChannels(carol, 1)
	ht.AssertNumPendingOpenChannels(dave, 1)

	// We expect chanPoint3 to confirm.
	ht.MineBlocksAndAssertNumTxes(1, 1)

	// Force Close the channel and test the opening flow without preselected
	// utxos.
	// Before we tested the channel funding with a selected coin, now we
	// want to make sure that our internal coin selection also adheres to
	// the restictions of unstable utxos.
	// We create the unconfirmed sweeper originating utxo just like before
	// by force-closing a channel from dave's side.
	ht.CloseChannelAssertPending(dave, chanPoint3, true)
	ht.MineBlocksAndAssertNumTxes(1, 1)

	// Make sure Carol sees her to_remote output from the force close tx.
	ht.AssertNumPendingSweeps(carol, 1)

	// Mine one block to trigger the sweep transaction.
	ht.MineEmptyBlocks(1)

	// Wait for the to_remote sweep tx to show up in carol's wallet.
	ht.AssertNumUTXOsUnconfirmed(carol, 1)

	// Calculate the maximum amount our wallet has for the channel funding
	// so that we will use all utxos.
	carolBalance := carol.RPC.WalletBalance()

	// Now calculate the fee for the channel opening transaction. We don't
	// have to keep a channel reserve because we are using STATIC_REMOTE_KEY
	// channels.
	// NOTE: The TotalBalance includes the unconfirmed balance as well.
	chanSize = btcutil.Amount(carolBalance.TotalBalance) -
		fundingFee(2, false)

	// We are trying to open a channel with the maximum amount and expect it
	// to fail because one of the utxos cannot be used because it is
	// unstable.
	expectedErr = fmt.Errorf("not enough witness outputs to create " +
		"funding transaction")

	ht.OpenChannelAssertErr(carol, dave,
		lntest.OpenChannelParams{
			Amt:              chanSize,
			SpendUnconfirmed: true,
			CommitmentType:   cType,
		}, expectedErr)

	// Confirm the to_remote sweep utxo.
	ht.MineBlocksAndAssertNumTxes(1, 1)

	ht.AssertNumUTXOsConfirmed(carol, 2)

	// Now after the sweep utxo is confirmed it is stable and can be used
	// for channel openings again.
	update = ht.OpenChannelAssertPending(carol, dave,
		lntest.OpenChannelParams{
			Amt:              chanSize,
			SpendUnconfirmed: true,
			CommitmentType:   cType,
		})
	chanPoint4 := lntest.ChanPointFromPendingUpdate(update)

	// Verify that both nodes know about the channel.
	ht.AssertNumPendingOpenChannels(carol, 1)
	ht.AssertNumPendingOpenChannels(dave, 1)

	ht.MineBlocksAndAssertNumTxes(1, 1)

	ht.CloseChannel(carol, chanPoint1)
	ht.CloseChannel(carol, chanPoint4)
}
