package itest

import (
	"fmt"
	"math"
	"time"

	"github.com/btcsuite/btcd/btcutil"
	"github.com/btcsuite/btcd/wire"
	"github.com/lightningnetwork/lnd/contractcourt"
	"github.com/lightningnetwork/lnd/fn"
	"github.com/lightningnetwork/lnd/lncfg"
	"github.com/lightningnetwork/lnd/lnrpc"
	"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
	"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
	"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
	"github.com/lightningnetwork/lnd/lntest"
	"github.com/lightningnetwork/lnd/lntest/node"
	"github.com/lightningnetwork/lnd/lntest/wait"
	"github.com/lightningnetwork/lnd/lntypes"
	"github.com/lightningnetwork/lnd/lnwallet/chainfee"
	"github.com/lightningnetwork/lnd/routing"
	"github.com/lightningnetwork/lnd/sweep"
	"github.com/stretchr/testify/require"
)

// testSweepCPFPAnchorOutgoingTimeout checks when a channel is force closed by
// a local node due to the outgoing HTLC times out, the anchor output is used
// for CPFPing the force close tx.
//
// Setup:
//  1. Fund Alice with 1 UTXO - she only needs one for the funding process,
//  2. Fund Bob with 1 UTXO - he only needs one for the funding process, and
//     the change output will be used for sweeping his anchor on local commit.
//  3. Create a linear network from Alice -> Bob -> Carol.
//  4. Alice pays an invoice to Carol through Bob, with Carol holding the
//     settlement.
//  5. Carol goes offline.
//
// Test:
//  1. Bob force closes the channel with Carol, using the anchor output for
//     CPFPing the force close tx.
//  2. Bob's anchor output is swept and fee bumped based on its deadline and
//     budget.
func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) {
	// Setup testing params.
	//
	// Invoice is 100k sats.
	invoiceAmt := btcutil.Amount(100_000)

	// Use the smallest CLTV so we can mine fewer blocks.
	cltvDelta := routing.MinCLTVDelta

	// deadlineDeltaAnchor is the expected deadline delta for the CPFP
	// anchor sweeping tx.
	deadlineDeltaAnchor := uint32(cltvDelta / 2)

	// startFeeRateAnchor is the starting fee rate for the CPFP anchor
	// sweeping tx.
	startFeeRateAnchor := chainfee.SatPerKWeight(2500)

	// Set up the fee estimator to return the testing fee rate when the
	// conf target is the deadline.
	//
	// TODO(yy): switch to conf when `blockbeat` is in place.
	// ht.SetFeeEstimateWithConf(startFeeRateAnchor, deadlineDeltaAnchor)
	ht.SetFeeEstimate(startFeeRateAnchor)

	// htlcValue is the outgoing HTLC's value.
	htlcValue := invoiceAmt

	// htlcBudget is the budget used to sweep the outgoing HTLC.
	htlcBudget := htlcValue.MulF64(contractcourt.DefaultBudgetRatio)

	// cpfpBudget is the budget used to sweep the CPFP anchor.
	// In addition to the htlc amount to protect we also need to include
	// the anchor amount itself for the budget.
	cpfpBudget := (htlcValue - htlcBudget).MulF64(
		contractcourt.DefaultBudgetRatio,
	) + contractcourt.AnchorOutputValue

	// Create a preimage, that will be held by Carol.
	var preimage lntypes.Preimage
	copy(preimage[:], ht.Random32Bytes())
	payHash := preimage.Hash()

	// We now set up the force close scenario. We will create a network
	// from Alice -> Bob -> Carol, where Alice will send a payment to Carol
	// via Bob, Carol goes offline. We expect Bob to sweep his anchor and
	// outgoing HTLC.
	//
	// Prepare params.
	cfg := []string{
		"--protocol.anchors",
		// Use a small CLTV to mine less blocks.
		fmt.Sprintf("--bitcoin.timelockdelta=%d", cltvDelta),
		// Use a very large CSV, this way to_local outputs are never
		// swept so we can focus on testing HTLCs.
		fmt.Sprintf("--bitcoin.defaultremotedelay=%v", cltvDelta*10),
	}
	cfgs := [][]string{cfg, cfg, cfg}

	openChannelParams := lntest.OpenChannelParams{
		Amt: invoiceAmt * 10,
	}

	// Create a three hop network: Alice -> Bob -> Carol.
	chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, openChannelParams)

	// Unwrap the results.
	abChanPoint, bcChanPoint := chanPoints[0], chanPoints[1]
	alice, bob, carol := nodes[0], nodes[1], nodes[2]

	// For neutrino backend, we need one more UTXO for Bob to create his
	// sweeping txns.
	if ht.IsNeutrinoBackend() {
		ht.FundCoins(btcutil.SatoshiPerBitcoin, bob)
	}

	// Subscribe the invoice.
	streamCarol := carol.RPC.SubscribeSingleInvoice(payHash[:])

	// With the network active, we'll now add a hodl invoice at Carol's
	// end.
	invoiceReq := &invoicesrpc.AddHoldInvoiceRequest{
		Value:      int64(invoiceAmt),
		CltvExpiry: finalCltvDelta,
		Hash:       payHash[:],
	}
	invoice := carol.RPC.AddHoldInvoice(invoiceReq)

	// Let Alice pay the invoices.
	req := &routerrpc.SendPaymentRequest{
		PaymentRequest: invoice.PaymentRequest,
		TimeoutSeconds: 60,
		FeeLimitMsat:   noFeeLimitMsat,
	}

	// Assert the payments are inflight.
	ht.SendPaymentAndAssertStatus(alice, req, lnrpc.Payment_IN_FLIGHT)

	// Wait for Carol to mark invoice as accepted. There is a small gap to
	// bridge between adding the htlc to the channel and executing the exit
	// hop logic.
	ht.AssertInvoiceState(streamCarol, lnrpc.Invoice_ACCEPTED)

	// At this point, all 3 nodes should now have an active channel with
	// the created HTLCs pending on all of them.
	//
	// Alice should have one outgoing HTLCs on channel Alice -> Bob.
	ht.AssertOutgoingHTLCActive(alice, abChanPoint, payHash[:])

	// Bob should have one incoming HTLC on channel Alice -> Bob, and one
	// outgoing HTLC on channel Bob -> Carol.
	ht.AssertIncomingHTLCActive(bob, abChanPoint, payHash[:])
	ht.AssertOutgoingHTLCActive(bob, bcChanPoint, payHash[:])

	// Carol should have one incoming HTLC on channel Bob -> Carol.
	ht.AssertIncomingHTLCActive(carol, bcChanPoint, payHash[:])

	// Let Carol go offline so we can focus on testing Bob's sweeping
	// behavior.
	ht.Shutdown(carol)

	// We'll now mine enough blocks to trigger Bob to force close channel
	// Bob->Carol due to his outgoing HTLC is about to timeout. With the
	// default outgoing broadcast delta of zero, this will be the same
	// height as the outgoing htlc's expiry height.
	numBlocks := padCLTV(uint32(
		invoiceReq.CltvExpiry - lncfg.DefaultOutgoingBroadcastDelta,
	))
	ht.MineEmptyBlocks(int(numBlocks))

	// Assert Bob's force closing tx has been broadcast.
	closeTxid := ht.AssertNumTxsInMempool(1)[0]

	// Remember the force close height so we can calculate the deadline
	// height.
	forceCloseHeight := ht.CurrentHeight()

	// Bob should have two pending sweeps,
	// - anchor sweeping from his local commitment.
	// - anchor sweeping from his remote commitment (invalid).
	//
	// TODO(yy): consider only sweeping the anchor from the local
	// commitment. Previously we would sweep up to three versions of
	// anchors because we don't know which one will be confirmed - if we
	// only broadcast the local anchor sweeping, our peer can broadcast
	// their commitment tx and replaces ours. With the new fee bumping, we
	// should be safe to only sweep our local anchor since we RBF it on
	// every new block, which destroys the remote's ability to pin us.
	sweeps := ht.AssertNumPendingSweeps(bob, 2)

	// The two anchor sweeping should have the same deadline height.
	deadlineHeight := forceCloseHeight + deadlineDeltaAnchor
	require.Equal(ht, deadlineHeight, sweeps[0].DeadlineHeight)
	require.Equal(ht, deadlineHeight, sweeps[1].DeadlineHeight)

	// Remember the deadline height for the CPFP anchor.
	anchorDeadline := sweeps[0].DeadlineHeight

	// Mine a block so Bob's force closing tx stays in the mempool, which
	// also triggers the CPFP anchor sweep.
	ht.MineEmptyBlocks(1)

	// Bob should still have two pending sweeps,
	// - anchor sweeping from his local commitment.
	// - anchor sweeping from his remote commitment (invalid).
	ht.AssertNumPendingSweeps(bob, 2)

	// We now check the expected fee and fee rate are used for Bob's anchor
	// sweeping tx.
	//
	// We should see Bob's anchor sweeping tx triggered by the above
	// block, along with his force close tx.
	txns := ht.GetNumTxsFromMempool(2)

	// Find the sweeping tx.
	sweepTx := ht.FindSweepingTxns(txns, 1, closeTxid)[0]

	// Get the weight for Bob's anchor sweeping tx.
	txWeight := ht.CalculateTxWeight(sweepTx)

	// Bob should start with the initial fee rate of 2500 sat/kw.
	startFeeAnchor := startFeeRateAnchor.FeeForWeight(txWeight)

	// Calculate the fee and fee rate of Bob's sweeping tx.
	fee := uint64(ht.CalculateTxFee(sweepTx))
	feeRate := uint64(ht.CalculateTxFeeRate(sweepTx))

	// feeFuncWidth is the width of the fee function. By the time we got
	// here, we've already mined one block, and the fee function maxes
	// out one block before the deadline, so the width is the original
	// deadline minus 2.
	feeFuncWidth := deadlineDeltaAnchor - 2

	// Calculate the expected delta increased per block.
	feeDelta := (cpfpBudget - startFeeAnchor).MulF64(
		1 / float64(feeFuncWidth),
	)

	// We expect the startingFee and startingFeeRate being used. Allow some
	// deviation because weight estimates during tx generation are
	// estimates.
	//
	// TODO(yy): unify all the units and types re int vs uint!
	require.InEpsilonf(ht, uint64(startFeeAnchor), fee, 0.01,
		"want %d, got %d", startFeeAnchor, fee)
	require.InEpsilonf(ht, uint64(startFeeRateAnchor), feeRate,
		0.01, "want %d, got %d", startFeeRateAnchor, fee)

	// We now mine deadline-2 empty blocks. For each block mined, Bob
	// should perform an RBF on his CPFP anchor sweeping tx. By the end of
	// this iteration, we expect Bob to use up his CPFP budget after one
	// more block.
	for i := uint32(1); i <= feeFuncWidth-1; i++ {
		// Mine an empty block. Since the sweeping tx is not confirmed,
		// Bob's fee bumper should increase its fees.
		ht.MineEmptyBlocks(1)

		// Bob should still have two pending sweeps,
		// - anchor sweeping from his local commitment.
		// - anchor sweeping from his remote commitment (invalid).
		ht.AssertNumPendingSweeps(bob, 2)

		// Make sure Bob's old sweeping tx has been removed from the
		// mempool.
		ht.AssertTxNotInMempool(sweepTx.TxHash())

		// We expect to see two txns in the mempool,
		// - Bob's force close tx.
		// - Bob's anchor sweep tx.
		ht.AssertNumTxsInMempool(2)

		// We expect the fees to increase by i*delta.
		expectedFee := startFeeAnchor + feeDelta.MulF64(float64(i))
		expectedFeeRate := chainfee.NewSatPerKWeight(
			expectedFee, txWeight,
		)

		// We should see Bob's anchor sweeping tx being fee bumped
		// since it's not confirmed, along with his force close tx.
		txns = ht.GetNumTxsFromMempool(2)

		// Find the sweeping tx.
		sweepTx = ht.FindSweepingTxns(txns, 1, closeTxid)[0]

		// Calculate the fee rate of Bob's new sweeping tx.
		feeRate = uint64(ht.CalculateTxFeeRate(sweepTx))

		// Calculate the fee of Bob's new sweeping tx.
		fee = uint64(ht.CalculateTxFee(sweepTx))

		ht.Logf("Bob(position=%v): txWeight=%v, expected: [fee=%d, "+
			"feerate=%v], got: [fee=%v, feerate=%v]",
			feeFuncWidth-i, txWeight, expectedFee,
			expectedFeeRate, fee, feeRate)

		// Assert Bob's tx has the expected fee and fee rate.
		require.InEpsilonf(ht, uint64(expectedFee), fee, 0.01,
			"deadline=%v, want %d, got %d", i, expectedFee, fee)
		require.InEpsilonf(ht, uint64(expectedFeeRate), feeRate, 0.01,
			"deadline=%v, want %d, got %d", i, expectedFeeRate,
			feeRate)
	}

	// We now check the budget has been used up at the deadline-1 block.
	//
	// Once out of the above loop, we expect to be 2 blocks before the CPFP
	// deadline.
	currentHeight := ht.CurrentHeight()
	require.Equal(ht, int(anchorDeadline-2), int(currentHeight))

	// Mine one more block, we'd use up all the CPFP budget.
	ht.MineEmptyBlocks(1)

	// Make sure Bob's old sweeping tx has been removed from the mempool.
	ht.AssertTxNotInMempool(sweepTx.TxHash())

	// Get the last sweeping tx - we should see two txns here, Bob's anchor
	// sweeping tx and his force close tx.
	txns = ht.GetNumTxsFromMempool(2)

	// Find the sweeping tx.
	sweepTx = ht.FindSweepingTxns(txns, 1, closeTxid)[0]

	// Calculate the fee of Bob's new sweeping tx.
	fee = uint64(ht.CalculateTxFee(sweepTx))

	// Assert the budget is now used up.
	require.InEpsilonf(ht, uint64(cpfpBudget), fee, 0.01, "want %d, got %d",
		cpfpBudget, fee)

	// Mine one more block. Since Bob's budget has been used up, there
	// won't be any more sweeping attempts. We now assert this by checking
	// that the sweeping tx stayed unchanged.
	ht.MineEmptyBlocks(1)

	// Get the current sweeping tx and assert it stays unchanged.
	//
	// We expect two txns here, one for the anchor sweeping, the other for
	// the force close tx.
	txns = ht.GetNumTxsFromMempool(2)

	// Find the sweeping tx.
	currentSweepTx := ht.FindSweepingTxns(txns, 1, closeTxid)[0]

	// Assert the anchor sweep tx stays unchanged.
	require.Equal(ht, sweepTx.TxHash(), currentSweepTx.TxHash())

	// Mine a block to confirm Bob's sweeping and force close txns, this is
	// needed to clean up the mempool.
	ht.MineBlocksAndAssertNumTxes(1, 2)

	// The above mined block should confirm Bob's force close tx, and his
	// contractcourt will offer the HTLC to his sweeper. We are not testing
	// the HTLC sweeping behaviors so we just perform a simple check and
	// exit the test.
	ht.AssertNumPendingSweeps(bob, 1)

	// Finally, clean the mempool for the next test.
	ht.CleanShutDown()
}

