Skip to content

Commit 1789e46

Browse files
committed
Force explicit double -> int conversion for CFeeRate constructor
This resolves an issue where estimatesmartfee would return 999 sat/byte instead of 1000, due to floating point loss of precision Thanks to sipa for suggesting is_integral.
1 parent 53a6590 commit 1789e46

File tree

4 files changed

+13
-8
lines changed

4 files changed

+13
-8
lines changed

src/policy/feerate.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,15 @@ class CFeeRate
2020
{
2121
private:
2222
CAmount nSatoshisPerK; // unit is satoshis-per-1,000-bytes
23+
2324
public:
2425
/** Fee rate of 0 satoshis per kB */
2526
CFeeRate() : nSatoshisPerK(0) { }
26-
explicit CFeeRate(const CAmount& _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { }
27+
template<typename I>
28+
CFeeRate(const I _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) {
29+
// We've previously had bugs creep in from silent double->int conversion...
30+
static_assert(std::is_integral<I>::value, "CFeeRate should be used without floats");
31+
}
2732
/** Constructor for a fee rate in satoshis per kB. The size in bytes must not exceed (2^63 - 1)*/
2833
CFeeRate(const CAmount& nFeePaid, size_t nBytes);
2934
/**

src/policy/fees.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,7 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr
714714
if (median < 0)
715715
return CFeeRate(0);
716716

717-
return CFeeRate(median);
717+
return CFeeRate(llround(median));
718718
}
719719

720720
unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon horizon) const
@@ -901,7 +901,7 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation
901901

902902
if (median < 0) return CFeeRate(0); // error condition
903903

904-
return CFeeRate(median);
904+
return CFeeRate(llround(median));
905905
}
906906

907907

src/test/mempool_tests.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -559,15 +559,15 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
559559
// ... we should keep the same min fee until we get a block
560560
pool.removeForBlock(vtx, 1);
561561
SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE);
562-
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/2);
562+
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/2.0));
563563
// ... then feerate should drop 1/2 each halflife
564564

565565
SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2);
566-
BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/4);
566+
BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/4.0));
567567
// ... with a 1/2 halflife when mempool is < 1/2 its target size
568568

569569
SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
570-
BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/8);
570+
BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/8.0));
571571
// ... with a 1/4 halflife when mempool is < 1/4 its target size
572572

573573
SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);

src/txmempool.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,7 @@ const CTxMemPool::setEntries & CTxMemPool::GetMemPoolChildren(txiter entry) cons
981981
CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const {
982982
LOCK(cs);
983983
if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0)
984-
return CFeeRate(rollingMinimumFeeRate);
984+
return CFeeRate(llround(rollingMinimumFeeRate));
985985

986986
int64_t time = GetTime();
987987
if (time > lastRollingFeeUpdate + 10) {
@@ -999,7 +999,7 @@ CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const {
999999
return CFeeRate(0);
10001000
}
10011001
}
1002-
return std::max(CFeeRate(rollingMinimumFeeRate), incrementalRelayFee);
1002+
return std::max(CFeeRate(llround(rollingMinimumFeeRate)), incrementalRelayFee);
10031003
}
10041004

10051005
void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) {

0 commit comments

Comments
 (0)