mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-09-28 21:44:13 +02:00
sweep: start the sweeping if there are normal inputs
We now start the sweeping process if there are normal inputs to partially cover the budget.
This commit is contained in:
@@ -327,6 +327,21 @@ func (b *BudgetInputSet) copyInputs() []*SweeperInput {
|
|||||||
return inputs
|
return inputs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasNormalInput return a bool to indicate whether there exists an input that
|
||||||
|
// doesn't require a TxOut. When an input has no required outputs, it's either a
|
||||||
|
// wallet input, or an input we want to sweep.
|
||||||
|
func (b *BudgetInputSet) hasNormalInput() bool {
|
||||||
|
for _, inp := range b.inputs {
|
||||||
|
if inp.RequiredTxOut() != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// AddWalletInputs adds wallet inputs to the set until the specified budget is
|
// AddWalletInputs adds wallet inputs to the set until the specified budget is
|
||||||
// met. When sweeping inputs with required outputs, although there's budget
|
// met. When sweeping inputs with required outputs, although there's budget
|
||||||
// specified, it cannot be directly spent from these required outputs. Instead,
|
// specified, it cannot be directly spent from these required outputs. Instead,
|
||||||
@@ -350,11 +365,6 @@ func (b *BudgetInputSet) AddWalletInputs(wallet Wallet) error {
|
|||||||
return fmt.Errorf("list unspent witness: %w", err)
|
return fmt.Errorf("list unspent witness: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit early if there are no wallet UTXOs.
|
|
||||||
if len(utxos) == 0 {
|
|
||||||
return fmt.Errorf("%w: empty wallet", ErrNotEnoughInputs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort the UTXOs by putting smaller values at the start of the slice
|
// Sort the UTXOs by putting smaller values at the start of the slice
|
||||||
// to avoid locking large UTXO for sweeping.
|
// to avoid locking large UTXO for sweeping.
|
||||||
//
|
//
|
||||||
@@ -377,8 +387,20 @@ func (b *BudgetInputSet) AddWalletInputs(wallet Wallet) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Warn("Not enough wallet UTXOs to cover the budget, sweeping " +
|
// Exit if there are no inputs can contribute to the fees.
|
||||||
"anyway...")
|
if !b.hasNormalInput() {
|
||||||
|
return ErrNotEnoughInputs
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's at least one input that can contribute to fees, we allow
|
||||||
|
// the sweep to continue, even though the full budget can't be met.
|
||||||
|
// Maybe later more wallet inputs will become available and we can add
|
||||||
|
// them if needed.
|
||||||
|
budget := b.Budget()
|
||||||
|
total, spendable := b.inputAmts()
|
||||||
|
log.Warnf("Not enough wallet UTXOs: need budget=%v, has spendable=%v, "+
|
||||||
|
"total=%v, missing at least %v, sweeping anyway...", budget,
|
||||||
|
spendable, total, budget-spendable)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -416,6 +438,28 @@ func (b *BudgetInputSet) Inputs() []input.Input {
|
|||||||
return inputs
|
return inputs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// inputAmts returns two values for the set - the total input amount, and the
|
||||||
|
// spendable amount. Only the spendable amount can be used to pay the fees.
|
||||||
|
func (b *BudgetInputSet) inputAmts() (btcutil.Amount, btcutil.Amount) {
|
||||||
|
var (
|
||||||
|
totalAmt btcutil.Amount
|
||||||
|
spendableAmt btcutil.Amount
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, inp := range b.inputs {
|
||||||
|
output := btcutil.Amount(inp.SignDesc().Output.Value)
|
||||||
|
totalAmt += output
|
||||||
|
|
||||||
|
if inp.RequiredTxOut() != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
spendableAmt += output
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalAmt, spendableAmt
|
||||||
|
}
|
||||||
|
|
||||||
// StartingFeeRate returns the max starting fee rate found in the inputs.
|
// StartingFeeRate returns the max starting fee rate found in the inputs.
|
||||||
//
|
//
|
||||||
// NOTE: part of the InputSet interface.
|
// NOTE: part of the InputSet interface.
|
||||||
|
@@ -456,6 +456,13 @@ func TestAddWalletInputsNotEnoughInputs(t *testing.T) {
|
|||||||
mockInput.On("RequiredTxOut").Return(&wire.TxOut{})
|
mockInput.On("RequiredTxOut").Return(&wire.TxOut{})
|
||||||
defer mockInput.AssertExpectations(t)
|
defer mockInput.AssertExpectations(t)
|
||||||
|
|
||||||
|
sd := &input.SignDescriptor{
|
||||||
|
Output: &wire.TxOut{
|
||||||
|
Value: budget,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mockInput.On("SignDesc").Return(sd).Once()
|
||||||
|
|
||||||
// Create a pending input that requires 10k satoshis.
|
// Create a pending input that requires 10k satoshis.
|
||||||
pi := &SweeperInput{
|
pi := &SweeperInput{
|
||||||
Input: mockInput,
|
Input: mockInput,
|
||||||
@@ -484,6 +491,71 @@ func TestAddWalletInputsNotEnoughInputs(t *testing.T) {
|
|||||||
require.Len(t, set.inputs, 2)
|
require.Len(t, set.inputs, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestAddWalletInputsEmptyWalletSuccess checks that when the wallet is empty,
|
||||||
|
// if there is a normal input, no error is returned.
|
||||||
|
func TestAddWalletInputsEmptyWalletSuccess(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
wallet := &MockWallet{}
|
||||||
|
defer wallet.AssertExpectations(t)
|
||||||
|
|
||||||
|
// Specify the min and max confs used in
|
||||||
|
// ListUnspentWitnessFromDefaultAccount.
|
||||||
|
minConf, maxConf := int32(1), int32(math.MaxInt32)
|
||||||
|
|
||||||
|
// Assume the desired budget is 10k satoshis.
|
||||||
|
const budget = 10_000
|
||||||
|
|
||||||
|
// Create a mock input that has required outputs.
|
||||||
|
mockInput1 := &input.MockInput{}
|
||||||
|
defer mockInput1.AssertExpectations(t)
|
||||||
|
|
||||||
|
mockInput1.On("RequiredTxOut").Return(&wire.TxOut{})
|
||||||
|
|
||||||
|
sd := &input.SignDescriptor{
|
||||||
|
Output: &wire.TxOut{
|
||||||
|
Value: budget,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mockInput1.On("SignDesc").Return(sd).Once()
|
||||||
|
|
||||||
|
// Create a pending input that requires 10k satoshis.
|
||||||
|
pi1 := &SweeperInput{
|
||||||
|
Input: mockInput1,
|
||||||
|
params: Params{Budget: budget},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a mock input that doesn't require outputs.
|
||||||
|
mockInput2 := &input.MockInput{}
|
||||||
|
defer mockInput2.AssertExpectations(t)
|
||||||
|
|
||||||
|
mockInput2.On("RequiredTxOut").Return(nil)
|
||||||
|
sd2 := &input.SignDescriptor{
|
||||||
|
Output: &wire.TxOut{
|
||||||
|
Value: budget,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mockInput2.On("SignDesc").Return(sd2).Once()
|
||||||
|
|
||||||
|
// Create a pending input that requires 10k satoshis.
|
||||||
|
pi2 := &SweeperInput{
|
||||||
|
Input: mockInput2,
|
||||||
|
params: Params{Budget: budget},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock the wallet to return empty utxos.
|
||||||
|
wallet.On("ListUnspentWitnessFromDefaultAccount",
|
||||||
|
minConf, maxConf).Return([]*lnwallet.Utxo{}, nil).Once()
|
||||||
|
|
||||||
|
// Initialize an input set with the pending inputs.
|
||||||
|
set := BudgetInputSet{inputs: []*SweeperInput{pi1, pi2}}
|
||||||
|
|
||||||
|
// Add wallet inputs to the input set, which should return no error
|
||||||
|
// although the wallet is empty.
|
||||||
|
err := set.AddWalletInputs(wallet)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
// TestAddWalletInputsSuccess checks that when there are enough wallet utxos,
|
// TestAddWalletInputsSuccess checks that when there are enough wallet utxos,
|
||||||
// they are added to the input set.
|
// they are added to the input set.
|
||||||
func TestAddWalletInputsSuccess(t *testing.T) {
|
func TestAddWalletInputsSuccess(t *testing.T) {
|
||||||
|
Reference in New Issue
Block a user