sweep: add wallet inputs to reach dust limit

This commit allows sweeper to sweep inputs that on its own are not able
to form a sweep transaction that meets the dust limit.

This functionality is useful for sweeping small outputs. In the future,
this will be particularly important to sweep anchors. Anchors will
typically be spent with a relatively large fee to pay for the parent tx.
It will then be necessary to attach an additional wallet utxo.
This commit is contained in:
Joost Jager
2019-12-10 16:06:45 +01:00
parent 8353b6fd6e
commit e01600fdb8
7 changed files with 299 additions and 34 deletions

View File

@ -99,6 +99,12 @@ func createSweeperTestContext(t *testing.T) *sweeperTestContext {
store := NewMockSweeperStore()
backend := newMockBackend(t, notifier)
backend.walletUtxos = []*lnwallet.Utxo{
{
Value: btcutil.Amount(10000),
AddressType: lnwallet.WitnessPubKey,
},
}
estimator := newMockFeeEstimator(10000, chainfee.FeePerKwFloor)
@ -407,7 +413,10 @@ func TestDust(t *testing.T) {
}
// No sweep transaction is expected now. The sweeper should recognize
// that the sweep output will not be relayed and not generate the tx.
// 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)
@ -433,6 +442,50 @@ func TestDust(t *testing.T) {
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)
_, err := ctx.sweeper.SweepInput(
&dustInput,
Params{Fee: FeePreference{FeeRate: chainfee.FeePerKwFloor}},
)
if err != nil {
t.Fatal(err)
}
ctx.tick()
sweepTx := ctx.receiveTx()
if len(sweepTx.TxIn) != 2 {
t.Fatalf("Expected tx to sweep 2 inputs, but contains %v "+
"inputs instead", len(sweepTx.TxIn))
}
// Calculate expected output value based on wallet utxo of 10000 sats.
expectedOutputValue := int64(294 + 10000 - 180)
if sweepTx.TxOut[0].Value != expectedOutputValue {
t.Fatalf("Expected output value of %v, but got %v",
expectedOutputValue, sweepTx.TxOut[0].Value)
}
ctx.backend.mine()
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) {