Add IOU and XRP Amounts

This commit is contained in:
seelabs
2015-10-03 14:19:36 -04:00
parent 906ef761ba
commit 3d9589f010
11 changed files with 565 additions and 17 deletions

View File

@@ -2552,6 +2552,8 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\protocol\AccountID.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\protocol\AmountSpec.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\protocol\Book.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\protocol\BuildInfo.h">

View File

@@ -3099,6 +3099,9 @@
<ClInclude Include="..\..\src\ripple\protocol\AccountID.h">
<Filter>ripple\protocol</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\protocol\AmountSpec.h">
<Filter>ripple\protocol</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\protocol\Book.h">
<Filter>ripple\protocol</Filter>
</ClInclude>

View File

@@ -19,6 +19,7 @@
#include <BeastConfig.h>
#include <ripple/ledger/ReadView.h>
#include <ripple/protocol/AmountSpec.h>
#include <ripple/protocol/STAmount.h>
#include <ripple/protocol/Indexes.h>
@@ -48,6 +49,16 @@ creditLimit (
return result;
}
IOUAmount
creditLimit2 (
ReadView const& v,
AccountID const& acc,
AccountID const& iss,
Currency const& cur)
{
return toAmount<IOUAmount> (creditLimit (v, acc, iss, cur));
}
STAmount creditBalance (
ReadView const& view,
AccountID const& account,
@@ -72,4 +83,15 @@ STAmount creditBalance (
return result;
}
IOUAmount
creditBalance2 (
ReadView const& v,
AccountID const& acc,
AccountID const& iss,
Currency const& cur)
{
return toAmount<IOUAmount> (creditBalance (v, acc, iss, cur));
}
} // ripple

View File

@@ -22,6 +22,7 @@
#include <ripple/ledger/View.h>
#include <ripple/protocol/STAmount.h>
#include <ripple/protocol/IOUAmount.h>
namespace ripple {
@@ -38,6 +39,13 @@ STAmount creditLimit (
AccountID const& issuer,
Currency const& currency);
IOUAmount
creditLimit2 (
ReadView const& v,
AccountID const& acc,
AccountID const& iss,
Currency const& cur);
/** Returns the amount of IOUs issued by issuer that are held by an account
@param ledger the ledger to check against.
@param account the account of interest.
@@ -50,6 +58,13 @@ STAmount creditBalance (
AccountID const& issuer,
Currency const& currency);
IOUAmount
creditBalance2 (
ReadView const& v,
AccountID const& acc,
AccountID const& iss,
Currency const& cur);
} // ripple
#endif

View File

@@ -17,6 +17,9 @@
*/
//==============================================================================
#ifndef RIPPLE_LEDGER_TESTS_PATHSET_H_INCLUDED
#define RIPPLE_LEDGER_TESTS_PATHSET_H_INCLUDED
#include <ripple/basics/Log.h>
#include <ripple/protocol/TxFlags.h>
#include <ripple/test/jtx.h>
@@ -134,3 +137,5 @@ private:
} // test
} // ripple
#endif

View File

@@ -0,0 +1,289 @@
//------------------------------------------------------------------------------
/*
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_AMOUNTSPEC_H_INCLUDED
#define RIPPLE_PROTOCOL_AMOUNTSPEC_H_INCLUDED
#include <ripple/protocol/IOUAmount.h>
#include <ripple/protocol/XRPAmount.h>
#include <ripple/protocol/STAmount.h>
namespace ripple {
struct AmountSpec
{
bool native;
union
{
XRPAmount xrp;
IOUAmount iou;
};
boost::optional<AccountID> issuer;
boost::optional<Currency> currency;
private:
friend
std::ostream&
operator << (
std::ostream& stream,
AmountSpec const& amt)
{
if (amt.native)
stream << to_string (amt.xrp);
else
stream << to_string (amt.iou);
if (amt.currency)
stream << "/(" << *amt.currency << ")";
if (amt.issuer)
stream << "/" << *amt.issuer << "";
return stream;
}
};
struct EitherAmount
{
#ifndef NDEBUG
bool native = false;
#endif
union
{
IOUAmount iou;
XRPAmount xrp;
};
EitherAmount () = default;
explicit
EitherAmount (IOUAmount const& a)
:iou(a)
{
}
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
// ignore warning about half of iou amount being uninitialized
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
explicit
EitherAmount (XRPAmount const& a)
:xrp(a)
{
#ifndef NDEBUG
native = true;
#endif
}
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
explicit
EitherAmount (AmountSpec const& a)
{
#ifndef NDEBUG
native = a.native;
#endif
if (a.native)
xrp = a.xrp;
else
iou = a.iou;
}
};
template <class T>
T&
get (EitherAmount& amt)
{
static_assert(sizeof(T) == -1, "Must used specialized function");
return T(0);
}
template <>
inline
IOUAmount&
get<IOUAmount> (EitherAmount& amt)
{
assert (!amt.native);
return amt.iou;
}
template <>
inline
XRPAmount&
get<XRPAmount> (EitherAmount& amt)
{
assert (amt.native);
return amt.xrp;
}
template <class T>
T const&
get (EitherAmount const& amt)
{
static_assert(sizeof(T) == -1, "Must used specialized function");
return T(0);
}
template <>
inline
IOUAmount const&
get<IOUAmount> (EitherAmount const& amt)
{
assert (!amt.native);
return amt.iou;
}
template <>
inline
XRPAmount const&
get<XRPAmount> (EitherAmount const& amt)
{
assert (amt.native);
return amt.xrp;
}
inline
AmountSpec
toAmountSpec (STAmount const& amt)
{
assert (amt.mantissa () < std::numeric_limits<std::int64_t>::max ());
bool const isNeg = amt.negative ();
std::int64_t const sMant =
isNeg ? - std::int64_t (amt.mantissa ()) : amt.mantissa ();
AmountSpec result;
result.native = isXRP (amt);
if (result.native)
{
result.xrp = XRPAmount (sMant);
}
else
{
result.iou = IOUAmount (sMant, amt.exponent ());
result.issuer = amt.issue().account;
result.currency = amt.issue().currency;
}
return result;
}
inline
AmountSpec
toAmountSpec (
EitherAmount const& ea,
boost::optional<Currency> const& c)
{
AmountSpec r;
r.native = (!c || isXRP (*c));
r.currency = c;
assert (ea.native == r.native);
if (r.native)
{
r.xrp = ea.xrp;
}
else
{
r.iou = ea.iou;
}
return r;
}
inline
STAmount
toSTAmount (IOUAmount const& iou, Issue const& iss)
{
bool const isNeg = iou.signum() < 0;
std::uint64_t const umant = isNeg ? - iou.mantissa () : iou.mantissa ();
return STAmount (iss, umant, iou.exponent (), /*native*/ false, isNeg,
STAmount::unchecked ());
}
inline
STAmount
toSTAmount (IOUAmount const& iou)
{
return toSTAmount (iou, noIssue ());
}
inline
STAmount
toSTAmount (XRPAmount const& xrp)
{
bool const isNeg = xrp.signum() < 0;
std::uint64_t const umant = isNeg ? - xrp.drops () : xrp.drops ();
return STAmount (umant, isNeg);
}
inline
STAmount
toSTAmount (XRPAmount const& xrp, Issue const& iss)
{
assert (isXRP(iss.account) && isXRP(iss.currency));
return toSTAmount (xrp);
}
template <class T>
T
toAmount (STAmount const& amt)
{
static_assert(sizeof(T) == -1, "Must used specialized function");
return T(0);
}
template <>
inline
STAmount
toAmount<STAmount> (STAmount const& amt)
{
return amt;
}
template <>
inline
IOUAmount
toAmount<IOUAmount> (STAmount const& amt)
{
assert (amt.mantissa () < std::numeric_limits<std::int64_t>::max ());
bool const isNeg = amt.negative ();
std::int64_t const sMant =
isNeg ? - std::int64_t (amt.mantissa ()) : amt.mantissa ();
assert (! isXRP (amt));
return IOUAmount (sMant, amt.exponent ());
}
template <>
inline
XRPAmount
toAmount<XRPAmount> (STAmount const& amt)
{
assert (amt.mantissa () < std::numeric_limits<std::int64_t>::max ());
bool const isNeg = amt.negative ();
std::int64_t const sMant =
isNeg ? - std::int64_t (amt.mantissa ()) : amt.mantissa ();
AmountSpec result;
assert (isXRP (amt));
return XRPAmount (sMant);
}
}
#endif

View File

@@ -140,6 +140,13 @@ public:
std::string
to_string (IOUAmount const& amount);
IOUAmount
mulRatio (
IOUAmount const& amt,
std::uint32_t num,
std::uint32_t den,
bool roundUp);
}
#endif

View File

