From 94af42da444c6e536413d29cb7b3ad4051fd7112 Mon Sep 17 00:00:00 2001 From: Nik Bougalis Date: Fri, 21 Aug 2015 10:05:18 -0700 Subject: [PATCH] Support STAmount conversions to XRPAmount and IOUAmount --- src/ripple/protocol/STAmount.h | 9 +++ src/ripple/protocol/XRPAmount.h | 45 +++++++++----- src/ripple/protocol/impl/STAmount.cpp | 60 +++++++++++++++++++ src/ripple/protocol/tests/STAmount.test.cpp | 66 +++++++++++++++++++++ 4 files changed, 164 insertions(+), 16 deletions(-) diff --git a/src/ripple/protocol/STAmount.h b/src/ripple/protocol/STAmount.h index 2f22bfcf9..b9d5ad8fb 100644 --- a/src/ripple/protocol/STAmount.h +++ b/src/ripple/protocol/STAmount.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include // namespace ripple { @@ -114,6 +116,10 @@ public: STAmount (Issue const& issue, int mantissa, int exponent = 0); + // Legacy support for new-style amounts + STAmount (IOUAmount const& amount, Issue const& issue); + STAmount (XRPAmount const& amount); + STBase* copy (std::size_t n, void* buf) const override { @@ -271,6 +277,9 @@ public: { return (mValue == 0) && mIsNative; } + + XRPAmount xrp () const; + IOUAmount iou () const; }; //------------------------------------------------------------------------------ diff --git a/src/ripple/protocol/XRPAmount.h b/src/ripple/protocol/XRPAmount.h index 44ee4d0a1..0c04f00ee 100644 --- a/src/ripple/protocol/XRPAmount.h +++ b/src/ripple/protocol/XRPAmount.h @@ -25,6 +25,7 @@ #include #include #include +#include using beast::zero; @@ -35,82 +36,94 @@ class XRPAmount , private boost::additive { private: - std::int64_t value_; + std::int64_t drops_; public: - /** @{ */ XRPAmount () = default; XRPAmount (XRPAmount const& other) = default; XRPAmount& operator= (XRPAmount const& other) = default; XRPAmount (beast::Zero) - : value_ (0) + : drops_ (0) { } XRPAmount& operator= (beast::Zero) { - value_ = 0; + drops_ = 0; return *this; } - XRPAmount (std::int64_t value) - : value_ (value) + template ::value>> + XRPAmount (Integer drops) + : drops_ (static_cast (drops)) { } + template ::value>> XRPAmount& - operator= (std::int64_t v) + operator= (Integer drops) { - value_ = v; + drops_ = static_cast (drops); return *this; } XRPAmount& operator+= (XRPAmount const& other) { - value_ += other.value_; + drops_ += other.drops_; return *this; } XRPAmount& operator-= (XRPAmount const& other) { - value_ -= other.value_; + drops_ -= other.drops_; return *this; } XRPAmount operator- () const { - return { -value_ }; + return { -drops_ }; } bool operator==(XRPAmount const& other) const { - return value_ == other.value_; + return drops_ == other.drops_; } bool operator<(XRPAmount const& other) const { - return value_ < other.value_; + return drops_ < other.drops_; } /** Returns true if the amount is not zero */ explicit operator bool() const noexcept { - return value_ != 0; + return drops_ != 0; } /** Return the sign of the amount */ int signum() const noexcept { - return (value_ < 0) ? -1 : (value_ ? 1 : 0); + return (drops_ < 0) ? -1 : (drops_ ? 1 : 0); + } + + /** Returns the number of drops */ + std::int64_t + drops () const + { + return drops_; } }; @@ -118,7 +131,7 @@ public: inline bool isLegalAmount (XRPAmount const& amount) { - return amount <= SYSTEM_CURRENCY_START; + return amount.drops () <= SYSTEM_CURRENCY_START; } } diff --git a/src/ripple/protocol/impl/STAmount.cpp b/src/ripple/protocol/impl/STAmount.cpp index 3a8ee807d..bee22e448 100644 --- a/src/ripple/protocol/impl/STAmount.cpp +++ b/src/ripple/protocol/impl/STAmount.cpp @@ -248,12 +248,72 @@ STAmount::STAmount (Issue const& issue, { } +// Legacy support for new-style amounts +STAmount::STAmount (IOUAmount const& amount, Issue const& issue) + : mIssue (issue) + , mOffset (amount.exponent ()) + , mIsNative (false) + , mIsNegative (amount < zero) +{ + if (mIsNegative) + mValue = static_cast (-amount.mantissa ()); + else + mValue = static_cast (amount.mantissa ()); + + canonicalize (); +} + +STAmount::STAmount (XRPAmount const& amount) + : mOffset (0) + , mIsNative (true) + , mIsNegative (amount < zero) +{ + if (mIsNegative) + mValue = static_cast (-amount.drops ()); + else + mValue = static_cast (amount.drops ()); + + canonicalize (); +} + std::unique_ptr STAmount::construct (SerialIter& sit, SField const& name) { return std::make_unique(sit, name); } +//------------------------------------------------------------------------------ +// +// Conversion +// +//------------------------------------------------------------------------------ +XRPAmount STAmount::xrp () const +{ + if (!mIsNative) + throw std::logic_error ("Cannot return non-native STAmount as XRPAmount"); + + auto drops = static_cast (mValue); + + if (mIsNegative) + drops = -drops; + + return { drops }; +} + +IOUAmount STAmount::iou () const +{ + if (mIsNative) + throw std::logic_error ("Cannot return native STAmount as IOUAmount"); + + auto mantissa = static_cast (mValue); + auto exponent = mOffset; + + if (mIsNegative) + mantissa = -mantissa; + + return { mantissa, exponent }; +} + //------------------------------------------------------------------------------ // // Operators diff --git a/src/ripple/protocol/tests/STAmount.test.cpp b/src/ripple/protocol/tests/STAmount.test.cpp index 52a2ab4c0..4a12fe480 100644 --- a/src/ripple/protocol/tests/STAmount.test.cpp +++ b/src/ripple/protocol/tests/STAmount.test.cpp @@ -567,6 +567,70 @@ public: #endif } + void + testConvertXRP () + { + testcase ("STAmount to XRPAmount conversions"); + + Issue const usd { Currency (0x5553440000000000), AccountID (0x4985601) }; + Issue const xrp { xrpIssue () }; + + for (std::uint64_t drops = 100000000000000000; drops != 1; drops = drops / 10) + { + auto const t = amountFromString (xrp, std::to_string (drops)); + auto const s = t.xrp (); + expect (s.drops() == drops); + expect (t == STAmount (XRPAmount (drops))); + expect (s == XRPAmount (drops)); + } + + try + { + auto const t = amountFromString (usd, "136500"); + fail (to_string (t.xrp ())); + } + catch (std::logic_error const&) + { + pass (); + } + catch (...) + { + fail ("wrong exception"); + } + } + + void + testConvertIOU () + { + testcase ("STAmount to IOUAmount conversions"); + + Issue const usd { Currency (0x5553440000000000), AccountID (0x4985601) }; + Issue const xrp { xrpIssue () }; + + for (std::uint64_t dollars = 10000000000; dollars != 1; dollars = dollars / 10) + { + auto const t = amountFromString (usd, std::to_string (dollars)); + auto const s = t.iou (); + expect (t == STAmount (s, usd)); + expect (s.mantissa () == t.mantissa ()); + expect (s.exponent () == t.exponent ()); + } + + try + { + auto const t = amountFromString (xrp, "136500"); + fail (to_string (t.iou ())); + } + catch (std::logic_error const&) + { + pass (); + } + catch (...) + { + fail ("wrong exception"); + } + } + //-------------------------------------------------------------------------- void run () @@ -577,6 +641,8 @@ public: testArithmetic (); testUnderflow (); testRounding (); + testConvertXRP (); + testConvertIOU (); } };