multi: merge DetermineFeePerKw and Estimate

This commit moves `DetermineFeePerKw` into the `Estimate` method on
`FeePreference`. A few callsites previously calling `DetermineFeePerKw`
without the max fee rate is now also temporarily fixed by forcing them
to use `Estimate` with the default sweeper max fee rate.
This commit is contained in:
yyforyongyu
2023-11-03 18:48:09 +08:00
parent 18b06b7303
commit 6ff6c86155
8 changed files with 228 additions and 179 deletions

View File

@@ -27,6 +27,10 @@ var (
// ErrNoFeePreference is returned when we attempt to satisfy a sweep
// request from a client whom did not specify a fee preference.
ErrNoFeePreference = errors.New("no fee preference specified")
// ErrFeePreferenceConflict is returned when both a fee rate and a conf
// target is set for a fee preference.
ErrFeePreferenceConflict = errors.New("fee preference conflict")
)
// FeePreference allows callers to express their time value for inclusion of a
@@ -50,35 +54,70 @@ func (p FeePreference) String() string {
}
// Estimate returns a fee rate for the given fee preference. It ensures that
// the fee rate respects the bounds of the relay fee and the specified max fee
// rates.
//
// TODO(yy): add tests.
// the fee rate respects the bounds of the relay fee and the max fee rates, if
// specified.
func (f FeePreference) Estimate(estimator chainfee.Estimator,
maxFeeRate chainfee.SatPerKWeight) (chainfee.SatPerKWeight, error) {
var (
feeRate chainfee.SatPerKWeight
err error
)
switch {
// Ensure a type of fee preference is specified to prevent using a
// default below.
case f.FeeRate == 0 && f.ConfTarget == 0:
return 0, ErrNoFeePreference
// If both values are set, then we'll return an error as we require a
// strict directive.
case f.FeeRate != 0 && f.ConfTarget != 0:
return 0, ErrFeePreferenceConflict
// If the target number of confirmations is set, then we'll use that to
// consult our fee estimator for an adequate fee.
case f.ConfTarget != 0:
feeRate, err = estimator.EstimateFeePerKW((f.ConfTarget))
if err != nil {
return 0, fmt.Errorf("unable to query fee "+
"estimator: %w", err)
}
// If a manual sat/kw fee rate is set, then we'll use that directly.
// We'll need to convert it to sat/kw as this is what we use
// internally.
case f.FeeRate != 0:
feeRate = f.FeeRate
// Because the user can specify 1 sat/vByte on the RPC
// interface, which corresponds to 250 sat/kw, we need to bump
// that to the minimum "safe" fee rate which is 253 sat/kw.
if feeRate == chainfee.AbsoluteFeePerKwFloor {
log.Infof("Manual fee rate input of %d sat/kw is "+
"too low, using %d sat/kw instead", feeRate,
chainfee.FeePerKwFloor)
feeRate = chainfee.FeePerKwFloor
}
}
// Get the relay fee as the min fee rate.
minFeeRate := estimator.RelayFeePerKW()
// Ensure a type of fee preference is specified to prevent using a
// default below.
if f.FeeRate == 0 && f.ConfTarget == 0 {
return 0, ErrNoFeePreference
}
feeRate, err := DetermineFeePerKw(estimator, f)
if err != nil {
return 0, err
}
// If that bumped fee rate of at least 253 sat/kw is still lower than
// the relay fee rate, we return an error to let the user know. Note
// that "Relay fee rate" may mean slightly different things depending
// on the backend. For bitcoind, it is effectively max(relay fee, min
// mempool fee).
if feeRate < minFeeRate {
return 0, fmt.Errorf("%w: got %v, minimum is %v",
ErrFeePreferenceTooLow, feeRate, minFeeRate)
}
// If the estimated fee rate is above the maximum allowed fee rate,
// default to the max fee rate.
if feeRate > maxFeeRate {
// If a maxFeeRate is specified and the estimated fee rate is above the
// maximum allowed fee rate, default to the max fee rate.
if maxFeeRate != 0 && feeRate > maxFeeRate {
log.Warnf("Estimated fee rate %v exceeds max allowed fee "+
"rate %v, using max fee rate instead", feeRate,
maxFeeRate)
@@ -89,80 +128,6 @@ func (f FeePreference) Estimate(estimator chainfee.Estimator,
return feeRate, nil
}
// DetermineFeePerKw will determine the fee in sat/kw that should be paid given
// an estimator, a confirmation target, and a manual value for sat/byte. A
// value is chosen based on the two free parameters as one, or both of them can
// be zero.
//
// TODO(yy): move it into the above `Estimate`.
func DetermineFeePerKw(feeEstimator chainfee.Estimator,
feePref FeePreference) (chainfee.SatPerKWeight, error) {
switch {
// If both values are set, then we'll return an error as we require a
// strict directive.
case feePref.FeeRate != 0 && feePref.ConfTarget != 0:
return 0, fmt.Errorf("only FeeRate or ConfTarget should " +
"be set for FeePreferences")
// If the target number of confirmations is set, then we'll use that to
// consult our fee estimator for an adequate fee.
case feePref.ConfTarget != 0:
feePerKw, err := feeEstimator.EstimateFeePerKW(
uint32(feePref.ConfTarget),
)
if err != nil {
return 0, fmt.Errorf("unable to query fee "+
"estimator: %v", err)
}
return feePerKw, nil
// If a manual sat/byte fee rate is set, then we'll use that directly.
// We'll need to convert it to sat/kw as this is what we use
// internally.
case feePref.FeeRate != 0:
feePerKW := feePref.FeeRate
// Because the user can specify 1 sat/vByte on the RPC
// interface, which corresponds to 250 sat/kw, we need to bump
// that to the minimum "safe" fee rate which is 253 sat/kw.
if feePerKW == chainfee.AbsoluteFeePerKwFloor {
log.Infof("Manual fee rate input of %d sat/kw is "+
"too low, using %d sat/kw instead", feePerKW,
chainfee.FeePerKwFloor)
feePerKW = chainfee.FeePerKwFloor
}
// If that bumped fee rate of at least 253 sat/kw is still lower
// than the relay fee rate, we return an error to let the user
// know. Note that "Relay fee rate" may mean slightly different
// things depending on the backend. For bitcoind, it is
// effectively max(relay fee, min mempool fee).
minFeePerKW := feeEstimator.RelayFeePerKW()
if feePerKW < minFeePerKW {
return 0, fmt.Errorf("manual fee rate input of %d "+
"sat/kw is too low to be accepted into the "+
"mempool or relayed to the network", feePerKW)
}
return feePerKW, nil
// Otherwise, we'll attempt a relaxed confirmation target for the
// transaction
default:
feePerKw, err := feeEstimator.EstimateFeePerKW(
defaultNumBlocksEstimate,
)
if err != nil {
return 0, fmt.Errorf("unable to query fee estimator: "+
"%v", err)
}
return feePerKw, nil
}
}
// UtxoSource is an interface that allows a caller to access a source of UTXOs
// to use when crafting sweep transactions.
type UtxoSource interface {