github.com/decred/dcrlnd@v0.7.6/lnwallet/chainfee/minfeemanager.go (about)

     1  package chainfee
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  )
     7  
     8  const defaultUpdateInterval = 10 * time.Minute
     9  
    10  // minFeeManager is used to store and update the minimum fee that is required
    11  // by a transaction to be accepted to the mempool. The minFeeManager ensures
    12  // that the backend used to fetch the fee is not queried too regularly.
    13  type minFeeManager struct {
    14  	mu                sync.Mutex
    15  	minFeePerKW       AtomPerKByte
    16  	lastUpdatedTime   time.Time
    17  	minUpdateInterval time.Duration
    18  	fetchFeeFunc      fetchFee
    19  }
    20  
    21  // fetchFee represents a function that can be used to fetch a fee.
    22  type fetchFee func() (AtomPerKByte, error)
    23  
    24  // newMinFeeManager creates a new minFeeManager and uses the
    25  // given fetchMinFee function to set the minFeePerKW of the minFeeManager.
    26  // This function requires the fetchMinFee function to succeed.
    27  func newMinFeeManager(minUpdateInterval time.Duration,
    28  	fetchMinFee fetchFee) (*minFeeManager, error) {
    29  
    30  	minFee, err := fetchMinFee()
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  
    35  	// Ensure that the minimum fee we use is always clamped by our fee
    36  	// floor.
    37  	if minFee < FeePerKBFloor {
    38  		minFee = FeePerKBFloor
    39  	}
    40  
    41  	return &minFeeManager{
    42  		minFeePerKW:       minFee,
    43  		lastUpdatedTime:   time.Now(),
    44  		minUpdateInterval: minUpdateInterval,
    45  		fetchFeeFunc:      fetchMinFee,
    46  	}, nil
    47  }
    48  
    49  // fetchMinFee returns the stored minFeePerKW if it has been updated recently
    50  // or if the call to the chain backend fails. Otherwise, it sets the stored
    51  // minFeePerKW to the fee returned from the backend and floors it based on
    52  // our fee floor.
    53  func (m *minFeeManager) fetchMinFee() AtomPerKByte {
    54  	m.mu.Lock()
    55  	defer m.mu.Unlock()
    56  
    57  	if time.Since(m.lastUpdatedTime) < m.minUpdateInterval {
    58  		return m.minFeePerKW
    59  	}
    60  
    61  	newMinFee, err := m.fetchFeeFunc()
    62  	if err != nil {
    63  		log.Errorf("Unable to fetch updated min fee from chain "+
    64  			"backend. Using last known min fee instead: %v", err)
    65  
    66  		return m.minFeePerKW
    67  	}
    68  
    69  	// By default, we'll use the backend node's minimum fee as the
    70  	// minimum fee rate we'll propose for transactions. However, if this
    71  	// happens to be lower than our fee floor, we'll enforce that instead.
    72  	m.minFeePerKW = newMinFee
    73  	if m.minFeePerKW < FeePerKBFloor {
    74  		m.minFeePerKW = FeePerKBFloor
    75  	}
    76  	m.lastUpdatedTime = time.Now()
    77  
    78  	log.Debugf("Using minimum fee rate of %v sat/kw",
    79  		int64(m.minFeePerKW))
    80  
    81  	return m.minFeePerKW
    82  }