lnwallet: switch fee estimation to return sat/kw fee rates

In this commit, we modify our FeeEstimator interface to return an
estimated fee rate in sat/kw. Recently, due to low fees on the network,
users have been experiencing failures broadcasting transactions due to
not meeting specific fee requirements. This was happening more often
than not, as the estimated fee returned by backend nodes (bitcoind and
btcd) only takes into account vbytes, rather than weight. The fees
returned are also expressed in sat/kb, so we must take care that we do
not lose precision while converting to sat/kw. In the event that this
happens, a fee floor of 253 sat/kw has been added. This fee rate
originates from bitcoind rounding up the conversion from weight to
vbytes.
This commit is contained in:
Wilmer Paulino 2018-07-27 18:37:05 -07:00
parent 30b156c706
commit b3d5d7ab9c
No known key found for this signature in database
GPG Key ID: 6DF57B9F9514972F
2 changed files with 175 additions and 150 deletions

@ -8,38 +8,49 @@ import (
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
) )
// SatPerVByte represents a fee rate in satoshis per vbyte. const (
type SatPerVByte btcutil.Amount // FeePerKwFloor is the lowest fee rate in sat/kw that we should use for
// determining transaction fees.
FeePerKwFloor SatPerKWeight = 253
)
// FeeForVSize calculates the fee resulting from this fee rate and // SatPerKVByte represents a fee rate in sat/kb.
// the given vsize in vbytes. type SatPerKVByte btcutil.Amount
func (s SatPerVByte) FeeForVSize(vbytes int64) btcutil.Amount {
return btcutil.Amount(s) * btcutil.Amount(vbytes) // FeeForVSize calculates the fee resulting from this fee rate and the given
// vsize in vbytes.
func (s SatPerKVByte) FeeForVSize(vbytes int64) btcutil.Amount {
return btcutil.Amount(s) * btcutil.Amount(vbytes) / 1000
} }
// FeePerKWeight converts the fee rate into SatPerKWeight. // FeePerKWeight converts the current fee rate from sat/kb to sat/kw.
func (s SatPerVByte) FeePerKWeight() SatPerKWeight { func (s SatPerKVByte) FeePerKWeight() SatPerKWeight {
return SatPerKWeight(s * 1000 / blockchain.WitnessScaleFactor) return SatPerKWeight(s / blockchain.WitnessScaleFactor)
} }
// SatPerKWeight represents a fee rate in satoshis per kilo weight unit. // SatPerKWeight represents a fee rate in sat/kw.
type SatPerKWeight btcutil.Amount type SatPerKWeight btcutil.Amount
// FeeForWeight calculates the fee resulting from this fee rate and the // FeeForWeight calculates the fee resulting from this fee rate and the given
// given weight in weight units (wu). // weight in weight units (wu).
func (s SatPerKWeight) FeeForWeight(wu int64) btcutil.Amount { func (s SatPerKWeight) FeeForWeight(wu int64) btcutil.Amount {
// The resulting fee is rounded down, as specified in BOLT#03. // The resulting fee is rounded down, as specified in BOLT#03.
return btcutil.Amount(s) * btcutil.Amount(wu) / 1000 return btcutil.Amount(s) * btcutil.Amount(wu) / 1000
} }
// FeePerKVByte converts the current fee rate from sat/kw to sat/kb.
func (s SatPerKWeight) FeePerKVByte() SatPerKVByte {
return SatPerKVByte(s * blockchain.WitnessScaleFactor)
}
// FeeEstimator provides the ability to estimate on-chain transaction fees for // FeeEstimator provides the ability to estimate on-chain transaction fees for
// various combinations of transaction sizes and desired confirmation time // various combinations of transaction sizes and desired confirmation time
// (measured by number of blocks). // (measured by number of blocks).
type FeeEstimator interface { type FeeEstimator interface {
// EstimateFeePerVSize takes in a target for the number of blocks until // EstimateFeePerKW takes in a target for the number of blocks until an
// an initial confirmation and returns the estimated fee expressed in // initial confirmation and returns the estimated fee expressed in
// satoshis/vbyte. // sat/kw.
EstimateFeePerVSize(numBlocks uint32) (SatPerVByte, error) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error)
// Start signals the FeeEstimator to start any processes or goroutines // Start signals the FeeEstimator to start any processes or goroutines
// it needs to perform its duty. // it needs to perform its duty.
@ -54,16 +65,16 @@ type FeeEstimator interface {
// requests. It is designed to be replaced by a proper fee calculation // requests. It is designed to be replaced by a proper fee calculation
// implementation. // implementation.
type StaticFeeEstimator struct { type StaticFeeEstimator struct {
// FeeRate is the static fee rate in satoshis-per-vbyte that will be // FeePerKW is the static fee rate in satoshis-per-vbyte that will be
// returned by this fee estimator. // returned by this fee estimator.
FeeRate SatPerVByte FeePerKW SatPerKWeight
} }
// EstimateFeePerVSize will return a static value for fee calculations. // EstimateFeePerKW will return a static value for fee calculations.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the FeeEstimator interface.
func (e StaticFeeEstimator) EstimateFeePerVSize(numBlocks uint32) (SatPerVByte, error) { func (e StaticFeeEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) {
return e.FeeRate, nil return e.FeePerKW, nil
} }
// Start signals the FeeEstimator to start any processes or goroutines // Start signals the FeeEstimator to start any processes or goroutines
@ -90,16 +101,16 @@ var _ FeeEstimator = (*StaticFeeEstimator)(nil)
// by the RPC interface of an active btcd node. This implementation will proxy // by the RPC interface of an active btcd node. This implementation will proxy
// any fee estimation requests to btcd's RPC interface. // any fee estimation requests to btcd's RPC interface.
type BtcdFeeEstimator struct { type BtcdFeeEstimator struct {
// fallBackFeeRate is the fall back fee rate in satoshis per vbyte that // fallbackFeePerKW is the fall back fee rate in sat/kw that is returned
// is returned if the fee estimator does not yet have enough data to // if the fee estimator does not yet have enough data to actually
// actually produce fee estimates. // produce fee estimates.
fallBackFeeRate SatPerVByte fallbackFeePerKW SatPerKWeight
// minFeeRate is the minimum relay fee, in sat/vbyte, of the backend // minFeePerKW is the minimum fee, in sat/kw, that we should enforce.
// node. This will be used as the default fee rate of a transaction when // This will be used as the default fee rate for a transaction when the
// the estimated fee rate is too low to allow the transaction to // estimated fee rate is too low to allow the transaction to propagate
// propagate through the network. // through the network.
minFeeRate SatPerVByte minFeePerKW SatPerKWeight
btcdConn *rpcclient.Client btcdConn *rpcclient.Client
} }
@ -110,7 +121,7 @@ type BtcdFeeEstimator struct {
// the occasion that the estimator has insufficient data, or returns zero for a // the occasion that the estimator has insufficient data, or returns zero for a
// fee estimate. // fee estimate.
func NewBtcdFeeEstimator(rpcConfig rpcclient.ConnConfig, func NewBtcdFeeEstimator(rpcConfig rpcclient.ConnConfig,
fallBackFeeRate SatPerVByte) (*BtcdFeeEstimator, error) { fallBackFeeRate SatPerKWeight) (*BtcdFeeEstimator, error) {
rpcConfig.DisableConnectOnNew = true rpcConfig.DisableConnectOnNew = true
rpcConfig.DisableAutoReconnect = false rpcConfig.DisableAutoReconnect = false
@ -120,8 +131,8 @@ func NewBtcdFeeEstimator(rpcConfig rpcclient.ConnConfig,
} }
return &BtcdFeeEstimator{ return &BtcdFeeEstimator{
fallBackFeeRate: fallBackFeeRate, fallbackFeePerKW: fallBackFeeRate,
btcdConn: chainConn, btcdConn: chainConn,
}, nil }, nil
} }
@ -146,9 +157,20 @@ func (b *BtcdFeeEstimator) Start() error {
return err return err
} }
// The fee rate is expressed in sat/KB, so we'll manually convert it to // The fee rate is expressed in sat/kb, so we'll manually convert it to
// our desired sat/vbyte rate. // our desired sat/kw rate.
b.minFeeRate = SatPerVByte(relayFee / 1000) minRelayFeePerKw := SatPerKVByte(relayFee).FeePerKWeight()
// By default, we'll use the backend node's minimum relay fee as the
// minimum fee rate we'll propose for transacations. However, if this
// happens to be lower than our fee floor, we'll enforce that instead.
b.minFeePerKW = minRelayFeePerKw
if b.minFeePerKW < FeePerKwFloor {
b.minFeePerKW = FeePerKwFloor
}
walletLog.Debugf("Using minimum fee rate of %v sat/kw",
int64(b.minFeePerKW))
return nil return nil
} }
@ -163,13 +185,12 @@ func (b *BtcdFeeEstimator) Stop() error {
return nil return nil
} }
// EstimateFeePerVSize takes in a target for the number of blocks until an // EstimateFeePerKW takes in a target for the number of blocks until an initial
// initial confirmation and returns the estimated fee expressed in // confirmation and returns the estimated fee expressed in sat/kw.
// satoshis/vbyte.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the FeeEstimator interface.
func (b *BtcdFeeEstimator) EstimateFeePerVSize(numBlocks uint32) (SatPerVByte, error) { func (b *BtcdFeeEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) {
feeEstimate, err := b.fetchEstimatePerVSize(numBlocks) feeEstimate, err := b.fetchEstimate(numBlocks)
switch { switch {
// If the estimator doesn't have enough data, or returns an error, then // If the estimator doesn't have enough data, or returns an error, then
// to return a proper value, then we'll return the default fall back // to return a proper value, then we'll return the default fall back
@ -179,16 +200,15 @@ func (b *BtcdFeeEstimator) EstimateFeePerVSize(numBlocks uint32) (SatPerVByte, e
fallthrough fallthrough
case feeEstimate == 0: case feeEstimate == 0:
return b.fallBackFeeRate, nil return b.fallbackFeePerKW, nil
} }
return feeEstimate, nil return feeEstimate, nil
} }
// fetchEstimate returns a fee estimate for a transaction to be confirmed in // fetchEstimate returns a fee estimate for a transaction to be confirmed in
// confTarget blocks. The estimate is returned in sat/vbyte. // confTarget blocks. The estimate is returned in sat/kw.
func (b *BtcdFeeEstimator) fetchEstimatePerVSize( func (b *BtcdFeeEstimator) fetchEstimate(confTarget uint32) (SatPerKWeight, error) {
confTarget uint32) (SatPerVByte, error) {
// First, we'll fetch the estimate for our confirmation target. // First, we'll fetch the estimate for our confirmation target.
btcPerKB, err := b.btcdConn.EstimateFee(int64(confTarget)) btcPerKB, err := b.btcdConn.EstimateFee(int64(confTarget))
if err != nil { if err != nil {
@ -202,23 +222,21 @@ func (b *BtcdFeeEstimator) fetchEstimatePerVSize(
return 0, err return 0, err
} }
// The value returned is expressed in fees per KB, while we want // Since we use fee rates in sat/kw internally, we'll convert the
// fee-per-byte, so we'll divide by 1000 to map to satoshis-per-byte // estimated fee rate from its sat/kb representation to sat/kw.
// before returning the estimate. satPerKw := SatPerKVByte(satPerKB).FeePerKWeight()
satPerByte := SatPerVByte(satPerKB / 1000)
// Before proceeding, we'll make sure that this fee rate respects the // Finally, we'll enforce our fee floor.
// minimum relay fee set on the backend node. if satPerKw < b.minFeePerKW {
if satPerByte < b.minFeeRate { walletLog.Debugf("Estimated fee rate of %v sat/kw is too low, "+
walletLog.Debugf("Using backend node's minimum relay fee rate "+ "using fee floor of %v sat/kw instead", b.minFeePerKW)
"of %v sat/vbyte", b.minFeeRate) satPerKw = b.minFeePerKW
satPerByte = b.minFeeRate
} }
walletLog.Debugf("Returning %v sat/vbyte for conf target of %v", walletLog.Debugf("Returning %v sat/kw for conf target of %v",
int64(satPerByte), confTarget) int64(satPerKw), confTarget)
return satPerByte, nil return satPerKw, nil
} }
// A compile-time assertion to ensure that BtcdFeeEstimator implements the // A compile-time assertion to ensure that BtcdFeeEstimator implements the
@ -229,16 +247,16 @@ var _ FeeEstimator = (*BtcdFeeEstimator)(nil)
// backed by the RPC interface of an active bitcoind node. This implementation // backed by the RPC interface of an active bitcoind node. This implementation
// will proxy any fee estimation requests to bitcoind's RPC interface. // will proxy any fee estimation requests to bitcoind's RPC interface.
type BitcoindFeeEstimator struct { type BitcoindFeeEstimator struct {
// fallBackFeeRate is the fall back fee rate in satoshis per vbyte that // fallbackFeePerKW is the fallback fee rate in sat/kw that is returned
// is returned if the fee estimator does not yet have enough data to // if the fee estimator does not yet have enough data to actually
// actually produce fee estimates. // produce fee estimates.
fallBackFeeRate SatPerVByte fallbackFeePerKW SatPerKWeight
// minFeeRate is the minimum relay fee, in sat/vbyte, of the backend // minFeePerKW is the minimum fee, in sat/kw, that we should enforce.
// node. This will be used as the default fee rate of a transaction when // This will be used as the default fee rate for a transaction when the
// the estimated fee rate is too low to allow the transaction to // estimated fee rate is too low to allow the transaction to propagate
// propagate through the network. // through the network.
minFeeRate SatPerVByte minFeePerKW SatPerKWeight
bitcoindConn *rpcclient.Client bitcoindConn *rpcclient.Client
} }
@ -249,7 +267,7 @@ type BitcoindFeeEstimator struct {
// is used in the occasion that the estimator has insufficient data, or returns // is used in the occasion that the estimator has insufficient data, or returns
// zero for a fee estimate. // zero for a fee estimate.
func NewBitcoindFeeEstimator(rpcConfig rpcclient.ConnConfig, func NewBitcoindFeeEstimator(rpcConfig rpcclient.ConnConfig,
fallBackFeeRate SatPerVByte) (*BitcoindFeeEstimator, error) { fallBackFeeRate SatPerKWeight) (*BitcoindFeeEstimator, error) {
rpcConfig.DisableConnectOnNew = true rpcConfig.DisableConnectOnNew = true
rpcConfig.DisableAutoReconnect = false rpcConfig.DisableAutoReconnect = false
@ -261,8 +279,8 @@ func NewBitcoindFeeEstimator(rpcConfig rpcclient.ConnConfig,
} }
return &BitcoindFeeEstimator{ return &BitcoindFeeEstimator{
fallBackFeeRate: fallBackFeeRate, fallbackFeePerKW: fallBackFeeRate,
bitcoindConn: chainConn, bitcoindConn: chainConn,
}, nil }, nil
} }
@ -293,9 +311,20 @@ func (b *BitcoindFeeEstimator) Start() error {
return err return err
} }
// The fee rate is expressed in sat/KB, so we'll manually convert it to // The fee rate is expressed in sat/kb, so we'll manually convert it to
// our desired sat/vbyte rate. // our desired sat/kw rate.
b.minFeeRate = SatPerVByte(relayFee / 1000) minRelayFeePerKw := SatPerKVByte(relayFee).FeePerKWeight()
// By default, we'll use the backend node's minimum relay fee as the
// minimum fee rate we'll propose for transacations. However, if this
// happens to be lower than our fee floor, we'll enforce that instead.
b.minFeePerKW = minRelayFeePerKw
if b.minFeePerKW < FeePerKwFloor {
b.minFeePerKW = FeePerKwFloor
}
walletLog.Debugf("Using minimum fee rate of %v sat/kw",
int64(b.minFeePerKW))
return nil return nil
} }
@ -308,13 +337,12 @@ func (b *BitcoindFeeEstimator) Stop() error {
return nil return nil
} }
// EstimateFeePerVSize takes in a target for the number of blocks until an // EstimateFeePerKW takes in a target for the number of blocks until an initial
// initial confirmation and returns the estimated fee expressed in // confirmation and returns the estimated fee expressed in sat/kw.
// satoshis/vbyte.
// //
// NOTE: This method is part of the FeeEstimator interface. // NOTE: This method is part of the FeeEstimator interface.
func (b *BitcoindFeeEstimator) EstimateFeePerVSize(numBlocks uint32) (SatPerVByte, error) { func (b *BitcoindFeeEstimator) EstimateFeePerKW(numBlocks uint32) (SatPerKWeight, error) {
feeEstimate, err := b.fetchEstimatePerVSize(numBlocks) feeEstimate, err := b.fetchEstimate(numBlocks)
switch { switch {
// If the estimator doesn't have enough data, or returns an error, then // If the estimator doesn't have enough data, or returns an error, then
// to return a proper value, then we'll return the default fall back // to return a proper value, then we'll return the default fall back
@ -324,16 +352,15 @@ func (b *BitcoindFeeEstimator) EstimateFeePerVSize(numBlocks uint32) (SatPerVByt
fallthrough fallthrough
case feeEstimate == 0: case feeEstimate == 0:
return b.fallBackFeeRate, nil return b.fallbackFeePerKW, nil
} }
return feeEstimate, nil return feeEstimate, nil
} }
// fetchEstimatePerVSize returns a fee estimate for a transaction to be confirmed in // fetchEstimate returns a fee estimate for a transaction to be confirmed in
// confTarget blocks. The estimate is returned in sat/vbyte. // confTarget blocks. The estimate is returned in sat/kw.
func (b *BitcoindFeeEstimator) fetchEstimatePerVSize( func (b *BitcoindFeeEstimator) fetchEstimate(confTarget uint32) (SatPerKWeight, error) {
confTarget uint32) (SatPerVByte, error) {
// First, we'll send an "estimatesmartfee" command as a raw request, // First, we'll send an "estimatesmartfee" command as a raw request,
// since it isn't supported by btcd but is available in bitcoind. // since it isn't supported by btcd but is available in bitcoind.
target, err := json.Marshal(uint64(confTarget)) target, err := json.Marshal(uint64(confTarget))
@ -341,45 +368,44 @@ func (b *BitcoindFeeEstimator) fetchEstimatePerVSize(
return 0, err return 0, err
} }
// TODO: Allow selection of economical/conservative modifiers. // TODO: Allow selection of economical/conservative modifiers.
resp, err := b.bitcoindConn.RawRequest("estimatesmartfee", resp, err := b.bitcoindConn.RawRequest(
[]json.RawMessage{target}) "estimatesmartfee", []json.RawMessage{target},
)
if err != nil { if err != nil {
return 0, err return 0, err
} }
// Next, we'll parse the response to get the BTC per KB. // Next, we'll parse the response to get the BTC per KB.
feeEstimate := struct { feeEstimate := struct {
Feerate float64 `json:"feerate"` FeeRate float64 `json:"feerate"`
}{} }{}
err = json.Unmarshal(resp, &feeEstimate) err = json.Unmarshal(resp, &feeEstimate)
if err != nil { if err != nil {
return 0, err return 0, err
} }
// Next, we'll convert the returned value to satoshis, as it's // Next, we'll convert the returned value to satoshis, as it's currently
// currently returned in BTC. // returned in BTC.
satPerKB, err := btcutil.NewAmount(feeEstimate.Feerate) satPerKB, err := btcutil.NewAmount(feeEstimate.FeeRate)
if err != nil { if err != nil {
return 0, err return 0, err
} }
// The value returned is expressed in fees per KB, while we want // Since we use fee rates in sat/kw internally, we'll convert the
// fee-per-byte, so we'll divide by 1000 to map to satoshis-per-byte // estimated fee rate from its sat/kb representation to sat/kw.
// before returning the estimate. satPerKw := SatPerKVByte(satPerKB).FeePerKWeight()
satPerByte := SatPerVByte(satPerKB / 1000)
// Before proceeding, we'll make sure that this fee rate respects the // Finally, we'll enforce our fee floor.
// minimum relay fee set on the backend node. if satPerKw < b.minFeePerKW {
if satPerByte < b.minFeeRate { walletLog.Debugf("Estimated fee rate of %v sat/kw is too low, "+
walletLog.Debugf("Using backend node's minimum relay fee rate "+ "using fee floor of %v sat/kw instead", b.minFeePerKW)
"of %v sat/vbyte", b.minFeeRate) satPerKw = b.minFeePerKW
satPerByte = b.minFeeRate
} }
walletLog.Debugf("Returning %v sat/vbyte for conf target of %v", walletLog.Debugf("Returning %v sat/kw for conf target of %v",
int64(satPerByte), confTarget) int64(satPerKw), confTarget)
return satPerByte, nil return satPerKw, nil
} }
// A compile-time assertion to ensure that BitcoindFeeEstimator implements the // A compile-time assertion to ensure that BitcoindFeeEstimator implements the

