package
0.18.4-beta.rc2
Repository: https://github.com/lightningnetwork/lnd.git
Documentation: pkg.go.dev

# README

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 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.

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 the 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, commitment and HTLC transactions usually have some proportion of their outputs timelocked, preventing them from being used to pay fees immediately. For these transactions, wallet UTXOs are often needed to get them confirmed in a timely manner.

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 every time 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) / 1000 * 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.

# Functions

CraftSweepAllTx attempts to craft a WalletSweepPackage which will allow the caller to sweep ALL funds in ALL or SELECT outputs within the wallet to a list of outputs.
DisableLog disables all library log output.
New returns a new Sweeper instance.
NewBudgetAggregator creates a new instance of a BudgetAggregator.
NewBudgetInputSet creates a new BudgetInputSet.
NewLinearFeeFunction creates a new linear fee function and initializes it with a starting fee rate which is an estimated value returned from the fee estimator using the initial conf target.
NewMockNotifier instantiates a new mock notifier.
NewSweeperStore returns a new store instance.
NewTxPublisher creates a new TxPublisher.
UseLogger uses a specified Logger to output package logging info.

# Constants

Excluded is the state of a pending input that has been excluded and can no longer be swept.
Failed is the state when a pending input has too many failed publish atttempts or unknown broadcast error is returned.
Init is the initial state of a pending input.
PendingPublish specifies an input's state where it's already been included in a sweeping tx but the tx is not published yet.
Published is the state where the input's sweeping tx has successfully been published.
PublishFailed is the state when an error is returned from publishing the sweeping tx.
Swept is the final state of a pending input.
TxConfirmed is sent when the tx is confirmed.
TxFailed is sent when the broadcast attempt fails.
TxPublished is sent when the broadcast attempt is finished.
TxReplaced is sent when the original tx is replaced by a new one.

# Variables

DefaultDeadlineDelta defines a default deadline delta (1 week) to be used when sweeping inputs with no deadline pressure.
DefaultMaxFeeRate is the default maximum fee rate allowed within the UtxoSweeper.
DefaultMaxInputsPerTx specifies the default maximum number of inputs allowed in a single sweep tx.
ErrDeadlinesMismatch is returned when the deadlines of the input sets do not match.
ErrDustOutput is returned when the output value is below the dust limit.
ErrExclusiveGroupSpend is returned in case a different input of the same exclusive group was spent.
ErrFeePreferenceConflict is returned when both a fee rate and a conf target is set for a fee preference.
ErrFeePreferenceTooLow is returned when the fee preference gives a fee rate that's below the relay fee rate.
ErrInvalidBumpResult is returned when the bump result is invalid.
ErrLocktimeConflict is returned when inputs with different transaction nLockTime values are included in the same transaction.
ErrLocktimeImmature is returned when sweeping an input whose locktime is not reached.
ErrMaxPosition is returned when trying to increase the position of the fee function while it's already at its max.
ErrNoFeePreference is returned when we attempt to satisfy a sweep request from a client whom did not specify a fee preference.
ErrNotEnoughBudget is returned when the fee bumper decides the current budget cannot cover the fee.
ErrNotEnoughInputs is returned when there are not enough wallet inputs to construct a non-dust change output for an input set.
ErrRemoteSpend is returned in case an output that we try to sweep is confirmed in a tx of the remote party.
ErrSweeperShuttingDown is an error returned when a client attempts to make a request to the UtxoSweeper, but it is unable to handle it as it is/has already been stopped.
ErrThirdPartySpent is returned when a third party has spent the input in the sweeping tx.
ErrTxNoOutput is returned when an output cannot be created during tx preparation, usually due to the output being dust.
ErrTxNotFound is returned when querying using a txid that's not found in our db.
ErrUnknownUTXO is returned when creating a sweeping tx using an UTXO that's unknown to the wallet.

# Structs

BudgetAggregator is a budget-based aggregator that creates clusters based on deadlines and budgets of inputs.
BudgetInputSet implements the interface `InputSet`.
BumpRequest is used by the caller to give the Bumper the necessary info to create and manage potential fee bumps for a set of inputs.
BumpResult is used by the Bumper to send updates about the tx being broadcast.
DeliveryAddr is a pair of (address, amount) used to craft a transaction paying to more than one specified address.
FeeEstimateInfo allows callers to express their time value for inclusion of a transaction into a block via either a confirmation target, or a fee rate.
LinearFeeFunction implements the FeeFunction interface with a linear function: feeRate = startingFeeRate + position * delta.
MockNotifier simulates the chain notifier for test purposes.
Params contains the parameters that control the sweeping process.
PendingInputResponse contains information about an input that is currently being swept by the UtxoSweeper.
RBFInfo stores the information required to perform a RBF bump on a pending sweeping tx.
Result is the struct that is pushed through the result channel.
SweeperInput is created when an input reaches the main loop for the first time.
SweepOutput is an output used to sweep funds from a channel output.
TxPublisher is an implementation of the Bumper interface.
TxPublisherConfig is the config used to create a new TxPublisher.
TxRecord specifies a record of a tx that's stored in the database.
UtxoSweeper is responsible for sweeping outputs back into the wallet.
UtxoSweeperConfig contains dependencies of UtxoSweeper.
WalletSweepPackage is a package that gives the caller the ability to sweep relevant funds from a wallet in a single transaction.

# Interfaces

AuxSweeper is used to enable a 3rd party to further shape the sweeping transaction by adding a set of extra outputs to the sweeping transaction.
Bumper defines an interface that can be used by other subsystems for fee bumping.
CoinSelectionLocker is an interface that allows the caller to perform an operation, which is synchronized with all coin selection attempts.
FeeFunction defines an interface that is used to calculate fee rates for transactions.
FeePreference defines an interface that allows the caller to specify how the fee rate should be handled.
InputSet defines an interface that's responsible for filtering a set of inputs that can be swept economically.
OutputLeaser allows a caller to lease/release an output.
SweeperStore stores published txes.
UtxoAggregator defines an interface that takes a list of inputs and aggregate them into groups.
UtxoSource is an interface that allows a caller to access a source of UTXOs to use when crafting sweep transactions.
Wallet contains all wallet related functionality required by sweeper.

# Type aliases

BumpEvent represents the event of a fee bumping attempt.
InputsMap is a type alias for a set of pending inputs.
SweepState represents the current state of a pending input.