diff --git a/src/amount.h b/src/amount.h index 9b669eb91..eb314809b 100644 --- a/src/amount.h +++ b/src/amount.h @@ -1,166 +1,174 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_AMOUNT_H #define BITCOIN_AMOUNT_H #include "serialize.h" #include #include #include -#include struct Amount { private: int64_t amount; public: + Amount() : amount(0) {} Amount(int _camount) : amount(_camount) {} Amount(int64_t _camount) : amount(_camount) {} Amount(const Amount &_camount) : amount(_camount.amount) {} // Disable implicit construction from a floating-point value. Amount(double _camount) = delete; // Allow access to underlying value for non-monetary operations int64_t GetSatoshis() const { return amount; } /* * Implement standard operators */ Amount &operator+=(const Amount a) { amount += a.amount; return *this; } Amount &operator-=(const Amount a) { amount -= a.amount; return *this; } friend bool operator<(const Amount a, const Amount b) { return a.amount < b.amount; } friend bool operator==(const Amount a, const Amount b) { return a.amount == b.amount; } friend bool operator>(const Amount a, const Amount b) { return b.amount < a.amount; } friend bool operator!=(const Amount a, const Amount b) { return !(a.amount == b.amount); } friend bool operator<=(const Amount a, const Amount b) { return !(a.amount > b.amount); } friend bool operator>=(const Amount a, const Amount b) { return !(a.amount < b.amount); } friend Amount operator+(const Amount a, const Amount b) { return Amount(a.amount + b.amount); } friend Amount operator-(const Amount a, const Amount b) { return Amount(a.amount - b.amount); } // Implemented for allowing COIN as a base unit. - template ::value), - T>::type = 0> - friend Amount operator*(const T a, const Amount b) { + friend Amount operator*(const int64_t a, const Amount b) { return Amount(a * b.amount); } + friend Amount operator*(const int a, const Amount b) { + return Amount(a * b.amount); + } + // DO NOT IMPLEMENT + friend Amount operator*(const double a, const Amount b) = delete; + int64_t operator/(const Amount b) const { return amount / b.amount; } + Amount operator/(const int64_t b) const { return Amount(amount / b); } + Amount operator/(const int b) const { return Amount(amount / b); } + // DO NOT IMPLEMENT + Amount operator/(const double b) const = delete; // ostream support friend std::ostream &operator<<(std::ostream &stream, const Amount &ca) { return stream << ca.amount; } std::string ToString() const; // serialization support ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { READWRITE(amount); } }; /** Amount in satoshis (Can be negative) */ typedef int64_t CAmount; static const Amount COIN = 100000000; static const Amount CENT = 1000000; extern const std::string CURRENCY_UNIT; /** * No amount larger than this (in satoshi) is valid. * * Note that this constant is *not* the total money supply, which in Bitcoin * currently happens to be less than 21,000,000 BCC for various reasons, but * rather a sanity check. As this sanity check is used by consensus-critical * validation code, the exact value of the MAX_MONEY constant is consensus * critical; in unusual circumstances like a(nother) overflow bug that allowed * for the creation of coins out of thin air modification could lead to a fork. */ static const Amount MAX_MONEY = 21000000 * COIN; inline bool MoneyRange(const CAmount &nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } /** * Fee rate in satoshis per kilobyte: CAmount / kB */ class CFeeRate { private: // unit is satoshis-per-1,000-bytes CAmount nSatoshisPerK; public: /** Fee rate of 0 satoshis per kB */ CFeeRate() : nSatoshisPerK(0) {} explicit CFeeRate(const CAmount &_nSatoshisPerK) : nSatoshisPerK(_nSatoshisPerK) {} /** * 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; } /** * Return the fee in satoshis for the given size in bytes. */ CAmount GetFee(size_t nBytes) const; /** * Return the fee in satoshis for a size of 1000 bytes */ CAmount GetFeePerK() const { return GetFee(1000); } friend bool operator<(const CFeeRate &a, const CFeeRate &b) { return a.nSatoshisPerK < b.nSatoshisPerK; } friend bool operator>(const CFeeRate &a, const CFeeRate &b) { return a.nSatoshisPerK > b.nSatoshisPerK; } friend bool operator==(const CFeeRate &a, const CFeeRate &b) { return a.nSatoshisPerK == b.nSatoshisPerK; } friend bool operator<=(const CFeeRate &a, const CFeeRate &b) { return a.nSatoshisPerK <= b.nSatoshisPerK; } friend bool operator>=(const CFeeRate &a, const CFeeRate &b) { return a.nSatoshisPerK >= b.nSatoshisPerK; } CFeeRate &operator+=(const CFeeRate &a) { nSatoshisPerK += a.nSatoshisPerK; return *this; } std::string ToString() const; ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { READWRITE(nSatoshisPerK); } }; #endif // BITCOIN_AMOUNT_H diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp index 690fe7a48..46df6e0a0 100644 --- a/src/test/amount_tests.cpp +++ b/src/test/amount_tests.cpp @@ -1,147 +1,128 @@ // Copyright (c) 2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "amount.h" #include "test/test_bitcoin.h" #include -// Snippit to allow compile time introspection for multiplication operator -// Used to ensure that certain operations are not implemented on Amount -namespace Check { -class No { -public: - bool b[2]; -}; -template No operator*(const T &, const Arg &); - -bool Check(...); -No &Check(const No &); - -template struct MultiplicationExists { - enum { value = (sizeof(Check(*(T *)(0) * *(Arg *)(0))) != sizeof(No)) }; -}; -} // namespace Check - BOOST_FIXTURE_TEST_SUITE(amount_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(CAmountTests) { BOOST_CHECK(Amount(2) <= Amount(2)); BOOST_CHECK(Amount(2) <= Amount(3)); BOOST_CHECK(Amount(2) >= Amount(2)); BOOST_CHECK(Amount(3) >= Amount(2)); BOOST_CHECK(Amount(1) < Amount(2)); BOOST_CHECK(Amount(-1) < Amount(0)); BOOST_CHECK(Amount(2) > Amount(1)); BOOST_CHECK(Amount(0) > Amount(-1)); BOOST_CHECK(Amount(1) < Amount(2)); BOOST_CHECK(Amount(-1) < Amount(0)); BOOST_CHECK(Amount(2) > Amount(1)); BOOST_CHECK(Amount(0) > Amount(-1)); BOOST_CHECK(Amount(0) == Amount(0)); BOOST_CHECK(Amount(0) != Amount(1)); Amount amount(0); BOOST_CHECK_EQUAL(amount += Amount(1), Amount(1)); BOOST_CHECK_EQUAL(amount += Amount(-1), Amount(0)); BOOST_CHECK_EQUAL(amount -= Amount(1), Amount(-1)); BOOST_CHECK_EQUAL(amount -= Amount(-1), Amount(0)); BOOST_CHECK_EQUAL(COIN + COIN, Amount(2 * COIN)); BOOST_CHECK_EQUAL(2 * COIN + COIN, Amount(3 * COIN)); BOOST_CHECK_EQUAL(-1 * COIN + COIN, Amount(0)); BOOST_CHECK_EQUAL(COIN - COIN, Amount(0)); BOOST_CHECK_EQUAL(COIN - 2 * COIN, -1 * COIN); BOOST_CHECK_EQUAL(10 * Amount(10), Amount(100)); BOOST_CHECK_EQUAL(-1 * Amount(1), Amount(-1)); - // The C preprocessor will now allow passing a template type into a macro - // directly, so we must define these aliases. - using impl_int = Check::MultiplicationExists; - using impl_float = Check::MultiplicationExists; - BOOST_CHECK(impl_int::value); - BOOST_CHECK(!impl_float::value); + BOOST_CHECK_EQUAL(Amount(10) / 3, Amount(3)); + BOOST_CHECK_EQUAL(10 * COIN / COIN, 10.0); + BOOST_CHECK_EQUAL(Amount(10) / -3, Amount(-3)); + BOOST_CHECK_EQUAL(-10 * COIN / (-1 * COIN), 10.0); BOOST_CHECK_EQUAL(Amount(100).GetSatoshis() / 10, 10); // This should probably be Banker's rounding, // but that's a large change BOOST_CHECK_EQUAL(Amount(100).GetSatoshis() / 3, 33); BOOST_CHECK_EQUAL(Amount(101).GetSatoshis() / 3, 33); // Modulus BOOST_CHECK_EQUAL(COIN.GetSatoshis() % 1, Amount(0)); BOOST_CHECK_EQUAL((3 * COIN.GetSatoshis()) % (2 * COIN.GetSatoshis()), COIN); } BOOST_AUTO_TEST_CASE(GetFeeTest) { CFeeRate feeRate; feeRate = CFeeRate(0); // Must always return 0 BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0); BOOST_CHECK_EQUAL(feeRate.GetFee(1e5), 0); feeRate = CFeeRate(1000); // Must always just return the 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(-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); // 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); BOOST_CHECK_EQUAL(feeRate.GetFee(121), 14); BOOST_CHECK_EQUAL(feeRate.GetFee(122), 15); BOOST_CHECK_EQUAL(feeRate.GetFee(999), 122); BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), 123); 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.GetSatoshis(), std::numeric_limits::max() >> 1) .GetFeePerK(); } BOOST_AUTO_TEST_SUITE_END()