Repository: lightningnetwork/lnd (language: go)

Issue:
sweep: add docs and remove dead code
Straightforward PR - removes the dead code, and adds a readme file.


Discussion / hints:

Fix patch:
diff --git a/contractcourt/anchor_resolver.go b/contractcourt/anchor_resolver.go
index ec90b6ed34..b4d6877202 100644
--- a/contractcourt/anchor_resolver.go
+++ b/contractcourt/anchor_resolver.go
@@ -99,8 +99,6 @@ func (c *anchorResolver) Resolve(_ bool) (ContractResolver, error) {
 	// After a restart or when the remote force closes, the sweeper is not
 	// yet aware of the anchor. In that case, it will be added as new input
 	// to the sweeper.
-	relayFeeRate := c.Sweeper.RelayFeePerKW()
-
 	witnessType := input.CommitmentAnchor
 
 	// For taproot channels, we need to use the proper witness type.
@@ -116,9 +114,6 @@ func (c *anchorResolver) Resolve(_ bool) (ContractResolver, error) {
 	resultChan, err := c.Sweeper.SweepInput(
 		&anchorInput,
 		sweep.Params{
-			Fee: sweep.FeeEstimateInfo{
-				FeeRate: relayFeeRate,
-			},
 			// For normal anchor sweeping, the budget is 330 sats.
 			Budget: btcutil.Amount(
 				anchorInput.SignDesc().Output.Value,
diff --git a/docs/release-notes/release-notes-0.18.0.md b/docs/release-notes/release-notes-0.18.0.md
index 18969e6c4f..9559053542 100644
--- a/docs/release-notes/release-notes-0.18.0.md
+++ b/docs/release-notes/release-notes-0.18.0.md
@@ -204,7 +204,9 @@ bitcoin peers' feefilter values into account](https://github.com/lightningnetwor
   by default, and the feature is not yet advertised to the network.
 
 * Introduced [fee bumper](https://github.com/lightningnetwork/lnd/pull/8424) to
-  handle bumping the fees of sweeping transactions properly.
+  handle bumping the fees of sweeping transactions properly. A
+  [README.md](https://github.com/lightningnetwork/lnd/pull/8674) is added to
+  explain this new approach.
 
 ## RPC Additions
 
diff --git a/sweep/README.md b/sweep/README.md
new file mode 100644
index 0000000000..487ebdcddb
--- /dev/null
+++ b/sweep/README.md
@@ -0,0 +1,213 @@
+# Sweep
+
+`sweep` is a subservice that handles sweeping UTXOs back to `lnd`'s wallet. Its
+main purpose is to sweep back the outputs resulting from a force close
+transaction, although users can also call `BumpFee` to feed new unconfirmed
+inputs to be handled by the sweeper.
+
+In order to sweep economically, the sweeper needs to understand the time
+sensitivity and max fees that can be used when sweeping the inputs. This means
+each input must come with a deadline and a fee budget, which can be set via the
+RPC request or the config, otherwise the default values will be used. Once
+offered to the sweeper, when a new block arrives, inputs with the same deadline
+will be batched into a single sweeping transaction to minimize the cost.
+
+The sweeper will publish this transaction and monitor it for potential fee
+bumping, a process that won’t exit until the sweeping transaction is confirmed,
+or the specified budget has been used up.
+
+## Understanding Budget and Deadline
+
+There are two questions when spending a UTXO - how much fees to pay and what
+the confirmation target is, which gives us the concepts of budget and deadline.
+This is especially important when sweeping the outputs of a force close
+transaction - some of the outputs are time-sensitive, and may result in fund
+loss if not confirmed in time. On the other hand, we don’t want to pay more
+than what we can get back - if a sweeping transaction spends more than what is
+meant to be swept, we are losing money due to fees.
+
+To properly handle the case, the concept `budget` and `deadline` have been
+introduced to `lnd` since `v0.18.0` - for each new sweeping request, the
+sweeper requires the caller to specify a deadline and a budget so it can make
+economic decisions. A fee function is then created based on the budget and
+deadline, which proposes a fee rate to use for the sweeping transaction. When a
+new block arrives, unless the transaction is confirmed or the budget is used
+up, the sweeper will perform a fee bump on it via RBF.
+
+## Package Structure
+
+On a high level, a UTXO is offered to the sweeper via `SweepInput`. The sweeper
+keeps track of the pending inputs. When a new block arrives, it asks the
+`UtxoAggregator` to group all the pending inputs into batches via
+`ClusterInputs`. Each batch is an `InputSet`, and is sent to the `Bumper`. The
+`Bumper` creates a `FeeFunction` and a sweeping transaction using the
+`InputSet`, and monitors its confirmation status. Every time it's not confirmed
+when a new block arrives, the `Bumper` will perform an RBF by calling
+`IncreaseFeeRate` on the `FeeFunction`.
+
+```mermaid
+flowchart LR
+        subgraph SweepInput
+        UTXO1-->sweeper
+      UTXO2-->sweeper
+        UTXO3-->sweeper
+        UTXO["..."]-->sweeper
+        sweeper
+    end
+
+    subgraph ClusterInputs
+        sweeper-->UtxoAggregator
+      UtxoAggregator-->InputSet1
+        UtxoAggregator-->InputSet2
+        UtxoAggregator-->InputSet["..."]
+    end
+
+    subgraph Broadcast
+            InputSet1-->Bumper
+            InputSet2-->Bumper
+            InputSet-->Bumper
+    end
+
+    subgraph IncreaseFeeRate
+        FeeFunction-->Bumper
+    end
+
+        block["new block"] ==> ClusterInputs
+```
+
+#### `UtxoAggregator` and `InputSet`
+
+`UtxoAggregator` is an interface that handles the batching of inputs.
+`BudgetAggregator` implements this interface by grouping inputs with the same
+deadline together. Inputs with the same deadline express the same time
+sensitivity so it makes sense to sweep them in the same transaction. Once
+grouped, inputs in each batch are sorted based on their budgets. The only
+exception is inputs with `ExclusiveGroup` flag set, which will be swept alone.
+
+Once the batching is finished, an `InputSet` is returned, which is an interface
+used to decide whether a wallet UTXO is needed or not when creating the
+sweeping transaction. `BudgetInputSet` implements this interface by checking
+the sum of the output values from these inputs against the sum of their
+budgets - if the total budget cannot be covered, one or more wallet UTXOs are
+needed.
+
+For instance, when anchor output is swept to perform a CPFP, one or more wallet
+UTXOs are likely to be used to meet the specified budget, which is also the
+case when sweeping second-level HTLC transactions. However, if the sweeping
+transaction also contains other to-be-swept inputs, a wallet UTXO is no longer
+needed if their values can cover the total budget.
+
+#### `Bumper`
+
+`Bumper` is a transaction creator, publisher, and monitor that works on an
+`InputSet`. Once a sweeping transaction is created using the `InputSet`, the
+`Bumper` will monitor its confirmation status and attempt an RBF if the
+transaction is not confirmed in the next block. It relies on the `FeeFunction`
+to determine the new fee rate every block, and this new fee rate may or may not
+meet the BIP 125 fee requirements - in that case, the `Bumper` will try to
+perform an RBF again in the coming blocks.
+
+`TxPublisher` implements the `Bumper` interface. When a transaction is created
+for the first time, unless its budget has been used up, `TxPublisher` will
+guarantee that the initial publish meets the RBF requirements.
+
+#### `FeeFunction`
+
+`FeeFunction` is an interface that specifies a function over a starting fee
+rate, an ending fee rate, and a width (the deadline delta). It's used by the
+`Bumper` to suggest a new fee rate for bumping the sweeping transaction.
+
+`LinearFeeFunction` implements this interface using a linear function - it
+calculates a fee rate delta using `(ending_fee_rate - starting_fee_rate) /
+deadline`, and increases the fee rate by this delta value everytime a new block
+arrives. Once the deadline is passed, `LinearFeeFunction` will cap its
+returning fee rate at the ending fee rate.
+
+The starting fee rate is the estimated fee rate from the fee estimator, which
+is the result from calling `estimatesmartfee`(`bitcoind`),
+`estimatefee`(`btcd`), or `feeurl` depending on the config. This fee estimator
+is called using the deadline as the conf target, and the returned fee rate is
+used as the starting fee rate. This behavior can be overridden by setting the
+`--sat_per_vbyte` via `bumpfee` cli when fee bumping a specific input, which
+allows users to bypass the fee estimation and set the starting fee rate
+directly.
+
+The ending fee rate is the value from dividing the budget by the size of the
+sweeping transaction, and capped at the `--sweeper.maxfeerate`. The ending fee
+rate can be overridden by setting the `--budget` via `bumpfee` cli.
+
+For instance, suppose `lnd` is using `bitcoind` as its fee estimator, and an
+input with a deadline of 1000 blocks and a budget of 200,000 sats is being
+swept in a transaction that has a size of 500 vbytes, the fee function will be
+initialized with:
+
+- a starting fee rate of 10 sat/vB, which is the result from calling
+  `estimatesmartfee 1000`.
+- an ending fee rate of 400 sat/vB, which is the result of `200,000/500`.
+- a fee rate delta of 390 sat/kvB, which is the result of `(400 - 10) / 500 *
+  1000`.
+
+## Sweeping Outputs from a Force Close Transaction
+
+A force close transaction may have the following outputs:
+
+- Commit outputs, which are the  `to_local` and `to_remote` outputs.
+- HTLC outputs, which are the  `incoming_htlc` and `outgoing_htlc` outputs.
+- Anchor outputs, which are the local and remote anchor outputs.
+
+#### Sweeping Commit Outputs
+
+The only output we can spend is the `to_local` output. Because it can only be
+spent using our signature, there’s no time pressure here. By default, the
+sweeper will use a deadline of 1008 blocks as the confirmation target for
+non-time-sensitive outputs.  To overwrite the default, users can specify a
+value using the config `--sweeper.nodeadlineconftarget`.
+
+To specify the budget, users can use `--sweeper.budget.tolocal` to set the max
+allowed fees in sats, or use `--sweeper.budget.tolocalratio` to set a
+proportion of the `to_local` value to be used as the budget.
+
+#### Sweeping HTLC Outputs
+
+When facing a local force close transaction, HTLCs are spent in a two-stage
+setup - the first stage is to spend the outputs using pre-signed HTLC
+success/timeout transactions, the second stage is to spend the outputs from
+these success/timeout transactions. All these outputs are automatically handled
+by `lnd`. In specific,
+- For an incoming HTLC in stage one, the deadline is specified using its CLTV
+  from the timeout path. This output is time-sensitive.
+- For an outgoing HTLC in stage one, the deadline is derived from its
+  corresponding incoming HTLC’s CLTV. This output is time-sensitive.
+- For both incoming and outgoing HTLCs in stage two, because they can only be
+  spent by us, there is no time pressure to confirm them under a deadline.
+
+When facing a remote force close transaction, HTLCs can be directly spent from
+the commitment transaction, and both incoming and outgoing HTLCs are
+time-sensitive.
+
+By default, `lnd` will use 50% of the HTLC value as its budget. To customize
+it, users can specify `--sweeper.budget.deadlinehtlc` and
+`--sweeper.budget.deadlinehtlcratio` for time-sensitive HTLCs, and
+`--sweeper.budget.nodeadlinehtlc` and `--sweeper.budget.nodeadlinehtlcratio`
+for non-time-sensitive sweeps.
+
+#### Sweeping Anchor Outputs
+
+An anchor output is a special output that functions as “anchor” to speed up the
+unconfirmed force closing transaction via CPFP. If the force close transaction
+doesn't contain any HTLCs, the anchor output is generally uneconomical to sweep
+and will be ignored. However, if the force close transaction does contain
+time-sensitive outputs (HTLCs), the anchor output will be swept to CPFP the
+transaction and accelerate the force close process.
+
+For CPFP-purpose anchor sweeping, the deadline is the closest deadline value of
+all the HTLCs on the force close transaction. The budget, however, cannot be a
+ratio of the anchor output because the value is too small to contribute
+meaningful fees (330 sats). Since its purpose is to accelerate the force close
+transaction so the time-sensitive outputs can be swept, the budget is actually
+drawn from what we call “value under protection”, which is the sum of all HTLC
+outputs minus the sum of their budgets. By default, 50% of this value is used
+as the budget, to customize it, either use
+`--sweeper.budget.anchorcpfp` to specify sats, or use
+`--sweeper.budget.anchorcpfpratio` to specify a ratio.
+
diff --git a/sweep/aggregator.go b/sweep/aggregator.go
index a65fa39c23..c812cbe2b1 100644
--- a/sweep/aggregator.go
+++ b/sweep/aggregator.go
@@ -9,113 +9,6 @@ import (
 	"github.com/lightningnetwork/lnd/lnwallet/chainfee"
 )
 
-const (
-	// DefaultFeeRateBucketSize is the default size of fee rate buckets
-	// we'll use when clustering inputs into buckets with similar fee rates
-	// within the SimpleAggregator.
-	//
-	// Given a minimum relay fee rate of 1 sat/vbyte, a multiplier of 10
-	// would result in the following fee rate buckets up to the maximum fee
-	// rate:
-	//
-	//   #1: min = 1 sat/vbyte, max = 10 sat/vbyte
-	//   #2: min = 11 sat/vbyte, max = 20 sat/vbyte...
-	DefaultFeeRateBucketSize = 10
-)
-
-// inputCluster is a helper struct to gather a set of pending inputs that
-// should be swept with the specified fee rate.
-type inputCluster struct {
-	lockTime     *uint32
-	sweepFeeRate chainfee.SatPerKWeight
-	inputs       InputsMap
-}
-
-// createInputSets goes through the cluster's inputs and constructs sets of
-// inputs that can be used to generate a sweeping transaction. Each set
-// contains up to the configured maximum number of inputs. Negative yield
-// inputs are skipped.  No input sets with a total value after fees below the
-// dust limit are returned.
-func (c *inputCluster) createInputSets(maxFeeRate chainfee.SatPerKWeight,
-	maxInputs uint32) []InputSet {
-
-	// Turn the inputs into a slice so we can sort them.
-	inputList := make([]*SweeperInput, 0, len(c.inputs))
-	for _, input := range c.inputs {
-		inputList = append(inputList, input)
-	}
-
-	// Yield is calculated as the difference between value and added fee
-	// for this input. The fee calculation excludes fee components that are
-	// common to all inputs, as those wouldn't influence the order. The
-	// single component that is differentiating is witness size.
-	//
-	// For witness size, the upper limit is taken. The actual size depends
-	// on the signature length, which is not known yet at this point.
-	calcYield := func(input *SweeperInput) int64 {
-		size, _, err := input.WitnessType().SizeUpperBound()
-		if err != nil {
-			log.Errorf("Failed to get input weight: %v", err)
-
-			return 0
-		}
-
-		yield := input.SignDesc().Output.Value -
-			int64(c.sweepFeeRate.FeeForWeight(int64(size)))
-
-		return yield
-	}
-
-	// Sort input by yield. We will start constructing input sets starting
-	// with the highest yield inputs. This is to prevent the construction
-	// of a set with an output below the dust limit, causing the sweep
-	// process to stop, while there are still higher value inputs
-	// available. It also allows us to stop evaluating more inputs when the
-	// first input in this ordering is encountered with a negative yield.
-	sort.Slice(inputList, func(i, j int) bool {
-		// Because of the specific ordering and termination condition
-		// that is described above, we place force sweeps at the start
-		// of the list. Otherwise we can't be sure that they will be
-		// included in an input set.
-		if inputList[i].parameters().Immediate {
-			return true
-		}
-
-		return calcYield(inputList[i]) > calcYield(inputList[j])
-	})
-
-	// Select blocks of inputs up to the configured maximum number.
-	var sets []InputSet
-	for len(inputList) > 0 {
-		// Start building a set of positive-yield tx inputs under the
-		// condition that the tx will be published with the specified
-		// fee rate.
-		txInputs := newTxInputSet(c.sweepFeeRate, maxFeeRate, maxInputs)
-
-		// From the set of sweepable inputs, keep adding inputs to the
-		// input set until the tx output value no longer goes up or the
-		// maximum number of inputs is reached.
-		txInputs.addPositiveYieldInputs(inputList)
-
-		// If there are no positive yield inputs, we can stop here.
-		inputCount := len(txInputs.inputs)
-		if inputCount == 0 {
-			return sets
-		}
-
-		log.Infof("Candidate sweep set of size=%v (+%v wallet inputs),"+
-			" has yield=%v, weight=%v",
-			inputCount, len(txInputs.inputs)-inputCount,
-			txInputs.totalOutput()-txInputs.walletInputTotal,
-			txInputs.weightEstimate(true).weight())
-
-		sets = append(sets, txInputs)
-		inputList = inputList[inputCount:]
-	}
-
-	return sets
-}
-
 // UtxoAggregator defines an interface that takes a list of inputs and
 // aggregate them into groups. Each group is used as the inputs to create a
 // sweeping transaction.
@@ -125,345 +18,6 @@ type UtxoAggregator interface {
 	ClusterInputs(inputs InputsMap) []InputSet
 }
 
-// SimpleAggregator aggregates inputs known by the Sweeper based on each
-// input's locktime and feerate.
-type SimpleAggregator struct {
-	// FeeEstimator is used when crafting sweep transactions to estimate
-	// the necessary fee relative to the expected size of the sweep
-	// transaction.
-	FeeEstimator chainfee.Estimator
-
-	// MaxFeeRate is the maximum fee rate allowed within the
-	// SimpleAggregator.
-	MaxFeeRate chainfee.SatPerKWeight
-
-	// MaxInputsPerTx specifies the default maximum number of inputs allowed
-	// in a single sweep tx. If more need to be swept, multiple txes are
-	// created and published.
-	MaxInputsPerTx uint32
-
-	// FeeRateBucketSize is the default size of fee rate buckets we'll use
-	// when clustering inputs into buckets with similar fee rates within
-	// the SimpleAggregator.
-	//
-	// Given a minimum relay fee rate of 1 sat/vbyte, a fee rate bucket
-	// size of 10 would result in the following fee rate buckets up to the
-	// maximum fee rate:
-	//
-	//   #1: min = 1 sat/vbyte, max (exclusive) = 11 sat/vbyte
-	//   #2: min = 11 sat/vbyte, max (exclusive) = 21 sat/vbyte...
-	FeeRateBucketSize int
-}
-
-// Compile-time constraint to ensure SimpleAggregator implements UtxoAggregator.
-var _ UtxoAggregator = (*SimpleAggregator)(nil)
-
-// NewSimpleUtxoAggregator creates a new instance of a SimpleAggregator.
-func NewSimpleUtxoAggregator(estimator chainfee.Estimator,
-	max chainfee.SatPerKWeight, maxTx uint32) *SimpleAggregator {
-
-	return &SimpleAggregator{
-		FeeEstimator:      estimator,
-		MaxFeeRate:        max,
-		MaxInputsPerTx:    maxTx,
-		FeeRateBucketSize: DefaultFeeRateBucketSize,
-	}
-}
-
-// ClusterInputs creates a list of input clusters from the set of pending
-// inputs known by the UtxoSweeper. It clusters inputs by
-// 1) Required tx locktime
-// 2) Similar fee rates.
-func (s *SimpleAggregator) ClusterInputs(inputs InputsMap) []InputSet {
-	// We start by getting the inputs clusters by locktime. Since the
-	// inputs commit to the locktime, they can only be clustered together
-	// if the locktime is equal.
-	lockTimeClusters, nonLockTimeInputs := s.clusterByLockTime(inputs)
-
-	// Cluster the remaining inputs by sweep fee rate.
-	feeClusters := s.clusterBySweepFeeRate(nonLockTimeInputs)
-
-	// Since the inputs that we clustered by fee rate don't commit to a
-	// specific locktime, we can try to merge a locktime cluster with a fee
-	// cluster.
-	clusters := zipClusters(lockTimeClusters, feeClusters)
-
-	sort.Slice(clusters, func(i, j int) bool {
-		return clusters[i].sweepFeeRate >
-			clusters[j].sweepFeeRate
-	})
-
-	// Now that we have the clusters, we can create the input sets.
-	var inputSets []InputSet
-	for _, cluster := range clusters {
-		sets := cluster.createInputSets(
-			s.MaxFeeRate, s.MaxInputsPerTx,
-		)
-		inputSets = append(inputSets, sets...)
-	}
-
-	return inputSets
-}
-
-// clusterByLockTime takes the given set of pending inputs and clusters those
-// with equal locktime together. Each cluster contains a sweep fee rate, which
-// is determined by calculating the average fee rate of all inputs within that
-// cluster. In addition to the created clusters, inputs that did not specify a
-// required locktime are returned.
-func (s *SimpleAggregator) clusterByLockTime(
-	inputs InputsMap) ([]inputCluster, InputsMap) {
-
-	locktimes := make(map[uint32]InputsMap)
-	rem := make(InputsMap)
-
-	// Go through all inputs and check if they require a certain locktime.
-	for op, input := range inputs {
-		lt, ok := input.RequiredLockTime()
-		if !ok {
-			rem[op] = input
-			continue
-		}
-
-		// Check if we already have inputs with this locktime.
-		cluster, ok := locktimes[lt]
-		if !ok {
-			cluster = make(InputsMap)
-		}
-
-		// Get the fee rate based on the fee preference. If an error is
-		// returned, we'll skip sweeping this input for this round of
-		// cluster creation and retry it when we create the clusters
-		// from the pending inputs again.
-		feeRate, err := input.params.Fee.Estimate(
-			s.FeeEstimator, s.MaxFeeRate,
-		)
-		if err != nil {
-			log.Warnf("Skipping input %v: %v", op, err)
-			continue
-		}
-
-		log.Debugf("Adding input %v to cluster with locktime=%v, "+
-			"feeRate=%v", op, lt, feeRate)
-
-		// Attach the fee rate to the input.
-		input.lastFeeRate = feeRate
-
-		// Update the cluster about the updated input.
-		cluster[op] = input
-		locktimes[lt] = cluster
-	}
-
-	// We'll then determine the sweep fee rate for each set of inputs by
-	// calculating the average fee rate of the inputs within each set.
-	inputClusters := make([]inputCluster, 0, len(locktimes))
-	for lt, cluster := range locktimes {
-		lt := lt
-
-		var sweepFeeRate chainfee.SatPerKWeight
-		for _, input := range cluster {
-			sweepFeeRate += input.lastFeeRate
-		}
-
-		sweepFeeRate /= chainfee.SatPerKWeight(len(cluster))
-		inputClusters = append(inputClusters, inputCluster{
-			lockTime:     &lt,
-			sweepFeeRate: sweepFeeRate,
-			inputs:       cluster,
-		})
-	}
-
-	return inputClusters, rem
-}
-
-// clusterBySweepFeeRate takes the set of pending inputs within the UtxoSweeper
-// and clusters those together with similar fee rates. Each cluster contains a
-// sweep fee rate, which is determined by calculating the average fee rate of
-// all inputs within that cluster.
-func (s *SimpleAggregator) clusterBySweepFeeRate(
-	inputs InputsMap) []inputCluster {
-
-	bucketInputs := make(map[int]*bucketList)
-	inputFeeRates := make(map[wire.OutPoint]chainfee.SatPerKWeight)
-
-	// First, we'll group together all inputs with similar fee rates. This
-	// is done by determining the fee rate bucket they should belong in.
-	for op, input := range inputs {
-		feeRate, err := input.params.Fee.Estimate(
-			s.FeeEstimator, s.MaxFeeRate,
-		)
-		if err != nil {
-			log.Warnf("Skipping input %v: %v", op, err)
-			continue
-		}
-
-		// Only try to sweep inputs with an unconfirmed parent if the
-		// current sweep fee rate exceeds the parent tx fee rate. This
-		// assumes that such inputs are offered to the sweeper solely
-		// for the purpose of anchoring down the parent tx using cpfp.
-		parentTx := input.UnconfParent()
-		if parentTx != nil {
-			parentFeeRate :=
-				chainfee.SatPerKWeight(parentTx.Fee*1000) /
-					chainfee.SatPerKWeight(parentTx.Weight)
-
-			if parentFeeRate >= feeRate {
-				log.Debugf("Skipping cpfp input %v: "+
-					"fee_rate=%v, parent_fee_rate=%v", op,
-					feeRate, parentFeeRate)
-
-				continue
-			}
-		}
-
-		feeGroup := s.bucketForFeeRate(feeRate)
-
-		// Create a bucket list for this fee rate if there isn't one
-		// yet.
-		buckets, ok := bucketInputs[feeGroup]
-		if !ok {
-			buckets = &bucketList{}
-			bucketInputs[feeGroup] = buckets
-		}
-
-		// Request the bucket list to add this input. The bucket list
-		// will take into account exclusive group constraints.
-		buckets.add(input)
-
-		input.lastFeeRate = feeRate
-		inputFeeRates[op] = feeRate
-	}
-
-	// We'll then determine the sweep fee rate for each set of inputs by
-	// calculating the average fee rate of the inputs within each set.
-	inputClusters := make([]inputCluster, 0, len(bucketInputs))
-	for _, buckets := range bucketInputs {
-		for _, inputs := range buckets.buckets {
-			var sweepFeeRate chainfee.SatPerKWeight
-			for op := range inputs {
-				sweepFeeRate += inputFeeRates[op]
-			}
-			sweepFeeRate /= chainfee.SatPerKWeight(len(inputs))
-			inputClusters = append(inputClusters, inputCluster{
-				sweepFeeRate: sweepFeeRate,
-				inputs:       inputs,
-			})
-		}
-	}
-
-	return inputClusters
-}
-
-// bucketForFeeReate determines the proper bucket for a fee rate. This is done
-// in order to batch inputs with similar fee rates together.
-func (s *SimpleAggregator) bucketForFeeRate(
-	feeRate chainfee.SatPerKWeight) int {
-
-	relayFeeRate := s.FeeEstimator.RelayFeePerKW()
-
-	// Create an isolated bucket for sweeps at the minimum fee rate. This
-	// is to prevent very small outputs (anchors) from becoming
-	// uneconomical if their fee rate would be averaged with higher fee
-	// rate inputs in a regular bucket.
-	if feeRate == relayFeeRate {
-		return 0
-	}
-
-	return 1 + int(feeRate-relayFeeRate)/s.FeeRateBucketSize
-}
-
-// mergeClusters attempts to merge cluster a and b if they are compatible. The
-// new cluster will have the locktime set if a or b had a locktime set, and a
-// sweep fee rate that is the maximum of a and b's. If the two clusters are not
-// compatible, they will be returned unchanged.
-func mergeClusters(a, b inputCluster) []inputCluster {
-	newCluster := inputCluster{}
-
-	switch {
-	// Incompatible locktimes, return the sets without merging them.
-	case a.lockTime != nil && b.lockTime != nil &&
-		*a.lockTime != *b.lockTime:
-
-		return []inputCluster{a, b}
-
-	case a.lockTime != nil:
-		newCluster.lockTime = a.lockTime
-
-	case b.lockTime != nil:
-		newCluster.lockTime = b.lockTime
-	}
-
-	if a.sweepFeeRate > b.sweepFeeRate {
-		newCluster.sweepFeeRate = a.sweepFeeRate
-	} else {
-		newCluster.sweepFeeRate = b.sweepFeeRate
-	}
-
-	newCluster.inputs = make(InputsMap)
-
-	for op, in := range a.inputs {
-		newCluster.inputs[op] = in
-	}
-
-	for op, in := range b.inputs {
-		newCluster.inputs[op] = in
-	}
-
-	return []inputCluster{newCluster}
-}
-
-// zipClusters merges pairwise clusters from as and bs such that cluster a from
-// as is merged with a cluster from bs that has at least the fee rate of a.
-// This to ensure we don't delay confirmation by decreasing the fee rate (the
-// lock time inputs are typically second level HTLC transactions, that are time
-// sensitive).
-func zipClusters(as, bs []inputCluster) []inputCluster {
-	// Sort the clusters by decreasing fee rates.
-	sort.Slice(as, func(i, j int) bool {
-		return as[i].sweepFeeRate >
-			as[j].sweepFeeRate
-	})
-	sort.Slice(bs, func(i, j int) bool {
-		return bs[i].sweepFeeRate >
-			bs[j].sweepFeeRate
-	})
-
-	var (
-		finalClusters []inputCluster
-		j             int
-	)
-
-	// Go through each cluster in as, and merge with the next one from bs
-	// if it has at least the fee rate needed.
-	for i := range as {
-		a := as[i]
-
-		switch {
-		// If the fee rate for the next one from bs is at least a's, we
-		// merge.
-		case j < len(bs) && bs[j].sweepFeeRate >= a.sweepFeeRate:
-			merged := mergeClusters(a, bs[j])
-			finalClusters = append(finalClusters, merged...)
-
-			// Increment j for the next round.
-			j++
-
-		// We did not merge, meaning all the remaining clusters from bs
-		// have lower fee rate. Instead we add a directly to the final
-		// clusters.
-		default:
-			finalClusters = append(finalClusters, a)
-		}
-	}
-
-	// Add any remaining clusters from bs.
-	for ; j < len(bs); j++ {
-		b := bs[j]
-		finalClusters = append(finalClusters, b)
-	}
-
-	return finalClusters
-}
-
 // BudgetAggregator is a budget-based aggregator that creates clusters based on
 // deadlines and budgets of inputs.
 type BudgetAggregator struct {
diff --git a/sweep/bucket_list.go b/sweep/bucket_list.go
deleted file mode 100644
index e5a2cfea6f..0000000000
--- a/sweep/bucket_list.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package sweep
-
-// bucket contains a set of inputs that are not mutually exclusive.
-type bucket InputsMap
-
-// tryAdd tries to add a new input to this bucket.
-func (b bucket) tryAdd(input *SweeperInput) bool {
-	exclusiveGroup := input.params.ExclusiveGroup
-	if exclusiveGroup != nil {
-		for _, input := range b {
-			existingGroup := input.params.ExclusiveGroup
-
-			// Don't add an exclusive group input if other inputs
-			// are non-exclusive. The exclusive group input may be
-			// invalid (for example in the case of commitment
-			// anchors) and could thereby block sweeping of the
-			// other inputs.
-			if existingGroup == nil {
-				return false
-			}
-
-			// Don't combine inputs from the same exclusive group.
-			// Because only one input is valid, this may result in
-			// txes that are always invalid.
-			if *existingGroup == *exclusiveGroup {
-				return false
-			}
-		}
-	}
-
-	b[input.OutPoint()] = input
-
-	return true
-}
-
-// bucketList is a list of buckets that contain non-mutually exclusive inputs.
-type bucketList struct {
-	buckets []bucket
-}
-
-// add adds a new input. If the input is not accepted by any of the existing
-// buckets, a new bucket will be created.
-func (b *bucketList) add(input *SweeperInput) {
-	for _, existingBucket := range b.buckets {
-		if existingBucket.tryAdd(input) {
-			return
-		}
-	}
-
-	// Create a new bucket and add the input. It is not necessary to check
-	// the return value of tryAdd because it will always succeed on an empty
-	// bucket.
-	newBucket := make(bucket)
-	newBucket.tryAdd(input)
-	b.buckets = append(b.buckets, newBucket)
-}
diff --git a/sweep/sweeper.go b/sweep/sweeper.go
index 4abf59d1a7..2754485ea1 100644
--- a/sweep/sweeper.go
+++ b/sweep/sweeper.go
@@ -43,11 +43,6 @@ var (
 
 // Params contains the parameters that control the sweeping process.
 type Params struct {
-	// Fee is the fee preference of the client who requested the input to be
-	// swept. If a confirmation target is specified, then we'll map it into
-	// a fee rate whenever we attempt to cluster inputs for a sweep.
-	Fee FeePreference
-
 	// ExclusiveGroup is an identifier that, if set, prevents other inputs
 	// with the same identifier from being batched together.
 	ExclusiveGroup *uint64
@@ -211,13 +206,6 @@ func (p *SweeperInput) String() string {
 	return fmt.Sprintf("%v (%v)", p.Input.OutPoint(), p.Input.WitnessType())
 }
 
-// parameters returns the sweep parameters for this input.
-//
-// NOTE: Part of the txInput interface.
-func (p *SweeperInput) parameters() Params {
-	return p.params
-}
-
 // terminated returns a boolean indicating whether the input has reached a
 // final state.
 func (p *SweeperInput) terminated() bool {
@@ -512,18 +500,6 @@ func (s *UtxoSweeper) SweepInput(inp input.Input,
 		return nil, errors.New("nil input received")
 	}
 
-	// Ensure the client provided a sane fee preference.
-	//
-	// TODO(yy): remove this check?
-	if params.Fee != nil {
-		_, err := params.Fee.Estimate(
-			s.cfg.FeeEstimator, s.cfg.MaxFeeRate.FeePerKWeight(),
-		)
-		if err != nil {
-			return nil, err
-		}
-	}
-
 	absoluteTimeLock, _ := inp.RequiredLockTime()
 	log.Infof("Sweep request received: out_point=%v, witness_type=%v, "+
 		"relative_time_lock=%v, absolute_time_lock=%v, amount=%v, "+
@@ -1145,7 +1121,6 @@ func (s *UtxoSweeper) handleUpdateReq(req *updateReq) (
 	// Create the updated parameters struct. Leave the exclusive group
 	// unchanged.
 	newParams := Params{
-		Fee:             req.params.Fee,
 		StartingFeeRate: req.params.StartingFeeRate,
 		Immediate:       req.params.Immediate,
 		Budget:          req.params.Budget,
diff --git a/sweep/tx_input_set.go b/sweep/tx_input_set.go
index 5d7b3f7973..cfa4a0ba4e 100644
--- a/sweep/tx_input_set.go
+++ b/sweep/tx_input_set.go
@@ -14,23 +14,6 @@ import (
 	"github.com/lightningnetwork/lnd/lnwallet/chainfee"
 )
 
-// addConstraints defines the constraints to apply when adding an input.
-type addConstraints uint8
-
-const (
-	// constraintsRegular is for regular input sweeps that should have a positive
-	// yield.
-	constraintsRegular addConstraints = iota
-
-	// constraintsWallet is for wallet inputs that are only added to bring up the tx
-	// output value.
-	constraintsWallet
-
-	// constraintsForce is for inputs that should be swept even with a negative
-	// yield at the set fee rate.
-	constraintsForce
-)
-
 var (
 	// ErrNotEnoughInputs is returned when there are not enough wallet
 	// inputs to construct a non-dust change output for an input set.
@@ -83,443 +66,6 @@ type InputSet interface {
 	StartingFeeRate() fn.Option[chainfee.SatPerKWeight]
 }
 
-type txInputSetState struct {
-	// feeRate is the fee rate to use for the sweep transaction.
-	feeRate chainfee.SatPerKWeight
-
-	// maxFeeRate is the max allowed fee rate configured by the user.
-	maxFeeRate chainfee.SatPerKWeight
-
-	// inputTotal is the total value of all inputs.
-	inputTotal btcutil.Amount
-
-	// requiredOutput is the sum of the outputs committed to by the inputs.
-	requiredOutput btcutil.Amount
-
-	// changeOutput is the value of the change output. This will be what is
-	// left over after subtracting the requiredOutput and the tx fee from
-	// the inputTotal.
-	//
-	// NOTE: This might be below the dust limit, or even negative since it
-	// is the change remaining in csse we pay the fee for a change output.
-	changeOutput btcutil.Amount
-
-	// inputs is the set of tx inputs.
-	inputs []input.Input
-
-	// walletInputTotal is the total value of inputs coming from the wallet.
-	walletInputTotal btcutil.Amount
-
-	// force indicates that this set must be swept even if the total yield
-	// is negative.
-	force bool
-}
-
-// weightEstimate is the (worst case) tx weight with the current set of
-// inputs. It takes a parameter whether to add a change output or not.
-func (t *txInputSetState) weightEstimate(change bool) *weightEstimator {
-	weightEstimate := newWeightEstimator(t.feeRate, t.maxFeeRate)
-	for _, i := range t.inputs {
-		// Can ignore error, because it has already been checked when
-		// calculating the yields.
-		_ = weightEstimate.add(i)
-
-		r := i.RequiredTxOut()
-		if r != nil {
-			weightEstimate.addOutput(r)
-		}
-	}
-
-	// Add a change output to the weight estimate if requested.
-	if change {
-		weightEstimate.addP2TROutput()
-	}
-
-	return weightEstimate
-}
-
-// totalOutput is the total amount left for us after paying fees.
-//
-// NOTE: This might be dust.
-func (t *txInputSetState) totalOutput() btcutil.Amount {
-	return t.requiredOutput + t.changeOutput
-}
-
-func (t *txInputSetState) clone() txInputSetState {
-	s := txInputSetState{
-		feeRate:          t.feeRate,
-		inputTotal:       t.inputTotal,
-		changeOutput:     t.changeOutput,
-		requiredOutput:   t.requiredOutput,
-		walletInputTotal: t.walletInputTotal,
-		force:            t.force,
-		inputs:           make([]input.Input, len(t.inputs)),
-	}
-	copy(s.inputs, t.inputs)
-
-	return s
-}
-
-// txInputSet is an object that accumulates tx inputs and keeps running counters
-// on various properties of the tx.
-type txInputSet struct {
-	txInputSetState
-
-	// maxInputs is the maximum number of inputs that will be accepted in
-	// the set.
-	maxInputs uint32
-}
-
-// Compile-time constraint to ensure txInputSet implements InputSet.
-var _ InputSet = (*txInputSet)(nil)
-
-// newTxInputSet constructs a new, empty input set.
-func newTxInputSet(feePerKW, maxFeeRate chainfee.SatPerKWeight,
-	maxInputs uint32) *txInputSet {
-
-	state := txInputSetState{
-		feeRate:    feePerKW,
-		maxFeeRate: maxFeeRate,
-	}
-
-	b := txInputSet{
-		maxInputs:       maxInputs,
-		txInputSetState: state,
-	}
-
-	return &b
-}
-
-// Inputs returns the inputs that should be used to create a tx.
-func (t *txInputSet) Inputs() []input.Input {
-	return t.inputs
-}
-
-// Budget gives the total amount that can be used as fees by this input set.
-//
-// NOTE: this field is only used for `BudgetInputSet`.
-func (t *txInputSet) Budget() btcutil.Amount {
-	return t.totalOutput()
-}
-
-// DeadlineHeight gives the block height that this set must be confirmed by.
-//
-// NOTE: this field is only used for `BudgetInputSet`.
-func (t *txInputSet) DeadlineHeight() int32 {
-	return 0
-}
-
-// StartingFeeRate returns the max starting fee rate found in the inputs.
-//
-// NOTE: this field is only used for `BudgetInputSet`.
-func (t *txInputSet) StartingFeeRate() fn.Option[chainfee.SatPerKWeight] {
-	return fn.None[chainfee.SatPerKWeight]()
-}
-
-// NeedWalletInput returns true if the input set needs more wallet inputs.
-func (t *txInputSet) NeedWalletInput() bool {
-	return !t.enoughInput()
-}
-
-// enoughInput returns true if we've accumulated enough inputs to pay the fees
-// and have at least one output that meets the dust limit.
-func (t *txInputSet) enoughInput() bool {
-	// If we have a change output above dust, then we certainly have enough
-	// inputs to the transaction.
-	if t.changeOutput >= lnwallet.DustLimitForSize(input.P2TRSize) {
-		return true
-	}
-
-	// We did not have enough input for a change output. Check if we have
-	// enough input to pay the fees for a transaction with no change
-	// output.
-	fee := t.weightEstimate(false).feeWithParent()
-	if t.inputTotal < t.requiredOutput+fee {
-		return false
-	}
-
-	// We could pay the fees, but we still need at least one output to be
-	// above the dust limit for the tx to be valid (we assume that these
-	// required outputs only get added if they are above dust)
-	for _, inp := range t.inputs {
-		if inp.RequiredTxOut() != nil {
-			return true
-		}
-	}
-
-	return false
-}
-
-// add adds a new input to the set. It returns a bool indicating whether the
-// input was added to the set. An input is rejected if it decreases the tx
-// output value after paying fees.
-func (t *txInputSet) addToState(inp input.Input,
-	constraints addConstraints) *txInputSetState {
-
-	// Stop if max inputs is reached. Do not count additional wallet inputs,
-	// because we don't know in advance how many we may need.
-	if constraints != constraintsWallet &&
-		uint32(len(t.inputs)) >= t.maxInputs {
-
-		return nil
-	}
-
-	// If the input comes with a required tx out that is below dust, we
-	// won't add it.
-	//
-	// NOTE: only HtlcSecondLevelAnchorInput returns non-nil RequiredTxOut.
-	reqOut := inp.RequiredTxOut()
-	if reqOut != nil {
-		// Fetch the dust limit for this output.
-		dustLimit := lnwallet.DustLimitForSize(len(reqOut.PkScript))
-		if btcutil.Amount(reqOut.Value) < dustLimit {
-			log.Errorf("Rejected input=%v due to dust required "+
-				"output=%v, limit=%v", inp, reqOut.Value,
-				dustLimit)
-
-			// TODO(yy): we should not return here for force
-			// sweeps. This means when sending sweeping request,
-			// one must be careful to not create dust outputs. In
-			// an extreme rare case, where the
-			// minRelayTxFee/discardfee is increased when sending
-			// the request, what's considered non-dust at the
-			// caller side will be dust here, causing a force sweep
-			// to fail.
-			return nil
-		}
-	}
-
-	// Clone the current set state.
-	newSet := t.clone()
-
-	// Add the new input.
-	newSet.inputs = append(newSet.inputs, inp)
-
-	// Add the value of the new input.
-	value := btcutil.Amount(inp.SignDesc().Output.Value)
-	newSet.inputTotal += value
-
-	// Recalculate the tx fee.
-	fee := newSet.weightEstimate(true).feeWithParent()
-
-	// Calculate the new output value.
-	if reqOut != nil {
-		newSet.requiredOutput += btcutil.Amount(reqOut.Value)
-	}
-
-	// NOTE: `changeOutput` could be negative here if this input is using
-	// constraintsForce.
-	newSet.changeOutput = newSet.inputTotal - newSet.requiredOutput - fee
-
-	// Calculate the yield of this input from the change in total tx output
-	// value.
-	inputYield := newSet.totalOutput() - t.totalOutput()
-
-	switch constraints {
-	// Don't sweep inputs that cost us more to sweep than they give us.
-	case constraintsRegular:
-		if inputYield <= 0 {
-			log.Debugf("Rejected regular input=%v due to negative "+
-				"yield=%v", value, inputYield)
-
-			return nil
-		}
-
-	// For force adds, no further constraints apply.
-	//
-	// NOTE: because the inputs are sorted with force sweeps being placed
-	// at the start of the list, we should never see an input with
-	// constraintsForce come after an input with constraintsRegular. In
-	// other words, though we may have negative `changeOutput` from
-	// including force sweeps, `inputYield` should always increase when
-	// adding regular inputs.
-	case constraintsForce:
-		newSet.force = true
-
-	// We are attaching a wallet input to raise the tx output value above
-	// the dust limit.
-	case constraintsWallet:
-		// Skip this wallet input if adding it would lower the output
-		// value.
-		//
-		// TODO(yy): change to inputYield < 0 to allow sweeping for
-		// UTXO aggregation only?
-		if inputYield <= 0 {
-			log.Debugf("Rejected wallet input=%v due to negative "+
-				"yield=%v", value, inputYield)
-
-			return nil
-		}
-
-		// Calculate the total value that we spend in this tx from the
-		// wallet if we'd add this wallet input.
-		newSet.walletInputTotal += value
-
-		// In any case, we don't want to lose money by sweeping. If we
-		// don't get more out of the tx than we put in ourselves, do not
-		// add this wallet input. If there is at least one force sweep
-		// in the set, this does no longer apply.
-		//
-		// We should only add wallet inputs to get the tx output value
-		// above the dust limit, otherwise we'd only burn into fees.
-		// This is guarded by tryAddWalletInputsIfNeeded.
-		//
-		// TODO(joostjager): Possibly require a max ratio between the
-		// value of the wallet input and what we get out of this
-		// transaction. To prevent attaching and locking a big utxo for
-		// very little benefit.
-		if newSet.force {
-			break
-		}
-
-		// TODO(yy): change from `>=` to `>` to allow non-negative
-		// sweeping - we won't gain more coins from this sweep, but
-		// aggregating small UTXOs.
-		if newSet.walletInputTotal >= newSet.totalOutput() {
-			// TODO(yy): further check this case as it seems we can
-			// never reach here because it'd mean `inputYield` is
-			// already <= 0?
-			log.Debugf("Rejecting wallet input of %v, because it "+
-				"would make a negative yielding transaction "+
-				"(%v)", value,
-				newSet.totalOutput()-newSet.walletInputTotal)
-
-			return nil
-		}
-	}
-
-	return &newSet
-}
-
-// add adds a new input to the set. It returns a bool indicating whether the
-// input was added to the set. An input is rejected if it decreases the tx
-// output value after paying fees.
-func (t *txInputSet) add(input input.Input, constraints addConstraints) bool {
-	newState := t.addToState(input, constraints)
-	if newState == nil {
-		return false
-	}
-
-	t.txInputSetState = *newState
-
-	return true
-}
-
-// addPositiveYieldInputs adds sweepableInputs that have a positive yield to the
-// input set. This function assumes that the list of inputs is sorted descending
-// by yield.
-//
-// TODO(roasbeef): Consider including some negative yield inputs too to clean
-// up the utxo set even if it costs us some fees up front.  In the spirit of
-// minimizing any negative externalities we cause for the Bitcoin system as a
-// whole.
-func (t *txInputSet) addPositiveYieldInputs(sweepableInputs []*SweeperInput) {
-	for i, inp := range sweepableInputs {
-		// Apply relaxed constraints for force sweeps.
-		constraints := constraintsRegular
-		if inp.parameters().Immediate {
-			constraints = constraintsForce
-		}
-
-		// Try to add the input to the transaction. If that doesn't
-		// succeed because it wouldn't increase the output value,
-		// return. Assuming inputs are sorted by yield, any further
-		// inputs wouldn't increase the output value either.
-		if !t.add(inp, constraints) {
-			var rem []input.Input
-			for j := i; j < len(sweepableInputs); j++ {
-				rem = append(rem, sweepableInputs[j])
-			}
-			log.Debugf("%d negative yield inputs not added to "+
-				"input set: %v", len(rem),
-				inputTypeSummary(rem))
-			return
-		}
-
-		log.Debugf("Added positive yield input %v to input set",
-			inputTypeSummary([]input.Input{inp}))
-	}
-
-	// We managed to add all inputs to the set.
-}
-
-// AddWalletInputs adds wallet inputs to the set until a non-dust output can be
-// made. This non-dust output is either a change output or a required output.
-// Return an error if there are not enough wallet inputs.
-func (t *txInputSet) AddWalletInputs(wallet Wallet) error {
-	// Check the current output value and add wallet utxos if needed to
-	// push the output value to the lower limit.
-	if err := t.tryAddWalletInputsIfNeeded(wallet); err != nil {
-		return err
-	}
-
-	// If the output value of this block of inputs does not reach the dust
-	// limit, stop sweeping. Because of the sorting, continuing with the
-	// remaining inputs will only lead to sets with an even lower output
-	// value.
-	if !t.enoughInput() {
-		// The change output is always a p2tr here.
-		dl := lnwallet.DustLimitForSize(input.P2TRSize)
-		log.Debugf("Input set value %v (required=%v, change=%v) "+
-			"below dust limit of %v", t.totalOutput(),
-			t.requiredOutput, t.changeOutput, dl)
-
-		return ErrNotEnoughInputs
-	}
-
-	return nil
-}
-
-// tryAddWalletInputsIfNeeded retrieves utxos from the wallet and tries adding
-// as many as required to bring the tx output value above the given minimum.
-func (t *txInputSet) tryAddWalletInputsIfNeeded(wallet Wallet) error {
-	// If we've already have enough to pay the transaction fees and have at
-	// least one output materialize, no action is needed.
-	if t.enoughInput() {
-		return nil
-	}
-
-	// Retrieve wallet utxos. Only consider confirmed utxos to prevent
-	// problems around RBF rules for unconfirmed inputs. This currently
-	// ignores the configured coin selection strategy.
-	utxos, err := wallet.ListUnspentWitnessFromDefaultAccount(
-		1, math.MaxInt32,
-	)
-	if err != nil {
-		return err
-	}
-
-	// Sort the UTXOs by putting smaller values at the start of the slice
-	// to avoid locking large UTXO for sweeping.
-	//
-	// TODO(yy): add more choices to CoinSelectionStrategy and use the
-	// configured value here.
-	sort.Slice(utxos, func(i, j int) bool {
-		return utxos[i].Value < utxos[j].Value
-	})
-
-	for _, utxo := range utxos {
-		input, err := createWalletTxInput(utxo)
-		if err != nil {
-			return err
-		}
-
-		// If the wallet input isn't positively-yielding at this fee
-		// rate, skip it.
-		if !t.add(input, constraintsWallet) {
-			continue
-		}
-
-		// Return if we've reached the minimum output amount.
-		if t.enoughInput() {
-			return nil
-		}
-	}
-
-	// We were not able to reach the minimum output amount.
-	return nil
-}
-
 // createWalletTxInput converts a wallet utxo into an object that can be added
 // to the other inputs to sweep.
 func createWalletTxInput(utxo *lnwallet.Utxo) (input.Input, error) {


Test patch:
diff --git a/contractcourt/commit_sweep_resolver_test.go b/contractcourt/commit_sweep_resolver_test.go
index bf6f70cbc1..f2b43b0f80 100644
--- a/contractcourt/commit_sweep_resolver_test.go
+++ b/contractcourt/commit_sweep_resolver_test.go
@@ -1,7 +1,6 @@
 package contractcourt
 
 import (
-	"fmt"
 	"testing"
 	"time"
 
@@ -135,20 +134,6 @@ func (s *mockSweeper) SweepInput(input input.Input, params sweep.Params) (
 
 	s.sweptInputs <- input
 
-	// TODO(yy): replace mockSweeper with `mock.Mock`.
-	if params.Fee != nil {
-		fee, ok := params.Fee.(sweep.FeeEstimateInfo)
-		if !ok {
-			return nil, fmt.Errorf("unexpected fee type: %T",
-				params.Fee)
-		}
-
-		// Update the deadlines used if it's set.
-		if fee.ConfTarget != 0 {
-			s.deadlines = append(s.deadlines, int(fee.ConfTarget))
-		}
-	}
-
 	// Update the deadlines used if it's set.
 	params.DeadlineHeight.WhenSome(func(d int32) {
 		s.deadlines = append(s.deadlines, int(d))
diff --git a/sweep/aggregator_test.go b/sweep/aggregator_test.go
index b9b86379b4..289f2780e4 100644
--- a/sweep/aggregator_test.go
+++ b/sweep/aggregator_test.go
@@ -3,430 +3,21 @@ package sweep
 import (
 	"bytes"
 	"errors"
-	"reflect"
-	"sort"
 	"testing"
 
 	"github.com/btcsuite/btcd/btcutil"
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
 	"github.com/btcsuite/btcd/wire"
-	"github.com/davecgh/go-spew/spew"
 	"github.com/lightningnetwork/lnd/fn"
 	"github.com/lightningnetwork/lnd/input"
 	"github.com/lightningnetwork/lnd/lnwallet/chainfee"
 	"github.com/stretchr/testify/require"
 )
 
-//nolint:lll
 var (
-	testInputsA = InputsMap{
-		wire.OutPoint{Hash: chainhash.Hash{}, Index: 0}: &SweeperInput{},
-		wire.OutPoint{Hash: chainhash.Hash{}, Index: 1}: &SweeperInput{},
-		wire.OutPoint{Hash: chainhash.Hash{}, Index: 2}: &SweeperInput{},
-	}
-
-	testInputsB = InputsMap{
-		wire.OutPoint{Hash: chainhash.Hash{}, Index: 10}: &SweeperInput{},
-		wire.OutPoint{Hash: chainhash.Hash{}, Index: 11}: &SweeperInput{},
-		wire.OutPoint{Hash: chainhash.Hash{}, Index: 12}: &SweeperInput{},
-	}
-
-	testInputsC = InputsMap{
-		wire.OutPoint{Hash: chainhash.Hash{}, Index: 0}:  &SweeperInput{},
-		wire.OutPoint{Hash: chainhash.Hash{}, Index: 1}:  &SweeperInput{},
-		wire.OutPoint{Hash: chainhash.Hash{}, Index: 2}:  &SweeperInput{},
-		wire.OutPoint{Hash: chainhash.Hash{}, Index: 10}: &SweeperInput{},
-		wire.OutPoint{Hash: chainhash.Hash{}, Index: 11}: &SweeperInput{},
-		wire.OutPoint{Hash: chainhash.Hash{}, Index: 12}: &SweeperInput{},
-	}
-
 	testHeight = int32(800000)
 )
 
-// TestMergeClusters check that we properly can merge clusters together,
-// according to their required locktime.
-func TestMergeClusters(t *testing.T) {
-	t.Parallel()
-
-	lockTime1 := uint32(100)
-	lockTime2 := uint32(200)
-
-	testCases := []struct {
-		name string
-		a    inputCluster
-		b    inputCluster
-		res  []inputCluster
-	}{
-		{
-			name: "max fee rate",
-			a: inputCluster{
-				sweepFeeRate: 5000,
-				inputs:       testInputsA,
-			},
-			b: inputCluster{
-				sweepFeeRate: 7000,
-				inputs:       testInputsB,
-			},
-			res: []inputCluster{
-				{
-					sweepFeeRate: 7000,
-					inputs:       testInputsC,
-				},
-			},
-		},
-		{
-			name: "same locktime",
-			a: inputCluster{
-				lockTime:     &lockTime1,
-				sweepFeeRate: 5000,
-				inputs:       testInputsA,
-			},
-			b: inputCluster{
-				lockTime:     &lockTime1,
-				sweepFeeRate: 7000,
-				inputs:       testInputsB,
-			},
-			res: []inputCluster{
-				{
-					lockTime:     &lockTime1,
-					sweepFeeRate: 7000,
-					inputs:       testInputsC,
-				},
-			},
-		},
-		{
-			name: "diff locktime",
-			a: inputCluster{
-				lockTime:     &lockTime1,
-				sweepFeeRate: 5000,
-				inputs:       testInputsA,
-			},
-			b: inputCluster{
-				lockTime:     &lockTime2,
-				sweepFeeRate: 7000,
-				inputs:       testInputsB,
-			},
-			res: []inputCluster{
-				{
-					lockTime:     &lockTime1,
-					sweepFeeRate: 5000,
-					inputs:       testInputsA,
-				},
-				{
-					lockTime:     &lockTime2,
-					sweepFeeRate: 7000,
-					inputs:       testInputsB,
-				},
-			},
-		},
-	}
-
-	for _, test := range testCases {
-		merged := mergeClusters(test.a, test.b)
-		if !reflect.DeepEqual(merged, test.res) {
-			t.Fatalf("[%s] unexpected result: %v",
-				test.name, spew.Sdump(merged))
-		}
-	}
-}
-
-// TestZipClusters tests that we can merge lists of inputs clusters correctly.
-func TestZipClusters(t *testing.T) {
-	t.Parallel()
-
-	createCluster := func(inp InputsMap,
-		f chainfee.SatPerKWeight) inputCluster {
-
-		return inputCluster{
-			sweepFeeRate: f,
-			inputs:       inp,
-		}
-	}
-
-	testCases := []struct {
-		name string
-		as   []inputCluster
-		bs   []inputCluster
-		res  []inputCluster
-	}{
-		{
-			name: "merge A into B",
-			as: []inputCluster{
-				createCluster(testInputsA, 5000),
-			},
-			bs: []inputCluster{
-				createCluster(testInputsB, 7000),
-			},
-			res: []inputCluster{
-				createCluster(testInputsC, 7000),
-			},
-		},
-		{
-			name: "A can't merge with B",
-			as: []inputCluster{
-				createCluster(testInputsA, 7000),
-			},
-			bs: []inputCluster{
-				createCluster(testInputsB, 5000),
-			},
-			res: []inputCluster{
-				createCluster(testInputsA, 7000),
-				createCluster(testInputsB, 5000),
-			},
-		},
-		{
-			name: "empty bs",
-			as: []inputCluster{
-				createCluster(testInputsA, 7000),
-			},
-			bs: []inputCluster{},
-			res: []inputCluster{
-				createCluster(testInputsA, 7000),
-			},
-		},
-		{
-			name: "empty as",
-			as:   []inputCluster{},
-			bs: []inputCluster{
-				createCluster(testInputsB, 5000),
-			},
-			res: []inputCluster{
-				createCluster(testInputsB, 5000),
-			},
-		},
-
-		{
-			name: "zip 3xA into 3xB",
-			as: []inputCluster{
-				createCluster(testInputsA, 5000),
-				createCluster(testInputsA, 5000),
-				createCluster(testInputsA, 5000),
-			},
-			bs: []inputCluster{
-				createCluster(testInputsB, 7000),
-				createCluster(testInputsB, 7000),
-				createCluster(testInputsB, 7000),
-			},
-			res: []inputCluster{
-				createCluster(testInputsC, 7000),
-				createCluster(testInputsC, 7000),
-				createCluster(testInputsC, 7000),
-			},
-		},
-		{
-			name: "zip A into 3xB",
-			as: []inputCluster{
-				createCluster(testInputsA, 2500),
-			},
-			bs: []inputCluster{
-				createCluster(testInputsB, 3000),
-				createCluster(testInputsB, 2000),
-				createCluster(testInputsB, 1000),
-			},
-			res: []inputCluster{
-				createCluster(testInputsC, 3000),
-				createCluster(testInputsB, 2000),
-				createCluster(testInputsB, 1000),
-			},
-		},
-	}
-
-	for _, test := range testCases {
-		zipped := zipClusters(test.as, test.bs)
-		if !reflect.DeepEqual(zipped, test.res) {
-			t.Fatalf("[%s] unexpected result: %v",
-				test.name, spew.Sdump(zipped))
-		}
-	}
-}
-
-// TestClusterByLockTime tests the method clusterByLockTime works as expected.
-func TestClusterByLockTime(t *testing.T) {
-	t.Parallel()
-
-	// Create a mock FeePreference.
-	mockFeePref := &MockFeePreference{}
-
-	// Create a test param with a dummy fee preference. This is needed so
-	// `feeRateForPreference` won't throw an error.
-	param := Params{Fee: mockFeePref}
-
-	// We begin the test by creating three clusters of inputs, the first
-	// cluster has a locktime of 1, the second has a locktime of 2, and the
-	// final has no locktime.
-	lockTime1 := uint32(1)
-	lockTime2 := uint32(2)
-
-	// Create cluster one, which has a locktime of 1.
-	input1LockTime1 := &input.MockInput{}
-	input2LockTime1 := &input.MockInput{}
-	input1LockTime1.On("RequiredLockTime").Return(lockTime1, true)
-	input2LockTime1.On("RequiredLockTime").Return(lockTime1, true)
-
-	// Create cluster two, which has a locktime of 2.
-	input3LockTime2 := &input.MockInput{}
-	input4LockTime2 := &input.MockInput{}
-	input3LockTime2.On("RequiredLockTime").Return(lockTime2, true)
-	input4LockTime2.On("RequiredLockTime").Return(lockTime2, true)
-
-	// Create cluster three, which has no locktime.
-	input5NoLockTime := &input.MockInput{}
-	input6NoLockTime := &input.MockInput{}
-	input5NoLockTime.On("RequiredLockTime").Return(uint32(0), false)
-	input6NoLockTime.On("RequiredLockTime").Return(uint32(0), false)
-
-	// With the inner Input being mocked, we can now create the pending
-	// inputs.
-	input1 := &SweeperInput{Input: input1LockTime1, params: param}
-	input2 := &SweeperInput{Input: input2LockTime1, params: param}
-	input3 := &SweeperInput{Input: input3LockTime2, params: param}
-	input4 := &SweeperInput{Input: input4LockTime2, params: param}
-	input5 := &SweeperInput{Input: input5NoLockTime, params: param}
-	input6 := &SweeperInput{Input: input6NoLockTime, params: param}
-
-	// Create the pending inputs map, which will be passed to the method
-	// under test.
-	//
-	// NOTE: we don't care the actual outpoint values as long as they are
-	// unique.
-	inputs := InputsMap{
-		wire.OutPoint{Index: 1}: input1,
-		wire.OutPoint{Index: 2}: input2,
-		wire.OutPoint{Index: 3}: input3,
-		wire.OutPoint{Index: 4}: input4,
-		wire.OutPoint{Index: 5}: input5,
-		wire.OutPoint{Index: 6}: input6,
-	}
-
-	// Create expected clusters so we can shorten the line length in the
-	// test cases below.
-	cluster1 := InputsMap{
-		wire.OutPoint{Index: 1}: input1,
-		wire.OutPoint{Index: 2}: input2,
-	}
-	cluster2 := InputsMap{
-		wire.OutPoint{Index: 3}: input3,
-		wire.OutPoint{Index: 4}: input4,
-	}
-
-	// cluster3 should be the remaining inputs since they don't have
-	// locktime.
-	cluster3 := InputsMap{
-		wire.OutPoint{Index: 5}: input5,
-		wire.OutPoint{Index: 6}: input6,
-	}
-
-	const (
-		// Set the min fee rate to be 1000 sat/kw.
-		minFeeRate = chainfee.SatPerKWeight(1000)
-
-		// Set the max fee rate to be 10,000 sat/kw.
-		maxFeeRate = chainfee.SatPerKWeight(10_000)
-	)
-
-	// Create a test aggregator.
-	s := NewSimpleUtxoAggregator(nil, maxFeeRate, 100)
-
-	testCases := []struct {
-		name string
-		// setupMocker takes a testing fee rate and makes a mocker over
-		// `Estimate` that always return the testing fee rate.
-		setupMocker             func()
-		testFeeRate             chainfee.SatPerKWeight
-		expectedClusters        []inputCluster
-		expectedRemainingInputs InputsMap
-	}{
-		{
-			// Test a successful case where the locktime clusters
-			// are created and the no-locktime cluster is returned
-			// as the remaining inputs.
-			name: "successfully create clusters",
-			setupMocker: func() {
-				// Expect the four inputs with locktime to call
-				// this method.
-				mockFeePref.On("Estimate", nil, maxFeeRate).
-					Return(minFeeRate+1, nil).Times(4)
-			},
-			// Use a fee rate above the min value so we don't hit
-			// an error when performing fee estimation.
-			//
-			// TODO(yy): we should customize the returned fee rate
-			// for each input to further test the averaging logic.
-			// Or we can split the method into two, one for
-			// grouping the clusters and the other for averaging
-			// the fee rates so it's easier to be tested.
-			testFeeRate: minFeeRate + 1,
-			expectedClusters: []inputCluster{
-				{
-					lockTime:     &lockTime1,
-					sweepFeeRate: minFeeRate + 1,
-					inputs:       cluster1,
-				},
-				{
-					lockTime:     &lockTime2,
-					sweepFeeRate: minFeeRate + 1,
-					inputs:       cluster2,
-				},
-			},
-			expectedRemainingInputs: cluster3,
-		},
-		{
-			// Test that when the input is skipped when the fee
-			// estimation returns an error.
-			name: "error from fee estimation",
-			setupMocker: func() {
-				mockFeePref.On("Estimate", nil, maxFeeRate).
-					Return(chainfee.SatPerKWeight(0),
-						errors.New("dummy")).Times(4)
-			},
-
-			// Use a fee rate below the min value so we hit an
-			// error when performing fee estimation.
-			testFeeRate:      minFeeRate - 1,
-			expectedClusters: []inputCluster{},
-			// Remaining inputs should stay untouched.
-			expectedRemainingInputs: cluster3,
-		},
-	}
-
-	//nolint:paralleltest
-	for _, tc := range testCases {
-		tc := tc
-
-		t.Run(tc.name, func(t *testing.T) {
-			// Apply the test fee rate so `feeRateForPreference` is
-			// mocked to return the specified value.
-			tc.setupMocker()
-
-			// Assert the mocked methods are called as expeceted.
-			defer mockFeePref.AssertExpectations(t)
-
-			// Call the method under test.
-			clusters, remainingInputs := s.clusterByLockTime(inputs)
-
-			// Sort by locktime as the order is not guaranteed.
-			sort.Slice(clusters, func(i, j int) bool {
-				return *clusters[i].lockTime <
-					*clusters[j].lockTime
-			})
-
-			// Validate the values are returned as expected.
-			require.Equal(t, tc.expectedClusters, clusters)
-			require.Equal(t, tc.expectedRemainingInputs,
-				remainingInputs,
-			)
-
-			// Assert the mocked methods are called as expeceted.
-			input1LockTime1.AssertExpectations(t)
-			input2LockTime1.AssertExpectations(t)
-			input3LockTime2.AssertExpectations(t)
-			input4LockTime2.AssertExpectations(t)
-			input5NoLockTime.AssertExpectations(t)
-			input6NoLockTime.AssertExpectations(t)
-		})
-	}
-}
-
 // TestBudgetAggregatorFilterInputs checks that inputs with low budget are
 // filtered out.
 func TestBudgetAggregatorFilterInputs(t *testing.T) {
diff --git a/sweep/fee_bumper_test.go b/sweep/fee_bumper_test.go
index e2b2cfe14e..63a828654d 100644
--- a/sweep/fee_bumper_test.go
+++ b/sweep/fee_bumper_test.go
@@ -2,14 +2,17 @@ package sweep
 
 import (
 	"fmt"
+	"sync/atomic"
 	"testing"
 	"time"
 
 	"github.com/btcsuite/btcd/btcutil"
+	"github.com/btcsuite/btcd/chaincfg/chainhash"
 	"github.com/btcsuite/btcd/rpcclient"
 	"github.com/btcsuite/btcd/wire"
 	"github.com/lightningnetwork/lnd/chainntnfs"
 	"github.com/lightningnetwork/lnd/input"
+	"github.com/lightningnetwork/lnd/keychain"
 	"github.com/lightningnetwork/lnd/lnwallet"
 	"github.com/lightningnetwork/lnd/lnwallet/chainfee"
 	"github.com/stretchr/testify/mock"
@@ -25,8 +28,37 @@ var (
 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	}
+
+	testInputCount atomic.Uint64
 )
 
+func createTestInput(value int64,
+	witnessType input.WitnessType) input.BaseInput {
+
+	hash := chainhash.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		byte(testInputCount.Add(1))}
+
+	input := input.MakeBaseInput(
+		&wire.OutPoint{
+			Hash: hash,
+		},
+		witnessType,
+		&input.SignDescriptor{
+			Output: &wire.TxOut{
+				Value: value,
+			},
+			KeyDesc: keychain.KeyDescriptor{
+				PubKey: testPubKey,
+			},
+		},
+		0,
+		nil,
+	)
+
+	return input
+}
+
 // TestBumpResultValidate tests the validate method of the BumpResult struct.
 func TestBumpResultValidate(t *testing.T) {
 	t.Parallel()
diff --git a/sweep/mock_test.go b/sweep/mock_test.go
index 356d2e3a8c..605b8d14ec 100644
--- a/sweep/mock_test.go
+++ b/sweep/mock_test.go
@@ -1,10 +1,6 @@
 package sweep
 
 import (
-	"sync"
-	"testing"
-	"time"
-
 	"github.com/btcsuite/btcd/btcutil"
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
 	"github.com/btcsuite/btcd/wire"
@@ -15,251 +11,6 @@ import (
 	"github.com/stretchr/testify/mock"
 )
 
-// mockBackend simulates a chain backend for realistic behaviour in unit tests
-// around double spends.
-type mockBackend struct {
-	t *testing.T
-
-	lock sync.Mutex
-
-	notifier *MockNotifier
-
-	confirmedSpendInputs map[wire.OutPoint]struct{}
-
-	unconfirmedTxes        map[chainhash.Hash]*wire.MsgTx
-	unconfirmedSpendInputs map[wire.OutPoint]struct{}
-
-	publishChan chan wire.MsgTx
-
-	walletUtxos []*lnwallet.Utxo
-	utxoCnt     int
-}
-
-func newMockBackend(t *testing.T, notifier *MockNotifier) *mockBackend {
-	return &mockBackend{
-		t:                      t,
-		notifier:               notifier,
-		unconfirmedTxes:        make(map[chainhash.Hash]*wire.MsgTx),
-		confirmedSpendInputs:   make(map[wire.OutPoint]struct{}),
-		unconfirmedSpendInputs: make(map[wire.OutPoint]struct{}),
-		publishChan:            make(chan wire.MsgTx, 2),
-	}
-}
-
-func (b *mockBackend) BackEnd() string {
-	return "mockbackend"
-}
-
-func (b *mockBackend) CheckMempoolAcceptance(tx *wire.MsgTx) error {
-	return nil
-}
-
-func (b *mockBackend) publishTransaction(tx *wire.MsgTx) error {
-	b.lock.Lock()
-	defer b.lock.Unlock()
-
-	txHash := tx.TxHash()
-	if _, ok := b.unconfirmedTxes[txHash]; ok {
-		// Tx already exists
-		testLog.Tracef("mockBackend duplicate tx %v", tx.TxHash())
-		return lnwallet.ErrDoubleSpend
-	}
-
-	for _, in := range tx.TxIn {
-		if _, ok := b.unconfirmedSpendInputs[in.PreviousOutPoint]; ok {
-			// Double spend
-			testLog.Tracef("mockBackend double spend tx %v",
-				tx.TxHash())
-			return lnwallet.ErrDoubleSpend
-		}
-
-		if _, ok := b.confirmedSpendInputs[in.PreviousOutPoint]; ok {
-			// Already included in block
-			testLog.Tracef("mockBackend already in block tx %v",
-				tx.TxHash())
-			return lnwallet.ErrDoubleSpend
-		}
-	}
-
-	b.unconfirmedTxes[txHash] = tx
-	for _, in := range tx.TxIn {
-		b.unconfirmedSpendInputs[in.PreviousOutPoint] = struct{}{}
-	}
-
-	testLog.Tracef("mockBackend publish tx %v", tx.TxHash())
-
-	return nil
-}
-
-func (b *mockBackend) PublishTransaction(tx *wire.MsgTx, _ string) error {
-	log.Tracef("Publishing tx %v", tx.TxHash())
-	err := b.publishTransaction(tx)
-	select {
-	case b.publishChan <- *tx:
-	case <-time.After(defaultTestTimeout):
-		b.t.Fatalf("unexpected tx published")
-	}
-
-	return err
-}
-
-func (b *mockBackend) ListUnspentWitnessFromDefaultAccount(minConfs,
-	maxConfs int32) ([]*lnwallet.Utxo, error) {
-
-	b.lock.Lock()
-	defer b.lock.Unlock()
-
-	// Each time we list output, we increment the utxo counter, to
-	// ensure we don't return the same outpoint every time.
-	b.utxoCnt++
-
-	for i := range b.walletUtxos {
-		b.walletUtxos[i].OutPoint.Hash[0] = byte(b.utxoCnt)
-	}
-
-	return b.walletUtxos, nil
-}
-
-func (b *mockBackend) WithCoinSelectLock(f func() error) error {
-	return f()
-}
-
-func (b *mockBackend) deleteUnconfirmed(txHash chainhash.Hash) {
-	b.lock.Lock()
-	defer b.lock.Unlock()
-
-	tx, ok := b.unconfirmedTxes[txHash]
-	if !ok {
-		// Tx already exists
-		testLog.Errorf("mockBackend delete tx not existing %v", txHash)
-		return
-	}
-
-	testLog.Tracef("mockBackend delete tx %v", tx.TxHash())
-	delete(b.unconfirmedTxes, txHash)
-	for _, in := range tx.TxIn {
-		delete(b.unconfirmedSpendInputs, in.PreviousOutPoint)
-	}
-}
-
-func (b *mockBackend) mine() {
-	b.lock.Lock()
-	defer b.lock.Unlock()
-
-	notifications := make(map[wire.OutPoint]*wire.MsgTx)
-	for _, tx := range b.unconfirmedTxes {
-		testLog.Tracef("mockBackend mining tx %v", tx.TxHash())
-		for _, in := range tx.TxIn {
-			b.confirmedSpendInputs[in.PreviousOutPoint] = struct{}{}
-			notifications[in.PreviousOutPoint] = tx
-		}
-	}
-	b.unconfirmedSpendInputs = make(map[wire.OutPoint]struct{})
-	b.unconfirmedTxes = make(map[chainhash.Hash]*wire.MsgTx)
-
-	for outpoint, tx := range notifications {
-		testLog.Tracef("mockBackend delivering spend ntfn for %v",
-			outpoint)
-		b.notifier.SpendOutpoint(outpoint, *tx)
-	}
-}
-
-func (b *mockBackend) isDone() bool {
-	return len(b.unconfirmedTxes) == 0
-}
-
-func (b *mockBackend) RemoveDescendants(*wire.MsgTx) error {
-	return nil
-}
-
-func (b *mockBackend) FetchTx(chainhash.Hash) (*wire.MsgTx, error) {
-	return nil, nil
-}
-
-func (b *mockBackend) CancelRebroadcast(tx chainhash.Hash) {
-}
-
-// GetTransactionDetails returns a detailed description of a tx given its
-// transaction hash.
-func (b *mockBackend) GetTransactionDetails(txHash *chainhash.Hash) (
-	*lnwallet.TransactionDetail, error) {
-
-	return nil, nil
-}
-
-// mockFeeEstimator implements a mock fee estimator. It closely resembles
-// lnwallet.StaticFeeEstimator with the addition that fees can be changed for
-// testing purposes in a thread safe manner.
-//
-// TODO(yy): replace it with chainfee.MockEstimator once it's merged.
-type mockFeeEstimator struct {
-	feePerKW chainfee.SatPerKWeight
-
-	relayFee chainfee.SatPerKWeight
-
-	blocksToFee map[uint32]chainfee.SatPerKWeight
-
-	// A closure that when set is used instead of the
-	// mockFeeEstimator.EstimateFeePerKW method.
-	estimateFeePerKW func(numBlocks uint32) (chainfee.SatPerKWeight, error)
-
-	lock sync.Mutex
-}
-
-func newMockFeeEstimator(feePerKW,
-	relayFee chainfee.SatPerKWeight) *mockFeeEstimator {
-
-	return &mockFeeEstimator{
-		feePerKW:    feePerKW,
-		relayFee:    relayFee,
-		blocksToFee: make(map[uint32]chainfee.SatPerKWeight),
-	}
-}
-
-func (e *mockFeeEstimator) updateFees(feePerKW,
-	relayFee chainfee.SatPerKWeight) {
-
-	e.lock.Lock()
-	defer e.lock.Unlock()
-
-	e.feePerKW = feePerKW
-	e.relayFee = relayFee
-}
-
-func (e *mockFeeEstimator) EstimateFeePerKW(numBlocks uint32) (
-	chainfee.SatPerKWeight, error) {
-
-	e.lock.Lock()
-	defer e.lock.Unlock()
-
-	if e.estimateFeePerKW != nil {
-		return e.estimateFeePerKW(numBlocks)
-	}
-
-	if fee, ok := e.blocksToFee[numBlocks]; ok {
-		return fee, nil
-	}
-
-	return e.feePerKW, nil
-}
-
-func (e *mockFeeEstimator) RelayFeePerKW() chainfee.SatPerKWeight {
-	e.lock.Lock()
-	defer e.lock.Unlock()
-
-	return e.relayFee
-}
-
-func (e *mockFeeEstimator) Start() error {
-	return nil
-}
-
-func (e *mockFeeEstimator) Stop() error {
-	return nil
-}
-
-var _ chainfee.Estimator = (*mockFeeEstimator)(nil)
-
 // MockSweeperStore is a mock implementation of sweeper store. This type is
 // exported, because it is currently used in nursery tests too.
 type MockSweeperStore struct {
diff --git a/sweep/sweeper_test.go b/sweep/sweeper_test.go
index bfa3ff778f..e25864bcbe 100644
--- a/sweep/sweeper_test.go
+++ b/sweep/sweeper_test.go
@@ -2,61 +2,23 @@ package sweep
 
 import (
 	"errors"
-	"fmt"
-	"os"
-	"runtime/pprof"
-	"sync/atomic"
 	"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/build"
 	"github.com/lightningnetwork/lnd/chainntnfs"
-	"github.com/lightningnetwork/lnd/channeldb"
 	"github.com/lightningnetwork/lnd/fn"
 	"github.com/lightningnetwork/lnd/input"
-	"github.com/lightningnetwork/lnd/keychain"
-	lnmock "github.com/lightningnetwork/lnd/lntest/mock"
-	"github.com/lightningnetwork/lnd/lntest/wait"
-	"github.com/lightningnetwork/lnd/lnwallet"
 	"github.com/lightningnetwork/lnd/lnwallet/chainfee"
 	"github.com/stretchr/testify/mock"
 	"github.com/stretchr/testify/require"
 )
 
 var (
-	testLog = build.NewSubLogger("SWPR_TEST", nil)
-
-	testMaxSweepAttempts = 3
-
-	testMaxInputsPerTx = uint32(3)
-
-	defaultFeePref = Params{Fee: FeeEstimateInfo{ConfTarget: 1}}
-
 	errDummy = errors.New("dummy error")
-)
-
-type sweeperTestContext struct {
-	t *testing.T
-
-	sweeper   *UtxoSweeper
-	notifier  *MockNotifier
-	estimator *mockFeeEstimator
-	backend   *mockBackend
-	store     SweeperStore
-	publisher *MockBumper
-
-	publishChan   chan wire.MsgTx
-	currentHeight int32
-}
-
-var (
-	spendableInputs []*input.BaseInput
-	testInputCount  atomic.Uint64
 
 	testPubKey, _ = btcec.ParsePubKey([]byte{
 		0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a,
@@ -70,2051 +32,6 @@ var (
 	})
 )
 
-func createTestInput(value int64, witnessType input.WitnessType) input.BaseInput {
-	hash := chainhash.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-		byte(testInputCount.Add(1))}
-
-	input := input.MakeBaseInput(
-		&wire.OutPoint{
-			Hash: hash,
-		},
-		witnessType,
-		&input.SignDescriptor{
-			Output: &wire.TxOut{
-				Value: value,
-			},
-			KeyDesc: keychain.KeyDescriptor{
-				PubKey: testPubKey,
-			},
-		},
-		0,
-		nil,
-	)
-
-	return input
-}
-
-func init() {
-	// Create a set of test spendable inputs.
-	for i := 0; i < 20; i++ {
-		input := createTestInput(int64(10000+i*500),
-			input.CommitmentTimeLock)
-
-		spendableInputs = append(spendableInputs, &input)
-	}
-}
-
-func createSweeperTestContext(t *testing.T) *sweeperTestContext {
-	notifier := NewMockNotifier(t)
-
-	// Create new store.
-	cdb, err := channeldb.MakeTestDB(t)
-	require.NoError(t, err)
-
-	var chain chainhash.Hash
-	store, err := NewSweeperStore(cdb, &chain)
-	require.NoError(t, err)
-
-	backend := newMockBackend(t, notifier)
-	backend.walletUtxos = []*lnwallet.Utxo{
-		{
-			Value:       btcutil.Amount(1_000_000),
-			AddressType: lnwallet.WitnessPubKey,
-		},
-	}
-
-	estimator := newMockFeeEstimator(10000, chainfee.FeePerKwFloor)
-
-	aggregator := NewSimpleUtxoAggregator(
-		estimator, DefaultMaxFeeRate.FeePerKWeight(),
-		testMaxInputsPerTx,
-	)
-
-	// Create a mock fee bumper.
-	mockBumper := &MockBumper{}
-	t.Cleanup(func() {
-		mockBumper.AssertExpectations(t)
-	})
-
-	ctx := &sweeperTestContext{
-		notifier:      notifier,
-		publishChan:   backend.publishChan,
-		t:             t,
-		estimator:     estimator,
-		backend:       backend,
-		store:         store,
-		currentHeight: mockChainHeight,
-		publisher:     mockBumper,
-	}
-
-	ctx.sweeper = New(&UtxoSweeperConfig{
-		Notifier: notifier,
-		Wallet:   backend,
-		Store:    store,
-		Signer:   &lnmock.DummySigner{},
-		GenSweepScript: func() ([]byte, error) {
-			script := make([]byte, input.P2WPKHSize)
-			script[0] = 0
-			script[1] = 20
-			return script, nil
-		},
-		FeeEstimator:   estimator,
-		MaxInputsPerTx: testMaxInputsPerTx,
-		MaxFeeRate:     DefaultMaxFeeRate,
-		Aggregator:     aggregator,
-		Publisher:      mockBumper,
-	})
-
-	ctx.sweeper.Start()
-
-	return ctx
-}
-
-func (ctx *sweeperTestContext) restartSweeper() {
-	ctx.t.Helper()
-
-	ctx.sweeper.Stop()
-	ctx.sweeper = New(ctx.sweeper.cfg)
-	ctx.sweeper.Start()
-}
-
-func (ctx *sweeperTestContext) finish(expectedGoroutineCount int) {
-	// We assume that when finish is called, sweeper has finished all its
-	// goroutines. This implies that the waitgroup is empty.
-	signalChan := make(chan struct{})
-	go func() {
-		ctx.sweeper.wg.Wait()
-		close(signalChan)
-	}()
-
-	// Simulate exits of the expected number of running goroutines.
-	for i := 0; i < expectedGoroutineCount; i++ {
-		ctx.sweeper.wg.Done()
-	}
-
-	// We now expect the Wait to succeed.
-	select {
-	case <-signalChan:
-	case <-time.After(time.Second):
-		pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
-
-		ctx.t.Fatalf("lingering goroutines detected after test " +
-			"is finished")
-	}
-
-	// Restore waitgroup state to what it was before.
-	ctx.sweeper.wg.Add(expectedGoroutineCount)
-
-	// Stop sweeper.
-	ctx.sweeper.Stop()
-
-	// We should have consumed and asserted all published transactions in
-	// our unit tests.
-	ctx.assertNoTx()
-	if !ctx.backend.isDone() {
-		ctx.t.Fatal("unconfirmed txes remaining")
-	}
-}
-
-func (ctx *sweeperTestContext) assertNoTx() {
-	ctx.t.Helper()
-	select {
-	case <-ctx.publishChan:
-		ctx.t.Fatalf("unexpected transactions published")
-	default:
-	}
-}
-
-func (ctx *sweeperTestContext) receiveTx() wire.MsgTx {
-	ctx.t.Helper()
-
-	// Every time we want to receive a tx, we send a new block epoch to the
-	// sweeper to trigger a sweeping action.
-	ctx.notifier.NotifyEpochNonBlocking(ctx.currentHeight + 1)
-
-	var tx wire.MsgTx
-	select {
-	case tx = <-ctx.publishChan:
-		return tx
-	case <-time.After(5 * time.Second):
-		pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
-
-		ctx.t.Fatalf("tx not published")
-	}
-	return tx
-}
-
-func (ctx *sweeperTestContext) expectResult(c chan Result, expected error) {
-	ctx.t.Helper()
-	select {
-	case result := <-c:
-		if result.Err != expected {
-			ctx.t.Fatalf("expected %v result, but got %v",
-				expected, result.Err,
-			)
-		}
-	case <-time.After(defaultTestTimeout):
-		ctx.t.Fatalf("no result received")
-	}
-}
-
-func (ctx *sweeperTestContext) assertPendingInputs(inputs ...input.Input) {
-	ctx.t.Helper()
-
-	inputSet := make(map[wire.OutPoint]struct{}, len(inputs))
-	for _, input := range inputs {
-		inputSet[input.OutPoint()] = struct{}{}
-	}
-
-	inputsMap, err := ctx.sweeper.PendingInputs()
-	if err != nil {
-		ctx.t.Fatal(err)
-	}
-	if len(inputsMap) != len(inputSet) {
-		ctx.t.Fatalf("expected %d pending inputs, got %d",
-			len(inputSet), len(inputsMap))
-	}
-	for input := range inputsMap {
-		if _, ok := inputSet[input]; !ok {
-			ctx.t.Fatalf("found unexpected input %v", input)
-		}
-	}
-}
-
-// assertTxSweepsInputs ensures that the transaction returned within the value
-// received from resultChan spends the given inputs.
-func assertTxSweepsInputs(t *testing.T, sweepTx *wire.MsgTx,
-	inputs ...input.Input) {
-
-	t.Helper()
-
-	if len(sweepTx.TxIn) != len(inputs) {
-		t.Fatalf("expected sweep tx to contain %d inputs, got %d",
-			len(inputs), len(sweepTx.TxIn))
-	}
-	m := make(map[wire.OutPoint]struct{}, len(inputs))
-	for _, input := range inputs {
-		m[input.OutPoint()] = struct{}{}
-	}
-	for _, txIn := range sweepTx.TxIn {
-		if _, ok := m[txIn.PreviousOutPoint]; !ok {
-			t.Fatalf("expected tx %v to spend input %v",
-				txIn.PreviousOutPoint, sweepTx.TxHash())
-		}
-	}
-}
-
-// assertTxFeeRate asserts that the transaction was created with the given
-// inputs and fee rate.
-//
-// NOTE: This assumes that transactions only have one output, as this is the
-// only type of transaction the UtxoSweeper can create at the moment.
-func assertTxFeeRate(t *testing.T, tx *wire.MsgTx,
-	expectedFeeRate chainfee.SatPerKWeight, changePk []byte,
-	inputs ...input.Input) {
-
-	t.Helper()
-
-	if len(tx.TxIn) != len(inputs) {
-		t.Fatalf("expected %d inputs, got %d", len(tx.TxIn), len(inputs))
-	}
-
-	m := make(map[wire.OutPoint]input.Input, len(inputs))
-	for _, input := range inputs {
-		m[input.OutPoint()] = input
-	}
-
-	var inputAmt int64
-	for _, txIn := range tx.TxIn {
-		input, ok := m[txIn.PreviousOutPoint]
-		if !ok {
-			t.Fatalf("expected input %v to be provided",
-				txIn.PreviousOutPoint)
-		}
-		inputAmt += input.SignDesc().Output.Value
-	}
-	outputAmt := tx.TxOut[0].Value
-
-	fee := btcutil.Amount(inputAmt - outputAmt)
-	_, estimator, err := getWeightEstimate(inputs, nil, 0, 0, changePk)
-	require.NoError(t, err)
-
-	txWeight := estimator.weight()
-
-	expectedFee := expectedFeeRate.FeeForWeight(int64(txWeight))
-	if fee != expectedFee {
-		t.Fatalf("expected fee rate %v results in %v fee, got %v fee",
-			expectedFeeRate, expectedFee, fee)
-	}
-}
-
-// assertNumSweeps asserts that the expected number of sweeps has been found in
-// the sweeper's store.
-func assertNumSweeps(t *testing.T, sweeper *UtxoSweeper, num int) {
-	err := wait.NoError(func() error {
-		sweeps, err := sweeper.ListSweeps()
-		if err != nil {
-			return err
-		}
-
-		if len(sweeps) != num {
-			return fmt.Errorf("want %d sweeps, got %d",
-				num, len(sweeps))
-		}
-
-		return nil
-	}, 5*time.Second)
-	require.NoError(t, err, "timeout checking num of sweeps")
-}
-
-// TestSuccess tests the sweeper happy flow.
-func TestSuccess(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	inp := spendableInputs[0]
-
-	// Sweeping an input without a fee preference should result in an error.
-	_, err := ctx.sweeper.SweepInput(inp, Params{
-		Fee: &FeeEstimateInfo{},
-	})
-	require.ErrorIs(t, err, ErrNoFeePreference)
-
-	// Mock the Broadcast method to succeed.
-	bumpResultChan := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{{
-				PreviousOutPoint: inp.OutPoint(),
-			}},
-		}
-
-		// Send the first event.
-		bumpResultChan <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	})
-
-	resultChan, err := ctx.sweeper.SweepInput(inp, defaultFeePref)
-	require.NoError(t, err)
-
-	sweepTx := ctx.receiveTx()
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 1)
-
-	// Mock a confirmed event.
-	bumpResultChan <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx,
-		FeeRate: 10,
-		Fee:     100,
-	}
-
-	// Mine a block to confirm the sweep tx.
-	ctx.backend.mine()
-
-	select {
-	case result := <-resultChan:
-		if result.Err != nil {
-			t.Fatalf("expected successful spend, but received "+
-				"error %v instead", result.Err)
-		}
-		if result.Tx.TxHash() != sweepTx.TxHash() {
-			t.Fatalf("expected sweep tx ")
-		}
-	case <-time.After(5 * time.Second):
-		t.Fatalf("no result received")
-	}
-
-	ctx.finish(1)
-}
-
-// TestDust asserts that inputs that are not big enough to raise above the dust
-// limit, are held back until the total set does surpass the limit.
-func TestDust(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	// Sweeping a single output produces a tx of 486 weight units. With the
-	// test fee rate, the sweep tx will pay 4860 sat in fees.
-	//
-	// Create an input so that the output after paying fees is still
-	// positive (400 sat), but less than the dust limit (537 sat) for the
-	// sweep tx output script (P2WPKH).
-	dustInput := createTestInput(5260, input.CommitmentTimeLock)
-
-	_, err := ctx.sweeper.SweepInput(&dustInput, defaultFeePref)
-	require.NoError(t, err)
-
-	// No sweep transaction is expected now. The sweeper should recognize
-	// that the sweep output will not be relayed and not generate the tx. It
-	// isn't possible to attach a wallet utxo either, because the added
-	// weight would create a negatively yielding transaction at this fee
-	// rate.
-
-	// Sweep another input that brings the tx output above the dust limit.
-	largeInput := createTestInput(100000, input.CommitmentTimeLock)
-
-	// Mock the Broadcast method to succeed.
-	bumpResultChan := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: largeInput.OutPoint()},
-				{PreviousOutPoint: dustInput.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	})
-
-	_, err = ctx.sweeper.SweepInput(&largeInput, defaultFeePref)
-	require.NoError(t, err)
-
-	// The second input brings the sweep output above the dust limit. We
-	// expect a sweep tx now.
-	sweepTx := ctx.receiveTx()
-	require.Len(t, sweepTx.TxIn, 2, "unexpected num of tx inputs")
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 1)
-
-	// Mock a confirmed event.
-	bumpResultChan <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx,
-		FeeRate: 10,
-		Fee:     100,
-	}
-
-	ctx.backend.mine()
-
-	ctx.finish(1)
-}
-
-// TestWalletUtxo asserts that inputs that are not big enough to raise above the
-// dust limit are accompanied by a wallet utxo to make them sweepable.
-func TestWalletUtxo(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	// Sweeping a single output produces a tx of 439 weight units. At the
-	// fee floor, the sweep tx will pay 439*253/1000 = 111 sat in fees.
-	//
-	// Create an input so that the output after paying fees is still
-	// positive (183 sat), but less than the dust limit (537 sat) for the
-	// sweep tx output script (P2WPKH).
-	//
-	// What we now expect is that the sweeper will attach a utxo from the
-	// wallet. This increases the tx weight to 712 units with a fee of 180
-	// sats. The tx yield becomes then 294-180 = 114 sats.
-	dustInput := createTestInput(294, input.WitnessKeyHash)
-
-	// Mock the Broadcast method to succeed.
-	bumpResultChan := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: dustInput.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	})
-
-	_, err := ctx.sweeper.SweepInput(
-		&dustInput,
-		Params{Fee: FeeEstimateInfo{FeeRate: chainfee.FeePerKwFloor}},
-	)
-	require.NoError(t, err)
-
-	sweepTx := ctx.receiveTx()
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 1)
-
-	ctx.backend.mine()
-
-	// Mock a confirmed event.
-	bumpResultChan <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx,
-		FeeRate: 10,
-		Fee:     100,
-	}
-
-	ctx.finish(1)
-}
-
-// TestNegativeInput asserts that no inputs with a negative yield are swept.
-// Negative yield means that the value minus the added fee is negative.
-func TestNegativeInput(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	// Sweep an input large enough to cover fees, so in any case the tx
-	// output will be above the dust limit.
-	largeInput := createTestInput(100000, input.CommitmentNoDelay)
-	largeInputResult, err := ctx.sweeper.SweepInput(
-		&largeInput, defaultFeePref,
-	)
-	require.NoError(t, err)
-
-	// Sweep an additional input with a negative net yield. The weight of
-	// the HtlcAcceptedRemoteSuccess input type adds more in fees than its
-	// value at the current fee level.
-	negInput := createTestInput(2900, input.HtlcOfferedRemoteTimeout)
-	negInputResult, err := ctx.sweeper.SweepInput(&negInput, defaultFeePref)
-	require.NoError(t, err)
-
-	// Sweep a third input that has a smaller output than the previous one,
-	// but yields positively because of its lower weight.
-	positiveInput := createTestInput(2800, input.CommitmentNoDelay)
-
-	// Mock the Broadcast method to succeed.
-	bumpResultChan := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: largeInput.OutPoint()},
-				{PreviousOutPoint: positiveInput.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	positiveInputResult, err := ctx.sweeper.SweepInput(
-		&positiveInput, defaultFeePref,
-	)
-	require.NoError(t, err)
-
-	// We expect that a sweep tx is published now, but it should only
-	// contain the large input. The negative input should stay out of sweeps
-	// until fees come down to get a positive net yield.
-	sweepTx1 := ctx.receiveTx()
-	assertTxSweepsInputs(t, &sweepTx1, &largeInput, &positiveInput)
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 1)
-
-	ctx.backend.mine()
-
-	// Mock a confirmed event.
-	bumpResultChan <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx1,
-		FeeRate: 10,
-		Fee:     100,
-	}
-
-	ctx.expectResult(largeInputResult, nil)
-	ctx.expectResult(positiveInputResult, nil)
-
-	// Lower fee rate so that the negative input is no longer negative.
-	ctx.estimator.updateFees(1000, 1000)
-
-	// Create another large input.
-	secondLargeInput := createTestInput(100000, input.CommitmentNoDelay)
-
-	// Mock the Broadcast method to succeed.
-	bumpResultChan = make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: negInput.OutPoint()},
-				{PreviousOutPoint: secondLargeInput.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	secondLargeInputResult, err := ctx.sweeper.SweepInput(
-		&secondLargeInput, defaultFeePref,
-	)
-	require.NoError(t, err)
-
-	sweepTx2 := ctx.receiveTx()
-	assertTxSweepsInputs(t, &sweepTx2, &secondLargeInput, &negInput)
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 2)
-
-	ctx.backend.mine()
-
-	// Mock a confirmed event.
-	bumpResultChan <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx2,
-		FeeRate: 10,
-		Fee:     100,
-	}
-
-	ctx.expectResult(secondLargeInputResult, nil)
-	ctx.expectResult(negInputResult, nil)
-
-	ctx.finish(1)
-}
-
-// TestChunks asserts that large sets of inputs are split into multiple txes.
-func TestChunks(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	// Mock the Broadcast method to succeed on the first chunk.
-	bumpResultChan1 := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan1, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		//nolint:lll
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: spendableInputs[0].OutPoint()},
-				{PreviousOutPoint: spendableInputs[1].OutPoint()},
-				{PreviousOutPoint: spendableInputs[2].OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan1 <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	// Mock the Broadcast method to succeed on the second chunk.
-	bumpResultChan2 := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan2, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		//nolint:lll
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: spendableInputs[3].OutPoint()},
-				{PreviousOutPoint: spendableInputs[4].OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan2 <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	// Sweep five inputs.
-	for _, input := range spendableInputs[:5] {
-		_, err := ctx.sweeper.SweepInput(input, defaultFeePref)
-		require.NoError(t, err)
-	}
-
-	// We expect two txes to be published because of the max input count of
-	// three.
-	sweepTx1 := ctx.receiveTx()
-	require.Len(t, sweepTx1.TxIn, 3)
-
-	sweepTx2 := ctx.receiveTx()
-	require.Len(t, sweepTx2.TxIn, 2)
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 2)
-
-	ctx.backend.mine()
-
-	// Mock a confirmed event.
-	bumpResultChan1 <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx1,
-		FeeRate: 10,
-		Fee:     100,
-	}
-	bumpResultChan2 <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx2,
-		FeeRate: 10,
-		Fee:     100,
-	}
-
-	ctx.finish(1)
-}
-
-// TestRemoteSpend asserts that remote spends are properly detected and handled
-// both before the sweep is published as well as after.
-func TestRemoteSpend(t *testing.T) {
-	t.Run("pre-sweep", func(t *testing.T) {
-		testRemoteSpend(t, false)
-	})
-	t.Run("post-sweep", func(t *testing.T) {
-		testRemoteSpend(t, true)
-	})
-}
-
-func testRemoteSpend(t *testing.T, postSweep bool) {
-	ctx := createSweeperTestContext(t)
-
-	// Create a fake sweep tx that spends the second input as the first
-	// will be spent by the remote.
-	tx := &wire.MsgTx{
-		TxIn: []*wire.TxIn{
-			{PreviousOutPoint: spendableInputs[1].OutPoint()},
-		},
-	}
-
-	// Mock the Broadcast method to succeed.
-	bumpResultChan := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan, nil).Run(func(args mock.Arguments) {
-		// Send the first event.
-		bumpResultChan <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	resultChan1, err := ctx.sweeper.SweepInput(
-		spendableInputs[0], defaultFeePref,
-	)
-	require.NoError(t, err)
-
-	resultChan2, err := ctx.sweeper.SweepInput(
-		spendableInputs[1], defaultFeePref,
-	)
-	require.NoError(t, err)
-
-	// Spend the input with an unknown tx.
-	remoteTx := &wire.MsgTx{
-		TxIn: []*wire.TxIn{
-			{PreviousOutPoint: spendableInputs[0].OutPoint()},
-		},
-	}
-	err = ctx.backend.publishTransaction(remoteTx)
-	require.NoError(t, err)
-
-	if postSweep {
-		// Tx publication by sweeper returns ErrDoubleSpend. Sweeper
-		// will retry the inputs without reporting a result. It could be
-		// spent by the remote party.
-		ctx.receiveTx()
-
-		// Wait until the sweep tx has been saved to db.
-		assertNumSweeps(t, ctx.sweeper, 1)
-	}
-
-	ctx.backend.mine()
-
-	select {
-	case result := <-resultChan1:
-		if result.Err != ErrRemoteSpend {
-			t.Fatalf("expected remote spend")
-		}
-		if result.Tx.TxHash() != remoteTx.TxHash() {
-			t.Fatalf("expected remote spend tx")
-		}
-	case <-time.After(5 * time.Second):
-		t.Fatalf("no result received")
-	}
-
-	if !postSweep {
-		// Assert that the sweeper sweeps the remaining input.
-		sweepTx := ctx.receiveTx()
-		require.Len(t, sweepTx.TxIn, 1)
-
-		// Wait until the sweep tx has been saved to db.
-		assertNumSweeps(t, ctx.sweeper, 1)
-
-		ctx.backend.mine()
-
-		// Mock a confirmed event.
-		bumpResultChan <- &BumpResult{
-			Event:   TxConfirmed,
-			Tx:      &sweepTx,
-			FeeRate: 10,
-			Fee:     100,
-		}
-
-		ctx.expectResult(resultChan2, nil)
-
-		ctx.finish(1)
-	} else {
-		// Expected sweeper to be still listening for spend of the
-		// error input.
-		ctx.finish(2)
-
-		select {
-		case r := <-resultChan2:
-			require.NoError(t, r.Err)
-			require.Equal(t, r.Tx.TxHash(), tx.TxHash())
-
-		default:
-		}
-	}
-}
-
-// TestIdempotency asserts that offering the same input multiple times is
-// handled correctly.
-func TestIdempotency(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	input := spendableInputs[0]
-
-	// Mock the Broadcast method to succeed.
-	bumpResultChan := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: input.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	resultChan1, err := ctx.sweeper.SweepInput(input, defaultFeePref)
-	require.NoError(t, err)
-
-	resultChan2, err := ctx.sweeper.SweepInput(input, defaultFeePref)
-	require.NoError(t, err)
-
-	sweepTx := ctx.receiveTx()
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 1)
-
-	resultChan3, err := ctx.sweeper.SweepInput(input, defaultFeePref)
-	require.NoError(t, err)
-
-	// Spend the input of the sweep tx.
-	ctx.backend.mine()
-
-	// Mock a confirmed event.
-	bumpResultChan <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx,
-		FeeRate: 10,
-		Fee:     100,
-	}
-
-	ctx.expectResult(resultChan1, nil)
-	ctx.expectResult(resultChan2, nil)
-	ctx.expectResult(resultChan3, nil)
-
-	// Offer the same input again. The sweeper will register a spend ntfn
-	// for this input. Because the input has already been spent, it will
-	// immediately receive the spend notification with a spending tx hash.
-	// Because the sweeper kept track of all of its sweep txes, it will
-	// recognize the spend as its own.
-	resultChan4, err := ctx.sweeper.SweepInput(input, defaultFeePref)
-	require.NoError(t, err)
-	ctx.expectResult(resultChan4, nil)
-
-	// Timer is still running, but spend notification was delivered before
-	// it expired.
-	ctx.finish(1)
-}
-
-// TestNoInputs asserts that nothing happens if nothing happens.
-func TestNoInputs(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	// No tx should appear. This is asserted in finish().
-	ctx.finish(1)
-}
-
-// TestRestart asserts that the sweeper picks up sweeping properly after
-// a restart.
-func TestRestart(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	// Sweep input and expect sweep tx.
-	input1 := spendableInputs[0]
-
-	// Mock the Broadcast method to succeed.
-	bumpResultChan1 := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan1, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: input1.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan1 <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	_, err := ctx.sweeper.SweepInput(input1, defaultFeePref)
-	require.NoError(t, err)
-
-	sweepTx1 := ctx.receiveTx()
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 1)
-
-	// Restart sweeper.
-	ctx.restartSweeper()
-
-	// Simulate other subsystem (e.g. contract resolver) re-offering inputs.
-	spendChan1, err := ctx.sweeper.SweepInput(input1, defaultFeePref)
-	require.NoError(t, err)
-
-	input2 := spendableInputs[1]
-
-	// Mock the Broadcast method to succeed.
-	bumpResultChan2 := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan2, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: input2.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan2 <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	spendChan2, err := ctx.sweeper.SweepInput(input2, defaultFeePref)
-	require.NoError(t, err)
-
-	// Spend inputs of sweep txes and verify that spend channels signal
-	// spends.
-	ctx.backend.mine()
-
-	// Sweeper should recognize that its sweep tx of the previous run is
-	// spending the input.
-	select {
-	case result := <-spendChan1:
-		if result.Err != nil {
-			t.Fatalf("expected successful sweep")
-		}
-	case <-time.After(defaultTestTimeout):
-		t.Fatalf("no result received")
-	}
-
-	// Timer tick should trigger republishing a sweep for the remaining
-	// input.
-	sweepTx2 := ctx.receiveTx()
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 2)
-
-	ctx.backend.mine()
-
-	// Mock a confirmed event.
-	bumpResultChan1 <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx1,
-		FeeRate: 10,
-		Fee:     100,
-	}
-	bumpResultChan2 <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx2,
-		FeeRate: 10,
-		Fee:     100,
-	}
-
-	select {
-	case result := <-spendChan2:
-		if result.Err != nil {
-			t.Fatalf("expected successful sweep")
-		}
-	case <-time.After(defaultTestTimeout):
-		t.Fatalf("no result received")
-	}
-
-	// Restart sweeper again. No action is expected.
-	ctx.restartSweeper()
-
-	ctx.finish(1)
-}
-
-// TestRestartRemoteSpend asserts that the sweeper picks up sweeping properly
-// after a restart with remote spend.
-func TestRestartRemoteSpend(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	// Get testing inputs.
-	input1 := spendableInputs[0]
-	input2 := spendableInputs[1]
-
-	// Create a fake sweep tx that spends the second input as the first
-	// will be spent by the remote.
-	tx := &wire.MsgTx{
-		TxIn: []*wire.TxIn{
-			{PreviousOutPoint: input2.OutPoint()},
-		},
-	}
-
-	// Mock the Broadcast method to succeed.
-	bumpResultChan := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan, nil).Run(func(args mock.Arguments) {
-		// Send the first event.
-		bumpResultChan <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	_, err := ctx.sweeper.SweepInput(input1, defaultFeePref)
-	require.NoError(t, err)
-
-	// Sweep another input.
-	_, err = ctx.sweeper.SweepInput(input2, defaultFeePref)
-	require.NoError(t, err)
-
-	sweepTx := ctx.receiveTx()
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 1)
-
-	// Restart sweeper.
-	ctx.restartSweeper()
-
-	// Replace the sweep tx with a remote tx spending input 2.
-	ctx.backend.deleteUnconfirmed(sweepTx.TxHash())
-
-	remoteTx := &wire.MsgTx{
-		TxIn: []*wire.TxIn{
-			{PreviousOutPoint: input1.OutPoint()},
-		},
-	}
-	err = ctx.backend.publishTransaction(remoteTx)
-	require.NoError(t, err)
-
-	// Mine remote spending tx.
-	ctx.backend.mine()
-
-	// Mock the Broadcast method to succeed.
-	bumpResultChan = make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan, nil).Run(func(args mock.Arguments) {
-		// Send the first event.
-		bumpResultChan <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	// Simulate other subsystem (e.g. contract resolver) re-offering input
-	// 2.
-	spendChan, err := ctx.sweeper.SweepInput(input2, defaultFeePref)
-	require.NoError(t, err)
-
-	// Expect sweeper to construct a new tx, because input 1 was spend
-	// remotely.
-	sweepTx = ctx.receiveTx()
-
-	ctx.backend.mine()
-
-	// Mock a confirmed event.
-	bumpResultChan <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx,
-		FeeRate: 10,
-		Fee:     100,
-	}
-
-	ctx.expectResult(spendChan, nil)
-
-	ctx.finish(1)
-}
-
-// TestRestartConfirmed asserts that the sweeper picks up sweeping properly
-// after a restart with a confirm of our own sweep tx.
-func TestRestartConfirmed(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	// Sweep input.
-	input := spendableInputs[0]
-
-	// Mock the Broadcast method to succeed.
-	bumpResultChan := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: input.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	_, err := ctx.sweeper.SweepInput(input, defaultFeePref)
-	require.NoError(t, err)
-
-	sweepTx := ctx.receiveTx()
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 1)
-
-	// Restart sweeper.
-	ctx.restartSweeper()
-
-	// Mine the sweep tx.
-	ctx.backend.mine()
-
-	// Mock a confirmed event.
-	bumpResultChan <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx,
-		FeeRate: 10,
-		Fee:     100,
-	}
-
-	// Simulate other subsystem (e.g. contract resolver) re-offering input
-	// 0.
-	spendChan, err := ctx.sweeper.SweepInput(input, defaultFeePref)
-	require.NoError(t, err)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	// Here we expect again a successful sweep.
-	ctx.expectResult(spendChan, nil)
-
-	ctx.finish(1)
-}
-
-// TestRetry tests the sweeper retry flow.
-func TestRetry(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	inp0 := spendableInputs[0]
-	inp1 := spendableInputs[1]
-
-	// Mock the Broadcast method to succeed.
-	bumpResultChan1 := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan1, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: inp0.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan1 <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	resultChan0, err := ctx.sweeper.SweepInput(inp0, defaultFeePref)
-	require.NoError(t, err)
-
-	// We expect a sweep to be published.
-	sweepTx1 := ctx.receiveTx()
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 1)
-
-	// Mock the Broadcast method to succeed on the second sweep.
-	bumpResultChan2 := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan2, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: inp1.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan2 <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	// Offer a fresh input.
-	resultChan1, err := ctx.sweeper.SweepInput(inp1, defaultFeePref)
-	require.NoError(t, err)
-
-	// A single tx is expected to be published.
-	sweepTx2 := ctx.receiveTx()
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 2)
-
-	ctx.backend.mine()
-
-	// Mock a confirmed event.
-	bumpResultChan1 <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx1,
-		FeeRate: 10,
-		Fee:     100,
-	}
-	bumpResultChan2 <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx2,
-		FeeRate: 10,
-		Fee:     100,
-	}
-
-	ctx.expectResult(resultChan0, nil)
-	ctx.expectResult(resultChan1, nil)
-
-	ctx.finish(1)
-}
-
-// TestDifferentFeePreferences ensures that the sweeper can have different
-// transactions for different fee preferences. These transactions should be
-// broadcast from highest to lowest fee rate.
-func TestDifferentFeePreferences(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	// Throughout this test, we'll be attempting to sweep three inputs, two
-	// with the higher fee preference, and the last with the lower. We do
-	// this to ensure the sweeper can broadcast distinct transactions for
-	// each sweep with a different fee preference.
-	lowFeePref := FeeEstimateInfo{ConfTarget: 12}
-	lowFeeRate := chainfee.SatPerKWeight(5000)
-	ctx.estimator.blocksToFee[lowFeePref.ConfTarget] = lowFeeRate
-
-	highFeePref := FeeEstimateInfo{ConfTarget: 6}
-	highFeeRate := chainfee.SatPerKWeight(10000)
-	ctx.estimator.blocksToFee[highFeePref.ConfTarget] = highFeeRate
-
-	input1 := spendableInputs[0]
-	input2 := spendableInputs[1]
-	input3 := spendableInputs[2]
-
-	// Mock the Broadcast method to succeed on the first sweep.
-	bumpResultChan1 := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan1, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: input1.OutPoint()},
-				{PreviousOutPoint: input2.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan1 <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	// Mock the Broadcast method to succeed on the second sweep.
-	bumpResultChan2 := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan2, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: input3.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan2 <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	resultChan1, err := ctx.sweeper.SweepInput(
-		input1, Params{Fee: highFeePref},
-	)
-	require.NoError(t, err)
-
-	resultChan2, err := ctx.sweeper.SweepInput(
-		input2, Params{Fee: highFeePref},
-	)
-	require.NoError(t, err)
-
-	resultChan3, err := ctx.sweeper.SweepInput(
-		input3, Params{Fee: lowFeePref},
-	)
-	require.NoError(t, err)
-
-	// The first transaction broadcast should be the one spending the
-	// higher fee rate inputs.
-	sweepTx1 := ctx.receiveTx()
-
-	// The second should be the one spending the lower fee rate inputs.
-	sweepTx2 := ctx.receiveTx()
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 2)
-
-	// With the transactions broadcast, we'll mine a block to so that the
-	// result is delivered to each respective client.
-	ctx.backend.mine()
-
-	// Mock a confirmed event.
-	bumpResultChan1 <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx1,
-		FeeRate: 10,
-		Fee:     100,
-	}
-	bumpResultChan2 <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx2,
-		FeeRate: 10,
-		Fee:     100,
-	}
-
-	resultChans := []chan Result{resultChan1, resultChan2, resultChan3}
-	for _, resultChan := range resultChans {
-		ctx.expectResult(resultChan, nil)
-	}
-
-	ctx.finish(1)
-}
-
-// TestPendingInputs ensures that the sweeper correctly determines the inputs
-// pending to be swept.
-func TestPendingInputs(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	// Throughout this test, we'll be attempting to sweep three inputs, two
-	// with the higher fee preference, and the last with the lower. We do
-	// this to ensure the sweeper can return all pending inputs, even those
-	// with different fee preferences.
-	const (
-		lowFeeRate  = 5000
-		highFeeRate = 10000
-	)
-
-	lowFeePref := FeeEstimateInfo{
-		ConfTarget: 12,
-	}
-	ctx.estimator.blocksToFee[lowFeePref.ConfTarget] = lowFeeRate
-
-	highFeePref := FeeEstimateInfo{
-		ConfTarget: 6,
-	}
-	ctx.estimator.blocksToFee[highFeePref.ConfTarget] = highFeeRate
-
-	input1 := spendableInputs[0]
-	input2 := spendableInputs[1]
-	input3 := spendableInputs[2]
-
-	// Mock the Broadcast method to succeed on the first sweep.
-	bumpResultChan1 := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan1, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: input1.OutPoint()},
-				{PreviousOutPoint: input2.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan1 <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	// Mock the Broadcast method to succeed on the second sweep.
-	bumpResultChan2 := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan2, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: input3.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan2 <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	resultChan1, err := ctx.sweeper.SweepInput(
-		input1, Params{Fee: highFeePref},
-	)
-	require.NoError(t, err)
-
-	_, err = ctx.sweeper.SweepInput(
-		input2, Params{Fee: highFeePref},
-	)
-	require.NoError(t, err)
-
-	resultChan3, err := ctx.sweeper.SweepInput(
-		input3, Params{Fee: lowFeePref},
-	)
-	require.NoError(t, err)
-
-	// We should expect to see all inputs pending.
-	ctx.assertPendingInputs(input1, input2, input3)
-
-	// We should expect to see both sweep transactions broadcast - one for
-	// the higher feerate, the other for the lower.
-	sweepTx1 := ctx.receiveTx()
-	sweepTx2 := ctx.receiveTx()
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 2)
-
-	// Mine these txns, and we should expect to see the results delivered.
-	ctx.backend.mine()
-
-	// Mock a confirmed event.
-	bumpResultChan1 <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx1,
-		FeeRate: 10,
-		Fee:     100,
-	}
-	bumpResultChan2 <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx2,
-		FeeRate: 10,
-		Fee:     100,
-	}
-
-	ctx.expectResult(resultChan1, nil)
-	ctx.expectResult(resultChan3, nil)
-	ctx.assertPendingInputs()
-
-	ctx.finish(1)
-}
-
-// TestExclusiveGroup tests the sweeper exclusive group functionality.
-func TestExclusiveGroup(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	input1 := spendableInputs[0]
-	input2 := spendableInputs[1]
-	input3 := spendableInputs[2]
-
-	// Mock the Broadcast method to succeed on the first sweep.
-	bumpResultChan1 := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan1, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: input1.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan1 <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	// Mock the Broadcast method to succeed on the second sweep.
-	bumpResultChan2 := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan2, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: input2.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan2 <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	// Mock the Broadcast method to succeed on the third sweep.
-	bumpResultChan3 := make(chan *BumpResult, 1)
-	ctx.publisher.On("Broadcast", mock.Anything).Return(
-		bumpResultChan3, nil).Run(func(args mock.Arguments) {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn: []*wire.TxIn{
-				{PreviousOutPoint: input3.OutPoint()},
-			},
-		}
-
-		// Send the first event.
-		bumpResultChan3 <- &BumpResult{
-			Event: TxPublished,
-			Tx:    tx,
-		}
-
-		// Due to a mix of new and old test frameworks, we need to
-		// manually call the method to get the test to pass.
-		//
-		// TODO(yy): remove the test context and replace them will
-		// mocks.
-		err := ctx.backend.PublishTransaction(tx, "")
-		require.NoError(t, err)
-	}).Once()
-
-	// Sweep three inputs in the same exclusive group.
-	var results []chan Result
-	for i := 0; i < 3; i++ {
-		exclusiveGroup := uint64(1)
-		result, err := ctx.sweeper.SweepInput(
-			spendableInputs[i], Params{
-				Fee:            FeeEstimateInfo{ConfTarget: 6},
-				ExclusiveGroup: &exclusiveGroup,
-			},
-		)
-		require.NoError(t, err)
-		results = append(results, result)
-	}
-
-	// We expect all inputs to be published in separate transactions, even
-	// though they share the same fee preference.
-	sweepTx1 := ctx.receiveTx()
-	require.Len(t, sweepTx1.TxIn, 1)
-
-	sweepTx2 := ctx.receiveTx()
-	sweepTx3 := ctx.receiveTx()
-
-	// Remove all txes except for the one that sweeps the first
-	// input. This simulates the sweeps being conflicting.
-	ctx.backend.deleteUnconfirmed(sweepTx2.TxHash())
-	ctx.backend.deleteUnconfirmed(sweepTx3.TxHash())
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 3)
-
-	// Mine the first sweep tx.
-	ctx.backend.mine()
-
-	// Mock a confirmed event.
-	bumpResultChan1 <- &BumpResult{
-		Event:   TxConfirmed,
-		Tx:      &sweepTx1,
-		FeeRate: 10,
-		Fee:     100,
-	}
-	bumpResultChan2 <- &BumpResult{
-		Event: TxFailed,
-		Tx:    &sweepTx2,
-	}
-	bumpResultChan2 <- &BumpResult{
-		Event: TxFailed,
-		Tx:    &sweepTx3,
-	}
-
-	// Expect the first input to be swept by the confirmed sweep tx.
-	result0 := <-results[0]
-	if result0.Err != nil {
-		t.Fatal("expected first input to be swept")
-	}
-
-	// Expect the other two inputs to return an error. They have no chance
-	// of confirming.
-	result1 := <-results[1]
-	if result1.Err != ErrExclusiveGroupSpend {
-		t.Fatal("expected second input to be canceled")
-	}
-
-	result2 := <-results[2]
-	if result2.Err != ErrExclusiveGroupSpend {
-		t.Fatal("expected third input to be canceled")
-	}
-}
-
-type testInput struct {
-	*input.BaseInput
-
-	locktime *uint32
-	reqTxOut *wire.TxOut
-}
-
-func (i *testInput) RequiredLockTime() (uint32, bool) {
-	if i.locktime != nil {
-		return *i.locktime, true
-	}
-
-	return 0, false
-}
-
-func (i *testInput) RequiredTxOut() *wire.TxOut {
-	return i.reqTxOut
-}
-
-// CraftInputScript is a custom sign method for the testInput type that will
-// encode the spending outpoint and the tx input index as part of the returned
-// witness.
-func (i *testInput) CraftInputScript(_ input.Signer, txn *wire.MsgTx,
-	hashCache *txscript.TxSigHashes,
-	prevOutputFetcher txscript.PrevOutputFetcher,
-	txinIdx int) (*input.Script, error) {
-
-	// We'll encode the outpoint in the witness, so we can assert that the
-	// expected input was signed at the correct index.
-	op := i.OutPoint()
-	return &input.Script{
-		Witness: [][]byte{
-			// We encode the hash of the outpoint...
-			op.Hash[:],
-			// ..the outpoint index...
-			{byte(op.Index)},
-			// ..and finally the tx input index.
-			{byte(txinIdx)},
-		},
-	}, nil
-}
-
-// assertSignedIndex goes through all inputs to the tx and checks that all
-// testInputs have witnesses corresponding to the outpoints they are spending,
-// and are signed at the correct tx input index. All found testInputs are
-// returned such that we can sum up and sanity check that all testInputs were
-// part of the sweep.
-func assertSignedIndex(t *testing.T, tx *wire.MsgTx,
-	testInputs map[wire.OutPoint]*testInput) map[wire.OutPoint]struct{} {
-
-	found := make(map[wire.OutPoint]struct{})
-	for idx, txIn := range tx.TxIn {
-		op := txIn.PreviousOutPoint
-
-		// Not a testInput, it won't have the test encoding we require
-		// to check outpoint and index.
-		if _, ok := testInputs[op]; !ok {
-			continue
-		}
-
-		if _, ok := found[op]; ok {
-			t.Fatalf("input already used")
-		}
-
-		// Check it was signes spending the correct outpoint, and at
-		// the expected tx input index.
-		require.Equal(t, txIn.Witness[0], op.Hash[:])
-		require.Equal(t, txIn.Witness[1], []byte{byte(op.Index)})
-		require.Equal(t, txIn.Witness[2], []byte{byte(idx)})
-		found[op] = struct{}{}
-	}
-
-	return found
-}
-
-// TestLockTimes checks that the sweeper properly groups inputs requiring the
-// same locktime together into sweep transactions.
-func TestLockTimes(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	// We increase the number of max inputs to a tx so that won't
-	// impact our test.
-	ctx.sweeper.cfg.MaxInputsPerTx = 100
-
-	// We also need to update the aggregator about this new config.
-	ctx.sweeper.cfg.Aggregator = NewSimpleUtxoAggregator(
-		ctx.estimator, DefaultMaxFeeRate.FeePerKWeight(), 100,
-	)
-
-	// We will set up the lock times in such a way that we expect the
-	// sweeper to divide the inputs into 4 diffeerent transactions.
-	const numSweeps = 4
-
-	// Sweep 8 inputs, using 4 different lock times.
-	var (
-		results         []chan Result
-		inputs          = make(map[wire.OutPoint]input.Input)
-		clusters        = make(map[uint32][]input.Input)
-		bumpResultChans = make([]chan *BumpResult, 0, 4)
-	)
-	for i := 0; i < numSweeps*2; i++ {
-		lt := uint32(10 + (i % numSweeps))
-		inp := &testInput{
-			BaseInput: spendableInputs[i],
-			locktime:  &lt,
-		}
-
-		op := inp.OutPoint()
-		inputs[op] = inp
-
-		cluster, ok := clusters[lt]
-		if !ok {
-			cluster = make([]input.Input, 0)
-		}
-		cluster = append(cluster, inp)
-		clusters[lt] = cluster
-	}
-
-	for i := 0; i < 3; i++ {
-		inp := spendableInputs[i+numSweeps*2]
-		inputs[inp.OutPoint()] = inp
-
-		lt := uint32(10 + (i % numSweeps))
-		clusters[lt] = append(clusters[lt], inp)
-	}
-
-	for lt, cluster := range clusters {
-		// Create a fake sweep tx.
-		tx := &wire.MsgTx{
-			TxIn:     []*wire.TxIn{},
-			LockTime: lt,
-		}
-
-		// Append the inputs.
-		for _, inp := range cluster {
-			txIn := &wire.TxIn{
-				PreviousOutPoint: inp.OutPoint(),
-			}
-			tx.TxIn = append(tx.TxIn, txIn)
-		}
-
-		// Mock the Broadcast method to succeed on current sweep.
-		bumpResultChan := make(chan *BumpResult, 1)
-		bumpResultChans = append(bumpResultChans, bumpResultChan)
-		ctx.publisher.On("Broadcast", mock.Anything).Return(
-			bumpResultChan, nil).Run(func(args mock.Arguments) {
-			// Send the first event.
-			bumpResultChan <- &BumpResult{
-				Event: TxPublished,
-				Tx:    tx,
-			}
-
-			// Due to a mix of new and old test frameworks, we need
-			// to manually call the method to get the test to pass.
-			//
-			// TODO(yy): remove the test context and replace them
-			// will mocks.
-			err := ctx.backend.PublishTransaction(tx, "")
-			require.NoError(t, err)
-		}).Once()
-	}
-
-	// Make all the sweeps.
-	for _, inp := range inputs {
-		result, err := ctx.sweeper.SweepInput(
-			inp, Params{
-				Fee: FeeEstimateInfo{ConfTarget: 6},
-			},
-		)
-		require.NoError(t, err)
-
-		results = append(results, result)
-	}
-
-	// Check the sweeps transactions, ensuring all inputs are there, and
-	// all the locktimes are satisfied.
-	sweepTxes := make([]wire.MsgTx, 0, numSweeps)
-	for i := 0; i < numSweeps; i++ {
-		sweepTx := ctx.receiveTx()
-		sweepTxes = append(sweepTxes, sweepTx)
-
-		for _, txIn := range sweepTx.TxIn {
-			op := txIn.PreviousOutPoint
-			inp, ok := inputs[op]
-			require.True(t, ok)
-
-			delete(inputs, op)
-
-			// If this input had a required locktime, ensure the tx
-			// has that set correctly.
-			lt, ok := inp.RequiredLockTime()
-			if !ok {
-				continue
-			}
-
-			require.EqualValues(t, lt, sweepTx.LockTime)
-		}
-	}
-
-	// Wait until the sweep tx has been saved to db.
-	assertNumSweeps(t, ctx.sweeper, 4)
-
-	// Mine the sweeps.
-	ctx.backend.mine()
-
-	for i, bumpResultChan := range bumpResultChans {
-		// Mock a confirmed event.
-		bumpResultChan <- &BumpResult{
-			Event:   TxConfirmed,
-			Tx:      &sweepTxes[i],
-			FeeRate: 10,
-			Fee:     100,
-		}
-	}
-
-	// The should be no inputs not foud in any of the sweeps.
-	require.Empty(t, inputs)
-
-	// Results should all come back.
-	for i, resultChan := range results {
-		select {
-		case result := <-resultChan:
-			require.NoError(t, result.Err)
-		case <-time.After(1 * time.Second):
-			t.Fatalf("result %v did not come back", i)
-		}
-	}
-}
-
-// TestSweeperShutdownHandling tests that we notify callers when the sweeper
-// cannot handle requests since it's in the process of shutting down.
-func TestSweeperShutdownHandling(t *testing.T) {
-	ctx := createSweeperTestContext(t)
-
-	// Make the backing notifier break down. This is what happens during
-	// lnd shut down, since the notifier is stopped before the sweeper.
-	require.Len(t, ctx.notifier.epochChan, 1)
-	for epochChan := range ctx.notifier.epochChan {
-		close(epochChan)
-	}
-
-	// Give the collector some time to exit.
-	time.Sleep(50 * time.Millisecond)
-
-	// Now trying to sweep inputs should return an error on the error
-	// channel.
-	resultChan, err := ctx.sweeper.SweepInput(
-		spendableInputs[0], defaultFeePref,
-	)
-	require.NoError(t, err)
-
-	select {
-	case res := <-resultChan:
-		require.Equal(t, ErrSweeperShuttingDown, res.Err)
-
-	case <-time.After(defaultTestTimeout):
-		t.Fatalf("no result arrived")
-	}
-
-	// Stop the sweeper properly.
-	err = ctx.sweeper.Stop()
-	require.NoError(t, err)
-
-	// Now attempting to sweep an input should error out immediately.
-	_, err = ctx.sweeper.SweepInput(
-		spendableInputs[0], defaultFeePref,
-	)
-	require.Error(t, err)
-}
-
 // TestMarkInputsPendingPublish checks that given a list of inputs with
 // different states, only the non-terminal state will be marked as `Published`.
 func TestMarkInputsPendingPublish(t *testing.T) {
diff --git a/sweep/tx_input_set_test.go b/sweep/tx_input_set_test.go
index 2cf97dd0ba..b6a87b378b 100644
--- a/sweep/tx_input_set_test.go
+++ b/sweep/tx_input_set_test.go
@@ -14,237 +14,12 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
-// TestTxInputSet tests adding various sized inputs to the set.
-func TestTxInputSet(t *testing.T) {
-	const (
-		feeRate   = 1000
-		maxInputs = 10
-	)
-	set := newTxInputSet(feeRate, 0, maxInputs)
-
-	// Create a 300 sat input. The fee to sweep this input to a P2WKH output
-	// is 439 sats. That means that this input yields -139 sats and we
-	// expect it not to be added.
-	if set.add(createP2WKHInput(300), constraintsRegular) {
-		t.Fatal("expected add of negatively yielding input to fail")
-	}
-
-	// A 700 sat input should be accepted into the set, because it yields
-	// positively.
-	if !set.add(createP2WKHInput(700), constraintsRegular) {
-		t.Fatal("expected add of positively yielding input to succeed")
-	}
-
-	fee := set.weightEstimate(true).feeWithParent()
-	require.Equal(t, btcutil.Amount(487), fee)
-
-	// The tx output should now be 700-487 = 213 sats. The dust limit isn't
-	// reached yet.
-	if set.totalOutput() != 213 {
-		t.Fatal("unexpected output value")
-	}
-	if set.enoughInput() {
-		t.Fatal("expected dust limit not yet to be reached")
-	}
-
-	// Add a 1000 sat input. This increases the tx fee to 760 sats. The tx
-	// output should now be 1000+700 - 760 = 940 sats.
-	if !set.add(createP2WKHInput(1000), constraintsRegular) {
-		t.Fatal("expected add of positively yielding input to succeed")
-	}
-	if set.totalOutput() != 940 {
-		t.Fatal("unexpected output value")
-	}
-	if !set.enoughInput() {
-		t.Fatal("expected dust limit to be reached")
-	}
-}
-
-// TestTxInputSetFromWallet tests adding a wallet input to a TxInputSet to reach
-// the dust limit.
-func TestTxInputSetFromWallet(t *testing.T) {
-	const (
-		feeRate   = 500
-		maxInputs = 10
-	)
-
-	wallet := &mockWallet{}
-	set := newTxInputSet(feeRate, 0, maxInputs)
-
-	// Add a 500 sat input to the set. It yields positively, but doesn't
-	// reach the output dust limit.
-	if !set.add(createP2WKHInput(500), constraintsRegular) {
-		t.Fatal("expected add of positively yielding input to succeed")
-	}
-	if set.enoughInput() {
-		t.Fatal("expected dust limit not yet to be reached")
-	}
-
-	// Expect that adding a negative yield input fails.
-	if set.add(createP2WKHInput(50), constraintsRegular) {
-		t.Fatal("expected negative yield input add to fail")
-	}
-
-	// Force add the negative yield input. It should succeed.
-	if !set.add(createP2WKHInput(50), constraintsForce) {
-		t.Fatal("expected forced add to succeed")
-	}
-
-	err := set.AddWalletInputs(wallet)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if !set.enoughInput() {
-		t.Fatal("expected dust limit to be reached")
-	}
-}
-
 // createP2WKHInput returns a P2WKH test input with the specified amount.
 func createP2WKHInput(amt btcutil.Amount) input.Input {
 	input := createTestInput(int64(amt), input.WitnessKeyHash)
 	return &input
 }
 
-type mockWallet struct {
-	Wallet
-}
-
-func (m *mockWallet) ListUnspentWitnessFromDefaultAccount(minConfs, maxConfs int32) (
-	[]*lnwallet.Utxo, error) {
-
-	return []*lnwallet.Utxo{
-		{
-			AddressType: lnwallet.WitnessPubKey,
-			Value:       10000,
-		},
-	}, nil
-}
-
-type reqInput struct {
-	input.Input
-
-	txOut *wire.TxOut
-}
-
-func (r *reqInput) RequiredTxOut() *wire.TxOut {
-	return r.txOut
-}
-
-// TestTxInputSetRequiredOutput tests that the tx input set behaves as expected
-// when we add inputs that have required tx outs.
-func TestTxInputSetRequiredOutput(t *testing.T) {
-	const (
-		feeRate   = 1000
-		maxInputs = 10
-	)
-	set := newTxInputSet(feeRate, 0, maxInputs)
-
-	// Attempt to add an input with a required txout below the dust limit.
-	// This should fail since we cannot trim such outputs.
-	inp := &reqInput{
-		Input: createP2WKHInput(500),
-		txOut: &wire.TxOut{
-			Value:    500,
-			PkScript: make([]byte, input.P2PKHSize),
-		},
-	}
-	require.False(t, set.add(inp, constraintsRegular),
-		"expected adding dust required tx out to fail")
-
-	// Create a 1000 sat input that also has a required TxOut of 1000 sat.
-	// The fee to sweep this input to a P2WKH output is 439 sats.
-	inp = &reqInput{
-		Input: createP2WKHInput(1000),
-		txOut: &wire.TxOut{
-			Value:    1000,
-			PkScript: make([]byte, input.P2WPKHSize),
-		},
-	}
-	require.True(t, set.add(inp, constraintsRegular), "failed adding input")
-
-	// The fee needed to pay for this input and output should be 439 sats.
-	fee := set.weightEstimate(false).feeWithParent()
-	require.Equal(t, btcutil.Amount(439), fee)
-
-	// Since the tx set currently pays no fees, we expect the current
-	// change to actually be negative, since this is what it would cost us
-	// in fees to add a change output.
-	feeWithChange := set.weightEstimate(true).feeWithParent()
-	if set.changeOutput != -feeWithChange {
-		t.Fatalf("expected negative change of %v, had %v",
-			-feeWithChange, set.changeOutput)
-	}
-
-	// This should also be reflected by not having enough input.
-	require.False(t, set.enoughInput())
-
-	// Get a weight estimate without change output, and add an additional
-	// input to it.
-	dummyInput := createP2WKHInput(1000)
-	weight := set.weightEstimate(false)
-	require.NoError(t, weight.add(dummyInput))
-
-	// Now we add a an input that is large enough to pay the fee for the
-	// transaction without a change output, but not large enough to afford
-	// adding a change output.
-	extraInput1 := weight.feeWithParent() + 100
-	require.True(t, set.add(
-		createP2WKHInput(extraInput1), constraintsRegular,
-	), "expected add of positively yielding input to succeed")
-
-	// The change should be negative, since we would have to add a change
-	// output, which we cannot yet afford.
-	if set.changeOutput >= 0 {
-		t.Fatal("expected change to be negaitve")
-	}
-
-	// Even though we cannot afford a change output, the tx set is valid,
-	// since we can pay the fees without the change output.
-	require.True(t, set.enoughInput())
-
-	// Get another weight estimate, this time with a change output, and
-	// figure out how much we must add to afford a change output.
-	weight = set.weightEstimate(true)
-	require.NoError(t, weight.add(dummyInput))
-
-	// We add what is left to reach this value.
-	extraInput2 := weight.feeWithParent() - extraInput1 + 100
-
-	// Add this input, which should result in the change now being 100 sats.
-	require.True(t, set.add(
-		createP2WKHInput(extraInput2), constraintsRegular,
-	))
-
-	// The change should be 100, since this is what is left after paying
-	// fees in case of a change output.
-	change := set.changeOutput
-	if change != 100 {
-		t.Fatalf("expected change be 100, was %v", change)
-	}
-
-	// Even though the change output is dust, we have enough for fees, and
-	// we have an output, so it should be considered enough to craft a
-	// valid sweep transaction.
-	require.True(t, set.enoughInput())
-
-	// Finally we add an input that should push the change output above the
-	// dust limit.
-	weight = set.weightEstimate(true)
-	require.NoError(t, weight.add(dummyInput))
-
-	// We expect the change to everything that is left after paying the tx
-	// fee.
-	extraInput3 := weight.feeWithParent() - extraInput1 - extraInput2 + 1000
-	require.True(t, set.add(createP2WKHInput(extraInput3), constraintsRegular))
-
-	change = set.changeOutput
-	if change != 1000 {
-		t.Fatalf("expected change to be %v, had %v", 1000, change)
-	}
-	require.True(t, set.enoughInput())
-}
-
 // TestNewBudgetInputSet checks `NewBudgetInputSet` correctly validates the
 // supplied inputs and returns the error.
 func TestNewBudgetInputSet(t *testing.T) {