[lbry] fees: replace estimatefee with esimatesmartfee

This commit is contained in:
Roy Lee 2022-01-29 16:27:52 -08:00
parent d126d0c10e
commit 7513046f70
5 changed files with 97 additions and 70 deletions

View file

@ -100,9 +100,15 @@ type Config struct {
// This can be nil if the address index is not enabled.
AddrIndex *indexers.AddrIndex
// FeeEstimatator provides a feeEstimator. If it is not nil, the mempool
// records all new transactions it observes into the feeEstimator.
FeeEstimator *FeeEstimator
// AddTxToFeeEstimation defines an optional function to be called whenever a
// new transaction is added to the mempool, which can be used to track fees
// for the purposes of smart fee estimation.
AddTxToFeeEstimation func(txHash *chainhash.Hash, fee, size int64)
// RemoveTxFromFeeEstimation defines an optional function to be called
// whenever a transaction is removed from the mempool in order to track fee
// estimation.
RemoveTxFromFeeEstimation func(txHash *chainhash.Hash)
}
// Policy houses the policy (configuration parameters) which is used to
@ -491,6 +497,13 @@ func (mp *TxPool) removeTransaction(tx *btcutil.Tx, removeRedeemers bool) {
delete(mp.outpoints, txIn.PreviousOutPoint)
}
delete(mp.pool, *txHash)
// Inform associated fee estimator that the transaction has been removed
// from the mempool
if mp.cfg.RemoveTxFromFeeEstimation != nil {
mp.cfg.RemoveTxFromFeeEstimation(txHash)
}
atomic.StoreInt64(&mp.lastUpdated, time.Now().Unix())
}
}
@ -559,9 +572,11 @@ func (mp *TxPool) addTransaction(utxoView *blockchain.UtxoViewpoint, tx *btcutil
mp.cfg.AddrIndex.AddUnconfirmedTx(tx, utxoView)
}
// Record this tx for fee estimation if enabled.
if mp.cfg.FeeEstimator != nil {
mp.cfg.FeeEstimator.ObserveTransaction(txD)
// Inform the associated fee estimator that a new transaction has been added
// to the mempool.
size := GetTxVirtualSize(txD.Tx)
if mp.cfg.AddTxToFeeEstimation != nil {
mp.cfg.AddTxToFeeEstimation(txD.Tx.Hash(), txD.Fee, size)
}
return txD

View file

@ -8,6 +8,7 @@ import (
"github.com/lbryio/lbcd/blockchain"
"github.com/lbryio/lbcd/chaincfg"
"github.com/lbryio/lbcd/chaincfg/chainhash"
"github.com/lbryio/lbcd/fees"
"github.com/lbryio/lbcd/mempool"
"github.com/lbryio/lbcd/peer"
"github.com/lbryio/lbcd/wire"
@ -37,5 +38,5 @@ type Config struct {
DisableCheckpoints bool
MaxPeers int
FeeEstimator *mempool.FeeEstimator
FeeEstimator *fees.Estimator
}

View file

@ -16,6 +16,7 @@ import (
"github.com/lbryio/lbcd/chaincfg"
"github.com/lbryio/lbcd/chaincfg/chainhash"
"github.com/lbryio/lbcd/database"
"github.com/lbryio/lbcd/fees"
"github.com/lbryio/lbcd/mempool"
peerpkg "github.com/lbryio/lbcd/peer"
"github.com/lbryio/lbcd/wire"
@ -205,7 +206,7 @@ type SyncManager struct {
nextCheckpoint *chaincfg.Checkpoint
// An optional fee estimator.
feeEstimator *mempool.FeeEstimator
feeEstimator *fees.Estimator
}
// resetHeaderState sets the headers-first mode state to values appropriate for
@ -1414,6 +1415,13 @@ func (sm *SyncManager) handleBlockchainNotification(notification *blockchain.Not
iv := wire.NewInvVect(wire.InvTypeBlock, block.Hash())
sm.peerNotifier.RelayInventory(iv, block.MsgBlock().Header)
if !sm.feeEstimator.IsEnabled() {
// fee estimation can only start after we have performed an initial
// sync, otherwise we'll start adding mempool transactions at the
// wrong height.
sm.feeEstimator.Enable(block.Height())
}
// A block has been connected to the main block chain.
case blockchain.NTBlockConnected:
block, ok := notification.Data.(*btcutil.Block)
@ -1422,6 +1430,12 @@ func (sm *SyncManager) handleBlockchainNotification(notification *blockchain.Not
break
}
// Account for transactions mined in the newly connected block for fee
// estimation. This must be done before attempting to remove
// transactions from the mempool because the mempool will alert the
// estimator of the txs that are leaving
sm.feeEstimator.ProcessBlock(block)
// Remove all of the transactions (except the coinbase) in the
// connected block from the transaction pool. Secondly, remove any
// transactions which are now double spends as a result of these
@ -1438,20 +1452,6 @@ func (sm *SyncManager) handleBlockchainNotification(notification *blockchain.Not
sm.peerNotifier.AnnounceNewTransactions(acceptedTxs)
}
// Register block with the fee estimator, if it exists.
if sm.feeEstimator != nil {
err := sm.feeEstimator.RegisterBlock(block)
// If an error is somehow generated then the fee estimator
// has entered an invalid state. Since it doesn't know how
// to recover, create a new one.
if err != nil {
sm.feeEstimator = mempool.NewFeeEstimator(
mempool.DefaultEstimateFeeMaxRollback,
mempool.DefaultEstimateFeeMinRegisteredBlocks)
}
}
// A block has been disconnected from the main block chain.
case blockchain.NTBlockDisconnected:
block, ok := notification.Data.(*btcutil.Block)
@ -1473,10 +1473,6 @@ func (sm *SyncManager) handleBlockchainNotification(notification *blockchain.Not
}
}
// Rollback previous block recorded by the fee estimator.
if sm.feeEstimator != nil {
sm.feeEstimator.Rollback(block.Hash())
}
}
}

View file

@ -36,6 +36,7 @@ import (
"github.com/lbryio/lbcd/chaincfg"
"github.com/lbryio/lbcd/chaincfg/chainhash"
"github.com/lbryio/lbcd/database"
"github.com/lbryio/lbcd/fees"
"github.com/lbryio/lbcd/mempool"
"github.com/lbryio/lbcd/mining"
"github.com/lbryio/lbcd/mining/cpuminer"
@ -893,7 +894,7 @@ func handleEstimateFee(s *rpcServer, cmd interface{}, closeChan <-chan struct{})
}
}
feeRate, err := s.cfg.FeeEstimator.EstimateFee(uint32(c.NumBlocks))
feeRate, err := s.cfg.FeeEstimator.EstimateFee(int32(c.NumBlocks))
if err != nil {
return nil, &btcjson.RPCError{
@ -906,12 +907,36 @@ func handleEstimateFee(s *rpcServer, cmd interface{}, closeChan <-chan struct{})
return float64(feeRate), nil
}
// handleEstimateSmartFee implements the estimatesmartfee command.
//
// The default estimation mode when unset is assumed as "conservative". As of
// 2018-12, the only supported mode is "conservative".
func handleEstimateSmartFee(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
c := cmd.(*btcjson.EstimateSmartFeeCmd)
rpcsLog.Debugf("EstimateSmartFee is not implemented; falling back to EstimateFee. Requested mode: %s", c.EstimateMode)
mode := btcjson.EstimateModeConservative
if c.EstimateMode != nil {
mode = *c.EstimateMode
}
return handleEstimateFee(s, &btcjson.EstimateFeeCmd{NumBlocks: c.ConfTarget}, closeChan)
if mode != btcjson.EstimateModeConservative {
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCInvalidParameter,
Message: "Only the default and conservative modes " +
"are supported for smart fee estimation at the moment",
}
}
fee, err := s.cfg.FeeEstimator.EstimateFee(int32(c.ConfTarget))
if err != nil {
return nil, internalRPCError(err.Error(), "Could not estimate fee")
}
feeRate := float64(fee) / btcutil.SatoshiPerBitcoin
return &btcjson.EstimateSmartFeeResult{
FeeRate: &feeRate,
Blocks: c.ConfTarget,
}, nil
}
func handleGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
@ -4013,6 +4038,7 @@ type rpcServer struct {
gbtWorkState *gbtWorkState
helpCacher *helpCacher
requestProcessShutdown chan struct{}
feeEstimator *fees.Estimator
quit chan int
}
@ -4846,7 +4872,7 @@ type rpcserverConfig struct {
// The fee estimator keeps track of how long transactions are left in
// the mempool before they are mined into blocks.
FeeEstimator *mempool.FeeEstimator
FeeEstimator *fees.Estimator
// Services represents the services supported by this node.
Services wire.ServiceFlag
@ -4860,6 +4886,7 @@ func newRPCServer(config *rpcserverConfig) (*rpcServer, error) {
gbtWorkState: newGbtWorkState(config.TimeSource),
helpCacher: newHelpCacher(),
requestProcessShutdown: make(chan struct{}),
feeEstimator: config.FeeEstimator,
quit: make(chan int),
}
if cfg.RPCUser != "" && cfg.RPCPass != "" {

View file

@ -14,6 +14,7 @@ import (
"fmt"
"math"
"net"
"path"
"runtime"
"sort"
"strconv"
@ -31,6 +32,7 @@ import (
claimtrieconfig "github.com/lbryio/lbcd/claimtrie/config"
"github.com/lbryio/lbcd/connmgr"
"github.com/lbryio/lbcd/database"
"github.com/lbryio/lbcd/fees"
"github.com/lbryio/lbcd/mempool"
"github.com/lbryio/lbcd/mining"
"github.com/lbryio/lbcd/mining/cpuminer"
@ -38,6 +40,7 @@ import (
"github.com/lbryio/lbcd/peer"
"github.com/lbryio/lbcd/txscript"
"github.com/lbryio/lbcd/wire"
"github.com/lbryio/lbcutil"
btcutil "github.com/lbryio/lbcutil"
"github.com/lbryio/lbcutil/bloom"
)
@ -241,7 +244,7 @@ type server struct {
// The fee estimator keeps track of how long transactions are left in
// the mempool before they are mined into blocks.
feeEstimator *mempool.FeeEstimator
feeEstimator *fees.Estimator
// cfCheckptCaches stores a cached slice of filter headers for cfcheckpt
// messages for each filter type.
@ -2417,13 +2420,7 @@ func (s *server) Stop() error {
s.rpcServer.Stop()
}
// Save fee estimator state in the database.
s.db.Update(func(tx database.Tx) error {
metadata := tx.Metadata()
metadata.Put(mempool.EstimateFeeDatabaseKey, s.feeEstimator.Save())
return nil
})
s.feeEstimator.Close()
// Signal the remaining goroutines to quit.
close(s.quit)
@ -2755,35 +2752,25 @@ func newServer(listenAddrs, agentBlacklist, agentWhitelist []string,
return nil, err
}
// Search for a FeeEstimator state in the database. If none can be found
// or if it cannot be loaded, create a new one.
db.Update(func(tx database.Tx) error {
metadata := tx.Metadata()
feeEstimationData := metadata.Get(mempool.EstimateFeeDatabaseKey)
if feeEstimationData != nil {
// delete it from the database so that we don't try to restore the
// same thing again somehow.
metadata.Delete(mempool.EstimateFeeDatabaseKey)
feC := fees.EstimatorConfig{
MinBucketFee: cfg.minRelayTxFee,
MaxBucketFee: lbcutil.Amount(fees.DefaultMaxBucketFeeMultiplier) * cfg.minRelayTxFee,
MaxConfirms: fees.DefaultMaxConfirmations,
FeeRateStep: fees.DefaultFeeRateStep,
DatabaseFile: path.Join(cfg.DataDir, "feesdb"),
// If there is an error, log it and make a new fee estimator.
var err error
s.feeEstimator, err = mempool.RestoreFeeEstimator(feeEstimationData)
if err != nil {
peerLog.Errorf("Failed to restore fee estimator %v", err)
}
}
return nil
})
// If no feeEstimator has been found, or if the one that has been found
// is behind somehow, create a new one and start over.
if s.feeEstimator == nil || s.feeEstimator.LastKnownHeight() != s.chain.BestSnapshot().Height {
s.feeEstimator = mempool.NewFeeEstimator(
mempool.DefaultEstimateFeeMaxRollback,
mempool.DefaultEstimateFeeMinRegisteredBlocks)
// 1e5 is the previous (up to 1.1.0) mempool.DefaultMinRelayTxFee that
// un-upgraded wallets will be using, so track this particular rate
// explicitly. Note that bumping this value will cause the existing fees
// database to become invalid and will force nodes to explicitly delete
// it.
ExtraBucketFee: 1e5,
}
fe, err := fees.NewEstimator(&feC)
if err != nil {
return nil, err
}
s.feeEstimator = fe
txC := mempool.Config{
Policy: mempool.Policy{
@ -2804,11 +2791,12 @@ func newServer(listenAddrs, agentBlacklist, agentWhitelist []string,
CalcSequenceLock: func(tx *btcutil.Tx, view *blockchain.UtxoViewpoint) (*blockchain.SequenceLock, error) {
return s.chain.CalcSequenceLock(tx, view, true)
},
IsDeploymentActive: s.chain.IsDeploymentActive,
SigCache: s.sigCache,
HashCache: s.hashCache,
AddrIndex: s.addrIndex,
FeeEstimator: s.feeEstimator,
IsDeploymentActive: s.chain.IsDeploymentActive,
SigCache: s.sigCache,
HashCache: s.hashCache,
AddrIndex: s.addrIndex,
AddTxToFeeEstimation: s.feeEstimator.AddMemPoolTransaction,
RemoveTxFromFeeEstimation: s.feeEstimator.RemoveMemPoolTransaction,
}
s.txMemPool = mempool.New(&txC)