@ -13,57 +13,56 @@ import (
func TestFeeRateTypes(t *testing.T) { func TestFeeRateTypes(t *testing.T) {
t.Parallel() t.Parallel()
// Let our fee rate be 100 sat/vbyte. // We'll be calculating the transaction fees for the given measurements
feePerVSize := lnwallet.SatPerVByte(100) // using different fee rates and expecting them to match.
const vsize = 300
const weight = vsize * 4
// It is also equivalent to 25000 sat/kw. // Test the conversion from sat/kw to sat/kb.
feePerKw := feePerVSize.FeePerKWeight() for feePerKw := lnwallet.SatPerKWeight(250); feePerKw < 10000; feePerKw += 50 {
if feePerKw != 25000 { feePerKB := feePerKw.FeePerKVByte()
t.Fatalf("expected %d sat/kw, got %d sat/kw", 25000, if feePerKB != lnwallet.SatPerKVByte(feePerKw*4) {
feePerKw) t.Fatalf("expected %d sat/kb, got %d sat/kb when "+
} "converting from %d sat/kw", feePerKw*4,
feePerKB, feePerKw)
const txVSize = 300
// We'll now run through a set of values for the fee per vsize type,
// making sure the conversion to sat/kw and fee calculation is done
// correctly.
for f := lnwallet.SatPerVByte(0); f <= 40; f++ {
fPerKw := f.FeePerKWeight()
// The kw is always 250*vsize.
if fPerKw != lnwallet.SatPerKWeight(f*250) {
t.Fatalf("expected %d sat/kw, got %d sat/kw, when "+
"converting %d sat/vbyte", f*250, fPerKw, f)
} }
// The tx fee should simply be f*txvsize. // The resulting transaction fee should be the same when using
fee := f.FeeForVSize(txVSize) // both rates.
if fee != btcutil.Amount(f*txVSize) { expectedFee := btcutil.Amount(feePerKw * weight / 1000)
t.Fatalf("expected tx fee to be %d sat, was %d sat", fee1 := feePerKw.FeeForWeight(weight)
f*txVSize, fee) if fee1 != expectedFee {
t.Fatalf("expected fee of %d sats, got %d sats",
expectedFee, fee1)
} }
fee2 := feePerKB.FeeForVSize(vsize)
// The weight is 4*vsize. Fee calculation from the fee/kw if fee2 != expectedFee {
// should result in the same fee. t.Fatalf("expected fee of %d sats, got %d sats",
fee2 := fPerKw.FeeForWeight(txVSize * 4) expectedFee, fee2)
if fee != fee2 {
t.Fatalf("fee calculated from vsize (%d) not equal "+
"fee calculated from weight (%d)", fee, fee2)
} }
} }
// Do the same for fee per kw. // Test the conversion from sat/kb to sat/kw.
for f := lnwallet.SatPerKWeight(0); f < 1500; f++ { for feePerKB := lnwallet.SatPerKVByte(1000); feePerKB < 40000; feePerKB += 1000 {
weight := int64(txVSize * 4) feePerKw := feePerKB.FeePerKWeight()
if feePerKw != lnwallet.SatPerKWeight(feePerKB/4) {
t.Fatalf("expected %d sat/kw, got %d sat/kw when "+
"converting from %d sat/kb", feePerKB/4,
feePerKw, feePerKB)
}
// The expected fee is weight*f / 1000, since the fee is // The resulting transaction fee should be the same when using
// denominated per 1000 wu. // both rates.
expFee := btcutil.Amount(weight) * btcutil.Amount(f) / 1000 expectedFee := btcutil.Amount(feePerKB * vsize / 1000)
fee := f.FeeForWeight(weight) fee1 := feePerKB.FeeForVSize(vsize)
if fee != expFee { if fee1 != expectedFee {
t.Fatalf("expected fee to be %d sat, was %d", t.Fatalf("expected fee of %d sats, got %d sats",
fee, expFee) expectedFee, fee1)
}
fee2 := feePerKw.FeeForWeight(weight)
if fee2 != expectedFee {
t.Fatalf("expected fee of %d sats, got %d sats",
expectedFee, fee2)
} }
} }
} }
@ -73,22 +72,22 @@ func TestFeeRateTypes(t *testing.T) {
func TestStaticFeeEstimator(t *testing.T) { func TestStaticFeeEstimator(t *testing.T) {
t.Parallel() t.Parallel()
const feePerVSize = 100 const feePerKw = lnwallet.FeePerKwFloor
feeEstimator := &lnwallet.StaticFeeEstimator{ feeEstimator := &lnwallet.StaticFeeEstimator{
FeeRate: feePerVSize, FeePerKW: feePerKw,
} }
if err := feeEstimator.Start(); err != nil { if err := feeEstimator.Start(); err != nil {
t.Fatalf("unable to start fee estimator: %v", err) t.Fatalf("unable to start fee estimator: %v", err)
} }
defer feeEstimator.Stop() defer feeEstimator.Stop()
feeRate, err := feeEstimator.EstimateFeePerVSize(6) feeRate, err := feeEstimator.EstimateFeePerKW(6)
if err != nil { if err != nil {
t.Fatalf("unable to get fee rate: %v", err) t.Fatalf("unable to get fee rate: %v", err)
} }
if feeRate != feePerVSize { if feeRate != feePerKw {
t.Fatalf("expected fee rate %v, got %v", feePerVSize, feeRate) t.Fatalf("expected fee rate %v, got %v", feePerKw, feeRate)
} }
} }