lnwallet: add feerate types, make estimator return sat/vbyte

This commit is contained in:
Johan T. Halseth committed 2018-02-26 22:42:24 +01:00
1 parent 158c78da60
commit 68557e43c1
1 file changed
+63 -92
+63 -92
View File
@@ -8,19 +8,38 @@ import (
"github.com/roasbeef/btcutil" "github.com/roasbeef/btcutil"
) )
// SatPerVByte represents a fee rate in satoshis per vbyte.
type SatPerVByte btcutil.Amount
// FeeForVSize calculates the fee resulting from this fee rate and
// the given vsize in vbytes.
func (s SatPerVByte) FeeForVSize(vbytes int64) btcutil.Amount {
return btcutil.Amount(s) * btcutil.Amount(vbytes)
}
// FeePerKWeight converts the fee rate into SatPerKWeight.
func (s SatPerVByte) FeePerKWeight() SatPerKWeight {
return SatPerKWeight(s * 1000 / blockchain.WitnessScaleFactor)
}
// SatPerKWeight represents a fee rate in satoshis per kilo weight unit.
type SatPerKWeight btcutil.Amount
// FeeForWeight calculates the fee resulting from this fee rate and the
// given weight in weight units (wu).
func (s SatPerKWeight) FeeForWeight(wu int64) btcutil.Amount {
// The resulting fee is rounded down, as specified in BOLT#03.
return btcutil.Amount(s) * btcutil.Amount(wu) / 1000
}
// 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 {
// EstimateFeePerByte takes in a target for the number of blocks until // EstimateFeePerVSize takes in a target for the number of blocks until
// an initial confirmation and returns the estimated fee expressed in // an initial confirmation and returns the estimated fee expressed in
// satoshis/byte. // satoshis/vbyte.
EstimateFeePerByte(numBlocks uint32) (btcutil.Amount, error) EstimateFeePerVSize(numBlocks uint32) (SatPerVByte, error)
// EstimateFeePerWeight takes in a target for the number of blocks
// until an initial confirmation and returns the estimated fee
// expressed in satoshis/weight.
EstimateFeePerWeight(numBlocks uint32) (btcutil.Amount, 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.
@@ -35,26 +54,18 @@ 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-byte that will be // FeeRate is the static fee rate in satoshis-per-vbyte that will be
// returned by this fee estimator. Queries for the fee rate in weight // returned by this fee estimator.
// units will be scaled accordingly. FeeRate SatPerVByte
FeeRate btcutil.Amount
} }
// EstimateFeePerByte will return a static value for fee calculations. // EstimateFeePerVSize 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) EstimateFeePerByte(numBlocks uint32) (btcutil.Amount, error) { func (e StaticFeeEstimator) EstimateFeePerVSize(numBlocks uint32) (SatPerVByte, error) {
return e.FeeRate, nil return e.FeeRate, nil
} }
// EstimateFeePerWeight will return a static value for fee calculations.
//
// NOTE: This method is part of the FeeEstimator interface.
func (e StaticFeeEstimator) EstimateFeePerWeight(numBlocks uint32) (btcutil.Amount, error) {
return e.FeeRate / blockchain.WitnessScaleFactor, nil
}
// 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.
// //
@@ -79,10 +90,10 @@ 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 byte that // fallBackFeeRate is the fall back fee rate in satoshis per vbyte that
// is returned if the fee estimator does not yet have enough data to // is returned if the fee estimator does not yet have enough data to
// actually produce fee estimates. // actually produce fee estimates.
fallBackFeeRate btcutil.Amount fallBackFeeRate SatPerVByte
btcdConn *rpcclient.Client btcdConn *rpcclient.Client
} }
@@ -93,7 +104,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 btcutil.Amount) (*BtcdFeeEstimator, error) { fallBackFeeRate SatPerVByte) (*BtcdFeeEstimator, error) {
rpcConfig.DisableConnectOnNew = true rpcConfig.DisableConnectOnNew = true
rpcConfig.DisableAutoReconnect = false rpcConfig.DisableAutoReconnect = false
@@ -130,11 +141,13 @@ func (b *BtcdFeeEstimator) Stop() error {
return nil return nil
} }
// EstimateFeePerByte takes in a target for the number of blocks until an // EstimateFeePerVSize takes in a target for the number of blocks until an
// initial confirmation and returns the estimated fee expressed in // initial confirmation and returns the estimated fee expressed in
// satoshis/byte. // satoshis/vbyte.
func (b *BtcdFeeEstimator) EstimateFeePerByte(numBlocks uint32) (btcutil.Amount, error) { //
feeEstimate, err := b.fetchEstimatePerByte(numBlocks) // NOTE: This method is part of the FeeEstimator interface.
func (b *BtcdFeeEstimator) EstimateFeePerVSize(numBlocks uint32) (SatPerVByte, error) {
feeEstimate, err := b.fetchEstimatePerVSize(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
@@ -150,31 +163,10 @@ func (b *BtcdFeeEstimator) EstimateFeePerByte(numBlocks uint32) (btcutil.Amount,
return feeEstimate, nil return feeEstimate, nil
} }
// EstimateFeePerWeight takes in a target for the number of blocks until an
// initial confirmation and returns the estimated fee expressed in
// satoshis/weight.
func (b *BtcdFeeEstimator) EstimateFeePerWeight(numBlocks uint32) (btcutil.Amount, error) {
feePerByte, err := b.EstimateFeePerByte(numBlocks)
if err != nil {
return 0, err
}
// We'll scale down the fee per byte to fee per weight, as for each raw
// byte, there's 1/4 unit of weight mapped to it.
satWeight := feePerByte / blockchain.WitnessScaleFactor
// If this ends up scaling down to a zero sat/weight amount, then we'll
// use the default fallback fee rate.
if satWeight == 0 {
return b.fallBackFeeRate / blockchain.WitnessScaleFactor, nil
}
return satWeight, nil
}
// fetchEstimate returns a fee estimate for a transaction be be confirmed in // fetchEstimate returns a fee estimate for a transaction be be confirmed in
// confTarget blocks. The estimate is returned in sat/byte. // confTarget blocks. The estimate is returned in sat/vbyte.
func (b *BtcdFeeEstimator) fetchEstimatePerByte(confTarget uint32) (btcutil.Amount, error) { func (b *BtcdFeeEstimator) fetchEstimatePerVSize(
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 {
@@ -189,14 +181,14 @@ func (b *BtcdFeeEstimator) fetchEstimatePerByte(confTarget uint32) (btcutil.Amou
} }
// The value returned is expressed in fees per KB, while we want // The value returned is expressed in fees per KB, while we want
// fee-per-byte, so we'll divide by 1024 to map to satoshis-per-byte // fee-per-byte, so we'll divide by 1000 to map to satoshis-per-byte
// before returning the estimate. // before returning the estimate.
satPerByte := satPerKB / 1024 satPerByte := satPerKB / 1000
walletLog.Debugf("Returning %v sat/byte for conf target of %v", walletLog.Debugf("Returning %v sat/vbyte for conf target of %v",
int64(satPerByte), confTarget) int64(satPerByte), confTarget)
return satPerByte, nil return SatPerVByte(satPerByte), nil
} }
// A compile-time assertion to ensure that BtcdFeeEstimator implements the // A compile-time assertion to ensure that BtcdFeeEstimator implements the
@@ -207,10 +199,10 @@ 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 byte that // fallBackFeeRate is the fall back fee rate in satoshis per vbyte that
// is returned if the fee estimator does not yet have enough data to // is returned if the fee estimator does not yet have enough data to
// actually produce fee estimates. // actually produce fee estimates.
fallBackFeeRate btcutil.Amount fallBackFeeRate SatPerVByte
bitcoindConn *rpcclient.Client bitcoindConn *rpcclient.Client
} }
@@ -221,7 +213,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 btcutil.Amount) (*BitcoindFeeEstimator, error) { fallBackFeeRate SatPerVByte) (*BitcoindFeeEstimator, error) {
rpcConfig.DisableConnectOnNew = true rpcConfig.DisableConnectOnNew = true
rpcConfig.DisableAutoReconnect = false rpcConfig.DisableAutoReconnect = false
@@ -254,11 +246,13 @@ func (b *BitcoindFeeEstimator) Stop() error {
return nil return nil
} }
// EstimateFeePerByte takes in a target for the number of blocks until an // EstimateFeePerVSize takes in a target for the number of blocks until an
// initial confirmation and returns the estimated fee expressed in // initial confirmation and returns the estimated fee expressed in
// satoshis/byte. // satoshis/vbyte.
func (b *BitcoindFeeEstimator) EstimateFeePerByte(numBlocks uint32) (btcutil.Amount, error) { //
feeEstimate, err := b.fetchEstimatePerByte(numBlocks) // NOTE: This method is part of the FeeEstimator interface.
func (b *BitcoindFeeEstimator) EstimateFeePerVSize(numBlocks uint32) (SatPerVByte, error) {
feeEstimate, err := b.fetchEstimatePerVSize(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
@@ -274,33 +268,10 @@ func (b *BitcoindFeeEstimator) EstimateFeePerByte(numBlocks uint32) (btcutil.Amo
return feeEstimate, nil return feeEstimate, nil
} }
// EstimateFeePerWeight takes in a target for the number of blocks until an // fetchEstimatePerVSize returns a fee estimate for a transaction be be confirmed in
// initial confirmation and returns the estimated fee expressed in // confTarget blocks. The estimate is returned in sat/vbyte.
// satoshis/weight. func (b *BitcoindFeeEstimator) fetchEstimatePerVSize(
func (b *BitcoindFeeEstimator) EstimateFeePerWeight(numBlocks uint32) (btcutil.Amount, error) { confTarget uint32) (SatPerVByte, error) {
feePerByte, err := b.EstimateFeePerByte(numBlocks)
if err != nil {
return 0, err
}
// We'll scale down the fee per byte to fee per weight, as for each raw
// byte, there's 1/4 unit of weight mapped to it.
satWeight := feePerByte / blockchain.WitnessScaleFactor
// If this ends up scaling down to a zero sat/weight amount, then we'll
// use the default fallback fee rate.
// TODO(aakselrod): maybe use the per-byte rate if it's non-zero?
// Otherwise, we can return a higher sat/byte than sat/weight.
if satWeight == 0 {
return b.fallBackFeeRate / blockchain.WitnessScaleFactor, nil
}
return satWeight, nil
}
// fetchEstimate returns a fee estimate for a transaction be be confirmed in
// confTarget blocks. The estimate is returned in sat/byte.
func (b *BitcoindFeeEstimator) fetchEstimatePerByte(confTarget uint32) (btcutil.Amount, 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))
@@ -335,10 +306,10 @@ func (b *BitcoindFeeEstimator) fetchEstimatePerByte(confTarget uint32) (btcutil.
// before returning the estimate. // before returning the estimate.
satPerByte := satPerKB / 1000 satPerByte := satPerKB / 1000
walletLog.Debugf("Returning %v sat/byte for conf target of %v", walletLog.Debugf("Returning %v sat/vbyte for conf target of %v",
int64(satPerByte), confTarget) int64(satPerByte), confTarget)
return satPerByte, nil return SatPerVByte(satPerByte), nil
} }
// A compile-time assertion to ensure that BitcoindFeeEstimator implements the // A compile-time assertion to ensure that BitcoindFeeEstimator implements the