// testSweepCPFPAnchorIncomingTimeout checks when a channel is force closed by
// a local node due to the incoming HTLC is about to time out, the anchor
// output is used for CPFPing the force close tx.
//
// Setup:
//  1. Fund Alice with 1 UTXOs - she only needs one for the funding process,
//  2. Fund Bob with 1 UTXO - he only needs one for the funding process, and
//     the change output will be used for sweeping his anchor on local commit.
//  3. Create a linear network from Alice -> Bob -> Carol.
//  4. Alice pays an invoice to Carol through Bob.
//  5. Alice goes offline.
//  6. Carol settles the invoice.
//
// Test:
//  1. Bob force closes the channel with Alice, using the anchor output for
//     CPFPing the force close tx.
//  2. Bob's anchor output is swept and fee bumped based on its deadline and
//     budget.
func testSweepCPFPAnchorIncomingTimeout(ht *lntest.HarnessTest) {
	// Setup testing params.
	//
	// Invoice is 100k sats.
	invoiceAmt := btcutil.Amount(100_000)

	// Use the smallest CLTV so we can mine fewer blocks.
	cltvDelta := routing.MinCLTVDelta

	// goToChainDelta is the broadcast delta of Bob's incoming HTLC. When
	// the block height is at CLTV-goToChainDelta, Bob will force close the
	// channel Alice=>Bob.
	goToChainDelta := uint32(lncfg.DefaultIncomingBroadcastDelta)

	// deadlineDeltaAnchor is the expected deadline delta for the CPFP
	// anchor sweeping tx.
	deadlineDeltaAnchor := goToChainDelta / 2

	// startFeeRateAnchor is the starting fee rate for the CPFP anchor
	// sweeping tx.
	startFeeRateAnchor := chainfee.SatPerKWeight(2500)

	// Set up the fee estimator to return the testing fee rate when the
	// conf target is the deadline.
	//
	// TODO(yy): switch to conf when `blockbeat` is in place.
	// ht.SetFeeEstimateWithConf(startFeeRateAnchor, deadlineDeltaAnchor)
	ht.SetFeeEstimate(startFeeRateAnchor)

	// Create a preimage, that will be held by Carol.
	var preimage lntypes.Preimage
	copy(preimage[:], ht.Random32Bytes())
	payHash := preimage.Hash()

	// We now set up the force close scenario. We will create a network
	// from Alice -> Bob -> Carol, where Alice will send a payment to Carol
	// via Bob, Alice goes offline, Carol settles the payment. We expect
	// Bob to sweep his anchor and incoming HTLC.
	//
	// Prepare params.
	cfg := []string{
		"--protocol.anchors",
		// Use a small CLTV to mine less blocks.
		fmt.Sprintf("--bitcoin.timelockdelta=%d", cltvDelta),
		// Use a very large CSV, this way to_local outputs are never
		// swept so we can focus on testing HTLCs.
		fmt.Sprintf("--bitcoin.defaultremotedelay=%v", cltvDelta*10),
	}
	cfgs := [][]string{cfg, cfg, cfg}

	openChannelParams := lntest.OpenChannelParams{
		Amt: invoiceAmt * 10,
	}

	// Create a three hop network: Alice -> Bob -> Carol.
	chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, openChannelParams)

	// Unwrap the results.
	abChanPoint, bcChanPoint := chanPoints[0], chanPoints[1]
	alice, bob, carol := nodes[0], nodes[1], nodes[2]

	// For neutrino backend, we need one more UTXO for Bob to create his
	// sweeping txns.
	if ht.IsNeutrinoBackend() {
		ht.FundCoins(btcutil.SatoshiPerBitcoin, bob)
	}

	// Subscribe the invoice.
	streamCarol := carol.RPC.SubscribeSingleInvoice(payHash[:])

	// With the network active, we'll now add a hodl invoice at Carol's
	// end.
	invoiceReq := &invoicesrpc.AddHoldInvoiceRequest{
		Value:      int64(invoiceAmt),
		CltvExpiry: finalCltvDelta,
		Hash:       payHash[:],
	}
	invoice := carol.RPC.AddHoldInvoice(invoiceReq)

	// Let Alice pay the invoices.
	req := &routerrpc.SendPaymentRequest{
		PaymentRequest: invoice.PaymentRequest,
		TimeoutSeconds: 60,
		FeeLimitMsat:   noFeeLimitMsat,
	}

	// Assert the payments are inflight.
	ht.SendPaymentAndAssertStatus(alice, req, lnrpc.Payment_IN_FLIGHT)

	// Wait for Carol to mark invoice as accepted. There is a small gap to
	// bridge between adding the htlc to the channel and executing the exit
	// hop logic.
	ht.AssertInvoiceState(streamCarol, lnrpc.Invoice_ACCEPTED)

	// At this point, all 3 nodes should now have an active channel with
	// the created HTLCs pending on all of them.
	//
	// Alice should have one outgoing HTLCs on channel Alice -> Bob.
	ht.AssertOutgoingHTLCActive(alice, abChanPoint, payHash[:])

	// Bob should have one incoming HTLC on channel Alice -> Bob, and one
	// outgoing HTLC on channel Bob -> Carol.
	htlc := ht.AssertIncomingHTLCActive(bob, abChanPoint, payHash[:])
	ht.AssertOutgoingHTLCActive(bob, bcChanPoint, payHash[:])

	// Calculate the budget used for Bob's anchor sweeping.
	//
	// htlcValue is the incoming HTLC's value.
	htlcValue := btcutil.Amount(htlc.Amount)

	// htlcBudget is the budget used to sweep the incoming HTLC.
	htlcBudget := htlcValue.MulF64(contractcourt.DefaultBudgetRatio)

	// cpfpBudget is the budget used to sweep the CPFP anchor.
	// In addition to the htlc amount to protect we also need to include
	// the anchor amount itself for the budget.
	cpfpBudget := (htlcValue - htlcBudget).MulF64(
		contractcourt.DefaultBudgetRatio,
	) + contractcourt.AnchorOutputValue

	// Carol should have one incoming HTLC on channel Bob -> Carol.
	ht.AssertIncomingHTLCActive(carol, bcChanPoint, payHash[:])

	// Let Alice go offline. Once Bob later learns the preimage, he
	// couldn't settle it with Alice so he has to go onchain to collect it.
	ht.Shutdown(alice)

	// Carol settles invoice.
	carol.RPC.SettleInvoice(preimage[:])

	// Bob should have settled his outgoing HTLC with Carol.
	ht.AssertHTLCNotActive(bob, bcChanPoint, payHash[:])

	// We'll now mine enough blocks to trigger Bob to force close channel
	// Alice->Bob due to his incoming HTLC is about to timeout. With the
	// default incoming broadcast delta of 10, this will be the same
	// height as the incoming htlc's expiry height minus 10.
	forceCloseHeight := htlc.ExpirationHeight - goToChainDelta

	// Mine till the goToChainHeight is reached.
	currentHeight := ht.CurrentHeight()
	numBlocks := forceCloseHeight - currentHeight
	ht.MineEmptyBlocks(int(numBlocks))

	// Assert Bob's force closing tx has been broadcast.
	closeTxid := ht.AssertNumTxsInMempool(1)[0]

	// Bob should have two pending sweeps,
	// - anchor sweeping from his local commitment.
	// - anchor sweeping from his remote commitment (invalid).
	sweeps := ht.AssertNumPendingSweeps(bob, 2)

	// The two anchor sweeping should have the same deadline height.
	deadlineHeight := forceCloseHeight + deadlineDeltaAnchor
	require.Equal(ht, deadlineHeight, sweeps[0].DeadlineHeight)
	require.Equal(ht, deadlineHeight, sweeps[1].DeadlineHeight)

	// Remember the deadline height for the CPFP anchor.
	anchorDeadline := sweeps[0].DeadlineHeight

	// Mine a block so Bob's force closing tx stays in the mempool, which
	// also triggers the CPFP anchor sweep.
	ht.MineEmptyBlocks(1)

	// Bob should still have two pending sweeps,
	// - anchor sweeping from his local commitment.
	// - anchor sweeping from his remote commitment (invalid).
	ht.AssertNumPendingSweeps(bob, 2)

	// We now check the expected fee and fee rate are used for Bob's anchor
	// sweeping tx.
	//
	// We should see Bob's anchor sweeping tx triggered by the above
	// block, along with his force close tx.
	txns := ht.GetNumTxsFromMempool(2)

	// Find the sweeping tx.
	sweepTx := ht.FindSweepingTxns(txns, 1, closeTxid)[0]

	// Get the weight for Bob's anchor sweeping tx.
	txWeight := ht.CalculateTxWeight(sweepTx)

	// Bob should start with the initial fee rate of 2500 sat/kw.
	startFeeAnchor := startFeeRateAnchor.FeeForWeight(txWeight)

	// Calculate the fee and fee rate of Bob's sweeping tx.
	fee := uint64(ht.CalculateTxFee(sweepTx))
	feeRate := uint64(ht.CalculateTxFeeRate(sweepTx))

	// feeFuncWidth is the width of the fee function. By the time we got
	// here, we've already mined one block, and the fee function maxes
	// out one block before the deadline, so the width is the original
	// deadline minus 2.
	feeFuncWidth := deadlineDeltaAnchor - 2

	// Calculate the expected delta increased per block.
	feeDelta := (cpfpBudget - startFeeAnchor).MulF64(
		1 / float64(feeFuncWidth),
	)

	// We expect the startingFee and startingFeeRate being used. Allow some
	// deviation because weight estimates during tx generation are
	// estimates.
	//
	// TODO(yy): unify all the units and types re int vs uint!
	require.InEpsilonf(ht, uint64(startFeeAnchor), fee, 0.01,
		"want %d, got %d", startFeeAnchor, fee)
	require.InEpsilonf(ht, uint64(startFeeRateAnchor), feeRate,
		0.01, "want %d, got %d", startFeeRateAnchor, fee)

	// We now mine deadline-2 empty blocks. For each block mined, Bob
	// should perform an RBF on his CPFP anchor sweeping tx. By the end of
	// this iteration, we expect Bob to use up his CPFP budget after one
	// more block.
	for i := uint32(1); i <= feeFuncWidth-1; i++ {
		// Mine an empty block. Since the sweeping tx is not confirmed,
		// Bob's fee bumper should increase its fees.
		ht.MineEmptyBlocks(1)

		// Bob should still have two pending sweeps,
		// - anchor sweeping from his local commitment.
		// - anchor sweeping from his remote commitment (invalid).
		ht.AssertNumPendingSweeps(bob, 2)

		// Make sure Bob's old sweeping tx has been removed from the
		// mempool.
		ht.AssertTxNotInMempool(sweepTx.TxHash())

		// We expect to see two txns in the mempool,
		// - Bob's force close tx.
		// - Bob's anchor sweep tx.
		ht.AssertNumTxsInMempool(2)

		// We expect the fees to increase by i*delta.
		expectedFee := startFeeAnchor + feeDelta.MulF64(float64(i))
		expectedFeeRate := chainfee.NewSatPerKWeight(
			expectedFee, txWeight,
		)

		// We should see Bob's anchor sweeping tx being fee bumped
		// since it's not confirmed, along with his force close tx.
		txns = ht.GetNumTxsFromMempool(2)

		// Find the sweeping tx.
		sweepTx = ht.FindSweepingTxns(txns, 1, closeTxid)[0]

		// Calculate the fee rate of Bob's new sweeping tx.
		feeRate = uint64(ht.CalculateTxFeeRate(sweepTx))

		// Calculate the fee of Bob's new sweeping tx.
		fee = uint64(ht.CalculateTxFee(sweepTx))

		ht.Logf("Bob(position=%v): txWeight=%v, expected: [fee=%d, "+
			"feerate=%v], got: [fee=%v, feerate=%v]",
			feeFuncWidth-i, txWeight, expectedFee,
			expectedFeeRate, fee, feeRate)

		// Assert Bob's tx has the expected fee and fee rate.
		require.InEpsilonf(ht, uint64(expectedFee), fee, 0.01,
			"deadline=%v, want %d, got %d", i, expectedFee, fee)
		require.InEpsilonf(ht, uint64(expectedFeeRate), feeRate, 0.01,
			"deadline=%v, want %d, got %d", i, expectedFeeRate,
			feeRate)
	}

	// We now check the budget has been used up at the deadline-1 block.
	//
	// Once out of the above loop, we expect to be 2 blocks before the CPFP
	// deadline.
	currentHeight = ht.CurrentHeight()
	require.Equal(ht, int(anchorDeadline-2), int(currentHeight))

	// Mine one more block, we'd use up all the CPFP budget.
	ht.MineEmptyBlocks(1)

	// Make sure Bob's old sweeping tx has been removed from the mempool.
	ht.AssertTxNotInMempool(sweepTx.TxHash())

	// Get the last sweeping tx - we should see two txns here, Bob's anchor
	// sweeping tx and his force close tx.
	txns = ht.GetNumTxsFromMempool(2)

	// Find the sweeping tx.
	sweepTx = ht.FindSweepingTxns(txns, 1, closeTxid)[0]

	// Calculate the fee of Bob's new sweeping tx.
	fee = uint64(ht.CalculateTxFee(sweepTx))

	// Assert the budget is now used up.
	require.InEpsilonf(ht, uint64(cpfpBudget), fee, 0.01, "want %d, got %d",
		cpfpBudget, fee)

	// Mine one more block. Since Bob's budget has been used up, there
	// won't be any more sweeping attempts. We now assert this by checking
	// that the sweeping tx stayed unchanged.
	ht.MineEmptyBlocks(1)

	// Get the current sweeping tx and assert it stays unchanged.
	//
	// We expect two txns here, one for the anchor sweeping, the other for
	// the force close tx.
	txns = ht.GetNumTxsFromMempool(2)

	// Find the sweeping tx.
	currentSweepTx := ht.FindSweepingTxns(txns, 1, closeTxid)[0]

	// Assert the anchor sweep tx stays unchanged.
	require.Equal(ht, sweepTx.TxHash(), currentSweepTx.TxHash())

	// Mine a block to confirm Bob's sweeping and force close txns, this is
	// needed to clean up the mempool.
	ht.MineBlocksAndAssertNumTxes(1, 2)

	// The above mined block should confirm Bob's force close tx, and his
	// contractcourt will offer the HTLC to his sweeper. We are not testing
	// the HTLC sweeping behaviors so we just perform a simple check and
	// exit the test.
	ht.AssertNumPendingSweeps(bob, 1)

	// Finally, clean the mempool for the next test.
	ht.CleanShutDown()
}

