Skip to content

Commit 7fb2333

Browse files
authored
Merge pull request #8674 from yyforyongyu/sweeper-remove-and-docs
sweep: add docs and remove dead code
2 parents 2b74a34 + e855421 commit 7fb2333

File tree

13 files changed

+248
-3968
lines changed

13 files changed

+248
-3968
lines changed

contractcourt/anchor_resolver.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,6 @@ func (c *anchorResolver) Resolve(_ bool) (ContractResolver, error) {
9999
// After a restart or when the remote force closes, the sweeper is not
100100
// yet aware of the anchor. In that case, it will be added as new input
101101
// to the sweeper.
102-
relayFeeRate := c.Sweeper.RelayFeePerKW()
103-
104102
witnessType := input.CommitmentAnchor
105103

106104
// For taproot channels, we need to use the proper witness type.
@@ -116,9 +114,6 @@ func (c *anchorResolver) Resolve(_ bool) (ContractResolver, error) {
116114
resultChan, err := c.Sweeper.SweepInput(
117115
&anchorInput,
118116
sweep.Params{
119-
Fee: sweep.FeeEstimateInfo{
120-
FeeRate: relayFeeRate,
121-
},
122117
// For normal anchor sweeping, the budget is 330 sats.
123118
Budget: btcutil.Amount(
124119
anchorInput.SignDesc().Output.Value,

contractcourt/commit_sweep_resolver_test.go

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package contractcourt
22

33
import (
4-
"fmt"
54
"testing"
65
"time"
76

@@ -135,20 +134,6 @@ func (s *mockSweeper) SweepInput(input input.Input, params sweep.Params) (
135134

136135
s.sweptInputs <- input
137136

138-
// TODO(yy): replace mockSweeper with `mock.Mock`.
139-
if params.Fee != nil {
140-
fee, ok := params.Fee.(sweep.FeeEstimateInfo)
141-
if !ok {
142-
return nil, fmt.Errorf("unexpected fee type: %T",
143-
params.Fee)
144-
}
145-
146-
// Update the deadlines used if it's set.
147-
if fee.ConfTarget != 0 {
148-
s.deadlines = append(s.deadlines, int(fee.ConfTarget))
149-
}
150-
}
151-
152137
// Update the deadlines used if it's set.
153138
params.DeadlineHeight.WhenSome(func(d int32) {
154139
s.deadlines = append(s.deadlines, int(d))

docs/release-notes/release-notes-0.18.0.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,9 @@ bitcoin peers' feefilter values into account](https://github.com/lightningnetwor
204204
by default, and the feature is not yet advertised to the network.
205205

206206
* Introduced [fee bumper](https://github.com/lightningnetwork/lnd/pull/8424) to
207-
handle bumping the fees of sweeping transactions properly.
207+
handle bumping the fees of sweeping transactions properly. A
208+
[README.md](https://github.com/lightningnetwork/lnd/pull/8674) is added to
209+
explain this new approach.
208210

209211
## RPC Additions
210212

sweep/README.md

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
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

Comments
 (0)