diff --git a/lnwallet/fee_estimator.go b/lnwallet/fee_estimator.go index 87c71f105..5c9c6b08f 100644 --- a/lnwallet/fee_estimator.go +++ b/lnwallet/fee_estimator.go @@ -72,3 +72,120 @@ func (e StaticFeeEstimator) Stop() error { // A compile-time assertion to ensure that StaticFeeEstimator implements the // FeeEstimator interface. var _ FeeEstimator = (*StaticFeeEstimator)(nil) + +// BtcdFeeEstimator is an implementation of the FeeEstimator interface backed +// by the RPC interface of an active btcd node. This implementation will proxy +// any fee estimation requests to btcd's RPC interace. +type BtcdFeeEstimator struct { + // fallBackFeeRate is the fall back fee rate in satoshis per byte that + // is returned if the fee estimator does not yet have enough data to + // actually produce fee estimates. + fallBackFeeRate btcutil.Amount + + btcdConn *rpcclient.Client +} + +// NewBtcdFeeEstimator creates a new BtcdFeeEstimator given a fully populated +// rpc config that is able to successfully connect and authenticate with the +// btcd node, and also a fall back fee rate. The fallback fee rate is used in +// the occasion that the estimator has insufficient data, or returns zero for a +// fee estimate. +func NewBtcdFeeEstimator(rpcConfig rpcclient.ConnConfig, + fallBackFeeRate btcutil.Amount) (*BtcdFeeEstimator, error) { + + rpcConfig.DisableConnectOnNew = true + rpcConfig.DisableAutoReconnect = false + chainConn, err := rpcclient.New(&rpcConfig, nil) + if err != nil { + return nil, err + } + + return &BtcdFeeEstimator{ + fallBackFeeRate: fallBackFeeRate, + btcdConn: chainConn, + }, nil +} + +// Start signals the FeeEstimator to start any processes or goroutines +// it needs to perform its duty. +// +// NOTE: This method is part of the FeeEstimator interface. +func (b *BtcdFeeEstimator) Start() error { + if err := b.btcdConn.Connect(20); err != nil { + return err + } + + return nil +} + +// Stop stops any spawned goroutines and cleans up the resources used +// by the fee estimator. +// +// NOTE: This method is part of the FeeEstimator interface. +func (b *BtcdFeeEstimator) Stop() error { + b.btcdConn.Shutdown() + + return nil +} + +// EstimateFeePerByte takes in a target for the number of blocks until an +// initial confirmation and returns the estimated fee expressed in +// satoshis/byte. +func (b *BtcdFeeEstimator) EstimateFeePerByte(numBlocks uint32) (btcutil.Amount, error) { + feeEstimate, err := b.fetchEstimatePerByte(numBlocks) + switch { + // 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 + // fee rate. + case err != nil: + walletLog.Errorf("unable to query estimator: %v", err) + fallthrough + + case feeEstimate == 0: + return b.fallBackFeeRate, 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. + return btcutil.Amount(feePerByte / blockchain.WitnessScaleFactor), nil +} + +// fetchEstimate returns a fee estimate for a transaction be be confirmed in +// confTarget blocks. The estimate is returned in sat/byte. +func (b *BtcdFeeEstimator) fetchEstimatePerByte(confTarget uint32) (btcutil.Amount, error) { + // First, we'll fetch the estimate for our confirmation target. + btcPerKB, err := b.btcdConn.EstimateFee(int64(confTarget)) + if err != nil { + return 0, err + } + + // Next, we'll convert the returned value to satoshis, as it's + // currently returned in BTC. + satPerKB := uint64(btcPerKB * 10e8) + + // 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 + // before returning the estimate. + satPerByte := btcutil.Amount(satPerKB / 1024) + + walletLog.Debugf("Returning %v sat/byte for conf target of %v", + int64(satPerByte), confTarget) + + return satPerByte, nil +} + +// A compile-time assertion to ensure that BtcdFeeEstimator implements the +// FeeEstimator interface. +var _ FeeEstimator = (*BtcdFeeEstimator)(nil)