// testSweepHTLCs checks the sweeping behavior for HTLC outputs. Since HTLCs
// are time-sensitive, we expect to see both the incoming and outgoing HTLCs
// are fee bumped properly based on their budgets and deadlines.
//
// Setup:
//  1. Fund Alice with 1 UTXOs - she only needs one for the funding process,
//  2. Fund Bob with 3 UTXOs - he needs one for the funding process, one for
//     his CPFP anchor sweeping, and one for sweeping his outgoing HTLC.
//  3. Create a linear network from Alice -> Bob -> Carol.
//  4. Alice pays two invoices to Carol, with Carol holding the settlement.
//  5. Alice goes offline.
//  7. Carol settles one of the invoices with Bob, so Bob has an incoming HTLC
//     that he can claim onchain since he has the preimage.
//  8. Carol goes offline.
//  9. Assert Bob sweeps his incoming and outgoing HTLCs with the expected fee
//     rates.
//
// Test:
//  1. Bob's outgoing HTLC is swept and fee bumped based on its deadline and
//     budget.
//  2. Bob's incoming HTLC is swept and fee bumped based on its deadline and
//     budget.
func testSweepHTLCs(ht *lntest.HarnessTest) {
	// Setup testing params.
	//
	// Invoice is 100k sats.
	invoiceAmt := btcutil.Amount(100_000)

	// Use the smallest CLTV so we can mine fewer blocks.
	cltvDelta := routing.MinCLTVDelta

	// Start tracking the deadline delta of Bob's HTLCs. We need one block
	// for the CSV lock, and another block to trigger the sweeper to sweep.
	outgoingHTLCDeadline := int32(cltvDelta - 2)
	incomingHTLCDeadline := int32(lncfg.DefaultIncomingBroadcastDelta - 2)

	// startFeeRate1 and startFeeRate2 are returned by the fee estimator in
	// sat/kw. They will be used as the starting fee rate for the linear
	// fee func used by Bob. The values are chosen from calling the cli in
	// bitcoind:
	// - `estimatesmartfee 18 conservative`.
	// - `estimatesmartfee 10 conservative`.
	startFeeRate1 := chainfee.SatPerKWeight(2500)
	startFeeRate2 := chainfee.SatPerKWeight(3000)

	// Set up the fee estimator to return the testing fee rate when the
	// conf target is the deadline.
	ht.SetFeeEstimateWithConf(startFeeRate1, uint32(outgoingHTLCDeadline))
	ht.SetFeeEstimateWithConf(startFeeRate2, uint32(incomingHTLCDeadline))

	// Create two preimages, one that will be settled, the other be hold.
	var preimageSettled, preimageHold lntypes.Preimage
	copy(preimageSettled[:], ht.Random32Bytes())
	copy(preimageHold[:], ht.Random32Bytes())
	payHashSettled := preimageSettled.Hash()
	payHashHold := preimageHold.Hash()

	// We now set up the force close scenario. We will create a network
	// from Alice -> Bob -> Carol, where Alice will send two payments to
	// Carol via Bob, Alice goes offline, then Carol settles the first
	// payment, goes offline. We expect Bob to sweep his incoming and
	// outgoing HTLCs.
	//
	// Prepare params.
	cfg := []string{
		"--protocol.anchors",
		// Use a small CLTV to mine less blocks.
		fmt.Sprintf("--bitcoin.timelockdelta=%d", cltvDelta),
		// Use a very large CSV, this way to_local outputs are never
		// swept so we can focus on testing HTLCs.
		fmt.Sprintf("--bitcoin.defaultremotedelay=%v", cltvDelta*10),
	}
	cfgs := [][]string{cfg, cfg, cfg}

	openChannelParams := lntest.OpenChannelParams{
		Amt: invoiceAmt * 10,
	}

	// Create a three hop network: Alice -> Bob -> Carol.
	chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, openChannelParams)

	// Unwrap the results.
	abChanPoint, bcChanPoint := chanPoints[0], chanPoints[1]
	alice, bob, carol := nodes[0], nodes[1], nodes[2]

	// Bob needs two more wallet utxos:
	// - when sweeping anchors, he needs one utxo for each sweep.
	// - when sweeping HTLCs, he needs one utxo for each sweep.
	ht.FundCoins(btcutil.SatoshiPerBitcoin, bob)
	ht.FundCoins(btcutil.SatoshiPerBitcoin, bob)

	// For neutrino backend, we need two more UTXOs for Bob to create his
	// sweeping txns.
	if ht.IsNeutrinoBackend() {
		ht.FundCoins(btcutil.SatoshiPerBitcoin, bob)
		ht.FundCoins(btcutil.SatoshiPerBitcoin, bob)
	}

	// Subscribe the invoices.
	stream1 := carol.RPC.SubscribeSingleInvoice(payHashSettled[:])
	stream2 := carol.RPC.SubscribeSingleInvoice(payHashHold[:])

	// With the network active, we'll now add two hodl invoices at Carol's
	// end.
	invoiceReqSettle := &invoicesrpc.AddHoldInvoiceRequest{
		Value:      int64(invoiceAmt),
		CltvExpiry: finalCltvDelta,
		Hash:       payHashSettled[:],
	}
	invoiceSettle := carol.RPC.AddHoldInvoice(invoiceReqSettle)

	invoiceReqHold := &invoicesrpc.AddHoldInvoiceRequest{
		Value:      int64(invoiceAmt),
		CltvExpiry: finalCltvDelta,
		Hash:       payHashHold[:],
	}
	invoiceHold := carol.RPC.AddHoldInvoice(invoiceReqHold)

	// Let Alice pay the invoices.
	req1 := &routerrpc.SendPaymentRequest{
		PaymentRequest: invoiceSettle.PaymentRequest,
		TimeoutSeconds: 60,
		FeeLimitMsat:   noFeeLimitMsat,
	}
	req2 := &routerrpc.SendPaymentRequest{
		PaymentRequest: invoiceHold.PaymentRequest,
		TimeoutSeconds: 60,
		FeeLimitMsat:   noFeeLimitMsat,
	}

	// Assert the payments are inflight.
	ht.SendPaymentAndAssertStatus(alice, req1, lnrpc.Payment_IN_FLIGHT)
	ht.SendPaymentAndAssertStatus(alice, req2, lnrpc.Payment_IN_FLIGHT)

	// Wait for Carol to mark invoice as accepted. There is a small gap to
	// bridge between adding the htlc to the channel and executing the exit
	// hop logic.
	ht.AssertInvoiceState(stream1, lnrpc.Invoice_ACCEPTED)
	ht.AssertInvoiceState(stream2, lnrpc.Invoice_ACCEPTED)

	// At this point, all 3 nodes should now have an active channel with
	// the created HTLCs pending on all of them.
	//
	// Alice should have two outgoing HTLCs on channel Alice -> Bob.
	ht.AssertOutgoingHTLCActive(alice, abChanPoint, payHashSettled[:])
	ht.AssertOutgoingHTLCActive(alice, abChanPoint, payHashHold[:])

	// Bob should have two incoming HTLCs on channel Alice -> Bob, and two
	// outgoing HTLCs on channel Bob -> Carol.
	ht.AssertIncomingHTLCActive(bob, abChanPoint, payHashSettled[:])
	ht.AssertIncomingHTLCActive(bob, abChanPoint, payHashHold[:])
	ht.AssertOutgoingHTLCActive(bob, bcChanPoint, payHashSettled[:])
	ht.AssertOutgoingHTLCActive(bob, bcChanPoint, payHashHold[:])

	// Carol should have two incoming HTLCs on channel Bob -> Carol.
	ht.AssertIncomingHTLCActive(carol, bcChanPoint, payHashSettled[:])
	ht.AssertIncomingHTLCActive(carol, bcChanPoint, payHashHold[:])

	// Let Alice go offline. Once Bob later learns the preimage, he
	// couldn't settle it with Alice so he has to go onchain to collect it.
	ht.Shutdown(alice)

	// Carol settles the first invoice.
	carol.RPC.SettleInvoice(preimageSettled[:])

	// Let Carol go offline so we can focus on testing Bob's sweeping
	// behavior.
	ht.Shutdown(carol)

	// Bob should have settled his outgoing HTLC with Carol.
	ht.AssertHTLCNotActive(bob, bcChanPoint, payHashSettled[:])

	// We'll now mine enough blocks to trigger Bob to force close channel
	// Bob->Carol due to his outgoing HTLC is about to timeout. With the
	// default outgoing broadcast delta of zero, this will be the same
	// height as the htlc expiry height.
	numBlocks := padCLTV(uint32(
		invoiceReqHold.CltvExpiry - lncfg.DefaultOutgoingBroadcastDelta,
	))
	ht.MineBlocks(int(numBlocks))

	// Before we mine empty blocks to check the RBF behavior, we need to be
	// aware that Bob's incoming HTLC will expire before his outgoing HTLC
	// deadline is reached. This happens because the incoming HTLC is sent
	// onchain at CLTVDelta-BroadcastDelta=18-10=8, which means after 8
	// blocks are mined, we expect Bob force closes the channel Alice->Bob.
	blocksTillIncomingSweep := cltvDelta -
		lncfg.DefaultIncomingBroadcastDelta

	// Bob should now have two pending sweeps, one for the anchor on the
	// local commitment, the other on the remote commitment.
	ht.AssertNumPendingSweeps(bob, 2)

	// Assert Bob's force closing tx has been broadcast.
	ht.AssertNumTxsInMempool(1)

	// Mine the force close tx, which triggers Bob's contractcourt to offer
	// his outgoing HTLC to his sweeper.
	//
	// NOTE: HTLC outputs are only offered to sweeper when the force close
	// tx is confirmed and the CSV has reached.
	ht.MineBlocksAndAssertNumTxes(1, 1)

	// Update the blocks left till Bob force closes Alice->Bob.
	blocksTillIncomingSweep--

	// Bob should have two pending sweeps, one for the anchor sweeping, the
	// other for the outgoing HTLC.
	ht.AssertNumPendingSweeps(bob, 2)

	// Mine one block to confirm Bob's anchor sweeping tx, which will
	// trigger his sweeper to publish the HTLC sweeping tx.
	ht.MineBlocksAndAssertNumTxes(1, 1)

	// Update the blocks left till Bob force closes Alice->Bob.
	blocksTillIncomingSweep--

	// Bob should now have one sweep and one sweeping tx in the mempool.
	ht.AssertNumPendingSweeps(bob, 1)
	outgoingSweep := ht.GetNumTxsFromMempool(1)[0]

	// Check the shape of the sweeping tx - we expect it to be
	// 2-input-2-output as a wallet utxo is used and a required output is
	// made.
	require.Len(ht, outgoingSweep.TxIn, 2)
	require.Len(ht, outgoingSweep.TxOut, 2)

	// Calculate the ending fee rate.
	//
	// TODO(yy): the budget we use to sweep the first-level outgoing HTLC
	// is twice its value. This is a temporary mitigation to prevent
	// cascading FCs and the test should be updated once it's properly
	// fixed.
	outgoingBudget := 2 * invoiceAmt
	outgoingTxSize := ht.CalculateTxWeight(outgoingSweep)
	outgoingEndFeeRate := chainfee.NewSatPerKWeight(
		outgoingBudget, outgoingTxSize,
	)

	// Assert the initial sweeping tx is using the start fee rate.
	outgoingStartFeeRate := ht.CalculateTxFeeRate(outgoingSweep)
	require.InEpsilonf(ht, uint64(startFeeRate1),
		uint64(outgoingStartFeeRate), 0.01, "want %d, got %d",
		startFeeRate1, outgoingStartFeeRate)

	// Now the start fee rate is checked, we can calculate the fee rate
	// delta.
	outgoingFeeRateDelta := (outgoingEndFeeRate - outgoingStartFeeRate) /
		chainfee.SatPerKWeight(outgoingHTLCDeadline-1)

	// outgoingFuncPosition records the position of Bob's fee function used
	// for his outgoing HTLC sweeping tx.
	outgoingFuncPosition := int32(0)

	// assertSweepFeeRate is a helper closure that asserts the expected fee
	// rate is used at the given position for a sweeping tx.
	assertSweepFeeRate := func(sweepTx *wire.MsgTx,
		startFeeRate, delta chainfee.SatPerKWeight,
		txSize lntypes.WeightUnit,
		deadline, position int32, desc string) {

		// Bob's HTLC sweeping tx should be fee bumped.
		feeRate := ht.CalculateTxFeeRate(sweepTx)
		expectedFeeRate := startFeeRate + delta*chainfee.SatPerKWeight(
			position,
		)

		ht.Logf("Bob's %s HTLC (deadline=%v): txWeight=%v, want "+
			"feerate=%v, got feerate=%v, delta=%v", desc,
			deadline-position, txSize, expectedFeeRate,
			feeRate, delta)

		require.InEpsilonf(ht, uint64(expectedFeeRate), uint64(feeRate),
			0.01, "want %v, got %v in tx=%v", expectedFeeRate,
			feeRate, sweepTx.TxHash())
	}

	// We now mine enough blocks to trigger Bob to force close channel
	// Alice->Bob. Along the way, we will check his outgoing HTLC sweeping
	// tx is RBFed as expected.
	for i := 0; i < blocksTillIncomingSweep-1; i++ {
		// Mine an empty block. Since the sweeping tx is not confirmed,
		// Bob's fee bumper should increase its fees.
		ht.MineEmptyBlocks(1)

		// Update Bob's fee function position.
		outgoingFuncPosition++

		// We should see Bob's sweeping tx in the mempool.
		ht.AssertNumTxsInMempool(1)

		// Make sure Bob's old sweeping tx has been removed from the
		// mempool.
		ht.AssertTxNotInMempool(outgoingSweep.TxHash())

		// Bob should still have the outgoing HTLC sweep.
		ht.AssertNumPendingSweeps(bob, 1)

		// We should see Bob's replacement tx in the mempool.
		outgoingSweep = ht.GetNumTxsFromMempool(1)[0]

		// Bob's outgoing HTLC sweeping tx should be fee bumped.
		assertSweepFeeRate(
			outgoingSweep, outgoingStartFeeRate,
			outgoingFeeRateDelta, outgoingTxSize,
			outgoingHTLCDeadline, outgoingFuncPosition, "Outgoing",
		)
	}

	// Once exited the above loop and mine one more block, we'd have mined
	// enough blocks to trigger Bob to force close his channel with Alice.
	ht.MineEmptyBlocks(1)

	// Update Bob's fee function position.
	outgoingFuncPosition++

	// Bob should now have three pending sweeps:
	// 1. the outgoing HTLC output.
	// 2. the anchor output from his local commitment.
	// 3. the anchor output from his remote commitment.
	ht.AssertNumPendingSweeps(bob, 3)

	// We should see two txns in the mempool:
	// 1. Bob's outgoing HTLC sweeping tx.
	// 2. Bob's force close tx for Alice->Bob.
	txns := ht.GetNumTxsFromMempool(2)

	// Find the force close tx - we expect it to have a single input.
	closeTx := txns[0]
	if len(closeTx.TxIn) != 1 {
		closeTx = txns[1]
	}

	// We don't care the behavior of the anchor sweep in this test, so we
	// mine the force close tx to trigger Bob's contractcourt to offer his
	// incoming HTLC to his sweeper.
	ht.MineBlockWithTx(closeTx)

	// Update Bob's fee function position.
	outgoingFuncPosition++

	// Bob should now have three pending sweeps:
	// 1. the outgoing HTLC output on Bob->Carol.
	// 2. the incoming HTLC output on Alice->Bob.
	// 3. the anchor sweeping on Alice-> Bob.
	ht.AssertNumPendingSweeps(bob, 3)

	// Mine one block, which will trigger his sweeper to publish his
	// incoming HTLC sweeping tx.
	ht.MineEmptyBlocks(1)

	// Update the fee function's positions.
	outgoingFuncPosition++

	// We should see three txns in the mempool:
	// 1. the outgoing HTLC sweeping tx.
	// 2. the incoming HTLC sweeping tx.
	// 3. the anchor sweeping tx.
	txns = ht.GetNumTxsFromMempool(3)

	abCloseTxid := closeTx.TxHash()

	// Identify the sweeping txns spent from Alice->Bob.
	txns = ht.FindSweepingTxns(txns, 2, abCloseTxid)

	// Identify the anchor and incoming HTLC sweeps - if the tx has 1
	// output, then it's the anchor sweeping tx.
	var incomingSweep, anchorSweep = txns[0], txns[1]
	if len(anchorSweep.TxOut) != 1 {
		incomingSweep, anchorSweep = anchorSweep, incomingSweep
	}

	// Calculate the ending fee rate for the incoming HTLC sweep.
	incomingBudget := invoiceAmt.MulF64(contractcourt.DefaultBudgetRatio)
	incomingTxSize := ht.CalculateTxWeight(incomingSweep)
	incomingEndFeeRate := chainfee.NewSatPerKWeight(
		incomingBudget, incomingTxSize,
	)

	// Assert the initial sweeping tx is using the start fee rate.
	incomingStartFeeRate := ht.CalculateTxFeeRate(incomingSweep)
	require.InEpsilonf(ht, uint64(startFeeRate2),
		uint64(incomingStartFeeRate), 0.01, "want %d, got %d in tx=%v",
		startFeeRate2, incomingStartFeeRate, incomingSweep.TxHash())

	// Now the start fee rate is checked, we can calculate the fee rate
	// delta.
	incomingFeeRateDelta := (incomingEndFeeRate - incomingStartFeeRate) /
		chainfee.SatPerKWeight(incomingHTLCDeadline-1)

	// incomingFuncPosition records the position of Bob's fee function used
	// for his incoming HTLC sweeping tx.
	incomingFuncPosition := int32(0)

	// Mine the anchor sweeping tx to reduce noise in this test.
	ht.MineBlockWithTx(anchorSweep)

	// Update the fee function's positions.
	outgoingFuncPosition++
	incomingFuncPosition++

	// identifySweepTxns is a helper closure that identifies the incoming
	// and outgoing HTLC sweeping txns. It always assumes there are two
	// sweeping txns in the mempool, and returns the incoming HTLC sweep
	// first.
	identifySweepTxns := func() (*wire.MsgTx, *wire.MsgTx) {
		// We should see two txns in the mempool:
		// 1. the outgoing HTLC sweeping tx.
		// 2. the incoming HTLC sweeping tx.
		txns = ht.GetNumTxsFromMempool(2)

		var incoming, outgoing *wire.MsgTx

		// The sweeping tx has two inputs, one from wallet, the other
		// from the force close tx. We now check whether the first tx
		// spends from the force close tx of Alice->Bob.
		found := fn.Any(func(inp *wire.TxIn) bool {
			return inp.PreviousOutPoint.Hash == abCloseTxid
		}, txns[0].TxIn)

		// If the first tx spends an outpoint from the force close tx
		// of Alice->Bob, then it must be the incoming HTLC sweeping
		// tx.
		if found {
			incoming, outgoing = txns[0], txns[1]
		} else {
			// Otherwise the second tx must be the incoming HTLC
			// sweep.
			incoming, outgoing = txns[1], txns[0]
		}

		return incoming, outgoing
	}

	//nolint:lll
	// For neutrino backend, we need to give it more time to sync the
	// blocks. There's a potential bug we need to fix:
	// 2024-04-18 23:36:07.046 [ERR] NTFN: unable to get missed blocks: starting height 487 is greater than ending height 486
	//
	// TODO(yy): investigate and fix it.
	time.Sleep(10 * time.Second)

	// We should see Bob's sweeping txns in the mempool.
	incomingSweep, outgoingSweep = identifySweepTxns()

	// We now mine enough blocks till we reach the end of the outgoing
	// HTLC's deadline. Along the way, we check the expected fee rates are
	// used for both incoming and outgoing HTLC sweeping txns.
	//
	// NOTE: We need to subtract 1 from the deadline as the budget must be
	// used up before the deadline.
	blocksLeft := outgoingHTLCDeadline - outgoingFuncPosition - 1
	for i := int32(0); i < blocksLeft; i++ {
		// Mine an empty block.
		ht.MineEmptyBlocks(1)

		// Update Bob's fee function position.
		outgoingFuncPosition++
		incomingFuncPosition++

		// We should see two txns in the mempool,
		// - the incoming HTLC sweeping tx.
		// - the outgoing HTLC sweeping tx.
		ht.AssertNumTxsInMempool(2)

		// Make sure Bob's old sweeping txns have been removed from the
		// mempool.
		ht.AssertTxNotInMempool(outgoingSweep.TxHash())
		ht.AssertTxNotInMempool(incomingSweep.TxHash())

		// Bob should have two pending sweeps:
		// 1. the outgoing HTLC output on Bob->Carol.
		// 2. the incoming HTLC output on Alice->Bob.
		ht.AssertNumPendingSweeps(bob, 2)

		// We should see Bob's replacement txns in the mempool.
		incomingSweep, outgoingSweep = identifySweepTxns()

		// Bob's outgoing HTLC sweeping tx should be fee bumped.
		assertSweepFeeRate(
			outgoingSweep, outgoingStartFeeRate,
			outgoingFeeRateDelta, outgoingTxSize,
			outgoingHTLCDeadline, outgoingFuncPosition, "Outgoing",
		)

		// Bob's incoming HTLC sweeping tx should be fee bumped.
		assertSweepFeeRate(
			incomingSweep, incomingStartFeeRate,
			incomingFeeRateDelta, incomingTxSize,
			incomingHTLCDeadline, incomingFuncPosition, "Incoming",
		)
	}

	// Mine an empty block.
	ht.MineEmptyBlocks(1)

	// We should see Bob's old txns in the mempool.
	currentIncomingSweep, currentOutgoingSweep := identifySweepTxns()
	require.Equal(ht, incomingSweep.TxHash(), currentIncomingSweep.TxHash())
	require.Equal(ht, outgoingSweep.TxHash(), currentOutgoingSweep.TxHash())

	// Mine a block to confirm the HTLC sweeps.
	ht.MineBlocksAndAssertNumTxes(1, 2)
}