@@ -20,7 +20,11 @@
#ifndef RIPPLE_PROTOCOL_QUALITY_H_INCLUDED
#define RIPPLE_PROTOCOL_QUALITY_H_INCLUDED
#include <ripple/protocol/AmountSpec.h>
#include <ripple/protocol/IOUAmount.h>
#include <ripple/protocol/STAmount.h>
#include <ripple/protocol/XRPAmount.h>
#include <cstdint>
#include <ostream>
@@ -35,11 +39,18 @@ namespace ripple {
For offers, "in" is always TakerPays and "out" is
always TakerGets.
*/
struct Amounts
template<class TIn, class TOut>
struct TAmounts
{
Amounts() = default;
TAmounts() = default;
Amounts (STAmount const& in_, STAmount const& out_)
TAmounts (beast::Zero, beast::Zero)
: in (beast::zero)
, out (beast::zero)
{
}
TAmounts (TIn const& in_, TOut const& out_)
: in (in_)
, out (out_)
{
@@ -52,20 +63,46 @@ struct Amounts
return in <= zero || out <= zero;
}
STAmount in;
STAmount out;
TAmounts& operator+=(TAmounts const& rhs)
{
in += rhs.in;
out += rhs.out;
return *this;
}
TAmounts& operator-=(TAmounts const& rhs)
{
in -= rhs.in;
out -= rhs.out;
return *this;
}
TIn in;
TOut out;
};
inline
template<class TIn, class TOut>
TAmounts<TIn, TOut> make_Amounts(TIn const& in, TOut const& out)
{
return TAmounts<TIn, TOut>(in, out);
}
using Amounts = TAmounts<STAmount, STAmount>;
template<class TIn, class TOut>
bool
operator== (Amounts const& lhs, Amounts const& rhs) noexcept
operator== (
TAmounts<TIn, TOut> const& lhs,
TAmounts<TIn, TOut> const& rhs) noexcept
{
return lhs.in == rhs.in && lhs.out == rhs.out;
}
inline
template<class TIn, class TOut>
bool
operator!= (Amounts const& lhs, Amounts const& rhs) noexcept
operator!= (
TAmounts<TIn, TOut> const& lhs,
TAmounts<TIn, TOut> const& rhs) noexcept
{
return ! (lhs == rhs);
}
@@ -101,6 +138,13 @@ public:
explicit
Quality (Amounts const& amount);
/** Create a quality from the ratio of two amounts. */
template<class TIn, class TOut>
Quality (TOut const& out, TIn const& in)
: Quality (Amounts (toSTAmount (in),
toSTAmount (out)))
{}
/** Advances to the next higher quality level. */
/** @{ */
Quality&
@@ -133,6 +177,21 @@ public:
Amounts
ceil_in (Amounts const& amount, STAmount const& limit) const;
template<class TIn, class TOut>
TAmounts<TIn, TOut>
ceil_in (TAmounts<TIn, TOut> const& amount, TIn const& limit) const
{
if (amount.in <= limit)
return amount;
// Use the existing STAmount implementation for now, but consider
// replacing with code specific to IOUAMount and XRPAmount
Amounts stAmt (toSTAmount (amount.in), toSTAmount (amount.out));
STAmount stLim (toSTAmount (limit));
auto const stRes = ceil_in (stAmt, stLim);
return TAmounts<TIn, TOut> (toAmount<TIn> (stRes.in), toAmount<TOut> (stRes.out));
}
/** Returns the scaled amount with out capped.
Math is avoided if the result is exact. The input is clamped
to prevent money creation.
@@ -140,6 +199,21 @@ public:
Amounts
ceil_out (Amounts const& amount, STAmount const& limit) const;
template<class TIn, class TOut>
TAmounts<TIn, TOut>
ceil_out (TAmounts<TIn, TOut> const& amount, TOut const& limit) const
{
if (amount.out <= limit)
return amount;
// Use the existing STAmount implementation for now, but consider
// replacing with code specific to IOUAMount and XRPAmount
Amounts stAmt (toSTAmount (amount.in), toSTAmount (amount.out));
STAmount stLim (toSTAmount (limit));
auto const stRes = ceil_out (stAmt, stLim);
return TAmounts<TIn, TOut> (toAmount<TIn> (stRes.in), toAmount<TOut> (stRes.out));
}
/** Returns `true` if lhs is lower quality than `rhs`.
Lower quality means the taker receives a worse deal.
Higher quality is better for the taker.
@@ -151,6 +225,13 @@ public:
return lhs.m_value > rhs.m_value;
}
friend
bool
operator> (Quality const& lhs, Quality const& rhs) noexcept
{
return lhs.m_value < rhs.m_value;
}
friend
bool
operator== (Quality const& lhs, Quality const& rhs) noexcept

View File

@@ -312,6 +312,15 @@ amountFromJson (SField const& name, Json::Value const& v);
bool
amountFromJsonNoThrow (STAmount& result, Json::Value const& jvSource);
// IOUAmount and XRPAmount define toSTAmount, defining this
// trivial conversion here makes writing generic code easier
inline
STAmount const&
toSTAmount (STAmount const& a)
{
return a;
}
//------------------------------------------------------------------------------
//
// Observers

View File

@@ -23,6 +23,7 @@
#include <ripple/protocol/SystemParameters.h>
#include <beast/utility/Zero.h>
#include <boost/operators.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <cstdint>
#include <string>
#include <type_traits>
@@ -134,6 +135,27 @@ to_string (XRPAmount const& amount)
return std::to_string (amount.drops ());
}
inline
XRPAmount
mulRatio (
XRPAmount const& amt,
std::uint32_t num,
std::uint32_t den,
bool roundUp)
{
using namespace boost::multiprecision;
int128_t const den128 (den);
int128_t const num128 (num);
int128_t const amt128 (amt.drops ());
auto const m = int128_t (amt.drops ()) * num;
auto r = m / den;
if (roundUp && r >= 0 && (m % den))
r += 1;
if (r > std::numeric_limits<std::int64_t>::max ())
throw std::overflow_error ("XRP mulRatio overflow");
return XRPAmount (r.convert_to<std::int64_t> ());
}
/** Returns true if the amount does not exceed the initial XRP in existence. */
inline
bool isLegalAmount (XRPAmount const& amount)

View File

@@ -20,23 +20,24 @@
#include <BeastConfig.h>
#include <ripple/basics/contract.h>
#include <ripple/protocol/IOUAmount.h>
#include <boost/multiprecision/cpp_int.hpp>
#include <algorithm>
#include <numeric>
#include <iterator>
#include <stdexcept>
namespace ripple {
/* The range for the mantissa when normalized */
static std::int64_t const minMantissa = 1000000000000000ull;
static std::int64_t const maxMantissa = 9999999999999999ull;
/* The range for the exponent when normalized */
static int const minExponent = -96;
static int const maxExponent = 80;
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;
@@ -244,4 +245,96 @@ to_string (IOUAmount const& amount)
return ret;
}
IOUAmount
mulRatio (
IOUAmount const& amt,
std::uint32_t num,
std::uint32_t den,
bool roundUp)
{
using namespace boost::multiprecision;
static std::vector<uint128_t> const logTable = []
{
std::vector<uint128_t> result;
result.reserve (30); // 2^96 is largest intermediate result size
uint128_t cur (1);
for (int i = 0; i < 30; ++i)
{
result.push_back (cur);
cur *= 10;
};
return result;
}();
// Note: Returns -1 for v == 0
static auto log10Floor = [](uint128_t const& v)
{
auto const l = std::lower_bound (logTable.begin (), logTable.end (), v);
int index = std::distance (logTable.begin (), l);
if (*l != v)
--index;
return index;
};
static auto log10Ceil = [](uint128_t const& v)
{
auto const l = std::lower_bound (logTable.begin (), logTable.end (), v);
return int(std::distance (logTable.begin (), l));
};
static auto const fl64 =
log10Floor (std::numeric_limits<std::int64_t>::max ());
bool const neg = amt.mantissa () < 0;
uint128_t const den128 (den);
uint128_t const mul =
uint128_t (neg ? -amt.mantissa () : amt.mantissa ()) * uint128_t (num);
auto low = mul / den128;
uint128_t rem (mul - low * den128);
int exponent = amt.exponent ();
if (rem)
{
auto const roomToGrow = fl64 - log10Ceil (low);
if (roomToGrow > 0)
{
exponent -= roomToGrow;
low *= logTable[roomToGrow];
rem *= logTable[roomToGrow];
}
auto const addRem = rem / den128;
low += addRem;
rem = rem - addRem * den128;
}
bool hasRem = bool(rem);
auto const mustShrink = log10Ceil (low) - fl64;
if (mustShrink > 0)
{
uint128_t const sav (low);
exponent += mustShrink;
low /= logTable[mustShrink];
if (!hasRem && roundUp)
hasRem = bool(sav - low * logTable[mustShrink]);
}
std::int64_t mantissa = low.convert_to<std::int64_t> ();
// normalize before rounding
if (neg)
mantissa *= -1;
IOUAmount result (mantissa, exponent);
if (roundUp && hasRem && !neg)
{
if (!result)
{
return IOUAmount (minMantissa, minExponent);
}
return IOUAmount (result.mantissa() + 1, result.exponent());
}
return result;
}
}