Skip to content

Commit c7bea07

Browse files
committed
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.
1 parent b6daa3b commit c7bea07

File tree

2 files changed

+123
-7
lines changed

2 files changed

+123
-7
lines changed

sweep/tx_input_set.go

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,21 @@ func (b *BudgetInputSet) copyInputs() []*SweeperInput {
327327
return inputs
328328
}
329329

330+
// hasNormalInput return a bool to indicate whether there exists an input that
331+
// doesn't require a TxOut. When an input has no required outputs, it's either a
332+
// wallet input, or an input we want to sweep.
333+
func (b *BudgetInputSet) hasNormalInput() bool {
334+
for _, inp := range b.inputs {
335+
if inp.RequiredTxOut() != nil {
336+
continue
337+
}
338+
339+
return true
340+
}
341+
342+
return false
343+
}
344+
330345
// AddWalletInputs adds wallet inputs to the set until the specified budget is
331346
// met. When sweeping inputs with required outputs, although there's budget
332347
// specified, it cannot be directly spent from these required outputs. Instead,
@@ -350,11 +365,6 @@ func (b *BudgetInputSet) AddWalletInputs(wallet Wallet) error {
350365
return fmt.Errorf("list unspent witness: %w", err)
351366
}
352367

353-
// Exit early if there are no wallet UTXOs.
354-
if len(utxos) == 0 {
355-
return fmt.Errorf("%w: empty wallet", ErrNotEnoughInputs)
356-
}
357-
358368
// Sort the UTXOs by putting smaller values at the start of the slice
359369
// to avoid locking large UTXO for sweeping.
360370
//
@@ -377,8 +387,20 @@ func (b *BudgetInputSet) AddWalletInputs(wallet Wallet) error {
377387
}
378388
}
379389

380-
log.Warn("Not enough wallet UTXOs to cover the budget, sweeping " +
381-
"anyway...")
390+
// Exit if there are no inputs can contribute to the fees.
391+
if !b.hasNormalInput() {
392+
return ErrNotEnoughInputs
393+
}
394+
395+
// If there's at least one input that can contribute to fees, we allow
396+
// the sweep to continue, even though the full budget can't be met.
397+
// Maybe later more wallet inputs will become available and we can add
398+
// them if needed.
399+
budget := b.Budget()
400+
total, spendable := b.inputAmts()
401+
log.Warnf("Not enough wallet UTXOs: need budget=%v, has spendable=%v, "+
402+
"total=%v, missing at least %v, sweeping anyway...", budget,
403+
spendable, total, budget-spendable)
382404

383405
return nil
384406
}
@@ -416,6 +438,28 @@ func (b *BudgetInputSet) Inputs() []input.Input {
416438
return inputs
417439
}
418440

441+
// inputAmts returns two values for the set - the total input amount, and the
442+
// spendable amount. Only the spendable amount can be used to pay the fees.
443+
func (b *BudgetInputSet) inputAmts() (btcutil.Amount, btcutil.Amount) {
444+
var (
445+
totalAmt btcutil.Amount
446+
spendableAmt btcutil.Amount
447+
)
448+
449+
for _, inp := range b.inputs {
450+
output := btcutil.Amount(inp.SignDesc().Output.Value)
451+
totalAmt += output
452+
453+
if inp.RequiredTxOut() != nil {
454+
continue
455+
}
456+
457+
spendableAmt += output
458+
}
459+
460+
return totalAmt, spendableAmt
461+
}
462+
419463
// StartingFeeRate returns the max starting fee rate found in the inputs.
420464
//
421465
// NOTE: part of the InputSet interface.

sweep/tx_input_set_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,13 @@ func TestAddWalletInputsNotEnoughInputs(t *testing.T) {
456456
mockInput.On("RequiredTxOut").Return(&wire.TxOut{})
457457
defer mockInput.AssertExpectations(t)
458458

459+
sd := &input.SignDescriptor{
460+
Output: &wire.TxOut{
461+
Value: budget,
462+
},
463+
}
464+
mockInput.On("SignDesc").Return(sd).Once()
465+
459466
// Create a pending input that requires 10k satoshis.
460467
pi := &SweeperInput{
461468
Input: mockInput,
@@ -484,6 +491,71 @@ func TestAddWalletInputsNotEnoughInputs(t *testing.T) {
484491
require.Len(t, set.inputs, 2)
485492
}
486493

494+
// TestAddWalletInputsEmptyWalletSuccess checks that when the wallet is empty,
495+
// if there is a normal input, no error is returned.
496+
func TestAddWalletInputsEmptyWalletSuccess(t *testing.T) {
497+
t.Parallel()
498+
499+
wallet := &MockWallet{}
500+
defer wallet.AssertExpectations(t)
501+
502+
// Specify the min and max confs used in
503+
// ListUnspentWitnessFromDefaultAccount.
504+
minConf, maxConf := int32(1), int32(math.MaxInt32)
505+
506+
// Assume the desired budget is 10k satoshis.
507+
const budget = 10_000
508+
509+
// Create a mock input that has required outputs.
510+
mockInput1 := &input.MockInput{}
511+
defer mockInput1.AssertExpectations(t)
512+
513+
mockInput1.On("RequiredTxOut").Return(&wire.TxOut{})
514+
515+
sd := &input.SignDescriptor{
516+
Output: &wire.TxOut{
517+
Value: budget,
518+
},
519+
}
520+
mockInput1.On("SignDesc").Return(sd).Once()
521+
522+
// Create a pending input that requires 10k satoshis.
523+
pi1 := &SweeperInput{
524+
Input: mockInput1,
525+
params: Params{Budget: budget},
526+
}
527+
528+
// Create a mock input that doesn't require outputs.
529+
mockInput2 := &input.MockInput{}
530+
defer mockInput2.AssertExpectations(t)
531+
532+
mockInput2.On("RequiredTxOut").Return(nil)
533+
sd2 := &input.SignDescriptor{
534+
Output: &wire.TxOut{
535+
Value: budget,
536+
},
537+
}
538+
mockInput2.On("SignDesc").Return(sd2).Once()
539+
540+
// Create a pending input that requires 10k satoshis.
541+
pi2 := &SweeperInput{
542+
Input: mockInput2,
543+
params: Params{Budget: budget},
544+
}
545+
546+
// Mock the wallet to return empty utxos.
547+
wallet.On("ListUnspentWitnessFromDefaultAccount",
548+
minConf, maxConf).Return([]*lnwallet.Utxo{}, nil).Once()
549+
550+
// Initialize an input set with the pending inputs.
551+
set := BudgetInputSet{inputs: []*SweeperInput{pi1, pi2}}
552+
553+
// Add wallet inputs to the input set, which should return no error
554+
// although the wallet is empty.
555+
err := set.AddWalletInputs(wallet)
556+
require.NoError(t, err)
557+
}
558+
487559
// TestAddWalletInputsSuccess checks that when there are enough wallet utxos,
488560
// they are added to the input set.
489561
func TestAddWalletInputsSuccess(t *testing.T) {

0 commit comments

Comments
 (0)