mirror of
https://github.com/mempool/mempool.git
synced 2026-08-13 12:33:11 +02:00
fix: resolve bug in fee rate range and filtering logic
This commit is contained in:
parent
11475e30e2
commit
f4cf989a66
2 changed files with 40 additions and 4 deletions
|
|
@ -37,4 +37,27 @@ describe('Common', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Effective Fee Statistics', () => {
|
||||
test('returns safe defaults for blocks with only coinbase', () => {
|
||||
const coinbaseTx = { weight: 1000, fee: 0, txid: 'coinbase0' };
|
||||
const result = Common.calcEffectiveFeeStatistics([coinbaseTx]);
|
||||
|
||||
expect(result.medianFee).toBe(0);
|
||||
expect(result.feeRange).toEqual([0, 0, 0, 0, 0, 0, 0]);
|
||||
});
|
||||
|
||||
test('excludes coinbase from fee stats when multiple txs', () => {
|
||||
const coinbaseTx = { weight: 1000, fee: 0, txid: 'coinbase0' };
|
||||
const tx1 = { weight: 400, fee: 100, txid: 'tx1' }; // vsize 100, rate 1 sat/vB
|
||||
const tx2 = { weight: 400, fee: 250, txid: 'tx2' }; // vsize 100, rate 2.5 sat/vB
|
||||
|
||||
const result = Common.calcEffectiveFeeStatistics([coinbaseTx, tx1, tx2]);
|
||||
|
||||
// Verify that coinbase (fee 0) was excluded from stats
|
||||
// Fee range min/max should be > 0 (not affected by coinbase's 0 fee)
|
||||
expect(result.feeRange[0]).toBeGreaterThan(0); // min fee
|
||||
expect(result.feeRange[6]).toBeGreaterThan(0); // max fee
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1030,8 +1030,18 @@ export class Common {
|
|||
}
|
||||
|
||||
static calcEffectiveFeeStatistics(transactions: { weight: number, fee?: number, effectiveFeePerVsize?: number, txid: string, acceleration?: boolean }[]): EffectiveFeeStats {
|
||||
const sortedTxs = transactions.map(tx => { return { txid: tx.txid, weight: tx.weight, rate: tx.effectiveFeePerVsize || ((tx.fee || 0) / (tx.weight / 4)) }; }).sort((a, b) => a.rate - b.rate);
|
||||
const totalWeight = transactions.reduce((acc, tx) => acc + tx.weight, 0);
|
||||
// return early with safe default values
|
||||
if (transactions.length <= 1) {
|
||||
return {
|
||||
medianFee: 0,
|
||||
feeRange: [0, 0, 0, 0, 0, 0, 0],
|
||||
};
|
||||
}
|
||||
// assume the first transaction is a coinbase if the fee is falsy (0 or undefined)
|
||||
const nonCoinbaseTransactions = transactions[0].fee ? transactions : transactions.slice(1);
|
||||
|
||||
const sortedTxs = nonCoinbaseTransactions.map(tx => { return { txid: tx.txid, weight: tx.weight, rate: tx.effectiveFeePerVsize || ((tx.fee || 0) / (tx.weight / 4)) }; }).sort((a, b) => a.rate - b.rate);
|
||||
const totalWeight = nonCoinbaseTransactions.reduce((acc, tx) => acc + tx.weight, 0);
|
||||
|
||||
// include any unused space
|
||||
let weightCount = config.MEMPOOL.BLOCK_WEIGHT_UNITS - totalWeight;
|
||||
|
|
@ -1060,7 +1070,7 @@ export class Common {
|
|||
// b) the minimum effective fee rate in the last 2% of transactions (in block order)
|
||||
const minFee = Math.min(
|
||||
Common.getNthPercentile(1, sortedTxs).rate,
|
||||
transactions.slice(-transactions.length / 50).reduce((min, tx) => { return Math.min(min, tx.effectiveFeePerVsize || ((tx.fee || 0) / (tx.weight / 4))); }, Infinity)
|
||||
nonCoinbaseTransactions.slice(Math.ceil(nonCoinbaseTransactions.length * 49 / 50)).reduce((min, tx) => { return Math.min(min, tx.effectiveFeePerVsize || ((tx.fee || 0) / (tx.weight / 4))); }, Infinity)
|
||||
);
|
||||
|
||||
// maximum effective fee heuristic:
|
||||
|
|
@ -1069,7 +1079,7 @@ export class Common {
|
|||
// b) the maximum effective fee rate in the first 2% of transactions (in block order)
|
||||
const maxFee = Math.max(
|
||||
Common.getNthPercentile(99, sortedTxs).rate,
|
||||
transactions.slice(0, transactions.length / 50).reduce((max, tx) => { return Math.max(max, tx.effectiveFeePerVsize || ((tx.fee || 0) / (tx.weight / 4))); }, 0)
|
||||
nonCoinbaseTransactions.slice(0, nonCoinbaseTransactions.length / 50).reduce((max, tx) => { return Math.max(max, tx.effectiveFeePerVsize || ((tx.fee || 0) / (tx.weight / 4))); }, 0)
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
@ -1083,6 +1093,9 @@ export class Common {
|
|||
}
|
||||
|
||||
static getNthPercentile(n: number, sortedDistribution: any[]): any {
|
||||
if (sortedDistribution.length === 0) {
|
||||
return { rate: 0 };
|
||||
}
|
||||
return sortedDistribution[Math.floor((sortedDistribution.length - 1) * (n / 100))];
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue