diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj index 90f82f345..fd6110a9d 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj +++ b/Builds/VisualStudio2013/RippleD.vcxproj @@ -2767,6 +2767,10 @@ True True + + True + True + True True @@ -2887,6 +2891,8 @@ + + @@ -2963,6 +2969,10 @@ True True + + True + True + True True @@ -2995,6 +3005,10 @@ True True + + True + True + @@ -3007,6 +3021,8 @@ + + Document protoc --cpp_out=..\..\build\proto --proto_path=%(RelativeDir) %(Identity) diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters index 2136ad6e7..e0e7611ba 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters @@ -3486,6 +3486,9 @@ ripple\protocol\impl + + ripple\protocol\impl + ripple\protocol\impl @@ -3582,6 +3585,9 @@ ripple\protocol + + ripple\protocol + ripple\protocol @@ -3690,6 +3696,9 @@ ripple\protocol\tests + + ripple\protocol\tests + ripple\protocol\tests @@ -3714,6 +3723,9 @@ ripple\protocol\tests + + ripple\protocol\tests + ripple\protocol @@ -3732,6 +3744,9 @@ ripple\protocol + + ripple\protocol + ripple\proto diff --git a/src/ripple/protocol/IOUAmount.h b/src/ripple/protocol/IOUAmount.h new file mode 100644 index 000000000..8dca03395 --- /dev/null +++ b/src/ripple/protocol/IOUAmount.h @@ -0,0 +1,142 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_PROTOCOL_IOUAMOUNT_H_INCLUDED +#define RIPPLE_PROTOCOL_IOUAMOUNT_H_INCLUDED + +#include +#include +#include +#include +#include + +using beast::zero; + +namespace ripple { + +/** Floating point representation of amounts with high dynamic range + + Amounts are stored as a normalized signed mantissa and an exponent. The + range of the normalized exponent is [-96,80] and the range of the absolute + value of the normalized mantissa is [1000000000000000, 9999999999999999]. + + Arithmetic operations can throw std::overflow_error during normalization + if the amount exceeds the largest representable amount, but underflows + will silently trunctate to zero. +*/ +class IOUAmount + : private boost::totally_ordered + , private boost::additive +{ +private: + std::int64_t mantissa_; + int exponent_; + + /** Adjusts the mantissa and exponent to the proper range. + + This can throw if the amount cannot be normalized, or is larger than + the largest value that can be represented as an IOU amount. Amounts + that are too small to be represented normalize to 0. + */ + void + normalize (); + +public: + IOUAmount () = default; + IOUAmount (IOUAmount const& other) = default; + IOUAmount&operator= (IOUAmount const& other) = default; + + IOUAmount (beast::Zero) + { + *this = zero; + } + + IOUAmount (std::int64_t mantissa, int exponent) + : mantissa_ (mantissa) + , exponent_ (exponent) + { + normalize (); + } + + IOUAmount& + operator= (beast::Zero) + { + // The -100 is used to allow 0 to sort less than small positive values + // which will have a large negative exponent. + mantissa_ = 0; + exponent_ = -100; + return *this; + } + + IOUAmount& + operator+= (IOUAmount const& other); + + IOUAmount& + operator-= (IOUAmount const& other) + { + *this += -other; + return *this; + } + + IOUAmount + operator- () const + { + return { -mantissa_, exponent_ }; + } + + bool + operator==(IOUAmount const& other) const + { + return exponent_ == other.exponent_ && + mantissa_ == other.mantissa_; + } + + bool + operator<(IOUAmount const& other) const; + + /** Returns true if the amount is not zero */ + explicit + operator bool() const noexcept + { + return mantissa_ != 0; + } + + /** Return the sign of the amount */ + int + signum() const noexcept + { + return (mantissa_ < 0) ? -1 : (mantissa_ ? 1 : 0); + } + + int + exponent() const noexcept + { + return exponent_; + } + + std::int64_t + mantissa() const noexcept + { + return mantissa_; + } +}; + +} + +#endif diff --git a/src/ripple/protocol/XRPAmount.h b/src/ripple/protocol/XRPAmount.h new file mode 100644 index 000000000..44ee4d0a1 --- /dev/null +++ b/src/ripple/protocol/XRPAmount.h @@ -0,0 +1,126 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_PROTOCOL_XRPAMOUNT_H_INCLUDED +#define RIPPLE_PROTOCOL_XRPAMOUNT_H_INCLUDED + +#include +#include +#include +#include +#include + +using beast::zero; + +namespace ripple { + +class XRPAmount + : private boost::totally_ordered + , private boost::additive +{ +private: + std::int64_t value_; + +public: + /** @{ */ + XRPAmount () = default; + XRPAmount (XRPAmount const& other) = default; + XRPAmount& operator= (XRPAmount const& other) = default; + + XRPAmount (beast::Zero) + : value_ (0) + { + } + + XRPAmount& + operator= (beast::Zero) + { + value_ = 0; + return *this; + } + + XRPAmount (std::int64_t value) + : value_ (value) + { + } + + XRPAmount& + operator= (std::int64_t v) + { + value_ = v; + return *this; + } + + XRPAmount& + operator+= (XRPAmount const& other) + { + value_ += other.value_; + return *this; + } + + XRPAmount& + operator-= (XRPAmount const& other) + { + value_ -= other.value_; + return *this; + } + + XRPAmount + operator- () const + { + return { -value_ }; + } + + bool + operator==(XRPAmount const& other) const + { + return value_ == other.value_; + } + + bool + operator<(XRPAmount const& other) const + { + return value_ < other.value_; + } + + /** Returns true if the amount is not zero */ + explicit + operator bool() const noexcept + { + return value_ != 0; + } + + /** Return the sign of the amount */ + int + signum() const noexcept + { + return (value_ < 0) ? -1 : (value_ ? 1 : 0); + } +}; + +/** Returns true if the amount does not exceed the initial XRP in existence. */ +inline +bool isLegalAmount (XRPAmount const& amount) +{ + return amount <= SYSTEM_CURRENCY_START; +} + +} + +#endif diff --git a/src/ripple/protocol/impl/IOUAmount.cpp b/src/ripple/protocol/impl/IOUAmount.cpp new file mode 100644 index 000000000..207549242 --- /dev/null +++ b/src/ripple/protocol/impl/IOUAmount.cpp @@ -0,0 +1,148 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include + +namespace ripple { + +void +IOUAmount::normalize () +{ + /* The range for the exponent when normalized */ + static int const minExponent = -96; + static int const maxExponent = 80; + + /* The range for the mantissa when normalized */ + static std::int64_t const minMantissa = 1000000000000000ull; + static std::int64_t const maxMantissa = 9999999999999999ull; + + if (mantissa_ == 0) + { + *this = zero; + return; + } + + bool const negative = (mantissa_ < 0); + + if (negative) + mantissa_ = -mantissa_; + + while ((mantissa_ < minMantissa) && (exponent_ > minExponent)) + { + mantissa_ *= 10; + --exponent_; + } + + while (mantissa_ > maxMantissa) + { + if (exponent_ >= maxExponent) + throw std::overflow_error ("IOUAmount::normalize"); + + mantissa_ /= 10; + ++exponent_; + } + + if ((exponent_ < minExponent) || (mantissa_ < minMantissa)) + { + *this = zero; + return; + } + + if (exponent_ > maxExponent) + throw std::overflow_error ("value overflow"); + + if (negative) + mantissa_ = -mantissa_; +} + +IOUAmount& +IOUAmount::operator+= (IOUAmount const& other) +{ + if (other == zero) + return *this; + + if (*this == zero) + { + *this = other; + return *this; + } + + auto m = other.mantissa_; + auto e = other.exponent_; + + while (exponent_ < e) + { + mantissa_ /= 10; + ++exponent_; + } + + while (e < exponent_) + { + m /= 10; + ++e; + } + + // This addition cannot overflow an std::int64_t but we may throw from + // normalize if the result isn't representable. + mantissa_ += m; + + if (mantissa_ >= -10 && mantissa_ <= 10) + { + *this = zero; + return *this; + } + + normalize (); + + return *this; +} + +bool +IOUAmount::operator<(IOUAmount const& other) const +{ + // If the two amounts have different signs (zero is treated as positive) + // then the comparison is true iff the left is negative. + bool const lneg = mantissa_ < 0; + bool const rneg = other.mantissa_ < 0; + + if (lneg != rneg) + return lneg; + + // Both have same sign and the left is zero: the right must be + // greater than 0. + if (mantissa_ == 0) + return other.mantissa_ > 0; + + // Both have same sign, the right is zero and the left is non-zero. + if (other.mantissa_ == 0) + return false; + + // Both have the same sign, compare by exponents: + if (exponent_ > other.exponent_) + return lneg; + if (exponent_ < other.exponent_) + return !lneg; + + // If equal exponents, compare mantissas + return mantissa_ < other.mantissa_; +} + +} diff --git a/src/ripple/protocol/impl/STAmount.cpp b/src/ripple/protocol/impl/STAmount.cpp index 4f32250e5..438a99921 100644 --- a/src/ripple/protocol/impl/STAmount.cpp +++ b/src/ripple/protocol/impl/STAmount.cpp @@ -18,6 +18,8 @@ //============================================================================== #include + +#include #include #include #include @@ -621,7 +623,9 @@ void STAmount::canonicalize () if ((mOffset < cMinOffset) || (mValue < cMinValue)) { mValue = 0; - mOffset = 0; + mIsNegative = false; + mOffset = -100; + return; } if (mOffset > cMaxOffset) @@ -913,7 +917,6 @@ operator- (STAmount const& value) // Arithmetic // //------------------------------------------------------------------------------ - STAmount divide (STAmount const& num, STAmount const& den, Issue const& issue) { diff --git a/src/ripple/protocol/tests/IOUAmount.test.cpp b/src/ripple/protocol/tests/IOUAmount.test.cpp new file mode 100644 index 000000000..45e110951 --- /dev/null +++ b/src/ripple/protocol/tests/IOUAmount.test.cpp @@ -0,0 +1,161 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include + +namespace ripple { + +class IOUAmount_test : public beast::unit_test::suite +{ +public: + void testZero () + { + testcase ("zero"); + + IOUAmount const z (0, 0); + + expect (z.mantissa () == 0); + expect (z.exponent () == -100); + expect (!z); + expect (z.signum () == 0); + expect (z == zero); + + expect ((z + z) == z); + expect ((z - z) == z); + expect (z == -z); + + IOUAmount const zz (zero); + expect (z == zz); + } + + void testSigNum () + { + testcase ("signum"); + + IOUAmount const neg (-1, 0); + expect (neg.signum () < 0); + + IOUAmount const zer (0, 0); + expect (zer.signum () == 0); + + IOUAmount const pos (1, 0); + expect (pos.signum () > 0); + } + + void testBeastZero () + { + testcase ("beast::Zero Comparisons"); + + { + IOUAmount z (zero); + expect (z == zero); + expect (z >= zero); + expect (z <= zero); + unexpected (z != zero); + unexpected (z > zero); + unexpected (z < zero); + } + + { + IOUAmount const neg (-2, 0); + expect (neg < zero); + expect (neg <= zero); + expect (neg != zero); + unexpected (neg == zero); + } + + { + IOUAmount const pos (2, 0); + expect (pos > zero); + expect (pos >= zero); + expect (pos != zero); + unexpected (pos == zero); + } + } + + void testComparisons () + { + testcase ("IOU Comparisons"); + + IOUAmount const n (-2, 0); + IOUAmount const z (0, 0); + IOUAmount const p (2, 0); + + expect (z == z); + expect (z >= z); + expect (z <= z); + expect (z == -z); + unexpected (z > z); + unexpected (z < z); + unexpected (z != z); + unexpected (z != -z); + + expect (n < z); + expect (n <= z); + expect (n != z); + unexpected (n > z); + unexpected (n >= z); + unexpected (n == z); + + expect (p > z); + expect (p >= z); + expect (p != z); + unexpected (p < z); + unexpected (p <= z); + unexpected (p == z); + + expect (n < p); + expect (n <= p); + expect (n != p); + unexpected (n > p); + unexpected (n >= p); + unexpected (n == p); + + expect (p > n); + expect (p >= n); + expect (p != n); + unexpected (p < n); + unexpected (p <= n); + unexpected (p == n); + + expect (p > -p); + expect (p >= -p); + expect (p != -p); + + expect (n < -n); + expect (n <= -n); + expect (n != -n); + } + + //-------------------------------------------------------------------------- + + void run () + { + testZero (); + testSigNum (); + testBeastZero (); + testComparisons (); + } +}; + +BEAST_DEFINE_TESTSUITE(IOUAmount,protocol,ripple); + +} // ripple diff --git a/src/ripple/protocol/tests/XRPAmount.test.cpp b/src/ripple/protocol/tests/XRPAmount.test.cpp new file mode 100644 index 000000000..cb7d0e8be --- /dev/null +++ b/src/ripple/protocol/tests/XRPAmount.test.cpp @@ -0,0 +1,125 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include + +namespace ripple { + +class XRPAmount_test : public beast::unit_test::suite +{ +public: + void testSigNum () + { + testcase ("signum"); + + for (auto i : { -1, 0, 1}) + { + XRPAmount const x(i); + + if (i < 0) + expect (x.signum () < 0); + else if (i > 0) + expect (x.signum () > 0); + else + expect (x.signum () == 0); + } + } + + void testBeastZero () + { + testcase ("beast::Zero Comparisons"); + + for (auto i : { -1, 0, 1}) + { + XRPAmount const x (i); + + expect ((i == 0) == (x == zero)); + expect ((i != 0) == (x != zero)); + expect ((i < 0) == (x < zero)); + expect ((i > 0) == (x > zero)); + expect ((i <= 0) == (x <= zero)); + expect ((i >= 0) == (x >= zero)); + + expect ((0 == i) == (zero == x)); + expect ((0 != i) == (zero != x)); + expect ((0 < i) == (zero < x)); + expect ((0 > i) == (zero > x)); + expect ((0 <= i) == (zero <= x)); + expect ((0 >= i) == (zero >= x)); + } + } + + void testComparisons () + { + testcase ("XRP Comparisons"); + + for (auto i : { -1, 0, 1}) + { + XRPAmount const x (i); + + for (auto j : { -1, 0, 1}) + { + XRPAmount const y (j); + + expect ((i == j) == (x == y)); + expect ((i != j) == (x != y)); + expect ((i < j) == (x < y)); + expect ((i > j) == (x > y)); + expect ((i <= j) == (x <= y)); + expect ((i >= j) == (x >= y)); + } + } + } + + void testAddSub () + { + testcase ("Addition & Subtraction"); + + for (auto i : { -1, 0, 1}) + { + XRPAmount const x (i); + + for (auto j : { -1, 0, 1}) + { + XRPAmount const y (j); + + expect (XRPAmount(i + j) == (x + y)); + expect (XRPAmount(i - j) == (x - y)); + + expect ((x + y) == (y + x)); // addition is commutative + } + } + } + + //-------------------------------------------------------------------------- + + void run () + { + testSigNum (); + testBeastZero (); + testComparisons (); + testAddSub (); + } +}; + +BEAST_DEFINE_TESTSUITE(XRPAmount,protocol,ripple); + +} // ripple diff --git a/src/ripple/unity/protocol.cpp b/src/ripple/unity/protocol.cpp index 7aba59f52..254a4dab0 100644 --- a/src/ripple/unity/protocol.cpp +++ b/src/ripple/unity/protocol.cpp @@ -56,10 +56,12 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -68,6 +70,7 @@ #include #include #include +#include #if DOXYGEN #include