diff --git a/contractcourt/commit_sweep_resolver.go b/contractcourt/commit_sweep_resolver.go index 195d423c9..507e8bb76 100644 --- a/contractcourt/commit_sweep_resolver.go +++ b/contractcourt/commit_sweep_resolver.go @@ -13,17 +13,12 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/sweep" ) -const ( - // commitOutputConfTarget is the default confirmation target we'll use - // for sweeps of commit outputs that belong to us. - commitOutputConfTarget = 6 -) - // commitSweepResolver is a resolver that will attempt to sweep the commitment // output paying to us, in the case that the remote party broadcasts their // version of the commitment transaction. We can sweep this output immediately, @@ -347,12 +342,23 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) { // TODO(roasbeef): instead of ading ctrl block to the sign desc, make // new input type, have sweeper set it? - // With our input constructed, we'll now offer it to the - // sweeper. - c.log.Infof("sweeping commit output") + // Calculate the budget for the sweeping this input. + budget := calculateBudget( + btcutil.Amount(inp.SignDesc().Output.Value), + c.Budget.ToLocalRatio, c.Budget.ToLocal, + ) + c.log.Infof("Sweeping commit output using budget=%v", budget) - feePref := sweep.FeeEstimateInfo{ConfTarget: commitOutputConfTarget} - resultChan, err := c.Sweeper.SweepInput(inp, sweep.Params{Fee: feePref}) + // With our input constructed, we'll now offer it to the sweeper. + resultChan, err := c.Sweeper.SweepInput( + inp, sweep.Params{ + Budget: budget, + + // Specify a nil deadline here as there's no time + // pressure. + DeadlineHeight: fn.None[int32](), + }, + ) 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 5cff3a6d5..6730cddc8 100644 --- a/contractcourt/commit_sweep_resolver_test.go +++ b/contractcourt/commit_sweep_resolver_test.go @@ -129,15 +129,18 @@ func (s *mockSweeper) SweepInput(input input.Input, params sweep.Params) ( s.sweptInputs <- input - // TODO(yy): use `mock.Mock` to avoid the conversion. - fee, ok := params.Fee.(sweep.FeeEstimateInfo) - if !ok { - return nil, fmt.Errorf("unexpected fee type: %T", params.Fee) - } + // TODO(yy): replace mockSweeper with `mock.Mock`. + if params.Fee != nil { + fee, ok := params.Fee.(sweep.FeeEstimateInfo) + if !ok { + return nil, fmt.Errorf("unexpected fee type: %T", + params.Fee) + } - // Update the deadlines used if it's set. - if fee.ConfTarget != 0 { - s.deadlines = append(s.deadlines, int(fee.ConfTarget)) + // Update the deadlines used if it's set. + if fee.ConfTarget != 0 { + s.deadlines = append(s.deadlines, int(fee.ConfTarget)) + } } result := make(chan sweep.Result, 1) diff --git a/contractcourt/config.go b/contractcourt/config.go index 7f4563fe1..b5466c6e2 100644 --- a/contractcourt/config.go +++ b/contractcourt/config.go @@ -111,3 +111,27 @@ func DefaultBudgetConfig() *BudgetConfig { NoDeadlineHTLCRatio: DefaultBudgetRatio, } } + +// calculateBudget takes an output value, a configured ratio and budget value, +// and returns the budget to use for sweeping the output. If the budget value +// is set, it will be used as cap. +func calculateBudget(value btcutil.Amount, ratio float64, + max btcutil.Amount) btcutil.Amount { + + // If ratio is not set, using the default value. + if ratio == 0 { + ratio = DefaultBudgetRatio + } + + budget := value.MulF64(ratio) + + log.Tracef("Calculated budget=%v using value=%v, ratio=%v, cap=%v", + budget, value, ratio, max) + + if max != 0 && budget > max { + log.Debugf("Calculated budget=%v is capped at %v", budget, max) + return max + } + + return budget +} diff --git a/contractcourt/config_test.go b/contractcourt/config_test.go index c22e32df2..e7bc22a7a 100644 --- a/contractcourt/config_test.go +++ b/contractcourt/config_test.go @@ -3,6 +3,7 @@ package contractcourt import ( "testing" + "github.com/btcsuite/btcd/btcutil" "github.com/stretchr/testify/require" ) @@ -81,3 +82,51 @@ func TestBudgetConfigValidate(t *testing.T) { }) } } + +// TestCalculateBudget checks that the budget calculation works as expected. +func TestCalculateBudget(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + value btcutil.Amount + ratio float64 + max btcutil.Amount + expected btcutil.Amount + }{ + { + // When the ratio is not specified, the default 0.5 + // should be used. + name: "use default ratio", + value: btcutil.Amount(1000), + ratio: 0, + max: 0, + expected: btcutil.Amount(500), + }, + { + // When the ratio is specified, the default is not + // used. + name: "use specified ratio", + value: btcutil.Amount(1000), + ratio: 0.1, + max: 0, + expected: btcutil.Amount(100), + }, + { + // When the max is specified, the budget should be + // capped at that value. + name: "budget capped at max", + value: btcutil.Amount(1000), + ratio: 0.1, + max: btcutil.Amount(1), + expected: btcutil.Amount(1), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + budget := calculateBudget(tc.value, tc.ratio, tc.max) + require.Equal(t, tc.expected, budget) + }) + } +}