20#include <xrpl/basics/LocalValue.h>
21#include <xrpl/basics/Log.h>
22#include <xrpl/basics/Number.h>
23#include <xrpl/basics/base_uint.h>
24#include <xrpl/basics/contract.h>
25#include <xrpl/basics/safe_cast.h>
26#include <xrpl/beast/core/LexicalCast.h>
27#include <xrpl/beast/utility/Zero.h>
28#include <xrpl/beast/utility/instrumentation.h>
29#include <xrpl/json/json_forwards.h>
30#include <xrpl/json/json_value.h>
31#include <xrpl/protocol/AccountID.h>
32#include <xrpl/protocol/Asset.h>
33#include <xrpl/protocol/IOUAmount.h>
34#include <xrpl/protocol/Issue.h>
35#include <xrpl/protocol/MPTAmount.h>
36#include <xrpl/protocol/MPTIssue.h>
37#include <xrpl/protocol/Protocol.h>
38#include <xrpl/protocol/SField.h>
39#include <xrpl/protocol/STAmount.h>
40#include <xrpl/protocol/STBase.h>
41#include <xrpl/protocol/STNumber.h>
42#include <xrpl/protocol/Serializer.h>
43#include <xrpl/protocol/SystemParameters.h>
44#include <xrpl/protocol/UintTypes.h>
45#include <xrpl/protocol/XRPAmount.h>
46#include <xrpl/protocol/jss.h>
48#include <boost/algorithm/string/classification.hpp>
49#include <boost/algorithm/string/split.hpp>
50#include <boost/multiprecision/detail/default_ops.hpp>
51#include <boost/multiprecision/fwd.hpp>
52#include <boost/regex/v5/regbase.hpp>
53#include <boost/regex/v5/regex.hpp>
54#include <boost/regex/v5/regex_fwd.hpp>
55#include <boost/regex/v5/regex_match.hpp>
75getStaticSTAmountCanonicalizeSwitchover()
77 static LocalValue<bool> r{
true};
85 return *getStaticSTAmountCanonicalizeSwitchover();
91 *getStaticSTAmountCanonicalizeSwitchover() = v;
103 Throw<std::runtime_error>(error);
105 amount.
exponent() == 0,
"ripple::getInt64Value : exponent is zero");
111 "ripple::getInt64Value : mantissa must roundtrip");
172 Throw<std::runtime_error>(
"negative zero is not canonical");
184 Throw<std::runtime_error>(
"invalid native currency");
189 Throw<std::runtime_error>(
"invalid native account");
192 int offset =
static_cast<int>(
value >> (64 - 10));
194 value &= ~(1023ull << (64 - 10));
198 bool isNegative = (offset & 256) == 0;
199 offset = (offset & 255) - 97;
204 Throw<std::runtime_error>(
"invalid currency value");
216 Throw<std::runtime_error>(
"invalid currency value");
236 , mIsNegative(negative)
240 "ripple::STAmount::STAmount(SField, std::uint64_t, bool) : maximum "
246 , mAsset(from.mAsset)
247 , mValue(from.mValue)
248 , mOffset(from.mOffset)
249 , mIsNegative(from.mIsNegative)
253 "ripple::STAmount::STAmount(SField, STAmount) : maximum input");
263 , mIsNegative(mantissa != 0 && negative)
267 "ripple::STAmount::STAmount(std::uint64_t, bool) : maximum mantissa "
272 : mAsset(
xrpIssue()), mOffset(0), mIsNegative(amount <
beast::zero)
275 mValue = unsafe_cast<std::uint64_t>(-amount.
drops());
277 mValue = unsafe_cast<std::uint64_t>(amount.
drops());
285 return std::make_unique<STAmount>(sit, name);
297 return emplace(n, buf, std::move(*
this));
309 Throw<std::logic_error>(
310 "Cannot return non-native STAmount as XRPAmount");
313 XRPL_ASSERT(
mOffset == 0,
"ripple::STAmount::xrp : amount is canonical");
324 if (
native() || !holds<Issue>())
325 Throw<std::logic_error>(
"Cannot return non-IOU STAmount as IOUAmount");
339 if (!holds<MPTIssue>())
340 Throw<std::logic_error>(
"Cannot return STAmount as MPTAmount");
343 XRPL_ASSERT(
mOffset == 0,
"ripple::STAmount::mpt : amount is canonical");
356 "ripple::STAmount::operator=(IOUAmount) : is not XRP");
390 Throw<std::runtime_error>(
"Can't add amounts that are't comparable!");
392 if (v2 == beast::zero)
395 if (v1 == beast::zero)
445 if ((fv >= -10) && (fv <= 10))
488 if (offerOut == beast::zero)
493 if (r == beast::zero)
497 "ripple::getRate : exponent inside range");
499 return (ret << (64 - 8)) | r.
mantissa();
553 if (*
this == beast::zero)
562 bool const scientific(
578 XRPL_ASSERT(
mOffset + 43 > 0,
"ripple::STAmount::getText : minimum offset");
580 size_t const pad_prefix = 27;
581 size_t const pad_suffix = 23;
585 val.
append(pad_prefix,
'0');
587 val.
append(pad_suffix,
'0');
589 size_t const offset(
mOffset + 43);
591 auto pre_from(val.
begin());
592 auto const pre_to(val.
begin() + offset);
594 auto const post_from(val.
begin() + offset);
595 auto post_to(val.
end());
600 pre_from += pad_prefix;
603 post_to >= post_from,
604 "ripple::STAmount::getText : first distance check");
606 pre_from =
std::find_if(pre_from, pre_to, [](
char c) {
return c !=
'0'; });
611 post_to -= pad_suffix;
614 post_to >= post_from,
615 "ripple::STAmount::getText : second distance check");
620 [](
char c) {
return c !=
'0'; })
624 if (pre_from == pre_to)
627 ret.
append(pre_from, pre_to);
629 if (post_to != post_from)
632 ret.
append(post_from, post_to);
651 XRPL_ASSERT(
mOffset == 0,
"ripple::STAmount::add : zero offset");
660 auto u8 =
static_cast<unsigned char>(
cMPToken >> 56);
662 u8 |=
static_cast<unsigned char>(
cPositive >> 56);
669 if (*
this == beast::zero)
689 return v && (*v == *
this);
735 Throw<std::runtime_error>(
736 "Native currency amount out of range");
739 Throw<std::runtime_error>(
"MPT amount out of range");
746 auto set = [&](
auto const& val) {
771 Throw<std::runtime_error>(
772 "Native currency amount out of range");
774 Throw<std::runtime_error>(
"MPT amount out of range");
782 Throw<std::runtime_error>(
"Native currency amount out of range");
784 Throw<std::runtime_error>(
"MPT amount out of range");
811 Throw<std::runtime_error>(
"value overflow");
826 Throw<std::runtime_error>(
"value overflow");
830 "ripple::STAmount::canonicalize : value inside range");
833 "ripple::STAmount::canonicalize : offset inside range");
836 "ripple::STAmount::canonicalize : value or offset set");
863 int exponent =
static_cast<int>(rate >> (64 - 8)) - 100;
873 Throw<std::runtime_error>(
874 "XRP and MPT must be specified as integral amount.");
875 return {asset, parts.mantissa, parts.exponent, parts.negative};
890 Throw<std::runtime_error>(
891 "XRP may not be specified with a null Json value");
896 Throw<std::runtime_error>(
"Invalid Asset's Json specification");
898 value = v[jss::value];
899 if (v.
isMember(jss::mpt_issuance_id))
902 currencyOrMPTID = v[jss::mpt_issuance_id];
906 currencyOrMPTID = v[jss::currency];
907 issuer = v[jss::issuer];
920 boost::split(elements, val, boost::is_any_of(
"\t\n\r ,/"));
922 if (elements.
size() > 3)
923 Throw<std::runtime_error>(
"invalid amount string");
927 if (elements.
size() > 1)
928 currencyOrMPTID = elements[1];
930 if (elements.
size() > 2)
931 issuer = elements[2];
938 bool const native = !currencyOrMPTID.
isString() ||
945 Throw<std::runtime_error>(
"XRP may not be specified as an object");
955 Throw<std::runtime_error>(
"invalid MPTokenIssuanceID");
962 Throw<std::runtime_error>(
"invalid currency");
965 Throw<std::runtime_error>(
"invalid issuer");
967 Throw<std::runtime_error>(
"invalid issuer");
976 if (value.
asInt() >= 0)
995 Throw<std::runtime_error>(
996 "XRP and MPT must be specified as integral amount.");
1000 Throw<std::runtime_error>(
"invalid amount type");
1017 <<
"amountFromJsonNoThrow: caught: " << e.
what();
1039 Throw<std::runtime_error>(
1040 "Can't compare amounts that are't comparable!");
1098 boost::multiprecision::uint128_t ret;
1100 boost::multiprecision::multiply(ret, multiplier, multiplicand);
1105 Throw<std::overflow_error>(
1110 return static_cast<uint64_t
>(ret);
1120 boost::multiprecision::uint128_t ret;
1122 boost::multiprecision::multiply(ret, multiplier, multiplicand);
1128 Throw<std::overflow_error>(
1134 return static_cast<uint64_t
>(ret);
1140 if (den == beast::zero)
1141 Throw<std::runtime_error>(
"division by zero");
1143 if (num == beast::zero)
1178 numOffset - denOffset - 17,
1185 if (v1 == beast::zero || v2 == beast::zero)
1193 if (minV > 3000000000ull)
1194 Throw<std::runtime_error>(
"Native value overflow");
1196 if (((maxV >> 32) * minV) > 2095475792ull)
1197 Throw<std::runtime_error>(
"Native value overflow");
1206 if (minV > 3037000499ull)
1207 Throw<std::runtime_error>(
"MPT value overflow");
1209 if (((maxV >> 32) * minV) > 2147483648ull)
1210 Throw<std::runtime_error>(
"MPT value overflow");
1212 return STAmount(asset, minV * maxV);
1218 return STAmount{asset, r.mantissa(), r.exponent()};
1251 offset1 + offset2 + 14,
1291 value += (loops >= 2) ? 9 : 10;
1325 bool hadRemainder =
false;
1333 hadRemainder |= (value != (newValue * 10));
1338 (hadRemainder && roundUp) ? 10 : 9;
1360class DontAffectNumberRoundMode
1367 DontAffectNumberRoundMode(DontAffectNumberRoundMode
const&) =
delete;
1369 DontAffectNumberRoundMode&
1370 operator=(DontAffectNumberRoundMode
const&) =
delete;
1381 typename MightSaveRound>
1389 if (v1 == beast::zero || v2 == beast::zero)
1392 bool const xrp = asset.
native();
1399 if (minV > 3000000000ull)
1400 Throw<std::runtime_error>(
"Native value overflow");
1402 if (((maxV >> 32) * minV) > 2095475792ull)
1403 Throw<std::runtime_error>(
"Native value overflow");
1413 if (minV > 3037000499ull)
1414 Throw<std::runtime_error>(
"MPT value overflow");
1416 if (((maxV >> 32) * minV) > 2147483648ull)
1417 Throw<std::runtime_error>(
"MPT value overflow");
1419 return STAmount(asset, minV * maxV);
1456 int offset = offset1 + offset2 + 14;
1457 if (resultNegative != roundUp)
1459 CanonicalizeFunc(xrp, amount, offset, roundUp);
1465 return STAmount(asset, amount, offset, resultNegative);
1468 if (roundUp && !resultNegative && !result)
1482 return STAmount(asset, amount, offset, resultNegative);
1494 return mulRoundImpl<canonicalizeRound, DontAffectNumberRoundMode>(
1495 v1, v2, asset, roundUp);
1505 return mulRoundImpl<canonicalizeRoundStrict, NumberRoundModeGuard>(
1506 v1, v2, asset, roundUp);
1511template <
typename MightSaveRound>
1519 if (den == beast::zero)
1520 Throw<std::runtime_error>(
"division by zero");
1522 if (num == beast::zero)
1557 numVal,
tenTo17, denVal, (resultNegative != roundUp) ? denVal - 1 : 0);
1559 int offset = numOffset - denOffset - 17;
1561 if (resultNegative != roundUp)
1570 MightSaveRound
const savedRound(
1571 roundUp ^ resultNegative ? upward : downward);
1572 return STAmount(asset, amount, offset, resultNegative);
1575 if (roundUp && !resultNegative && !result)
1589 return STAmount(asset, amount, offset, resultNegative);
1601 return divRoundImpl<DontAffectNumberRoundMode>(num, den, asset, roundUp);
1611 return divRoundImpl<NumberRoundModeGuard>(num, den, asset, roundUp);
bool isObjectOrNull() const
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
static std::uint64_t const cMaxNativeN
STAmount & operator=(beast::Zero)
int exponent() const noexcept
STBase * move(std::size_t n, void *buf) override
Asset const & asset() const
static std::uint64_t const cPositive
constexpr TIss const & get() const
static int const cMaxOffset
static int const cMinOffset
STAmount & operator+=(STAmount const &)
static std::uint64_t const 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
static std::uint64_t const cIssuedCurrency
static std::uint64_t const cMPToken
SerializedTypeID getSType() const override
static std::uint64_t const cMaxValue
STAmount const & value() const noexcept
std::string getText() const override
bool negative() const noexcept
bool isEquivalent(STBase const &t) const override
STBase * copy(std::size_t n, void *buf) const override
static std::uint64_t const cValueMask
Issue const & issue() const
std::uint64_t mantissa() const noexcept
std::string getFullText() const override
STAmount(SerialIter &sit, SField const &name)
bool native() 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 value_type drops() const
Returns the number of drops.
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(PreclaimContext const &ctx, AccountID const &src)
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 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)
static std::uint64_t const tenTo17
std::uint64_t constexpr maxMPTokenAmount
The maximum amount of MPTokenIssuance.
STAmount amountFromQuality(std::uint64_t rate)
bool getSTAmountCanonicalizeSwitchover()
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
void setSTAmountCanonicalizeSwitchover(bool v)
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 &.