|
| 1 | +# Sweep |
| 2 | + |
| 3 | +`sweep` is a subservice that handles sweeping UTXOs back to `lnd`'s wallet. Its |
| 4 | +main purpose is to sweep back the outputs resulting from a force close |
| 5 | +transaction, although users can also call `BumpFee` to feed new unconfirmed |
| 6 | +inputs to be handled by the sweeper. |
| 7 | + |
| 8 | +In order to sweep economically, the sweeper needs to understand the time |
| 9 | +sensitivity and max fees that can be used when sweeping the inputs. This means |
| 10 | +each input must come with a deadline and a fee budget, which can be set via the |
| 11 | +RPC request or the config, otherwise the default values will be used. Once |
| 12 | +offered to the sweeper, when a new block arrives, inputs with the same deadline |
| 13 | +will be batched into a single sweeping transaction to minimize the cost. |
| 14 | + |
| 15 | +The sweeper will publish this transaction and monitor it for potential fee |
| 16 | +bumping, a process that won’t exit until the sweeping transaction is confirmed, |
| 17 | +or the specified budget has been used up. |
| 18 | + |
| 19 | +## Understanding Budget and Deadline |
| 20 | + |
| 21 | +There are two questions when spending a UTXO - how much fees to pay and what |
| 22 | +the confirmation target is, which gives us the concepts of budget and deadline. |
| 23 | +This is especially important when sweeping the outputs of a force close |
| 24 | +transaction - some of the outputs are time-sensitive, and may result in fund |
| 25 | +loss if not confirmed in time. On the other hand, we don’t want to pay more |
| 26 | +than what we can get back - if a sweeping transaction spends more than what is |
| 27 | +meant to be swept, we are losing money due to fees. |
| 28 | + |
| 29 | +To properly handle the case, the concept `budget` and `deadline` have been |
| 30 | +introduced to `lnd` since `v0.18.0` - for each new sweeping request, the |
| 31 | +sweeper requires the caller to specify a deadline and a budget so it can make |
| 32 | +economic decisions. A fee function is then created based on the budget and |
| 33 | +deadline, which proposes a fee rate to use for the sweeping transaction. When a |
| 34 | +new block arrives, unless the transaction is confirmed or the budget is used |
| 35 | +up, the sweeper will perform a fee bump on it via RBF. |
| 36 | + |
| 37 | +## Package Structure |
| 38 | + |
| 39 | +On a high level, a UTXO is offered to the sweeper via `SweepInput`. The sweeper |
| 40 | +keeps track of the pending inputs. When a new block arrives, it asks the |
| 41 | +`UtxoAggregator` to group all the pending inputs into batches via |
| 42 | +`ClusterInputs`. Each batch is an `InputSet`, and is sent to the `Bumper`. The |
| 43 | +`Bumper` creates a `FeeFunction` and a sweeping transaction using the |
| 44 | +`InputSet`, and monitors its confirmation status. Every time it's not confirmed |
| 45 | +when a new block arrives, the `Bumper` will perform an RBF by calling |
| 46 | +`IncreaseFeeRate` on the `FeeFunction`. |
| 47 | + |
| 48 | +```mermaid |
| 49 | +flowchart LR |
| 50 | + subgraph SweepInput |
| 51 | + UTXO1-->sweeper |
| 52 | + UTXO2-->sweeper |
| 53 | + UTXO3-->sweeper |
| 54 | + UTXO["..."]-->sweeper |
| 55 | + sweeper |
| 56 | + end |
| 57 | +
|
| 58 | + subgraph ClusterInputs |
| 59 | + sweeper-->UtxoAggregator |
| 60 | + UtxoAggregator-->InputSet1 |
| 61 | + UtxoAggregator-->InputSet2 |
| 62 | + UtxoAggregator-->InputSet["..."] |
| 63 | + end |
| 64 | +
|
| 65 | + subgraph Broadcast |
| 66 | + InputSet1-->Bumper |
| 67 | + InputSet2-->Bumper |
| 68 | + InputSet-->Bumper |
| 69 | + end |
| 70 | +
|
| 71 | + subgraph IncreaseFeeRate |
| 72 | + FeeFunction-->Bumper |
| 73 | + end |
| 74 | +
|
| 75 | + block["new block"] ==> ClusterInputs |
| 76 | +``` |
| 77 | + |
| 78 | +#### `UtxoAggregator` and `InputSet` |
| 79 | + |
| 80 | +`UtxoAggregator` is an interface that handles the batching of inputs. |
| 81 | +`BudgetAggregator` implements this interface by grouping inputs with the same |
| 82 | +deadline together. Inputs with the same deadline express the same time |
| 83 | +sensitivity so it makes sense to sweep them in the same transaction. Once |
| 84 | +grouped, inputs in each batch are sorted based on their budgets. The only |
| 85 | +exception is inputs with `ExclusiveGroup` flag set, which will be swept alone. |
| 86 | + |
| 87 | +Once the batching is finished, an `InputSet` is returned, which is an interface |
| 88 | +used to decide whether a wallet UTXO is needed or not when creating the |
| 89 | +sweeping transaction. `BudgetInputSet` implements this interface by checking |
| 90 | +the sum of the output values from these inputs against the sum of their |
| 91 | +budgets - if the total budget cannot be covered, one or more wallet UTXOs are |
| 92 | +needed. |
| 93 | + |
| 94 | +For instance, when anchor output is swept to perform a CPFP, one or more wallet |
| 95 | +UTXOs are likely to be used to meet the specified budget, which is also the |
| 96 | +case when sweeping second-level HTLC transactions. However, if the sweeping |
| 97 | +transaction also contains other to-be-swept inputs, a wallet UTXO is no longer |
| 98 | +needed if their values can cover the total budget. |
| 99 | + |
| 100 | +#### `Bumper` |
| 101 | + |
| 102 | +`Bumper` is a transaction creator, publisher, and monitor that works on an |
| 103 | +`InputSet`. Once a sweeping transaction is created using the `InputSet`, the |
| 104 | +`Bumper` will monitor its confirmation status and attempt an RBF if the |
| 105 | +transaction is not confirmed in the next block. It relies on the `FeeFunction` |
| 106 | +to determine the new fee rate every block, and this new fee rate may or may not |
| 107 | +meet the BIP 125 fee requirements - in that case, the `Bumper` will try to |
| 108 | +perform an RBF again in the coming blocks. |
| 109 | + |
| 110 | +`TxPublisher` implements the `Bumper` interface. When a transaction is created |
| 111 | +for the first time, unless its budget has been used up, `TxPublisher` will |
| 112 | +guarantee that the initial publish meets the RBF requirements. |
| 113 | + |
| 114 | +#### `FeeFunction` |
| 115 | + |
| 116 | +`FeeFunction` is an interface that specifies a function over a starting fee |
| 117 | +rate, an ending fee rate, and a width (the deadline delta). It's used by the |
| 118 | +`Bumper` to suggest a new fee rate for bumping the sweeping transaction. |
| 119 | + |
| 120 | +`LinearFeeFunction` implements this interface using a linear function - it |
| 121 | +calculates a fee rate delta using `(ending_fee_rate - starting_fee_rate) / |
| 122 | +deadline`, and increases the fee rate by this delta value everytime a new block |
| 123 | +arrives. Once the deadline is passed, `LinearFeeFunction` will cap its |
| 124 | +returning fee rate at the ending fee rate. |
| 125 | + |
| 126 | +The starting fee rate is the estimated fee rate from the fee estimator, which |
| 127 | +is the result from calling `estimatesmartfee`(`bitcoind`), |
| 128 | +`estimatefee`(`btcd`), or `feeurl` depending on the config. This fee estimator |
| 129 | +is called using the deadline as the conf target, and the returned fee rate is |
| 130 | +used as the starting fee rate. This behavior can be overridden by setting the |
| 131 | +`--sat_per_vbyte` via `bumpfee` cli when fee bumping a specific input, which |
| 132 | +allows users to bypass the fee estimation and set the starting fee rate |
| 133 | +directly. |
| 134 | + |
| 135 | +The ending fee rate is the value from dividing the budget by the size of the |
| 136 | +sweeping transaction, and capped at the `--sweeper.maxfeerate`. The ending fee |
| 137 | +rate can be overridden by setting the `--budget` via `bumpfee` cli. |
| 138 | + |
| 139 | +For instance, suppose `lnd` is using `bitcoind` as its fee estimator, and an |
| 140 | +input with a deadline of 1000 blocks and a budget of 200,000 sats is being |
| 141 | +swept in a transaction that has a size of 500 vbytes, the fee function will be |
| 142 | +initialized with: |
| 143 | + |
| 144 | +- a starting fee rate of 10 sat/vB, which is the result from calling |
| 145 | + `estimatesmartfee 1000`. |
| 146 | +- an ending fee rate of 400 sat/vB, which is the result of `200,000/500`. |
| 147 | +- a fee rate delta of 390 sat/kvB, which is the result of `(400 - 10) / 500 * |
| 148 | + 1000`. |
| 149 | + |
| 150 | +## Sweeping Outputs from a Force Close Transaction |
| 151 | + |
| 152 | +A force close transaction may have the following outputs: |
| 153 | + |
| 154 | +- Commit outputs, which are the `to_local` and `to_remote` outputs. |
| 155 | +- HTLC outputs, which are the `incoming_htlc` and `outgoing_htlc` outputs. |
| 156 | +- Anchor outputs, which are the local and remote anchor outputs. |
| 157 | + |
| 158 | +#### Sweeping Commit Outputs |
| 159 | + |
| 160 | +The only output we can spend is the `to_local` output. Because it can only be |
| 161 | +spent using our signature, there’s no time pressure here. By default, the |
| 162 | +sweeper will use a deadline of 1008 blocks as the confirmation target for |
| 163 | +non-time-sensitive outputs. To overwrite the default, users can specify a |
| 164 | +value using the config `--sweeper.nodeadlineconftarget`. |
| 165 | + |
| 166 | +To specify the budget, users can use `--sweeper.budget.tolocal` to set the max |
| 167 | +allowed fees in sats, or use `--sweeper.budget.tolocalratio` to set a |
| 168 | +proportion of the `to_local` value to be used as the budget. |
| 169 | + |
| 170 | +#### Sweeping HTLC Outputs |
| 171 | + |
| 172 | +When facing a local force close transaction, HTLCs are spent in a two-stage |
| 173 | +setup - the first stage is to spend the outputs using pre-signed HTLC |
| 174 | +success/timeout transactions, the second stage is to spend the outputs from |
| 175 | +these success/timeout transactions. All these outputs are automatically handled |
| 176 | +by `lnd`. In specific, |
| 177 | +- For an incoming HTLC in stage one, the deadline is specified using its CLTV |
| 178 | + from the timeout path. This output is time-sensitive. |
| 179 | +- For an outgoing HTLC in stage one, the deadline is derived from its |
| 180 | + corresponding incoming HTLC’s CLTV. This output is time-sensitive. |
| 181 | +- For both incoming and outgoing HTLCs in stage two, because they can only be |
| 182 | + spent by us, there is no time pressure to confirm them under a deadline. |
| 183 | + |
| 184 | +When facing a remote force close transaction, HTLCs can be directly spent from |
| 185 | +the commitment transaction, and both incoming and outgoing HTLCs are |
| 186 | +time-sensitive. |
| 187 | + |
| 188 | +By default, `lnd` will use 50% of the HTLC value as its budget. To customize |
| 189 | +it, users can specify `--sweeper.budget.deadlinehtlc` and |
| 190 | +`--sweeper.budget.deadlinehtlcratio` for time-sensitive HTLCs, and |
| 191 | +`--sweeper.budget.nodeadlinehtlc` and `--sweeper.budget.nodeadlinehtlcratio` |
| 192 | +for non-time-sensitive sweeps. |
| 193 | + |
| 194 | +#### Sweeping Anchor Outputs |
| 195 | + |
| 196 | +An anchor output is a special output that functions as “anchor” to speed up the |
| 197 | +unconfirmed force closing transaction via CPFP. If the force close transaction |
| 198 | +doesn't contain any HTLCs, the anchor output is generally uneconomical to sweep |
| 199 | +and will be ignored. However, if the force close transaction does contain |
| 200 | +time-sensitive outputs (HTLCs), the anchor output will be swept to CPFP the |
| 201 | +transaction and accelerate the force close process. |
| 202 | + |
| 203 | +For CPFP-purpose anchor sweeping, the deadline is the closest deadline value of |
| 204 | +all the HTLCs on the force close transaction. The budget, however, cannot be a |
| 205 | +ratio of the anchor output because the value is too small to contribute |
| 206 | +meaningful fees (330 sats). Since its purpose is to accelerate the force close |
| 207 | +transaction so the time-sensitive outputs can be swept, the budget is actually |
| 208 | +drawn from what we call “value under protection”, which is the sum of all HTLC |
| 209 | +outputs minus the sum of their budgets. By default, 50% of this value is used |
| 210 | +as the budget, to customize it, either use |
| 211 | +`--sweeper.budget.anchorcpfp` to specify sats, or use |
| 212 | +`--sweeper.budget.anchorcpfpratio` to specify a ratio. |
| 213 | + |
0 commit comments