mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Merge branch 'ripple/smart-escrow' into ripple/se/supported
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
#ifndef XRPL_BASICS_NUMBER_H_INCLUDED
|
||||
#define XRPL_BASICS_NUMBER_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
@@ -13,42 +16,252 @@ class Number;
|
||||
std::string
|
||||
to_string(Number const& amount);
|
||||
|
||||
template <typename T>
|
||||
constexpr std::optional<int>
|
||||
logTen(T value)
|
||||
{
|
||||
int log = 0;
|
||||
while (value >= 10 && value % 10 == 0)
|
||||
{
|
||||
value /= 10;
|
||||
++log;
|
||||
}
|
||||
if (value == 1)
|
||||
return log;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool
|
||||
isPowerOfTen(T value)
|
||||
{
|
||||
while (value >= 10 && value % 10 == 0)
|
||||
value /= 10;
|
||||
return value == 1;
|
||||
return logTen(value).has_value();
|
||||
}
|
||||
|
||||
/** MantissaRange defines a range for the mantissa of a normalized Number.
|
||||
*
|
||||
* The mantissa is in the range [min, max], where
|
||||
* * min is a power of 10, and
|
||||
* * max = min * 10 - 1.
|
||||
*
|
||||
* The mantissa_scale enum indicates whether the range is "small" or "large".
|
||||
* This intentionally restricts the number of MantissaRanges that can be
|
||||
* instantiated to two: one for each scale.
|
||||
*
|
||||
* The "small" scale is based on the behavior of STAmount for IOUs. It has a min
|
||||
* value of 10^15, and a max value of 10^16-1. This was sufficient for
|
||||
* uses before Lending Protocol was implemented, mostly related to AMM.
|
||||
*
|
||||
* However, it does not have sufficient precision to represent the full integer
|
||||
* range of int64_t values (-2^63 to 2^63-1), which are needed for XRP and MPT
|
||||
* values. The implementation of SingleAssetVault, and LendingProtocol need to
|
||||
* represent those integer values accurately and precisely, both for the
|
||||
* STNumber field type, and for internal calculations. That necessitated the
|
||||
* "large" scale.
|
||||
*
|
||||
* The "large" scale is intended to represent all values that can be represented
|
||||
* by an STAmount - IOUs, XRP, and MPTs. It has a min value of 10^18, and a max
|
||||
* value of 10^19-1.
|
||||
*
|
||||
* Note that if the mentioned amendments are eventually retired, this class
|
||||
* should be left in place, but the "small" scale option should be removed. This
|
||||
* will allow for future expansion beyond 64-bits if it is ever needed.
|
||||
*/
|
||||
struct MantissaRange
|
||||
{
|
||||
using rep = std::uint64_t;
|
||||
enum mantissa_scale { small, large };
|
||||
|
||||
explicit constexpr MantissaRange(mantissa_scale scale_)
|
||||
: min(getMin(scale_))
|
||||
, max(min * 10 - 1)
|
||||
, log(logTen(min).value_or(-1))
|
||||
, scale(scale_)
|
||||
{
|
||||
}
|
||||
|
||||
rep min;
|
||||
rep max;
|
||||
int log;
|
||||
mantissa_scale scale;
|
||||
|
||||
private:
|
||||
static constexpr rep
|
||||
getMin(mantissa_scale scale_)
|
||||
{
|
||||
switch (scale_)
|
||||
{
|
||||
case small:
|
||||
return 1'000'000'000'000'000ULL;
|
||||
case large:
|
||||
return 1'000'000'000'000'000'000ULL;
|
||||
default:
|
||||
// Since this can never be called outside a non-constexpr
|
||||
// context, this throw assures that the build fails if an
|
||||
// invalid scale is used.
|
||||
throw std::runtime_error("Unknown mantissa scale");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Like std::integral, but only 64-bit integral types.
|
||||
template <class T>
|
||||
concept Integral64 =
|
||||
std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::uint64_t>;
|
||||
|
||||
/** Number is a floating point type that can represent a wide range of values.
|
||||
*
|
||||
* It can represent all values that can be represented by an STAmount -
|
||||
* regardless of asset type - XRPAmount, MPTAmount, and IOUAmount, with at least
|
||||
* as much precision as those types require.
|
||||
*
|
||||
* ---- Internal Representation ----
|
||||
*
|
||||
* Internally, Number is represented with three values:
|
||||
* 1. a bool sign flag,
|
||||
* 2. a std::uint64_t mantissa,
|
||||
* 3. an int exponent.
|
||||
*
|
||||
* The internal mantissa is an unsigned integer in the range defined by the
|
||||
* current MantissaRange. The exponent is an integer in the range
|
||||
* [minExponent, maxExponent].
|
||||
*
|
||||
* See the description of MantissaRange for more details on the ranges.
|
||||
*
|
||||
* A non-zero mantissa is (almost) always normalized, meaning it and the
|
||||
* exponent are grown or shrunk until the mantissa is in the range
|
||||
* [MantissaRange.min, MantissaRange.max].
|
||||
*
|
||||
* Note:
|
||||
* 1. Normalization can be disabled by using the "unchecked" ctor tag. This
|
||||
* should only be used at specific conversion points, some constexpr
|
||||
* values, and in unit tests.
|
||||
* 2. The max of the "large" range, 10^19-1, is the largest 10^X-1 value that
|
||||
* fits in an unsigned 64-bit number. (10^19-1 < 2^64-1 and
|
||||
* 10^20-1 > 2^64-1). This avoids under- and overflows.
|
||||
*
|
||||
* ---- External Interface ----
|
||||
*
|
||||
* The external interface of Number consists of a std::int64_t mantissa, which
|
||||
* is restricted to 63-bits, and an int exponent, which must be in the range
|
||||
* [minExponent, maxExponent]. The range of the mantissa depends on which
|
||||
* MantissaRange is currently active. For the "short" range, the mantissa will
|
||||
* be between 10^15 and 10^16-1. For the "large" range, the mantissa will be
|
||||
* between -(2^63-1) and 2^63-1. As noted above, the "large" range is needed to
|
||||
* represent the full range of valid XRP and MPT integer values accurately.
|
||||
*
|
||||
* Note:
|
||||
* 1. 2^63-1 is between 10^18 and 10^19-1, which are the limits of the "large"
|
||||
* mantissa range.
|
||||
* 2. The functions mantissa() and exponent() return the external view of the
|
||||
* Number value, specifically using a signed 63-bit mantissa. This may
|
||||
* require altering the internal representation to fit into that range
|
||||
* before the value is returned. The interface guarantees consistency of
|
||||
* the two values.
|
||||
* 3. Number cannot represent -2^63 (std::numeric_limits<std::int64_t>::min())
|
||||
* as an exact integer, but it doesn't need to, because all asset values
|
||||
* on-ledger are non-negative. This is due to implementation details of
|
||||
* several operations which use unsigned arithmetic internally. This is
|
||||
* sufficient to represent all valid XRP values (where the absolute value
|
||||
* can not exceed INITIAL_XRP: 10^17), and MPT values (where the absolute
|
||||
* value can not exceed maxMPTokenAmount: 2^63-1).
|
||||
*
|
||||
* ---- Mantissa Range Switching ----
|
||||
*
|
||||
* The mantissa range may be changed at runtime via setMantissaScale(). The
|
||||
* default mantissa range is "large". The range is updated whenever transaction
|
||||
* processing begins, based on whether SingleAssetVault or LendingProtocol are
|
||||
* enabled. If either is enabled, the mantissa range is set to "large". If not,
|
||||
* it is set to "small", preserving backward compatibility and correct
|
||||
* "amendment-gating".
|
||||
*
|
||||
* It is extremely unlikely that any more calls to setMantissaScale() will be
|
||||
* needed outside of unit tests.
|
||||
*
|
||||
* ---- Usage With Different Ranges ----
|
||||
*
|
||||
* Outside of unit tests, and existing checks, code that uses Number should not
|
||||
* know or care which mantissa range is active.
|
||||
*
|
||||
* The results of computations using Numbers with a small mantissa may differ
|
||||
* from computations using Numbers with a large mantissa, specifically as it
|
||||
* effects the results after rounding. That is why the large mantissa range is
|
||||
* amendment gated in transaction processing.
|
||||
*
|
||||
* It is extremely unlikely that any more calls to getMantissaScale() will be
|
||||
* needed outside of unit tests.
|
||||
*
|
||||
* Code that uses Number should not assume or check anything about the
|
||||
* mantissa() or exponent() except that they fit into the "large" range
|
||||
* specified in the "External Interface" section.
|
||||
*
|
||||
* ----- Unit Tests -----
|
||||
*
|
||||
* Within unit tests, it may be useful to explicitly switch between the two
|
||||
* ranges, or to check which range is active when checking the results of
|
||||
* computations. If the test is doing the math directly, the
|
||||
* set/getMantissaScale() functions may be most appropriate. However, if the
|
||||
* test has anything to do with transaction processing, it should enable or
|
||||
* disable the amendments that control the mantissa range choice
|
||||
* (SingleAssetVault and LendingProtocol), and/or check if either of those
|
||||
* amendments are enabled to determine which result to expect.
|
||||
*
|
||||
*/
|
||||
class Number
|
||||
{
|
||||
using rep = std::int64_t;
|
||||
rep mantissa_{0};
|
||||
using internalrep = MantissaRange::rep;
|
||||
|
||||
bool negative_{false};
|
||||
internalrep mantissa_{0};
|
||||
int exponent_{std::numeric_limits<int>::lowest()};
|
||||
|
||||
public:
|
||||
// The range for the mantissa when normalized
|
||||
constexpr static std::int64_t minMantissa = 1'000'000'000'000'000LL;
|
||||
static_assert(isPowerOfTen(minMantissa));
|
||||
constexpr static std::int64_t maxMantissa = minMantissa * 10 - 1;
|
||||
static_assert(maxMantissa == 9'999'999'999'999'999LL);
|
||||
|
||||
// The range for the exponent when normalized
|
||||
constexpr static int minExponent = -32768;
|
||||
constexpr static int maxExponent = 32768;
|
||||
|
||||
constexpr static internalrep maxRep = std::numeric_limits<rep>::max();
|
||||
static_assert(maxRep == 9'223'372'036'854'775'807);
|
||||
static_assert(-maxRep == std::numeric_limits<rep>::min() + 1);
|
||||
|
||||
// May need to make unchecked private
|
||||
struct unchecked
|
||||
{
|
||||
explicit unchecked() = default;
|
||||
};
|
||||
|
||||
// Like unchecked, normalized is used with the ctors that take an
|
||||
// internalrep mantissa. Unlike unchecked, those ctors will normalize the
|
||||
// value.
|
||||
// Only unit tests are expected to use this class
|
||||
struct normalized
|
||||
{
|
||||
explicit normalized() = default;
|
||||
};
|
||||
|
||||
explicit constexpr Number() = default;
|
||||
|
||||
Number(rep mantissa);
|
||||
explicit Number(rep mantissa, int exponent);
|
||||
explicit constexpr Number(rep mantissa, int exponent, unchecked) noexcept;
|
||||
explicit constexpr Number(
|
||||
bool negative,
|
||||
internalrep mantissa,
|
||||
int exponent,
|
||||
unchecked) noexcept;
|
||||
// Assume unsigned values are... unsigned. i.e. positive
|
||||
explicit constexpr Number(
|
||||
internalrep mantissa,
|
||||
int exponent,
|
||||
unchecked) noexcept;
|
||||
// Only unit tests are expected to use this ctor
|
||||
explicit Number(
|
||||
bool negative,
|
||||
internalrep mantissa,
|
||||
int exponent,
|
||||
normalized);
|
||||
// Assume unsigned values are... unsigned. i.e. positive
|
||||
explicit Number(internalrep mantissa, int exponent, normalized);
|
||||
|
||||
constexpr rep
|
||||
mantissa() const noexcept;
|
||||
@@ -78,11 +291,11 @@ public:
|
||||
Number&
|
||||
operator/=(Number const& x);
|
||||
|
||||
static constexpr Number
|
||||
static Number
|
||||
min() noexcept;
|
||||
static constexpr Number
|
||||
static Number
|
||||
max() noexcept;
|
||||
static constexpr Number
|
||||
static Number
|
||||
lowest() noexcept;
|
||||
|
||||
/** Conversions to Number are implicit and conversions away from Number
|
||||
@@ -96,7 +309,8 @@ public:
|
||||
friend constexpr bool
|
||||
operator==(Number const& x, Number const& y) noexcept
|
||||
{
|
||||
return x.mantissa_ == y.mantissa_ && x.exponent_ == y.exponent_;
|
||||
return x.negative_ == y.negative_ && x.mantissa_ == y.mantissa_ &&
|
||||
x.exponent_ == y.exponent_;
|
||||
}
|
||||
|
||||
friend constexpr bool
|
||||
@@ -110,8 +324,8 @@ public:
|
||||
{
|
||||
// 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 = x.mantissa_ < 0;
|
||||
bool const rneg = y.mantissa_ < 0;
|
||||
bool const lneg = x.negative_;
|
||||
bool const rneg = y.negative_;
|
||||
|
||||
if (lneg != rneg)
|
||||
return lneg;
|
||||
@@ -139,7 +353,7 @@ public:
|
||||
constexpr int
|
||||
signum() const noexcept
|
||||
{
|
||||
return (mantissa_ < 0) ? -1 : (mantissa_ ? 1 : 0);
|
||||
return negative_ ? -1 : (mantissa_ ? 1 : 0);
|
||||
}
|
||||
|
||||
Number
|
||||
@@ -169,6 +383,15 @@ public:
|
||||
return os << to_string(x);
|
||||
}
|
||||
|
||||
friend std::string
|
||||
to_string(Number const& amount);
|
||||
|
||||
friend Number
|
||||
root(Number f, unsigned d);
|
||||
|
||||
friend Number
|
||||
root2(Number f);
|
||||
|
||||
// Thread local rounding control. Default is to_nearest
|
||||
enum rounding_mode { to_nearest, towards_zero, downward, upward };
|
||||
static rounding_mode
|
||||
@@ -177,44 +400,206 @@ public:
|
||||
static rounding_mode
|
||||
setround(rounding_mode mode);
|
||||
|
||||
/** Returns which mantissa scale is currently in use for normalization.
|
||||
*
|
||||
* If you think you need to call this outside of unit tests, no you don't.
|
||||
*/
|
||||
static MantissaRange::mantissa_scale
|
||||
getMantissaScale();
|
||||
/** Changes which mantissa scale is used for normalization.
|
||||
*
|
||||
* If you think you need to call this outside of unit tests, no you don't.
|
||||
*/
|
||||
static void
|
||||
setMantissaScale(MantissaRange::mantissa_scale scale);
|
||||
|
||||
inline static internalrep
|
||||
minMantissa()
|
||||
{
|
||||
return range_.get().min;
|
||||
}
|
||||
|
||||
inline static internalrep
|
||||
maxMantissa()
|
||||
{
|
||||
return range_.get().max;
|
||||
}
|
||||
|
||||
inline static int
|
||||
mantissaLog()
|
||||
{
|
||||
return range_.get().log;
|
||||
}
|
||||
|
||||
/// oneSmall is needed because the ranges are private
|
||||
constexpr static Number
|
||||
oneSmall();
|
||||
/// oneLarge is needed because the ranges are private
|
||||
constexpr static Number
|
||||
oneLarge();
|
||||
|
||||
// And one is needed because it needs to choose between oneSmall and
|
||||
// oneLarge based on the current range
|
||||
static Number
|
||||
one();
|
||||
|
||||
template <Integral64 T>
|
||||
[[nodiscard]]
|
||||
std::pair<T, int>
|
||||
normalizeToRange(T minMantissa, T maxMantissa) const;
|
||||
|
||||
private:
|
||||
static thread_local rounding_mode mode_;
|
||||
// The available ranges for mantissa
|
||||
|
||||
constexpr static MantissaRange smallRange{MantissaRange::small};
|
||||
static_assert(isPowerOfTen(smallRange.min));
|
||||
static_assert(smallRange.min == 1'000'000'000'000'000LL);
|
||||
static_assert(smallRange.max == 9'999'999'999'999'999LL);
|
||||
static_assert(smallRange.log == 15);
|
||||
static_assert(smallRange.min < maxRep);
|
||||
static_assert(smallRange.max < maxRep);
|
||||
constexpr static MantissaRange largeRange{MantissaRange::large};
|
||||
static_assert(isPowerOfTen(largeRange.min));
|
||||
static_assert(largeRange.min == 1'000'000'000'000'000'000ULL);
|
||||
static_assert(largeRange.max == internalrep(9'999'999'999'999'999'999ULL));
|
||||
static_assert(largeRange.log == 18);
|
||||
static_assert(largeRange.min < maxRep);
|
||||
static_assert(largeRange.max > maxRep);
|
||||
|
||||
// The range for the mantissa when normalized.
|
||||
// Use reference_wrapper to avoid making copies, and prevent accidentally
|
||||
// changing the values inside the range.
|
||||
static thread_local std::reference_wrapper<MantissaRange const> range_;
|
||||
|
||||
void
|
||||
normalize();
|
||||
constexpr bool
|
||||
|
||||
/** Normalize Number components to an arbitrary range.
|
||||
*
|
||||
* min/maxMantissa are parameters because this function is used by both
|
||||
* normalize(), which reads from range_, and by normalizeToRange,
|
||||
* which is public and can accept an arbitrary range from the caller.
|
||||
*/
|
||||
template <class T>
|
||||
static void
|
||||
normalize(
|
||||
bool& negative,
|
||||
T& mantissa,
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa);
|
||||
|
||||
template <class T>
|
||||
friend void
|
||||
doNormalize(
|
||||
bool& negative,
|
||||
T& mantissa_,
|
||||
int& exponent_,
|
||||
MantissaRange::rep const& minMantissa,
|
||||
MantissaRange::rep const& maxMantissa);
|
||||
|
||||
bool
|
||||
isnormal() const noexcept;
|
||||
|
||||
// Copy the number, but modify the exponent by "exponentDelta". Because the
|
||||
// mantissa doesn't change, the result will be "mostly" normalized, but the
|
||||
// exponent could go out of range, so it will be checked.
|
||||
Number
|
||||
shiftExponent(int exponentDelta) const;
|
||||
|
||||
// Safely convert rep (int64) mantissa to internalrep (uint64). If the rep
|
||||
// is negative, returns the positive value. This takes a little extra work
|
||||
// because converting std::numeric_limits<std::int64_t>::min() flirts with
|
||||
// UB, and can vary across compilers.
|
||||
static internalrep
|
||||
externalToInternal(rep mantissa);
|
||||
|
||||
class Guard;
|
||||
};
|
||||
|
||||
inline constexpr Number::Number(
|
||||
bool negative,
|
||||
internalrep mantissa,
|
||||
int exponent,
|
||||
unchecked) noexcept
|
||||
: negative_(negative), mantissa_{mantissa}, exponent_{exponent}
|
||||
{
|
||||
}
|
||||
|
||||
inline constexpr Number::Number(
|
||||
internalrep mantissa,
|
||||
int exponent,
|
||||
unchecked) noexcept
|
||||
: Number(false, mantissa, exponent, unchecked{})
|
||||
{
|
||||
}
|
||||
|
||||
constexpr static Number numZero{};
|
||||
|
||||
inline constexpr Number::Number(rep mantissa, int exponent, unchecked) noexcept
|
||||
: mantissa_{mantissa}, exponent_{exponent}
|
||||
inline Number::Number(
|
||||
bool negative,
|
||||
internalrep mantissa,
|
||||
int exponent,
|
||||
normalized)
|
||||
: Number(negative, mantissa, exponent, unchecked{})
|
||||
{
|
||||
normalize();
|
||||
}
|
||||
|
||||
inline Number::Number(internalrep mantissa, int exponent, normalized)
|
||||
: Number(false, mantissa, exponent, normalized{})
|
||||
{
|
||||
}
|
||||
|
||||
inline Number::Number(rep mantissa, int exponent)
|
||||
: mantissa_{mantissa}, exponent_{exponent}
|
||||
: Number(mantissa < 0, externalToInternal(mantissa), exponent, normalized{})
|
||||
{
|
||||
normalize();
|
||||
}
|
||||
|
||||
inline Number::Number(rep mantissa) : Number{mantissa, 0}
|
||||
{
|
||||
}
|
||||
|
||||
/** Returns the mantissa of the external view of the Number.
|
||||
*
|
||||
* Please see the "---- External Interface ----" section of the class
|
||||
* documentation for an explanation of why the internal value may be modified.
|
||||
*/
|
||||
inline constexpr Number::rep
|
||||
Number::mantissa() const noexcept
|
||||
{
|
||||
return mantissa_;
|
||||
auto m = mantissa_;
|
||||
if (m > maxRep)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
!isnormal() || (m % 10 == 0 && m / 10 <= maxRep),
|
||||
"xrpl::Number::mantissa",
|
||||
"large normalized mantissa has no remainder");
|
||||
m /= 10;
|
||||
}
|
||||
auto const sign = negative_ ? -1 : 1;
|
||||
return sign * static_cast<Number::rep>(m);
|
||||
}
|
||||
|
||||
/** Returns the exponent of the external view of the Number.
|
||||
*
|
||||
* Please see the "---- External Interface ----" section of the class
|
||||
* documentation for an explanation of why the internal value may be modified.
|
||||
*/
|
||||
inline constexpr int
|
||||
Number::exponent() const noexcept
|
||||
{
|
||||
return exponent_;
|
||||
auto e = exponent_;
|
||||
if (mantissa_ > maxRep)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
!isnormal() || (mantissa_ % 10 == 0 && mantissa_ / 10 <= maxRep),
|
||||
"xrpl::Number::exponent",
|
||||
"large normalized mantissa has no remainder");
|
||||
++e;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
@@ -226,15 +611,17 @@ Number::operator+() const noexcept
|
||||
inline constexpr Number
|
||||
Number::operator-() const noexcept
|
||||
{
|
||||
if (mantissa_ == 0)
|
||||
return Number{};
|
||||
auto x = *this;
|
||||
x.mantissa_ = -x.mantissa_;
|
||||
x.negative_ = !x.negative_;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline Number&
|
||||
Number::operator++()
|
||||
{
|
||||
*this += Number{1000000000000000, -15, unchecked{}};
|
||||
*this += one();
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -249,7 +636,7 @@ Number::operator++(int)
|
||||
inline Number&
|
||||
Number::operator--()
|
||||
{
|
||||
*this -= Number{1000000000000000, -15, unchecked{}};
|
||||
*this -= one();
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -299,30 +686,54 @@ operator/(Number const& x, Number const& y)
|
||||
return z;
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
inline Number
|
||||
Number::min() noexcept
|
||||
{
|
||||
return Number{minMantissa, minExponent, unchecked{}};
|
||||
return Number{false, range_.get().min, minExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
inline Number
|
||||
Number::max() noexcept
|
||||
{
|
||||
return Number{maxMantissa, maxExponent, unchecked{}};
|
||||
return Number{
|
||||
false, std::min(range_.get().max, maxRep), maxExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
inline Number
|
||||
Number::lowest() noexcept
|
||||
{
|
||||
return -Number{maxMantissa, maxExponent, unchecked{}};
|
||||
return Number{
|
||||
true, std::min(range_.get().max, maxRep), maxExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline constexpr bool
|
||||
inline bool
|
||||
Number::isnormal() const noexcept
|
||||
{
|
||||
auto const abs_m = mantissa_ < 0 ? -mantissa_ : mantissa_;
|
||||
return minMantissa <= abs_m && abs_m <= maxMantissa &&
|
||||
minExponent <= exponent_ && exponent_ <= maxExponent;
|
||||
MantissaRange const& range = range_;
|
||||
auto const abs_m = mantissa_;
|
||||
return *this == Number{} ||
|
||||
(range.min <= abs_m && abs_m <= range.max &&
|
||||
(abs_m <= maxRep || abs_m % 10 == 0) && minExponent <= exponent_ &&
|
||||
exponent_ <= maxExponent);
|
||||
}
|
||||
|
||||
template <Integral64 T>
|
||||
std::pair<T, int>
|
||||
Number::normalizeToRange(T minMantissa, T maxMantissa) const
|
||||
{
|
||||
bool negative = negative_;
|
||||
internalrep mantissa = mantissa_;
|
||||
int exponent = exponent_;
|
||||
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
XRPL_ASSERT_PARTS(
|
||||
!negative,
|
||||
"xrpl::Number::normalizeToRange",
|
||||
"Number is non-negative for unsigned range.");
|
||||
Number::normalize(negative, mantissa, exponent, minMantissa, maxMantissa);
|
||||
|
||||
auto const sign = negative ? -1 : 1;
|
||||
return std::make_pair(static_cast<T>(sign * mantissa), exponent);
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
@@ -368,6 +779,20 @@ squelch(Number const& x, Number const& limit) noexcept
|
||||
return x;
|
||||
}
|
||||
|
||||
inline std::string
|
||||
to_string(MantissaRange::mantissa_scale const& scale)
|
||||
{
|
||||
switch (scale)
|
||||
{
|
||||
case MantissaRange::small:
|
||||
return "small";
|
||||
case MantissaRange::large:
|
||||
return "large";
|
||||
default:
|
||||
throw std::runtime_error("Bad scale");
|
||||
}
|
||||
}
|
||||
|
||||
class saveNumberRoundMode
|
||||
{
|
||||
Number::rounding_mode mode_;
|
||||
@@ -406,6 +831,34 @@ public:
|
||||
operator=(NumberRoundModeGuard const&) = delete;
|
||||
};
|
||||
|
||||
/** Sets the new scale and restores the old scale when it leaves scope.
|
||||
*
|
||||
* If you think you need to use this class outside of unit tests, no you don't.
|
||||
*
|
||||
*/
|
||||
class NumberMantissaScaleGuard
|
||||
{
|
||||
MantissaRange::mantissa_scale const saved_;
|
||||
|
||||
public:
|
||||
explicit NumberMantissaScaleGuard(
|
||||
MantissaRange::mantissa_scale scale) noexcept
|
||||
: saved_{Number::getMantissaScale()}
|
||||
{
|
||||
Number::setMantissaScale(scale);
|
||||
}
|
||||
|
||||
~NumberMantissaScaleGuard()
|
||||
{
|
||||
Number::setMantissaScale(saved_);
|
||||
}
|
||||
|
||||
NumberMantissaScaleGuard(NumberMantissaScaleGuard const&) = delete;
|
||||
|
||||
NumberMantissaScaleGuard&
|
||||
operator=(NumberMantissaScaleGuard const&) = delete;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif // XRPL_BASICS_NUMBER_H_INCLUDED
|
||||
|
||||
50
include/xrpl/ledger/Credit.h
Normal file
50
include/xrpl/ledger/Credit.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef XRPL_LEDGER_CREDIT_H_INCLUDED
|
||||
#define XRPL_LEDGER_CREDIT_H_INCLUDED
|
||||
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Calculate the maximum amount of IOUs that an account can hold
|
||||
@param ledger the ledger to check against.
|
||||
@param account the account of interest.
|
||||
@param issuer the issuer of the IOU.
|
||||
@param currency the IOU to check.
|
||||
@return The maximum amount that can be held.
|
||||
*/
|
||||
/** @{ */
|
||||
STAmount
|
||||
creditLimit(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
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.
|
||||
@param issuer the issuer of the IOU.
|
||||
@param currency the IOU to check.
|
||||
*/
|
||||
/** @{ */
|
||||
STAmount
|
||||
creditBalance(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& issuer,
|
||||
Currency const& currency);
|
||||
/** @} */
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
@@ -61,6 +61,9 @@ enum FreezeHandling { fhIGNORE_FREEZE, fhZERO_IF_FROZEN };
|
||||
/** Controls the treatment of unauthorized MPT balances */
|
||||
enum AuthHandling { ahIGNORE_AUTH, ahZERO_IF_UNAUTHORIZED };
|
||||
|
||||
/** Controls whether to include the account's full spendable balance */
|
||||
enum SpendableHandling { shSIMPLE_BALANCE, shFULL_BALANCE };
|
||||
|
||||
[[nodiscard]] bool
|
||||
isGlobalFrozen(ReadView const& view, AccountID const& issuer);
|
||||
|
||||
@@ -305,86 +308,57 @@ isLPTokenFrozen(
|
||||
Issue const& asset,
|
||||
Issue const& asset2);
|
||||
|
||||
// Returns the amount an account can spend without going into debt.
|
||||
// Returns the amount an account can spend.
|
||||
//
|
||||
// <-- saAmount: amount of currency held by account. May be negative.
|
||||
[[nodiscard]] STAmount
|
||||
accountHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Currency const& currency,
|
||||
AccountID const& issuer,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
beast::Journal j);
|
||||
|
||||
[[nodiscard]] STAmount
|
||||
accountHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Issue const& issue,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
beast::Journal j);
|
||||
|
||||
[[nodiscard]] STAmount
|
||||
accountHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
MPTIssue const& mptIssue,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
AuthHandling zeroIfUnauthorized,
|
||||
beast::Journal j);
|
||||
|
||||
[[nodiscard]] STAmount
|
||||
accountHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Asset const& asset,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
AuthHandling zeroIfUnauthorized,
|
||||
beast::Journal j);
|
||||
|
||||
// Returns the amount an account can spend total.
|
||||
// If shSIMPLE_BALANCE is specified, this is the amount the account can spend
|
||||
// without going into debt.
|
||||
//
|
||||
// These functions use accountHolds, but unlike accountHolds:
|
||||
// * The account can go into debt.
|
||||
// * If the account is the asset issuer the only limit is defined by the asset /
|
||||
// If shFULL_BALANCE is specified, this is the amount the account can spend
|
||||
// total. Specifically:
|
||||
// * The account can go into debt if using a trust line, and the other side has
|
||||
// a non-zero limit.
|
||||
// * If the account is the asset issuer the limit is defined by the asset /
|
||||
// issuance.
|
||||
//
|
||||
// <-- saAmount: amount of currency held by account. May be negative.
|
||||
[[nodiscard]] STAmount
|
||||
accountSpendable(
|
||||
accountHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Currency const& currency,
|
||||
AccountID const& issuer,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
beast::Journal j);
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
|
||||
|
||||
[[nodiscard]] STAmount
|
||||
accountSpendable(
|
||||
accountHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Issue const& issue,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
beast::Journal j);
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
|
||||
|
||||
[[nodiscard]] STAmount
|
||||
accountSpendable(
|
||||
accountHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
MPTIssue const& mptIssue,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
AuthHandling zeroIfUnauthorized,
|
||||
beast::Journal j);
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
|
||||
|
||||
[[nodiscard]] STAmount
|
||||
accountSpendable(
|
||||
accountHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Asset const& asset,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
AuthHandling zeroIfUnauthorized,
|
||||
beast::Journal j);
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
|
||||
|
||||
// Returns the amount an account can spend of the currency type saDefault, or
|
||||
// returns saDefault if this account is the issuer of the currency in
|
||||
@@ -655,7 +629,7 @@ createPseudoAccount(
|
||||
uint256 const& pseudoOwnerKey,
|
||||
SField const& ownerField);
|
||||
|
||||
// Returns true iff sleAcct is a pseudo-account or specific
|
||||
// Returns true if and only if sleAcct is a pseudo-account or specific
|
||||
// pseudo-accounts in pseudoFieldFilter.
|
||||
//
|
||||
// Returns false if sleAcct is
|
||||
@@ -710,13 +684,16 @@ checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag);
|
||||
* - If withdrawing to self, succeed.
|
||||
* - If not, checks if the receiver requires deposit authorization, and if
|
||||
* the sender has it.
|
||||
* - Checks that the receiver will not exceed the limit (IOU trustline limit
|
||||
* or MPT MaximumAmount).
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
canWithdraw(
|
||||
AccountID const& from,
|
||||
ReadView const& view,
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
SLE::const_ref toSle,
|
||||
STAmount const& amount,
|
||||
bool hasDestinationTag);
|
||||
|
||||
/** Checks that can withdraw funds from an object to itself or a destination.
|
||||
@@ -730,12 +707,15 @@ canWithdraw(
|
||||
* - If withdrawing to self, succeed.
|
||||
* - If not, checks if the receiver requires deposit authorization, and if
|
||||
* the sender has it.
|
||||
* - Checks that the receiver will not exceed the limit (IOU trustline limit
|
||||
* or MPT MaximumAmount).
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
canWithdraw(
|
||||
AccountID const& from,
|
||||
ReadView const& view,
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
STAmount const& amount,
|
||||
bool hasDestinationTag);
|
||||
|
||||
/** Checks that can withdraw funds from an object to itself or a destination.
|
||||
@@ -749,6 +729,8 @@ canWithdraw(
|
||||
* - If withdrawing to self, succeed.
|
||||
* - If not, checks if the receiver requires deposit authorization, and if
|
||||
* the sender has it.
|
||||
* - Checks that the receiver will not exceed the limit (IOU trustline limit
|
||||
* or MPT MaximumAmount).
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
canWithdraw(ReadView const& view, STTx const& tx);
|
||||
|
||||
@@ -121,7 +121,7 @@ toAmount(
|
||||
{
|
||||
if (isXRP(issue))
|
||||
return STAmount(issue, static_cast<std::int64_t>(n));
|
||||
return STAmount(issue, n.mantissa(), n.exponent());
|
||||
return STAmount(issue, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -26,8 +26,10 @@ class IOUAmount : private boost::totally_ordered<IOUAmount>,
|
||||
private boost::additive<IOUAmount>
|
||||
{
|
||||
private:
|
||||
std::int64_t mantissa_;
|
||||
int exponent_;
|
||||
using mantissa_type = std::int64_t;
|
||||
using exponent_type = int;
|
||||
mantissa_type mantissa_;
|
||||
exponent_type exponent_;
|
||||
|
||||
/** Adjusts the mantissa and exponent to the proper range.
|
||||
|
||||
@@ -38,18 +40,14 @@ private:
|
||||
void
|
||||
normalize();
|
||||
|
||||
public:
|
||||
/* The range for the mantissa when normalized */
|
||||
static std::int64_t constexpr minMantissa = 1000000000000000ull;
|
||||
static std::int64_t constexpr maxMantissa = 9999999999999999ull;
|
||||
/* The range for the exponent when normalized */
|
||||
static int constexpr minExponent = -96;
|
||||
static int constexpr maxExponent = 80;
|
||||
static IOUAmount
|
||||
fromNumber(Number const& number);
|
||||
|
||||
public:
|
||||
IOUAmount() = default;
|
||||
explicit IOUAmount(Number const& other);
|
||||
IOUAmount(beast::Zero);
|
||||
IOUAmount(std::int64_t mantissa, int exponent);
|
||||
IOUAmount(mantissa_type mantissa, exponent_type exponent);
|
||||
|
||||
IOUAmount& operator=(beast::Zero);
|
||||
|
||||
@@ -78,10 +76,10 @@ public:
|
||||
int
|
||||
signum() const noexcept;
|
||||
|
||||
int
|
||||
exponent_type
|
||||
exponent() const noexcept;
|
||||
|
||||
std::int64_t
|
||||
mantissa_type
|
||||
mantissa() const noexcept;
|
||||
|
||||
static IOUAmount
|
||||
@@ -99,7 +97,7 @@ inline IOUAmount::IOUAmount(beast::Zero)
|
||||
*this = beast::zero;
|
||||
}
|
||||
|
||||
inline IOUAmount::IOUAmount(std::int64_t mantissa, int exponent)
|
||||
inline IOUAmount::IOUAmount(mantissa_type mantissa, exponent_type exponent)
|
||||
: mantissa_(mantissa), exponent_(exponent)
|
||||
{
|
||||
normalize();
|
||||
@@ -156,13 +154,13 @@ IOUAmount::signum() const noexcept
|
||||
return (mantissa_ < 0) ? -1 : (mantissa_ ? 1 : 0);
|
||||
}
|
||||
|
||||
inline int
|
||||
inline IOUAmount::exponent_type
|
||||
IOUAmount::exponent() const noexcept
|
||||
{
|
||||
return exponent_;
|
||||
}
|
||||
|
||||
inline std::int64_t
|
||||
inline IOUAmount::mantissa_type
|
||||
IOUAmount::mantissa() const noexcept
|
||||
{
|
||||
return mantissa_;
|
||||
|
||||
@@ -37,6 +37,9 @@ public:
|
||||
bool
|
||||
native() const;
|
||||
|
||||
bool
|
||||
integral() const;
|
||||
|
||||
friend constexpr std::weak_ordering
|
||||
operator<=>(Issue const& lhs, Issue const& rhs);
|
||||
};
|
||||
|
||||
@@ -46,6 +46,12 @@ public:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
integral() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr bool
|
||||
|
||||
@@ -233,6 +233,7 @@ std::size_t constexpr maxMPTokenMetadataLength = 1024;
|
||||
|
||||
/** The maximum amount of MPTokenIssuance */
|
||||
std::uint64_t constexpr maxMPTokenAmount = 0x7FFF'FFFF'FFFF'FFFFull;
|
||||
static_assert(Number::maxRep >= maxMPTokenAmount);
|
||||
|
||||
/** The maximum length of Data payload */
|
||||
std::size_t constexpr maxDataPayloadLength = 256;
|
||||
|
||||
@@ -135,7 +135,10 @@ public:
|
||||
sMD_Always = 0x10, // value when node containing it is affected at all
|
||||
sMD_BaseTen = 0x20, // value is treated as base 10, overriding behavior
|
||||
sMD_PseudoAccount = 0x40, // if this field is set in an ACCOUNT_ROOT
|
||||
// _only_, then it is a pseudo-account
|
||||
// _only_, then it is a pseudo-account
|
||||
sMD_NeedsAsset = 0x80, // This field needs to be associated with an
|
||||
// asset before it is serialized as a ledger
|
||||
// object. Intended for STNumber.
|
||||
sMD_Default =
|
||||
sMD_ChangeOrig | sMD_ChangeNew | sMD_DeleteFinal | sMD_Create
|
||||
};
|
||||
|
||||
@@ -138,7 +138,7 @@ public:
|
||||
|
||||
template <AssetType A>
|
||||
STAmount(A const& asset, Number const& number)
|
||||
: STAmount(asset, number.mantissa(), number.exponent())
|
||||
: STAmount(fromNumber(asset, number))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -282,6 +282,10 @@ public:
|
||||
mpt() const;
|
||||
|
||||
private:
|
||||
template <AssetType A>
|
||||
static STAmount
|
||||
fromNumber(A const& asset, Number const& number);
|
||||
|
||||
static std::unique_ptr<STAmount>
|
||||
construct(SerialIter&, SField const& name);
|
||||
|
||||
@@ -345,10 +349,19 @@ STAmount::STAmount(
|
||||
, mIsNegative(negative)
|
||||
{
|
||||
// mValue is uint64, but needs to fit in the range of int64
|
||||
XRPL_ASSERT(
|
||||
mValue <= std::numeric_limits<std::int64_t>::max(),
|
||||
"xrpl::STAmount::STAmount(SField, A, std::uint64_t, int, bool) : "
|
||||
"maximum mantissa input");
|
||||
if (Number::getMantissaScale() == MantissaRange::small)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
mValue <= std::numeric_limits<std::int64_t>::max(),
|
||||
"xrpl::STAmount::STAmount(SField, A, std::uint64_t, int, bool) : "
|
||||
"maximum mantissa input");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (integral() && mValue > std::numeric_limits<std::int64_t>::max())
|
||||
throw std::overflow_error(
|
||||
"STAmount mantissa is too large " + std::to_string(mantissa));
|
||||
}
|
||||
canonicalize();
|
||||
}
|
||||
|
||||
@@ -542,14 +555,23 @@ STAmount::operator=(XRPAmount const& amount)
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline STAmount&
|
||||
STAmount::operator=(Number const& number)
|
||||
template <AssetType A>
|
||||
inline STAmount
|
||||
STAmount::fromNumber(A const& a, Number const& number)
|
||||
{
|
||||
mIsNegative = number.mantissa() < 0;
|
||||
mValue = mIsNegative ? -number.mantissa() : number.mantissa();
|
||||
mOffset = number.exponent();
|
||||
canonicalize();
|
||||
return *this;
|
||||
bool const negative = number.mantissa() < 0;
|
||||
Number const working{negative ? -number : number};
|
||||
Asset asset{a};
|
||||
if (asset.integral())
|
||||
{
|
||||
std::uint64_t const intValue = static_cast<std::int64_t>(working);
|
||||
return STAmount{asset, intValue, 0, negative};
|
||||
}
|
||||
|
||||
auto const [mantissa, exponent] =
|
||||
working.normalizeToRange(cMinValue, cMaxValue);
|
||||
|
||||
return STAmount{asset, mantissa, exponent, negative};
|
||||
}
|
||||
|
||||
inline void
|
||||
@@ -699,17 +721,32 @@ getRate(STAmount const& offerOut, STAmount const& offerIn);
|
||||
* @param rounding Optional Number rounding mode
|
||||
*
|
||||
*/
|
||||
STAmount
|
||||
[[nodiscard]] STAmount
|
||||
roundToScale(
|
||||
STAmount const& value,
|
||||
std::int32_t scale,
|
||||
Number::rounding_mode rounding = Number::getround());
|
||||
|
||||
/** Round an arbitrary precision Number IN PLACE to the precision of a given
|
||||
* Asset.
|
||||
*
|
||||
* This is used to ensure that calculations do not collect dust for IOUs, or
|
||||
* fractional amounts for the integral types XRP and MPT.
|
||||
*
|
||||
* @param asset The relevant asset
|
||||
* @param value The lvalue to be rounded
|
||||
*/
|
||||
template <AssetType A>
|
||||
void
|
||||
roundToAsset(A const& asset, Number& value)
|
||||
{
|
||||
value = STAmount{asset, value};
|
||||
}
|
||||
|
||||
/** Round an arbitrary precision Number to the precision of a given Asset.
|
||||
*
|
||||
* This is used to ensure that calculations do not collect dust beyond the
|
||||
* precision of the reference value for IOUs, or fractional amounts for the
|
||||
* integral types XRP and MPT.
|
||||
* This is used to ensure that calculations do not collect dust beyond specified
|
||||
* scale for IOUs, or fractional amounts for the integral types XRP and MPT.
|
||||
*
|
||||
* @param asset The relevant asset
|
||||
* @param value The value to be rounded
|
||||
@@ -718,7 +755,7 @@ roundToScale(
|
||||
* @param rounding Optional Number rounding mode
|
||||
*/
|
||||
template <AssetType A>
|
||||
Number
|
||||
[[nodiscard]] Number
|
||||
roundToAsset(
|
||||
A const& asset,
|
||||
Number const& value,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <xrpl/basics/CountedObject.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/protocol/STBase.h>
|
||||
#include <xrpl/protocol/STTakesAsset.h>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
@@ -19,8 +20,19 @@ namespace xrpl {
|
||||
* it can represent a value of any token type (XRP, IOU, or MPT)
|
||||
* without paying the storage cost of duplicating asset information
|
||||
* that may be deduced from the context.
|
||||
*
|
||||
* STNumber derives from STTakesAsset, so that it can be associated with the
|
||||
* related Asset during transaction processing. Which asset is relevant depends
|
||||
* on the object and transaction. As of this writing, only Vault, LoanBroker,
|
||||
* and Loan objects use STNumber fields. All of those fields represent amounts
|
||||
* of the Vault's Asset, so they should be associated with the Vault's Asset.
|
||||
*
|
||||
* e.g.
|
||||
* associateAsset(*loanSle, asset);
|
||||
* associateAsset(*brokerSle, asset);
|
||||
* associateAsset(*vaultSle, asset);
|
||||
*/
|
||||
class STNumber : public STBase, public CountedObject<STNumber>
|
||||
class STNumber : public STTakesAsset, public CountedObject<STNumber>
|
||||
{
|
||||
private:
|
||||
Number value_;
|
||||
@@ -56,6 +68,9 @@ public:
|
||||
bool
|
||||
isDefault() const override;
|
||||
|
||||
void
|
||||
associateAsset(Asset const& a) override;
|
||||
|
||||
operator Number() const
|
||||
{
|
||||
return value_;
|
||||
|
||||
63
include/xrpl/protocol/STTakesAsset.h
Normal file
63
include/xrpl/protocol/STTakesAsset.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#ifndef XRPL_PROTOCOL_STTAKESASSET_H_INCLUDED
|
||||
#define XRPL_PROTOCOL_STTAKESASSET_H_INCLUDED
|
||||
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/STBase.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Intermediate class for any STBase-derived class to store an Asset.
|
||||
*
|
||||
* In the class definition, this class should be specified as a base class
|
||||
* _instead_ of STBase.
|
||||
*
|
||||
* Specifically, the Asset is only stored and used at runtime. It should not be
|
||||
* serialized to the ledger.
|
||||
*
|
||||
* The derived class decides what to do with the Asset, and when. It will not
|
||||
* necessarily be set at any given time. As of this writing, only STNumber uses
|
||||
* it to round the stored Number to the Asset's precision both when associated,
|
||||
* and when serializing the Number.
|
||||
*/
|
||||
class STTakesAsset : public STBase
|
||||
{
|
||||
protected:
|
||||
std::optional<Asset> asset_;
|
||||
|
||||
public:
|
||||
using STBase::STBase;
|
||||
using STBase::operator=;
|
||||
|
||||
virtual void
|
||||
associateAsset(Asset const& a);
|
||||
};
|
||||
|
||||
inline void
|
||||
STTakesAsset::associateAsset(Asset const& a)
|
||||
{
|
||||
asset_.emplace(a);
|
||||
}
|
||||
|
||||
class STLedgerEntry;
|
||||
|
||||
/** Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
|
||||
*
|
||||
* This function iterates over all fields in the given ledger entry. For each
|
||||
* field that is set and has the SField::sMD_NeedsAsset metadata flag, it calls
|
||||
* `associateAsset` on that field with the given Asset. Such field must be
|
||||
* derived from STTakesAsset - if it is not, the conversion will throw.
|
||||
*
|
||||
* Typically, associateAsset should be called near the end of doApply() of any
|
||||
* Transactor classes on the SLEs of any new or modified ledger entries
|
||||
* containing STNumber fields, after doing all of the modifications t the SLEs.
|
||||
*
|
||||
* @param sle The ledger entry whose fields will be updated.
|
||||
* @param asset The Asset to associate with the relevant fields.
|
||||
*
|
||||
*/
|
||||
void
|
||||
associateAsset(STLedgerEntry& sle, Asset const& asset);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
@@ -23,6 +23,8 @@ systemName()
|
||||
|
||||
/** Number of drops in the genesis account. */
|
||||
constexpr XRPAmount INITIAL_XRP{100'000'000'000 * DROPS_PER_XRP};
|
||||
static_assert(INITIAL_XRP.drops() == 100'000'000'000'000'000);
|
||||
static_assert(Number::maxRep >= INITIAL_XRP.drops());
|
||||
|
||||
/** Returns true if the amount does not exceed the initial XRP in existence. */
|
||||
inline bool
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
XRPL_FEATURE(SmartEscrow, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (BatchInnerSigs, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(LendingProtocol, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(LendingProtocol, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(PermissionDelegationV1_1, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (DirectoryLimit, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (IncludeKeyletFields, Supported::yes, VoteBehavior::DefaultNo)
|
||||
@@ -32,7 +32,7 @@ XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo
|
||||
XRPL_FIX (AMMv1_3, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(PermissionedDEX, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(Batch, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(SingleAssetVault, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(SingleAssetVault, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (PayChanCancelAfter, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (InvalidTxFlags, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (FrozenLPTokenTransfer, Supported::yes, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -548,7 +548,7 @@ LEDGER_ENTRY(ltLOAN, 0x0089, Loan, loan, ({
|
||||
{sfStartDate, soeREQUIRED},
|
||||
{sfPaymentInterval, soeREQUIRED},
|
||||
{sfGracePeriod, soeDEFAULT},
|
||||
{sfPreviousPaymentDate, soeDEFAULT},
|
||||
{sfPreviousPaymentDueDate, soeDEFAULT},
|
||||
{sfNextPaymentDueDate, soeDEFAULT},
|
||||
// The loan object tracks these values:
|
||||
//
|
||||
|
||||
@@ -102,7 +102,7 @@ TYPED_SFIELD(sfMutableFlags, UINT32, 53)
|
||||
TYPED_SFIELD(sfStartDate, UINT32, 54)
|
||||
TYPED_SFIELD(sfPaymentInterval, UINT32, 55)
|
||||
TYPED_SFIELD(sfGracePeriod, UINT32, 56)
|
||||
TYPED_SFIELD(sfPreviousPaymentDate, UINT32, 57)
|
||||
TYPED_SFIELD(sfPreviousPaymentDueDate, UINT32, 57)
|
||||
TYPED_SFIELD(sfNextPaymentDueDate, UINT32, 58)
|
||||
TYPED_SFIELD(sfPaymentRemaining, UINT32, 59)
|
||||
TYPED_SFIELD(sfPaymentTotal, UINT32, 60)
|
||||
@@ -212,22 +212,22 @@ TYPED_SFIELD(sfLoanID, UINT256, 38)
|
||||
|
||||
// number (common)
|
||||
TYPED_SFIELD(sfNumber, NUMBER, 1)
|
||||
TYPED_SFIELD(sfAssetsAvailable, NUMBER, 2)
|
||||
TYPED_SFIELD(sfAssetsMaximum, NUMBER, 3)
|
||||
TYPED_SFIELD(sfAssetsTotal, NUMBER, 4)
|
||||
TYPED_SFIELD(sfLossUnrealized, NUMBER, 5)
|
||||
TYPED_SFIELD(sfDebtTotal, NUMBER, 6)
|
||||
TYPED_SFIELD(sfDebtMaximum, NUMBER, 7)
|
||||
TYPED_SFIELD(sfCoverAvailable, NUMBER, 8)
|
||||
TYPED_SFIELD(sfAssetsAvailable, NUMBER, 2, SField::sMD_NeedsAsset | SField::sMD_Default)
|
||||
TYPED_SFIELD(sfAssetsMaximum, NUMBER, 3, SField::sMD_NeedsAsset | SField::sMD_Default)
|
||||
TYPED_SFIELD(sfAssetsTotal, NUMBER, 4, SField::sMD_NeedsAsset | SField::sMD_Default)
|
||||
TYPED_SFIELD(sfLossUnrealized, NUMBER, 5, SField::sMD_NeedsAsset | SField::sMD_Default)
|
||||
TYPED_SFIELD(sfDebtTotal, NUMBER, 6, SField::sMD_NeedsAsset | SField::sMD_Default)
|
||||
TYPED_SFIELD(sfDebtMaximum, NUMBER, 7, SField::sMD_NeedsAsset | SField::sMD_Default)
|
||||
TYPED_SFIELD(sfCoverAvailable, NUMBER, 8, SField::sMD_NeedsAsset | SField::sMD_Default)
|
||||
TYPED_SFIELD(sfLoanOriginationFee, NUMBER, 9)
|
||||
TYPED_SFIELD(sfLoanServiceFee, NUMBER, 10)
|
||||
TYPED_SFIELD(sfLatePaymentFee, NUMBER, 11)
|
||||
TYPED_SFIELD(sfClosePaymentFee, NUMBER, 12)
|
||||
TYPED_SFIELD(sfPrincipalOutstanding, NUMBER, 13)
|
||||
TYPED_SFIELD(sfPrincipalOutstanding, NUMBER, 13, SField::sMD_NeedsAsset | SField::sMD_Default)
|
||||
TYPED_SFIELD(sfPrincipalRequested, NUMBER, 14)
|
||||
TYPED_SFIELD(sfTotalValueOutstanding, NUMBER, 15)
|
||||
TYPED_SFIELD(sfTotalValueOutstanding, NUMBER, 15, SField::sMD_NeedsAsset | SField::sMD_Default)
|
||||
TYPED_SFIELD(sfPeriodicPayment, NUMBER, 16)
|
||||
TYPED_SFIELD(sfManagementFeeOutstanding, NUMBER, 17)
|
||||
TYPED_SFIELD(sfManagementFeeOutstanding, NUMBER, 17, SField::sMD_NeedsAsset | SField::sMD_Default)
|
||||
|
||||
// 32-bit signed (common)
|
||||
TYPED_SFIELD(sfLoanScale, INT32, 1)
|
||||
|
||||
Reference in New Issue
Block a user