diff options
author | Matt Corallo <git@bluematt.me> | 2017-09-11 15:47:09 -0400 |
---|---|---|
committer | Matt Corallo <git@bluematt.me> | 2017-09-11 18:07:52 -0400 |
commit | 1789e4675b17f274fcb0761321e6fd249a102f40 (patch) | |
tree | d72c88f1091a160d8cfb6aff41aa13d57b9072ce | |
parent | 53a6590f496b25174c740927243bf8307541b0b9 (diff) |
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.
-rw-r--r-- | src/policy/feerate.h | 7 | ||||
-rw-r--r-- | src/policy/fees.cpp | 4 | ||||
-rw-r--r-- | src/test/mempool_tests.cpp | 6 | ||||
-rw-r--r-- | src/txmempool.cpp | 4 |
4 files changed, 13 insertions, 8 deletions
diff --git a/src/policy/feerate.h b/src/policy/feerate.h index 7e519e3efa..3449cdd699 100644 --- a/src/policy/feerate.h +++ b/src/policy/feerate.h @@ -20,10 +20,15 @@ class CFeeRate { private: CAmount nSatoshisPerK; // unit is satoshis-per-1,000-bytes + public: /** Fee rate of 0 satoshis per kB */ CFeeRate() : nSatoshisPerK(0) { } - explicit CFeeRate(const CAmount& _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { } + template<typename I> + CFeeRate(const I _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { + // We've previously had bugs creep in from silent double->int conversion... + static_assert(std::is_integral<I>::value, "CFeeRate should be used without floats"); + } /** Constructor for a fee rate in satoshis per kB. The size in bytes must not exceed (2^63 - 1)*/ CFeeRate(const CAmount& nFeePaid, size_t nBytes); /** diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index ca774cd74b..8056f385ab 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -714,7 +714,7 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr if (median < 0) return CFeeRate(0); - return CFeeRate(median); + return CFeeRate(llround(median)); } unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon horizon) const @@ -901,7 +901,7 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation if (median < 0) return CFeeRate(0); // error condition - return CFeeRate(median); + return CFeeRate(llround(median)); } diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index 51b28d09fa..116210a297 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -559,15 +559,15 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) // ... we should keep the same min fee until we get a block pool.removeForBlock(vtx, 1); SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE); - BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/2); + BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/2.0)); // ... then feerate should drop 1/2 each halflife SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2); - BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/4); + BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/4.0)); // ... with a 1/2 halflife when mempool is < 1/2 its target size SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); - BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/8); + BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/8.0)); // ... with a 1/4 halflife when mempool is < 1/4 its target size SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index f68d677646..776d3f36ca 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -981,7 +981,7 @@ const CTxMemPool::setEntries & CTxMemPool::GetMemPoolChildren(txiter entry) cons CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const { LOCK(cs); if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0) - return CFeeRate(rollingMinimumFeeRate); + return CFeeRate(llround(rollingMinimumFeeRate)); int64_t time = GetTime(); if (time > lastRollingFeeUpdate + 10) { @@ -999,7 +999,7 @@ CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const { return CFeeRate(0); } } - return std::max(CFeeRate(rollingMinimumFeeRate), incrementalRelayFee); + return std::max(CFeeRate(llround(rollingMinimumFeeRate)), incrementalRelayFee); } void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) { |