diff --git a/contractcourt/commit_sweep_resolver.go b/contractcourt/commit_sweep_resolver.go index 62a455f1d..f2061c0fa 100644 --- a/contractcourt/commit_sweep_resolver.go +++ b/contractcourt/commit_sweep_resolver.go @@ -206,7 +206,7 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) { c.log.Infof("sweeping commit output") feePref := sweep.FeePreference{ConfTarget: commitOutputConfTarget} - resultChan, err := c.Sweeper.SweepInput(inp, feePref) + resultChan, err := c.Sweeper.SweepInput(inp, sweep.Params{Fee: feePref}) if err != nil { c.log.Errorf("unable to sweep input: %v", err) diff --git a/contractcourt/commit_sweep_resolver_test.go b/contractcourt/commit_sweep_resolver_test.go index 957e0802c..4ca018334 100644 --- a/contractcourt/commit_sweep_resolver_test.go +++ b/contractcourt/commit_sweep_resolver_test.go @@ -102,8 +102,8 @@ func newMockSweeper() *mockSweeper { } } -func (s *mockSweeper) SweepInput(input input.Input, - feePreference sweep.FeePreference) (chan sweep.Result, error) { +func (s *mockSweeper) SweepInput(input input.Input, params sweep.Params) ( + chan sweep.Result, error) { s.sweptInputs <- input diff --git a/contractcourt/interfaces.go b/contractcourt/interfaces.go index 682eb2acb..45e9b0bd4 100644 --- a/contractcourt/interfaces.go +++ b/contractcourt/interfaces.go @@ -43,8 +43,8 @@ type OnionProcessor interface { // UtxoSweeper defines the sweep functions that contract court requires. type UtxoSweeper interface { // SweepInput sweeps inputs back into the wallet. - SweepInput(input input.Input, - feePreference sweep.FeePreference) (chan sweep.Result, error) + SweepInput(input input.Input, params sweep.Params) (chan sweep.Result, + error) // CreateSweepTx accepts a list of inputs and signs and generates a txn // that spends from them. This method also makes an accurate fee diff --git a/lnrpc/walletrpc/walletkit_server.go b/lnrpc/walletrpc/walletkit_server.go index dbbd898da..53a25e7bc 100644 --- a/lnrpc/walletrpc/walletkit_server.go +++ b/lnrpc/walletrpc/walletkit_server.go @@ -536,7 +536,7 @@ func (w *WalletKit) BumpFee(ctx context.Context, } input := input.NewBaseInput(op, witnessType, signDesc, uint32(currentHeight)) - if _, err = w.cfg.Sweeper.SweepInput(input, feePreference); err != nil { + if _, err = w.cfg.Sweeper.SweepInput(input, sweep.Params{Fee: feePreference}); err != nil { return nil, err } diff --git a/sweep/sweeper.go b/sweep/sweeper.go index 5329e7a0d..6fedcc4b3 100644 --- a/sweep/sweeper.go +++ b/sweep/sweeper.go @@ -61,6 +61,14 @@ var ( DefaultMaxSweepAttempts = 10 ) +// Params contains the parameters that control the sweeping process. +type Params struct { + // Fee is the fee preference of the client who requested the input to be + // swept. If a confirmation target is specified, then we'll map it into + // a fee rate whenever we attempt to cluster inputs for a sweep. + Fee FeePreference +} + // pendingInput is created when an input reaches the main loop for the first // time. It tracks all relevant state that is needed for sweeping. type pendingInput struct { @@ -84,11 +92,8 @@ type pendingInput struct { // made to sweep this tx. publishAttempts int - // feePreference is the fee preference of the client who requested the - // input to be swept. If a confirmation target is specified, then we'll - // map it into a fee rate whenever we attempt to cluster inputs for a - // sweep. - feePreference FeePreference + // params contains the parameters that control the sweeping process. + params Params // lastFeeRate is the most recent fee rate used for this input within a // transaction broadcast to the network. @@ -266,9 +271,9 @@ type Result struct { // sweepInputMessage structs are used in the internal channel between the // SweepInput call and the sweeper main loop. type sweepInputMessage struct { - input input.Input - feePreference FeePreference - resultChan chan Result + input input.Input + params Params + resultChan chan Result } // New returns a new Sweeper instance. @@ -368,26 +373,27 @@ func (s *UtxoSweeper) Stop() error { // Because it is an interface and we don't know what is exactly behind it, we // cannot make a local copy in sweeper. func (s *UtxoSweeper) SweepInput(input input.Input, - feePreference FeePreference) (chan Result, error) { + params Params) (chan Result, error) { if input == nil || input.OutPoint() == nil || input.SignDesc() == nil { return nil, errors.New("nil input received") } // Ensure the client provided a sane fee preference. - if _, err := s.feeRateForPreference(feePreference); err != nil { + if _, err := s.feeRateForPreference(params.Fee); err != nil { return nil, err } log.Infof("Sweep request received: out_point=%v, witness_type=%v, "+ "time_lock=%v, amount=%v, fee_preference=%v", input.OutPoint(), input.WitnessType(), input.BlocksToMaturity(), - btcutil.Amount(input.SignDesc().Output.Value), feePreference) + btcutil.Amount(input.SignDesc().Output.Value), + params.Fee) sweeperInput := &sweepInputMessage{ - input: input, - feePreference: feePreference, - resultChan: make(chan Result, 1), + input: input, + params: params, + resultChan: make(chan Result, 1), } // Deliver input to main event loop. @@ -470,7 +476,7 @@ func (s *UtxoSweeper) collector(blockEpochs <-chan *chainntnfs.BlockEpoch) { listeners: []chan Result{input.resultChan}, input: input.input, minPublishHeight: bestHeight, - feePreference: input.feePreference, + params: input.params, } s.pendingInputs[outpoint] = pendInput @@ -653,7 +659,7 @@ func (s *UtxoSweeper) clusterBySweepFeeRate() []inputCluster { // First, we'll group together all inputs with similar fee rates. This // is done by determining the fee rate bucket they should belong in. for op, input := range s.pendingInputs { - feeRate, err := s.feeRateForPreference(input.feePreference) + feeRate, err := s.feeRateForPreference(input.params.Fee) if err != nil { log.Warnf("Skipping input %v: %v", op, err) continue @@ -1072,9 +1078,9 @@ func (s *UtxoSweeper) handleBumpFeeReq(req *bumpFeeReq, } log.Debugf("Updating fee preference for %v from %v to %v", req.input, - pendingInput.feePreference, req.feePreference) + pendingInput.params.Fee, req.feePreference) - pendingInput.feePreference = req.feePreference + pendingInput.params.Fee = req.feePreference // We'll reset the input's publish height to the current so that a new // transaction can be created that replaces the transaction currently diff --git a/sweep/sweeper_test.go b/sweep/sweeper_test.go index 69c92daa8..8f9d3d7f3 100644 --- a/sweep/sweeper_test.go +++ b/sweep/sweeper_test.go @@ -25,7 +25,7 @@ var ( testMaxInputsPerTx = 3 - defaultFeePref = FeePreference{ConfTarget: 1} + defaultFeePref = Params{Fee: FeePreference{ConfTarget: 1}} ) type sweeperTestContext struct { @@ -354,7 +354,7 @@ func TestSuccess(t *testing.T) { ctx := createSweeperTestContext(t) // Sweeping an input without a fee preference should result in an error. - _, err := ctx.sweeper.SweepInput(spendableInputs[0], FeePreference{}) + _, err := ctx.sweeper.SweepInput(spendableInputs[0], Params{}) if err != ErrNoFeePreference { t.Fatalf("expected ErrNoFeePreference, got %v", err) } @@ -1003,17 +1003,23 @@ func TestDifferentFeePreferences(t *testing.T) { ctx.estimator.blocksToFee[highFeePref.ConfTarget] = highFeeRate input1 := spendableInputs[0] - resultChan1, err := ctx.sweeper.SweepInput(input1, highFeePref) + resultChan1, err := ctx.sweeper.SweepInput( + input1, Params{Fee: highFeePref}, + ) if err != nil { t.Fatal(err) } input2 := spendableInputs[1] - resultChan2, err := ctx.sweeper.SweepInput(input2, highFeePref) + resultChan2, err := ctx.sweeper.SweepInput( + input2, Params{Fee: highFeePref}, + ) if err != nil { t.Fatal(err) } input3 := spendableInputs[2] - resultChan3, err := ctx.sweeper.SweepInput(input3, lowFeePref) + resultChan3, err := ctx.sweeper.SweepInput( + input3, Params{Fee: lowFeePref}, + ) if err != nil { t.Fatal(err) } @@ -1067,16 +1073,23 @@ func TestPendingInputs(t *testing.T) { ctx.estimator.blocksToFee[highFeePref.ConfTarget] = highFeeRate input1 := spendableInputs[0] - resultChan1, err := ctx.sweeper.SweepInput(input1, highFeePref) + resultChan1, err := ctx.sweeper.SweepInput( + input1, Params{Fee: highFeePref}, + ) if err != nil { t.Fatal(err) } input2 := spendableInputs[1] - if _, err := ctx.sweeper.SweepInput(input2, highFeePref); err != nil { + _, err = ctx.sweeper.SweepInput( + input2, Params{Fee: highFeePref}, + ) + if err != nil { t.Fatal(err) } input3 := spendableInputs[2] - resultChan3, err := ctx.sweeper.SweepInput(input3, lowFeePref) + resultChan3, err := ctx.sweeper.SweepInput( + input3, Params{Fee: lowFeePref}, + ) if err != nil { t.Fatal(err) } @@ -1132,7 +1145,9 @@ func TestBumpFeeRBF(t *testing.T) { input := createTestInput( btcutil.SatoshiPerBitcoin, input.CommitmentTimeLock, ) - sweepResult, err := ctx.sweeper.SweepInput(&input, lowFeePref) + sweepResult, err := ctx.sweeper.SweepInput( + &input, Params{Fee: lowFeePref}, + ) if err != nil { t.Fatal(err) } diff --git a/utxonursery.go b/utxonursery.go index 2e749130f..d05a04ed5 100644 --- a/utxonursery.go +++ b/utxonursery.go @@ -201,7 +201,7 @@ type NurseryConfig struct { Store NurseryStore // Sweep sweeps an input back to the wallet. - SweepInput func(input.Input, sweep.FeePreference) (chan sweep.Result, error) + SweepInput func(input.Input, sweep.Params) (chan sweep.Result, error) } // utxoNursery is a system dedicated to incubating time-locked outputs created @@ -778,7 +778,9 @@ func (u *utxoNursery) sweepMatureOutputs(classHeight uint32, // passed in with disastrous consequences. local := output - resultChan, err := u.cfg.SweepInput(&local, feePref) + resultChan, err := u.cfg.SweepInput( + &local, sweep.Params{Fee: feePref}, + ) if err != nil { return err } diff --git a/utxonursery_test.go b/utxonursery_test.go index 6b0d6de00..579bddcfe 100644 --- a/utxonursery_test.go +++ b/utxonursery_test.go @@ -983,7 +983,7 @@ func newMockSweeper(t *testing.T) *mockSweeper { } func (s *mockSweeper) sweepInput(input input.Input, - _ sweep.FeePreference) (chan sweep.Result, error) { + _ sweep.Params) (chan sweep.Result, error) { utxnLog.Debugf("mockSweeper sweepInput called for %v", *input.OutPoint())