Merge #7796: [amount] Add support for negative fee rates

facf5a4 [amount] tests: Fix off-by-one mistake (MarcoFalke)
fa2da2c [amount] Add support for negative fee rates (MarcoFalke)
11114a6 [amount] test negative fee rates and full constructor (MarcoFalke)
This commit is contained in:
Wladimir J. van der Laan 2016-04-14 11:40:11 +02:00
commit 536b75e946
No known key found for this signature in database
GPG key ID: 74810B012346C9A6
3 changed files with 51 additions and 9 deletions

View file

@ -9,20 +9,30 @@
const std::string CURRENCY_UNIT = "BTC"; const std::string CURRENCY_UNIT = "BTC";
CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nSize) CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nBytes_)
{ {
assert(nBytes_ <= uint64_t(std::numeric_limits<int64_t>::max()));
int64_t nSize = int64_t(nBytes_);
if (nSize > 0) if (nSize > 0)
nSatoshisPerK = nFeePaid * 1000 / nSize; nSatoshisPerK = nFeePaid * 1000 / nSize;
else else
nSatoshisPerK = 0; nSatoshisPerK = 0;
} }
CAmount CFeeRate::GetFee(size_t nSize) const CAmount CFeeRate::GetFee(size_t nBytes_) const
{ {
assert(nBytes_ <= uint64_t(std::numeric_limits<int64_t>::max()));
int64_t nSize = int64_t(nBytes_);
CAmount nFee = nSatoshisPerK * nSize / 1000; CAmount nFee = nSatoshisPerK * nSize / 1000;
if (nFee == 0 && nSize != 0 && nSatoshisPerK > 0) if (nFee == 0 && nSize != 0) {
if (nSatoshisPerK > 0)
nFee = CAmount(1); nFee = CAmount(1);
if (nSatoshisPerK < 0)
nFee = CAmount(-1);
}
return nFee; return nFee;
} }

View file

@ -11,6 +11,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string> #include <string>
/** Amount in satoshis (Can be negative) */
typedef int64_t CAmount; typedef int64_t CAmount;
static const CAmount COIN = 100000000; static const CAmount COIN = 100000000;
@ -30,22 +31,24 @@ extern const std::string CURRENCY_UNIT;
static const CAmount MAX_MONEY = 21000000 * COIN; static const CAmount MAX_MONEY = 21000000 * COIN;
inline bool MoneyRange(const CAmount& nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } inline bool MoneyRange(const CAmount& nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
/** Type-safe wrapper class for fee rates /**
* (how much to pay based on transaction size) * Fee rate in satoshis per kilobyte: CAmount / kB
*/ */
class CFeeRate class CFeeRate
{ {
private: private:
CAmount nSatoshisPerK; // unit is satoshis-per-1,000-bytes CAmount nSatoshisPerK; // unit is satoshis-per-1,000-bytes
public: public:
/** Fee rate of 0 satoshis per kB */
CFeeRate() : nSatoshisPerK(0) { } CFeeRate() : nSatoshisPerK(0) { }
explicit CFeeRate(const CAmount& _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { } explicit CFeeRate(const CAmount& _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { }
CFeeRate(const CAmount& nFeePaid, size_t nSize); /** 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);
CFeeRate(const CFeeRate& other) { nSatoshisPerK = other.nSatoshisPerK; } CFeeRate(const CFeeRate& other) { nSatoshisPerK = other.nSatoshisPerK; }
/** /**
* Return the fee in satoshis for the given size in bytes. * Return the fee in satoshis for the given size in bytes.
*/ */
CAmount GetFee(size_t size) const; CAmount GetFee(size_t nBytes) const;
/** /**
* Return the fee in satoshis for a size of 1000 bytes * Return the fee in satoshis for a size of 1000 bytes
*/ */

View file

@ -27,6 +27,15 @@ BOOST_AUTO_TEST_CASE(GetFeeTest)
BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), 1e3); BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), 1e3);
BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), 9e3); BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), 9e3);
feeRate = CFeeRate(-1000);
// Must always just return -1 * arg
BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0);
BOOST_CHECK_EQUAL(feeRate.GetFee(1), -1);
BOOST_CHECK_EQUAL(feeRate.GetFee(121), -121);
BOOST_CHECK_EQUAL(feeRate.GetFee(999), -999);
BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), -1e3);
BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), -9e3);
feeRate = CFeeRate(123); feeRate = CFeeRate(123);
// Truncates the result, if not integer // Truncates the result, if not integer
BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0); BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0);
@ -37,6 +46,26 @@ BOOST_AUTO_TEST_CASE(GetFeeTest)
BOOST_CHECK_EQUAL(feeRate.GetFee(999), 122); BOOST_CHECK_EQUAL(feeRate.GetFee(999), 122);
BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), 123); BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), 123);
BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), 1107); BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), 1107);
feeRate = CFeeRate(-123);
// Truncates the result, if not integer
BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0);
BOOST_CHECK_EQUAL(feeRate.GetFee(8), -1); // Special case: returns -1 instead of 0
BOOST_CHECK_EQUAL(feeRate.GetFee(9), -1);
// Check full constructor
// default value
BOOST_CHECK(CFeeRate(CAmount(-1), 1000) == CFeeRate(-1));
BOOST_CHECK(CFeeRate(CAmount(0), 1000) == CFeeRate(0));
BOOST_CHECK(CFeeRate(CAmount(1), 1000) == CFeeRate(1));
// lost precision (can only resolve satoshis per kB)
BOOST_CHECK(CFeeRate(CAmount(1), 1001) == CFeeRate(0));
BOOST_CHECK(CFeeRate(CAmount(2), 1001) == CFeeRate(1));
// some more integer checks
BOOST_CHECK(CFeeRate(CAmount(26), 789) == CFeeRate(32));
BOOST_CHECK(CFeeRate(CAmount(27), 789) == CFeeRate(34));
// Maximum size in bytes, should not crash
CFeeRate(MAX_MONEY, std::numeric_limits<size_t>::max() >> 1).GetFeePerK();
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()