1#include <xrpl/protocol/STAmount.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Number.h>
5#include <xrpl/basics/contract.h>
6#include <xrpl/basics/safe_cast.h>
7#include <xrpl/beast/utility/Zero.h>
8#include <xrpl/beast/utility/instrumentation.h>
9#include <xrpl/json/json_forwards.h>
10#include <xrpl/json/json_value.h>
11#include <xrpl/protocol/AccountID.h>
12#include <xrpl/protocol/Asset.h>
13#include <xrpl/protocol/Concepts.h>
14#include <xrpl/protocol/Feature.h>
15#include <xrpl/protocol/IOUAmount.h>
16#include <xrpl/protocol/Issue.h>
17#include <xrpl/protocol/MPTAmount.h>
18#include <xrpl/protocol/MPTIssue.h>
19#include <xrpl/protocol/Protocol.h>
20#include <xrpl/protocol/Rules.h>
21#include <xrpl/protocol/SField.h>
22#include <xrpl/protocol/STArray.h>
23#include <xrpl/protocol/STBase.h>
24#include <xrpl/protocol/STNumber.h>
25#include <xrpl/protocol/STObject.h>
26#include <xrpl/protocol/Serializer.h>
27#include <xrpl/protocol/SystemParameters.h>
28#include <xrpl/protocol/UintTypes.h>
29#include <xrpl/protocol/XRPAmount.h>
30#include <xrpl/protocol/jss.h>
32#include <boost/algorithm/string/classification.hpp>
33#include <boost/algorithm/string/split.hpp>
34#include <boost/multiprecision/detail/default_ops.hpp>
63 XRPL_ASSERT(amount.
exponent() == 0,
"xrpl::getInt64Value : exponent is zero");
69 "xrpl::getInt64Value : mantissa must roundtrip");
96 return v1.
native() == v2.
native() && issue1.currency == issue2.currency;
100 return issue1 == issue2;
162 int offset =
static_cast<int>(
value >> (64 - 10));
164 value &= ~(1023ull << (64 - 10));
168 bool const isNegative = (offset & 256) == 0;
169 offset = (offset & 255) - 97;
205 "xrpl::STAmount::STAmount(SField, std::uint64_t, bool) : maximum "
218 "xrpl::STAmount::STAmount(SField, STAmount) : maximum input");
229 "xrpl::STAmount::STAmount(std::uint64_t, bool) : maximum mantissa "
263 return emplace(n, buf, std::move(*
this));
278 XRPL_ASSERT(
offset_ == 0,
"xrpl::STAmount::xrp : amount is canonical");
308 XRPL_ASSERT(
offset_ == 0,
"xrpl::STAmount::mpt : amount is canonical");
319 XRPL_ASSERT(
integral() ==
false,
"xrpl::STAmount::operator=(IOUAmount) : is not integral");
343 auto const originalMantissa = number.
mantissa();
435 "xrpl::getRate : exponent inside range");
437 return (ret << (64 - 8)) | r.
mantissa();
498 return ((rhs.
negative() ? -rhs : rhs) + (lhs.
negative() ? -lhs : lhs)) <= kMaxLoss;
519 UNREACHABLE(
"STAmount::canAdd : unexpected STAmount type");
601 UNREACHABLE(
"STAmount::canSubtract : unexpected STAmount type");
674 XRPL_ASSERT(
offset_ + 43 > 0,
"xrpl::STAmount::getText : minimum offset");
676 size_t const padPrefix = 27;
677 size_t const padSuffix = 23;
681 val.
append(padPrefix,
'0');
683 val.
append(padSuffix,
'0');
685 size_t const offset(
offset_ + 43);
687 auto preFrom(val.
begin());
688 auto const preTo(val.
begin() + offset);
690 auto const postFrom(val.
begin() + offset);
691 auto postTo(val.
end());
696 preFrom += padPrefix;
698 XRPL_ASSERT(postTo >= postFrom,
"xrpl::STAmount::getText : first distance check");
700 preFrom =
std::find_if(preFrom, preTo, [](
char c) {
return c !=
'0'; });
707 XRPL_ASSERT(postTo >= postFrom,
"xrpl::STAmount::getText : second distance check");
712 [](
char c) {
return c !=
'0'; })
716 if (preFrom == preTo)
722 ret.
append(preFrom, preTo);
725 if (postTo != postFrom)
728 ret.
append(postFrom, postTo);
747 auto u8 =
static_cast<unsigned char>(
kMpToken >> 56);
749 u8 |=
static_cast<unsigned char>(
kPositive >> 56);
754 [&](
Issue const& issue) {
757 XRPL_ASSERT(
offset_ == 0,
"xrpl::STAmount::add : zero offset");
793 auto const* v =
dynamic_cast<STAmount const*
>(&t);
794 return (v !=
nullptr) && (*v == *
this);
844 auto set = [&](
auto const& val) {
845 auto const value = val.value();
902 int const exponent =
static_cast<int>(rate >> (64 - 8)) - 100;
913 return {asset, parts.mantissa, parts.exponent, parts.negative};
935 value = v[jss::value];
936 if (v.
isMember(jss::mpt_issuance_id))
939 currencyOrMPTID = v[jss::mpt_issuance_id];
943 currencyOrMPTID = v[jss::currency];
944 issuer = v[jss::issuer];
957 boost::split(elements, val, boost::is_any_of(
"\t\n\r ,/"));
959 if (elements.
size() > 3)
964 if (elements.
size() > 1)
965 currencyOrMPTID = elements[1];
967 if (elements.
size() > 2)
968 issuer = elements[2];
1011 if (value.asInt() >= 0)
1017 parts.
mantissa = value.asAbsUInt();
1021 else if (value.isUInt())
1025 else if (value.isString())
1050 JLOG(
debugLog().warn()) <<
"amountFromJsonNoThrow: caught: " << e.
what();
1105 if (value.mantissa() == 0)
1138 JLOG(j.
error()) <<
"hasInvalidAmount: depth exceeds 10";
1156 case STI_LEDGERENTRY:
1157 case STI_TRANSACTION:
1165 dynamic_cast<STObject const*
>(&field) ==
nullptr,
1166 "xrpl::hasInvalidAmount : unhandled STObject type");
1189 boost::multiprecision::uint128_t ret;
1191 boost::multiprecision::multiply(ret, multiplier, multiplicand);
1197 std::format(
"overflow: ({} * {}) / {}", multiplier, multiplicand, divisor));
1200 return static_cast<uint64_t
>(ret);
1210 boost::multiprecision::uint128_t ret;
1212 boost::multiprecision::multiply(ret, multiplier, multiplicand);
1219 "overflow: (({} * {}) + {}) / {}", multiplier, multiplicand, rounding, divisor));
1222 return static_cast<uint64_t
>(ret);
1266 numOffset - denOffset - 17,
1281 if (minV > 3000000000ull)
1284 if (((maxV >> 32) * minV) > 2095475792ull)
1294 if (minV > 3037000499ull)
1297 if (((maxV >> 32) * minV) > 2147483648ull)
1300 return STAmount(asset, minV * maxV);
1343 value += (loops >= 2) ? 9 : 10;
1373 bool hadRemainder =
false;
1381 hadRemainder |= (value != (newValue * 10));
1385 value += (hadRemainder && roundUp) ? 10 : 9;
1407 if (value.integral())
1417 if (value.exponent() >=
scale)
1427 return (value + referenceValue) - referenceValue;
1434class DontAffectNumberRoundMode
1441 DontAffectNumberRoundMode(DontAffectNumberRoundMode
const&) =
delete;
1443 DontAffectNumberRoundMode&
1444 operator=(DontAffectNumberRoundMode
const&) =
delete;
1448roundMode(
bool const resultNegative,
bool const roundUp)
1459 return roundUp ^ resultNegative ? Upward : Downward;
1465 bool const resultNegative,
1472 auto result =
STAmount{asset, number};
1473 [[maybe_unused]]
bool const nonzeroPositiveRoundUp =
1477 "xrpl::roundNumberResult : positive rounded-up MPT result is representable");
1479 if (roundUp && !resultNegative && !result)
1491 if (asset.integral())
1506template <
void (*CanonicalizeFunc)(
bool, std::u
int64_t&,
int&,
bool),
typename MightSaveRound>
1518 if (minV > 3000000000ull)
1521 if (((maxV >> 32) * minV) > 2095475792ull)
1532 if (minV > 3037000499ull)
1535 if (((maxV >> 32) * minV) > 2147483648ull)
1538 return STAmount(asset, minV * maxV);
1554 return roundNumberResult(asset, resultNegative, roundUp, result);
1588 int offset = offset1 + offset2 + 14;
1589 if (resultNegative != roundUp)
1591 CanonicalizeFunc(asset.
integral(), amount, offset, roundUp);
1597 return STAmount(asset, amount, offset, resultNegative);
1600 if (roundUp && !resultNegative && !result)
1614 return STAmount(asset, amount, offset, resultNegative);
1633template <
typename MightSaveRound>
1656 return roundNumberResult(asset, resultNegative, roundUp, result);
1691 int offset = numOffset - denOffset - 17;
1693 if (resultNegative != roundUp)
1701 MightSaveRound
const savedRound(roundUp ^ resultNegative ? Upward : Downward);
1702 return STAmount(asset, amount, offset, resultNegative);
1705 if (roundUp && !resultNegative && !result)
1719 return STAmount(asset, amount, offset, resultNegative);
A generic endpoint for log messages.
bool isNull() const
isNull() tests to see if this field is null.
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
std::string asString() const
Returns the unquoted string value.
bool isObjectOrNull() const
bool isMember(char const *key) const
Return true if the object has a member named key.
constexpr bool native() const
constexpr bool holds() const
constexpr value_type const & value() const
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Floating point representation of amounts with high dynamic range.
A currency issued by an account.
constexpr value_type value() const
Returns the underlying value.
constexpr MPTID const & getMptID() const
Number is a floating point type that can represent a wide range of values.
constexpr rep mantissa() const noexcept
Returns the mantissa of the external view of the Number.
constexpr int exponent() const noexcept
Returns the exponent of the external view of the Number.
constexpr bool holds() const noexcept
void setIssue(Asset const &asset)
Set the Issue for this amount.
static constexpr std::uint64_t kIssuedCurrency
std::string getFullText() const override
static STAmount fromNumber(A const &asset, Number const &number)
void add(Serializer &s) const override
static std::uint64_t const kURateOne
std::uint64_t mantissa() const noexcept
static constexpr std::uint64_t kPositive
int signum() const noexcept
bool isEquivalent(STBase const &t) const override
STBase * copy(std::size_t n, void *buf) const override
std::string getText() const override
static constexpr int kMinOffset
SerializedTypeID getSType() const override
bool negative() const noexcept
static constexpr std::uint64_t kMaxNativeN
bool isDefault() const override
bool integral() const noexcept
static constexpr std::uint64_t kValueMask
STAmount & operator=(beast::Zero)
static std::unique_ptr< STAmount > construct(SerialIter &, SField const &name)
bool native() const noexcept
Asset const & asset() const
STAmount & operator+=(STAmount const &)
void setJson(json::Value &) const
static constexpr std::uint64_t kMpToken
json::Value getJson(JsonOptions=JsonOptions::Values::None) const override
STAmount & operator-=(STAmount const &)
int exponent() const noexcept
bool isZeroAtScale(int scale) const
Checks if this amount evaluates to zero when constrained to a specific accounting scale.
static constexpr std::uint64_t kMinValue
static constexpr std::uint64_t kMaxValue
STBase * move(std::size_t n, void *buf) override
static constexpr int kMaxOffset
STAmount const & value() const noexcept
STAmount(SerialIter &sit, SField const &name)
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)
virtual SerializedTypeID getSType() const
int addBitString(BaseUInt< Bits, Tag > const &v)
int add8(unsigned char byteValue)
constexpr value_type drops() const
Returns the number of drops.
T make_reverse_iterator(T... args)
@ Object
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.
constexpr BaseUInt< Bits, Tag > operator+(BaseUInt< Bits, Tag > const &a, BaseUInt< Bits, Tag > const &b)
static std::int64_t getMPTValue(STAmount const &amount)
STAmount divide(STAmount const &amount, Rate const &rate)
bool operator<(Slice const &lhs, Slice const &rhs) noexcept
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
static STAmount mulRoundImpl(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
constexpr bool operator==(BaseUInt< Bits, Tag > const &lhs, BaseUInt< Bits, Tag > const &rhs)
bool isLegalMPT(STAmount const &value)
bool isXRP(AccountID const &c)
beast::Journal debugLog()
Returns a debug journal.
NumberParts partsFromString(std::string const &number)
Number operator-(Number const &x, Number const &y)
STAmount mulRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
bool isFeatureEnabled(UInt256 const &feature, bool resultIfNoRules)
Check whether a feature is enabled in the current ledger rules.
int scale(Number const &number, Asset const &asset)
Get the scale of a Number for a given asset.
bool isLegalNet(STAmount const &value)
bool validJSONAsset(json::Value const &jv)
STAmount amountFromString(Asset const &asset, std::string const &amount)
std::optional< Rules > const & getCurrentTransactionRules()
bool toCurrency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
static std::uint64_t const kTenTO14
STAmount divRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
constexpr Dest unsafeCast(Src s) noexcept
STAmount roundToScale(STAmount const &value, std::int32_t scale, Number::RoundingMode rounding=Number::getround())
Round an arbitrary precision Amount to the precision of an STAmount that has a given exponent.
bool canAdd(STAmount const &amt1, STAmount const &amt2)
Safely checks if two STAmount values can be added without overflow, underflow, or precision loss.
bool amountFromJsonNoThrow(STAmount &result, json::Value const &jvSource)
STAmount amountFromQuality(std::uint64_t rate)
BaseUInt< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
static std::int64_t getSNValue(STAmount const &amount)
static bool areComparable(STAmount const &v1, STAmount const &v2)
STAmount amountFromJson(SField const &name, json::Value const &v)
bool hasInvalidAmount(STBase const &field, beast::Journal j)
static std::uint64_t const kTenTO17
STAmount mulRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
static std::int64_t getInt64Value(STAmount const &amount, bool valid, char const *error)
bool canSubtract(STAmount const &amt1, STAmount const &amt2)
Determines if it is safe to subtract one STAmount from another.
Dest safeDowncast(Src *s) noexcept
static std::uint64_t muldivRound(std::uint64_t multiplier, std::uint64_t multiplicand, std::uint64_t divisor, std::uint64_t rounding)
STAmount divRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
constexpr bool kIsMptissueV
static std::uint64_t muldiv(std::uint64_t multiplier, std::uint64_t multiplicand, std::uint64_t divisor)
static std::string const & systemCurrencyCode()
STAmount multiply(STAmount const &amount, Number const &frac, Number::RoundingMode rm)
static STAmount divRoundImpl(STAmount const &num, STAmount const &den, Asset const &asset, bool roundUp)
constexpr std::uint64_t kMaxMpTokenAmount
The maximum amount of MPTokenIssuance.
constexpr XRPAmount kInitialXrp
Configure the native currency.
static void canonicalizeRoundStrict(bool integral, std::uint64_t &value, int &offset, bool roundUp)
bool toIssuer(AccountID &, std::string const &)
Convert hex or base58 string to AccountID.
static std::uint64_t const kTenTO14M1
static void canonicalizeRound(bool integral, std::uint64_t &value, int &offset, bool)
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Note, should be treated as flags that can be | and &.