Skip to content

Commit

Permalink
If less than 50% of congestion threshold, return min fee rate (#1393)
Browse files Browse the repository at this point in the history
  • Loading branch information
lazynina authored Aug 2, 2024
1 parent 7ad8fff commit d0c6ce9
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 12 deletions.
20 changes: 12 additions & 8 deletions lib/pos_fee_estimator.go
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,9 @@ func (posFeeEstimator *PoSFeeEstimator) estimateTxnFeeGivenTransactionRegister(
// 1. Compute the maximum size of numPastBlocks blocks as maxBlockSize * numPastBlocks, called maxSizeOfNumBlocks.
// 2. Iterate over all transactions in the transaction register in fee time order until the total size of
// the transactions is greater than maxSizeOfNumBlocks and append those transactions to a slice.
// 3. If there are no transactions in the slice after step 2, return the minimum network fee.
// 3. If there are no transactions in the slice after step 2 or if the total size of these transactions is
// less than 50% of the congestion threshold (congestionFactorBasisPoint * maxBlockSize) / (MaxBasisPoints * 2),
// return the minimum network fee.
// 4. Compute the priority fee bucket for the transactions in the slice using the priorityPercentileBasisPoints param
// by calling getPriorityFeeBucketFromTxns on the slice.
// 5. If the resulting priority fee bucket from step 4 is less than the global minimum network fee, return the global
Expand Down Expand Up @@ -675,8 +677,13 @@ func (posFeeEstimator *PoSFeeEstimator) estimateFeeRateNanosPerKBGivenTransactio
}
}
globalMinFeeRate, _ := txnRegister.minimumNetworkFeeNanosPerKB.Uint64()
if len(txns) == 0 {
// If there are no txns in the transaction register, we simply return the minimum network fee.
// Compute the congestion threshold. If our congestion factor is 100% (or 10,000 bps),
// then congestion threshold is simply max block size * numPastBlocks
congestionThreshold := (congestionFactorBasisPoints * maxSizeOfNumBlocks) / MaxBasisPoints
halfCongestionThreshold := congestionThreshold / 2
if len(txns) == 0 || totalTxnsSize < halfCongestionThreshold {
// If there are no txns in the transaction register or the total size of transactions
// is less than 50% of the congestion threshold, we simply return the minimum network fee.
return globalMinFeeRate
}

Expand All @@ -687,11 +694,8 @@ func (posFeeEstimator *PoSFeeEstimator) estimateFeeRateNanosPerKBGivenTransactio
txnRegister.feeBucketGrowthRateBasisPoints,
)