// testSweepCommitOutputAndAnchor checks when a channel is force closed without
// any time-sensitive HTLCs, the anchor output is swept without any CPFP
// attempts. In addition, the to_local output should be swept using the
// specified deadline and budget.
//
// Setup:
//  1. Fund Alice with 1 UTXOs - she only needs one for the funding process,
//     and no wallet utxos are needed for her sweepings.
//  2. Fund Bob with no UTXOs - his sweeping txns don't need wallet utxos as he
//     doesn't need to sweep any time-sensitive outputs.
//  3. Alice opens a channel with Bob, and successfully sends him an HTLC.
//  4. Alice force closes the channel.
//
// Test:
//  1. Alice's anchor sweeping is not attempted, instead, it should be swept
//     together with her to_local output using the no deadline path.
//  2. Bob would also sweep his anchor and to_local outputs in a single
//     sweeping tx using the no deadline path.
//  3. Both Alice and Bob's RBF attempts are using the fee rates calculated
//     from the deadline and budget.
//  4. Wallet UTXOs requirements are met - neither Alice nor Bob needs wallet
//     utxos to finish their sweeps.
func testSweepCommitOutputAndAnchor(ht *lntest.HarnessTest) {
	// Setup testing params for Alice.
	//
	// deadline is the expected deadline when sweeping the anchor and
	// to_local output. We will use a customized deadline to test the
	// config.
	deadline := uint32(1000)

	// The actual deadline used by the fee function will be one block off
	// from the deadline configured as we require one block to be mined to
	// trigger the sweep.
	deadlineA, deadlineB := deadline-1, deadline-1

	// startFeeRate is returned by the fee estimator in sat/kw. This
	// will be used as the starting fee rate for the linear fee func used
	// by Alice. Since there are no time-sensitive HTLCs, Alice's sweeper
	// should start with the above default deadline, which will result in
	// the min relay fee rate being used since it's >= MaxBlockTarget.
	startFeeRate := chainfee.FeePerKwFloor

	// Set up the fee estimator to return the testing fee rate when the
	// conf target is the deadline.
	ht.SetFeeEstimateWithConf(startFeeRate, deadlineA)

	// toLocalCSV is the CSV delay for Alice's to_local output. We use a
	// small value to save us from mining blocks.
	//
	// NOTE: once the force close tx is confirmed, we expect anchor
	// sweeping starts. Then two more block later the commit output
	// sweeping starts.
	//
	// NOTE: The CSV value is chosen to be 3 instead of 2, to reduce the
	// possibility of flakes as there is a race between the two goroutines:
	// G1 - Alice's sweeper receives the commit output.
	// G2 - Alice's sweeper receives the new block mined.
	// G1 is triggered by the same block being received by Alice's
	// contractcourt, deciding the commit output is mature and offering it
	// to her sweeper. Normally, we'd expect G2 to be finished before G1
	// because it's the same block processed by both contractcourt and
	// sweeper. However, if G2 is delayed (maybe the sweeper is slow in
	// finishing its previous round), G1 may finish before G2. This will
	// cause the sweeper to add the commit output to its pending inputs,
	// and once G2 fires, it will then start sweeping this output,
	// resulting a valid sweep tx being created using her commit and anchor
	// outputs.
	//
	// TODO(yy): fix the above issue by making sure subsystems share the
	// same view on current block height.
	toLocalCSV := 3

	// htlcAmt is the amount of the HTLC in sats, this should be Alice's
	// to_remote amount that goes to Bob.
	htlcAmt := int64(100_000)

	// fundAmt is the funding amount.
	fundAmt := btcutil.Amount(1_000_000)

	// We now set up testing params for Bob.
	//
	// bobBalance is the push amount when Alice opens the channel with Bob.
	// We will use zero here so Bob's balance equals to the htlc amount by
	// the time Alice force closes.
	bobBalance := btcutil.Amount(0)

	// We now set up the force close scenario. Alice will open a channel
	// with Bob, send an HTLC, Bob settles it, and then Alice force closes
	// the channel without any pending HTLCs.
	//
	// Prepare node params.
	cfg := []string{
		"--protocol.anchors",
		fmt.Sprintf("--sweeper.nodeadlineconftarget=%v", deadline),
		fmt.Sprintf("--bitcoin.defaultremotedelay=%v", toLocalCSV),
	}
	cfgs := [][]string{cfg, cfg}

	openChannelParams := lntest.OpenChannelParams{
		Amt:     fundAmt,
		PushAmt: bobBalance,
	}

	// Create a two hop network: Alice -> Bob.
	chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, openChannelParams)

	// Unwrap the results.
	chanPoint := chanPoints[0]
	alice, bob := nodes[0], nodes[1]

	invoice := &lnrpc.Invoice{
		Memo:       "bob",
		Value:      htlcAmt,
		CltvExpiry: finalCltvDelta,
	}
	resp := bob.RPC.AddInvoice(invoice)

	// Send a payment with a specified finalCTLVDelta, and assert it's
	// succeeded.
	req := &routerrpc.SendPaymentRequest{
		PaymentRequest: resp.PaymentRequest,
		TimeoutSeconds: 60,
		FeeLimitMsat:   noFeeLimitMsat,
	}
	ht.SendPaymentAssertSettled(alice, req)

	// Assert Alice's to_remote (Bob's to_local) output is the htlc amount.
	ht.AssertChannelLocalBalance(bob, chanPoint, htlcAmt)
	bobToLocal := htlcAmt

	// Get Alice's channel to calculate Alice's to_local output amount.
	aliceChan := ht.GetChannelByChanPoint(alice, chanPoint)
	expectedToLocal := int64(fundAmt) - aliceChan.CommitFee - htlcAmt -
		330*2

	// Assert Alice's to_local output is correct.
	aliceToLocal := aliceChan.LocalBalance
	require.EqualValues(ht, expectedToLocal, aliceToLocal)

	// Alice force closes the channel.
	ht.CloseChannelAssertPending(alice, chanPoint, true)

	// Now that the channel has been force closed, it should show up in the
	// PendingChannels RPC under the waiting close section.
	ht.AssertChannelWaitingClose(alice, chanPoint)

	// Alice should see 2 anchor sweeps for the local and remote commitment.
	// Even without HTLCs at stake the anchors are registered with the
	// sweeper subsytem.
	ht.AssertNumPendingSweeps(alice, 2)

	// Bob did not force close the channel therefore he should have no
	// pending sweeps.
	ht.AssertNumPendingSweeps(bob, 0)

	// Mine a block to confirm Alice's force closing tx. Once it's
	// confirmed, we should see both Alice and Bob's anchors being offered
	// to their sweepers.
	ht.MineBlocksAndAssertNumTxes(1, 1)

	// Alice should have one pending sweep,
	// - anchor sweeping from her local commitment.
	ht.AssertNumPendingSweeps(alice, 1)

	// Bob should have two pending sweeps,
	// - anchor sweeping from the remote anchor on Alice's commit tx.
	// - commit sweeping from the to_remote on Alice's commit tx.
	ht.AssertNumPendingSweeps(bob, 2)

	// Mine one more empty block should trigger Bob's sweeping. Since we
	// use a CSV of 3, this means Alice's to_local output is one block away
	// from being mature.
	ht.MineEmptyBlocks(1)

	// We expect to see one sweeping tx in the mempool:
	// - Alice's anchor sweeping tx must have been failed due to the fee
	//   rate chosen in this test - the anchor sweep tx has no output.
	// - Bob's sweeping tx, which sweeps both his anchor and commit outputs.
	bobSweepTx := ht.GetNumTxsFromMempool(1)[0]

	// We expect two pending sweeps for Bob - anchor and commit outputs.
	pendingSweepBob := ht.AssertNumPendingSweeps(bob, 2)[0]

	// The sweeper may be one block behind contractcourt, so we double
	// check the actual deadline.
	//
	// TODO(yy): assert they are equal once blocks are synced via
	// `blockbeat`.
	currentHeight := int32(ht.CurrentHeight())
	actualDeadline := int32(pendingSweepBob.DeadlineHeight) - currentHeight
	if actualDeadline != int32(deadlineB) {
		ht.Logf("!!! Found unsynced block between sweeper and "+
			"contractcourt, expected deadline=%v, got=%v",
			deadlineB, actualDeadline)

		deadlineB = uint32(actualDeadline)
	}

	// Alice should still have one pending sweep - the anchor output.
	ht.AssertNumPendingSweeps(alice, 1)

	// We now check Bob's sweeping tx.
	//
	// Bob's sweeping tx should have 2 inputs, one from his commit output,
	// the other from his anchor output.
	require.Len(ht, bobSweepTx.TxIn, 2)

	// Because Bob is sweeping without deadline pressure, the starting fee
	// rate should be the min relay fee rate.
	bobStartFeeRate := ht.CalculateTxFeeRate(bobSweepTx)
	require.InEpsilonf(ht, uint64(chainfee.FeePerKwFloor),
		uint64(bobStartFeeRate), 0.01, "want %v, got %v",
		chainfee.FeePerKwFloor, bobStartFeeRate)

	// With Bob's starting fee rate being validated, we now calculate his
	// ending fee rate and fee rate delta.
	//
	// Bob sweeps two inputs - anchor and commit, so the starting budget
	// should come from the sum of these two.
	bobValue := btcutil.Amount(bobToLocal + 330)
	bobBudget := bobValue.MulF64(contractcourt.DefaultBudgetRatio)

	// Calculate the ending fee rate and fee rate delta used in his fee
	// function.
	bobTxWeight := ht.CalculateTxWeight(bobSweepTx)
	bobEndingFeeRate := chainfee.NewSatPerKWeight(bobBudget, bobTxWeight)
	bobFeeRateDelta := (bobEndingFeeRate - bobStartFeeRate) /
		chainfee.SatPerKWeight(deadlineB-1)

	// Mine an empty block, which should trigger Alice's contractcourt to
	// offer her commit output to the sweeper.
	ht.MineEmptyBlocks(1)

	// Alice should have both anchor and commit as the pending sweep
	// requests.
	aliceSweeps := ht.AssertNumPendingSweeps(alice, 2)
	aliceAnchor, aliceCommit := aliceSweeps[0], aliceSweeps[1]
	if aliceAnchor.AmountSat > aliceCommit.AmountSat {
		aliceAnchor, aliceCommit = aliceCommit, aliceAnchor
	}

	// The sweeper may be one block behind contractcourt, so we double
	// check the actual deadline.
	//
	// TODO(yy): assert they are equal once blocks are synced via
	// `blockbeat`.
	currentHeight = int32(ht.CurrentHeight())
	actualDeadline = int32(aliceCommit.DeadlineHeight) - currentHeight
	if actualDeadline != int32(deadlineA) {
		ht.Logf("!!! Found unsynced block between Alice's sweeper and "+
			"contractcourt, expected deadline=%v, got=%v",
			deadlineA, actualDeadline)

		deadlineA = uint32(actualDeadline)
	}

	// We now wait for 30 seconds to overcome the flake - there's a block
	// race between contractcourt and sweeper, causing the sweep to be
	// broadcast earlier.
	//
	// TODO(yy): remove this once `blockbeat` is in place.
	aliceStartPosition := 0
	var aliceFirstSweepTx *wire.MsgTx
	err := wait.NoError(func() error {
		mem := ht.GetRawMempool()
		if len(mem) != 2 {
			return fmt.Errorf("want 2, got %v in mempool: %v",
				len(mem), mem)
		}

		// If there are two txns, it means Alice's sweep tx has been
		// created and published.
		aliceStartPosition = 1

		txns := ht.GetNumTxsFromMempool(2)
		aliceFirstSweepTx = txns[0]

		// Reassign if the second tx is larger.
		if txns[1].TxOut[0].Value > aliceFirstSweepTx.TxOut[0].Value {
			aliceFirstSweepTx = txns[1]
		}

		return nil
	}, wait.DefaultTimeout)
	ht.Logf("Checking mempool got: %v", err)

	// Mine an empty block, which should trigger Alice's sweeper to publish
	// her commit sweep along with her anchor output.
	ht.MineEmptyBlocks(1)

	// If Alice has already published her initial sweep tx, the above mined
	// block would trigger an RBF. We now need to assert the mempool has
	// removed the replaced tx.
	if aliceFirstSweepTx != nil {
		ht.AssertTxNotInMempool(aliceFirstSweepTx.TxHash())
	}

	// We also remember the positions of fee functions used by Alice and
	// Bob. They will be used to calculate the expected fee rates later.
	//
	// Alice's sweeping tx has just been created, so she is at the starting
	// position. For Bob, due to the above mined blocks, his fee function
	// is now at position 2.
	alicePosition, bobPosition := uint32(aliceStartPosition), uint32(2)

	// We should see two txns in the mempool:
	// - Alice's sweeping tx, which sweeps her commit output at the
	//   starting fee rate - Alice's anchor output won't be swept with her
	//   commit output together because they have different deadlines.
	// - Bob's previous sweeping tx, which sweeps both his anchor and
	//   commit outputs, at the starting fee rate.
	txns := ht.GetNumTxsFromMempool(2)

	// Assume the first tx is Alice's sweeping tx, if the second tx has a
	// larger output value, then that's Alice's as her to_local value is
	// much gearter.
	aliceSweepTx := txns[0]
	bobSweepTx = txns[1]

	// Swap them if bobSweepTx is smaller.
	if bobSweepTx.TxOut[0].Value > aliceSweepTx.TxOut[0].Value {
		aliceSweepTx, bobSweepTx = bobSweepTx, aliceSweepTx
	}

	// We now check Alice's sweeping tx.
	//
	// Alice's sweeping tx should have a shape of 1-in-1-out since it's not
	// used for CPFP, so it shouldn't take any wallet utxos.
	require.Len(ht, aliceSweepTx.TxIn, 1)
	require.Len(ht, aliceSweepTx.TxOut, 1)

	// We now check Alice's sweeping tx to see if it's already published.
	//
	// TODO(yy): remove this check once we have better block control.
	aliceSweeps = ht.AssertNumPendingSweeps(alice, 2)
	aliceCommit = aliceSweeps[0]
	if aliceCommit.AmountSat < aliceSweeps[1].AmountSat {
		aliceCommit = aliceSweeps[1]
	}
	if aliceCommit.BroadcastAttempts > 1 {
		ht.Logf("!!! Alice's commit sweep has already been broadcast, "+
			"broadcast_attempts=%v", aliceCommit.BroadcastAttempts)
		alicePosition = aliceCommit.BroadcastAttempts
	}

	// Alice's sweeping tx should use the min relay fee rate as there's no
	// deadline pressure.
	aliceStartingFeeRate := chainfee.FeePerKwFloor

	// With Alice's starting fee rate being validated, we now calculate her
	// ending fee rate and fee rate delta.
	//
	// Alice sweeps two inputs - anchor and commit, so the starting budget
	// should come from the sum of these two. However, due to the value
	// being too large, the actual ending fee rate used should be the
	// sweeper's max fee rate configured.
	aliceTxWeight := uint64(ht.CalculateTxWeight(aliceSweepTx))
	aliceEndingFeeRate := sweep.DefaultMaxFeeRate.FeePerKWeight()
	aliceFeeRateDelta := (aliceEndingFeeRate - aliceStartingFeeRate) /
		chainfee.SatPerKWeight(deadlineA-1)

	aliceFeeRate := ht.CalculateTxFeeRate(aliceSweepTx)
	expectedFeeRateAlice := aliceStartingFeeRate +
		aliceFeeRateDelta*chainfee.SatPerKWeight(alicePosition)
	require.InEpsilonf(ht, uint64(expectedFeeRateAlice),
		uint64(aliceFeeRate), 0.02, "want %v, got %v",
		expectedFeeRateAlice, aliceFeeRate)

	// We now check Bob' sweeping tx.
	//
	// The above mined block will trigger Bob's sweeper to RBF his previous
	// sweeping tx, which will fail due to RBF rule#4 - the additional fees
	// paid are not sufficient. This happens as our default incremental
	// relay fee rate is 1 sat/vb, with the tx size of 771 weight units, or
	// 192 vbytes, we need to pay at least 192 sats more to be able to RBF.
	// However, since Bob's budget delta is (100_000 + 330) * 0.5 / 1008 =
	// 49.77 sats, it means Bob can only perform a successful RBF every 4
	// blocks.
	//
	// Assert Bob's sweeping tx is not RBFed.
	bobFeeRate := ht.CalculateTxFeeRate(bobSweepTx)
	expectedFeeRateBob := bobStartFeeRate
	require.InEpsilonf(ht, uint64(expectedFeeRateBob), uint64(bobFeeRate),
		0.01, "want %d, got %d", expectedFeeRateBob, bobFeeRate)

	// reloclateAlicePosition is a temp hack to find the actual fee
	// function position used for Alice. Due to block sync issue among the
	// subsystems, we can end up having this situation:
	// - sweeper is at block 2, starts sweeping an input with deadline 100.
	// - fee bumper is at block 1, and thinks the conf target is 99.
	// - new block 3 arrives, the func now is at position 2.
	//
	// TODO(yy): fix it using `blockbeat`.
	reloclateAlicePosition := func() {
		// Mine an empty block to trigger the possible RBF attempts.
		ht.MineEmptyBlocks(1)

		// Increase the positions for both fee functions.
		alicePosition++
		bobPosition++

		// We expect two pending sweeps for both nodes as we are mining
		// empty blocks.
		ht.AssertNumPendingSweeps(alice, 2)
		ht.AssertNumPendingSweeps(bob, 2)

		// We expect to see both Alice's and Bob's sweeping txns in the
		// mempool.
		ht.AssertNumTxsInMempool(2)

		// Make sure Alice's old sweeping tx has been removed from the
		// mempool.
		ht.AssertTxNotInMempool(aliceSweepTx.TxHash())

		// We should see two txns in the mempool:
		// - Alice's sweeping tx, which sweeps both her anchor and
		//   commit outputs, using the increased fee rate.
		// - Bob's previous sweeping tx, which sweeps both his anchor
		//   and commit outputs, at the possible increased fee rate.
		txns = ht.GetNumTxsFromMempool(2)

		// Assume the first tx is Alice's sweeping tx, if the second tx
		// has a larger output value, then that's Alice's as her
		// to_local value is much gearter.
		aliceSweepTx = txns[0]
		bobSweepTx = txns[1]

		// Swap them if bobSweepTx is smaller.
		if bobSweepTx.TxOut[0].Value > aliceSweepTx.TxOut[0].Value {
			aliceSweepTx, bobSweepTx = bobSweepTx, aliceSweepTx
		}

		// Alice's sweeping tx should be increased.
		aliceFeeRate := ht.CalculateTxFeeRate(aliceSweepTx)
		expectedFeeRate := aliceStartingFeeRate +
			aliceFeeRateDelta*chainfee.SatPerKWeight(alicePosition)

		ht.Logf("Alice(deadline=%v): txWeight=%v, want feerate=%v, "+
			"got feerate=%v, delta=%v", deadlineA-alicePosition,
			aliceTxWeight, expectedFeeRate, aliceFeeRate,
			aliceFeeRateDelta)

		nextPosition := alicePosition + 1
		nextFeeRate := aliceStartingFeeRate +
			aliceFeeRateDelta*chainfee.SatPerKWeight(nextPosition)

		// Calculate the distances.
		delta := math.Abs(float64(aliceFeeRate - expectedFeeRate))
		deltaNext := math.Abs(float64(aliceFeeRate - nextFeeRate))

		// Exit early if the first distance is smaller - it means we
		// are at the right fee func position.
		if delta < deltaNext {
			require.InEpsilonf(ht, uint64(expectedFeeRate),
				uint64(aliceFeeRate), 0.02, "want %v, got %v "+
					"in tx=%v", expectedFeeRate,
				aliceFeeRate, aliceSweepTx.TxHash())

			return
		}

		alicePosition++
		ht.Logf("Jump position for Alice(deadline=%v): txWeight=%v, "+
			"want feerate=%v, got feerate=%v, delta=%v",
			deadlineA-alicePosition, aliceTxWeight, nextFeeRate,
			aliceFeeRate, aliceFeeRateDelta)

		require.InEpsilonf(ht, uint64(nextFeeRate),
			uint64(aliceFeeRate), 0.02, "want %v, got %v in tx=%v",
			nextFeeRate, aliceFeeRate, aliceSweepTx.TxHash())
	}

	reloclateAlicePosition()

	// We now mine 7 empty blocks. For each block mined, we'd see Alice's
	// sweeping tx being RBFed. For Bob, he performs a fee bump every
	// block, but will only publish a tx every 4 blocks mined as some of
	// the fee bumps is not sufficient to meet the fee requirements
	// enforced by RBF. Since his fee function is already at position 1,
	// mining 7 more blocks means he will RBF his sweeping tx twice.
	for i := 1; i < 7; i++ {
		// Mine an empty block to trigger the possible RBF attempts.
		ht.MineEmptyBlocks(1)

		// Increase the positions for both fee functions.
		alicePosition++
		bobPosition++

		// We expect two pending sweeps for both nodes as we are mining
		// empty blocks.
		ht.AssertNumPendingSweeps(alice, 2)
		ht.AssertNumPendingSweeps(bob, 2)

		// We expect to see both Alice's and Bob's sweeping txns in the
		// mempool.
		ht.AssertNumTxsInMempool(2)

		// Make sure Alice's old sweeping tx has been removed from the
		// mempool.
		ht.AssertTxNotInMempool(aliceSweepTx.TxHash())

		// Make sure Bob's old sweeping tx has been removed from the
		// mempool. Since Bob's sweeping tx will only be successfully
		// RBFed every 4 blocks, his old sweeping tx only will be
		// removed when there are 4 blocks increased.
		if bobPosition%4 == 0 {
			ht.AssertTxNotInMempool(bobSweepTx.TxHash())
		}

		// We should see two txns in the mempool:
		// - Alice's sweeping tx, which sweeps both her anchor and
		//   commit outputs, using the increased fee rate.
		// - Bob's previous sweeping tx, which sweeps both his anchor
		//   and commit outputs, at the possible increased fee rate.
		txns := ht.GetNumTxsFromMempool(2)

		// Assume the first tx is Alice's sweeping tx, if the second tx
		// has a larger output value, then that's Alice's as her
		// to_local value is much gearter.
		aliceSweepTx = txns[0]
		bobSweepTx = txns[1]

		// Swap them if bobSweepTx is smaller.
		if bobSweepTx.TxOut[0].Value > aliceSweepTx.TxOut[0].Value {
			aliceSweepTx, bobSweepTx = bobSweepTx, aliceSweepTx
		}

		// We now check Alice's sweeping tx.
		//
		// Alice's sweeping tx should have a shape of 1-in-1-out since
		// it's not used for CPFP, so it shouldn't take any wallet
		// utxos.
		require.Len(ht, aliceSweepTx.TxIn, 1)
		require.Len(ht, aliceSweepTx.TxOut, 1)

		// Alice's sweeping tx should be increased.
		aliceFeeRate := ht.CalculateTxFeeRate(aliceSweepTx)
		expectedFeeRateAlice := aliceStartingFeeRate +
			aliceFeeRateDelta*chainfee.SatPerKWeight(alicePosition)

		ht.Logf("Alice(deadline=%v): txWeight=%v, want feerate=%v, "+
			"got feerate=%v, delta=%v", deadlineA-alicePosition,
			aliceTxWeight, expectedFeeRateAlice, aliceFeeRate,
			aliceFeeRateDelta)

		require.InEpsilonf(ht, uint64(expectedFeeRateAlice),
			uint64(aliceFeeRate), 0.02, "want %v, got %v in tx=%v",
			expectedFeeRateAlice, aliceFeeRate,
			aliceSweepTx.TxHash())

		// We now check Bob' sweeping tx.
		bobFeeRate := ht.CalculateTxFeeRate(bobSweepTx)

		// accumulatedDelta is the delta that Bob has accumulated so
		// far. This will only be added when there's a successful RBF
		// attempt.
		accumulatedDelta := bobFeeRateDelta *
			chainfee.SatPerKWeight(bobPosition)

		// Bob's sweeping tx will only be successfully RBFed every 4
		// blocks.
		if bobPosition%4 == 0 {
			expectedFeeRateBob = bobStartFeeRate + accumulatedDelta
		}

		ht.Logf("Bob(deadline=%v): txWeight=%v, want feerate=%v, "+
			"got feerate=%v, delta=%v", deadlineB-bobPosition,
			bobTxWeight, expectedFeeRateBob, bobFeeRate,
			bobFeeRateDelta)

		require.InEpsilonf(ht, uint64(expectedFeeRateBob),
			uint64(bobFeeRate), 0.02, "want %d, got %d in tx=%v",
			expectedFeeRateBob, bobFeeRate, bobSweepTx.TxHash())
	}

	// Mine a block to confirm both sweeping txns, this is needed to clean
	// up the mempool.
	ht.MineBlocksAndAssertNumTxes(1, 2)
}

