mirror of
https://github.com/mempool/mempool.git
synced 2026-08-13 12:33:11 +02:00
Merge pull request #6263 from jramos0/hot-fix
[Bug] Lower fee rate range = 0 #5757
This commit is contained in:
commit
a7864a5421
3 changed files with 43 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 {
|
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);
|
// return early with safe default values
|
||||||
const totalWeight = transactions.reduce((acc, tx) => acc + tx.weight, 0);
|
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
|
// include any unused space
|
||||||
let weightCount = config.MEMPOOL.BLOCK_WEIGHT_UNITS - totalWeight;
|
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)
|
// b) the minimum effective fee rate in the last 2% of transactions (in block order)
|
||||||
const minFee = Math.min(
|
const minFee = Math.min(
|
||||||
Common.getNthPercentile(1, sortedTxs).rate,
|
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:
|
// 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)
|
// b) the maximum effective fee rate in the first 2% of transactions (in block order)
|
||||||
const maxFee = Math.max(
|
const maxFee = Math.max(
|
||||||
Common.getNthPercentile(99, sortedTxs).rate,
|
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 {
|
return {
|
||||||
|
|
@ -1083,6 +1093,9 @@ export class Common {
|
||||||
}
|
}
|
||||||
|
|
||||||
static getNthPercentile(n: number, sortedDistribution: any[]): any {
|
static getNthPercentile(n: number, sortedDistribution: any[]): any {
|
||||||
|
if (sortedDistribution.length === 0) {
|
||||||
|
return { rate: 0 };
|
||||||
|
}
|
||||||
return sortedDistribution[Math.floor((sortedDistribution.length - 1) * (n / 100))];
|
return sortedDistribution[Math.floor((sortedDistribution.length - 1) * (n / 100))];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
3
contributors/jramos0.txt
Normal file
3
contributors/jramos0.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of February 6, 2025.
|
||||||
|
|
||||||
|
Signed: jramos0
|
||||||
Loading…
Add table
Add a link
Reference in a new issue