// Compute the congestion threshold. If our congestion factor is 100% (or 10,000 bps),
// then congestion threshold is simply max block size * numPastBlocks
congestionThreshold := (congestionFactorBasisPoints * maxSizeOfNumBlocks) / MaxBasisPoints
// If the total size of the txns in the transaction register is less than the computed congestion threshold,
// we return one bucket lower than the Priority fee.
// If the total size of the txns in the transaction register is less than the computed congestion threshold
// but greater than half the congestion threshold, we return one bucket lower than the Priority fee.
if totalTxnsSize <= congestionThreshold {
// When we're below the congestion threshold, we want to suggest one fee bucket *lower*
// than the Priority fee we got in the previous step. This mechanism allows fees to drop
Expand Down
70 changes: 66 additions & 4 deletions lib/pos_fee_estimator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import (
func TestFeeEstimator(t *testing.T) {
randSource := rand.New(rand.NewSource(2373))
globalParams := _testGetDefaultGlobalParams()

// Set test global params min fee rate to 100
globalParams.MinimumNetworkFeeNanosPerKB = 100
globalParams.MempoolMaxSizeBytes = uint64(1e9)
mempoolBackupIntervalMillis := uint64(30000)

Expand All @@ -34,8 +35,12 @@ func TestFeeEstimator(t *testing.T) {
globalParams.MinimumNetworkFeeNanosPerKB,
big.NewFloat(float64(globalParams.MinimumNetworkFeeNanosPerKB)),
mempool.txnRegister.feeBucketGrowthRateBasisPoints)
// set the feeMin to the second fee bucket.
feeMin := minFeeBucketMax + 1
// set the feeMin to the third fee bucket.
secondMinFeeBucketMin, secondMinFeeBucketMax := computeFeeTimeBucketRangeFromFeeNanosPerKB(
minFeeBucketMax+1,
big.NewFloat(float64(globalParams.MinimumNetworkFeeNanosPerKB)),
mempool.txnRegister.feeBucketGrowthRateBasisPoints)
feeMin := secondMinFeeBucketMax + 1
// Construct a FeeEstimator with no transactions in it. We should get the minimum fee bucket.
// We make some dummy block to get around validations.
posFeeEstimator := &PoSFeeEstimator{}
Expand Down Expand Up @@ -162,6 +167,7 @@ func TestFeeEstimator(t *testing.T) {

// Update the global params
globalParams = _testGetDefaultGlobalParams()
globalParams.MinimumNetworkFeeNanosPerKB = 100
globalParams.MempoolCongestionFactorBasisPoints = congestionFactor
globalParams.MempoolPastBlocksCongestionFactorBasisPoints = congestionFactor
globalParams.MempoolPriorityPercentileBasisPoints = priorityPercentileBasisPoints
Expand Down Expand Up @@ -214,6 +220,7 @@ func TestFeeEstimator(t *testing.T) {

// Update the global params
globalParams = _testGetDefaultGlobalParams()
globalParams.MinimumNetworkFeeNanosPerKB = 100
globalParams.MempoolCongestionFactorBasisPoints = congestionFactor
globalParams.MempoolPastBlocksCongestionFactorBasisPoints = congestionFactor
globalParams.MempoolPriorityPercentileBasisPoints = priorityPercentileBasisPoints
Expand Down Expand Up @@ -266,6 +273,7 @@ func TestFeeEstimator(t *testing.T) {

// Update the global params
globalParams = _testGetDefaultGlobalParams()
globalParams.MinimumNetworkFeeNanosPerKB = 100
globalParams.MempoolCongestionFactorBasisPoints = congestionFactor
globalParams.MempoolPastBlocksCongestionFactorBasisPoints = congestionFactor
globalParams.MempoolPriorityPercentileBasisPoints = priorityPercentileBasisPoints
Expand All @@ -282,7 +290,7 @@ func TestFeeEstimator(t *testing.T) {
{
// Okay now make congestion factor 50% and make the max block size be more than 2x the
// total size of transactions we added. We should get the previous fee bucket, in this
// case this is the minimum fee rate bucket.
// case this is the second minimum fee rate bucket.
congestionFactor := uint64(50 * 100)
priorityPercentileBasisPoints := uint64(10000)
maxBlockSizeMempool := 2 * numBytesMempool
Expand All @@ -292,6 +300,59 @@ func TestFeeEstimator(t *testing.T) {
if maxBlockSizeMempool > maxBlockSizePastBlocks {
maxBlockSizeHybrid = maxBlockSizeMempool
}
estimatedMempoolFeeRate = posFeeEstimator.estimateFeeRateNanosPerKBGivenTransactionRegister(
posFeeEstimator.mempoolTransactionRegister, congestionFactor, priorityPercentileBasisPoints, 1,
maxBlockSizeMempool)
require.Equal(t, secondMinFeeBucketMin, estimatedMempoolFeeRate)
estimatedMempoolFee, err = posFeeEstimator.mempoolFeeEstimate(txn, congestionFactor,
priorityPercentileBasisPoints, maxBlockSizeMempool)
require.NoError(t, err)
validateTxnFee(t, txn, estimatedMempoolFee, estimatedMempoolFeeRate)

// Let's do the same for past blocks estimator
estimatedPastBlocksFeeRate = posFeeEstimator.estimateFeeRateNanosPerKBGivenTransactionRegister(
posFeeEstimator.pastBlocksTransactionRegister, congestionFactor, priorityPercentileBasisPoints, 1,
maxBlockSizePastBlocks)
require.Equal(t, secondMinFeeBucketMin, estimatedPastBlocksFeeRate)
estimatedPastBlocksFee, err = posFeeEstimator.pastBlocksFeeEstimate(txn, congestionFactor,
priorityPercentileBasisPoints, maxBlockSizePastBlocks)
require.NoError(t, err)
validateTxnFee(t, txn, estimatedPastBlocksFee, estimatedPastBlocksFeeRate)

// Both the mempool and next block fee and fee rates should be equal since we have
// everything in the same fee bucket.
require.Equal(t, estimatedMempoolFee, estimatedPastBlocksFee)
require.Equal(t, estimatedMempoolFeeRate, estimatedPastBlocksFeeRate)

// Update the global params
globalParams = _testGetDefaultGlobalParams()
globalParams.MinimumNetworkFeeNanosPerKB = 100
globalParams.MempoolCongestionFactorBasisPoints = congestionFactor
globalParams.MempoolPastBlocksCongestionFactorBasisPoints = congestionFactor
globalParams.MempoolPriorityPercentileBasisPoints = priorityPercentileBasisPoints
globalParams.MempoolPastBlocksPriorityPercentileBasisPoints = priorityPercentileBasisPoints
globalParams.SoftMaxBlockSizeBytesPoS = maxBlockSizeHybrid
require.NoError(t, posFeeEstimator.UpdateGlobalParams(globalParams))

// And the hybrid estimator is just the max, but for completeness, we check it.
estimatedHybridFee, err = posFeeEstimator.EstimateFee(txn, 0)
require.NoError(t, err)
require.Equal(t, estimatedMempoolFee, estimatedHybridFee)
require.Equal(t, estimatedPastBlocksFee, estimatedHybridFee)
}
{
// Okay now make congestion factor 100% and make the max block size be 2x + 2 the
// total size of transactions we added. We should get the global min fee rate if
// we have less than 50% of the congestion threshold.
congestionFactor := uint64(100 * 100)
priorityPercentileBasisPoints := uint64(10000)
maxBlockSizeMempool := 2 * (numBytesMempool + 1)
maxBlockSizePastBlocks := 2 * (numBytesPastBlocks + 1)
// We use the max to determine which to pass to the hybrid estimator.
maxBlockSizeHybrid := maxBlockSizePastBlocks
if maxBlockSizeMempool > maxBlockSizePastBlocks {
maxBlockSizeHybrid = maxBlockSizeMempool
}
estimatedMempoolFeeRate = posFeeEstimator.estimateFeeRateNanosPerKBGivenTransactionRegister(
posFeeEstimator.mempoolTransactionRegister, congestionFactor, priorityPercentileBasisPoints, 1,
maxBlockSizeMempool)
Expand All @@ -318,6 +379,7 @@ func TestFeeEstimator(t *testing.T) {

// Update the global params
globalParams = _testGetDefaultGlobalParams()
globalParams.MinimumNetworkFeeNanosPerKB = 100
globalParams.MempoolCongestionFactorBasisPoints = congestionFactor
globalParams.MempoolPastBlocksCongestionFactorBasisPoints = congestionFactor
globalParams.MempoolPriorityPercentileBasisPoints = priorityPercentileBasisPoints
Expand Down

0 comments on commit d0c6ce9

Please sign in to comment.