// testBumpFee checks that when a new input is requested, it's first bumped via
// CPFP, then RBF. Along the way, we check the `BumpFee` can properly update
// the fee function used by supplying new params.
func testBumpFee(ht *lntest.HarnessTest) {
	runBumpFee(ht, ht.Alice)
}

// runBumpFee checks the `BumpFee` RPC can properly bump the fee of a given
// input.
func runBumpFee(ht *lntest.HarnessTest, alice *node.HarnessNode) {
	// Skip this test for neutrino, as it's not aware of mempool
	// transactions.
	if ht.IsNeutrinoBackend() {
		ht.Skipf("skipping BumpFee test for neutrino backend")
	}

	// startFeeRate is the min fee rate in sats/vbyte. This value should be
	// used as the starting fee rate when the default no deadline is used.
	startFeeRate := uint64(1)

	// We'll start the test by sending Alice some coins, which she'll use
	// to send to Bob.
	ht.FundCoins(btcutil.SatoshiPerBitcoin, alice)

	// Alice sends a coin to herself.
	tx := ht.SendCoins(alice, alice, btcutil.SatoshiPerBitcoin)
	txid := tx.TxHash()

	// Alice now tries to bump the first output on this tx.
	op := &lnrpc.OutPoint{
		TxidBytes:   txid[:],
		OutputIndex: uint32(0),
	}
	value := btcutil.Amount(tx.TxOut[0].Value)

	// assertPendingSweepResp is a helper closure that asserts the response
	// from `PendingSweep` RPC is returned with expected values. It also
	// returns the sweeping tx for further checks.
	assertPendingSweepResp := func(broadcastAttempts uint32, budget uint64,
		deadline uint32, startingFeeRate uint64) *wire.MsgTx {

		// Alice should still have one pending sweep.
		pendingSweep := ht.AssertNumPendingSweeps(alice, 1)[0]

		// Validate all fields returned from `PendingSweeps` are as
		// expected.
		require.Equal(ht, op.TxidBytes, pendingSweep.Outpoint.TxidBytes)
		require.Equal(ht, op.OutputIndex,
			pendingSweep.Outpoint.OutputIndex)
		require.Equal(ht, walletrpc.WitnessType_TAPROOT_PUB_KEY_SPEND,
			pendingSweep.WitnessType)
		require.EqualValuesf(ht, value, pendingSweep.AmountSat,
			"amount not matched: want=%d, got=%d", value,
			pendingSweep.AmountSat)
		require.True(ht, pendingSweep.Immediate)

		require.Equal(ht, broadcastAttempts,
			pendingSweep.BroadcastAttempts)
		require.EqualValuesf(ht, budget, pendingSweep.Budget,
			"budget not matched: want=%d, got=%d", budget,
			pendingSweep.Budget)

		// Since the request doesn't specify a deadline, we expect the
		// existing deadline to be used.
		require.Equalf(ht, deadline, pendingSweep.DeadlineHeight,
			"deadline height not matched: want=%d, got=%d",
			deadline, pendingSweep.DeadlineHeight)

		// Since the request specifies a starting fee rate, we expect
		// that to be used as the starting fee rate.
		require.Equalf(ht, startingFeeRate,
			pendingSweep.RequestedSatPerVbyte, "requested "+
				"starting fee rate not matched: want=%d, "+
				"got=%d", startingFeeRate,
			pendingSweep.RequestedSatPerVbyte)

		// We expect to see Alice's original tx and her CPFP tx in the
		// mempool.
		txns := ht.GetNumTxsFromMempool(2)

		// Find the sweeping tx - assume it's the first item, if it has
		// the same txid as the parent tx, use the second item.
		sweepTx := txns[0]
		if sweepTx.TxHash() == tx.TxHash() {
			sweepTx = txns[1]
		}

		return sweepTx
	}

	// assertFeeRateEqual is a helper closure that asserts the fee rate of
	// the pending sweep tx is equal to the expected fee rate.
	assertFeeRateEqual := func(expected uint64) {
		err := wait.NoError(func() error {
			// Alice should still have one pending sweep.
			pendingSweep := ht.AssertNumPendingSweeps(alice, 1)[0]

			if pendingSweep.SatPerVbyte == expected {
				return nil
			}

			return fmt.Errorf("expected current fee rate %d, got "+
				"%d", expected, pendingSweep.SatPerVbyte)
		}, wait.DefaultTimeout)
		require.NoError(ht, err, "fee rate not updated")
	}

	// assertFeeRateGreater is a helper closure that asserts the fee rate
	// of the pending sweep tx is greater than the expected fee rate.
	assertFeeRateGreater := func(expected uint64) {
		err := wait.NoError(func() error {
			// Alice should still have one pending sweep.
			pendingSweep := ht.AssertNumPendingSweeps(alice, 1)[0]

			if pendingSweep.SatPerVbyte > expected {
				return nil
			}

			return fmt.Errorf("expected current fee rate greater "+
				"than %d, got %d", expected,
				pendingSweep.SatPerVbyte)
		}, wait.DefaultTimeout)
		require.NoError(ht, err, "fee rate not updated")
	}

	// First bump request - we'll specify nothing except `Immediate` to let
	// the sweeper handle the fee, and we expect a fee func that has,
	// - starting fee rate: 1 sat/vbyte (min relay fee rate).
	// - deadline: 1008 (default deadline).
	// - budget: 50% of the input value.
	bumpFeeReq := &walletrpc.BumpFeeRequest{
		Outpoint: op,
		// We use a force param to create the sweeping tx immediately.
		Immediate: true,
	}
	alice.RPC.BumpFee(bumpFeeReq)

	// Since the request doesn't specify a deadline, we expect the default
	// deadline to be used.
	currentHeight := int32(ht.CurrentHeight())
	deadline := uint32(currentHeight + sweep.DefaultDeadlineDelta)

	// Assert the pending sweep is created with the expected values:
	// - broadcast attempts: 1.
	// - starting fee rate: 1 sat/vbyte (min relay fee rate).
	// - deadline: 1008 (default deadline).
	// - budget: 50% of the input value.
	sweepTx1 := assertPendingSweepResp(1, uint64(value/2), deadline, 0)

	// Since the request doesn't specify a starting fee rate, we expect the
	// min relay fee rate is used as the current fee rate.
	assertFeeRateEqual(startFeeRate)

	// testFeeRate sepcifies a starting fee rate in sat/vbyte.
	const testFeeRate = uint64(100)

	// Second bump request - we will specify the fee rate and expect a fee
	// func that has,
	// - starting fee rate: 100 sat/vbyte.
	// - deadline: 1008 (default deadline).
	// - budget: 50% of the input value.
	bumpFeeReq = &walletrpc.BumpFeeRequest{
		Outpoint: op,
		// We use a force param to create the sweeping tx immediately.
		Immediate:   true,
		SatPerVbyte: testFeeRate,
	}
	alice.RPC.BumpFee(bumpFeeReq)

	// Alice's old sweeping tx should be replaced.
	ht.AssertTxNotInMempool(sweepTx1.TxHash())

	// Assert the pending sweep is created with the expected values:
	// - broadcast attempts: 2.
	// - starting fee rate: 100 sat/vbyte.
	// - deadline: 1008 (default deadline).
	// - budget: 50% of the input value.
	sweepTx2 := assertPendingSweepResp(
		2, uint64(value/2), deadline, testFeeRate,
	)

	// We expect the requested starting fee rate to be the current fee
	// rate.
	assertFeeRateEqual(testFeeRate)

	// testBudget specifies a budget in sats.
	testBudget := uint64(float64(value) * 0.1)

	// Third bump request - we will specify the budget and expect a fee
	// func that has,
	// - starting fee rate: 100 sat/vbyte, stays unchanged.
	// - deadline: 1008 (default deadline).
	// - budget: 10% of the input value.
	bumpFeeReq = &walletrpc.BumpFeeRequest{
		Outpoint: op,
		// We use a force param to create the sweeping tx immediately.
		Immediate: true,
		Budget:    testBudget,
	}
	alice.RPC.BumpFee(bumpFeeReq)

	// Alice's old sweeping tx should be replaced.
	ht.AssertTxNotInMempool(sweepTx2.TxHash())

	// Assert the pending sweep is created with the expected values:
	// - broadcast attempts: 3.
	// - starting fee rate: 100 sat/vbyte, stays unchanged.
	// - deadline: 1008 (default deadline).
	// - budget: 10% of the input value.
	sweepTx3 := assertPendingSweepResp(3, testBudget, deadline, 0)

	// We expect the current fee rate to be increased because we ensure the
	// initial broadcast always succeeds.
	assertFeeRateGreater(testFeeRate)

	// Create a test deadline delta to use in the next test.
	testDeadlineDelta := uint32(100)
	deadlineHeight := uint32(currentHeight) + testDeadlineDelta

	// Fourth bump request - we will specify the deadline and expect a fee
	// func that has,
	// - starting fee rate: 100 sat/vbyte, stays unchanged.
	// - deadline: 100.
	// - budget: 10% of the input value, stays unchanged.
	bumpFeeReq = &walletrpc.BumpFeeRequest{
		Outpoint: op,
		// We use a force param to create the sweeping tx immediately.
		Immediate:  true,
		TargetConf: testDeadlineDelta,
	}
	alice.RPC.BumpFee(bumpFeeReq)

	// Alice's old sweeping tx should be replaced.
	ht.AssertTxNotInMempool(sweepTx3.TxHash())

	// Assert the pending sweep is created with the expected values:
	// - broadcast attempts: 4.
	// - starting fee rate: 100 sat/vbyte, stays unchanged.
	// - deadline: 100.
	// - budget: 10% of the input value, stays unchanged.
	sweepTx4 := assertPendingSweepResp(4, testBudget, deadlineHeight, 0)

	// We expect the current fee rate to be increased because we ensure the
	// initial broadcast always succeeds.
	assertFeeRateGreater(testFeeRate)

	// Fifth bump request - we test the behavior of `Immediate` - every
	// time it's called, the fee function will keep increasing the fee rate
	// until the broadcast can succeed. The fee func that has,
	// - starting fee rate: 100 sat/vbyte, stays unchanged.
	// - deadline: 100, stays unchanged.
	// - budget: 10% of the input value, stays unchanged.
	bumpFeeReq = &walletrpc.BumpFeeRequest{
		Outpoint: op,
		// We use a force param to create the sweeping tx immediately.
		Immediate: true,
	}
	alice.RPC.BumpFee(bumpFeeReq)

	// Alice's old sweeping tx should be replaced.
	ht.AssertTxNotInMempool(sweepTx4.TxHash())

	// Assert the pending sweep is created with the expected values:
	// - broadcast attempts: 5.
	// - starting fee rate: 100 sat/vbyte, stays unchanged.
	// - deadline: 100, stays unchanged.
	// - budget: 10% of the input value, stays unchanged.
	sweepTx5 := assertPendingSweepResp(5, testBudget, deadlineHeight, 0)

	// We expect the current fee rate to be increased because we ensure the
	// initial broadcast always succeeds.
	assertFeeRateGreater(testFeeRate)

	smallBudget := uint64(1000)

	// Finally, we test the behavior of lowering the fee rate. The fee func
	// that has,
	// - starting fee rate: 1 sat/vbyte.
	// - deadline: 1008.
	// - budget: 1000 sats.
	bumpFeeReq = &walletrpc.BumpFeeRequest{
		Outpoint: op,
		// We use a force param to create the sweeping tx immediately.
		Immediate:   true,
		SatPerVbyte: startFeeRate,
		Budget:      smallBudget,
		TargetConf:  uint32(sweep.DefaultDeadlineDelta),
	}
	alice.RPC.BumpFee(bumpFeeReq)

	// Assert the pending sweep is created with the expected values:
	// - broadcast attempts: 6.
	// - starting fee rate: 1 sat/vbyte.
	// - deadline: 1008.
	// - budget: 1000 sats.
	sweepTx6 := assertPendingSweepResp(
		6, smallBudget, deadline, startFeeRate,
	)

	// Since this budget is too small to cover the RBF, we expect the
	// sweeping attempt to fail.
	//
	require.Equal(ht, sweepTx5.TxHash(), sweepTx6.TxHash(), "tx5 should "+
		"not be replaced: tx5=%v, tx6=%v", sweepTx5.TxHash(),
		sweepTx6.TxHash())

	// We expect the current fee rate to be increased because we ensure the
	// initial broadcast always succeeds.
	assertFeeRateGreater(testFeeRate)

	// Clean up the mempol.
	ht.MineBlocksAndAssertNumTxes(1, 2)
}

// testBumpForceCloseFee tests that when a force close transaction, in
// particular a commitment which has no HTLCs at stake, can be bumped via the
// rpc endpoint `BumpForceCloseFee`.
//
// NOTE: This test does not check for a specific fee rate because channel force
// closures should be bumped taking a budget into account not a specific
// fee rate.
func testBumpForceCloseFee(ht *lntest.HarnessTest) {
	// Skip this test for neutrino, as it's not aware of mempool
	// transactions.
	if ht.IsNeutrinoBackend() {
		ht.Skipf("skipping BumpForceCloseFee test for neutrino backend")
	}
	// fundAmt is the funding amount.
	fundAmt := btcutil.Amount(1_000_000)

	// We add a push amount because otherwise no anchor for the counter
	// party will be created which influences the commitment fee
	// calculation.
	pushAmt := btcutil.Amount(50_000)

	openChannelParams := lntest.OpenChannelParams{
		Amt:     fundAmt,
		PushAmt: pushAmt,
	}

	// Bumping the close fee rate is only possible for anchor channels.
	cfg := []string{
		"--protocol.anchors",
	}
	cfgs := [][]string{cfg, cfg}

	// Create a two hop network: Alice -> Bob.
	chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, openChannelParams)

	// Unwrap the results.
	chanPoint := chanPoints[0]
	alice := nodes[0]

	// We need to fund alice with 2 wallet inputs so that we can test to
	// increase the fee rate of the anchor cpfp via two subsequent calls of
	// the`BumpForceCloseFee` rpc cmd.
	//
	// TODO (ziggie): Make sure we use enough wallet inputs so that both
	// anchor transactions (local, remote commitment tx) can be created and
	// broadcasted. Not sure if we really need this, because we can be sure
	// as soon as one anchor transactions makes it into the mempool that the
	// others will fail anyways?
	ht.FundCoinsP2TR(btcutil.SatoshiPerBitcoin, alice)

	// Alice force closes the channel which has no HTLCs at stake.
	_, closingTxID := ht.CloseChannelAssertPending(alice, chanPoint, true)
	require.NotNil(ht, closingTxID)

	// Alice should see one waiting close channel.
	ht.AssertNumWaitingClose(alice, 1)

	// Alice should have 2 registered sweep inputs. The anchor of the local
	// commitment tx and the anchor of the remote commitment tx.
	ht.AssertNumPendingSweeps(alice, 2)

	// Calculate the commitment tx fee rate.
	closingTx := ht.AssertTxInMempool(closingTxID)
	require.NotNil(ht, closingTx)

	// The default commitment fee for anchor channels is capped at 2500
	// sat/kw but there might be some inaccuracies because of the witness
	// signature length therefore we calculate the exact value here.
	closingFeeRate := ht.CalculateTxFeeRate(closingTx)

	// We increase the fee rate of the fee function by 100% to make sure
	// we trigger a cpfp-transaction.
	newFeeRate := closingFeeRate * 2

	// We need to make sure that the budget can cover the fees for bumping.
	// However we also want to make sure that the budget is not too large
	// so that the delta of the fee function does not increase the feerate
	// by a single sat hence NOT rbfing the anchor sweep every time a new
	// block is found and a new sweep broadcast is triggered.
	//
	// NOTE:
	// We expect an anchor sweep with 2 inputs (anchor input + a wallet
	// input) and 1 p2tr output. This transaction has a weight of approx.
	// 725 wu. This info helps us to calculate the delta of the fee
	// function.
	// EndFeeRate: 100_000 sats/725 wu * 1000 = 137931 sat/kw
	// StartingFeeRate: 5000 sat/kw
	// delta = (137931-5000)/1008 = 132 sat/kw (which is lower than
	// 250 sat/kw) => hence we are violating BIP 125 Rule 4, which is
	// exactly what we want here to test the subsequent calling of the
	// bumpclosefee rpc.
	cpfpBudget := 100_000

	bumpFeeReq := &walletrpc.BumpForceCloseFeeRequest{
		ChanPoint:       chanPoint,
		StartingFeerate: uint64(newFeeRate.FeePerVByte()),
		Budget:          uint64(cpfpBudget),
		// We use a force param to create the sweeping tx immediately.
		Immediate: true,
	}
	alice.RPC.BumpForceCloseFee(bumpFeeReq)

	// We expect the initial closing transaction and the local anchor cpfp
	// transaction because alice force closed the channel.
	//
	// NOTE: We don't compare a feerate but only make sure that a cpfp
	// transaction was triggered. The sweeper increases the fee rate
	// periodically with every new incoming block and the selected fee
	// function.
	ht.AssertNumTxsInMempool(2)

	// Identify the cpfp anchor sweep.
	txns := ht.GetNumTxsFromMempool(2)
	cpfpSweep1 := ht.FindSweepingTxns(txns, 1, closingTx.TxHash())[0]

	// Mine an empty block and make sure the anchor cpfp is still in the
	// mempool hence the new block did not let the sweeper subsystem rbf
	// this anchor sweep transaction (because of the small fee delta).
	ht.MineEmptyBlocks(1)
	cpfpHash1 := cpfpSweep1.TxHash()
	ht.AssertTxInMempool(cpfpHash1)

	// Now Bump the fee rate again with a bigger starting fee rate of the
	// fee function.
	newFeeRate = closingFeeRate * 3

	bumpFeeReq = &walletrpc.BumpForceCloseFeeRequest{
		ChanPoint:       chanPoint,
		StartingFeerate: uint64(newFeeRate.FeePerVByte()),
		// The budget needs to be high enough to pay for the fee because
		// the anchor does not have an output value high enough to pay
		// for itself.
		Budget: uint64(cpfpBudget),
		// We use a force param to create the sweeping tx immediately.
		Immediate: true,
	}
	alice.RPC.BumpForceCloseFee(bumpFeeReq)

	// Make sure the old sweep is not in the mempool anymore, which proofs
	// that a new cpfp transaction replaced the old one paying higher fees.
	ht.AssertTxNotInMempool(cpfpHash1)

	// Identify the new cpfp transaction.
	// Both anchor sweeps result from the same closing tx (the local
	// commitment) hence proofing that the remote commitment transaction
	// and its cpfp transaction is invalid and not accepted into the
	// mempool.
	txns = ht.GetNumTxsFromMempool(2)
	ht.FindSweepingTxns(txns, 1, closingTx.TxHash())

	// Mine both transactions, the closing tx and the anchor cpfp tx.
	// This is needed to clean up the mempool.
	ht.MineBlocksAndAssertNumTxes(1, 2)
}
