1#include <xrpl/basics/LocalValue.h>
2#include <xrpl/basics/Log.h>
3#include <xrpl/basics/Number.h>
4#include <xrpl/basics/base_uint.h>
5#include <xrpl/basics/contract.h>
6#include <xrpl/basics/safe_cast.h>
7#include <xrpl/beast/core/LexicalCast.h>
8#include <xrpl/beast/utility/Zero.h>
9#include <xrpl/beast/utility/instrumentation.h>
10#include <xrpl/json/json_forwards.h>
11#include <xrpl/json/json_value.h>
12#include <xrpl/protocol/AccountID.h>
13#include <xrpl/protocol/Asset.h>
14#include <xrpl/protocol/IOUAmount.h>
15#include <xrpl/protocol/Issue.h>
16#include <xrpl/protocol/MPTAmount.h>
17#include <xrpl/protocol/MPTIssue.h>
18#include <xrpl/protocol/Protocol.h>
19#include <xrpl/protocol/SField.h>
20#include <xrpl/protocol/STAmount.h>
21#include <xrpl/protocol/STBase.h>
22#include <xrpl/protocol/STNumber.h>
23#include <xrpl/protocol/Serializer.h>
24#include <xrpl/protocol/SystemParameters.h>
25#include <xrpl/protocol/UintTypes.h>
26#include <xrpl/protocol/XRPAmount.h>
27#include <xrpl/protocol/jss.h>
29#include <boost/algorithm/string/classification.hpp>
30#include <boost/algorithm/string/split.hpp>
31#include <boost/multiprecision/detail/default_ops.hpp>
32#include <boost/multiprecision/fwd.hpp>
33#include <boost/regex/v5/regbase.hpp>
34#include <boost/regex/v5/regex.hpp>
35#include <boost/regex/v5/regex_fwd.hpp>
36#include <boost/regex/v5/regex_match.hpp>
61 Throw<std::runtime_error>(error);
63 amount.exponent() == 0,
"ripple::getInt64Value : exponent is zero");
69 "ripple::getInt64Value : mantissa must roundtrip");
71 if (amount.negative())
80 return getInt64Value(amount, amount.native(),
"amount is not native!");
87 amount, amount.holds<
MPTIssue>(),
"amount is not MPT!");
130 Throw<std::runtime_error>(
"negative zero is not canonical");
142 Throw<std::runtime_error>(
"invalid native currency");
147 Throw<std::runtime_error>(
"invalid native account");
150 int offset =
static_cast<int>(
value >> (64 - 10));
152 value &= ~(1023ull << (64 - 10));
156 bool isNegative = (offset & 256) == 0;
157 offset = (offset & 255) - 97;
162 Throw<std::runtime_error>(
"invalid currency value");
174 Throw<std::runtime_error>(
"invalid currency value");
194 , mIsNegative(negative)
198 "ripple::STAmount::STAmount(SField, std::uint64_t, bool) : maximum "
204 , mAsset(from.mAsset)
205 , mValue(from.mValue)
206 , mOffset(from.mOffset)
207 , mIsNegative(from.mIsNegative)
211 "ripple::STAmount::STAmount(SField, STAmount) : maximum input");
221 , mIsNegative(mantissa != 0 && negative)
225 "ripple::STAmount::STAmount(std::uint64_t, bool) : maximum mantissa "
230 : mAsset(
xrpIssue()), mOffset(0), mIsNegative(amount <
beast::zero)
233 mValue = unsafe_cast<std::uint64_t>(-amount.drops());
235 mValue = unsafe_cast<std::uint64_t>(amount.drops());
255 return emplace(n, buf, std::move(*
this));
267 Throw<std::logic_error>(
268 "Cannot return non-native STAmount as XRPAmount");
271 XRPL_ASSERT(
mOffset == 0,
"ripple::STAmount::xrp : amount is canonical");
283 Throw<std::logic_error>(
"Cannot return non-IOU STAmount as IOUAmount");
297 if (!holds<MPTIssue>())
298 Throw<std::logic_error>(
"Cannot return STAmount as MPTAmount");
301 XRPL_ASSERT(
mOffset == 0,
"ripple::STAmount::mpt : amount is canonical");
314 "ripple::STAmount::operator=(IOUAmount) : is not XRP");
348 Throw<std::runtime_error>(
"Can't add amounts that are't comparable!");
350 if (v2 == beast::zero)
353 if (v1 == beast::zero)
403 if ((fv >= -10) && (fv <= 10))
446 if (offerOut == beast::zero)
451 if (r == beast::zero)
455 "ripple::getRate : exponent inside range");
457 return (ret << (64 - 8)) | r.
mantissa();
493 if (a == beast::zero || b == beast::zero)
521 return ((rhs.
negative() ? -rhs : rhs) +
522 (lhs.
negative() ? -lhs : lhs)) <= maxLoss;
543 UNREACHABLE(
"STAmount::canAdd : unexpected STAmount type");
573 if (b == beast::zero)
618 UNREACHABLE(
"STAmount::canSubtract : unexpected STAmount type");
667 if (*
this == beast::zero)
676 bool const scientific(
692 XRPL_ASSERT(
mOffset + 43 > 0,
"ripple::STAmount::getText : minimum offset");
694 size_t const pad_prefix = 27;
695 size_t const pad_suffix = 23;
699 val.
append(pad_prefix,
'0');
701 val.
append(pad_suffix,
'0');
703 size_t const offset(
mOffset + 43);
705 auto pre_from(val.
begin());
706 auto const pre_to(val.
begin() + offset);
708 auto const post_from(val.
begin() + offset);
709 auto post_to(val.
end());
714 pre_from += pad_prefix;
717 post_to >= post_from,
718 "ripple::STAmount::getText : first distance check");
720 pre_from =
std::find_if(pre_from, pre_to, [](
char c) {
return c !=
'0'; });
725 post_to -= pad_suffix;
728 post_to >= post_from,
729 "ripple::STAmount::getText : second distance check");
734 [](
char c) {
return c !=
'0'; })
738 if (pre_from == pre_to)
741 ret.
append(pre_from, pre_to);
743 if (post_to != post_from)
746 ret.
append(post_from, post_to);
765 XRPL_ASSERT(
mOffset == 0,
"ripple::STAmount::add : zero offset");
774 auto u8 =
static_cast<unsigned char>(
cMPToken >> 56);
776 u8 |=
static_cast<unsigned char>(
cPositive >> 56);
783 if (*
this == beast::zero)
803 return v && (*v == *
this);
847 Throw<std::runtime_error>(
"Native currency amount out of range");
850 Throw<std::runtime_error>(
"MPT amount out of range");
856 auto set = [&](
auto const& val) {
865 Throw<std::runtime_error>(
"Unknown integral asset type");
881 Throw<std::runtime_error>(
882 "Native currency amount out of range");
884 Throw<std::runtime_error>(
"MPT amount out of range");
892 Throw<std::runtime_error>(
"Native currency amount out of range");
894 Throw<std::runtime_error>(
"MPT amount out of range");
921 Throw<std::runtime_error>(
"value overflow");
936 Throw<std::runtime_error>(
"value overflow");
940 "ripple::STAmount::canonicalize : value inside range");
943 "ripple::STAmount::canonicalize : offset inside range");
946 "ripple::STAmount::canonicalize : value or offset set");
973 int exponent =
static_cast<int>(rate >> (64 - 8)) - 100;
983 Throw<std::runtime_error>(
984 "XRP and MPT must be specified as integral amount.");
985 return {asset, parts.mantissa, parts.exponent, parts.negative};
1000 Throw<std::runtime_error>(
1001 "XRP may not be specified with a null Json value");
1006 Throw<std::runtime_error>(
"Invalid Asset's Json specification");
1008 value = v[jss::value];
1009 if (v.
isMember(jss::mpt_issuance_id))
1012 currencyOrMPTID = v[jss::mpt_issuance_id];
1016 currencyOrMPTID = v[jss::currency];
1017 issuer = v[jss::issuer];
1030 boost::split(elements, val, boost::is_any_of(
"\t\n\r ,/"));
1032 if (elements.
size() > 3)
1033 Throw<std::runtime_error>(
"invalid amount string");
1035 value = elements[0];
1037 if (elements.
size() > 1)
1038 currencyOrMPTID = elements[1];
1040 if (elements.
size() > 2)
1041 issuer = elements[2];
1048 bool const native = !currencyOrMPTID.
isString() ||
1055 Throw<std::runtime_error>(
"XRP may not be specified as an object");
1065 Throw<std::runtime_error>(
"invalid MPTokenIssuanceID");
1072 Throw<std::runtime_error>(
"invalid currency");
1075 Throw<std::runtime_error>(
"invalid issuer");
1077 Throw<std::runtime_error>(
"invalid issuer");
1086 if (value.
asInt() >= 0)
1105 Throw<std::runtime_error>(
1106 "XRP and MPT must be specified as integral amount.");
1110 Throw<std::runtime_error>(
"invalid amount type");
1127 <<
"amountFromJsonNoThrow: caught: " << e.
what();
1149 Throw<std::runtime_error>(
1150 "Can't compare amounts that are't comparable!");
1208 boost::multiprecision::uint128_t ret;
1210 boost::multiprecision::multiply(ret, multiplier, multiplicand);
1215 Throw<std::overflow_error>(
1220 return static_cast<uint64_t
>(ret);
1230 boost::multiprecision::uint128_t ret;
1232 boost::multiprecision::multiply(ret, multiplier, multiplicand);
1238 Throw<std::overflow_error>(
1244 return static_cast<uint64_t
>(ret);
1250 if (den == beast::zero)
1251 Throw<std::runtime_error>(
"division by zero");
1253 if (num == beast::zero)
1288 numOffset - denOffset - 17,
1295 if (v1 == beast::zero || v2 == beast::zero)
1303 if (minV > 3000000000ull)
1304 Throw<std::runtime_error>(
"Native value overflow");
1306 if (((maxV >> 32) * minV) > 2095475792ull)
1307 Throw<std::runtime_error>(
"Native value overflow");
1316 if (minV > 3037000499ull)
1317 Throw<std::runtime_error>(
"MPT value overflow");
1319 if (((maxV >> 32) * minV) > 2147483648ull)
1320 Throw<std::runtime_error>(
"MPT value overflow");
1322 return STAmount(asset, minV * maxV);
1328 return STAmount{asset, r.mantissa(), r.exponent()};
1361 offset1 + offset2 + 14,
1401 value += (loops >= 2) ? 9 : 10;
1435 bool hadRemainder =
false;
1443 hadRemainder |= (value != (newValue * 10));
1448 (hadRemainder && roundUp) ? 10 : 9;
1490 return (value + referenceValue) - referenceValue;
1497class DontAffectNumberRoundMode
1504 DontAffectNumberRoundMode(DontAffectNumberRoundMode
const&) =
delete;
1506 DontAffectNumberRoundMode&
1507 operator=(DontAffectNumberRoundMode
const&) =
delete;
1518 typename MightSaveRound>
1526 if (v1 == beast::zero || v2 == beast::zero)
1529 bool const xrp = asset.
native();
1536 if (minV > 3000000000ull)
1537 Throw<std::runtime_error>(
"Native value overflow");
1539 if (((maxV >> 32) * minV) > 2095475792ull)
1540 Throw<std::runtime_error>(
"Native value overflow");
1550 if (minV > 3037000499ull)
1551 Throw<std::runtime_error>(
"MPT value overflow");
1553 if (((maxV >> 32) * minV) > 2147483648ull)
1554 Throw<std::runtime_error>(
"MPT value overflow");
1556 return STAmount(asset, minV * maxV);
1593 int offset = offset1 + offset2 + 14;
1594 if (resultNegative != roundUp)
1596 CanonicalizeFunc(xrp, amount, offset, roundUp);
1602 return STAmount(asset, amount, offset, resultNegative);
1605 if (roundUp && !resultNegative && !result)
1619 return STAmount(asset, amount, offset, resultNegative);
1631 return mulRoundImpl<canonicalizeRound, DontAffectNumberRoundMode>(
1632 v1, v2, asset, roundUp);
1642 return mulRoundImpl<canonicalizeRoundStrict, NumberRoundModeGuard>(
1643 v1, v2, asset, roundUp);
1648template <
typename MightSaveRound>
1656 if (den == beast::zero)
1657 Throw<std::runtime_error>(
"division by zero");
1659 if (num == beast::zero)
1694 numVal,
tenTo17, denVal, (resultNegative != roundUp) ? denVal - 1 : 0);
1696 int offset = numOffset - denOffset - 17;
1698 if (resultNegative != roundUp)
1707 MightSaveRound
const savedRound(
1708 roundUp ^ resultNegative ? upward : downward);
1709 return STAmount(asset, amount, offset, resultNegative);
1712 if (roundUp && !resultNegative && !result)
1726 return STAmount(asset, amount, offset, resultNegative);
1738 return divRoundImpl<DontAffectNumberRoundMode>(num, den, asset, roundUp);
1748 return divRoundImpl<NumberRoundModeGuard>(num, den, asset, roundUp);
bool isObjectOrNull() const
UInt asAbsUInt() const
Correct absolute value from int or unsigned int.
std::string asString() const
Returns the unquoted string value.
bool isNull() const
isNull() tests to see if this field is null.
bool isMember(char const *key) const
Return true if the object has a member named key.
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
void setJson(Json::Value &jv) const
constexpr TIss const & get() const
constexpr bool holds() const
std::string getText() const
Floating point representation of amounts with high dynamic range.
int exponent() const noexcept
std::int64_t mantissa() const noexcept
A currency issued by an account.
constexpr value_type value() const
Returns the underlying value.
constexpr MPTID const & getMptID() const
constexpr bool holds() const noexcept
Json::Value getJson(JsonOptions=JsonOptions::none) const override
STAmount & operator=(beast::Zero)
int exponent() const noexcept
STBase * move(std::size_t n, void *buf) override
static constexpr std::uint64_t cValueMask
static constexpr std::uint64_t cPositive
Asset const & asset() const
constexpr TIss const & get() const
static int const cMaxOffset
static int const cMinOffset
static constexpr std::uint64_t cMPToken
STAmount & operator+=(STAmount const &)
static constexpr std::uint64_t cMinValue
static std::unique_ptr< STAmount > construct(SerialIter &, SField const &name)
void setIssue(Asset const &asset)
Set the Issue for this amount.
void setJson(Json::Value &) const
bool isDefault() const override
STAmount & operator-=(STAmount const &)
void add(Serializer &s) const override
SerializedTypeID getSType() const override
STAmount const & value() const noexcept
std::string getText() const override
bool negative() const noexcept
bool isEquivalent(STBase const &t) const override
static constexpr std::uint64_t cIssuedCurrency
static constexpr std::uint64_t cMaxValue
STBase * copy(std::size_t n, void *buf) const override
Issue const & issue() const
static constexpr std::uint64_t cMaxNativeN
std::uint64_t mantissa() const noexcept
std::string getFullText() const override
STAmount(SerialIter &sit, SField const &name)
bool native() const noexcept
bool integral() const noexcept
static std::uint64_t const uRateOne
A type which can be exported to a well known binary format.
SField const & getFName() const
static STBase * emplace(std::size_t n, void *buf, T &&val)
int addBitString(base_uint< Bits, Tag > const &v)
int add8(unsigned char i)
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
T make_reverse_iterator(T... args)
@ objectValue
object value (collection of name/value pairs).
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
static void canonicalizeRoundStrict(bool native, std::uint64_t &value, int &offset, bool roundUp)
STAmount divide(STAmount const &amount, Rate const &rate)
NumberParts partsFromString(std::string const &number)
bool isXRP(AccountID const &c)
bool canSubtract(STAmount const &amt1, STAmount const &amt2)
Determines if it is safe to subtract one STAmount from another.
bool to_issuer(AccountID &, std::string const &)
Convert hex or base58 string to AccountID.
static bool areComparable(STAmount const &v1, STAmount const &v2)
STAmount divRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
STAmount amountFromJson(SField const &name, Json::Value const &v)
bool canAdd(STAmount const &amt1, STAmount const &amt2)
Safely checks if two STAmount values can be added without overflow, underflow, or precision loss.
static std::uint64_t const tenTo17
std::uint64_t constexpr maxMPTokenAmount
The maximum amount of MPTokenIssuance.
STAmount amountFromQuality(std::uint64_t rate)
static std::string const & systemCurrencyCode()
bool set(T &target, std::string const &name, Section const §ion)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
static std::uint64_t const tenTo14m1
STAmount multiply(STAmount const &amount, Rate const &rate)
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
static std::int64_t getSNValue(STAmount const &amount)
bool validJSONAsset(Json::Value const &jv)
beast::Journal debugLog()
Returns a debug journal.
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
static std::int64_t getInt64Value(STAmount const &amount, bool valid, char const *error)
bool amountFromJsonNoThrow(STAmount &result, Json::Value const &jvSource)
static std::int64_t getMPTValue(STAmount const &amount)
bool operator<(Slice const &lhs, Slice const &rhs) noexcept
STAmount roundToScale(STAmount const &value, std::int32_t scale, Number::rounding_mode rounding=Number::getround())
Round an arbitrary precision Amount to the precision of an STAmount that has a given exponent.
STAmount divRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
static std::uint64_t muldiv(std::uint64_t multiplier, std::uint64_t multiplicand, std::uint64_t divisor)
Number operator-(Number const &x, Number const &y)
STAmount amountFromString(Asset const &asset, std::string const &amount)
STAmount mulRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
constexpr base_uint< Bits, Tag > operator+(base_uint< Bits, Tag > const &a, base_uint< Bits, Tag > const &b)
static STAmount mulRoundImpl(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
constexpr bool operator==(base_uint< Bits, Tag > const &lhs, base_uint< Bits, Tag > const &rhs)
static STAmount divRoundImpl(STAmount const &num, STAmount const &den, Asset const &asset, bool roundUp)
STAmount mulRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
static std::uint64_t muldiv_round(std::uint64_t multiplier, std::uint64_t multiplicand, std::uint64_t divisor, std::uint64_t rounding)
static std::uint64_t const tenTo14
bool getSTNumberSwitchover()
bool to_currency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
static void canonicalizeRound(bool native, std::uint64_t &value, int &offset, bool)
Note, should be treated as flags that can be | and &.