diff --git a/Builds/levelization/results/loops.txt b/Builds/levelization/results/loops.txt index ee7e6fd3bc6..2f2721b14cb 100644 --- a/Builds/levelization/results/loops.txt +++ b/Builds/levelization/results/loops.txt @@ -5,7 +5,7 @@ Loop: test.jtx test.unit_test test.unit_test == test.jtx Loop: xrpl.basics xrpl.json - xrpl.json ~= xrpl.basics + xrpl.json == xrpl.basics Loop: xrpld.app xrpld.core xrpld.app > xrpld.core diff --git a/include/xrpl/basics/MPTAmount.h b/include/xrpl/basics/MPTAmount.h index 6d1579d11d6..d0060489b4f 100644 --- a/include/xrpl/basics/MPTAmount.h +++ b/include/xrpl/basics/MPTAmount.h @@ -41,10 +41,11 @@ class MPTAmount : private boost::totally_ordered, private boost::additive { public: - using mpt_type = std::int64_t; + using value_type = std::int64_t; + static constexpr std::uint64_t cMaxMPTValue = 0x8000000000000000; protected: - mpt_type mpt_; + value_type value_; public: MPTAmount() = default; @@ -52,165 +53,104 @@ class MPTAmount : private boost::totally_ordered, constexpr MPTAmount& operator=(MPTAmount const& other) = default; - constexpr MPTAmount(beast::Zero) : mpt_(0) - { - } - - constexpr explicit MPTAmount(mpt_type value) : mpt_(value) - { - } - - constexpr MPTAmount& operator=(beast::Zero) - { - mpt_ = 0; - return *this; - } - - MPTAmount& - operator=(mpt_type value) - { - mpt_ = value; - return *this; - } - - constexpr MPTAmount - operator*(mpt_type const& rhs) const - { - return MPTAmount{mpt_ * rhs}; - } - - friend constexpr MPTAmount - operator*(mpt_type lhs, MPTAmount const& rhs) - { - // multiplication is commutative - return rhs * lhs; - } + constexpr explicit MPTAmount(value_type value); - MPTAmount& - operator+=(MPTAmount const& other) - { - mpt_ += other.mpt(); - return *this; - } - - MPTAmount& - operator-=(MPTAmount const& other) - { - mpt_ -= other.mpt(); - return *this; - } - - MPTAmount& - operator+=(mpt_type const& rhs) - { - mpt_ += rhs; - return *this; - } + constexpr MPTAmount& operator=(beast::Zero); MPTAmount& - operator-=(mpt_type const& rhs) - { - mpt_ -= rhs; - return *this; - } + operator+=(MPTAmount const& other); MPTAmount& - operator*=(mpt_type const& rhs) - { - mpt_ *= rhs; - return *this; - } + operator-=(MPTAmount const& other); MPTAmount - operator-() const - { - return MPTAmount{-mpt_}; - } + operator-() const; bool - operator==(MPTAmount const& other) const - { - return mpt_ == other.mpt_; - } + operator==(MPTAmount const& other) const; bool - operator==(mpt_type other) const - { - return mpt_ == other; - } + operator==(value_type other) const; bool - operator<(MPTAmount const& other) const - { - return mpt_ < other.mpt_; - } + operator<(MPTAmount const& other) const; /** Returns true if the amount is not zero */ - explicit constexpr operator bool() const noexcept - { - return mpt_ != 0; - } + explicit constexpr operator bool() const noexcept; /** Return the sign of the amount */ constexpr int - signum() const noexcept - { - return (mpt_ < 0) ? -1 : (mpt_ ? 1 : 0); - } + signum() const noexcept; Json::Value - jsonClipped() const - { - static_assert( - std::is_signed_v && std::is_integral_v, - "Expected MPTAmount to be a signed integral type"); - - constexpr auto min = std::numeric_limits::min(); - constexpr auto max = std::numeric_limits::max(); - - if (mpt_ < min) - return min; - if (mpt_ > max) - return max; - return static_cast(mpt_); - } + jsonClipped() const; /** Returns the underlying value. Code SHOULD NOT call this function unless the type has been abstracted away, e.g. in a templated function. */ - constexpr mpt_type - mpt() const - { - return mpt_; - } + constexpr value_type + value() const; friend std::istream& - operator>>(std::istream& s, MPTAmount& val) - { - s >> val.mpt_; - return s; - } + operator>>(std::istream& s, MPTAmount& val); static MPTAmount - minPositiveAmount() - { - return MPTAmount{1}; - } + minPositiveAmount(); }; +constexpr MPTAmount::MPTAmount(value_type value) : value_(value) +{ +} + +constexpr MPTAmount& MPTAmount::operator=(beast::Zero) +{ + value_ = 0; + return *this; +} + +/** Returns true if the amount is not zero */ +constexpr MPTAmount::operator bool() const noexcept +{ + return value_ != 0; +} + +/** Return the sign of the amount */ +constexpr int +MPTAmount::signum() const noexcept +{ + return (value_ < 0) ? -1 : (value_ ? 1 : 0); +} + +/** Returns the underlying value. Code SHOULD NOT call this + function unless the type has been abstracted away, + e.g. in a templated function. +*/ +constexpr MPTAmount::value_type +MPTAmount::value() const +{ + return value_; +} + +inline std::istream& +operator>>(std::istream& s, MPTAmount& val) +{ + s >> val.value_; + return s; +} + // Output MPTAmount as just the value. template std::basic_ostream& operator<<(std::basic_ostream& os, const MPTAmount& q) { - return os << q.mpt(); + return os << q.value(); } inline std::string to_string(MPTAmount const& amount) { - return std::to_string(amount.mpt()); + return std::to_string(amount.value()); } inline MPTAmount @@ -225,8 +165,8 @@ mulRatio( if (!den) Throw("division by zero"); - int128_t const amt128(amt.mpt()); - auto const neg = amt.mpt() < 0; + int128_t const amt128(amt.value()); + auto const neg = amt.value() < 0; auto const m = amt128 * num; auto r = m / den; if (m % den) @@ -236,9 +176,9 @@ mulRatio( if (neg && !roundUp) r -= 1; } - if (r > std::numeric_limits::max()) + if (r > std::numeric_limits::max()) Throw("XRP mulRatio overflow"); - return MPTAmount(r.convert_to()); + return MPTAmount(r.convert_to()); } } // namespace ripple diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index b127d259910..89c15f7d1b8 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -213,7 +213,7 @@ inline Number::Number(XRPAmount const& x) : Number{x.drops()} { } -inline Number::Number(MPTAmount const& x) : Number{x.mpt()} +inline Number::Number(MPTAmount const& x) : Number{x.value()} { } diff --git a/include/xrpl/protocol/Asset.h b/include/xrpl/protocol/Asset.h deleted file mode 100644 index 9eaadea69d1..00000000000 --- a/include/xrpl/protocol/Asset.h +++ /dev/null @@ -1,138 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2024 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_PROTOCOL_ASSET_H_INCLUDED -#define RIPPLE_PROTOCOL_ASSET_H_INCLUDED - -#include -#include -#include - -namespace ripple { - -class Asset -{ -private: - std::variant asset_; - -public: - Asset() = default; - - Asset(Issue const& issue); - - Asset(MPTIssue const& mpt); - - Asset(MPT const& mpt); - - Asset(uint192 const& mptID); - - explicit operator Issue() const; - - explicit operator MPTIssue() const; - - AccountID const& - getIssuer() const; - - constexpr Issue const& - issue() const; - - Issue& - issue(); - - constexpr MPTIssue const& - mptIssue() const; - - MPTIssue& - mptIssue(); - - constexpr bool - isMPT() const; - - constexpr bool - isIssue() const; - - std::string - getText() const; - - friend constexpr bool - operator==(Asset const& lhs, Asset const& rhs); - - friend constexpr bool - operator!=(Asset const& lhs, Asset const& rhs); -}; - -constexpr bool -Asset::isMPT() const -{ - return std::holds_alternative(asset_); -} - -constexpr bool -Asset::isIssue() const -{ - return std::holds_alternative(asset_); -} - -constexpr Issue const& -Asset::issue() const -{ - if (!std::holds_alternative(asset_)) - Throw("Asset is not Issue"); - return std::get(asset_); -} - -constexpr MPTIssue const& -Asset::mptIssue() const -{ - if (!std::holds_alternative(asset_)) - Throw("Asset is not MPT"); - return std::get(asset_); -} - -constexpr bool -operator==(Asset const& lhs, Asset const& rhs) -{ - if (lhs.isIssue() != rhs.isIssue()) - Throw("Assets are not comparable"); - if (lhs.isIssue()) - return lhs.issue() == rhs.issue(); - return lhs.mptIssue() == lhs.mptIssue(); -} - -constexpr bool -operator!=(Asset const& lhs, Asset const& rhs) -{ - return !(lhs == rhs); -} - -std::string -to_string(Asset const& asset); - -std::string -to_string(MPTIssue const& mpt); - -std::string -to_string(MPT const& mpt); - -bool -validJSONAsset(Json::Value const& jv); - -} // namespace ripple - -#endif // RIPPLE_PROTOCOL_ASSET_H_INCLUDED diff --git a/include/xrpl/protocol/Issue.h b/include/xrpl/protocol/Issue.h index 106ff62576e..5bf14e81c73 100644 --- a/include/xrpl/protocol/Issue.h +++ b/include/xrpl/protocol/Issue.h @@ -38,9 +38,13 @@ class Issue Currency currency{}; AccountID account{}; - Issue() = default; + Issue() + { + } - Issue(Currency const& c, AccountID const& a); + Issue(Currency const& c, AccountID const& a) : currency(c), account(a) + { + } AccountID const& getIssuer() const; diff --git a/include/xrpl/protocol/MPTIssue.h b/include/xrpl/protocol/MPTIssue.h index 3d28d8bb7e3..dc16d207a87 100644 --- a/include/xrpl/protocol/MPTIssue.h +++ b/include/xrpl/protocol/MPTIssue.h @@ -35,6 +35,8 @@ class MPTIssue MPTIssue(MPT const& mpt); + MPTIssue(uint192 const& id); + AccountID const& getIssuer() const; @@ -66,6 +68,22 @@ operator!=(MPTIssue const& lhs, MPTIssue const& rhs) return !(lhs.mpt_ == rhs.mpt_); } +MPT +getMPT(uint192 const&); + +inline MPT +badMPT() +{ + static MPT mpt{0, AccountID{0}}; + return mpt; +} + +inline bool +isXRP(uint192 const&) +{ + return false; +} + } // namespace ripple #endif // RIPPLE_PROTOCOL_MPTISSUE_H_INCLUDED diff --git a/include/xrpl/protocol/Rate.h b/include/xrpl/protocol/Rate.h index b065acb2316..fc9aa7f2c13 100644 --- a/include/xrpl/protocol/Rate.h +++ b/include/xrpl/protocol/Rate.h @@ -21,6 +21,7 @@ #define RIPPLE_PROTOCOL_RATE_H_INCLUDED #include +#include #include #include #include @@ -67,6 +68,9 @@ operator<<(std::ostream& os, Rate const& rate) STAmount multiply(STAmount const& amount, Rate const& rate); +STMPTAmount +multiply(STMPTAmount const& amount, Rate const& rate); + STAmount multiplyRound(STAmount const& amount, Rate const& rate, bool roundUp); diff --git a/include/xrpl/protocol/SField.h b/include/xrpl/protocol/SField.h index 07c1c7f49bb..4dedea748b6 100644 --- a/include/xrpl/protocol/SField.h +++ b/include/xrpl/protocol/SField.h @@ -42,7 +42,7 @@ Some fields have a different meaning for their // Forwards class STAccount; -class STAmount; +class STEitherAmount; class STIssue; class STBlob; template @@ -346,7 +346,7 @@ using SF_UINT384 = TypedField>; using SF_UINT512 = TypedField>; using SF_ACCOUNT = TypedField; -using SF_AMOUNT = TypedField; +using SF_AMOUNT = TypedField; using SF_ISSUE = TypedField; using SF_CURRENCY = TypedField; using SF_VL = TypedField; diff --git a/include/xrpl/protocol/SOTemplate.h b/include/xrpl/protocol/SOTemplate.h index c0fcfb64358..bf190cf28d8 100644 --- a/include/xrpl/protocol/SOTemplate.h +++ b/include/xrpl/protocol/SOTemplate.h @@ -39,6 +39,8 @@ enum SOEStyle { // constructed with STObject::makeInnerObject() }; +enum SOESupportMPT : bool { soeMPTYes = true, soeMPTNo = false }; + //------------------------------------------------------------------------------ /** An element in a SOTemplate. */ @@ -47,11 +49,19 @@ class SOElement // Use std::reference_wrapper so SOElement can be stored in a std::vector. std::reference_wrapper sField_; SOEStyle style_; + SOESupportMPT supportMpt_; public: - SOElement(SField const& fieldName, SOEStyle style) - : sField_(fieldName), style_(style) + SOElement( + SField const& fieldName, + SOEStyle style, + SOESupportMPT supportMpt = SOESupportMPT::soeMPTNo) + : sField_(fieldName), style_(style), supportMpt_(supportMpt) { + assert( + supportMpt_ == SOESupportMPT::soeMPTNo || + (supportMpt_ == SOESupportMPT::soeMPTYes && + fieldName.fieldType == STI_AMOUNT)); if (!sField_.get().isUseful()) { auto nm = std::to_string(fieldName.getCode()); @@ -73,6 +83,12 @@ class SOElement { return style_; } + + SOESupportMPT + supportMPT() const + { + return supportMpt_; + } }; //------------------------------------------------------------------------------ diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index de66119c80f..e630dd1c159 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -23,10 +23,9 @@ #include #include #include -#include #include #include -#include +#include #include #include #include @@ -34,11 +33,6 @@ namespace ripple { -template -concept AssetType = std::is_same_v || std::is_same_v || - std::is_same_v || std::is_convertible_v || - std::is_convertible_v; - // Internal form: // 1: If amount is zero, then value is zero and offset is -100 // 2: Otherwise: @@ -57,7 +51,7 @@ class STAmount final : public STBase, public CountedObject using rep = std::pair; private: - Asset mAsset; + Issue mIssue; mantissa_type mValue; exponent_type mOffset; bool mIsNative; // A shorthand for isXRP(mIssue). @@ -76,14 +70,13 @@ class STAmount final : public STBase, public CountedObject // Max native value on network. static const std::uint64_t cMaxNativeN = 100000000000000000ull; - static const std::uint64_t cIssuedCurrency = 0x8000000000000000ull; - static const std::uint64_t cPositive = 0x4000000000000000ull; - static const std::uint64_t cMPToken = 0x2000000000000000ull; - static const std::uint64_t cValueMask = ~(cPositive | cMPToken); + static const std::uint64_t cNotNative = 0x8000000000000000ull; + static const std::uint64_t cPosNative = 0x4000000000000000ull; static std::uint64_t const uRateOne; //-------------------------------------------------------------------------- + STAmount(std::uint64_t value, SerialIter& sit, SField const& name); STAmount(SerialIter& sit, SField const& name); struct unchecked @@ -92,19 +85,17 @@ class STAmount final : public STBase, public CountedObject }; // Do not call canonicalize - template STAmount( SField const& name, - A const& asset, + Issue const& issue, mantissa_type mantissa, exponent_type exponent, bool native, bool negative, unchecked); - template STAmount( - A const& asset, + Issue const& issue, mantissa_type mantissa, exponent_type exponent, bool native, @@ -112,10 +103,9 @@ class STAmount final : public STBase, public CountedObject unchecked); // Call canonicalize - template STAmount( SField const& name, - A const& asset, + Issue const& issue, mantissa_type mantissa, exponent_type exponent, bool native, @@ -128,10 +118,9 @@ class STAmount final : public STBase, public CountedObject std::uint64_t mantissa = 0, bool negative = false); - template STAmount( SField const& name, - A const& asset, + Issue const& issue, std::uint64_t mantissa = 0, int exponent = 0, bool negative = false); @@ -140,40 +129,26 @@ class STAmount final : public STBase, public CountedObject explicit STAmount(SField const& name, STAmount const& amt); - template STAmount( - A const& asset, + Issue const& issue, std::uint64_t mantissa = 0, int exponent = 0, - bool negative = false) - : mAsset(asset) - , mValue(mantissa) - , mOffset(exponent) - , mIsNegative(negative) - { - canonicalize(); - } + bool negative = false); // VFALCO Is this needed when we have the previous signature? - template STAmount( - A const& asset, + Issue const& issue, std::uint32_t mantissa, int exponent = 0, bool negative = false); - template - STAmount(A const& asset, std::int64_t mantissa, int exponent = 0); + STAmount(Issue const& issue, std::int64_t mantissa, int exponent = 0); - template - STAmount(A const& asset, int mantissa, int exponent = 0); + STAmount(Issue const& issue, int mantissa, int exponent = 0); // Legacy support for new-style amounts - template - STAmount(IOUAmount const& amount, A const& asset); + STAmount(IOUAmount const& amount, Issue const& issue); STAmount(XRPAmount const& amount); - template - STAmount(MPTAmount const& amount, A const& asset); operator Number() const; //-------------------------------------------------------------------------- @@ -188,33 +163,15 @@ class STAmount final : public STBase, public CountedObject bool native() const noexcept; - bool - isMPT() const noexcept; - - bool - isIssue() const noexcept; - - bool - isIOU() const noexcept; - - std::string - getTypeName() const noexcept; - bool negative() const noexcept; std::uint64_t mantissa() const noexcept; - Asset const& - asset() const; - Issue const& issue() const; - MPTIssue const& - mptIssue() const; - // These three are deprecated Currency const& getCurrency() const; @@ -272,9 +229,6 @@ class STAmount final : public STBase, public CountedObject void clear(Issue const& issue); - void - clear(MPT const& mpt); - void setIssuer(AccountID const& uIssuer); @@ -312,12 +266,6 @@ class STAmount final : public STBase, public CountedObject xrp() const; IOUAmount iou() const; - MPTAmount - mpt() const; - - template - void - setAsset(A const& a, bool native); private: static std::unique_ptr @@ -342,140 +290,6 @@ class STAmount final : public STBase, public CountedObject operator+(STAmount const& v1, STAmount const& v2); }; -template -void -STAmount::setAsset(const A& asset, bool native) -{ - if (native) - mAsset = xrpIssue(); - else - mAsset = asset; -} - -template -STAmount::STAmount( - SField const& name, - A const& asset, - mantissa_type mantissa, - exponent_type exponent, - bool native, - bool negative, - unchecked) - : STBase(name) - , mValue(mantissa) - , mOffset(exponent) - , mIsNative(native) - , mIsNegative(negative) -{ - setAsset(asset, native); -} - -template -STAmount::STAmount( - A const& asset, - mantissa_type mantissa, - exponent_type exponent, - bool native, - bool negative, - unchecked) - : mValue(mantissa) - , mOffset(exponent) - , mIsNative(native) - , mIsNegative(negative) -{ - setAsset(asset, native); -} - -template -STAmount::STAmount( - SField const& name, - A const& asset, - mantissa_type mantissa, - exponent_type exponent, - bool native, - bool negative) - : STBase(name) - , mValue(mantissa) - , mOffset(exponent) - , mIsNative(native) - , mIsNegative(negative) -{ - setAsset(asset, native); - canonicalize(); -} - -template -STAmount::STAmount( - SField const& name, - A const& asset, - std::uint64_t mantissa, - int exponent, - bool negative) - : STBase(name) - , mAsset(asset) - , mValue(mantissa) - , mOffset(exponent) - , mIsNegative(negative) -{ - assert(mValue <= std::numeric_limits::max()); - canonicalize(); -} - -template -STAmount::STAmount(A const& asset, std::int64_t mantissa, int exponent) - : mAsset(asset), mOffset(exponent) -{ - set(mantissa); - canonicalize(); -} - -template -STAmount::STAmount( - A const& asset, - std::uint32_t mantissa, - int exponent, - bool negative) - : STAmount(asset, safe_cast(mantissa), exponent, negative) -{ -} - -template -STAmount::STAmount(A const& asset, int mantissa, int exponent) - : STAmount(asset, safe_cast(mantissa), exponent) -{ -} - -// Legacy support for new-style amounts -template -STAmount::STAmount(IOUAmount const& amount, A const& asset) - : mAsset(asset) - , mOffset(amount.exponent()) - , mIsNative(false) - , mIsNegative(amount < beast::zero) -{ - if (mIsNegative) - mValue = static_cast(-amount.mantissa()); - else - mValue = static_cast(amount.mantissa()); - - canonicalize(); -} - -template -STAmount::STAmount(MPTAmount const& amount, A const& asset) - : mAsset(asset) - , mOffset(0) - , mIsNative(false) - , mIsNegative(amount < beast::zero) -{ - if (mIsNegative) - mValue = unsafe_cast(-amount.mpt()); - else - mValue = unsafe_cast(amount.mpt()); - - canonicalize(); -} - //------------------------------------------------------------------------------ // // Creation @@ -487,13 +301,7 @@ STAmount amountFromQuality(std::uint64_t rate); STAmount -amountFromString(Asset const& issue, std::string const& amount); - -STAmount -amountFromJson(SField const& name, Json::Value const& v); - -bool -amountFromJsonNoThrow(STAmount& result, Json::Value const& jvSource); +amountFromString(Issue const& issue, std::string const& amount); // IOUAmount and XRPAmount define toSTAmount, defining this // trivial conversion here makes writing generic code easier @@ -521,24 +329,6 @@ STAmount::native() const noexcept return mIsNative; } -inline bool -STAmount::isMPT() const noexcept -{ - return mAsset.isMPT(); -} - -inline bool -STAmount::isIssue() const noexcept -{ - return mAsset.isIssue(); -} - -inline bool -STAmount::isIOU() const noexcept -{ - return mAsset.isIssue() && !mIsNative; -} - inline bool STAmount::negative() const noexcept { @@ -551,34 +341,22 @@ STAmount::mantissa() const noexcept return mValue; } -inline Asset const& -STAmount::asset() const -{ - return mAsset; -} - inline Issue const& STAmount::issue() const { - return mAsset.issue(); -} - -inline MPTIssue const& -STAmount::mptIssue() const -{ - return mAsset.mptIssue(); + return mIssue; } inline Currency const& STAmount::getCurrency() const { - return mAsset.issue().currency; + return mIssue.currency; } inline AccountID const& STAmount::getIssuer() const { - return mAsset.getIssuer(); + return mIssue.account; } inline int @@ -590,9 +368,7 @@ STAmount::signum() const noexcept inline STAmount STAmount::zeroed() const { - if (mAsset.isIssue()) - return STAmount(mAsset.issue()); - return STAmount(mAsset.mptIssue()); + return STAmount(mIssue); } inline STAmount::operator bool() const noexcept @@ -604,8 +380,6 @@ inline STAmount::operator Number() const { if (mIsNative) return xrp(); - if (mAsset.isMPT()) - return mpt(); return iou(); } @@ -643,10 +417,7 @@ STAmount::clear() inline void STAmount::clear(STAmount const& saTmpl) { - if (saTmpl.isMPT()) - clear(saTmpl.mAsset.mptIssue()); - else - clear(saTmpl.issue()); + clear(saTmpl.mIssue); } inline void @@ -656,18 +427,11 @@ STAmount::clear(Issue const& issue) clear(); } -inline void -STAmount::clear(MPT const& mpt) -{ - mAsset = mpt; - clear(); -} - inline void STAmount::setIssuer(AccountID const& uIssuer) { - mAsset.issue().account = uIssuer; - setIssue(mAsset.issue()); + mIssue.account = uIssuer; + setIssue(mIssue); } inline STAmount const& @@ -676,12 +440,6 @@ STAmount::value() const noexcept return *this; } -inline bool -isLegalNet(STAmount const& value) -{ - return !value.native() || (value.mantissa() <= STAmount::cMaxNativeN); -} - //------------------------------------------------------------------------------ // // Operators @@ -732,17 +490,17 @@ STAmount operator-(STAmount const& v1, STAmount const& v2); STAmount -divide(STAmount const& v1, STAmount const& v2, Asset const& asset); +divide(STAmount const& v1, STAmount const& v2, Issue const& issue); STAmount -multiply(STAmount const& v1, STAmount const& v2, Asset const& asset); +multiply(STAmount const& v1, STAmount const& v2, Issue const& issue); // multiply rounding result in specified direction STAmount mulRound( STAmount const& v1, STAmount const& v2, - Asset const& asset, + Issue const& issue, bool roundUp); // multiply following the rounding directions more precisely. @@ -750,7 +508,7 @@ STAmount mulRoundStrict( STAmount const& v1, STAmount const& v2, - Asset const& asset, + Issue const& issue, bool roundUp); // divide rounding result in specified direction @@ -758,7 +516,7 @@ STAmount divRound( STAmount const& v1, STAmount const& v2, - Asset const& asset, + Issue const& issue, bool roundUp); // divide following the rounding directions more precisely. @@ -766,7 +524,7 @@ STAmount divRoundStrict( STAmount const& v1, STAmount const& v2, - Asset const& asset, + Issue const& issue, bool roundUp); // Someone is offering X for Y, what is the rate? @@ -780,13 +538,7 @@ getRate(STAmount const& offerOut, STAmount const& offerIn); inline bool isXRP(STAmount const& amount) { - return amount.isIssue() && isXRP(amount.issue().currency); -} - -inline bool -isMPT(STAmount const& amount) -{ - return amount.isMPT(); + return isXRP(amount.issue().currency); } // Since `canonicalize` does not have access to a ledger, this is needed to put @@ -822,18 +574,4 @@ class STAmountSO } // namespace ripple -//------------------------------------------------------------------------------ -namespace Json { -template <> -inline ripple::STAmount -getOrThrow(Json::Value const& v, ripple::SField const& field) -{ - using namespace ripple; - Json::StaticString const& key = field.getJsonName(); - if (!v.isMember(key)) - Throw(key); - Json::Value const& inner = v[key]; - return amountFromJson(field, inner); -} -} // namespace Json #endif diff --git a/include/xrpl/protocol/STEitherAmount.h b/include/xrpl/protocol/STEitherAmount.h new file mode 100644 index 00000000000..a65c35b6be5 --- /dev/null +++ b/include/xrpl/protocol/STEitherAmount.h @@ -0,0 +1,319 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_PROTOCOL_STEITHERAMOUNT_H_INCLUDED +#define RIPPLE_PROTOCOL_STEITHERAMOUNT_H_INCLUDED + +#include +#include + +namespace ripple { + +template +concept ValidAmountType = + std::is_same_v || std::is_same_v; + +// Currency or MPT issuance ID +template +concept ValidAssetType = + std::is_same_v || std::is_same_v; + +template +concept EitherAmountType = std::is_same_v || + std::is_same_v>; + +class STEitherAmount : public STBase, public CountedObject +{ +private: + std::variant amount_; + +public: + using value_type = STEitherAmount; + STEitherAmount() = default; + STEitherAmount(SerialIter& sit, SField const& name); + STEitherAmount(XRPAmount const& amount); + STEitherAmount(STAmount const& amount); + STEitherAmount(SField const& name, STAmount const& amount); + STEitherAmount(STMPTAmount const& amount); + + STEitherAmount& + operator=(STAmount const&); + STEitherAmount& + operator=(STMPTAmount const&); + STEitherAmount& + operator=(XRPAmount const&); + + SerializedTypeID + getSType() const override; + + std::string + getFullText() const override; + + std::string + getText() const override; + + Json::Value getJson(JsonOptions) const override; + + void + setJson(Json::Value&) const; + + void + add(Serializer& s) const override; + + bool + isEquivalent(const STBase& t) const override; + + bool + isDefault() const override; + + //------------------------------------------------------------------------------ + + bool + isMPT() const; + + bool + isIssue() const; + + STEitherAmount const& + value() const; + + std::variant const& + getValue() const; + + std::variant& + getValue(); + + AccountID const& + getIssuer() const; + + bool + badAsset() const; + + bool + sameAsset(STEitherAmount const& amount) const; + + bool + sameIssue(STEitherAmount const& amount) const; + + bool + negative() const; + + bool + native() const; + + STEitherAmount + zeroed() const; + + int + signum() const noexcept; + + template + T const& + get() const; + + template + T& + get(); + +private: + STBase* + copy(std::size_t n, void* buf) const override; + STBase* + move(std::size_t n, void* buf) override; +}; + +template +T const& +STEitherAmount::get() const +{ + if (std::holds_alternative(amount_)) + return std::get(amount_); + Throw("Invalid STEitherAmount conversion"); +} + +template +T& +STEitherAmount::get() +{ + if (std::holds_alternative(amount_)) + return std::get(amount_); + Throw("Invalid STEitherAmount conversion"); +} + +template +decltype(auto) +get(auto&& amount) +{ + using TAmnt = std::decay_t; + if constexpr (std::is_same_v) + { + if constexpr (std::is_lvalue_reference_v) + return amount.template get(); + else + return std::remove_reference_t(amount.template get()); + } + else if constexpr (std::is_same_v>) + { + static std::optional t; + if (amount.has_value()) + return std::make_optional(amount->template get()); + return t; + } + else if constexpr (std::is_convertible_v) + { + if constexpr (std::is_lvalue_reference_v) + return amount.operator STEitherAmount().template get(); + else + return std::remove_reference_t( + amount.operator STEitherAmount().template get()); + } + else + { + bool const alwaysFalse = !std::is_same_v; + static_assert(alwaysFalse, "Invalid STEitherAmount conversion"); + } +} + +STEitherAmount +amountFromJson(SField const& name, Json::Value const& v); + +bool +amountFromJsonNoThrow(STEitherAmount& result, Json::Value const& jvSource); + +bool +amountFromJsonNoThrow(STAmount& result, Json::Value const& jvSource); + +inline bool +operator==(STEitherAmount const& lhs, STEitherAmount const& rhs) +{ + return std::visit( + [&](T1 const& a1, T2 const& a2) { + if constexpr (std::is_same_v) + return a1 == a2; + else + return false; + }, + lhs.getValue(), + rhs.getValue()); + if (lhs.isIssue() == rhs.isIssue()) + return lhs.getValue() == rhs.getValue(); + return false; +} + +inline bool +operator!=(STEitherAmount const& lhs, STEitherAmount const& rhs) +{ + return !operator==(lhs, rhs); +} + +template +bool +sameAsset(T1 const& t1, T2 const& t2) +{ + if constexpr (std::is_same_v) + return t1 == t2; + else + return false; +} + +template +bool +badAsset(T const& t) +{ + if constexpr (std::is_same_v) + return badCurrency() == t; + else + return badMPT() == t; +} + +inline bool +isLegalNet(STEitherAmount const& value) +{ + if (value.isIssue()) + { + auto const& v = get(value); + return !v.native() || (v.mantissa() <= STAmount::cMaxNativeN); + } + return true; +} + +template +bool +isNative(T const& amount) +{ + if constexpr (std::is_same_v) + return false; + else if constexpr (std::is_same_v) + return amount.native(); +} + +template +bool +isMPT(T const& amount) +{ + if constexpr (std::is_same_v) + return true; + else if constexpr (std::is_same_v) + return false; +} + +template +bool +isMPT(T const& amount) +{ + if constexpr (std::is_same_v) + return amount.isMPT(); + else + return amount && amount->isMPT(); +} + +template +bool +isIssue(T const& amount) +{ + return !isMPT(amount); +} + +inline bool +isXRP(STEitherAmount const& amount) +{ + if (amount.isIssue()) + return isXRP(get(amount)); + return false; +} + +} // namespace ripple + +//------------------------------------------------------------------------------ +namespace Json { +template <> +inline ripple::STAmount +getOrThrow(Json::Value const& v, ripple::SField const& field) +{ + using namespace ripple; + Json::StaticString const& key = field.getJsonName(); + if (!v.isMember(key)) + Throw(key); + Json::Value const& inner = v[key]; + return get(amountFromJson(field, inner)); +} + +} // namespace Json + +#endif // RIPPLE_PROTOCOL_STEITHERAMOUNT_H_INCLUDED diff --git a/include/xrpl/protocol/STMPTAmount.h b/include/xrpl/protocol/STMPTAmount.h new file mode 100644 index 00000000000..822fb44de8b --- /dev/null +++ b/include/xrpl/protocol/STMPTAmount.h @@ -0,0 +1,123 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_PROTOCOL_STMPTAMOUNT_H_INCLUDED +#define RIPPLE_PROTOCOL_STMPTAMOUNT_H_INCLUDED + +#include +#include +#include +#include +#include + +namespace ripple { + +struct Rate; + +class STMPTAmount final : public MPTAmount, + public STBase, + public CountedObject +{ +private: + MPTIssue issue_; + +public: + static constexpr std::uint64_t cMPToken = 0x2000000000000000ull; + + STMPTAmount(std::uint64_t value, SerialIter& sit, SField const& name); + STMPTAmount( + SField const& name, + MPTIssue const& issue, + std::int64_t value = 0); + STMPTAmount(MPTIssue const& issue, std::uint64_t value); + STMPTAmount(MPTIssue const& issue, std::int64_t value = 0); + explicit STMPTAmount(value_type value = 0); + + SerializedTypeID + getSType() const override; + + std::string + getFullText() const override; + + std::string + getText() const override; + + Json::Value getJson(JsonOptions) const override; + + void + add(Serializer& s) const override; + + void + setJson(Json::Value& elem) const; + + bool + isEquivalent(const STBase& t) const override; + + bool + isDefault() const override; + + AccountID const& + getIssuer() const; + + MPTIssue const& + issue() const; + + uint192 + getCurrency() const; + + void + clear(); + + void + clear(MPTIssue const& issue); + + STMPTAmount + zeroed() const; + + int + signum() const noexcept; + + bool + operator==(STMPTAmount const& rhs) const; + + bool + operator!=(STMPTAmount const& rhs) const; +}; + +inline bool +STMPTAmount::operator==(STMPTAmount const& rhs) const +{ + return value_ == rhs.value_ && issue_ == rhs.issue_; +} + +inline bool +STMPTAmount::operator!=(STMPTAmount const& rhs) const +{ + return !operator==(rhs); +} + +STMPTAmount +amountFromString(MPTIssue const& issue, std::string const& amount); + +STMPTAmount +multiply(STMPTAmount const& amount, Rate const& rate); + +} // namespace ripple + +#endif // RIPPLE_PROTOCOL_STMPTAMOUNT_H_INCLUDED diff --git a/include/xrpl/protocol/STObject.h b/include/xrpl/protocol/STObject.h index e55351cbc24..3fa8588f3ee 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -27,9 +27,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -235,7 +235,7 @@ class STObject : public STBase, public CountedObject Blob getFieldVL(SField const& field) const; - STAmount const& + STEitherAmount const& getFieldAmount(SField const& field) const; STPathSet const& getFieldPathSet(SField const& field) const; @@ -370,7 +370,7 @@ class STObject : public STBase, public CountedObject setAccountID(SField const& field, AccountID const&); void - setFieldAmount(SField const& field, STAmount const&); + setFieldAmount(SField const& field, STEitherAmount const&); void setFieldIssue(SField const& field, STIssue const&); void diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index d0f9275d69c..6d666a3fb55 100644 --- a/include/xrpl/protocol/TER.h +++ b/include/xrpl/protocol/TER.h @@ -140,7 +140,8 @@ enum TEMcodes : TERUnderlyingType { temARRAY_EMPTY, temARRAY_TOO_LARGE, - temMPT_NOT_SUPPORTED + temMPT_NOT_SUPPORTED, + temMPT_INVALID_USAGE }; //------------------------------------------------------------------------------ diff --git a/include/xrpl/protocol/TxMeta.h b/include/xrpl/protocol/TxMeta.h index 7932a4c55a3..92114230da7 100644 --- a/include/xrpl/protocol/TxMeta.h +++ b/include/xrpl/protocol/TxMeta.h @@ -108,12 +108,12 @@ class TxMeta } void - setDeliveredAmount(STAmount const& delivered) + setDeliveredAmount(STEitherAmount const& delivered) { mDelivered = delivered; } - STAmount + STEitherAmount getDeliveredAmount() const { assert(hasDeliveredAmount()); @@ -132,7 +132,7 @@ class TxMeta std::uint32_t mIndex; int mResult; - std::optional mDelivered; + std::optional mDelivered; STArray mNodes; }; diff --git a/include/xrpl/protocol/UintTypes.h b/include/xrpl/protocol/UintTypes.h index b574319b734..8ccca8743ae 100644 --- a/include/xrpl/protocol/UintTypes.h +++ b/include/xrpl/protocol/UintTypes.h @@ -46,12 +46,6 @@ class NodeIDTag explicit NodeIDTag() = default; }; -class MPTTag -{ -public: - explicit MPTTag() = default; -}; - } // namespace detail /** Directory is an index into the directory of offer books. diff --git a/src/libxrpl/basics/MPTAmount.cpp b/src/libxrpl/basics/MPTAmount.cpp new file mode 100644 index 00000000000..6c9a50e4730 --- /dev/null +++ b/src/libxrpl/basics/MPTAmount.cpp @@ -0,0 +1,85 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include + +namespace ripple { + +MPTAmount& +MPTAmount::operator+=(MPTAmount const& other) +{ + value_ += other.value(); + return *this; +} + +MPTAmount& +MPTAmount::operator-=(MPTAmount const& other) +{ + value_ -= other.value(); + return *this; +} + +MPTAmount +MPTAmount::operator-() const +{ + return MPTAmount{-value_}; +} + +bool +MPTAmount::operator==(MPTAmount const& other) const +{ + return value_ == other.value_; +} + +bool +MPTAmount::operator==(value_type other) const +{ + return value_ == other; +} + +bool +MPTAmount::operator<(MPTAmount const& other) const +{ + return value_ < other.value_; +} + +Json::Value +MPTAmount::jsonClipped() const +{ + static_assert( + std::is_signed_v && std::is_integral_v, + "Expected MPTAmount to be a signed integral type"); + + constexpr auto min = std::numeric_limits::min(); + constexpr auto max = std::numeric_limits::max(); + + if (value_ < min) + return min; + if (value_ > max) + return max; + return static_cast(value_); +} + +MPTAmount +MPTAmount::minPositiveAmount() +{ + return MPTAmount{1}; +} + +} // namespace ripple diff --git a/src/libxrpl/protocol/Asset.cpp b/src/libxrpl/protocol/Asset.cpp deleted file mode 100644 index d3bd567a1fd..00000000000 --- a/src/libxrpl/protocol/Asset.cpp +++ /dev/null @@ -1,118 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2024 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -namespace ripple { - -Asset::Asset(Issue const& issue) : asset_(issue) -{ -} - -Asset::Asset(MPTIssue const& mpt) : asset_(mpt) -{ -} - -Asset::Asset(MPT const& mpt) : asset_(MPTIssue{mpt}) -{ -} - -Asset::operator Issue() const -{ - return issue(); -} - -Asset::operator MPTIssue() const -{ - return mptIssue(); -} - -AccountID const& -Asset::getIssuer() const -{ - if (isIssue()) - return issue().getIssuer(); - return mptIssue().getIssuer(); -} - -Asset::Asset(uint192 const& u) -{ - std::uint32_t sequence; - AccountID account; - memcpy(&sequence, u.data(), sizeof(sequence)); - sequence = boost::endian::big_to_native(sequence); - memcpy(account.data(), u.data() + sizeof(sequence), sizeof(AccountID)); - asset_ = std::make_pair(sequence, account); -} - -Issue& -Asset::issue() -{ - if (!std::holds_alternative(asset_)) - Throw("Asset is not Issue"); - return std::get(asset_); -} - -MPTIssue& -Asset::mptIssue() -{ - if (!std::holds_alternative(asset_)) - Throw("Asset is not MPT"); - return std::get(asset_); -} - -std::string -Asset::getText() const -{ - if (isIssue()) - return issue().getText(); - return to_string(mptIssue().getMptID()); -} - -std::string -to_string(Asset const& asset) -{ - if (asset.isIssue()) - return to_string(asset.issue()); - return to_string(asset.mptIssue().getMptID()); -} - -std::string -to_string(MPTIssue const& mptIssue) -{ - return to_string(mptIssue.getMptID()); -} - -std::string -to_string(MPT const& mpt) -{ - return to_string(getMptID(mpt.second, mpt.first)); -} - -bool -validJSONAsset(Json::Value const& jv) -{ - return (jv.isMember(jss::currency) && !jv.isMember(jss::mpt_issuance_id)) || - (!jv.isMember(jss::currency) && !jv.isMember(jss::issuer) && - jv.isMember(jss::mpt_issuance_id)); -} - -} // namespace ripple \ No newline at end of file diff --git a/src/libxrpl/protocol/Issue.cpp b/src/libxrpl/protocol/Issue.cpp index 8aff535fde7..d860e7062bc 100644 --- a/src/libxrpl/protocol/Issue.cpp +++ b/src/libxrpl/protocol/Issue.cpp @@ -26,10 +26,6 @@ namespace ripple { -Issue::Issue(Currency const& c, AccountID const& a) : currency(c), account(a) -{ -} - AccountID const& Issue::getIssuer() const { diff --git a/src/libxrpl/protocol/MPTIssue.cpp b/src/libxrpl/protocol/MPTIssue.cpp index fd3b6bbfa90..28806dce3aa 100644 --- a/src/libxrpl/protocol/MPTIssue.cpp +++ b/src/libxrpl/protocol/MPTIssue.cpp @@ -26,6 +26,11 @@ MPTIssue::MPTIssue(MPT const& mpt) : mpt_(mpt) { } +MPTIssue::MPTIssue(uint192 const& id) +{ + mpt_ = getMPT(id); +} + AccountID const& MPTIssue::getIssuer() const { @@ -50,4 +55,16 @@ MPTIssue::getMptID() const return ripple::getMptID(mpt_.second, mpt_.first); } +MPT +getMPT(uint192 const& id) +{ + std::uint32_t sequence; + AccountID account; + + memcpy(&sequence, id.data(), sizeof(sequence)); + sequence = boost::endian::big_to_native(sequence); + memcpy(account.data(), id.data() + sizeof(sequence), sizeof(AccountID)); + return std::make_pair(sequence, account); +} + } // namespace ripple diff --git a/src/libxrpl/protocol/Quality.cpp b/src/libxrpl/protocol/Quality.cpp index c6464eba9d2..38b641328b0 100644 --- a/src/libxrpl/protocol/Quality.cpp +++ b/src/libxrpl/protocol/Quality.cpp @@ -65,7 +65,7 @@ Quality::operator--(int) } template + *DivRoundFunc)(STAmount const&, STAmount const&, Issue const&, bool)> static Amounts ceil_in_impl( Amounts const& amount, @@ -77,7 +77,7 @@ ceil_in_impl( { Amounts result( limit, - DivRoundFunc(limit, quality.rate(), amount.out.asset(), roundUp)); + DivRoundFunc(limit, quality.rate(), amount.out.issue(), roundUp)); // Clamp out if (result.out > amount.out) result.out = amount.out; @@ -104,7 +104,7 @@ Quality::ceil_in_strict( } template + *MulRoundFunc)(STAmount const&, STAmount const&, Issue const&, bool)> static Amounts ceil_out_impl( Amounts const& amount, @@ -115,7 +115,7 @@ ceil_out_impl( if (amount.out > limit) { Amounts result( - MulRoundFunc(limit, quality.rate(), amount.in.asset(), roundUp), + MulRoundFunc(limit, quality.rate(), amount.in.issue(), roundUp), limit); // Clamp in if (result.in > amount.in) @@ -151,7 +151,7 @@ composed_quality(Quality const& lhs, Quality const& rhs) STAmount const rhs_rate(rhs.rate()); assert(rhs_rate != beast::zero); - STAmount const rate(mulRound(lhs_rate, rhs_rate, lhs_rate.asset(), true)); + STAmount const rate(mulRound(lhs_rate, rhs_rate, lhs_rate.issue(), true)); std::uint64_t const stored_exponent(rate.exponent() + 100); std::uint64_t const stored_mantissa(rate.mantissa()); diff --git a/src/libxrpl/protocol/Rate2.cpp b/src/libxrpl/protocol/Rate2.cpp index 01a3e7deca5..ecf6de2cb77 100644 --- a/src/libxrpl/protocol/Rate2.cpp +++ b/src/libxrpl/protocol/Rate2.cpp @@ -51,7 +51,20 @@ multiply(STAmount const& amount, Rate const& rate) if (rate == parityRate) return amount; - return multiply(amount, detail::as_amount(rate), amount.asset()); + return multiply(amount, detail::as_amount(rate), amount.issue()); +} + +STMPTAmount +multiply(STMPTAmount const& amount, Rate const& rate) +{ + assert(rate.value != 0); + + if (rate == parityRate) + return amount; + + return STMPTAmount{ + amount.issue(), + static_cast(amount.value() * Number{rate.value, -9})}; } STAmount @@ -62,7 +75,7 @@ multiplyRound(STAmount const& amount, Rate const& rate, bool roundUp) if (rate == parityRate) return amount; - return mulRound(amount, detail::as_amount(rate), amount.asset(), roundUp); + return mulRound(amount, detail::as_amount(rate), amount.issue(), roundUp); } STAmount diff --git a/src/libxrpl/protocol/STAmount.cpp b/src/libxrpl/protocol/STAmount.cpp index 355518347a9..c4cf6d9c151 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -79,56 +78,25 @@ getSNValue(STAmount const& amount) return ret; } -static std::int64_t -getMPTValue(STAmount const& amount) -{ - if (!amount.isMPT()) - Throw("amount is not native!"); - - auto ret = static_cast(amount.mantissa()); - - assert(static_cast(ret) == amount.mantissa()); - - if (amount.negative()) - ret = -ret; - - return ret; -} - static bool areComparable(STAmount const& v1, STAmount const& v2) { - return (v1.isMPT() && v2.isMPT() && - v1.asset().mptIssue() == v2.asset().mptIssue()) || - (v1.isIssue() && v1.native() == v2.native() && - v1.issue().currency == v2.issue().currency); + return v1.native() == v2.native() && + v1.issue().currency == v2.issue().currency; } -STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name) +STAmount::STAmount(std::uint64_t value, SerialIter& sit, SField const& name) + : STBase(name) { - // TODO MPT make sure backward compatible - - std::uint64_t value = sit.get64(); - // TODO must fix serialization for IOU, it incorrectly sets cMPToken - bool isMPT = (value & cMPToken) && !(value & cIssuedCurrency); - - // native or MPT - if ((value & cIssuedCurrency) == 0 || isMPT) + // native + if ((value & cNotNative) == 0) { - if (isMPT) - { - // mAsset = std::make_pair( - // sit.get32(), static_cast(sit.get160())); - mAsset = sit.get192(); - } - else - mAsset = xrpIssue(); // positive - if ((value & cPositive) != 0) + if ((value & cPosNative) != 0) { - mValue = value & cValueMask; + mValue = value & ~cPosNative; mOffset = 0; - mIsNative = !isMPT; + mIsNative = true; mIsNegative = false; return; } @@ -137,9 +105,9 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name) if (value == 0) Throw("negative zero is not canonical"); - mValue = value & cValueMask; + mValue = value; mOffset = 0; - mIsNative = !isMPT; + mIsNative = true; mIsNegative = true; return; } @@ -171,7 +139,7 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name) Throw("invalid currency value"); } - mAsset = issue; + mIssue = issue; mValue = value; mOffset = offset; mIsNegative = isNegative; @@ -182,22 +150,75 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name) if (offset != 512) Throw("invalid currency value"); - mAsset = issue; + mIssue = issue; mValue = 0; mOffset = 0; mIsNegative = false; canonicalize(); } +STAmount::STAmount(ripple::SerialIter& sit, const ripple::SField& name) + : STAmount(sit.get64(), sit, name) +{ +} + +STAmount::STAmount( + SField const& name, + Issue const& issue, + mantissa_type mantissa, + exponent_type exponent, + bool native, + bool negative, + unchecked) + : STBase(name) + , mIssue(issue) + , mValue(mantissa) + , mOffset(exponent) + , mIsNative(native) + , mIsNegative(negative) +{ +} + +STAmount::STAmount( + Issue const& issue, + mantissa_type mantissa, + exponent_type exponent, + bool native, + bool negative, + unchecked) + : mIssue(issue) + , mValue(mantissa) + , mOffset(exponent) + , mIsNative(native) + , mIsNegative(negative) +{ +} + +STAmount::STAmount( + SField const& name, + Issue const& issue, + mantissa_type mantissa, + exponent_type exponent, + bool native, + bool negative) + : STBase(name) + , mIssue(issue) + , mValue(mantissa) + , mOffset(exponent) + , mIsNative(native) + , mIsNegative(negative) +{ + canonicalize(); +} + STAmount::STAmount(SField const& name, std::int64_t mantissa) - : STBase(name), mAsset(xrpIssue()), mOffset(0), mIsNative(true) + : STBase(name), mOffset(0), mIsNative(true) { set(mantissa); } STAmount::STAmount(SField const& name, std::uint64_t mantissa, bool negative) : STBase(name) - , mAsset(xrpIssue()) , mValue(mantissa) , mOffset(0) , mIsNative(true) @@ -206,9 +227,25 @@ STAmount::STAmount(SField const& name, std::uint64_t mantissa, bool negative) assert(mValue <= std::numeric_limits::max()); } +STAmount::STAmount( + SField const& name, + Issue const& issue, + std::uint64_t mantissa, + int exponent, + bool negative) + : STBase(name) + , mIssue(issue) + , mValue(mantissa) + , mOffset(exponent) + , mIsNegative(negative) +{ + assert(mValue <= std::numeric_limits::max()); + canonicalize(); +} + STAmount::STAmount(SField const& name, STAmount const& from) : STBase(name) - , mAsset(from.mAsset) + , mIssue(from.mIssue) , mValue(from.mValue) , mOffset(from.mOffset) , mIsNegative(from.mIsNegative) @@ -220,8 +257,7 @@ STAmount::STAmount(SField const& name, STAmount const& from) //------------------------------------------------------------------------------ STAmount::STAmount(std::uint64_t mantissa, bool negative) - : mAsset(xrpIssue()) - , mValue(mantissa) + : mValue(mantissa) , mOffset(0) , mIsNative(true) , mIsNegative(mantissa != 0 && negative) @@ -229,11 +265,54 @@ STAmount::STAmount(std::uint64_t mantissa, bool negative) assert(mValue <= std::numeric_limits::max()); } -STAmount::STAmount(XRPAmount const& amount) - : mAsset(xrpIssue()) - , mOffset(0) - , mIsNative(true) +STAmount::STAmount( + Issue const& issue, + std::uint64_t mantissa, + int exponent, + bool negative) + : mIssue(issue), mValue(mantissa), mOffset(exponent), mIsNegative(negative) +{ + canonicalize(); +} + +STAmount::STAmount(Issue const& issue, std::int64_t mantissa, int exponent) + : mIssue(issue), mOffset(exponent) +{ + set(mantissa); + canonicalize(); +} + +STAmount::STAmount( + Issue const& issue, + std::uint32_t mantissa, + int exponent, + bool negative) + : STAmount(issue, safe_cast(mantissa), exponent, negative) +{ +} + +STAmount::STAmount(Issue const& issue, int mantissa, int exponent) + : STAmount(issue, safe_cast(mantissa), exponent) +{ +} + +// Legacy support for new-style amounts +STAmount::STAmount(IOUAmount const& amount, Issue const& issue) + : mIssue(issue) + , mOffset(amount.exponent()) + , mIsNative(false) , mIsNegative(amount < beast::zero) +{ + if (mIsNegative) + mValue = static_cast(-amount.mantissa()); + else + mValue = static_cast(amount.mantissa()); + + canonicalize(); +} + +STAmount::STAmount(XRPAmount const& amount) + : mOffset(0), mIsNative(true), mIsNegative(amount < beast::zero) { if (mIsNegative) mValue = unsafe_cast(-amount.drops()); @@ -284,7 +363,7 @@ STAmount::xrp() const IOUAmount STAmount::iou() const { - if (mIsNative || isMPT()) + if (mIsNative) Throw("Cannot return native STAmount as IOUAmount"); auto mantissa = static_cast(mValue); @@ -296,20 +375,6 @@ STAmount::iou() const return {mantissa, exponent}; } -MPTAmount -STAmount::mpt() const -{ - if (!isMPT()) - Throw("Cannot return STAmount as MPTAmount"); - - auto value = static_cast(mValue); - - if (mIsNegative) - value = -value; - - return MPTAmount{value}; -} - STAmount& STAmount::operator=(IOUAmount const& iou) { @@ -357,17 +422,14 @@ operator+(STAmount const& v1, STAmount const& v2) // Result must be in terms of v1 currency and issuer. return { v1.getFName(), - v1.asset(), + v1.issue(), v2.mantissa(), v2.exponent(), v2.negative()}; } - // TODO if (v1.native()) return {v1.getFName(), getSNValue(v1) + getSNValue(v2)}; - if (v1.isMPT()) - return {v1.mAsset, v1.mpt().mpt() + v2.mpt().mpt()}; if (getSTNumberSwitchover()) { @@ -404,18 +466,18 @@ operator+(STAmount const& v1, STAmount const& v2) std::int64_t fv = vv1 + vv2; if ((fv >= -10) && (fv <= 10)) - return {v1.getFName(), v1.asset()}; + return {v1.getFName(), v1.issue()}; if (fv >= 0) return STAmount{ v1.getFName(), - v1.asset(), + v1.issue(), static_cast(fv), ov1, false}; return STAmount{ - v1.getFName(), v1.asset(), static_cast(-fv), ov1, true}; + v1.getFName(), v1.issue(), static_cast(-fv), ov1, true}; } STAmount @@ -431,7 +493,7 @@ std::uint64_t const STAmount::uRateOne = getRate(STAmount(1), STAmount(1)); void STAmount::setIssue(Issue const& issue) { - mAsset = issue; + mIssue = issue; mIsNative = isXRP(*this); } @@ -476,13 +538,8 @@ STAmount::setJson(Json::Value& elem) const // It is an error for currency or issuer not to be specified for valid // json. elem[jss::value] = getText(); - if (mAsset.isMPT()) - elem[jss::mpt_issuance_id] = to_string(mAsset.mptIssue()); - else - { - elem[jss::currency] = to_string(mAsset.issue().currency); - elem[jss::issuer] = to_string(mAsset.issue().account); - } + elem[jss::currency] = to_string(mIssue.currency); + elem[jss::issuer] = to_string(mIssue.account); } else { @@ -508,7 +565,7 @@ STAmount::getFullText() const std::string ret; ret.reserve(64); - ret = getText() + "/" + mAsset.getText(); + ret = getText() + "/" + mIssue.getText(); return ret; } @@ -528,7 +585,7 @@ STAmount::getText() const bool const scientific( (mOffset != 0) && ((mOffset < -25) || (mOffset > -5))); - if (mIsNative || mAsset.isMPT() || scientific) + if (mIsNative || scientific) { ret.append(raw_value); @@ -607,44 +664,31 @@ Json::Value STAmount::getJson(JsonOptions) const void STAmount::add(Serializer& s) const { - // TODO MPT make sure backward compatible if (mIsNative) { assert(mOffset == 0); if (!mIsNegative) - s.add64(mValue | cPositive); + s.add64(mValue | cPosNative); else s.add64(mValue); } else { - if (mAsset.isMPT()) - { - if (mIsNegative) - s.add64(mValue | cMPToken); - else - s.add64(mValue | cMPToken | cPositive); - auto const& mptIssue = mAsset.mptIssue(); - s.addBitString(mptIssue.getMptID()); - } - else - { - if (*this == beast::zero) - s.add64(cIssuedCurrency); - else if (mIsNegative) // 512 = not native - s.add64( - mValue | - (static_cast(mOffset + 512 + 97) - << (64 - 10))); - else // 256 = positive - s.add64( - mValue | - (static_cast(mOffset + 512 + 256 + 97) - << (64 - 10))); - s.addBitString(mAsset.issue().currency); - s.addBitString(mAsset.issue().account); - } + if (*this == beast::zero) + s.add64(cNotNative); + else if (mIsNegative) // 512 = not native + s.add64( + mValue | + (static_cast(mOffset + 512 + 97) << (64 - 10))); + else // 256 = positive + s.add64( + mValue | + (static_cast(mOffset + 512 + 256 + 97) + << (64 - 10))); + + s.addBitString(mIssue.currency); + s.addBitString(mIssue.account); } } @@ -682,10 +726,10 @@ STAmount::isDefault() const void STAmount::canonicalize() { - if (isXRP(*this) || mAsset.isMPT()) + if (isXRP(*this)) { // native currency amounts should always have an offset of zero - mIsNative = isXRP(*this); + mIsNative = true; // log(2^64,10) ~ 19.2 if (mValue == 0 || mOffset <= -20) @@ -708,18 +752,9 @@ STAmount::canonicalize() { Number num( mIsNegative ? -mValue : mValue, mOffset, Number::unchecked{}); - if (mIsNative) - { - XRPAmount xrp{num}; - mIsNegative = xrp.drops() < 0; - mValue = mIsNegative ? -xrp.drops() : xrp.drops(); - } - else - { - MPTAmount c{num}; - mIsNegative = c.mpt() < 0; - mValue = mIsNegative ? -c.mpt() : c.mpt(); - } + XRPAmount xrp{num}; + mIsNegative = xrp.drops() < 0; + mValue = mIsNegative ? -xrp.drops() : xrp.drops(); mOffset = 0; } else @@ -828,7 +863,7 @@ amountFromQuality(std::uint64_t rate) } STAmount -amountFromString(Asset const& asset, std::string const& amount) +amountFromString(Issue const& issue, std::string const& amount) { static boost::regex const reNumber( "^" // the beginning of the string @@ -861,7 +896,7 @@ amountFromString(Asset const& asset, std::string const& amount) bool negative = (match[1].matched && (match[1] == "-")); // Can't specify XRP using fractional representation - if (isXRP(asset) && match[3].matched) + if (isXRP(issue) && match[3].matched) Throw("XRP must be specified in integral drops."); std::uint64_t mantissa; @@ -889,152 +924,7 @@ amountFromString(Asset const& asset, std::string const& amount) exponent += beast::lexicalCastThrow(std::string(match[7])); } - return {asset, mantissa, exponent, negative}; -} - -STAmount -amountFromJson(SField const& name, Json::Value const& v) -{ - STAmount::mantissa_type mantissa = 0; - STAmount::exponent_type exponent = 0; - bool negative = false; - Asset asset; - - Json::Value value; - Json::Value currencyOrMPTID; - Json::Value issuer; - bool isMPT = false; - - if (v.isNull()) - { - Throw( - "XRP may not be specified with a null Json value"); - } - else if (v.isObject()) - { - if (!validJSONAsset(v)) - Throw("Invalid Asset's Json specification"); - - value = v[jss::value]; - if (v.isMember(jss::mpt_issuance_id)) - { - isMPT = true; - currencyOrMPTID = v[jss::mpt_issuance_id]; - } - else - { - currencyOrMPTID = v[jss::currency]; - issuer = v[jss::issuer]; - } - } - else if (v.isArray()) - { - value = v.get(Json::UInt(0), 0); - currencyOrMPTID = v.get(Json::UInt(1), Json::nullValue); - issuer = v.get(Json::UInt(2), Json::nullValue); - } - else if (v.isString()) - { - std::string val = v.asString(); - std::vector elements; - boost::split(elements, val, boost::is_any_of("\t\n\r ,/")); - - if (elements.size() > 3) - Throw("invalid amount string"); - - value = elements[0]; - - if (elements.size() > 1) - currencyOrMPTID = elements[1]; - - if (elements.size() > 2) - issuer = elements[2]; - } - else - { - value = v; - } - - bool const native = !currencyOrMPTID.isString() || - currencyOrMPTID.asString().empty() || - (currencyOrMPTID.asString() == systemCurrencyCode()); - - if (native) - { - if (v.isObjectOrNull()) - Throw("XRP may not be specified as an object"); - asset = xrpIssue(); - } - else - { - if (isMPT) - { - // sequence (32 bits) + account (160 bits) - uint192 u; - if (!u.parseHex(currencyOrMPTID.asString())) - Throw("invalid MPTokenIssuanceID"); - asset = u; - } - else - { - Issue issue; - if (!to_currency(issue.currency, currencyOrMPTID.asString())) - Throw("invalid currency"); - if (!issuer.isString() || - !to_issuer(issue.account, issuer.asString())) - Throw("invalid issuer"); - if (isXRP(issue)) - Throw("invalid issuer"); - asset = issue; - } - } - - if (value.isInt()) - { - if (value.asInt() >= 0) - { - mantissa = value.asInt(); - } - else - { - mantissa = -value.asInt(); - negative = true; - } - } - else if (value.isUInt()) - { - mantissa = v.asUInt(); - } - else if (value.isString()) - { - auto const ret = amountFromString(asset, value.asString()); - - mantissa = ret.mantissa(); - exponent = ret.exponent(); - negative = ret.negative(); - } - else - { - Throw("invalid amount type"); - } - - return {name, asset, mantissa, exponent, native, negative}; -} - -bool -amountFromJsonNoThrow(STAmount& result, Json::Value const& jvSource) -{ - try - { - result = amountFromJson(sfGeneric, jvSource); - return true; - } - catch (const std::exception& e) - { - JLOG(debugLog().warn()) - << "amountFromJsonNoThrow: caught: " << e.what(); - } - return false; + return STAmount{issue, mantissa, exponent, negative}; } //------------------------------------------------------------------------------ @@ -1092,7 +982,7 @@ operator-(STAmount const& value) return value; return STAmount( value.getFName(), - value.asset(), + value.issue(), value.mantissa(), value.exponent(), value.native(), @@ -1154,20 +1044,20 @@ muldiv_round( } STAmount -divide(STAmount const& num, STAmount const& den, Asset const& asset) +divide(STAmount const& num, STAmount const& den, Issue const& issue) { if (den == beast::zero) Throw("division by zero"); if (num == beast::zero) - return {asset}; + return {issue}; std::uint64_t numVal = num.mantissa(); std::uint64_t denVal = den.mantissa(); int numOffset = num.exponent(); int denOffset = den.exponent(); - if (num.native() || num.isMPT()) + if (num.native()) { while (numVal < STAmount::cMinValue) { @@ -1177,7 +1067,7 @@ divide(STAmount const& num, STAmount const& den, Asset const& asset) } } - if (den.native() || den.isMPT()) + if (den.native()) { while (denVal < STAmount::cMinValue) { @@ -1192,19 +1082,19 @@ divide(STAmount const& num, STAmount const& den, Asset const& asset) // 10^32 to 10^33) followed by a division, so the result // is in the range of 10^16 to 10^15. return STAmount( - asset, + issue, muldiv(numVal, tenTo17, denVal) + 5, numOffset - denOffset - 17, num.negative() != den.negative()); } STAmount -multiply(STAmount const& v1, STAmount const& v2, Asset const& asset) +multiply(STAmount const& v1, STAmount const& v2, Issue const& issue) { if (v1 == beast::zero || v2 == beast::zero) - return STAmount(asset); + return STAmount(issue); - if (v1.native() && v2.native() && isXRP(asset)) + if (v1.native() && v2.native() && isXRP(issue)) { std::uint64_t const minV = getSNValue(v1) < getSNValue(v2) ? getSNValue(v1) : getSNValue(v2); @@ -1219,34 +1109,16 @@ multiply(STAmount const& v1, STAmount const& v2, Asset const& asset) return STAmount(v1.getFName(), minV * maxV); } - if (v1.isMPT() && v2.isMPT() && asset.isMPT()) - { - std::uint64_t const minV = getMPTValue(v1) < getMPTValue(v2) - ? getMPTValue(v1) - : getMPTValue(v2); - std::uint64_t const maxV = getMPTValue(v1) < getMPTValue(v2) - ? getMPTValue(v2) - : getMPTValue(v1); - - if (minV > 3000000000ull) // sqrt(cMaxNative) - Throw("Asset value overflow"); - - if (((maxV >> 32) * minV) > 2095475792ull) // cMaxNative / 2^32 - Throw("Asset value overflow"); - - return STAmount(asset, minV * maxV); - } if (getSTNumberSwitchover()) - return {IOUAmount{Number{v1} * Number{v2}}, asset}; + return {IOUAmount{Number{v1} * Number{v2}}, issue}; std::uint64_t value1 = v1.mantissa(); std::uint64_t value2 = v2.mantissa(); int offset1 = v1.exponent(); int offset2 = v2.exponent(); - // TODO MPT - if (v1.native() || v1.isMPT()) + if (v1.native()) { while (value1 < STAmount::cMinValue) { @@ -1255,7 +1127,7 @@ multiply(STAmount const& v1, STAmount const& v2, Asset const& asset) } } - if (v2.native() || v2.isMPT()) + if (v2.native()) { while (value2 < STAmount::cMinValue) { @@ -1269,7 +1141,7 @@ multiply(STAmount const& v1, STAmount const& v2, Asset const& asset) // range. Dividing their product by 10^14 maintains the // precision, by scaling the result to 10^16 to 10^18. return STAmount( - asset, + issue, muldiv(value1, value2, tenTo14) + 7, offset1 + offset2 + 14, v1.negative() != v2.negative()); @@ -1406,15 +1278,14 @@ static STAmount mulRoundImpl( STAmount const& v1, STAmount const& v2, - Asset const& asset, + Issue const& issue, bool roundUp) { if (v1 == beast::zero || v2 == beast::zero) - return {asset}; + return {issue}; - bool const xrp = isXRP(asset); + bool const xrp = isXRP(issue); - // TODO MPT if (v1.native() && v2.native() && xrp) { std::uint64_t minV = @@ -1430,30 +1301,11 @@ mulRoundImpl( return STAmount(v1.getFName(), minV * maxV); } - // TODO MPT - if (v1.isMPT() && v2.isMPT() && asset.isMPT()) - { - std::uint64_t minV = (getMPTValue(v1) < getMPTValue(v2)) - ? getMPTValue(v1) - : getMPTValue(v2); - std::uint64_t maxV = (getMPTValue(v1) < getMPTValue(v2)) - ? getMPTValue(v2) - : getMPTValue(v1); - - if (minV > 3000000000ull) // sqrt(cMaxNative) - Throw("Asset value overflow"); - - if (((maxV >> 32) * minV) > 2095475792ull) // cMaxNative / 2^32 - Throw("Asset value overflow"); - - return STAmount(asset, minV * maxV); - } std::uint64_t value1 = v1.mantissa(), value2 = v2.mantissa(); int offset1 = v1.exponent(), offset2 = v2.exponent(); - // TODO MPT - if (v1.native() || v1.isMPT()) + if (v1.native()) { while (value1 < STAmount::cMinValue) { @@ -1462,8 +1314,7 @@ mulRoundImpl( } } - // TODO MPT - if (v2.native() || v2.isMPT()) + if (v2.native()) { while (value2 < STAmount::cMinValue) { @@ -1494,7 +1345,7 @@ mulRoundImpl( // If appropriate, tell Number to round down. This gives the desired // result from STAmount::canonicalize. MightSaveRound const savedRound(Number::towards_zero); - return STAmount(asset, amount, offset, resultNegative); + return STAmount(issue, amount, offset, resultNegative); }(); if (roundUp && !resultNegative && !result) @@ -1511,7 +1362,7 @@ mulRoundImpl( amount = STAmount::cMinValue; offset = STAmount::cMinOffset; } - return STAmount(asset, amount, offset, resultNegative); + return STAmount(issue, amount, offset, resultNegative); } return result; } @@ -1520,22 +1371,22 @@ STAmount mulRound( STAmount const& v1, STAmount const& v2, - Asset const& asset, + Issue const& issue, bool roundUp) { return mulRoundImpl( - v1, v2, asset, roundUp); + v1, v2, issue, roundUp); } STAmount mulRoundStrict( STAmount const& v1, STAmount const& v2, - Asset const& asset, + Issue const& issue, bool roundUp) { return mulRoundImpl( - v1, v2, asset, roundUp); + v1, v2, issue, roundUp); } // We might need to use NumberRoundModeGuard. Allow the caller @@ -1545,20 +1396,19 @@ static STAmount divRoundImpl( STAmount const& num, STAmount const& den, - Asset const& asset, + Issue const& issue, bool roundUp) { if (den == beast::zero) Throw("division by zero"); if (num == beast::zero) - return {asset}; + return {issue}; std::uint64_t numVal = num.mantissa(), denVal = den.mantissa(); int numOffset = num.exponent(), denOffset = den.exponent(); - // TODO MPT - if (num.native() || num.isMPT()) + if (num.native()) { while (numVal < STAmount::cMinValue) { @@ -1567,8 +1417,7 @@ divRoundImpl( } } - // TODO MPT - if (den.native() || den.isMPT()) + if (den.native()) { while (denVal < STAmount::cMinValue) { @@ -1592,10 +1441,8 @@ divRoundImpl( int offset = numOffset - denOffset - 17; - // TODO MPT if (resultNegative != roundUp) - canonicalizeRound( - isXRP(asset) /*|| issue.isMPT()*/, amount, offset, roundUp); + canonicalizeRound(isXRP(issue), amount, offset, roundUp); STAmount result = [&]() { // If appropriate, tell Number the rounding mode we are using. @@ -1604,12 +1451,12 @@ divRoundImpl( using enum Number::rounding_mode; MightSaveRound const savedRound( roundUp ^ resultNegative ? upward : downward); - return STAmount(asset, amount, offset, resultNegative); + return STAmount(issue, amount, offset, resultNegative); }(); if (roundUp && !resultNegative && !result) { - if (isXRP(asset) || asset.isMPT()) + if (isXRP(issue)) { // return the smallest value above zero amount = 1; @@ -1621,7 +1468,7 @@ divRoundImpl( amount = STAmount::cMinValue; offset = STAmount::cMinOffset; } - return STAmount(asset, amount, offset, resultNegative); + return STAmount(issue, amount, offset, resultNegative); } return result; } @@ -1630,20 +1477,20 @@ STAmount divRound( STAmount const& num, STAmount const& den, - Asset const& asset, + Issue const& issue, bool roundUp) { - return divRoundImpl(num, den, asset, roundUp); + return divRoundImpl(num, den, issue, roundUp); } STAmount divRoundStrict( STAmount const& num, STAmount const& den, - Asset const& asset, + Issue const& issue, bool roundUp) { - return divRoundImpl(num, den, asset, roundUp); + return divRoundImpl(num, den, issue, roundUp); } } // namespace ripple diff --git a/src/libxrpl/protocol/STEitherAmount.cpp b/src/libxrpl/protocol/STEitherAmount.cpp new file mode 100644 index 00000000000..dd18665d816 --- /dev/null +++ b/src/libxrpl/protocol/STEitherAmount.cpp @@ -0,0 +1,442 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include + +#include + +namespace ripple { + +STEitherAmount::STEitherAmount(SerialIter& sit, SField const& name) + : STBase(name) +{ + auto const value = sit.get64(); + if ((value & STAmount::cNotNative) == 0 && + (value & STMPTAmount::cMPToken) != 0) + amount_.emplace(value, sit, name); + else + amount_.emplace(value, sit, name); +} + +STEitherAmount::STEitherAmount(XRPAmount const& amount) : amount_{amount} +{ +} + +STEitherAmount::STEitherAmount(STAmount const& amount) + : STBase(amount.getFName()), amount_{amount} +{ +} + +STEitherAmount::STEitherAmount(SField const& name, STAmount const& amount) + : STBase(name), amount_{amount} +{ +} + +STEitherAmount::STEitherAmount(STMPTAmount const& amount) + : STBase(amount.getFName()), amount_{amount} +{ +} + +STEitherAmount& +STEitherAmount::operator=(STAmount const& amount) +{ + setFName(amount.getFName()); + amount_ = amount; + return *this; +} + +STEitherAmount& +STEitherAmount::operator=(STMPTAmount const& amount) +{ + setFName(amount.getFName()); + amount_ = amount; + return *this; +} + +STEitherAmount& +STEitherAmount::operator=(XRPAmount const& amount) +{ + amount_ = amount; + return *this; +} + +SerializedTypeID +STEitherAmount::getSType() const +{ + return STI_AMOUNT; +} + +std::string +STEitherAmount::getFullText() const +{ + return std::visit([&](auto&& a) { return a.getFullText(); }, amount_); +} + +std::string +STEitherAmount::getText() const +{ + return std::visit([&](auto&& a) { return a.getText(); }, amount_); +} + +Json::Value STEitherAmount::getJson(JsonOptions) const +{ + return std::visit( + [&](auto&& a) { return a.getJson(JsonOptions::none); }, amount_); +} + +void +STEitherAmount::setJson(Json::Value& jv) const +{ + std::visit([&](auto&& a) { a.setJson(jv); }, amount_); +} + +void +STEitherAmount::add(Serializer& s) const +{ + std::visit([&](auto&& a) { a.add(s); }, amount_); +} + +bool +STEitherAmount::isEquivalent(const STBase& t) const +{ + const STEitherAmount* v = dynamic_cast(&t); + return v && + std::visit( + [&](auto&& a, auto&& a1) { return a.isEquivalent(a1); }, + amount_, + v->amount_); +} + +bool +STEitherAmount::isDefault() const +{ + return std::visit([&](auto&& a) { return a.isDefault(); }, amount_); +} +//------------------------------------------------------------------------------ +bool +STEitherAmount::isMPT() const +{ + return std::holds_alternative(amount_); +} + +bool +STEitherAmount::isIssue() const +{ + return std::holds_alternative(amount_); +} + +bool +STEitherAmount::badAsset() const +{ + if (isIssue()) + return badCurrency() == std::get(amount_).getCurrency(); + return badMPT() == std::get(amount_).getCurrency(); +} + +bool +STEitherAmount::negative() const +{ + if (isIssue()) + return std::get(amount_).negative(); + return false; +} + +bool +STEitherAmount::native() const +{ + if (isIssue()) + return std::get(amount_).native(); + return false; +} + +STEitherAmount +STEitherAmount::zeroed() const +{ + return std::visit( + [&](auto&& a) { return STEitherAmount{a.zeroed()}; }, amount_); +} + +bool +STEitherAmount::sameAsset(STEitherAmount const& amount) const +{ + return std::visit( + [&](T1&& a1, T2&& a2) { + if constexpr (std::is_same_v) + return a1.getCurrency() == a2.getCurrency(); + else + return false; + }, + amount_, + amount.amount_); +} + +bool +STEitherAmount::sameIssue(STEitherAmount const& amount) const +{ + return std::visit( + [&](T1&& a1, T2&& a2) { + if constexpr (std::is_same_v) + return a1.issue() == a2.issue(); + else + return false; + }, + amount_, + amount.amount_); +} + +STEitherAmount const& +STEitherAmount::value() const +{ + return *this; +} + +std::variant const& +STEitherAmount::getValue() const +{ + return amount_; +} + +std::variant& +STEitherAmount::getValue() +{ + return amount_; +} + +AccountID const& +STEitherAmount::getIssuer() const +{ + if (isIssue()) + return get().getIssuer(); + return get().getIssuer(); +} + +STBase* +STEitherAmount::copy(std::size_t n, void* buf) const +{ + return emplace(n, buf, *this); +} + +STBase* +STEitherAmount::move(std::size_t n, void* buf) +{ + return emplace(n, buf, std::move(*this)); +} + +int +STEitherAmount::signum() const noexcept +{ + return std::visit([&](auto&& a) { return a.signum(); }, amount_); +} + +static bool +validJSONIssue(Json::Value const& jv) +{ + return (jv.isMember(jss::currency) && !jv.isMember(jss::mpt_issuance_id)) || + (!jv.isMember(jss::currency) && !jv.isMember(jss::issuer) && + jv.isMember(jss::mpt_issuance_id)); +} + +STEitherAmount +amountFromJson(SField const& name, Json::Value const& v) +{ + STAmount::mantissa_type mantissa = 0; + STAmount::exponent_type exponent = 0; + bool negative = false; + std::variant issue; + + Json::Value value; + Json::Value currencyOrMPTID; + Json::Value issuer; + bool isMPT = false; + + if (v.isNull()) + { + Throw( + "XRP may not be specified with a null Json value"); + } + else if (v.isObject()) + { + if (!validJSONIssue(v)) + Throw("Invalid Issue's Json specification"); + + value = v[jss::value]; + if (v.isMember(jss::mpt_issuance_id)) + { + isMPT = true; + currencyOrMPTID = v[jss::mpt_issuance_id]; + } + else + { + currencyOrMPTID = v[jss::currency]; + issuer = v[jss::issuer]; + } + } + else if (v.isArray()) + { + value = v.get(Json::UInt(0), 0); + currencyOrMPTID = v.get(Json::UInt(1), Json::nullValue); + issuer = v.get(Json::UInt(2), Json::nullValue); + } + else if (v.isString()) + { + std::string val = v.asString(); + std::vector elements; + boost::split(elements, val, boost::is_any_of("\t\n\r ,/")); + + if (elements.size() > 3) + Throw("invalid amount string"); + + value = elements[0]; + + if (elements.size() > 1) + currencyOrMPTID = elements[1]; + + if (elements.size() > 2) + issuer = elements[2]; + } + else + { + value = v; + } + + bool const native = !currencyOrMPTID.isString() || + currencyOrMPTID.asString().empty() || + (currencyOrMPTID.asString() == systemCurrencyCode()); + + if (native) + { + if (v.isObjectOrNull()) + Throw("XRP may not be specified as an object"); + issue = xrpIssue(); + } + else + { + if (isMPT) + { + // sequence (32 bits) + account (160 bits) + uint192 u; + if (!u.parseHex(currencyOrMPTID.asString())) + Throw("invalid MPTokenIssuanceID"); + issue = u; + } + else + { + issue = Issue{}; + if (!to_currency( + std::get(issue).currency, + currencyOrMPTID.asString())) + Throw("invalid currency"); + if (!issuer.isString() || + !to_issuer(std::get(issue).account, issuer.asString())) + Throw("invalid issuer"); + if (isXRP(std::get(issue))) + Throw("invalid issuer"); + } + } + + if (value.isInt()) + { + if (value.asInt() >= 0) + { + mantissa = value.asInt(); + } + else + { + mantissa = -value.asInt(); + negative = true; + } + } + else if (value.isUInt()) + { + mantissa = v.asUInt(); + } + else if (value.isString()) + { + if (std::holds_alternative(issue)) + { + STAmount const ret = + amountFromString(std::get(issue), value.asString()); + mantissa = ret.mantissa(); + exponent = ret.exponent(); + negative = ret.negative(); + } + else + { + STMPTAmount const ret = + amountFromString(std::get(issue), value.asString()); + mantissa = ret.value(); + exponent = 0; + negative = false; + } + } + else + { + Throw("invalid amount type"); + } + + if (std::holds_alternative(issue)) + return STAmount{ + name, std::get(issue), mantissa, exponent, native, negative}; + while (exponent-- > 0) + mantissa *= 10; + if (mantissa > 0x8000000000000000) + Throw("MPT amount out of range"); + return STMPTAmount{ + name, std::get(issue), static_cast(mantissa)}; +} + +bool +amountFromJsonNoThrow(STEitherAmount& result, Json::Value const& jvSource) +{ + try + { + result = amountFromJson(sfGeneric, jvSource); + return true; + } + catch (const std::exception& e) + { + JLOG(debugLog().warn()) + << "amountFromJsonNoThrow: caught: " << e.what(); + } + return false; +} + +bool +amountFromJsonNoThrow(STAmount& result, Json::Value const& jvSource) +{ + try + { + STEitherAmount amount; + const bool res = amountFromJsonNoThrow(amount, jvSource); + if (res) + result = get(amount); + return res; + } + catch (const std::exception& e) + { + JLOG(debugLog().warn()) + << "amountFromJsonNoThrow: caught: " << e.what(); + } + return false; +} + +} // namespace ripple diff --git a/src/libxrpl/protocol/STMPTAmount.cpp b/src/libxrpl/protocol/STMPTAmount.cpp new file mode 100644 index 00000000000..ffec54a1b3c --- /dev/null +++ b/src/libxrpl/protocol/STMPTAmount.cpp @@ -0,0 +1,233 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include + +#include + +namespace ripple { + +STMPTAmount::STMPTAmount( + std::uint64_t value, + SerialIter& sit, + SField const& name) + : STBase(name) +{ + assert(value & cMPToken); + value_ = (value << 8) | sit.get8(); + value_ &= ~cMPToken; + + issue_ = sit.get192(); +} + +STMPTAmount::STMPTAmount( + SField const& name, + MPTIssue const& issue, + value_type value) + : MPTAmount(value), STBase(name), issue_(issue) +{ +} + +STMPTAmount::STMPTAmount(MPTIssue const& issue, value_type value) + : MPTAmount(value), issue_(issue) +{ +} + +STMPTAmount::STMPTAmount(MPTIssue const& issue, std::uint64_t value) + : issue_(issue) +{ + if (value > cMaxMPTValue) + Throw("MPTAmount is out of range"); + value_ = static_cast(value); +} + +STMPTAmount::STMPTAmount(value_type value) : MPTAmount(value) +{ +} + +SerializedTypeID +STMPTAmount::getSType() const +{ + return STI_AMOUNT; +} + +std::string +STMPTAmount::getFullText() const +{ + std::string ret; + + ret.reserve(64); + ret = getText() + "/" + to_string(issue_.getMptID()); + return ret; +} + +std::string +STMPTAmount::getText() const +{ + return std::to_string(value_); +} + +Json::Value STMPTAmount::getJson(JsonOptions) const +{ + Json::Value elem; + setJson(elem); + return elem; +} + +void +STMPTAmount::setJson(Json::Value& elem) const +{ + elem[jss::mpt_issuance_id] = to_string(issue_.getMptID()); + elem[jss::value] = getText(); +} + +void +STMPTAmount::add(Serializer& s) const +{ + auto u8 = static_cast(cMPToken >> 56); + s.add8(u8); + s.add64(value_); + s.addBitString(issue_.getMptID()); +} + +bool +STMPTAmount::isEquivalent(const STBase& t) const +{ + const STMPTAmount* v = dynamic_cast(&t); + return v && operator==(*v); +} + +bool +STMPTAmount::isDefault() const +{ + return value_ == 0 && issue_ == badMPT(); +} + +AccountID const& +STMPTAmount::getIssuer() const +{ + return issue_.getIssuer(); +} + +uint192 +STMPTAmount::getCurrency() const +{ + return issue_.getMptID(); +} + +MPTIssue const& +STMPTAmount::issue() const +{ + return issue_; +} + +void +STMPTAmount::clear() +{ + value_ = 0; +} + +void +STMPTAmount::clear(MPTIssue const& issue) +{ + issue_ = issue; + value_ = 0; +} + +STMPTAmount +STMPTAmount::zeroed() const +{ + return STMPTAmount{issue_}; +} + +int +STMPTAmount::signum() const noexcept +{ + return value_ ? 1 : 0; +} + +STMPTAmount +amountFromString(MPTIssue const& issue, std::string const& amount) +{ + static boost::regex const reNumber( + "^" // the beginning of the string + "([+]?)" // (optional) + character (MPT is positive) + "(0|[1-9][0-9]*)" // a number (no leading zeroes, unless 0) + "(\\.([0-9]+))?" // (optional) period followed by any number + "([eE]([+-]?)([0-9]+))?" // (optional) E, optional + or -, any number + "$", + boost::regex_constants::optimize); + + boost::smatch match; + + if (!boost::regex_match(amount, match, reNumber)) + Throw("MPT '" + amount + "' is not valid"); + + // Match fields: + // 0 = whole input + // 1 = sign + // 2 = integer portion + // 3 = whole fraction (with '.') + // 4 = fraction (without '.') + // 5 = whole exponent (with 'e') + // 6 = exponent sign + // 7 = exponent number + + // CHECKME: Why 32? Shouldn't this be 16? + if ((match[2].length() + match[4].length()) > 32) + Throw("Number '" + amount + "' is overlong"); + + // Can't specify MPT using fractional representation + if (match[3].matched) + Throw("MPT must be specified as integral."); + + std::int64_t mantissa; + int exponent; + + if (!match[4].matched) // integer only + { + mantissa = + beast::lexicalCastThrow(std::string(match[2])); + exponent = 0; + } + else + { + // integer and fraction + mantissa = beast::lexicalCastThrow(match[2] + match[4]); + exponent = -(match[4].length()); + } + + if (match[5].matched) + { + // we have an exponent + if (match[6].matched && (match[6] == "-")) + exponent -= beast::lexicalCastThrow(std::string(match[7])); + else + exponent += beast::lexicalCastThrow(std::string(match[7])); + } + + while (exponent-- > 0) + mantissa *= 10; + + return {issue, mantissa}; +} + +} // namespace ripple \ No newline at end of file diff --git a/src/libxrpl/protocol/STObject.cpp b/src/libxrpl/protocol/STObject.cpp index 7e62fc25bd6..c84e5b7bf57 100644 --- a/src/libxrpl/protocol/STObject.cpp +++ b/src/libxrpl/protocol/STObject.cpp @@ -165,6 +165,17 @@ STObject::applyTemplate(const SOTemplate& type) e.sField().fieldName, "may not be explicitly set to default."); } + if (iter->get().getSType() == STI_AMOUNT && !e.supportMPT()) + { + if (auto const v = dynamic_cast(&iter->get()); + v && v->isMPT()) + { + if (auto const& rules = getCurrentTransactionRules(); + rules && rules->enabled(featureMPTokensV1)) + throwFieldErr( + e.sField().fieldName, "doesn't support MPT"); + } + } v.emplace_back(std::move(*iter)); v_.erase(iter); } @@ -630,11 +641,11 @@ STObject::getFieldVL(SField const& field) const return Blob(b.data(), b.data() + b.size()); } -STAmount const& +STEitherAmount const& STObject::getFieldAmount(SField const& field) const { - static STAmount const empty{}; - return getFieldByConstRef(field, empty); + static STEitherAmount const empty{}; + return getFieldByConstRef(field, empty); } STPathSet const& @@ -748,7 +759,7 @@ STObject::setFieldVL(SField const& field, Slice const& s) } void -STObject::setFieldAmount(SField const& field, STAmount const& v) +STObject::setFieldAmount(SField const& field, STEitherAmount const& v) { setFieldUsingAssignment(field, v); } diff --git a/src/libxrpl/protocol/STParsedJSON.cpp b/src/libxrpl/protocol/STParsedJSON.cpp index ecc51d5275f..92044334431 100644 --- a/src/libxrpl/protocol/STParsedJSON.cpp +++ b/src/libxrpl/protocol/STParsedJSON.cpp @@ -556,8 +556,8 @@ parseLeaf( case STI_AMOUNT: try { - ret = - detail::make_stvar(amountFromJson(field, value)); + ret = detail::make_stvar( + amountFromJson(field, value)); } catch (std::exception const&) { diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index c43dac121d8..29ffc25c1d8 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -143,14 +143,9 @@ STTx::getMentionedAccounts() const } else if (auto samt = dynamic_cast(&it)) { - if (samt->isIssue()) - { - auto const& issuer = samt->getIssuer(); - if (!isXRP(issuer)) - list.insert(issuer); - } - else - list.insert(samt->getIssuer()); + auto const& issuer = samt->getIssuer(); + if (!isXRP(issuer)) + list.insert(issuer); } } @@ -548,9 +543,16 @@ isAccountAndMPTFieldOkay(STObject const& st) auto t = dynamic_cast(st.peekAtPIndex(i)); if (t && t->isDefault()) return false; - auto amt = dynamic_cast(st.peekAtPIndex(i)); - if (amt && amt->isMPT() && !isMPTAmountAllowed) - return false; + if (st.peekAtIndex(i).getSType() == STI_AMOUNT) + { + auto amt = dynamic_cast(st.peekAtPIndex(i)); + if (amt && amt->isMPT() && !isMPTAmountAllowed) + { + if (auto const& rules = getCurrentTransactionRules(); + rules && rules->enabled(featureMPTokensV1)) + return false; + } + } } return true; diff --git a/src/libxrpl/protocol/STVar.cpp b/src/libxrpl/protocol/STVar.cpp index 0cb52b5d24e..8dbe0a70550 100644 --- a/src/libxrpl/protocol/STVar.cpp +++ b/src/libxrpl/protocol/STVar.cpp @@ -133,7 +133,7 @@ STVar::STVar(SerialIter& sit, SField const& name, int depth) construct(sit, name); return; case STI_AMOUNT: - construct(sit, name); + construct(sit, name); return; case STI_UINT128: construct(sit, name); @@ -200,7 +200,7 @@ STVar::STVar(SerializedTypeID id, SField const& name) construct(name); return; case STI_AMOUNT: - construct(name); + construct(name); return; case STI_UINT128: construct(name); diff --git a/src/libxrpl/protocol/TER.cpp b/src/libxrpl/protocol/TER.cpp index cd0dac413e7..38842c34758 100644 --- a/src/libxrpl/protocol/TER.cpp +++ b/src/libxrpl/protocol/TER.cpp @@ -190,6 +190,7 @@ transResults() MAKE_ERROR(temINVALID, "The transaction is ill-formed."), MAKE_ERROR(temINVALID_FLAG, "The transaction has an invalid flag."), MAKE_ERROR(temMPT_NOT_SUPPORTED, "MPT is not supported."), + MAKE_ERROR(temMPT_INVALID_USAGE, "Invalid MPT usage."), MAKE_ERROR(temREDUNDANT, "The transaction is redundant."), MAKE_ERROR(temRIPPLE_EMPTY, "PathSet with no paths."), MAKE_ERROR(temUNCERTAIN, "In process of determining result. Never returned."), diff --git a/src/libxrpl/protocol/TxFormats.cpp b/src/libxrpl/protocol/TxFormats.cpp index 236d443009f..f5195f41411 100644 --- a/src/libxrpl/protocol/TxFormats.cpp +++ b/src/libxrpl/protocol/TxFormats.cpp @@ -162,12 +162,12 @@ TxFormats::TxFormats() ttPAYMENT, { {sfDestination, soeREQUIRED}, - {sfAmount, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTYes}, {sfSendMax, soeOPTIONAL}, {sfPaths, soeDEFAULT}, {sfInvoiceID, soeOPTIONAL}, {sfDestinationTag, soeOPTIONAL}, - {sfDeliverMin, soeOPTIONAL}, + {sfDeliverMin, soeOPTIONAL, soeMPTYes}, }, commonFields); @@ -377,7 +377,7 @@ TxFormats::TxFormats() add(jss::Clawback, ttCLAWBACK, { - {sfAmount, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTYes}, {sfMPTokenHolder, soeOPTIONAL}, }, commonFields); diff --git a/src/libxrpl/protocol/TxMeta.cpp b/src/libxrpl/protocol/TxMeta.cpp index 253d00e8414..85ed5dc487e 100644 --- a/src/libxrpl/protocol/TxMeta.cpp +++ b/src/libxrpl/protocol/TxMeta.cpp @@ -144,7 +144,7 @@ TxMeta::getAffectedAccounts() const (field.getFName() == sfTakerPays) || (field.getFName() == sfTakerGets)) { - auto lim = dynamic_cast(&field); + auto lim = dynamic_cast(&field); assert(lim); if (lim != nullptr) diff --git a/src/libxrpl/protocol/XChainAttestations.cpp b/src/libxrpl/protocol/XChainAttestations.cpp index 82e73445693..10a5aaf0549 100644 --- a/src/libxrpl/protocol/XChainAttestations.cpp +++ b/src/libxrpl/protocol/XChainAttestations.cpp @@ -107,7 +107,7 @@ AttestationBase::AttestationBase(STObject const& o) , publicKey{o[sfPublicKey]} , signature{o[sfSignature]} , sendingAccount{o[sfAccount]} - , sendingAmount{o[sfAmount]} + , sendingAmount{get(o[sfAmount])} , rewardAccount{o[sfAttestationRewardAccount]} , wasLockingChainSend{bool(o[sfWasLockingChainSend])} { @@ -132,7 +132,7 @@ AttestationBase::addHelper(STObject& o) const o[sfAttestationSignerAccount] = attestationSignerAccount; o[sfPublicKey] = publicKey; o[sfSignature] = signature; - o[sfAmount] = sendingAmount; + o[sfAmount] = STEitherAmount{sendingAmount}; o[sfAccount] = sendingAccount; o[sfAttestationRewardAccount] = rewardAccount; o[sfWasLockingChainSend] = wasLockingChainSend; @@ -225,7 +225,7 @@ AttestationClaim::message( STObject o{sfGeneric}; // Serialize in SField order to make python serializers easier to write o[sfXChainClaimID] = claimID; - o[sfAmount] = sendingAmount; + o[sfAmount] = STEitherAmount{sendingAmount}; if (dst) o[sfDestination] = *dst; o[sfOtherChainSource] = sendingAccount; @@ -276,7 +276,7 @@ AttestationCreateAccount::AttestationCreateAccount(STObject const& o) : AttestationBase(o) , createCount{o[sfXChainAccountCreateCount]} , toCreate{o[sfDestination]} - , rewardAmount{o[sfSignatureReward]} + , rewardAmount{get(o[sfSignatureReward])} { } @@ -352,7 +352,7 @@ AttestationCreateAccount::toSTObject() const o[sfXChainAccountCreateCount] = createCount; o[sfDestination] = toCreate; - o[sfSignatureReward] = rewardAmount; + o[sfSignatureReward] = STEitherAmount{rewardAmount}; return o; } @@ -371,8 +371,8 @@ AttestationCreateAccount::message( STObject o{sfGeneric}; // Serialize in SField order to make python serializers easier to write o[sfXChainAccountCreateCount] = createCount; - o[sfAmount] = sendingAmount; - o[sfSignatureReward] = rewardAmount; + o[sfAmount] = STEitherAmount{sendingAmount}; + o[sfSignatureReward] = STEitherAmount{rewardAmount}; o[sfDestination] = dst; o[sfOtherChainSource] = sendingAccount; o[sfAttestationRewardAccount] = rewardAccount; @@ -466,7 +466,7 @@ XChainClaimAttestation::XChainClaimAttestation(STObject const& o) : XChainClaimAttestation{ o[sfAttestationSignerAccount], PublicKey{o[sfPublicKey]}, - o[sfAmount], + get(o[sfAmount]), o[sfAttestationRewardAccount], o[sfWasLockingChainSend] != 0, o[~sfDestination]} {}; @@ -503,7 +503,7 @@ XChainClaimAttestation::toSTObject() const o[sfAttestationSignerAccount] = STAccount{sfAttestationSignerAccount, keyAccount}; o[sfPublicKey] = publicKey; - o[sfAmount] = STAmount{sfAmount, amount}; + o[sfAmount] = STEitherAmount(amount); o[sfAttestationRewardAccount] = STAccount{sfAttestationRewardAccount, rewardAccount}; o[sfWasLockingChainSend] = wasLockingChainSend; @@ -576,8 +576,8 @@ XChainCreateAccountAttestation::XChainCreateAccountAttestation( : XChainCreateAccountAttestation{ o[sfAttestationSignerAccount], PublicKey{o[sfPublicKey]}, - o[sfAmount], - o[sfSignatureReward], + get(o[sfAmount]), + get(o[sfSignatureReward]), o[sfAttestationRewardAccount], o[sfWasLockingChainSend] != 0, o[sfDestination]} {}; @@ -616,8 +616,8 @@ XChainCreateAccountAttestation::toSTObject() const o[sfAttestationSignerAccount] = STAccount{sfAttestationSignerAccount, keyAccount}; o[sfPublicKey] = publicKey; - o[sfAmount] = STAmount{sfAmount, amount}; - o[sfSignatureReward] = STAmount{sfSignatureReward, rewardAmount}; + o[sfAmount] = STEitherAmount{sfAmount, amount}; + o[sfSignatureReward] = STEitherAmount{sfSignatureReward, rewardAmount}; o[sfAttestationRewardAccount] = STAccount{sfAttestationRewardAccount, rewardAccount}; o[sfWasLockingChainSend] = wasLockingChainSend; diff --git a/src/test/app/AMMExtended_test.cpp b/src/test/app/AMMExtended_test.cpp index 96053b93b44..fc53ba3f016 100644 --- a/src/test/app/AMMExtended_test.cpp +++ b/src/test/app/AMMExtended_test.cpp @@ -1547,8 +1547,7 @@ struct AMMExtended_test : public jtx::AMMTest Env env = pathTestEnv(); fund(env, gw, {alice, bob, charlie}, {USD(11)}, Fund::All); AMM ammCharlie(env, charlie, XRP(10), USD(11)); - auto [st, sa, da] = - find_paths(env, alice, bob, USD(-1), XRP(1).value()); + auto [st, sa, da] = find_paths(env, alice, bob, USD(-1), XRP(1)); BEAST_EXPECT(sa == XRP(1)); BEAST_EXPECT(equal(da, USD(1))); if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1)) @@ -1565,8 +1564,7 @@ struct AMMExtended_test : public jtx::AMMTest fund(env, gw, {alice, bob, charlie}, {USD(11)}, Fund::All); AMM ammCharlie(env, charlie, XRP(11), USD(10)); env.close(); - auto [st, sa, da] = - find_paths(env, alice, bob, drops(-1), USD(1).value()); + auto [st, sa, da] = find_paths(env, alice, bob, drops(-1), USD(1)); BEAST_EXPECT(sa == USD(1)); BEAST_EXPECT(equal(da, XRP(1))); if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1)) @@ -1916,7 +1914,7 @@ struct AMMExtended_test : public jtx::AMMTest sendmax(EUR(500)), txflags(tfNoRippleDirect | tfPartialPayment)); - auto const carolUSD = env.balance(carol, USD).value(); + auto const carolUSD = get(env.balance(carol, USD)); BEAST_EXPECT(carolUSD > USD(0) && carolUSD < USD(50)); } diff --git a/src/test/app/Check_test.cpp b/src/test/app/Check_test.cpp index 31b45abf43a..0a899c82671 100644 --- a/src/test/app/Check_test.cpp +++ b/src/test/app/Check_test.cpp @@ -211,7 +211,7 @@ class Check_test : public beast::unit_test::suite Env env{*this, features}; - STAmount const startBalance{XRP(1000).value()}; + STAmount const startBalance{XRP(1000)}; env.fund(startBalance, gw, alice, bob); // Note that no trust line has been set up for alice, but alice can @@ -321,7 +321,7 @@ class Check_test : public beast::unit_test::suite Env env{*this, features | disallowIncoming}; - STAmount const startBalance{XRP(1000).value()}; + STAmount const startBalance{XRP(1000)}; env.fund(startBalance, gw, alice, bob); /* @@ -405,7 +405,7 @@ class Check_test : public beast::unit_test::suite Env env{*this, features}; - STAmount const startBalance{XRP(1000).value()}; + STAmount const startBalance{XRP(1000)}; env.fund(startBalance, gw1, gwF, alice, bob); // Bad fee. @@ -587,7 +587,7 @@ class Check_test : public beast::unit_test::suite Env env{*this, features}; XRPAmount const baseFeeDrops{env.current()->fees().base}; - STAmount const startBalance{XRP(300).value()}; + STAmount const startBalance{XRP(300)}; env.fund(startBalance, alice, bob); { // Basic XRP check. @@ -1192,8 +1192,8 @@ class Check_test : public beast::unit_test::suite double pct, double amount) { // Capture bob's and alice's balances so we can test at the end. - STAmount const aliceStart{env.balance(alice, USD.issue()).value()}; - STAmount const bobStart{env.balance(bob, USD.issue()).value()}; + STAmount const aliceStart{env.balance(alice, USD.issue())}; + STAmount const bobStart{env.balance(bob, USD.issue())}; // Set the modified quality. env(trust(truster, iou(1000)), inOrOut(pct)); @@ -1217,8 +1217,8 @@ class Check_test : public beast::unit_test::suite double pct, double amount) { // Capture bob's and alice's balances so we can test at the end. - STAmount const aliceStart{env.balance(alice, USD.issue()).value()}; - STAmount const bobStart{env.balance(bob, USD.issue()).value()}; + STAmount const aliceStart{env.balance(alice, USD.issue())}; + STAmount const bobStart{env.balance(bob, USD.issue())}; // Set the modified quality. env(trust(truster, iou(1000)), inOrOut(pct)); @@ -1281,7 +1281,7 @@ class Check_test : public beast::unit_test::suite double max2) { // Capture alice's balance so we can test at the end. It doesn't // make any sense to look at the balance of a gateway. - STAmount const aliceStart{env.balance(alice, USD.issue()).value()}; + STAmount const aliceStart{env.balance(alice, USD.issue())}; // Set the modified quality. env(trust(truster, iou(1000)), inOrOut(pct)); @@ -1314,7 +1314,7 @@ class Check_test : public beast::unit_test::suite double max2) { // Capture alice's balance so we can test at the end. It doesn't // make any sense to look at the balance of the issuer. - STAmount const aliceStart{env.balance(alice, USD.issue()).value()}; + STAmount const aliceStart{env.balance(alice, USD.issue())}; // Set the modified quality. env(trust(truster, iou(1000)), inOrOut(pct)); @@ -2147,8 +2147,10 @@ class Check_test : public beast::unit_test::suite // without comparing the currency. auto cmpReqAmount = [this, offerLine, checkLine](SF_AMOUNT const& sfield) { - STAmount const offerAmount = offerLine->at(sfield); - STAmount const checkAmount = checkLine->at(sfield); + STAmount const offerAmount = + get(offerLine->at(sfield)); + STAmount const checkAmount = + get(checkLine->at(sfield)); // Neither STAmount should be native. if (!BEAST_EXPECT( diff --git a/src/test/app/Flow_test.cpp b/src/test/app/Flow_test.cpp index 9d1257d16bf..250871747c4 100644 --- a/src/test/app/Flow_test.cpp +++ b/src/test/app/Flow_test.cpp @@ -551,7 +551,8 @@ struct Flow_test : public beast::unit_test::suite return std::stoull(bookDirStr, nullptr, 16); }(); std::uint64_t const actualRate = getRate( - usdOffer->at(sfTakerGets), usdOffer->at(sfTakerPays)); + get(usdOffer->at(sfTakerGets)), + get(usdOffer->at(sfTakerPays))); // We expect the actual rate of the offer to be worse // (larger) than the rate of the book page holding the @@ -763,7 +764,7 @@ struct Flow_test : public beast::unit_test::suite sendmax(EUR(500)), txflags(tfNoRippleDirect | tfPartialPayment)); - auto const carolUSD = env.balance(carol, USD).value(); + auto const carolUSD = get(env.balance(carol, USD)); BEAST_EXPECT(carolUSD > USD(0) && carolUSD < USD(50)); } diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index 3bda0a3911a..1128d7b1fd8 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -337,7 +337,8 @@ class MPToken_test : public beast::unit_test::suite env, alice, {.holders = {&bob}, - .xrpHolders = acctReserve + XRP(1).value().xrp()}); + .xrpHolders = + acctReserve + get(XRP(1).value()).xrp()}); mptAlice1.create(); MPTTester mptAlice2(env, alice, {.fund = false}); @@ -860,7 +861,8 @@ class MPToken_test : public beast::unit_test::suite mptAlice.create(); - env(offer(alice, mptAlice.mpt(100), XRP(100)), ter(telENV_RPC_FAILED)); + env(offer(alice, mptAlice.mpt(100), XRP(100)), + ter(temMPT_NOT_SUPPORTED)); env.close(); BEAST_EXPECT(expectOffers(env, alice, 0)); diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp index 2b4245a1ae4..76019741c34 100644 --- a/src/test/app/Offer_test.cpp +++ b/src/test/app/Offer_test.cpp @@ -1876,8 +1876,7 @@ class OfferBaseUtil_test : public beast::unit_test::suite jrr = ledgerEntryRoot(env, bob); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - std::to_string( - XRP(10000).value().mantissa() + XRP(250).value().mantissa())); + std::to_string((XRP(10000) + XRP(250)).value().mantissa())); auto jro = ledgerEntryOffer(env, carol, carolOfferSeq); BEAST_EXPECT( @@ -2302,12 +2301,12 @@ class OfferBaseUtil_test : public beast::unit_test::suite jtx::Account const& account, jtx::PrettyAmount const& expectBalance) { - auto const sleTrust = - env.le(keylet::line(account.id(), expectBalance.value().issue())); + auto const sleTrust = env.le( + keylet::line(account.id(), get(expectBalance).issue())); BEAST_EXPECT(sleTrust); if (sleTrust) { - Issue const issue = expectBalance.value().issue(); + Issue const issue = get(expectBalance).issue(); bool const accountLow = account.id() < issue.account; STAmount low{issue}; @@ -2316,10 +2315,13 @@ class OfferBaseUtil_test : public beast::unit_test::suite low.setIssuer(accountLow ? account.id() : issue.account); high.setIssuer(accountLow ? issue.account : account.id()); - BEAST_EXPECT(sleTrust->getFieldAmount(sfLowLimit) == low); - BEAST_EXPECT(sleTrust->getFieldAmount(sfHighLimit) == high); + BEAST_EXPECT( + get(sleTrust->getFieldAmount(sfLowLimit)) == low); + BEAST_EXPECT( + get(sleTrust->getFieldAmount(sfHighLimit)) == high); - STAmount actualBalance{sleTrust->getFieldAmount(sfBalance)}; + STAmount actualBalance{ + get(sleTrust->getFieldAmount(sfBalance))}; if (!accountLow) actualBalance.negate(); @@ -2953,8 +2955,10 @@ class OfferBaseUtil_test : public beast::unit_test::suite auto const& acctOffer = *(acctOffers.front()); BEAST_EXPECT(acctOffer[sfLedgerEntryType] == ltOFFER); - BEAST_EXPECT(acctOffer[sfTakerGets] == t.takerGets); - BEAST_EXPECT(acctOffer[sfTakerPays] == t.takerPays); + BEAST_EXPECT( + get(acctOffer[sfTakerGets]) == t.takerGets); + BEAST_EXPECT( + get(acctOffer[sfTakerPays]) == t.takerPays); } } @@ -3849,7 +3853,7 @@ class OfferBaseUtil_test : public beast::unit_test::suite auto const& offer = *offerPtr; BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER); BEAST_EXPECT( - offer[sfTakerGets] == + get(offer[sfTakerGets]) == STAmount(JPY.issue(), std::uint64_t(2230682446713524ul), -12)); BEAST_EXPECT(offer[sfTakerPays] == BTC(0.035378)); } @@ -3914,10 +3918,10 @@ class OfferBaseUtil_test : public beast::unit_test::suite auto const& offer = *offerPtr; BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER); BEAST_EXPECT( - offer[sfTakerGets] == + get(offer[sfTakerGets]) == STAmount(USD.issue(), std::uint64_t(2185847305256635), -14)); BEAST_EXPECT( - offer[sfTakerPays] == + get(offer[sfTakerPays]) == STAmount(JPY.issue(), std::uint64_t(2286608293434156), -12)); } } @@ -4105,7 +4109,8 @@ class OfferBaseUtil_test : public beast::unit_test::suite actorOffers.begin(), actorOffers.end(), [](std::shared_ptr& offer) { - return (*offer)[sfTakerGets].signum() == 0; + return get((*offer)[sfTakerGets]) + .signum() == 0; })); BEAST_EXPECT(offerCount == actor.offers); @@ -4262,7 +4267,8 @@ class OfferBaseUtil_test : public beast::unit_test::suite actorOffers.begin(), actorOffers.end(), [](std::shared_ptr& offer) { - return (*offer)[sfTakerGets].signum() == 0; + return get((*offer)[sfTakerGets]) + .signum() == 0; })); BEAST_EXPECT(offerCount == actor.offers); @@ -4811,7 +4817,8 @@ class OfferBaseUtil_test : public beast::unit_test::suite offers.emplace( (*sle)[sfSequence], std::make_pair( - (*sle)[sfTakerPays], (*sle)[sfTakerGets])); + get((*sle)[sfTakerPays]), + get((*sle)[sfTakerGets]))); }); // first offer @@ -5134,12 +5141,12 @@ class OfferBaseUtil_test : public beast::unit_test::suite env(pay(issuer, maker, EUR(1'000))); env.close(); - auto makerUSDBalance = env.balance(maker, USD).value(); - auto takerUSDBalance = env.balance(taker, USD).value(); - auto makerEURBalance = env.balance(maker, EUR).value(); - auto takerEURBalance = env.balance(taker, EUR).value(); - auto makerXRPBalance = env.balance(maker, XRP).value(); - auto takerXRPBalance = env.balance(taker, XRP).value(); + auto makerUSDBalance = get(env.balance(maker, USD)); + auto takerUSDBalance = get(env.balance(taker, USD)); + auto makerEURBalance = get(env.balance(maker, EUR)); + auto takerEURBalance = get(env.balance(taker, EUR)); + auto makerXRPBalance = get(env.balance(maker, XRP)); + auto takerXRPBalance = get(env.balance(taker, XRP)); // tfFillOrKill, TakerPays must be filled { @@ -5162,8 +5169,8 @@ class OfferBaseUtil_test : public beast::unit_test::suite { makerUSDBalance -= USD(100); takerUSDBalance += USD(100); - makerXRPBalance += XRP(100).value(); - takerXRPBalance -= XRP(100).value(); + makerXRPBalance += XRP(100); + takerXRPBalance -= XRP(100); } BEAST_EXPECT(expectOffers(env, taker, 0)); @@ -5181,8 +5188,8 @@ class OfferBaseUtil_test : public beast::unit_test::suite { makerUSDBalance += USD(100); takerUSDBalance -= USD(100); - makerXRPBalance -= XRP(100).value(); - takerXRPBalance += XRP(100).value(); + makerXRPBalance -= XRP(100); + takerXRPBalance += XRP(100); } BEAST_EXPECT(expectOffers(env, taker, 0)); @@ -5217,8 +5224,8 @@ class OfferBaseUtil_test : public beast::unit_test::suite makerUSDBalance -= USD(101); takerUSDBalance += USD(101); - makerXRPBalance += XRP(101).value() - txfee(env, 1); - takerXRPBalance -= XRP(101).value() + txfee(env, 1); + makerXRPBalance += XRP(101) - txfee(env, 1); + takerXRPBalance -= XRP(101) + txfee(env, 1); BEAST_EXPECT(expectOffers(env, taker, 0)); env(offer(maker, USD(101), XRP(101))); @@ -5230,8 +5237,8 @@ class OfferBaseUtil_test : public beast::unit_test::suite makerUSDBalance += USD(101); takerUSDBalance -= USD(101); - makerXRPBalance -= XRP(101).value() + txfee(env, 1); - takerXRPBalance += XRP(101).value() - txfee(env, 1); + makerXRPBalance -= XRP(101) + txfee(env, 1); + takerXRPBalance += XRP(101) - txfee(env, 1); BEAST_EXPECT(expectOffers(env, taker, 0)); env(offer(maker, USD(101), EUR(101))); diff --git a/src/test/app/Path_test.cpp b/src/test/app/Path_test.cpp index 1db15388ff0..cb0f031a360 100644 --- a/src/test/app/Path_test.cpp +++ b/src/test/app/Path_test.cpp @@ -197,7 +197,8 @@ class Path_test : public beast::unit_test::suite STAmount da; if (result.isMember(jss::destination_amount)) - da = amountFromJson(sfGeneric, result[jss::destination_amount]); + da = get( + amountFromJson(sfGeneric, result[jss::destination_amount])); STAmount sa; STPathSet paths; @@ -209,11 +210,12 @@ class Path_test : public beast::unit_test::suite auto const& path = alts[0u]; if (path.isMember(jss::source_amount)) - sa = amountFromJson(sfGeneric, path[jss::source_amount]); + sa = get( + amountFromJson(sfGeneric, path[jss::source_amount])); if (path.isMember(jss::destination_amount)) - da = amountFromJson( - sfGeneric, path[jss::destination_amount]); + da = get(amountFromJson( + sfGeneric, path[jss::destination_amount])); if (path.isMember(jss::paths_computed)) { @@ -1244,8 +1246,7 @@ class Path_test : public beast::unit_test::suite env.close(); env(offer(charlie, XRP(10), USD(10))); env.close(); - auto [st, sa, da] = - find_paths(env, alice, bob, USD(-1), XRP(100).value()); + auto [st, sa, da] = find_paths(env, alice, bob, USD(-1), XRP(100)); BEAST_EXPECT(sa == XRP(10)); BEAST_EXPECT(equal(da, USD(10))); if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1)) @@ -1268,7 +1269,7 @@ class Path_test : public beast::unit_test::suite env(offer(charlie, USD(10), XRP(10))); env.close(); auto [st, sa, da] = - find_paths(env, alice, bob, drops(-1), USD(100).value()); + find_paths(env, alice, bob, drops(-1), USD(100)); BEAST_EXPECT(sa == USD(10)); BEAST_EXPECT(equal(da, XRP(10))); if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1)) diff --git a/src/test/app/PayChan_test.cpp b/src/test/app/PayChan_test.cpp index a8eae4a25ec..d645d45db4d 100644 --- a/src/test/app/PayChan_test.cpp +++ b/src/test/app/PayChan_test.cpp @@ -65,7 +65,7 @@ struct PayChan_test : public beast::unit_test::suite auto const slep = view.read({ltPAYCHAN, chan}); if (!slep) return XRPAmount{-1}; - return (*slep)[sfAmount]; + return get((*slep)[sfAmount]); } static std::optional @@ -137,9 +137,9 @@ struct PayChan_test : public beast::unit_test::suite { // No signature claim with bad amounts (negative and non-xrp) - auto const iou = USDA(100).value(); - auto const negXRP = XRP(-100).value(); - auto const posXRP = XRP(100).value(); + auto const iou = USDA(100); + auto const negXRP = XRP(-100); + auto const posXRP = XRP(100); env(claim(alice, chan, iou, iou), ter(temBAD_AMOUNT)); env(claim(alice, chan, posXRP, iou), ter(temBAD_AMOUNT)); env(claim(alice, chan, iou, posXRP), ter(temBAD_AMOUNT)); @@ -215,13 +215,7 @@ struct PayChan_test : public beast::unit_test::suite { // Wrong signing key auto const sig = signClaimAuth(bob.pk(), bob.sk(), chan, XRP(1500)); - env(claim( - bob, - chan, - XRP(1500).value(), - XRP(1500).value(), - Slice(sig), - bob.pk()), + env(claim(bob, chan, XRP(1500), XRP(1500), Slice(sig), bob.pk()), ter(temBAD_SIGNER)); BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal); BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt); @@ -229,13 +223,7 @@ struct PayChan_test : public beast::unit_test::suite { // Bad signature auto const sig = signClaimAuth(bob.pk(), bob.sk(), chan, XRP(1500)); - env(claim( - bob, - chan, - XRP(1500).value(), - XRP(1500).value(), - Slice(sig), - alice.pk()), + env(claim(bob, chan, XRP(1500), XRP(1500), Slice(sig), alice.pk()), ter(temBAD_SIGNATURE)); BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal); BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt); @@ -551,7 +539,7 @@ struct PayChan_test : public beast::unit_test::suite { // claim the entire amount auto const preBob = env.balance(bob); - env(claim(alice, chan, channelFunds.value(), channelFunds.value())); + env(claim(alice, chan, channelFunds, channelFunds)); BEAST_EXPECT(channelBalance(*env.current(), chan) == channelFunds); BEAST_EXPECT(env.balance(bob) == preBob + channelFunds); } @@ -659,7 +647,7 @@ struct PayChan_test : public beast::unit_test::suite BEAST_EXPECT(channelExists(*env.current(), chan)); env(fset(bob, asfDisallowXRP)); - auto const reqBal = XRP(500).value(); + auto const reqBal = XRP(500); env(claim(alice, chan, reqBal, reqBal), ter(tecNO_TARGET)); } { @@ -673,7 +661,7 @@ struct PayChan_test : public beast::unit_test::suite BEAST_EXPECT(channelExists(*env.current(), chan)); env(fset(bob, asfDisallowXRP)); - auto const reqBal = XRP(500).value(); + auto const reqBal = XRP(500); env(claim(alice, chan, reqBal, reqBal)); } } @@ -741,15 +729,14 @@ struct PayChan_test : public beast::unit_test::suite env.close(); // alice claims. Fails because bob's lsfDepositAuth flag is set. - env(claim(alice, chan, XRP(500).value(), XRP(500).value()), - ter(tecNO_PERMISSION)); + env(claim(alice, chan, XRP(500), XRP(500)), ter(tecNO_PERMISSION)); env.close(); // Claim with signature auto const baseFee = env.current()->fees().base; auto const preBob = env.balance(bob); { - auto const delta = XRP(500).value(); + auto const delta = XRP(500); auto const sig = signClaimAuth(pk, alice.sk(), chan, delta); // alice claims with signature. Fails since bob has @@ -773,7 +760,7 @@ struct PayChan_test : public beast::unit_test::suite } { // Explore the limits of deposit preauthorization. - auto const delta = XRP(600).value(); + auto const delta = XRP(600); auto const sig = signClaimAuth(pk, alice.sk(), chan, delta); // carol claims and fails. Only channel participants (bob or @@ -810,7 +797,7 @@ struct PayChan_test : public beast::unit_test::suite { // bob removes preauthorization of alice. Once again she // cannot submit a claim. - auto const delta = XRP(800).value(); + auto const delta = XRP(800); env(deposit::unauth(bob, alice)); env.close(); @@ -1524,13 +1511,7 @@ struct PayChan_test : public beast::unit_test::suite auto const authAmt = XRP(100); auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, authAmt); - jv = claim( - bob, - chan, - authAmt.value(), - authAmt.value(), - Slice(sig), - alice.pk()); + jv = claim(bob, chan, authAmt, authAmt, Slice(sig), alice.pk()); jv["PublicKey"] = pkHex.substr(2, pkHex.size() - 2); env(jv, ter(temMALFORMED)); jv["PublicKey"] = pkHex.substr(0, pkHex.size() - 2); diff --git a/src/test/app/PayStrand_test.cpp b/src/test/app/PayStrand_test.cpp index f00a7361292..ec3a9a9b605 100644 --- a/src/test/app/PayStrand_test.cpp +++ b/src/test/app/PayStrand_test.cpp @@ -483,7 +483,7 @@ struct ExistingElementPool auto const sle = v.read(keylet::account(a)); if (!sle) return; - auto const b = (*sle)[sfBalance]; + auto const b = get((*sle)[sfBalance]); totalXRP += b.mantissa(); }; for (auto const& a : accounts) @@ -504,13 +504,13 @@ struct ExistingElementPool auto const sle = v.read(k); if (!sle) return STAmount{}; - return (*sle)[sfBalance]; + return get((*sle)[sfBalance]); }; auto lineBalance = [](ReadView const& v, ripple::Keylet const& k) { auto const sle = v.read(k); if (!sle) return STAmount{}; - return (*sle)[sfBalance]; + return get((*sle)[sfBalance]); }; std::uint64_t totalXRP[2]{}; for (auto ai1 = accounts.begin(), aie = accounts.end(); ai1 != aie; diff --git a/src/test/app/ReducedOffer_test.cpp b/src/test/app/ReducedOffer_test.cpp index a070051e435..a59d0dcd534 100644 --- a/src/test/app/ReducedOffer_test.cpp +++ b/src/test/app/ReducedOffer_test.cpp @@ -134,10 +134,14 @@ class ReducedOffer_test : public beast::unit_test::suite Json::Value bobOffer = ledgerEntryOffer(env, bob, bobOfferSeq); - STAmount const reducedTakerGets = amountFromJson( - sfTakerGets, bobOffer[jss::node][sfTakerGets.jsonName]); - STAmount const reducedTakerPays = amountFromJson( - sfTakerPays, bobOffer[jss::node][sfTakerPays.jsonName]); + STAmount const reducedTakerGets = + get(amountFromJson( + sfTakerGets, + bobOffer[jss::node][sfTakerGets.jsonName])); + STAmount const reducedTakerPays = + get(amountFromJson( + sfTakerPays, + bobOffer[jss::node][sfTakerPays.jsonName])); STAmount const bobGot = env.balance(bob) + bobsFee - bobInitialBalance; BEAST_EXPECT(reducedTakerPays < newOffer.in); @@ -290,12 +294,14 @@ class ReducedOffer_test : public beast::unit_test::suite Json::Value aliceOffer = ledgerEntryOffer(env, alice, aliceOfferSeq); - STAmount const reducedTakerGets = amountFromJson( - sfTakerGets, - aliceOffer[jss::node][sfTakerGets.jsonName]); - STAmount const reducedTakerPays = amountFromJson( - sfTakerPays, - aliceOffer[jss::node][sfTakerPays.jsonName]); + STAmount const reducedTakerGets = + get(amountFromJson( + sfTakerGets, + aliceOffer[jss::node][sfTakerGets.jsonName])); + STAmount const reducedTakerPays = + get(amountFromJson( + sfTakerPays, + aliceOffer[jss::node][sfTakerPays.jsonName])); STAmount const aliceGot = env.balance(alice) - aliceInitialBalance; BEAST_EXPECT(reducedTakerPays < inLedger.in); @@ -612,10 +618,10 @@ class ReducedOffer_test : public beast::unit_test::suite Amounts jsonOfferToAmounts(Json::Value const& json) { - STAmount const in = - amountFromJson(sfTakerPays, json[sfTakerPays.jsonName]); - STAmount const out = - amountFromJson(sfTakerGets, json[sfTakerGets.jsonName]); + STAmount const in = get( + amountFromJson(sfTakerPays, json[sfTakerPays.jsonName])); + STAmount const out = get( + amountFromJson(sfTakerGets, json[sfTakerGets.jsonName])); return {in, out}; } diff --git a/src/test/app/Regression_test.cpp b/src/test/app/Regression_test.cpp index f54a88ace00..609d425f1e9 100644 --- a/src/test/app/Regression_test.cpp +++ b/src/test/app/Regression_test.cpp @@ -86,7 +86,7 @@ struct Regression_test : public beast::unit_test::suite { auto const sle = next->read(keylet::account(Account("alice").id())); BEAST_EXPECT(sle); - auto balance = sle->getFieldAmount(sfBalance); + auto balance = get(sle->getFieldAmount(sfBalance)); BEAST_EXPECT(balance == aliceAmount); } @@ -108,7 +108,7 @@ struct Regression_test : public beast::unit_test::suite { auto const sle = next->read(keylet::account(Account("alice").id())); BEAST_EXPECT(sle); - auto balance = sle->getFieldAmount(sfBalance); + auto balance = get(sle->getFieldAmount(sfBalance)); BEAST_EXPECT(balance == XRP(0)); } @@ -194,7 +194,7 @@ struct Regression_test : public beast::unit_test::suite { BEAST_EXPECT(tx->getAccountID(sfAccount) == alice.id()); BEAST_EXPECT(tx->getTxnType() == ttACCOUNT_SET); - auto const fee = tx->getFieldAmount(sfFee); + auto const fee = get(tx->getFieldAmount(sfFee)); BEAST_EXPECT(fee == drops(expectedFees[i])); } } diff --git a/src/test/app/SetAuth_test.cpp b/src/test/app/SetAuth_test.cpp index 3dd8ab590a4..adb909314d3 100644 --- a/src/test/app/SetAuth_test.cpp +++ b/src/test/app/SetAuth_test.cpp @@ -38,8 +38,8 @@ struct SetAuth_test : public beast::unit_test::suite using namespace jtx; Json::Value jv; jv[jss::Account] = account.human(); - jv[jss::LimitAmount] = STAmount(Issue{to_currency(currency), dest}) - .getJson(JsonOptions::none); + jv[jss::LimitAmount] = + STAmount({to_currency(currency), dest}).getJson(JsonOptions::none); jv[jss::TransactionType] = jss::TrustSet; jv[jss::Flags] = tfSetfAuth; return jv; diff --git a/src/test/app/TheoreticalQuality_test.cpp b/src/test/app/TheoreticalQuality_test.cpp index 917d23377bf..cc52a1484c3 100644 --- a/src/test/app/TheoreticalQuality_test.cpp +++ b/src/test/app/TheoreticalQuality_test.cpp @@ -48,10 +48,11 @@ struct RippleCalcTestParams explicit RippleCalcTestParams(Json::Value const& jv) : srcAccount{*parseBase58(jv[jss::Account].asString())} , dstAccount{*parseBase58(jv[jss::Destination].asString())} - , dstAmt{amountFromJson(sfAmount, jv[jss::Amount])} + , dstAmt{get(amountFromJson(sfAmount, jv[jss::Amount]))} { if (jv.isMember(jss::SendMax)) - sendMax = amountFromJson(sfSendMax, jv[jss::SendMax]); + sendMax = + get(amountFromJson(sfSendMax, jv[jss::SendMax])); if (jv.isMember(jss::Paths)) { diff --git a/src/test/app/TxQ_test.cpp b/src/test/app/TxQ_test.cpp index e7b70203c91..a1df4d997b1 100644 --- a/src/test/app/TxQ_test.cpp +++ b/src/test/app/TxQ_test.cpp @@ -1257,8 +1257,10 @@ class TxQPosNegFlows_test : public beast::unit_test::suite // bankrupt Alice. Fails, because an account can't have // more than the minimum reserve in flight before the // last queued transaction - aliceFee = - env.le(alice)->getFieldAmount(sfBalance).xrp().drops() - (62); + aliceFee = get(env.le(alice)->getFieldAmount(sfBalance)) + .xrp() + .drops() - + (62); env(noop(alice), seq(aliceSeq), fee(aliceFee), @@ -1318,7 +1320,10 @@ class TxQPosNegFlows_test : public beast::unit_test::suite // with enough fee to bankrupt bob and make the // later transactions unable to pay their fees std::int64_t bobFee = - env.le(bob)->getFieldAmount(sfBalance).xrp().drops() - (9 * 10 - 1); + get(env.le(bob)->getFieldAmount(sfBalance)) + .xrp() + .drops() - + (9 * 10 - 1); env(noop(bob), seq(bobSeq + 5), fee(bobFee), @@ -1331,8 +1336,10 @@ class TxQPosNegFlows_test : public beast::unit_test::suite // // The attempt fails because the sum of bob's fees now exceeds the // (artificially lowered to 200 drops) account reserve. - bobFee = - env.le(bob)->getFieldAmount(sfBalance).xrp().drops() - (9 * 10); + bobFee = get(env.le(bob)->getFieldAmount(sfBalance)) + .xrp() + .drops() - + (9 * 10); env(noop(bob), seq(bobSeq + 5), fee(bobFee), diff --git a/src/test/app/XChain_test.cpp b/src/test/app/XChain_test.cpp index 4f24d17601e..21a5ed44c30 100644 --- a/src/test/app/XChain_test.cpp +++ b/src/test/app/XChain_test.cpp @@ -122,13 +122,13 @@ struct SEnv STAmount balance(jtx::Account const& account) const { - return env_.balance(account).value(); + return env_.balance(account); } STAmount balance(jtx::Account const& account, Issue const& issue) const { - return env_.balance(account, issue).value(); + return env_.balance(account, issue); } XRPAmount diff --git a/src/test/jtx/PathSet.h b/src/test/jtx/PathSet.h index 0f4c4ddd3dd..55a084501e5 100644 --- a/src/test/jtx/PathSet.h +++ b/src/test/jtx/PathSet.h @@ -40,8 +40,10 @@ countOffers( forEachItem( *env.current(), account, [&](std::shared_ptr const& sle) { if (sle->getType() == ltOFFER && - sle->getFieldAmount(sfTakerPays).issue() == takerPays && - sle->getFieldAmount(sfTakerGets).issue() == takerGets) + get(sle->getFieldAmount(sfTakerPays)).issue() == + takerPays && + get(sle->getFieldAmount(sfTakerGets)).issue() == + takerGets) ++count; }); return count; @@ -58,8 +60,8 @@ countOffers( forEachItem( *env.current(), account, [&](std::shared_ptr const& sle) { if (sle->getType() == ltOFFER && - sle->getFieldAmount(sfTakerPays) == takerPays && - sle->getFieldAmount(sfTakerGets) == takerGets) + get(sle->getFieldAmount(sfTakerPays)) == takerPays && + get(sle->getFieldAmount(sfTakerGets)) == takerGets) ++count; }); return count; diff --git a/src/test/jtx/amount.h b/src/test/jtx/amount.h index eebc18f3e8b..70a0bf51f34 100644 --- a/src/test/jtx/amount.h +++ b/src/test/jtx/amount.h @@ -73,8 +73,7 @@ constexpr XRPAmount dropsPerXRP{1'000'000}; struct PrettyAmount { private: - // VFALCO TODO should be Amount - STAmount amount_; + STEitherAmount amount_; std::string name_; public: @@ -87,6 +86,10 @@ struct PrettyAmount : amount_(amount), name_(name) { } + PrettyAmount(STMPTAmount const& amount, std::string const& name) + : amount_(amount), name_(name) + { + } /** drops */ template @@ -95,7 +98,7 @@ struct PrettyAmount std::enable_if_t< sizeof(T) >= sizeof(int) && std::is_integral_v && std::is_signed_v>* = nullptr) - : amount_((v > 0) ? v : -v, v < 0) + : amount_(STAmount((v > 0) ? v : -v, v < 0)) { } @@ -105,7 +108,7 @@ struct PrettyAmount T v, std::enable_if_t= sizeof(int) && std::is_unsigned_v>* = nullptr) - : amount_(v) + : amount_(STAmount(v)) { } @@ -120,13 +123,18 @@ struct PrettyAmount return name_; } - STAmount const& + STEitherAmount const& value() const { return amount_; } operator STAmount const &() const + { + return get(amount_); + } + + operator STEitherAmount const &() const { return amount_; } @@ -134,6 +142,13 @@ struct PrettyAmount operator AnyAmount() const; }; +template +A const& +get(PrettyAmount const& pa) +{ + return get(pa.value()); +} + inline bool operator==(PrettyAmount const& lhs, PrettyAmount const& rhs) { @@ -314,10 +329,6 @@ class IOU { return issue(); } - operator Asset() const - { - return issue(); - } template < class T, @@ -355,11 +366,11 @@ operator<<(std::ostream& os, IOU const& iou); //------------------------------------------------------------------------------ -/** Converts to MPT Issue or STAmount. +/** Converts to MPT Issue or STMPTAmount. Examples: MPT Converts to the underlying Issue - MPT(10) Returns STAmount of 10 of + MPT(10) Returns STMPTAmount of 10 of the underlying MPT */ class MPT @@ -379,7 +390,7 @@ class MPT return mptID; } - /** Implicit conversion to Issue. + /** Implicit conversion to MPTIssue. This allows passing an MPT value where an Issue is expected. @@ -390,34 +401,10 @@ class MPT } template - requires(sizeof(T) >= sizeof(int) && std::is_arithmetic_v) PrettyAmount + requires(sizeof(T) >= sizeof(int) && std::is_arithmetic_v) STMPTAmount operator()(T v) const { - // VFALCO NOTE Should throw if the - // representation of v is not exact. - return {amountFromString(mpt(), std::to_string(v)), name}; - } - - PrettyAmount operator()(epsilon_t) const; - PrettyAmount operator()(detail::epsilon_multiple) const; - - // VFALCO TODO - // STAmount operator()(char const* s) const; - - /** Returns None-of-Issue */ -#if 0 - None operator()(none_t) const - { - return {Issue{}}; - } -#endif - - friend BookSpec - operator~(MPT const& mpt) - { - assert(false); - Throw("MPT is not supported"); - return BookSpec{beast::zero, noCurrency()}; + return amountFromString(mptID, std::to_string(v)); } }; @@ -436,13 +423,22 @@ struct any_t struct AnyAmount { bool is_any; - STAmount value; + STEitherAmount value; AnyAmount() = delete; AnyAmount(AnyAmount const&) = default; AnyAmount& operator=(AnyAmount const&) = default; + AnyAmount(STEitherAmount const& amount) : is_any(false), value(amount) + { + } + + AnyAmount(STEitherAmount const& amount, any_t const*) + : is_any(true), value(amount) + { + } + AnyAmount(STAmount const& amount) : is_any(false), value(amount) { } @@ -452,13 +448,25 @@ struct AnyAmount { } + AnyAmount(STMPTAmount const& amount) : is_any(false), value(amount) + { + } + + AnyAmount(STMPTAmount const& amount, any_t const*) + : is_any(true), value(amount) + { + } + // Reset the issue to a specific account void to(AccountID const& id) { if (!is_any) return; - value.setIssuer(id); + if (value.isIssue()) + get(value).setIssuer(id); + else + Throw("AnyAmount: Amount is not STAmount"); } }; diff --git a/src/test/jtx/impl/AMM.cpp b/src/test/jtx/impl/AMM.cpp index c083b6df35c..9c5e17f7b75 100644 --- a/src/test/jtx/impl/AMM.cpp +++ b/src/test/jtx/impl/AMM.cpp @@ -221,7 +221,7 @@ AMM::balances( env_.journal); auto const lptAMMBalance = account ? ammLPHolds(*env_.current(), *amm, *account, env_.journal) - : amm->getFieldAmount(sfLPTokenBalance); + : get(amm->getFieldAmount(sfLPTokenBalance)); return {asset1Balance, asset2Balance, lptAMMBalance}; } return {STAmount{}, STAmount{}, STAmount{}}; @@ -253,7 +253,7 @@ AMM::getLPTokensBalance(std::optional const& account) const .iou(); if (auto const amm = env_.current()->read(keylet::amm(asset1_.issue(), asset2_.issue()))) - return amm->getFieldAmount(sfLPTokenBalance).iou(); + return get(amm->getFieldAmount(sfLPTokenBalance)).iou(); return IOUAmount{0}; } @@ -672,7 +672,7 @@ AMM::bid(BidArg const& arg) { auto const& auctionSlot = static_cast(amm->peekAtField(sfAuctionSlot)); - lastPurchasePrice_ = auctionSlot[sfPrice].iou(); + lastPurchasePrice_ = get(auctionSlot[sfPrice]).iou(); } } bidMin_ = std::nullopt; @@ -777,7 +777,8 @@ AMM::expectAuctionSlot(auto&& cb) const auto const slotInterval = ammAuctionTimeSlot( env_.app().timeKeeper().now().time_since_epoch().count(), auctionSlot); - auto const slotPrice = auctionSlot[sfPrice].iou(); + auto const slotPrice = + get(auctionSlot[sfPrice]).iou(); auto const authAccounts = auctionSlot.getFieldArray(sfAuthAccounts); return cb(slotFee, slotInterval, slotPrice, authAccounts); diff --git a/src/test/jtx/impl/AMMTest.cpp b/src/test/jtx/impl/AMMTest.cpp index 575e2e1d889..b7e4af927dd 100644 --- a/src/test/jtx/impl/AMMTest.cpp +++ b/src/test/jtx/impl/AMMTest.cpp @@ -249,7 +249,8 @@ AMMTest::find_paths( STAmount da; if (result.isMember(jss::destination_amount)) - da = amountFromJson(sfGeneric, result[jss::destination_amount]); + da = get( + amountFromJson(sfGeneric, result[jss::destination_amount])); STAmount sa; STPathSet paths; @@ -261,10 +262,12 @@ AMMTest::find_paths( auto const& path = alts[0u]; if (path.isMember(jss::source_amount)) - sa = amountFromJson(sfGeneric, path[jss::source_amount]); + sa = get( + amountFromJson(sfGeneric, path[jss::source_amount])); if (path.isMember(jss::destination_amount)) - da = amountFromJson(sfGeneric, path[jss::destination_amount]); + da = get( + amountFromJson(sfGeneric, path[jss::destination_amount])); if (path.isMember(jss::paths_computed)) { diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index 6f0f9e3fc73..b070d64f6e1 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -185,7 +185,7 @@ Env::balance(Account const& account) const auto const sle = le(account); if (!sle) return XRP(0); - return {sle->getFieldAmount(sfBalance), ""}; + return {get(sle->getFieldAmount(sfBalance)), ""}; } PrettyAmount @@ -196,7 +196,7 @@ Env::balance(Account const& account, Issue const& issue) const auto const sle = le(keylet::line(account.id(), issue)); if (!sle) return {STAmount(issue, 0), account.name()}; - auto amount = sle->getFieldAmount(sfBalance); + auto amount = get(sle->getFieldAmount(sfBalance)); amount.setIssuer(issue.account); if (account.id() > issue.account) amount.negate(); diff --git a/src/test/jtx/impl/TestHelpers.cpp b/src/test/jtx/impl/TestHelpers.cpp index b8105b1a631..b7104820228 100644 --- a/src/test/jtx/impl/TestHelpers.cpp +++ b/src/test/jtx/impl/TestHelpers.cpp @@ -117,11 +117,12 @@ expectLine( low.setIssuer(accountLow ? account : issue.account); high.setIssuer(accountLow ? issue.account : account); - expectDefaultTrustLine = sle->getFieldAmount(sfLowLimit) == low && - sle->getFieldAmount(sfHighLimit) == high; + expectDefaultTrustLine = + get(sle->getFieldAmount(sfLowLimit)) == low && + get(sle->getFieldAmount(sfHighLimit)) == high; } - auto amount = sle->getFieldAmount(sfBalance); + auto amount = get(sle->getFieldAmount(sfBalance)); amount.setIssuer(value.issue().account); if (!accountLow) amount.negate(); @@ -154,8 +155,11 @@ expectOffers( ++cnt; if (std::find_if( toMatch.begin(), toMatch.end(), [&](auto const& a) { - return a.in == sle->getFieldAmount(sfTakerPays) && - a.out == sle->getFieldAmount(sfTakerGets); + return a.in == + get( + sle->getFieldAmount(sfTakerPays)) && + a.out == + get(sle->getFieldAmount(sfTakerGets)); }) != toMatch.end()) ++matched; } @@ -330,7 +334,7 @@ channelBalance(ReadView const& view, uint256 const& chan) auto const slep = view.read({ltPAYCHAN, chan}); if (!slep) return XRPAmount{-1}; - return (*slep)[sfBalance]; + return get((*slep)[sfBalance]); } bool diff --git a/src/test/jtx/impl/amount.cpp b/src/test/jtx/impl/amount.cpp index 01fa5369592..752d68ef1fd 100644 --- a/src/test/jtx/impl/amount.cpp +++ b/src/test/jtx/impl/amount.cpp @@ -72,29 +72,40 @@ to_places(const T d, std::uint8_t places) std::ostream& operator<<(std::ostream& os, PrettyAmount const& amount) { - if (amount.value().native()) + if (amount.value().isIssue()) { - // measure in hundredths - auto const c = dropsPerXRP.drops() / 100; - auto const n = amount.value().mantissa(); - if (n < c) + auto const& stAmount = get(amount); + if (stAmount.native()) { - if (amount.value().negative()) - os << "-" << n << " drops"; - else - os << n << " drops"; - return os; + // measure in hundredths + auto const c = dropsPerXRP.drops() / 100; + auto const n = stAmount.mantissa(); + if (n < c) + { + if (stAmount.negative()) + os << "-" << n << " drops"; + else + os << n << " drops"; + return os; + } + auto const d = double(n) / dropsPerXRP.drops(); + if (stAmount.negative()) + os << "-"; + + os << to_places(d, 6) << " XRP"; + } + else + { + os << stAmount.getText() << "/" + << to_string(stAmount.issue().currency) << "(" << amount.name() + << ")"; } - auto const d = double(n) / dropsPerXRP.drops(); - if (amount.value().negative()) - os << "-"; - - os << to_places(d, 6) << " XRP"; } else { - os << amount.value().getText() << "/" - << to_string(amount.value().issue().currency) << "(" << amount.name() + auto const& mptAmount = get(amount); + os << mptAmount.getText() << "/" + << to_string(mptAmount.issue().getMptID()) << "(" << amount.name() << ")"; } return os; diff --git a/src/test/jtx/impl/balance.cpp b/src/test/jtx/impl/balance.cpp index 42330658eb0..f2c67db512c 100644 --- a/src/test/jtx/impl/balance.cpp +++ b/src/test/jtx/impl/balance.cpp @@ -35,7 +35,8 @@ balance::operator()(Env& env) const } else if (env.test.expect(sle)) { - env.test.expect(sle->getFieldAmount(sfBalance) == value_); + env.test.expect( + get(sle->getFieldAmount(sfBalance)) == value_); } } else @@ -47,7 +48,7 @@ balance::operator()(Env& env) const } else if (env.test.expect(sle)) { - auto amount = sle->getFieldAmount(sfBalance); + auto amount = get(sle->getFieldAmount(sfBalance)); amount.setIssuer(value_.issue().account); if (account_.id() > value_.issue().account) amount.negate(); diff --git a/src/test/jtx/impl/envconfig.cpp b/src/test/jtx/impl/envconfig.cpp index c9788a6d70f..94e02132c35 100644 --- a/src/test/jtx/impl/envconfig.cpp +++ b/src/test/jtx/impl/envconfig.cpp @@ -45,8 +45,8 @@ setupConfigForUnitTests(Config& cfg) // Default fees to old values, so tests don't have to worry about changes in // Config.h cfg.FEES.reference_fee = 10; - cfg.FEES.account_reserve = XRP(200).value().xrp().drops(); - cfg.FEES.owner_reserve = XRP(50).value().xrp().drops(); + cfg.FEES.account_reserve = get(XRP(200)).xrp().drops(); + cfg.FEES.owner_reserve = get(XRP(50)).xrp().drops(); // The Beta API (currently v2) is always available to tests cfg.BETA_RPC_API = true; diff --git a/src/test/jtx/impl/mpt.cpp b/src/test/jtx/impl/mpt.cpp index 4602eb88305..909154fb720 100644 --- a/src/test/jtx/impl/mpt.cpp +++ b/src/test/jtx/impl/mpt.cpp @@ -278,7 +278,7 @@ MPTTester::forObject( [[nodiscard]] bool MPTTester::checkMPTokenAmount( Account const& holder_, - std::uint64_t expectedAmount) const + std::int64_t expectedAmount) const { return forObject( [&](SLEP const& sle) { return expectedAmount == (*sle)[sfMPTAmount]; }, @@ -286,7 +286,7 @@ MPTTester::checkMPTokenAmount( } [[nodiscard]] bool -MPTTester::checkMPTokenOutstandingAmount(std::uint64_t expectedAmount) const +MPTTester::checkMPTokenOutstandingAmount(std::int64_t expectedAmount) const { return forObject([&](SLEP const& sle) { return expectedAmount == (*sle)[sfOutstandingAmount]; @@ -303,7 +303,7 @@ void MPTTester::pay( Account const& src, Account const& dest, - std::uint64_t amount, + std::int64_t amount, std::optional err) { assert(mpt_); @@ -330,15 +330,13 @@ MPTTester::pay( } else { - STAmount const saAmount = {*mpt_, amount}; - STAmount const saActual = - multiply(saAmount, transferRateMPT(*env_.current(), *mpt_)); + auto const actual = static_cast( + amount * Number{transferRate(*env_.current(), *mpt_).value, -9}); // Sender pays the transfer fee if any - env_.require(mptpay(*this, src, srcAmt - saActual.mpt().mpt())); + env_.require(mptpay(*this, src, srcAmt - actual)); env_.require(mptpay(*this, dest, destAmt + amount)); // Outstanding amount is reduced by the transfer fee if any - env_.require(mptpay( - *this, issuer_, outstnAmt - (saActual - saAmount).mpt().mpt())); + env_.require(mptpay(*this, issuer_, outstnAmt - (actual - amount))); } } @@ -346,7 +344,7 @@ void MPTTester::claw( Account const& issuer, Account const& holder, - std::uint64_t amount, + std::int64_t amount, std::optional err) { assert(mpt_); @@ -367,14 +365,14 @@ MPTTester::claw( mptpay(*this, holder, holderAmt - std::min(holderAmt, amount))); } -PrettyAmount -MPTTester::mpt(std::uint64_t amount) const +STMPTAmount +MPTTester::mpt(std::int64_t amount) const { assert(mpt_); return ripple::test::jtx::MPT(issuer_.name(), *mpt_)(amount); } -std::uint64_t +std::int64_t MPTTester::getAmount(Account const& account) const { assert(issuanceKey_); @@ -396,12 +394,13 @@ std::uint32_t MPTTester::getFlags(ripple::test::jtx::AccountP holder) const { std::uint32_t flags = 0; - assert(forObject( - [&](SLEP const& sle) { - flags = sle->getFlags(); - return true; - }, - holder)); + if (!forObject( + [&](SLEP const& sle) { + flags = sle->getFlags(); + return true; + }, + holder)) + Throw("Failed to get the flags"); return flags; } diff --git a/src/test/jtx/impl/offer.cpp b/src/test/jtx/impl/offer.cpp index 55a1af4beab..d6e2d89bc21 100644 --- a/src/test/jtx/impl/offer.cpp +++ b/src/test/jtx/impl/offer.cpp @@ -27,8 +27,8 @@ namespace jtx { Json::Value offer( Account const& account, - STAmount const& takerPays, - STAmount const& takerGets, + STEitherAmount const& takerPays, + STEitherAmount const& takerGets, std::uint32_t flags) { Json::Value jv; diff --git a/src/test/jtx/impl/paths.cpp b/src/test/jtx/impl/paths.cpp index 393e36e9d61..8c58f81a627 100644 --- a/src/test/jtx/impl/paths.cpp +++ b/src/test/jtx/impl/paths.cpp @@ -31,7 +31,8 @@ paths::operator()(Env& env, JTx& jt) const auto& jv = jt.jv; auto const from = env.lookup(jv[jss::Account].asString()); auto const to = env.lookup(jv[jss::Destination].asString()); - auto const amount = amountFromJson(sfAmount, jv[jss::Amount]); + auto const amount = + get(amountFromJson(sfAmount, jv[jss::Amount])); Pathfinder pf( std::make_shared( env.current(), env.app().journal("RippleLineCache")), diff --git a/src/test/jtx/impl/trust.cpp b/src/test/jtx/impl/trust.cpp index 320c7d05c7c..94f9d49ed46 100644 --- a/src/test/jtx/impl/trust.cpp +++ b/src/test/jtx/impl/trust.cpp @@ -66,7 +66,7 @@ trust( Json::Value claw( Account const& account, - STAmount const& amount, + STEitherAmount const& amount, std::optional const& mptHolder) { Json::Value jv; diff --git a/src/test/jtx/impl/xchain_bridge.cpp b/src/test/jtx/impl/xchain_bridge.cpp index 43b0e7c2f96..53b972288a6 100644 --- a/src/test/jtx/impl/xchain_bridge.cpp +++ b/src/test/jtx/impl/xchain_bridge.cpp @@ -216,7 +216,7 @@ claim_attestation( sk, stBridge, sendingAccount, - sendingAmount.value, + get(sendingAmount.value), rewardAccount, wasLockingChainSend, claimID, @@ -269,8 +269,8 @@ create_account_attestation( sk, stBridge, sendingAccount, - sendingAmount.value, - rewardAmount.value, + get(sendingAmount.value), + get(rewardAmount.value), rewardAccount, wasLockingChainSend, createCount, diff --git a/src/test/jtx/mpt.h b/src/test/jtx/mpt.h index 9353cbf27b7..e3ebb69d658 100644 --- a/src/test/jtx/mpt.h +++ b/src/test/jtx/mpt.h @@ -60,10 +60,10 @@ class mptpay private: MPTTester const& tester_; Account const& account_; - std::uint64_t const amount_; + std::int64_t const amount_; public: - mptpay(MPTTester& tester, Account const& account, std::uint64_t amount) + mptpay(MPTTester& tester, Account const& account, std::int64_t amount) : tester_(tester), account_(account), amount_(amount) { } @@ -97,7 +97,7 @@ struct MPTConstr struct MPTCreate { - std::optional maxAmt = std::nullopt; + std::optional maxAmt = std::nullopt; std::optional assetScale = std::nullopt; std::optional transferFee = std::nullopt; std::optional metadata = std::nullopt; @@ -166,11 +166,11 @@ class MPTTester set(MPTSet const& set = {}); [[nodiscard]] bool - checkMPTokenAmount(Account const& holder, std::uint64_t expectedAmount) + checkMPTokenAmount(Account const& holder, std::int64_t expectedAmount) const; [[nodiscard]] bool - checkMPTokenOutstandingAmount(std::uint64_t expectedAmount) const; + checkMPTokenOutstandingAmount(std::int64_t expectedAmount) const; [[nodiscard]] bool checkFlags(uint32_t const expectedFlags, AccountP holder = nullptr) const; @@ -186,18 +186,18 @@ class MPTTester void pay(Account const& src, Account const& dest, - std::uint64_t amount, + std::int64_t amount, std::optional err = std::nullopt); void claw( Account const& issuer, Account const& holder, - std::uint64_t amount, + std::int64_t amount, std::optional err = std::nullopt); - PrettyAmount - mpt(std::uint64_t amount) const; + STMPTAmount + mpt(std::int64_t amount) const; uint256 const& issuanceKey() const @@ -213,7 +213,7 @@ class MPTTester return *id_; } - std::uint64_t + std::int64_t getAmount(Account const& account) const; private: diff --git a/src/test/jtx/offer.h b/src/test/jtx/offer.h index 3951f4f934a..2d66b24436e 100644 --- a/src/test/jtx/offer.h +++ b/src/test/jtx/offer.h @@ -32,8 +32,8 @@ namespace jtx { Json::Value offer( Account const& account, - STAmount const& takerPays, - STAmount const& takerGets, + STEitherAmount const& takerPays, + STEitherAmount const& takerGets, std::uint32_t flags = 0); /** Cancel an offer. */ diff --git a/src/test/jtx/trust.h b/src/test/jtx/trust.h index 0d02c6e76c4..03e045591ce 100644 --- a/src/test/jtx/trust.h +++ b/src/test/jtx/trust.h @@ -43,7 +43,7 @@ trust( Json::Value claw( Account const& account, - STAmount const& amount, + STEitherAmount const& amount, std::optional const& mptHolder = std::nullopt); } // namespace jtx diff --git a/src/test/ledger/Invariants_test.cpp b/src/test/ledger/Invariants_test.cpp index 1fd1543f18b..3b748b9998a 100644 --- a/src/test/ledger/Invariants_test.cpp +++ b/src/test/ledger/Invariants_test.cpp @@ -106,7 +106,7 @@ class Invariants_test : public beast::unit_test::suite auto const sle = ac.view().peek(keylet::account(A1.id())); if (!sle) return false; - auto amt = sle->getFieldAmount(sfBalance); + auto amt = get(sle->getFieldAmount(sfBalance)); sle->setFieldAmount(sfBalance, amt + STAmount{500}); ac.view().update(sle); return true; diff --git a/src/test/nodestore/DatabaseShard_test.cpp b/src/test/nodestore/DatabaseShard_test.cpp index e185c43d157..ac8cafb0b8f 100644 --- a/src/test/nodestore/DatabaseShard_test.cpp +++ b/src/test/nodestore/DatabaseShard_test.cpp @@ -377,7 +377,9 @@ class DatabaseShard_test : public TestBase if (tx.first->getTxnType() == ttPAYMENT) { std::int64_t xrpAmount = - tx.first->getFieldAmount(sfAmount).xrp().decimalXRP(); + get(tx.first->getFieldAmount(sfAmount)) + .xrp() + .decimalXRP(); if (xrpAmount == iniAmount) ++iniCount; else diff --git a/src/test/protocol/Quality_test.cpp b/src/test/protocol/Quality_test.cpp index 64cf0c71b3a..741a341d980 100644 --- a/src/test/protocol/Quality_test.cpp +++ b/src/test/protocol/Quality_test.cpp @@ -29,7 +29,7 @@ class Quality_test : public beast::unit_test::suite // Create a raw, non-integral amount from mantissa and exponent STAmount static raw(std::uint64_t mantissa, int exponent) { - return STAmount(Issue{Currency(3), AccountID(3)}, mantissa, exponent); + return STAmount({Currency(3), AccountID(3)}, mantissa, exponent); } template diff --git a/src/test/protocol/STObject_test.cpp b/src/test/protocol/STObject_test.cpp index 39a5b9c2f65..b55c1959363 100644 --- a/src/test/protocol/STObject_test.cpp +++ b/src/test/protocol/STObject_test.cpp @@ -560,10 +560,10 @@ class STObject_test : public beast::unit_test::suite { STObject st(sfGeneric); - st[sfAmount] = STAmount{}; + st[sfAmount] = STEitherAmount{}; st[sfAccount] = AccountID{}; st[sfDigest] = uint256{}; - [&](STAmount) {}(st[sfAmount]); + [&](STEitherAmount) {}(st[sfAmount]); [&](AccountID) {}(st[sfAccount]); [&](uint256) {}(st[sfDigest]); } diff --git a/src/test/protocol/STTx_test.cpp b/src/test/protocol/STTx_test.cpp index e0f6796af33..38460d94ecb 100644 --- a/src/test/protocol/STTx_test.cpp +++ b/src/test/protocol/STTx_test.cpp @@ -1575,6 +1575,10 @@ class STTx_test : public beast::unit_test::suite } catch (std::exception const& ex) { + // TODO payload3 has TakerGets field incorrectly serialized + // with cMPToken bit set. It results in deserializing MPT + // instead of IOU. The error message is + // "gFID: uncommon name out of range 0" BEAST_EXPECT(strcmp(ex.what(), "Unknown field") == 0); } } diff --git a/src/test/rpc/AccountSet_test.cpp b/src/test/rpc/AccountSet_test.cpp index e5475e3f530..b84f7f191f3 100644 --- a/src/test/rpc/AccountSet_test.cpp +++ b/src/test/rpc/AccountSet_test.cpp @@ -388,7 +388,7 @@ class AccountSet_test : public beast::unit_test::suite auto const amount = USD(1); Rate const rate(transferRate * QUALITY_ONE); auto const amountWithRate = - toAmount(multiply(amount.value(), rate)); + toAmount(multiply(amount, rate)); env(pay(gw, alice, USD(10))); env.close(); @@ -455,7 +455,7 @@ class AccountSet_test : public beast::unit_test::suite auto const amount = USD(1); auto const amountWithRate = toAmount( - multiply(amount.value(), Rate(transferRate * QUALITY_ONE))); + multiply(amount, Rate(transferRate * QUALITY_ONE))); env(pay(gw, alice, USD(10))); env(pay(alice, bob, amount), sendmax(USD(10))); diff --git a/src/xrpld/app/ledger/AcceptedLedgerTx.cpp b/src/xrpld/app/ledger/AcceptedLedgerTx.cpp index e1ad68dff37..3a60f0d5eb6 100644 --- a/src/xrpld/app/ledger/AcceptedLedgerTx.cpp +++ b/src/xrpld/app/ledger/AcceptedLedgerTx.cpp @@ -57,7 +57,7 @@ AcceptedLedgerTx::AcceptedLedgerTx( if (mTxn->getTxnType() == ttOFFER_CREATE) { auto const& account = mTxn->getAccountID(sfAccount); - auto const amount = mTxn->getFieldAmount(sfTakerGets); + auto const amount = get(mTxn->getFieldAmount(sfTakerGets)); // If the offer create is not self funded then add the owner balance if (account != amount.issue().account) diff --git a/src/xrpld/app/ledger/Ledger.cpp b/src/xrpld/app/ledger/Ledger.cpp index bcd3b6d4ba7..1c76745b1c6 100644 --- a/src/xrpld/app/ledger/Ledger.cpp +++ b/src/xrpld/app/ledger/Ledger.cpp @@ -643,10 +643,11 @@ Ledger::setup() oldFees = baseFee || reserveBase || reserveIncrement; } { - auto const baseFeeXRP = sle->at(~sfBaseFeeDrops); - auto const reserveBaseXRP = sle->at(~sfReserveBaseDrops); + auto const baseFeeXRP = get(sle->at(~sfBaseFeeDrops)); + auto const reserveBaseXRP = + get(sle->at(~sfReserveBaseDrops)); auto const reserveIncrementXRP = - sle->at(~sfReserveIncrementDrops); + get(sle->at(~sfReserveIncrementDrops)); auto assign = [&ret]( XRPAmount& dest, std::optional const& src) { diff --git a/src/xrpld/app/ledger/OrderBookDB.cpp b/src/xrpld/app/ledger/OrderBookDB.cpp index d0eddadbacb..ff088d080c2 100644 --- a/src/xrpld/app/ledger/OrderBookDB.cpp +++ b/src/xrpld/app/ledger/OrderBookDB.cpp @@ -274,8 +274,10 @@ OrderBookDB::processTxn( data->isFieldPresent(sfTakerGets)) { auto listeners = getBookListeners( - {data->getFieldAmount(sfTakerGets).issue(), - data->getFieldAmount(sfTakerPays).issue()}); + {get(data->getFieldAmount(sfTakerGets)) + .issue(), + get(data->getFieldAmount(sfTakerPays)) + .issue()}); if (listeners) listeners->publish(jvObj, havePublished); } diff --git a/src/xrpld/app/ledger/detail/LedgerToJson.cpp b/src/xrpld/app/ledger/detail/LedgerToJson.cpp index 11f42cd66ec..25d7ae7b203 100644 --- a/src/xrpld/app/ledger/detail/LedgerToJson.cpp +++ b/src/xrpld/app/ledger/detail/LedgerToJson.cpp @@ -208,7 +208,7 @@ fillJsonTx( txn->getTxnType() == ttOFFER_CREATE) { auto const account = txn->getAccountID(sfAccount); - auto const amount = txn->getFieldAmount(sfTakerGets); + auto const amount = get(txn->getFieldAmount(sfTakerGets)); // If the offer create is not self funded then add the // owner balance diff --git a/src/xrpld/app/misc/FeeVoteImpl.cpp b/src/xrpld/app/misc/FeeVoteImpl.cpp index af57314ef6d..8a03f877351 100644 --- a/src/xrpld/app/misc/FeeVoteImpl.cpp +++ b/src/xrpld/app/misc/FeeVoteImpl.cpp @@ -217,7 +217,7 @@ FeeVoteImpl::doVoting( auto doVote = [](std::shared_ptr const& val, detail::VotableValue& value, SF_AMOUNT const& xrpField) { - if (auto const field = ~val->at(~xrpField); + if (auto const field = get(~val->at(~xrpField)); field && field->native()) { auto const vote = field->xrp(); diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index f7cdfa212a2..0755cf1ae63 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -2198,15 +2198,17 @@ NetworkOPsImp::pubValidation(std::shared_ptr const& val) // (The ~ operator converts the Proxy to a std::optional, which // simplifies later operations) - if (auto const baseFeeXRP = ~val->at(~sfBaseFeeDrops); + if (auto const baseFeeXRP = get(~val->at(~sfBaseFeeDrops)); baseFeeXRP && baseFeeXRP->native()) jvObj[jss::base_fee] = baseFeeXRP->xrp().jsonClipped(); - if (auto const reserveBaseXRP = ~val->at(~sfReserveBaseDrops); + if (auto const reserveBaseXRP = + get(~val->at(~sfReserveBaseDrops)); reserveBaseXRP && reserveBaseXRP->native()) jvObj[jss::reserve_base] = reserveBaseXRP->xrp().jsonClipped(); - if (auto const reserveIncXRP = ~val->at(~sfReserveIncrementDrops); + if (auto const reserveIncXRP = + get(~val->at(~sfReserveIncrementDrops)); reserveIncXRP && reserveIncXRP->native()) jvObj[jss::reserve_inc] = reserveIncXRP->xrp().jsonClipped(); @@ -3153,7 +3155,8 @@ NetworkOPsImp::transJson( if (transaction->getTxnType() == ttOFFER_CREATE) { auto const account = transaction->getAccountID(sfAccount); - auto const amount = transaction->getFieldAmount(sfTakerGets); + auto const amount = + get(transaction->getFieldAmount(sfTakerGets)); // If the offer create is not self funded then add the owner balance if (account != amount.issue().account) @@ -4390,8 +4393,10 @@ NetworkOPsImp::getBookPage( if (sleOffer) { auto const uOfferOwnerID = sleOffer->getAccountID(sfAccount); - auto const& saTakerGets = sleOffer->getFieldAmount(sfTakerGets); - auto const& saTakerPays = sleOffer->getFieldAmount(sfTakerPays); + auto const& saTakerGets = + get(sleOffer->getFieldAmount(sfTakerGets)); + auto const& saTakerPays = + get(sleOffer->getFieldAmount(sfTakerPays)); STAmount saOwnerFunds; bool firstOwnerOffer(true); @@ -4543,8 +4548,10 @@ NetworkOPsImp::getBookPage( if (sleOffer) { auto const uOfferOwnerID = sleOffer->getAccountID(sfAccount); - auto const& saTakerGets = sleOffer->getFieldAmount(sfTakerGets); - auto const& saTakerPays = sleOffer->getFieldAmount(sfTakerPays); + auto const& saTakerGets = + get(sleOffer->getFieldAmount(sfTakerGets)); + auto const& saTakerPays = + get(sleOffer->getFieldAmount(sfTakerPays)); STAmount saDirRate = obIterator.getCurrentRate(); STAmount saOwnerFunds; diff --git a/src/xrpld/app/misc/detail/AMMUtils.cpp b/src/xrpld/app/misc/detail/AMMUtils.cpp index efc80cf17b6..dd6f3d6a141 100644 --- a/src/xrpld/app/misc/detail/AMMUtils.cpp +++ b/src/xrpld/app/misc/detail/AMMUtils.cpp @@ -104,7 +104,8 @@ ammHolds( issues->second, freezeHandling, j); - return std::make_tuple(asset1, asset2, ammSle[sfLPTokenBalance]); + return std::make_tuple( + asset1, asset2, get(ammSle[sfLPTokenBalance])); } STAmount @@ -181,14 +182,14 @@ ammAccountHolds( if (isXRP(issue)) { if (auto const sle = view.read(keylet::account(ammAccountID))) - return (*sle)[sfBalance]; + return get((*sle)[sfBalance]); } else if (auto const sle = view.read( keylet::line(ammAccountID, issue.account, issue.currency)); sle && !isFrozen(view, ammAccountID, issue.currency, issue.account)) { - auto amount = (*sle)[sfBalance]; + auto amount = get((*sle)[sfBalance]); if (ammAccountID > issue.account) amount.negate(); amount.setIssuer(issue.account); @@ -392,8 +393,10 @@ isOnlyLiquidityProvider( } if (sle->getFieldU16(sfLedgerEntryType) != ltRIPPLE_STATE) return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE - auto const lowLimit = sle->getFieldAmount(sfLowLimit); - auto const highLimit = sle->getFieldAmount(sfHighLimit); + auto const lowLimit = + get(sle->getFieldAmount(sfLowLimit)); + auto const highLimit = + get(sle->getFieldAmount(sfHighLimit)); auto const isLPTrustline = lowLimit.getIssuer() == lpAccount || highLimit.getIssuer() == lpAccount; auto const isLPTokenTrustline = diff --git a/src/xrpld/app/misc/detail/TxQ.cpp b/src/xrpld/app/misc/detail/TxQ.cpp index 159a700cc3f..e0b386af754 100644 --- a/src/xrpld/app/misc/detail/TxQ.cpp +++ b/src/xrpld/app/misc/detail/TxQ.cpp @@ -39,7 +39,7 @@ getFeeLevelPaid(ReadView const& view, STTx const& tx) { auto const [baseFee, effectiveFeePaid] = [&view, &tx]() { XRPAmount baseFee = calculateBaseFee(view, tx); - XRPAmount feePaid = tx[sfFee].xrp(); + XRPAmount feePaid = get(tx[sfFee]).xrp(); // If baseFee is 0 then the cost of a basic transaction is free, but we // need the effective fee level to be non-zero. @@ -1078,7 +1078,7 @@ TxQ::apply( Transactions stuck in the queue are mitigated by LastLedgerSeq and MaybeTx::retriesRemaining. */ - auto const balance = (*sleAccount)[sfBalance].xrp(); + auto const balance = get((*sleAccount)[sfBalance]).xrp(); /* Get the minimum possible account reserve. If it is at least 10 * the base fee, and fees exceed this amount, the transaction can't be queued. diff --git a/src/xrpld/app/paths/Credit.cpp b/src/xrpld/app/paths/Credit.cpp index b3870937367..5e632fd4d7d 100644 --- a/src/xrpld/app/paths/Credit.cpp +++ b/src/xrpld/app/paths/Credit.cpp @@ -31,14 +31,14 @@ creditLimit( AccountID const& issuer, Currency const& currency) { - STAmount result(Issue{currency, account}); + STAmount result({currency, account}); auto sleRippleState = view.read(keylet::line(account, issuer, currency)); if (sleRippleState) { - result = sleRippleState->getFieldAmount( - account < issuer ? sfLowLimit : sfHighLimit); + result = get(sleRippleState->getFieldAmount( + account < issuer ? sfLowLimit : sfHighLimit)); result.setIssuer(account); } @@ -64,13 +64,13 @@ creditBalance( AccountID const& issuer, Currency const& currency) { - STAmount result(Issue{currency, account}); + STAmount result({currency, account}); auto sleRippleState = view.read(keylet::line(account, issuer, currency)); if (sleRippleState) { - result = sleRippleState->getFieldAmount(sfBalance); + result = get(sleRippleState->getFieldAmount(sfBalance)); if (account < issuer) result.negate(); result.setIssuer(account); diff --git a/src/xrpld/app/paths/PathRequest.cpp b/src/xrpld/app/paths/PathRequest.cpp index bb6a104bca2..4cd9f7d71f7 100644 --- a/src/xrpld/app/paths/PathRequest.cpp +++ b/src/xrpld/app/paths/PathRequest.cpp @@ -562,7 +562,7 @@ PathRequest::findPaths( }(); STAmount saMaxAmount = saSendMax.value_or( - STAmount(Issue{issue.currency, sourceAccount}, 1u, 0, true)); + STAmount({issue.currency, sourceAccount}, 1u, 0, true)); JLOG(m_journal.debug()) << iIdentifier << " Paths found, calling rippleCalc"; diff --git a/src/xrpld/app/paths/Pathfinder.cpp b/src/xrpld/app/paths/Pathfinder.cpp index a5fe2afe949..885a8ae9b47 100644 --- a/src/xrpld/app/paths/Pathfinder.cpp +++ b/src/xrpld/app/paths/Pathfinder.cpp @@ -176,10 +176,9 @@ Pathfinder::Pathfinder( , mSrcCurrency(uSrcCurrency) , mSrcIssuer(uSrcIssuer) , mSrcAmount(srcAmount.value_or(STAmount( - Issue{ - uSrcCurrency, - uSrcIssuer.value_or( - isXRP(uSrcCurrency) ? xrpAccount() : uSrcAccount)}, + {uSrcCurrency, + uSrcIssuer.value_or( + isXRP(uSrcCurrency) ? xrpAccount() : uSrcAccount)}, 1u, 0, true))) diff --git a/src/xrpld/app/paths/TrustLine.cpp b/src/xrpld/app/paths/TrustLine.cpp index 6390c8d2117..949a02a7ed8 100644 --- a/src/xrpld/app/paths/TrustLine.cpp +++ b/src/xrpld/app/paths/TrustLine.cpp @@ -29,9 +29,9 @@ TrustLineBase::TrustLineBase( std::shared_ptr const& sle, AccountID const& viewAccount) : key_(sle->key()) - , mLowLimit(sle->getFieldAmount(sfLowLimit)) - , mHighLimit(sle->getFieldAmount(sfHighLimit)) - , mBalance(sle->getFieldAmount(sfBalance)) + , mLowLimit(get(sle->getFieldAmount(sfLowLimit))) + , mHighLimit(get(sle->getFieldAmount(sfHighLimit))) + , mBalance(get(sle->getFieldAmount(sfBalance))) , mFlags(sle->getFieldU32(sfFlags)) , mViewLowest(mLowLimit.getIssuer() == viewAccount) { diff --git a/src/xrpld/app/tx/detail/AMMBid.cpp b/src/xrpld/app/tx/detail/AMMBid.cpp index 9de3762d2e3..308f140791e 100644 --- a/src/xrpld/app/tx/detail/AMMBid.cpp +++ b/src/xrpld/app/tx/detail/AMMBid.cpp @@ -40,6 +40,10 @@ AMMBid::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && + (isMPT(ctx.tx[~sfBidMin]) || isMPT(ctx.tx[~sfBidMax]))) + return temMPT_INVALID_USAGE; + if (ctx.tx.getFlags() & tfUniversalMask) { JLOG(ctx.j.debug()) << "AMM Bid: invalid flags."; @@ -52,7 +56,7 @@ AMMBid::preflight(PreflightContext const& ctx) return res; } - if (auto const bidMin = ctx.tx[~sfBidMin]) + if (auto const bidMin = get(ctx.tx[~sfBidMin])) { if (auto const res = invalidAMMAmount(*bidMin)) { @@ -61,7 +65,7 @@ AMMBid::preflight(PreflightContext const& ctx) } } - if (auto const bidMax = ctx.tx[~sfBidMax]) + if (auto const bidMax = get(ctx.tx[~sfBidMax])) { if (auto const res = invalidAMMAmount(*bidMax)) { @@ -94,7 +98,7 @@ AMMBid::preclaim(PreclaimContext const& ctx) return terNO_AMM; } - auto const lpTokensBalance = (*ammSle)[sfLPTokenBalance]; + auto const lpTokensBalance = get((*ammSle)[sfLPTokenBalance]); if (lpTokensBalance == beast::zero) return tecAMM_EMPTY; @@ -119,7 +123,7 @@ AMMBid::preclaim(PreclaimContext const& ctx) return tecAMM_INVALID_TOKENS; } - auto const bidMin = ctx.tx[~sfBidMin]; + auto const bidMin = get(ctx.tx[~sfBidMin]); if (bidMin) { @@ -135,7 +139,7 @@ AMMBid::preclaim(PreclaimContext const& ctx) } } - auto const bidMax = ctx.tx[~sfBidMax]; + auto const bidMax = get(ctx.tx[~sfBidMax]); if (bidMax) { if (bidMax->issue() != lpTokens.issue()) @@ -171,7 +175,7 @@ applyBid( sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2])); if (!ammSle) return {tecINTERNAL, false}; - STAmount const lptAMMBalance = (*ammSle)[sfLPTokenBalance]; + STAmount const lptAMMBalance = get((*ammSle)[sfLPTokenBalance]); auto const lpTokens = ammLPHolds(sb, *ammSle, account_, ctx_.journal); auto const& rules = ctx_.view().rules(); if (!rules.enabled(fixInnerObjTemplate)) @@ -253,8 +257,8 @@ applyBid( TER res = tesSUCCESS; - auto const bidMin = ctx_.tx[~sfBidMin]; - auto const bidMax = ctx_.tx[~sfBidMax]; + auto const bidMin = get(ctx_.tx[~sfBidMin]); + auto const bidMax = get(ctx_.tx[~sfBidMax]); auto getPayPrice = [&](Number const& computedPrice) -> Expected { @@ -303,7 +307,7 @@ applyBid( else { // Price the slot was purchased at. - STAmount const pricePurchased = auctionSlot[sfPrice]; + STAmount const pricePurchased = get(auctionSlot[sfPrice]); assert(timeSlot); auto const fractionUsed = (Number(*timeSlot) + 1) / AUCTION_SLOT_TIME_INTERVALS; diff --git a/src/xrpld/app/tx/detail/AMMCreate.cpp b/src/xrpld/app/tx/detail/AMMCreate.cpp index 237e1afa240..46dd9e583ab 100644 --- a/src/xrpld/app/tx/detail/AMMCreate.cpp +++ b/src/xrpld/app/tx/detail/AMMCreate.cpp @@ -41,14 +41,18 @@ AMMCreate::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && + (isMPT(ctx.tx[sfAmount]) || isMPT(ctx.tx[sfAmount2]))) + return temMPT_NOT_SUPPORTED; + if (ctx.tx.getFlags() & tfUniversalMask) { JLOG(ctx.j.debug()) << "AMM Instance: invalid flags."; return temINVALID_FLAG; } - auto const amount = ctx.tx[sfAmount]; - auto const amount2 = ctx.tx[sfAmount2]; + auto const amount = get(ctx.tx[sfAmount]); + auto const amount2 = get(ctx.tx[sfAmount2]); if (amount.issue() == amount2.issue()) { @@ -89,8 +93,8 @@ TER AMMCreate::preclaim(PreclaimContext const& ctx) { auto const accountID = ctx.tx[sfAccount]; - auto const amount = ctx.tx[sfAmount]; - auto const amount2 = ctx.tx[sfAmount2]; + auto const amount = get(ctx.tx[sfAmount]); + auto const amount2 = get(ctx.tx[sfAmount2]); // Check if AMM already exists for the token pair if (auto const ammKeylet = keylet::amm(amount.issue(), amount2.issue()); @@ -208,8 +212,8 @@ applyCreate( AccountID const& account_, beast::Journal j_) { - auto const amount = ctx_.tx[sfAmount]; - auto const amount2 = ctx_.tx[sfAmount2]; + auto const amount = get(ctx_.tx[sfAmount]); + auto const amount2 = get(ctx_.tx[sfAmount2]); auto const ammKeylet = keylet::amm(amount.issue(), amount2.issue()); diff --git a/src/xrpld/app/tx/detail/AMMDelete.cpp b/src/xrpld/app/tx/detail/AMMDelete.cpp index 89ce34052d2..43cc05c3168 100644 --- a/src/xrpld/app/tx/detail/AMMDelete.cpp +++ b/src/xrpld/app/tx/detail/AMMDelete.cpp @@ -58,7 +58,7 @@ AMMDelete::preclaim(PreclaimContext const& ctx) return terNO_AMM; } - auto const lpTokensBalance = (*ammSle)[sfLPTokenBalance]; + auto const lpTokensBalance = get((*ammSle)[sfLPTokenBalance]); if (lpTokensBalance != beast::zero) return tecAMM_NOT_EMPTY; diff --git a/src/xrpld/app/tx/detail/AMMDeposit.cpp b/src/xrpld/app/tx/detail/AMMDeposit.cpp index 9bbf5b4a60a..7f9b0c093e7 100644 --- a/src/xrpld/app/tx/detail/AMMDeposit.cpp +++ b/src/xrpld/app/tx/detail/AMMDeposit.cpp @@ -41,6 +41,14 @@ AMMDeposit::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1)) + { + if (isMPT(ctx.tx[~sfAmount]) || isMPT(ctx.tx[~sfAmount2])) + return temMPT_NOT_SUPPORTED; + if (isMPT(ctx.tx[~sfEPrice]) || isMPT(ctx.tx[~sfLPTokenOut])) + return temMPT_INVALID_USAGE; + } + auto const flags = ctx.tx.getFlags(); if (flags & tfDepositMask) { @@ -48,10 +56,10 @@ AMMDeposit::preflight(PreflightContext const& ctx) return temINVALID_FLAG; } - auto const amount = ctx.tx[~sfAmount]; - auto const amount2 = ctx.tx[~sfAmount2]; - auto const ePrice = ctx.tx[~sfEPrice]; - auto const lpTokens = ctx.tx[~sfLPTokenOut]; + auto const amount = get(ctx.tx[~sfAmount]); + auto const amount2 = get(ctx.tx[~sfAmount2]); + auto const ePrice = get(ctx.tx[~sfEPrice]); + auto const lpTokens = get(ctx.tx[~sfLPTokenOut]); auto const tradingFee = ctx.tx[~sfTradingFee]; // Valid options for the flags are: // tfLPTokens: LPTokenOut, [Amount, Amount2] @@ -223,7 +231,8 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) auto balance = [&](auto const& deposit) -> TER { if (isXRP(deposit)) { - auto const lpIssue = (*ammSle)[sfLPTokenBalance].issue(); + auto const lpIssue = + get((*ammSle)[sfLPTokenBalance]).issue(); // Adjust the reserve if LP doesn't have LPToken trustline auto const sle = ctx.view.read( keylet::line(accountID, lpIssue.account, lpIssue.currency)); @@ -244,8 +253,8 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) : tecUNFUNDED_AMM; }; - auto const amount = ctx.tx[~sfAmount]; - auto const amount2 = ctx.tx[~sfAmount2]; + auto const amount = get(ctx.tx[~sfAmount]); + auto const amount2 = get(ctx.tx[~sfAmount2]); auto const ammAccountID = ammSle->getAccountID(sfAccount); auto checkAmount = [&](std::optional const& amount, @@ -313,7 +322,7 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) } // Equal deposit lp tokens - if (auto const lpTokens = ctx.tx[~sfLPTokenOut]; + if (auto const lpTokens = get(ctx.tx[~sfLPTokenOut]); lpTokens && lpTokens->issue() != lptAMMBalance.issue()) { JLOG(ctx.j.debug()) << "AMM Deposit: invalid LPTokens."; @@ -339,10 +348,10 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) std::pair AMMDeposit::applyGuts(Sandbox& sb) { - auto const amount = ctx_.tx[~sfAmount]; - auto const amount2 = ctx_.tx[~sfAmount2]; - auto const ePrice = ctx_.tx[~sfEPrice]; - auto const lpTokensDeposit = ctx_.tx[~sfLPTokenOut]; + auto const amount = get(ctx_.tx[~sfAmount]); + auto const amount2 = get(ctx_.tx[~sfAmount2]); + auto const ePrice = get(ctx_.tx[~sfEPrice]); + auto const lpTokensDeposit = get(ctx_.tx[~sfLPTokenOut]); auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2])); if (!ammSle) return {tecINTERNAL, false}; // LCOV_EXCL_LINE diff --git a/src/xrpld/app/tx/detail/AMMVote.cpp b/src/xrpld/app/tx/detail/AMMVote.cpp index c4b6c612c63..dff94bf0d51 100644 --- a/src/xrpld/app/tx/detail/AMMVote.cpp +++ b/src/xrpld/app/tx/detail/AMMVote.cpp @@ -93,7 +93,7 @@ applyVote( auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2])); if (!ammSle) return {tecINTERNAL, false}; - STAmount const lptAMMBalance = (*ammSle)[sfLPTokenBalance]; + STAmount const lptAMMBalance = get((*ammSle)[sfLPTokenBalance]); auto const lpTokensNew = ammLPHolds(sb, *ammSle, account_, ctx_.journal); std::optional minTokens; std::size_t minPos{0}; diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.cpp b/src/xrpld/app/tx/detail/AMMWithdraw.cpp index 51b512aba0a..afc17690219 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/xrpld/app/tx/detail/AMMWithdraw.cpp @@ -41,6 +41,14 @@ AMMWithdraw::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1)) + { + if (isMPT(ctx.tx[~sfAmount]) || isMPT(ctx.tx[~sfAmount2])) + return temMPT_NOT_SUPPORTED; + if (isMPT(ctx.tx[~sfEPrice]) || isMPT(ctx.tx[~sfLPTokenIn])) + return temMPT_INVALID_USAGE; + } + auto const flags = ctx.tx.getFlags(); if (flags & tfWithdrawMask) { @@ -48,10 +56,10 @@ AMMWithdraw::preflight(PreflightContext const& ctx) return temINVALID_FLAG; } - auto const amount = ctx.tx[~sfAmount]; - auto const amount2 = ctx.tx[~sfAmount2]; - auto const ePrice = ctx.tx[~sfEPrice]; - auto const lpTokens = ctx.tx[~sfLPTokenIn]; + auto const amount = get(ctx.tx[~sfAmount]); + auto const amount2 = get(ctx.tx[~sfAmount2]); + auto const ePrice = get(ctx.tx[~sfEPrice]); + auto const lpTokens = get(ctx.tx[~sfLPTokenIn]); // Valid combinations are: // LPTokens // tfWithdrawAll @@ -181,8 +189,8 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx) return terNO_AMM; } - auto const amount = ctx.tx[~sfAmount]; - auto const amount2 = ctx.tx[~sfAmount2]; + auto const amount = get(ctx.tx[~sfAmount]); + auto const amount2 = get(ctx.tx[~sfAmount2]); auto const expected = ammHolds( ctx.view, @@ -253,8 +261,8 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx) auto const lpTokens = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j); - auto const lpTokensWithdraw = - tokensWithdraw(lpTokens, ctx.tx[~sfLPTokenIn], ctx.tx.getFlags()); + auto const lpTokensWithdraw = tokensWithdraw( + lpTokens, get(ctx.tx[~sfLPTokenIn]), ctx.tx.getFlags()); if (lpTokens <= beast::zero) { @@ -274,7 +282,7 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx) return tecAMM_INVALID_TOKENS; } - if (auto const ePrice = ctx.tx[~sfEPrice]; + if (auto const ePrice = get(ctx.tx[~sfEPrice]); ePrice && ePrice->issue() != lpTokens.issue()) { JLOG(ctx.j.debug()) << "AMM Withdraw: invalid EPrice."; @@ -295,9 +303,9 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx) std::pair AMMWithdraw::applyGuts(Sandbox& sb) { - auto const amount = ctx_.tx[~sfAmount]; - auto const amount2 = ctx_.tx[~sfAmount2]; - auto const ePrice = ctx_.tx[~sfEPrice]; + auto const amount = get(ctx_.tx[~sfAmount]); + auto const amount2 = get(ctx_.tx[~sfAmount2]); + auto const ePrice = get(ctx_.tx[~sfEPrice]); auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2])); if (!ammSle) return {tecINTERNAL, false}; // LCOV_EXCL_LINE @@ -307,8 +315,8 @@ AMMWithdraw::applyGuts(Sandbox& sb) return {tecINTERNAL, false}; // LCOV_EXCL_LINE auto const lpTokens = ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal); - auto const lpTokensWithdraw = - tokensWithdraw(lpTokens, ctx_.tx[~sfLPTokenIn], ctx_.tx.getFlags()); + auto const lpTokensWithdraw = tokensWithdraw( + lpTokens, get(ctx_.tx[~sfLPTokenIn]), ctx_.tx.getFlags()); // Due to rounding, the LPTokenBalance of the last LP // might not match the LP's trustline balance @@ -322,7 +330,7 @@ AMMWithdraw::applyGuts(Sandbox& sb) { if (withinRelativeDistance( lpTokens, - ammSle->getFieldAmount(sfLPTokenBalance), + get(ammSle->getFieldAmount(sfLPTokenBalance)), Number{1, -3})) { ammSle->setFieldAmount(sfLPTokenBalance, lpTokens); diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/xrpld/app/tx/detail/CashCheck.cpp index 8b5ef79b6d4..4e537432053 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -42,6 +42,10 @@ CashCheck::preflight(PreflightContext const& ctx) if (!isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && + (isMPT(ctx.tx[~sfAmount]) || isMPT(ctx.tx[~sfDeliverMin]))) + return temMPT_NOT_SUPPORTED; + if (ctx.tx.getFlags() & tfUniversalMask) { // There are no flags (other than universal) for CashCheck yet. @@ -50,8 +54,8 @@ CashCheck::preflight(PreflightContext const& ctx) } // Exactly one of Amount or DeliverMin must be present. - auto const optAmount = ctx.tx[~sfAmount]; - auto const optDeliverMin = ctx.tx[~sfDeliverMin]; + auto const optAmount = get(ctx.tx[~sfAmount]); + auto const optDeliverMin = get(ctx.tx[~sfDeliverMin]); if (static_cast(optAmount) == static_cast(optDeliverMin)) { @@ -136,11 +140,11 @@ CashCheck::preclaim(PreclaimContext const& ctx) // Preflight verified exactly one of Amount or DeliverMin is present. // Make sure the requested amount is reasonable. STAmount const value{[](STTx const& tx) { - auto const optAmount = tx[~sfAmount]; - return optAmount ? *optAmount : tx[sfDeliverMin]; + auto const optAmount = get(tx[~sfAmount]); + return optAmount ? *optAmount : get(tx[sfDeliverMin]); }(ctx.tx)}; - STAmount const sendMax = sleCheck->at(sfSendMax); + STAmount const sendMax = get(sleCheck->at(sfSendMax)); Currency const currency{value.getCurrency()}; if (currency != sendMax.getCurrency()) { @@ -283,12 +287,12 @@ CashCheck::doApply() // If it is not a check to self (as should be the case), then there's // work to do... auto viewJ = ctx_.app.journal("View"); - auto const optDeliverMin = ctx_.tx[~sfDeliverMin]; + auto const optDeliverMin = get(ctx_.tx[~sfDeliverMin]); bool const doFix1623{psb.rules().enabled(fix1623)}; if (srcId != account_) { - STAmount const sendMax = sleCheck->at(sfSendMax); + STAmount const sendMax = get(sleCheck->at(sfSendMax)); // Flow() doesn't do XRP to XRP transfers. if (sendMax.native()) @@ -307,7 +311,7 @@ CashCheck::doApply() STAmount const xrpDeliver{ optDeliverMin ? std::max(*optDeliverMin, std::min(sendMax, srcLiquid)) - : ctx_.tx.getFieldAmount(sfAmount)}; + : get(ctx_.tx.getFieldAmount(sfAmount))}; if (srcLiquid < xrpDeliver) { @@ -340,11 +344,12 @@ CashCheck::doApply() // transfer rate to account for. Since the transfer rate cannot // exceed 200%, we use 1/2 maxValue as our limit. STAmount const flowDeliver{ - optDeliverMin ? STAmount( - optDeliverMin->issue(), - STAmount::cMaxValue / 2, - STAmount::cMaxOffset) - : ctx_.tx.getFieldAmount(sfAmount)}; + optDeliverMin + ? STAmount( + optDeliverMin->issue(), + STAmount::cMaxValue / 2, + STAmount::cMaxOffset) + : get(ctx_.tx.getFieldAmount(sfAmount))}; // If a trust line does not exist yet create one. Issue const& trustLineIssue = flowDeliver.issue(); @@ -419,7 +424,7 @@ CashCheck::doApply() return tecNO_LINE; SF_AMOUNT const& tweakedLimit = destLow ? sfLowLimit : sfHighLimit; - STAmount const savedLimit = sleTrustLine->at(tweakedLimit); + STEitherAmount const savedLimit = sleTrustLine->at(tweakedLimit); // Make sure the tweaked limits are restored when we leave scope. scope_exit fixup( @@ -434,7 +439,7 @@ CashCheck::doApply() // while flow runs. STAmount const bigAmount( trustLineIssue, STAmount::cMaxValue, STAmount::cMaxOffset); - sleTrustLine->at(tweakedLimit) = bigAmount; + sleTrustLine->at(tweakedLimit) = STEitherAmount{bigAmount}; } // Let flow() do the heavy lifting on a check for an IOU. @@ -449,7 +454,7 @@ CashCheck::doApply() true, // owner pays transfer fee OfferCrossing::no, std::nullopt, - sleCheck->getFieldAmount(sfSendMax), + get(sleCheck->getFieldAmount(sfSendMax)), viewJ); if (result.result() != tesSUCCESS) diff --git a/src/xrpld/app/tx/detail/Change.cpp b/src/xrpld/app/tx/detail/Change.cpp index 909f35fc799..56d005f7429 100644 --- a/src/xrpld/app/tx/detail/Change.cpp +++ b/src/xrpld/app/tx/detail/Change.cpp @@ -38,6 +38,11 @@ Change::preflight(PreflightContext const& ctx) if (!isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && + (isMPT(ctx.tx[~sfBaseFeeDrops]) || isMPT(ctx.tx[~sfReserveBaseDrops]) || + isMPT(ctx.tx[~sfReserveIncrementDrops]))) + return temMPT_INVALID_USAGE; + auto account = ctx.tx.getAccountID(sfAccount); if (account != beast::zero) { @@ -46,7 +51,7 @@ Change::preflight(PreflightContext const& ctx) } // No point in going any further if the transaction fee is malformed. - auto const fee = ctx.tx.getFieldAmount(sfFee); + auto const fee = get(ctx.tx.getFieldAmount(sfFee)); if (!fee.native() || fee != beast::zero) { JLOG(ctx.j.warn()) << "Change: invalid fee"; @@ -181,8 +186,8 @@ Change::activateTrustLinesToSelfFix() return true; } - auto const& lo = tl->getFieldAmount(sfLowLimit); - auto const& hi = tl->getFieldAmount(sfHighLimit); + auto const& lo = get(tl->getFieldAmount(sfLowLimit)); + auto const& hi = get(tl->getFieldAmount(sfHighLimit)); if (lo != hi) { diff --git a/src/xrpld/app/tx/detail/Clawback.cpp b/src/xrpld/app/tx/detail/Clawback.cpp index 08c0098f3b2..d2cb619a84b 100644 --- a/src/xrpld/app/tx/detail/Clawback.cpp +++ b/src/xrpld/app/tx/detail/Clawback.cpp @@ -28,25 +28,27 @@ namespace ripple { +template NotTEC -Clawback::preflight(PreflightContext const& ctx) +preflightHelper(PreflightContext const& ctx) { if (!ctx.rules.enabled(featureClawback)) return temDISABLED; auto const mptHolder = ctx.tx[~sfMPTokenHolder]; - STAmount const clawAmount = ctx.tx[sfAmount]; - if ((mptHolder || clawAmount.isMPT()) && - !ctx.rules.enabled(featureMPTokensV1)) + auto const clawAmount = get(ctx.tx.getFieldAmount(sfAmount)); + bool constexpr isMPT = std::is_same_v; + + if (!ctx.rules.enabled(featureMPTokensV1) && (mptHolder || isMPT)) return temDISABLED; if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - if (!mptHolder && clawAmount.isMPT()) + if (!mptHolder && isMPT) return temMALFORMED; - if (mptHolder && !clawAmount.isMPT()) + if (mptHolder && !isMPT) return temMALFORMED; if (ctx.tx.getFlags() & tfClawbackMask) @@ -55,15 +57,14 @@ Clawback::preflight(PreflightContext const& ctx) AccountID const issuer = ctx.tx[sfAccount]; // The issuer field is used for the token holder if asset is IOU - AccountID const& holder = - clawAmount.isMPT() ? *mptHolder : clawAmount.getIssuer(); + AccountID const& holder = isMPT ? *mptHolder : clawAmount.getIssuer(); - if (clawAmount.isMPT()) + if constexpr (isMPT) { if (issuer == holder) return temMALFORMED; - if (clawAmount.mpt() > MPTAmount{maxMPTokenAmount} || + if (clawAmount > MPTAmount{maxMPTokenAmount} || clawAmount <= beast::zero) return temBAD_AMOUNT; } @@ -76,13 +77,15 @@ Clawback::preflight(PreflightContext const& ctx) return preflight2(ctx); } +template TER -Clawback::preclaim(PreclaimContext const& ctx) +preclaimHelper(PreclaimContext const& ctx) { AccountID const issuer = ctx.tx[sfAccount]; - STAmount const clawAmount = ctx.tx[sfAmount]; + auto const clawAmount = get(ctx.tx.getFieldAmount(sfAmount)); + bool constexpr isMPT = std::is_same_v; AccountID const& holder = - clawAmount.isMPT() ? ctx.tx[sfMPTokenHolder] : clawAmount.getIssuer(); + isMPT ? ctx.tx[sfMPTokenHolder] : clawAmount.getIssuer(); auto const sleIssuer = ctx.view.read(keylet::account(issuer)); auto const sleHolder = ctx.view.read(keylet::account(holder)); @@ -92,10 +95,10 @@ Clawback::preclaim(PreclaimContext const& ctx) if (sleHolder->isFieldPresent(sfAMMID)) return tecAMM_ACCOUNT; - if (clawAmount.isMPT()) + if constexpr (isMPT) { - auto const issuanceKey = - keylet::mptIssuance(clawAmount.mptIssue().mpt()); + auto const& mptIssue = clawAmount.issue(); + auto const issuanceKey = keylet::mptIssuance(mptIssue.mpt()); auto const sleIssuance = ctx.view.read(issuanceKey); if (!sleIssuance) return tecOBJECT_NOT_FOUND; @@ -112,7 +115,7 @@ Clawback::preclaim(PreclaimContext const& ctx) if (accountHolds( ctx.view, holder, - clawAmount.mptIssue(), + mptIssue, fhIGNORE_FREEZE, ahIGNORE_AUTH, ctx.j) <= beast::zero) @@ -128,12 +131,13 @@ Clawback::preclaim(PreclaimContext const& ctx) (issuerFlagsIn & lsfNoFreeze)) return tecNO_PERMISSION; - auto const sleRippleState = ctx.view.read( - keylet::line(holder, issuer, clawAmount.getCurrency())); + auto const& currency = clawAmount.getCurrency(); + auto const sleRippleState = + ctx.view.read(keylet::line(holder, issuer, currency)); if (!sleRippleState) return tecNO_LINE; - STAmount const& balance = (*sleRippleState)[sfBalance]; + STAmount const& balance = get((*sleRippleState)[sfBalance]); // If balance is positive, issuer must have higher address than holder if (balance > beast::zero && issuer < holder) @@ -153,64 +157,92 @@ Clawback::preclaim(PreclaimContext const& ctx) // the available balance of a trustline is prone to new changes (eg. // XLS-34). So we must use `accountHolds`. if (accountHolds( - ctx.view, - holder, - clawAmount.getCurrency(), - issuer, - fhIGNORE_FREEZE, - ctx.j) <= beast::zero) + ctx.view, holder, currency, issuer, fhIGNORE_FREEZE, ctx.j) <= + beast::zero) return tecINSUFFICIENT_FUNDS; } return tesSUCCESS; } +template TER -Clawback::doApply() +applyHelper(ApplyContext& ctx) { - AccountID const& issuer = account_; - STAmount clawAmount = ctx_.tx[sfAmount]; - AccountID const holder = clawAmount.isMPT() - ? ctx_.tx[sfMPTokenHolder] + AccountID const& issuer = ctx.tx[sfAccount]; + auto clawAmount = get(ctx.tx.getFieldAmount(sfAmount)); + bool constexpr isMPT = std::is_same_v; + AccountID const holder = isMPT + ? ctx.tx[sfMPTokenHolder] : clawAmount.getIssuer(); // cannot be reference because clawAmount is - // modified beblow + // modified below - if (clawAmount.isMPT()) + if constexpr (isMPT) { // Get the spendable balance. Must use `accountHolds`. - STAmount const spendableAmount = accountHolds( - view(), + STMPTAmount const spendableAmount = accountHolds( + ctx.view(), holder, - clawAmount.mptIssue(), + clawAmount.issue(), fhIGNORE_FREEZE, ahIGNORE_AUTH, - j_); + ctx.journal); - return rippleMPTCredit( - view(), holder, issuer, std::min(spendableAmount, clawAmount), j_); + return rippleCredit( + ctx.view(), + holder, + issuer, + std::min(spendableAmount, clawAmount), + ctx.journal); } + else + { + // Replace the `issuer` field with issuer's account if asset is IOU + clawAmount.setIssuer(issuer); + if (holder == issuer) + return tecINTERNAL; + + // Get the spendable balance. Must use `accountHolds`. + STAmount const spendableAmount = accountHolds( + ctx.view(), + holder, + clawAmount.getCurrency(), + clawAmount.getIssuer(), + fhIGNORE_FREEZE, + ctx.journal); - // Replace the `issuer` field with issuer's account if asset is IOU - clawAmount.setIssuer(issuer); - if (holder == issuer) - return tecINTERNAL; - - // Get the spendable balance. Must use `accountHolds`. - STAmount const spendableAmount = accountHolds( - view(), - holder, - clawAmount.getCurrency(), - clawAmount.getIssuer(), - fhIGNORE_FREEZE, - j_); - - return rippleCredit( - view(), - holder, - issuer, - std::min(spendableAmount, clawAmount), - true, - j_); + return rippleCredit( + ctx.view(), + holder, + issuer, + std::min(spendableAmount, clawAmount), + true, + ctx.journal); + } +} + +NotTEC +Clawback::preflight(PreflightContext const& ctx) +{ + return std::visit( + [&](T const&) { return preflightHelper(ctx); }, + ctx.tx[sfAmount].getValue()); +} + +TER +Clawback::preclaim(PreclaimContext const& ctx) +{ + return std::visit( + [&](T const&) { return preclaimHelper(ctx); }, + ctx.tx[sfAmount].getValue()); +} + +TER +Clawback::doApply() +{ + return std::visit( + [&](T const&) { return applyHelper(ctx_); }, + ctx_.tx[sfAmount].getValue()); } } // namespace ripple diff --git a/src/xrpld/app/tx/detail/CreateCheck.cpp b/src/xrpld/app/tx/detail/CreateCheck.cpp index 3a278eed738..46e38ee2976 100644 --- a/src/xrpld/app/tx/detail/CreateCheck.cpp +++ b/src/xrpld/app/tx/detail/CreateCheck.cpp @@ -38,6 +38,9 @@ CreateCheck::preflight(PreflightContext const& ctx) if (!isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfSendMax])) + return temMPT_NOT_SUPPORTED; + if (ctx.tx.getFlags() & tfUniversalMask) { // There are no flags (other than universal) for CreateCheck yet. @@ -52,7 +55,7 @@ CreateCheck::preflight(PreflightContext const& ctx) } { - STAmount const sendMax{ctx.tx.getFieldAmount(sfSendMax)}; + STAmount const sendMax{get(ctx.tx.getFieldAmount(sfSendMax))}; if (!isLegalNet(sendMax) || sendMax.signum() <= 0) { JLOG(ctx.j.warn()) << "Malformed transaction: bad sendMax amount: " @@ -110,7 +113,7 @@ CreateCheck::preclaim(PreclaimContext const& ctx) } { - STAmount const sendMax{ctx.tx[sfSendMax]}; + STAmount const sendMax{get(ctx.tx[sfSendMax])}; if (!sendMax.native()) { // The currency may not be globally frozen diff --git a/src/xrpld/app/tx/detail/CreateOffer.cpp b/src/xrpld/app/tx/detail/CreateOffer.cpp index 2a5145594a1..5c5ed56dbf1 100644 --- a/src/xrpld/app/tx/detail/CreateOffer.cpp +++ b/src/xrpld/app/tx/detail/CreateOffer.cpp @@ -32,7 +32,7 @@ TxConsequences CreateOffer::makeTxConsequences(PreflightContext const& ctx) { auto calculateMaxXRPSpend = [](STTx const& tx) -> XRPAmount { - auto const& amount{tx[sfTakerGets]}; + auto const& amount{get(tx[sfTakerGets])}; return amount.native() ? amount.xrp() : beast::zero; }; @@ -45,6 +45,10 @@ CreateOffer::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && + (isMPT(ctx.tx[sfTakerPays]) || isMPT(ctx.tx[sfTakerGets]))) + return temMPT_NOT_SUPPORTED; + auto& tx = ctx.tx; auto& j = ctx.j; @@ -80,8 +84,8 @@ CreateOffer::preflight(PreflightContext const& ctx) return temBAD_SEQUENCE; } - STAmount saTakerPays = tx[sfTakerPays]; - STAmount saTakerGets = tx[sfTakerGets]; + STAmount saTakerPays = get(tx[sfTakerPays]); + STAmount saTakerGets = get(tx[sfTakerGets]); if (!isLegalNet(saTakerPays) || !isLegalNet(saTakerGets)) return temBAD_AMOUNT; @@ -130,8 +134,8 @@ CreateOffer::preclaim(PreclaimContext const& ctx) { auto const id = ctx.tx[sfAccount]; - auto saTakerPays = ctx.tx[sfTakerPays]; - auto saTakerGets = ctx.tx[sfTakerGets]; + auto saTakerPays = get(ctx.tx[sfTakerPays]); + auto saTakerGets = get(ctx.tx[sfTakerGets]); auto const& uPaysIssuerID = saTakerPays.getIssuer(); auto const& uPaysCurrency = saTakerPays.getCurrency(); @@ -924,8 +928,8 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) bool const bFillOrKill(uTxFlags & tfFillOrKill); bool const bSell(uTxFlags & tfSell); - auto saTakerPays = ctx_.tx[sfTakerPays]; - auto saTakerGets = ctx_.tx[sfTakerGets]; + auto saTakerPays = get(ctx_.tx[sfTakerPays]); + auto saTakerGets = get(ctx_.tx[sfTakerGets]); auto const cancelSequence = ctx_.tx[~sfOfferSequence]; diff --git a/src/xrpld/app/tx/detail/DID.cpp b/src/xrpld/app/tx/detail/DID.cpp index 6e5a3108c72..c772f77223b 100644 --- a/src/xrpld/app/tx/detail/DID.cpp +++ b/src/xrpld/app/tx/detail/DID.cpp @@ -90,7 +90,7 @@ addSLE( // Check reserve availability for new object creation { - auto const balance = STAmount((*sleAccount)[sfBalance]).xrp(); + auto const balance = get((*sleAccount)[sfBalance]).xrp(); auto const reserve = ctx.view().fees().accountReserve((*sleAccount)[sfOwnerCount] + 1); diff --git a/src/xrpld/app/tx/detail/DeleteAccount.cpp b/src/xrpld/app/tx/detail/DeleteAccount.cpp index fb2f3fc507f..80136cca832 100644 --- a/src/xrpld/app/tx/detail/DeleteAccount.cpp +++ b/src/xrpld/app/tx/detail/DeleteAccount.cpp @@ -347,8 +347,10 @@ DeleteAccount::doApply() return ter; // Transfer any XRP remaining after the fee is paid to the destination: - (*dst)[sfBalance] = (*dst)[sfBalance] + mSourceBalance; - (*src)[sfBalance] = (*src)[sfBalance] - mSourceBalance; + (*dst)[sfBalance] = + STEitherAmount{get((*dst)[sfBalance]) + mSourceBalance}; + (*src)[sfBalance] = + STEitherAmount{get((*src)[sfBalance]) - mSourceBalance}; ctx_.deliver(mSourceBalance); assert((*src)[sfBalance] == XRPAmount(0)); diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index e34b675998d..3d45095954b 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -93,7 +93,7 @@ after(NetClock::time_point now, std::uint32_t mark) TxConsequences EscrowCreate::makeTxConsequences(PreflightContext const& ctx) { - return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()}; + return TxConsequences{ctx.tx, get(ctx.tx[sfAmount]).xrp()}; } NotTEC @@ -108,7 +108,7 @@ EscrowCreate::preflight(PreflightContext const& ctx) if (!isXRP(ctx.tx[sfAmount])) return temBAD_AMOUNT; - if (ctx.tx[sfAmount] <= beast::zero) + if (get(ctx.tx[sfAmount]) <= beast::zero) return temBAD_AMOUNT; // We must specify at least one timeout value @@ -212,14 +212,14 @@ EscrowCreate::doApply() // Check reserve and funds availability { - auto const balance = STAmount((*sle)[sfBalance]).xrp(); + auto const balance = get((*sle)[sfBalance]).xrp(); auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1); if (balance < reserve) return tecINSUFFICIENT_RESERVE; - if (balance < reserve + STAmount(ctx_.tx[sfAmount]).xrp()) + if (balance < reserve + get(ctx_.tx[sfAmount]).xrp()) return tecUNFUNDED; } @@ -276,7 +276,8 @@ EscrowCreate::doApply() } // Deduct owner's balance, increment owner count - (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; + (*sle)[sfBalance] = STEitherAmount{ + get((*sle)[sfBalance]) - get(ctx_.tx[sfAmount])}; adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); ctx_.view().update(sle); @@ -496,7 +497,8 @@ EscrowFinish::doApply() } // Transfer amount to destination - (*sled)[sfBalance] = (*sled)[sfBalance] + (*slep)[sfAmount]; + (*sled)[sfBalance] = STEitherAmount{ + get((*sled)[sfBalance]) + get((*slep)[sfAmount])}; ctx_.view().update(sled); // Adjust source owner count @@ -582,7 +584,8 @@ EscrowCancel::doApply() // Transfer amount back to owner, decrement owner count auto const sle = ctx_.view().peek(keylet::account(account)); - (*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount]; + (*sle)[sfBalance] = STEitherAmount{ + get((*sle)[sfBalance]) + get((*slep)[sfAmount])}; adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal); ctx_.view().update(sle); diff --git a/src/xrpld/app/tx/detail/InvariantCheck.cpp b/src/xrpld/app/tx/detail/InvariantCheck.cpp index e77d1851546..bb23bbc4f8a 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.cpp +++ b/src/xrpld/app/tx/detail/InvariantCheck.cpp @@ -68,7 +68,7 @@ TransactionFeeCheck::finalize( // We should never charge more for a transaction than the transaction // authorizes. It's possible to charge less in some circumstances. - if (fee > tx.getFieldAmount(sfFee).xrp()) + if (fee > get(tx.getFieldAmount(sfFee)).xrp()) { JLOG(j.fatal()) << "Invariant failed: fee paid is " << fee.drops() << " exceeds fee specified in transaction."; @@ -98,14 +98,16 @@ XRPNotCreated::visitEntry( switch (before->getType()) { case ltACCOUNT_ROOT: - drops_ -= (*before)[sfBalance].xrp().drops(); + drops_ -= get((*before)[sfBalance]).xrp().drops(); break; case ltPAYCHAN: - drops_ -= - ((*before)[sfAmount] - (*before)[sfBalance]).xrp().drops(); + drops_ -= (get((*before)[sfAmount]) - + get((*before)[sfBalance])) + .xrp() + .drops(); break; case ltESCROW: - drops_ -= (*before)[sfAmount].xrp().drops(); + drops_ -= get((*before)[sfAmount]).xrp().drops(); break; default: break; @@ -117,17 +119,18 @@ XRPNotCreated::visitEntry( switch (after->getType()) { case ltACCOUNT_ROOT: - drops_ += (*after)[sfBalance].xrp().drops(); + drops_ += get((*after)[sfBalance]).xrp().drops(); break; case ltPAYCHAN: if (!isDelete) - drops_ += ((*after)[sfAmount] - (*after)[sfBalance]) + drops_ += (get((*after)[sfAmount]) - + get((*after)[sfBalance])) .xrp() .drops(); break; case ltESCROW: if (!isDelete) - drops_ += (*after)[sfAmount].xrp().drops(); + drops_ += get((*after)[sfAmount]).xrp().drops(); break; default: break; @@ -190,10 +193,10 @@ XRPBalanceChecks::visitEntry( }; if (before && before->getType() == ltACCOUNT_ROOT) - bad_ |= isBad((*before)[sfBalance]); + bad_ |= isBad(get((*before)[sfBalance])); if (after && after->getType() == ltACCOUNT_ROOT) - bad_ |= isBad((*after)[sfBalance]); + bad_ |= isBad(get((*after)[sfBalance])); } bool @@ -234,10 +237,14 @@ NoBadOffers::visitEntry( }; if (before && before->getType() == ltOFFER) - bad_ |= isBad((*before)[sfTakerPays], (*before)[sfTakerGets]); + bad_ |= isBad( + get((*before)[sfTakerPays]), + get((*before)[sfTakerGets])); if (after && after->getType() == ltOFFER) - bad_ |= isBad((*after)[sfTakerPays], (*after)[sfTakerGets]); + bad_ |= isBad( + get((*after)[sfTakerPays]), + get((*after)[sfTakerGets])); } bool @@ -279,10 +286,10 @@ NoZeroEscrow::visitEntry( }; if (before && before->getType() == ltESCROW) - bad_ |= isBad((*before)[sfAmount]); + bad_ |= isBad(get((*before)[sfAmount])); if (after && after->getType() == ltESCROW) - bad_ |= isBad((*after)[sfAmount]); + bad_ |= isBad(get((*after)[sfAmount])); } bool @@ -441,8 +448,10 @@ NoXRPTrustLines::visitEntry( // relying on .native() just in case native somehow // were systematically incorrect xrpTrustLine_ = - after->getFieldAmount(sfLowLimit).issue() == xrpIssue() || - after->getFieldAmount(sfHighLimit).issue() == xrpIssue(); + get(after->getFieldAmount(sfLowLimit)).issue() == + xrpIssue() || + get(after->getFieldAmount(sfHighLimit)).issue() == + xrpIssue(); } } @@ -789,7 +798,7 @@ ValidClawback::finalize( if (trustlinesChanged == 1) { AccountID const issuer = tx.getAccountID(sfAccount); - STAmount const& amount = tx.getFieldAmount(sfAmount); + STAmount const& amount = get(tx.getFieldAmount(sfAmount)); AccountID const& holder = amount.getIssuer(); STAmount const holderBalance = accountHolds( view, holder, amount.getCurrency(), issuer, fhIGNORE_FREEZE, j); diff --git a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp index b884a791e78..782d8d5a23b 100644 --- a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp @@ -36,6 +36,10 @@ NFTokenAcceptOffer::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && + isMPT(ctx.tx[~sfNFTokenBrokerFee])) + return temMPT_INVALID_USAGE; + if (ctx.tx.getFlags() & tfNFTokenAcceptOfferMask) return temINVALID_FLAG; @@ -48,7 +52,7 @@ NFTokenAcceptOffer::preflight(PreflightContext const& ctx) // The `BrokerFee` field must not be present in direct mode but may be // present and greater than zero in brokered mode. - if (auto const bf = ctx.tx[~sfNFTokenBrokerFee]) + if (auto const bf = get(ctx.tx[~sfNFTokenBrokerFee])) { if (!bo || !so) return temMALFORMED; @@ -104,7 +108,8 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) return tecNFTOKEN_BUY_SELL_MISMATCH; // The two offers being brokered must be for the same asset: - if ((*bo)[sfAmount].issue() != (*so)[sfAmount].issue()) + if (get((*bo)[sfAmount]).issue() != + get((*so)[sfAmount]).issue()) return tecNFTOKEN_BUY_SELL_MISMATCH; // The two offers may not form a loop. A broker may not sell the @@ -115,7 +120,7 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // Ensure that the buyer is willing to pay at least as much as the // seller is requesting: - if ((*so)[sfAmount] > (*bo)[sfAmount]) + if (get((*so)[sfAmount]) > get((*bo)[sfAmount])) return tecINSUFFICIENT_PAYMENT; // If the buyer specified a destination @@ -150,15 +155,16 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // have, ensure that the seller will get at least as much as they want // to get *after* this fee is accounted for (but before the issuer's // cut, if any). - if (auto const brokerFee = ctx.tx[~sfNFTokenBrokerFee]) + if (auto const brokerFee = get(ctx.tx[~sfNFTokenBrokerFee])) { - if (brokerFee->issue() != (*bo)[sfAmount].issue()) + if (brokerFee->issue() != get((*bo)[sfAmount]).issue()) return tecNFTOKEN_BUY_SELL_MISMATCH; - if (brokerFee >= (*bo)[sfAmount]) + if (brokerFee >= get((*bo)[sfAmount])) return tecINSUFFICIENT_PAYMENT; - if ((*so)[sfAmount] > (*bo)[sfAmount] - *brokerFee) + if (get((*so)[sfAmount]) > + get((*bo)[sfAmount]) - *brokerFee) return tecINSUFFICIENT_PAYMENT; } } @@ -191,7 +197,7 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // // After this amendment, we allow an IOU issuer to buy an NFT with their // own currency - auto const needed = bo->at(sfAmount); + auto const needed = get(bo->at(sfAmount)); if (ctx.view.rules().enabled(fixNonFungibleTokensV1_2)) { if (accountFunds( @@ -234,7 +240,7 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) } // The account offering to buy must have funds: - auto const needed = so->at(sfAmount); + auto const needed = get(so->at(sfAmount)); if (!ctx.view.rules().enabled(fixNonFungibleTokensV1_2)) { if (accountHolds( @@ -280,7 +286,7 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) return tecINTERNAL; uint256 const& tokenID = offer->at(sfNFTokenID); - STAmount const& amount = offer->at(sfAmount); + STAmount const& amount = get(offer->at(sfAmount)); if (nft::getTransferFee(tokenID) != 0 && (nft::getFlags(tokenID) & nft::flagCreateTrustLines) == 0 && !amount.native()) @@ -362,7 +368,8 @@ NFTokenAcceptOffer::transferNFToken( // the deduction of the potential offer price. A small caveat here is // that the balance has already deducted the transaction fee, meaning // that the reserve requirement is a few drops higher. - auto const buyerBalance = sleBuyer->getFieldAmount(sfBalance); + auto const buyerBalance = + get(sleBuyer->getFieldAmount(sfBalance)); auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount); if (buyerOwnerCountAfter > buyerOwnerCountBefore) @@ -387,7 +394,8 @@ NFTokenAcceptOffer::acceptOffer(std::shared_ptr const& offer) auto const nftokenID = (*offer)[sfNFTokenID]; - if (auto amount = offer->getFieldAmount(sfAmount); amount != beast::zero) + if (auto amount = get(offer->getFieldAmount(sfAmount)); + amount != beast::zero) { // Calculate the issuer's cut from this sale, if any: if (auto const fee = nft::getTransferFee(nftokenID); fee != 0) @@ -448,7 +456,7 @@ NFTokenAcceptOffer::doApply() auto const nftokenID = (*so)[sfNFTokenID]; // The amount is what the buyer of the NFT pays: - STAmount amount = (*bo)[sfAmount]; + STAmount amount = get((*bo)[sfAmount]); // Three different folks may be paid. The order of operations is // important. @@ -464,7 +472,7 @@ NFTokenAcceptOffer::doApply() // being paid out than the seller authorized. That would be bad! // Send the broker the amount they requested. - if (auto const cut = ctx_.tx[~sfNFTokenBrokerFee]; + if (auto const cut = get(ctx_.tx[~sfNFTokenBrokerFee]); cut && cut.value() != beast::zero) { if (auto const r = pay(buyer, account_, cut.value()); diff --git a/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp b/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp index 43178d31b4a..0f42fadb316 100644 --- a/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp @@ -36,6 +36,9 @@ NFTokenCreateOffer::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) + return temMPT_NOT_SUPPORTED; + auto const txFlags = ctx.tx.getFlags(); if (txFlags & tfNFTokenCreateOfferMask) @@ -46,7 +49,7 @@ NFTokenCreateOffer::preflight(PreflightContext const& ctx) // Use implementation shared with NFTokenMint if (NotTEC notTec = nft::tokenOfferCreatePreflight( ctx.tx[sfAccount], - ctx.tx[sfAmount], + get(ctx.tx[sfAmount]), ctx.tx[~sfDestination], ctx.tx[~sfExpiration], nftFlags, @@ -79,7 +82,7 @@ NFTokenCreateOffer::preclaim(PreclaimContext const& ctx) ctx.view, ctx.tx[sfAccount], nft::getIssuer(nftokenID), - ctx.tx[sfAmount], + get(ctx.tx[sfAmount]), ctx.tx[~sfDestination], nft::getFlags(nftokenID), nft::getTransferFee(nftokenID), @@ -95,7 +98,7 @@ NFTokenCreateOffer::doApply() return nft::tokenOfferCreateApply( view(), ctx_.tx[sfAccount], - ctx_.tx[sfAmount], + get(ctx_.tx[sfAmount]), ctx_.tx[~sfDestination], ctx_.tx[~sfExpiration], ctx_.tx.getSeqProxy(), diff --git a/src/xrpld/app/tx/detail/NFTokenMint.cpp b/src/xrpld/app/tx/detail/NFTokenMint.cpp index d5c3a8707c2..30834df70f0 100644 --- a/src/xrpld/app/tx/detail/NFTokenMint.cpp +++ b/src/xrpld/app/tx/detail/NFTokenMint.cpp @@ -53,6 +53,9 @@ NFTokenMint::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[~sfAmount])) + return temMPT_NOT_SUPPORTED; + // Prior to fixRemoveNFTokenAutoTrustLine, transfer of an NFToken between // accounts allowed a TrustLine to be added to the issuer of that token // without explicit permission from that issuer. This was enabled by @@ -105,7 +108,7 @@ NFTokenMint::preflight(PreflightContext const& ctx) // because a Mint is only allowed to create a sell offer. if (NotTEC notTec = nft::tokenOfferCreatePreflight( ctx.tx[sfAccount], - ctx.tx[sfAmount], + get(ctx.tx[sfAmount]), ctx.tx[~sfDestination], ctx.tx[~sfExpiration], extractNFTokenFlagsFromTxFlags(ctx.tx.getFlags()), @@ -195,7 +198,7 @@ NFTokenMint::preclaim(PreclaimContext const& ctx) ctx.view, ctx.tx[sfAccount], ctx.tx[~sfIssuer].value_or(ctx.tx[sfAccount]), - ctx.tx[sfAmount], + get(ctx.tx[sfAmount]), ctx.tx[~sfDestination], extractNFTokenFlagsFromTxFlags(ctx.tx.getFlags()), ctx.tx[~sfTransferFee].value_or(0), @@ -323,7 +326,7 @@ NFTokenMint::doApply() if (TER const ter = nft::tokenOfferCreateApply( view(), ctx_.tx[sfAccount], - ctx_.tx[sfAmount], + get(ctx_.tx[sfAmount]), ctx_.tx[~sfDestination], ctx_.tx[~sfExpiration], ctx_.tx.getSeqProxy(), diff --git a/src/xrpld/app/tx/detail/NFTokenUtils.cpp b/src/xrpld/app/tx/detail/NFTokenUtils.cpp index b2ed29714df..876dfdf23d5 100644 --- a/src/xrpld/app/tx/detail/NFTokenUtils.cpp +++ b/src/xrpld/app/tx/detail/NFTokenUtils.cpp @@ -863,7 +863,7 @@ tokenOfferCreateApply( auto offer = std::make_shared(offerID); (*offer)[sfOwner] = acctID; (*offer)[sfNFTokenID] = nftokenID; - (*offer)[sfAmount] = amount; + (*offer)[sfAmount] = STEitherAmount{amount}; (*offer)[sfFlags] = sleFlags; (*offer)[sfOwnerNode] = *ownerNode; (*offer)[sfNFTokenOfferNode] = *offerNode; diff --git a/src/xrpld/app/tx/detail/Offer.h b/src/xrpld/app/tx/detail/Offer.h index a6f707ba561..27dd4fd5fe0 100644 --- a/src/xrpld/app/tx/detail/Offer.h +++ b/src/xrpld/app/tx/detail/Offer.h @@ -183,8 +183,8 @@ TOffer::TOffer(SLE::pointer const& entry, Quality quality) , m_quality(quality) , m_account(m_entry->getAccountID(sfAccount)) { - auto const tp = m_entry->getFieldAmount(sfTakerPays); - auto const tg = m_entry->getFieldAmount(sfTakerGets); + auto const tp = get(m_entry->getFieldAmount(sfTakerPays)); + auto const tg = get(m_entry->getFieldAmount(sfTakerGets)); m_amounts.in = toAmount(tp); m_amounts.out = toAmount(tg); this->issIn_ = tp.issue(); @@ -199,8 +199,8 @@ inline TOffer::TOffer( , m_quality(quality) , m_account(m_entry->getAccountID(sfAccount)) , m_amounts( - m_entry->getFieldAmount(sfTakerPays), - m_entry->getFieldAmount(sfTakerGets)) + get(m_entry->getFieldAmount(sfTakerPays)), + get(m_entry->getFieldAmount(sfTakerGets))) { } diff --git a/src/xrpld/app/tx/detail/PayChan.cpp b/src/xrpld/app/tx/detail/PayChan.cpp index d17736c4738..c7f20093302 100644 --- a/src/xrpld/app/tx/detail/PayChan.cpp +++ b/src/xrpld/app/tx/detail/PayChan.cpp @@ -149,9 +149,11 @@ closeChannel( if (!sle) return tefINTERNAL; - assert((*slep)[sfAmount] >= (*slep)[sfBalance]); - (*sle)[sfBalance] = - (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance]; + assert( + get((*slep)[sfAmount]) >= get((*slep)[sfBalance])); + (*sle)[sfBalance] = STEitherAmount{ + get((*sle)[sfBalance]) + get((*slep)[sfAmount]) - + get((*slep)[sfBalance])}; adjustOwnerCount(view, sle, -1, j); view.update(sle); @@ -165,7 +167,7 @@ closeChannel( TxConsequences PayChanCreate::makeTxConsequences(PreflightContext const& ctx) { - return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()}; + return TxConsequences{ctx.tx, get(ctx.tx[sfAmount]).xrp()}; } NotTEC @@ -177,7 +179,8 @@ PayChanCreate::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::zero)) + if (!isXRP(ctx.tx[sfAmount]) || + (get(ctx.tx[sfAmount]) <= beast::zero)) return temBAD_AMOUNT; if (ctx.tx[sfAccount] == ctx.tx[sfDestination]) @@ -199,14 +202,14 @@ PayChanCreate::preclaim(PreclaimContext const& ctx) // Check reserve and funds availability { - auto const balance = (*sle)[sfBalance]; + auto const balance = get((*sle)[sfBalance]); auto const reserve = ctx.view.fees().accountReserve((*sle)[sfOwnerCount] + 1); if (balance < reserve) return tecINSUFFICIENT_RESERVE; - if (balance < reserve + ctx.tx[sfAmount]) + if (balance < reserve + get(ctx.tx[sfAmount])) return tecUNFUNDED; } @@ -295,7 +298,8 @@ PayChanCreate::doApply() } // Deduct owner's balance, increment owner count - (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; + (*sle)[sfBalance] = STEitherAmount{ + get((*sle)[sfBalance]) - get(ctx_.tx[sfAmount])}; adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); ctx_.view().update(sle); @@ -307,7 +311,7 @@ PayChanCreate::doApply() TxConsequences PayChanFund::makeTxConsequences(PreflightContext const& ctx) { - return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()}; + return TxConsequences{ctx.tx, get(ctx.tx[sfAmount]).xrp()}; } NotTEC @@ -319,7 +323,8 @@ PayChanFund::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::zero)) + if (!isXRP(ctx.tx[sfAmount]) || + (get(ctx.tx[sfAmount]) <= beast::zero)) return temBAD_AMOUNT; return preflight2(ctx); @@ -371,14 +376,14 @@ PayChanFund::doApply() { // Check reserve and funds availability - auto const balance = (*sle)[sfBalance]; + auto const balance = get((*sle)[sfBalance]); auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount]); if (balance < reserve) return tecINSUFFICIENT_RESERVE; - if (balance < reserve + ctx_.tx[sfAmount]) + if (balance < reserve + get(ctx_.tx[sfAmount])) return tecUNFUNDED; } @@ -389,10 +394,12 @@ PayChanFund::doApply() return tecNO_DST; } - (*slep)[sfAmount] = (*slep)[sfAmount] + ctx_.tx[sfAmount]; + (*slep)[sfAmount] = STEitherAmount{ + get((*slep)[sfAmount]) + get(ctx_.tx[sfAmount])}; ctx_.view().update(slep); - (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; + (*sle)[sfBalance] = STEitherAmount{ + get((*sle)[sfBalance]) - get(ctx_.tx[sfAmount])}; ctx_.view().update(sle); return tesSUCCESS; @@ -406,11 +413,11 @@ PayChanClaim::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - auto const bal = ctx.tx[~sfBalance]; + auto const bal = get(ctx.tx[~sfBalance]); if (bal && (!isXRP(*bal) || *bal <= beast::zero)) return temBAD_AMOUNT; - auto const amt = ctx.tx[~sfAmount]; + auto const amt = get(ctx.tx[~sfAmount]); if (amt && (!isXRP(*amt) || *amt <= beast::zero)) return temBAD_AMOUNT; @@ -484,9 +491,11 @@ PayChanClaim::doApply() if (ctx_.tx[~sfBalance]) { - auto const chanBalance = slep->getFieldAmount(sfBalance).xrp(); - auto const chanFunds = slep->getFieldAmount(sfAmount).xrp(); - auto const reqBalance = ctx_.tx[sfBalance].xrp(); + auto const chanBalance = + get(slep->getFieldAmount(sfBalance)).xrp(); + auto const chanFunds = + get(slep->getFieldAmount(sfAmount)).xrp(); + auto const reqBalance = get(ctx_.tx[sfBalance]).xrp(); if (txAccount == dst && !ctx_.tx[~sfSignature]) return temBAD_SIGNATURE; @@ -533,7 +542,8 @@ PayChanClaim::doApply() (*slep)[sfBalance] = ctx_.tx[sfBalance]; XRPAmount const reqDelta = reqBalance - chanBalance; assert(reqDelta >= beast::zero); - (*sled)[sfBalance] = (*sled)[sfBalance] + reqDelta; + (*sled)[sfBalance] = + STEitherAmount{get((*sled)[sfBalance]) + reqDelta}; ctx_.view().update(sled); ctx_.view().update(slep); } @@ -549,7 +559,9 @@ PayChanClaim::doApply() if (ctx_.tx.getFlags() & tfClose) { // Channel will close immediately if dry or the receiver closes - if (dst == txAccount || (*slep)[sfBalance] == (*slep)[sfAmount]) + if (dst == txAccount || + get((*slep)[sfBalance]) == + get((*slep)[sfAmount])) return closeChannel( slep, ctx_.view(), k.key, ctx_.app.journal("View")); diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 7779f441d64..b268b25e9a5 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -28,12 +28,18 @@ namespace ripple { +template TxConsequences -Payment::makeTxConsequences(PreflightContext const& ctx) +makeTxConsequencesHelper(PreflightContext const& ctx); + +template <> +TxConsequences +makeTxConsequencesHelper(PreflightContext const& ctx) { auto calculateMaxXRPSpend = [](STTx const& tx) -> XRPAmount { - STAmount const maxAmount = - tx.isFieldPresent(sfSendMax) ? tx[sfSendMax] : tx[sfAmount]; + auto const maxAmount = tx.isFieldPresent(sfSendMax) + ? get(tx[sfSendMax]) + : get(tx[sfAmount]); // If there's no sfSendMax in XRP, and the sfAmount isn't // in XRP, then the transaction does not spend XRP. @@ -43,12 +49,62 @@ Payment::makeTxConsequences(PreflightContext const& ctx) return TxConsequences{ctx.tx, calculateMaxXRPSpend(ctx.tx)}; } +template +TxConsequences +makeTxConsequencesHelper(PreflightContext const& ctx) +{ + return TxConsequences{ctx.tx, beast::zero}; +} + +inline STMPTAmount const& +getMaxAmount(STMPTAmount const& dstAmount, AccountID const&) +{ + return dstAmount; +} + +inline STAmount +getMaxAmount(STAmount const& dstAmount, AccountID const& account) +{ + return STAmount( + {dstAmount.getCurrency(), account}, + dstAmount.mantissa(), + dstAmount.exponent(), + dstAmount < beast::zero); +} + +template +TMax +getMaxSourceAmount( + TDel const& dstAmount, + std::optional const& max, + AccountID const& account) +{ + if (max) + return *max; + + if constexpr (std::is_same_v) + { + if (isNative(dstAmount)) + return dstAmount; + else + return getMaxAmount(dstAmount, account); + } + else + Throw("Invalid sfAmount and sfSendMax combination"); +} + +template NotTEC -Payment::preflight(PreflightContext const& ctx) +preflightHelper(PreflightContext const& ctx) { if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (!ctx.rules.enabled(featureMPTokensV1) && + (isMPT(ctx.tx[sfAmount]) || isMPT(ctx.tx[~sfSendMax]) || + isMPT(ctx.tx[~sfDeliverMin]))) + return temDISABLED; + auto& tx = ctx.tx; auto& j = ctx.j; @@ -67,35 +123,18 @@ Payment::preflight(PreflightContext const& ctx) bool const bPaths = tx.isFieldPresent(sfPaths); bool const bMax = tx.isFieldPresent(sfSendMax); - STAmount const saDstAmount(tx.getFieldAmount(sfAmount)); + TDel const saDstAmount(get(tx.getFieldAmount(sfAmount))); - STAmount maxSourceAmount; auto const account = tx.getAccountID(sfAccount); - if (bMax) - maxSourceAmount = tx.getFieldAmount(sfSendMax); - else if (saDstAmount.native()) - maxSourceAmount = saDstAmount; - else - { - auto const asset = [&]() -> Asset { - if (saDstAmount.isMPT()) - return saDstAmount.asset(); - return Issue{saDstAmount.getCurrency(), account}; - }(); - maxSourceAmount = STAmount( - asset, - saDstAmount.mantissa(), - saDstAmount.exponent(), - saDstAmount < beast::zero); - } + auto const maxSourceAmount = getMaxSourceAmount( + saDstAmount, get(tx[~sfSendMax]), account); - auto const& uSrcAsset = maxSourceAmount.asset(); - auto const& uDstAsset = saDstAmount.asset(); + auto const& uSrcCurrency = maxSourceAmount.getCurrency(); + auto const& uDstCurrency = saDstAmount.getCurrency(); - // isZero() is XRP. FIX! - bool const bXRPDirect = isXRP(uSrcAsset) && isXRP(uDstAsset); - bool const bMPTDirect = isMPT(uSrcAsset) && isMPT(uDstAsset); + bool const bXRPDirect = isXRP(uSrcCurrency) && isXRP(uDstCurrency); + bool constexpr bMPTDirect = std::is_same_v; bool const bDirect = bXRPDirect || bMPTDirect; if (!isLegalNet(saDstAmount) || !isLegalNet(maxSourceAmount)) @@ -121,42 +160,44 @@ Payment::preflight(PreflightContext const& ctx) << "bad dst amount: " << saDstAmount.getFullText(); return temBAD_AMOUNT; } - if ((uSrcAsset.isIssue() && badCurrency() == uSrcAsset.issue().currency) || - (uDstAsset.isIssue() && badCurrency() == uDstAsset.issue().currency)) + if (badAsset(uSrcCurrency) || badAsset(uDstCurrency)) { JLOG(j.trace()) << "Malformed transaction: " - << "Bad currency."; + << "Bad asset."; return temBAD_CURRENCY; } - if (account == uDstAccountID && uSrcAsset == uDstAsset && !bPaths) + if (account == uDstAccountID && sameAsset(uSrcCurrency, uDstCurrency) && + !bPaths) { // You're signing yourself a payment. // If bPaths is true, you might be trying some arbitrage. JLOG(j.trace()) << "Malformed transaction: " << "Redundant payment from " << to_string(account) - << " to self without path for " << to_string(uDstAsset); + << " to self without path for " + << to_string(uDstCurrency); return temREDUNDANT; } - if (bDirect && bMax) + if (bXRPDirect && bMax) { // Consistent but redundant transaction. JLOG(j.trace()) << "Malformed transaction: " - << "SendMax specified for XRP to XRP or MPT to MPT."; - return temBAD_SEND_XRP_MAX; // TODO MPT new err code here and below + << "SendMax specified for XRP to XRP."; + return temBAD_SEND_XRP_MAX; } if (bDirect && bPaths) { // XRP is sent without paths. JLOG(j.trace()) << "Malformed transaction: " << "Paths specified for XRP to XRP or MPT to MPT."; + if (bMPTDirect) + return temMPT_NOT_SUPPORTED; return temBAD_SEND_XRP_PATHS; } - if (bDirect && partialPaymentAllowed) + if (bXRPDirect && partialPaymentAllowed) { // Consistent but redundant transaction. - JLOG(j.trace()) - << "Malformed transaction: " - << "Partial payment specified for XRP to XRP or MPT to MPT."; + JLOG(j.trace()) << "Malformed transaction: " + << "Partial payment specified for XRP to XRP."; return temBAD_SEND_XRP_PARTIAL; } if (bDirect && limitQuality) @@ -165,6 +206,8 @@ Payment::preflight(PreflightContext const& ctx) JLOG(j.trace()) << "Malformed transaction: " << "Limit quality specified for XRP to XRP or MPT to MPT."; + if (bMPTDirect) + return temMPT_NOT_SUPPORTED; return temBAD_SEND_XRP_LIMIT; } if (bDirect && !defaultPathsAllowed) @@ -173,10 +216,12 @@ Payment::preflight(PreflightContext const& ctx) JLOG(j.trace()) << "Malformed transaction: " << "No ripple direct specified for XRP to XRP or MPT to MPT."; + if (bMPTDirect) + return temMPT_NOT_SUPPORTED; return temBAD_SEND_XRP_NO_DIRECT; } - auto const deliverMin = tx[~sfDeliverMin]; + auto const deliverMin = get(ctx.tx[~sfDeliverMin]); if (deliverMin) { if (!partialPaymentAllowed) @@ -215,25 +260,28 @@ Payment::preflight(PreflightContext const& ctx) return preflight2(ctx); } +template TER -Payment::preclaim(PreclaimContext const& ctx) +preclaimHelper( + PreclaimContext const& ctx, + std::size_t maxPathSize, + std::size_t maxPathLength) { // Ripple if source or destination is non-native or if there are paths. std::uint32_t const uTxFlags = ctx.tx.getFlags(); bool const partialPaymentAllowed = uTxFlags & tfPartialPayment; auto const paths = ctx.tx.isFieldPresent(sfPaths); - auto const sendMax = ctx.tx[~sfSendMax]; + auto const sendMax = get(ctx.tx[~sfSendMax]); AccountID const uDstAccountID(ctx.tx[sfDestination]); - STAmount const saDstAmount(ctx.tx[sfAmount]); + TDel const saDstAmount(get(ctx.tx.getFieldAmount(sfAmount))); auto const k = keylet::account(uDstAccountID); auto const sleDst = ctx.view.read(k); if (!sleDst) { - // Destination account does not exist. - if (!saDstAmount.native()) + if constexpr (std::is_same_v) { JLOG(ctx.j.trace()) << "Delay transaction: Destination account does not exist."; @@ -242,29 +290,42 @@ Payment::preclaim(PreclaimContext const& ctx) // transaction would succeed. return tecNO_DST; } - else if (ctx.view.open() && partialPaymentAllowed) - { - // You cannot fund an account with a partial payment. - // Make retry work smaller, by rejecting this. - JLOG(ctx.j.trace()) << "Delay transaction: Partial payment not " - "allowed to create account."; - - // Another transaction could create the account and then this - // transaction would succeed. - return telNO_DST_PARTIAL; - } - else if (saDstAmount < STAmount(ctx.view.fees().accountReserve(0))) + else if constexpr (std::is_same_v) { - // accountReserve is the minimum amount that an account can have. - // Reserve is not scaled by load. - JLOG(ctx.j.trace()) - << "Delay transaction: Destination account does not exist. " - << "Insufficent payment to create account."; + // Destination account does not exist. + if (!saDstAmount.native()) + { + JLOG(ctx.j.trace()) + << "Delay transaction: Destination account does not exist."; - // TODO: dedupe - // Another transaction could create the account and then this - // transaction would succeed. - return tecNO_DST_INSUF_XRP; + // Another transaction could create the account and then this + // transaction would succeed. + return tecNO_DST; + } + else if (ctx.view.open() && partialPaymentAllowed) + { + // You cannot fund an account with a partial payment. + // Make retry work smaller, by rejecting this. + JLOG(ctx.j.trace()) << "Delay transaction: Partial payment not " + "allowed to create account."; + + // Another transaction could create the account and then this + // transaction would succeed. + return telNO_DST_PARTIAL; + } + else if (saDstAmount < STAmount(ctx.view.fees().accountReserve(0))) + { + // accountReserve is the minimum amount that an account can + // have. Reserve is not scaled by load. + JLOG(ctx.j.trace()) + << "Delay transaction: Destination account does not exist. " + << "Insufficent payment to create account."; + + // TODO: dedupe + // Another transaction could create the account and then this + // transaction would succeed. + return tecNO_DST_INSUF_XRP; + } } } else if ( @@ -283,13 +344,13 @@ Payment::preclaim(PreclaimContext const& ctx) } // Payment with at least one intermediate step and uses transitive balances. - if ((paths || sendMax || !saDstAmount.native()) && ctx.view.open()) + if ((paths || sendMax || !isNative(saDstAmount)) && ctx.view.open()) { STPathSet const& paths = ctx.tx.getFieldPathSet(sfPaths); - if (paths.size() > MaxPathSize || - std::any_of(paths.begin(), paths.end(), [](STPath const& path) { - return path.size() > MaxPathLength; + if (paths.size() > maxPathSize || + std::any_of(paths.begin(), paths.end(), [&](STPath const& path) { + return path.size() > maxPathLength; })) { return telBAD_PATH_COUNT; @@ -299,265 +360,320 @@ Payment::preclaim(PreclaimContext const& ctx) return tesSUCCESS; } +template TER -Payment::doApply() +applyHelper( + ApplyContext& ctx, + XRPAmount const& priorBalance, + XRPAmount const& sourceBalance) { - auto const deliverMin = ctx_.tx[~sfDeliverMin]; + auto const deliverMin = get(ctx.tx[~sfDeliverMin]); // Ripple if source or destination is non-native or if there are paths. - std::uint32_t const uTxFlags = ctx_.tx.getFlags(); + std::uint32_t const uTxFlags = ctx.tx.getFlags(); bool const partialPaymentAllowed = uTxFlags & tfPartialPayment; bool const limitQuality = uTxFlags & tfLimitQuality; bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect); - auto const paths = ctx_.tx.isFieldPresent(sfPaths); - auto const sendMax = ctx_.tx[~sfSendMax]; - - AccountID const uDstAccountID(ctx_.tx.getAccountID(sfDestination)); - STAmount const saDstAmount(ctx_.tx.getFieldAmount(sfAmount)); - STAmount maxSourceAmount; - if (sendMax) - maxSourceAmount = *sendMax; - else if (saDstAmount.native()) - maxSourceAmount = saDstAmount; - else - { - auto const asset = [&]() -> Asset { - if (saDstAmount.isMPT()) - return saDstAmount.asset(); - return Issue{saDstAmount.getCurrency(), account_}; - }(); - maxSourceAmount = STAmount( - asset, - saDstAmount.mantissa(), - saDstAmount.exponent(), - saDstAmount < beast::zero); - } + auto const paths = ctx.tx.isFieldPresent(sfPaths); + auto const sendMax = get(ctx.tx[~sfSendMax]); + auto const account = ctx.tx[sfAccount]; - JLOG(j_.trace()) << "maxSourceAmount=" << maxSourceAmount.getFullText() - << " saDstAmount=" << saDstAmount.getFullText(); + AccountID const uDstAccountID(ctx.tx.getAccountID(sfDestination)); + auto const saDstAmount(get(ctx.tx.getFieldAmount(sfAmount))); + auto const maxSourceAmount = + getMaxSourceAmount(saDstAmount, sendMax, account); + + JLOG(ctx.journal.trace()) + << "maxSourceAmount=" << maxSourceAmount.getFullText() + << " saDstAmount=" << saDstAmount.getFullText(); // Open a ledger for editing. auto const k = keylet::account(uDstAccountID); - SLE::pointer sleDst = view().peek(k); + SLE::pointer sleDst = ctx.view().peek(k); if (!sleDst) { std::uint32_t const seqno{ - view().rules().enabled(featureDeletableAccounts) ? view().seq() - : 1}; + ctx.view().rules().enabled(featureDeletableAccounts) + ? ctx.view().seq() + : 1}; // Create the account. sleDst = std::make_shared(k); sleDst->setAccountID(sfAccount, uDstAccountID); sleDst->setFieldU32(sfSequence, seqno); - view().insert(sleDst); + ctx.view().insert(sleDst); } else { // Tell the engine that we are intending to change the destination // account. The source account gets always charged a fee so it's always // marked as modified. - view().update(sleDst); + ctx.view().update(sleDst); } // Determine whether the destination requires deposit authorization. bool const reqDepositAuth = sleDst->getFlags() & lsfDepositAuth && - view().rules().enabled(featureDepositAuth); + ctx.view().rules().enabled(featureDepositAuth); - bool const depositPreauth = view().rules().enabled(featureDepositPreauth); + bool const depositPreauth = + ctx.view().rules().enabled(featureDepositPreauth); bool const bRipple = - paths || sendMax || !(saDstAmount.native() || saDstAmount.isMPT()); + paths || sendMax || !(isNative(saDstAmount) || isMPT(saDstAmount)); // If the destination has lsfDepositAuth set, then only direct XRP // payments (no intermediate steps) are allowed to the destination. if (!depositPreauth && bRipple && reqDepositAuth) return tecNO_PERMISSION; - if (bRipple) + if constexpr ( + std::is_same_v && std::is_same_v) { - // Ripple payment with at least one intermediate step and uses - // transitive balances. - - if (depositPreauth && reqDepositAuth) + if (bRipple) { - // If depositPreauth is enabled, then an account that requires - // authorization has two ways to get an IOU Payment in: - // 1. If Account == Destination, or - // 2. If Account is deposit preauthorized by destination. - if (uDstAccountID != account_) + // Ripple payment with at least one intermediate step and uses + // transitive balances. + + if (depositPreauth && reqDepositAuth) { - if (!view().exists( - keylet::depositPreauth(uDstAccountID, account_))) - return tecNO_PERMISSION; + // If depositPreauth is enabled, then an account that requires + // authorization has two ways to get an IOU Payment in: + // 1. If Account == Destination, or + // 2. If Account is deposit preauthorized by destination. + if (uDstAccountID != account) + { + if (!ctx.view().exists( + keylet::depositPreauth(uDstAccountID, account))) + return tecNO_PERMISSION; + } } - } - path::RippleCalc::Input rcInput; - rcInput.partialPaymentAllowed = partialPaymentAllowed; - rcInput.defaultPathsAllowed = defaultPathsAllowed; - rcInput.limitQuality = limitQuality; - rcInput.isLedgerOpen = view().open(); + path::RippleCalc::Input rcInput; + rcInput.partialPaymentAllowed = partialPaymentAllowed; + rcInput.defaultPathsAllowed = defaultPathsAllowed; + rcInput.limitQuality = limitQuality; + rcInput.isLedgerOpen = ctx.view().open(); - path::RippleCalc::Output rc; - { - PaymentSandbox pv(&view()); - JLOG(j_.debug()) << "Entering RippleCalc in payment: " - << ctx_.tx.getTransactionID(); - rc = path::RippleCalc::rippleCalculate( - pv, - maxSourceAmount, - saDstAmount, - uDstAccountID, - account_, - ctx_.tx.getFieldPathSet(sfPaths), - ctx_.app.logs(), - &rcInput); - // VFALCO NOTE We might not need to apply, depending - // on the TER. But always applying *should* - // be safe. - pv.apply(ctx_.rawView()); - } + path::RippleCalc::Output rc; + { + PaymentSandbox pv(&ctx.view()); + JLOG(ctx.journal.debug()) << "Entering RippleCalc in payment: " + << ctx.tx.getTransactionID(); + rc = path::RippleCalc::rippleCalculate( + pv, + maxSourceAmount, + saDstAmount, + uDstAccountID, + account, + ctx.tx.getFieldPathSet(sfPaths), + ctx.app.logs(), + &rcInput); + // VFALCO NOTE We might not need to apply, depending + // on the TER. But always applying *should* + // be safe. + pv.apply(ctx.rawView()); + } - // TODO: is this right? If the amount is the correct amount, was - // the delivered amount previously set? - if (rc.result() == tesSUCCESS && rc.actualAmountOut != saDstAmount) - { - if (deliverMin && rc.actualAmountOut < *deliverMin) - rc.setResult(tecPATH_PARTIAL); - else - ctx_.deliver(rc.actualAmountOut); - } + // TODO: is this right? If the amount is the correct amount, was + // the delivered amount previously set? + if (rc.result() == tesSUCCESS && rc.actualAmountOut != saDstAmount) + { + if (deliverMin && rc.actualAmountOut < *deliverMin) + rc.setResult(tecPATH_PARTIAL); + else + ctx.deliver(rc.actualAmountOut); + } - auto terResult = rc.result(); + auto terResult = rc.result(); - // Because of its overhead, if RippleCalc - // fails with a retry code, claim a fee - // instead. Maybe the user will be more - // careful with their path spec next time. - if (isTerRetry(terResult)) - terResult = tecPATH_DRY; - return terResult; + // Because of its overhead, if RippleCalc + // fails with a retry code, claim a fee + // instead. Maybe the user will be more + // careful with their path spec next time. + if (isTerRetry(terResult)) + terResult = tecPATH_DRY; + return terResult; + } } - else if (saDstAmount.isMPT()) + else if constexpr ( + std::is_same_v && std::is_same_v) { if (auto const ter = - requireAuth(view(), saDstAmount.mptIssue(), account_); + requireAuth(ctx.view(), saDstAmount.issue(), account); ter != tesSUCCESS) return ter; if (auto const ter = - requireAuth(view(), saDstAmount.mptIssue(), uDstAccountID); + requireAuth(ctx.view(), saDstAmount.issue(), uDstAccountID); ter != tesSUCCESS) return ter; if (auto const ter = canTransfer( - view(), saDstAmount.mptIssue(), account_, uDstAccountID); + ctx.view(), saDstAmount.issue(), account, uDstAccountID); ter != tesSUCCESS) return ter; - auto const& mpt = saDstAmount.mptIssue(); + auto const& mpt = saDstAmount.issue(); auto const& issuer = mpt.getIssuer(); // If globally/individually locked then // can't send between holders // holder can send back to issuer // issuer can send to holder - if (account_ != issuer && uDstAccountID != issuer && - (isFrozen(view(), account_, mpt) || - isFrozen(view(), uDstAccountID, mpt))) + if (account != issuer && uDstAccountID != issuer && + (isFrozen(ctx.view(), account, mpt) || + isFrozen(ctx.view(), uDstAccountID, mpt))) return tecMPT_LOCKED; - PaymentSandbox pv(&view()); + PaymentSandbox pv(&ctx.view()); auto const res = - accountSendMPT(pv, account_, uDstAccountID, saDstAmount, j_); - pv.apply(ctx_.rawView()); + accountSend(pv, account, uDstAccountID, saDstAmount, ctx.journal); + pv.apply(ctx.rawView()); return res; } - assert(saDstAmount.native()); + if constexpr (std::is_same_v) + { + assert(saDstAmount.native()); - // Direct XRP payment. + // Direct XRP payment. - auto const sleSrc = view().peek(keylet::account(account_)); - if (!sleSrc) - return tefINTERNAL; + auto const sleSrc = ctx.view().peek(keylet::account(account)); + if (!sleSrc) + return tefINTERNAL; - // uOwnerCount is the number of entries in this ledger for this - // account that require a reserve. - auto const uOwnerCount = sleSrc->getFieldU32(sfOwnerCount); + // uOwnerCount is the number of entries in this ledger for this + // account that require a reserve. + auto const uOwnerCount = sleSrc->getFieldU32(sfOwnerCount); - // This is the total reserve in drops. - auto const reserve = view().fees().accountReserve(uOwnerCount); + // This is the total reserve in drops. + auto const reserve = ctx.view().fees().accountReserve(uOwnerCount); - // mPriorBalance is the balance on the sending account BEFORE the - // fees were charged. We want to make sure we have enough reserve - // to send. Allow final spend to use reserve for fee. - auto const mmm = std::max(reserve, ctx_.tx.getFieldAmount(sfFee).xrp()); + // mPriorBalance is the balance on the sending account BEFORE the + // fees were charged. We want to make sure we have enough reserve + // to send. Allow final spend to use reserve for fee. + auto const mmm = std::max( + reserve, get(ctx.tx.getFieldAmount(sfFee)).xrp()); - if (mPriorBalance < saDstAmount.xrp() + mmm) - { - // Vote no. However the transaction might succeed, if applied in - // a different order. - JLOG(j_.trace()) << "Delay transaction: Insufficient funds: " - << " " << to_string(mPriorBalance) << " / " - << to_string(saDstAmount.xrp() + mmm) << " (" - << to_string(reserve) << ")"; - - return tecUNFUNDED_PAYMENT; - } + if (priorBalance < saDstAmount.xrp() + mmm) + { + // Vote no. However the transaction might succeed, if applied in + // a different order. + JLOG(ctx.journal.trace()) + << "Delay transaction: Insufficient funds: " + << " " << to_string(priorBalance) << " / " + << to_string(saDstAmount.xrp() + mmm) << " (" + << to_string(reserve) << ")"; + + return tecUNFUNDED_PAYMENT; + } - // AMMs can never receive an XRP payment. - // Must use AMMDeposit transaction instead. - if (sleDst->isFieldPresent(sfAMMID)) - return tecNO_PERMISSION; + // AMMs can never receive an XRP payment. + // Must use AMMDeposit transaction instead. + if (sleDst->isFieldPresent(sfAMMID)) + return tecNO_PERMISSION; - // The source account does have enough money. Make sure the - // source account has authority to deposit to the destination. - if (reqDepositAuth) - { - // If depositPreauth is enabled, then an account that requires - // authorization has three ways to get an XRP Payment in: - // 1. If Account == Destination, or - // 2. If Account is deposit preauthorized by destination, or - // 3. If the destination's XRP balance is - // a. less than or equal to the base reserve and - // b. the deposit amount is less than or equal to the base reserve, - // then we allow the deposit. - // - // Rule 3 is designed to keep an account from getting wedged - // in an unusable state if it sets the lsfDepositAuth flag and - // then consumes all of its XRP. Without the rule if an - // account with lsfDepositAuth set spent all of its XRP, it - // would be unable to acquire more XRP required to pay fees. - // - // We choose the base reserve as our bound because it is - // a small number that seldom changes but is always sufficient - // to get the account un-wedged. - if (uDstAccountID != account_) + // The source account does have enough money. Make sure the + // source account has authority to deposit to the destination. + if (reqDepositAuth) { - if (!view().exists(keylet::depositPreauth(uDstAccountID, account_))) + // If depositPreauth is enabled, then an account that requires + // authorization has three ways to get an XRP Payment in: + // 1. If Account == Destination, or + // 2. If Account is deposit preauthorized by destination, or + // 3. If the destination's XRP balance is + // a. less than or equal to the base reserve and + // b. the deposit amount is less than or equal to the base + // reserve, + // then we allow the deposit. + // + // Rule 3 is designed to keep an account from getting wedged + // in an unusable state if it sets the lsfDepositAuth flag and + // then consumes all of its XRP. Without the rule if an + // account with lsfDepositAuth set spent all of its XRP, it + // would be unable to acquire more XRP required to pay fees. + // + // We choose the base reserve as our bound because it is + // a small number that seldom changes but is always sufficient + // to get the account un-wedged. + if (uDstAccountID != account) { - // Get the base reserve. - XRPAmount const dstReserve{view().fees().accountReserve(0)}; - - if (saDstAmount > dstReserve || - sleDst->getFieldAmount(sfBalance) > dstReserve) - return tecNO_PERMISSION; + if (!ctx.view().exists( + keylet::depositPreauth(uDstAccountID, account))) + { + // Get the base reserve. + XRPAmount const dstReserve{ + ctx.view().fees().accountReserve(0)}; + + if (saDstAmount > dstReserve || + get(sleDst->getFieldAmount(sfBalance)) > + dstReserve) + return tecNO_PERMISSION; + } } } - } - // Do the arithmetic for the transfer and make the ledger change. - sleSrc->setFieldAmount(sfBalance, mSourceBalance - saDstAmount); - sleDst->setFieldAmount( - sfBalance, sleDst->getFieldAmount(sfBalance) + saDstAmount); + // Do the arithmetic for the transfer and make the ledger change. + sleSrc->setFieldAmount(sfBalance, sourceBalance - saDstAmount); + sleDst->setFieldAmount( + sfBalance, + get(sleDst->getFieldAmount(sfBalance)) + saDstAmount); - // Re-arm the password change fee if we can and need to. - if ((sleDst->getFlags() & lsfPasswordSpent)) - sleDst->clearFlag(lsfPasswordSpent); + // Re-arm the password change fee if we can and need to. + if ((sleDst->getFlags() & lsfPasswordSpent)) + sleDst->clearFlag(lsfPasswordSpent); + } + else if constexpr (std::is_same_v) + { + Throw("Payment: expected native amount"); + } return tesSUCCESS; } +TxConsequences +Payment::makeTxConsequences(PreflightContext const& ctx) +{ + return std::visit( + [&](TDel const&, TMax const&) { + return makeTxConsequencesHelper(ctx); + }, + ctx.tx[sfAmount].getValue(), + ctx.tx[~sfSendMax].value_or(ctx.tx[sfAmount]).getValue()); +} + +NotTEC +Payment::preflight(PreflightContext const& ctx) +{ + return std::visit( + [&](TDel const&, TMax const&) { + return preflightHelper(ctx); + }, + ctx.tx[sfAmount].getValue(), + ctx.tx[~sfSendMax].value_or(ctx.tx[sfAmount]).getValue()); +} + +TER +Payment::preclaim(PreclaimContext const& ctx) +{ + return std::visit( + [&](TDel const&, TMax const&) { + return preclaimHelper(ctx, MaxPathSize, MaxPathLength); + }, + ctx.tx[sfAmount].getValue(), + ctx.tx[~sfSendMax].value_or(ctx.tx[sfAmount]).getValue()); +} + +TER +Payment::doApply() +{ + return std::visit( + [&](TDel const&, TMax const&) { + return applyHelper(ctx_, mPriorBalance, mSourceBalance); + }, + ctx_.tx[sfAmount].getValue(), + ctx_.tx[~sfSendMax].value_or(ctx_.tx[sfAmount]).getValue()); +} + } // namespace ripple diff --git a/src/xrpld/app/tx/detail/SetOracle.cpp b/src/xrpld/app/tx/detail/SetOracle.cpp index 055143cc6fd..4d8ddaa6e5b 100644 --- a/src/xrpld/app/tx/detail/SetOracle.cpp +++ b/src/xrpld/app/tx/detail/SetOracle.cpp @@ -174,7 +174,7 @@ SetOracle::preclaim(PreclaimContext const& ctx) auto const reserve = ctx.view.fees().accountReserve( sleSetter->getFieldU32(sfOwnerCount) + adjustReserve); - auto const& balance = sleSetter->getFieldAmount(sfBalance); + auto const& balance = get(sleSetter->getFieldAmount(sfBalance)); if (balance < reserve) return tecINSUFFICIENT_RESERVE; diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index 954fc6543f1..173ed35003d 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -34,6 +34,9 @@ SetTrust::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[~sfLimitAmount])) + return temMPT_INVALID_USAGE; + auto& tx = ctx.tx; auto& j = ctx.j; @@ -45,7 +48,8 @@ SetTrust::preflight(PreflightContext const& ctx) return temINVALID_FLAG; } - STAmount const saLimitAmount(tx.getFieldAmount(sfLimitAmount)); + STAmount const saLimitAmount( + get(tx.getFieldAmount(sfLimitAmount))); if (!isLegalNet(saLimitAmount)) return temBAD_AMOUNT; @@ -100,7 +104,7 @@ SetTrust::preclaim(PreclaimContext const& ctx) return tefNO_AUTH_REQUIRED; } - auto const saLimitAmount = ctx.tx[sfLimitAmount]; + auto const saLimitAmount = get(ctx.tx[sfLimitAmount]); auto const currency = saLimitAmount.getCurrency(); auto const uDstAccountID = saLimitAmount.getIssuer(); @@ -171,7 +175,7 @@ SetTrust::preclaim(PreclaimContext const& ctx) ctx.view.read({ltAMM, sleDst->getFieldH256(sfAMMID)})) { if (auto const lpTokens = - ammSle->getFieldAmount(sfLPTokenBalance); + get(ammSle->getFieldAmount(sfLPTokenBalance)); lpTokens == beast::zero) return tecAMM_EMPTY; else if (lpTokens.getCurrency() != saLimitAmount.getCurrency()) @@ -190,7 +194,8 @@ SetTrust::doApply() { TER terResult = tesSUCCESS; - STAmount const saLimitAmount(ctx_.tx.getFieldAmount(sfLimitAmount)); + STAmount const saLimitAmount( + get(ctx_.tx.getFieldAmount(sfLimitAmount))); bool const bQualityIn(ctx_.tx.isFieldPresent(sfQualityIn)); bool const bQualityOut(ctx_.tx.isFieldPresent(sfQualityOut)); @@ -294,7 +299,7 @@ SetTrust::doApply() // Balances // - saLowBalance = sleRippleState->getFieldAmount(sfBalance); + saLowBalance = get(sleRippleState->getFieldAmount(sfBalance)); saHighBalance = -saLowBalance; // @@ -304,10 +309,12 @@ SetTrust::doApply() sleRippleState->setFieldAmount( !bHigh ? sfLowLimit : sfHighLimit, saLimitAllow); - saLowLimit = - !bHigh ? saLimitAllow : sleRippleState->getFieldAmount(sfLowLimit); - saHighLimit = - bHigh ? saLimitAllow : sleRippleState->getFieldAmount(sfHighLimit); + saLowLimit = !bHigh + ? saLimitAllow + : get(sleRippleState->getFieldAmount(sfLowLimit)); + saHighLimit = bHigh + ? saLimitAllow + : get(sleRippleState->getFieldAmount(sfHighLimit)); // // Quality in diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 42e9f0677ab..4889e51d9c3 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -100,7 +100,7 @@ preflight1(PreflightContext const& ctx) } // No point in going any further if the transaction fee is malformed. - auto const fee = ctx.tx.getFieldAmount(sfFee); + auto const fee = get(ctx.tx.getFieldAmount(sfFee)); if (!fee.native() || fee.negative() || !isLegalAmount(fee.xrp())) { JLOG(ctx.j.debug()) << "preflight1: invalid fee"; @@ -195,7 +195,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) if (!ctx.tx[sfFee].native()) return temBAD_FEE; - auto const feePaid = ctx.tx[sfFee].xrp(); + auto const feePaid = get(ctx.tx[sfFee]).xrp(); if (!isLegalAmount(feePaid) || feePaid < beast::zero) return temBAD_FEE; @@ -222,7 +222,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) if (!sle) return terNO_ACCOUNT; - auto const balance = (*sle)[sfBalance].xrp(); + auto const balance = get((*sle)[sfBalance]).xrp(); if (balance < feePaid) { @@ -245,7 +245,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) TER Transactor::payFee() { - auto const feePaid = ctx_.tx[sfFee].xrp(); + auto const feePaid = get(ctx_.tx[sfFee]).xrp(); auto const sle = view().peek(keylet::account(account_)); if (!sle) @@ -457,7 +457,7 @@ Transactor::apply() if (sle) { - mPriorBalance = STAmount{(*sle)[sfBalance]}.xrp(); + mPriorBalance = STAmount{get((*sle)[sfBalance])}.xrp(); mSourceBalance = mPriorBalance; TER result = consumeSeqProxy(sle); @@ -799,7 +799,8 @@ Transactor::reset(XRPAmount fee) // is missing then we can't very well charge it a fee, can we? return {tefINTERNAL, beast::zero}; - auto const balance = txnAcct->getFieldAmount(sfBalance).xrp(); + auto const balance = + get(txnAcct->getFieldAmount(sfBalance)).xrp(); // balance should have already been checked in checkFee / preFlight. assert(balance != beast::zero && (!view().open() || balance >= fee)); @@ -872,7 +873,7 @@ Transactor::operator()() stream << "preclaim result: " << transToken(result); bool applied = isTesSuccess(result); - auto fee = ctx_.tx.getFieldAmount(sfFee).xrp(); + auto fee = get(ctx_.tx.getFieldAmount(sfFee)).xrp(); if (ctx_.size() > oversizeMetaDataCap) result = tecOVERSIZE; @@ -921,8 +922,8 @@ Transactor::operator()() assert(before && after); if (doOffers && before && after && (before->getType() == ltOFFER) && - (before->getFieldAmount(sfTakerPays) == - after->getFieldAmount(sfTakerPays))) + (get(before->getFieldAmount(sfTakerPays)) == + get(after->getFieldAmount(sfTakerPays)))) { // Removal of offer found or made unfunded removedOffers.push_back(index); diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/xrpld/app/tx/detail/XChainBridge.cpp index f5633903567..0e0ab3079aa 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/xrpld/app/tx/detail/XChainBridge.cpp @@ -446,7 +446,7 @@ transferHelper( auto const reserve = psb.fees().accountReserve(ownerCount); auto const availableBalance = [&]() -> STAmount { - STAmount const curBal = (*sleSrc)[sfBalance]; + STAmount const curBal = get((*sleSrc)[sfBalance]); // Checking that account == src and postFeeBalance == curBal is // not strictly nessisary, but helps protect against future // changes @@ -488,8 +488,10 @@ transferHelper( psb.insert(sleDst); } - (*sleSrc)[sfBalance] = (*sleSrc)[sfBalance] - amt; - (*sleDst)[sfBalance] = (*sleDst)[sfBalance] + amt; + (*sleSrc)[sfBalance] = + STEitherAmount{get((*sleSrc)[sfBalance]) - amt}; + (*sleDst)[sfBalance] = STEitherAmount{ + sfBalance, get((*sleDst)[sfBalance]) + amt}; psb.update(sleSrc); psb.update(sleDst); @@ -933,7 +935,7 @@ applyClaimAttestations( return ScopeResult{ newAttResult, - (*sleClaimID)[sfSignatureReward], + get((*sleClaimID)[sfSignatureReward]), (*sleClaimID)[sfAccount]}; }(); @@ -1058,7 +1060,7 @@ applyCreateAccountAttestations( return Unexpected(tecINTERNAL); // Check reserve - auto const balance = (*sleDoor)[sfBalance]; + auto const balance = get((*sleDoor)[sfBalance]); auto const reserve = psb.fees().accountReserve((*sleDoor)[sfOwnerCount] + 1); @@ -1214,6 +1216,10 @@ attestationPreflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && + (isMPT(ctx.tx[sfAmount]) || isMPT(ctx.tx[~sfSignatureReward]))) + return temMPT_NOT_SUPPORTED; + if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; @@ -1381,6 +1387,11 @@ XChainCreateBridge::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && + (isMPT(ctx.tx[sfSignatureReward]) || + isMPT(ctx.tx[~sfMinAccountCreateAmount]))) + return temMPT_NOT_SUPPORTED; + if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; @@ -1496,7 +1507,7 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx) if (!sleAcc) return terNO_ACCOUNT; - auto const balance = (*sleAcc)[sfBalance]; + auto const balance = get((*sleAcc)[sfBalance]); auto const reserve = ctx.view.fees().accountReserve((*sleAcc)[sfOwnerCount] + 1); @@ -1562,6 +1573,11 @@ BridgeModify::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && + (isMPT(ctx.tx[~sfSignatureReward]) || + isMPT(ctx.tx[~sfMinAccountCreateAmount]))) + return temMPT_NOT_SUPPORTED; + if (ctx.tx.getFlags() & tfBridgeModifyMask) return temINVALID_FLAG; @@ -1672,11 +1688,14 @@ XChainClaim::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) + return temMPT_NOT_SUPPORTED; + if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; STXChainBridge const bridgeSpec = ctx.tx[sfXChainBridge]; - auto const amount = ctx.tx[sfAmount]; + auto const amount = get(ctx.tx[sfAmount]); if (amount.signum() <= 0 || (amount.issue() != bridgeSpec.lockingChainIssue() && @@ -1693,7 +1712,7 @@ XChainClaim::preclaim(PreclaimContext const& ctx) { AccountID const account = ctx.tx[sfAccount]; STXChainBridge const bridgeSpec = ctx.tx[sfXChainBridge]; - STAmount const& thisChainAmount = ctx.tx[sfAmount]; + STAmount const& thisChainAmount = get(ctx.tx[sfAmount]); auto const claimID = ctx.tx[sfXChainClaimID]; auto const sleBridge = readBridge(ctx.view, bridgeSpec); @@ -1780,7 +1799,7 @@ XChainClaim::doApply() AccountID const account = ctx_.tx[sfAccount]; auto const dst = ctx_.tx[sfDestination]; STXChainBridge const bridgeSpec = ctx_.tx[sfXChainBridge]; - STAmount const& thisChainAmount = ctx_.tx[sfAmount]; + STAmount const& thisChainAmount = get(ctx_.tx[sfAmount]); auto const claimID = ctx_.tx[sfXChainClaimID]; auto const claimIDKeylet = keylet::xChainClaimID(bridgeSpec, claimID); @@ -1852,7 +1871,7 @@ XChainClaim::doApply() (*sleClaimID)[sfAccount], sendingAmount, srcChain, - (*sleClaimID)[sfSignatureReward], + get((*sleClaimID)[sfSignatureReward]), }; }(); @@ -1892,7 +1911,7 @@ TxConsequences XChainCommit::makeTxConsequences(PreflightContext const& ctx) { auto const maxSpend = [&] { - auto const amount = ctx.tx[sfAmount]; + auto const amount = get(ctx.tx[sfAmount]); if (amount.native() && amount.signum() > 0) return amount.xrp(); return XRPAmount{beast::zero}; @@ -1910,10 +1929,13 @@ XChainCommit::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) + return temMPT_NOT_SUPPORTED; + if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; - auto const amount = ctx.tx[sfAmount]; + auto const amount = get(ctx.tx[sfAmount]); auto const bridgeSpec = ctx.tx[sfXChainBridge]; if (amount.signum() <= 0 || !isLegalNet(amount)) @@ -1959,12 +1981,14 @@ XChainCommit::preclaim(PreclaimContext const& ctx) if (isLockingChain) { - if (bridgeSpec.lockingChainIssue() != ctx.tx[sfAmount].issue()) + if (bridgeSpec.lockingChainIssue() != + get(ctx.tx[sfAmount]).issue()) return tecXCHAIN_BAD_TRANSFER_ISSUE; } else { - if (bridgeSpec.issuingChainIssue() != ctx.tx[sfAmount].issue()) + if (bridgeSpec.issuingChainIssue() != + get(ctx.tx[sfAmount]).issue()) return tecXCHAIN_BAD_TRANSFER_ISSUE; } @@ -1977,7 +2001,7 @@ XChainCommit::doApply() PaymentSandbox psb(&ctx_.view()); auto const account = ctx_.tx[sfAccount]; - auto const amount = ctx_.tx[sfAmount]; + auto const amount = get(ctx_.tx[sfAmount]); auto const bridgeSpec = ctx_.tx[sfXChainBridge]; if (!psb.read(keylet::account(account))) @@ -2024,6 +2048,10 @@ XChainCreateClaimID::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && + isMPT(ctx.tx[sfSignatureReward])) + return temMPT_NOT_SUPPORTED; + if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; @@ -2061,7 +2089,7 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx) if (!sleAcc) return terNO_ACCOUNT; - auto const balance = (*sleAcc)[sfBalance]; + auto const balance = get((*sleAcc)[sfBalance]); auto const reserve = ctx.view.fees().accountReserve((*sleAcc)[sfOwnerCount] + 1); @@ -2179,15 +2207,19 @@ XChainCreateAccountCommit::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (ctx.rules.enabled(featureMPTokensV1) && + (isMPT(ctx.tx[sfAmount]) || isMPT(ctx.tx[sfSignatureReward]))) + return temMPT_NOT_SUPPORTED; + if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; - auto const amount = ctx.tx[sfAmount]; + auto const amount = get(ctx.tx[sfAmount]); if (amount.signum() <= 0 || !amount.native()) return temBAD_AMOUNT; - auto const reward = ctx.tx[sfSignatureReward]; + auto const reward = get(ctx.tx[sfSignatureReward]); if (reward.signum() < 0 || !reward.native()) return temBAD_AMOUNT; @@ -2201,8 +2233,8 @@ TER XChainCreateAccountCommit::preclaim(PreclaimContext const& ctx) { STXChainBridge const bridgeSpec = ctx.tx[sfXChainBridge]; - STAmount const amount = ctx.tx[sfAmount]; - STAmount const reward = ctx.tx[sfSignatureReward]; + STAmount const amount = get(ctx.tx[sfAmount]); + STEitherAmount const reward = ctx.tx[sfSignatureReward]; auto const sleBridge = readBridge(ctx.view, bridgeSpec); if (!sleBridge) @@ -2216,7 +2248,7 @@ XChainCreateAccountCommit::preclaim(PreclaimContext const& ctx) } std::optional const minCreateAmount = - (*sleBridge)[~sfMinAccountCreateAmount]; + get((*sleBridge)[~sfMinAccountCreateAmount]); if (!minCreateAmount) return tecXCHAIN_CREATE_ACCOUNT_DISABLED; @@ -2247,7 +2279,7 @@ XChainCreateAccountCommit::preclaim(PreclaimContext const& ctx) STXChainBridge::ChainType const dstChain = STXChainBridge::otherChain(srcChain); - if (bridgeSpec.issue(srcChain) != ctx.tx[sfAmount].issue()) + if (bridgeSpec.issue(srcChain) != get(ctx.tx[sfAmount]).issue()) return tecXCHAIN_BAD_TRANSFER_ISSUE; if (!isXRP(bridgeSpec.issue(dstChain))) @@ -2262,8 +2294,8 @@ XChainCreateAccountCommit::doApply() PaymentSandbox psb(&ctx_.view()); AccountID const account = ctx_.tx[sfAccount]; - STAmount const amount = ctx_.tx[sfAmount]; - STAmount const reward = ctx_.tx[sfSignatureReward]; + STAmount const amount = get(ctx_.tx[sfAmount]); + STAmount const reward = get(ctx_.tx[sfSignatureReward]); STXChainBridge const bridge = ctx_.tx[sfXChainBridge]; auto const sle = psb.peek(keylet::account(account)); diff --git a/src/xrpld/app/tx/detail/applySteps.cpp b/src/xrpld/app/tx/detail/applySteps.cpp index 48d19070eae..3552b7a3574 100644 --- a/src/xrpld/app/tx/detail/applySteps.cpp +++ b/src/xrpld/app/tx/detail/applySteps.cpp @@ -323,8 +323,9 @@ TxConsequences::TxConsequences(NotTEC pfresult) TxConsequences::TxConsequences(STTx const& tx) : isBlocker_(false) , fee_( - tx[sfFee].native() && !tx[sfFee].negative() ? tx[sfFee].xrp() - : beast::zero) + tx[sfFee].native() && !tx[sfFee].negative() + ? get(tx[sfFee]).xrp() + : beast::zero) , potentialSpend_(beast::zero) , seqProx_(tx.getSeqProxy()) , sequencesConsumed_(tx.getSeqProxy().isSeq() ? 1 : 0) diff --git a/src/xrpld/ledger/View.h b/src/xrpld/ledger/View.h index 9185365ff5a..3ee99713dcc 100644 --- a/src/xrpld/ledger/View.h +++ b/src/xrpld/ledger/View.h @@ -26,10 +26,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -146,7 +146,7 @@ accountHolds( FreezeHandling zeroIfFrozen, beast::Journal j); -[[nodiscard]] STAmount +[[nodiscard]] STMPTAmount accountHolds( ReadView const& view, AccountID const& account, @@ -235,7 +235,7 @@ forEachItemAfter( transferRate(ReadView const& view, AccountID const& issuer); [[nodiscard]] Rate -transferRateMPT(ReadView const& view, MPT const& id); +transferRate(ReadView const& view, MPT const& id); /** Returns `true` if the directory is empty @param key The key of the directory @@ -448,11 +448,11 @@ rippleCredit( beast::Journal j); [[nodiscard]] TER -rippleMPTCredit( +rippleCredit( ApplyView& view, AccountID const& uSenderID, AccountID const& uReceiverID, - STAmount saAmount, + STMPTAmount saAmount, beast::Journal j); [[nodiscard]] TER @@ -465,11 +465,11 @@ accountSend( WaiveTransferFee waiveFee = WaiveTransferFee::No); [[nodiscard]] TER -accountSendMPT( +accountSend( ApplyView& view, AccountID const& from, AccountID const& to, - const STAmount& saAmount, + const STMPTAmount& saAmount, beast::Journal j, WaiveTransferFee waiveFee = WaiveTransferFee::No); diff --git a/src/xrpld/ledger/detail/PaymentSandbox.cpp b/src/xrpld/ledger/detail/PaymentSandbox.cpp index d182d22b56c..a25907626b4 100644 --- a/src/xrpld/ledger/detail/PaymentSandbox.cpp +++ b/src/xrpld/ledger/detail/PaymentSandbox.cpp @@ -299,13 +299,13 @@ PaymentSandbox::balanceChanges(ReadView const& view) const case ltACCOUNT_ROOT: lowID = xrpAccount(); highID = (*before)[sfAccount]; - oldBalance = (*before)[sfBalance]; + oldBalance = get((*before)[sfBalance]); newBalance = oldBalance.zeroed(); break; case ltRIPPLE_STATE: lowID = (*before)[sfLowLimit].getIssuer(); highID = (*before)[sfHighLimit].getIssuer(); - oldBalance = (*before)[sfBalance]; + oldBalance = get((*before)[sfBalance]); newBalance = oldBalance.zeroed(); break; case ltOFFER: @@ -324,13 +324,13 @@ PaymentSandbox::balanceChanges(ReadView const& view) const case ltACCOUNT_ROOT: lowID = xrpAccount(); highID = (*after)[sfAccount]; - newBalance = (*after)[sfBalance]; + newBalance = get((*after)[sfBalance]); oldBalance = newBalance.zeroed(); break; case ltRIPPLE_STATE: lowID = (*after)[sfLowLimit].getIssuer(); highID = (*after)[sfHighLimit].getIssuer(); - newBalance = (*after)[sfBalance]; + newBalance = get((*after)[sfBalance]); oldBalance = newBalance.zeroed(); break; case ltOFFER: @@ -350,14 +350,14 @@ PaymentSandbox::balanceChanges(ReadView const& view) const case ltACCOUNT_ROOT: lowID = xrpAccount(); highID = (*after)[sfAccount]; - oldBalance = (*before)[sfBalance]; - newBalance = (*after)[sfBalance]; + oldBalance = get((*before)[sfBalance]); + newBalance = get((*after)[sfBalance]); break; case ltRIPPLE_STATE: lowID = (*after)[sfLowLimit].getIssuer(); highID = (*after)[sfHighLimit].getIssuer(); - oldBalance = (*before)[sfBalance]; - newBalance = (*after)[sfBalance]; + oldBalance = get((*before)[sfBalance]); + newBalance = get((*after)[sfBalance]); break; case ltOFFER: // TBD diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index 02519df0925..403294d775d 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -278,7 +278,7 @@ accountHolds( } else { - amount = sle->getFieldAmount(sfBalance); + amount = get(sle->getFieldAmount(sfBalance)); if (account > issuer) { // Put balance in account terms. @@ -305,7 +305,7 @@ accountHolds( view, account, issue.currency, issue.account, zeroIfFrozen, j); } -STAmount +STMPTAmount accountHolds( ReadView const& view, AccountID const& account, @@ -314,7 +314,7 @@ accountHolds( AuthHandling zeroIfUnauthorized, beast::Journal j) { - STAmount amount; + STMPTAmount amount; auto const sleMpt = view.read(keylet::mptoken(issue.mpt(), account)); if (!sleMpt) @@ -326,7 +326,7 @@ accountHolds( auto const amt = sleMpt->getFieldU64(sfMPTAmount); auto const locked = sleMpt->getFieldU64(sfLockedAmount); if (amt > locked) - amount = STAmount{issue, amt - locked}; + amount = STMPTAmount{issue, amt - locked}; // only if auth check is needed, as it needs to do an additional read // operation @@ -430,7 +430,7 @@ xrpLiquid( ? XRPAmount{0} : view.fees().accountReserve(ownerCount); - auto const fullBalance = sle->getFieldAmount(sfBalance); + auto const fullBalance = get(sle->getFieldAmount(sfBalance)); auto const balance = view.balanceHook(id, xrpAccount(), fullBalance); @@ -564,7 +564,7 @@ transferRate(ReadView const& view, AccountID const& issuer) } Rate -transferRateMPT(ReadView const& view, MPT const& id) +transferRate(ReadView const& view, MPT const& id) { auto const sle = view.read(keylet::mptIssuance(id)); @@ -900,8 +900,9 @@ trustCreate( bSetHigh ? sfHighLimit : sfLowLimit, saLimit); sleRippleState->setFieldAmount( bSetHigh ? sfLowLimit : sfHighLimit, - STAmount(Issue{ - saBalance.getCurrency(), bSetDst ? uSrcAccountID : uDstAccountID})); + STAmount( + {saBalance.getCurrency(), + bSetDst ? uSrcAccountID : uDstAccountID})); if (uQualityIn) sleRippleState->setFieldU32( @@ -1053,7 +1054,8 @@ rippleCredit( // If the line exists, modify it accordingly. if (auto const sleRippleState = view.peek(index)) { - STAmount saBalance = sleRippleState->getFieldAmount(sfBalance); + STAmount saBalance = + get(sleRippleState->getFieldAmount(sfBalance)); if (bSenderHigh) saBalance.negate(); // Put balance in sender terms. @@ -1088,8 +1090,8 @@ rippleCredit( view.read(keylet::account(uSenderID))->getFlags() & lsfDefaultRipple) && !(uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) && - !sleRippleState->getFieldAmount( - !bSenderHigh ? sfLowLimit : sfHighLimit) + !get(sleRippleState->getFieldAmount( + !bSenderHigh ? sfLowLimit : sfHighLimit)) // Sender trust limit is 0. && !sleRippleState->getFieldU32( !bSenderHigh ? sfLowQualityIn : sfHighQualityIn) @@ -1134,7 +1136,7 @@ rippleCredit( return tesSUCCESS; } - STAmount const saReceiverLimit(Issue{currency, uReceiverID}); + STAmount const saReceiverLimit({currency, uReceiverID}); STAmount saBalance{saAmount}; saBalance.setIssuer(noAccount()); @@ -1235,7 +1237,7 @@ accountSend( } else { - assert(saAmount >= beast::zero && !saAmount.isMPT()); + assert(saAmount >= beast::zero); } /* If we aren't sending anything or if the sender is the same as the @@ -1288,7 +1290,7 @@ accountSend( if (sender) { - if (sender->getFieldAmount(sfBalance) < saAmount) + if (get(sender->getFieldAmount(sfBalance)) < saAmount) { // VFALCO Its laborious to have to mutate the // TER based on params everywhere @@ -1297,7 +1299,8 @@ accountSend( } else { - auto const sndBal = sender->getFieldAmount(sfBalance); + auto const sndBal = + get(sender->getFieldAmount(sfBalance)); view.creditHook(uSenderID, xrpAccount(), saAmount, sndBal); // Decrement XRP balance. @@ -1309,7 +1312,7 @@ accountSend( if (tesSUCCESS == terResult && receiver) { // Increment XRP balance. - auto const rcvBal = receiver->getFieldAmount(sfBalance); + auto const rcvBal = get(receiver->getFieldAmount(sfBalance)); receiver->setFieldAmount(sfBalance, rcvBal + saAmount); view.creditHook(xrpAccount(), uReceiverID, saAmount, -rcvBal); @@ -1336,25 +1339,25 @@ accountSend( } static TER -rippleSendMPT( +rippleSend( ApplyView& view, AccountID const& uSenderID, AccountID const& uReceiverID, - STAmount const& saAmount, - STAmount& saActual, + STMPTAmount const& saAmount, + STMPTAmount& saActual, beast::Journal j, WaiveTransferFee waiveFee) { assert(uSenderID != uReceiverID); - // Safe to get MPT since rippleSendMPT is only called by accountSendMPT + // Safe to get MPT since rippleSend is only called by accountSend auto const issuer = saAmount.getIssuer(); if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount()) { // Direct send: redeeming IOUs and/or sending own IOUs. auto const ter = - rippleMPTCredit(view, uSenderID, uReceiverID, saAmount, j); + rippleCredit(view, uSenderID, uReceiverID, saAmount, j); if (view.rules().enabled(featureDeletableAccounts) && ter != tesSUCCESS) return ter; saActual = saAmount; @@ -1362,15 +1365,11 @@ rippleSendMPT( } // Sending 3rd party MPTs: transit. - if (auto const sle = - view.read(keylet::mptIssuance(saAmount.mptIssue().mpt()))) + if (auto const sle = view.read(keylet::mptIssuance(saAmount.issue().mpt()))) { saActual = (waiveFee == WaiveTransferFee::Yes) ? saAmount - : multiply( - saAmount, - transferRateMPT( - view, static_cast(saAmount.mptIssue().mpt()))); + : multiply(saAmount, transferRate(view, saAmount.issue().mpt())); JLOG(j.debug()) << "rippleSend> " << to_string(uSenderID) << " - > " << to_string(uReceiverID) @@ -1378,26 +1377,26 @@ rippleSendMPT( << " cost=" << saActual.getFullText(); if (auto const terResult = - rippleMPTCredit(view, issuer, uReceiverID, saAmount, j); + rippleCredit(view, issuer, uReceiverID, saAmount, j); terResult != tesSUCCESS) return terResult; else - return rippleMPTCredit(view, uSenderID, issuer, saActual, j); + return rippleCredit(view, uSenderID, issuer, saActual, j); } return tecINTERNAL; } TER -accountSendMPT( +accountSend( ApplyView& view, AccountID const& uSenderID, AccountID const& uReceiverID, - STAmount const& saAmount, + STMPTAmount const& saAmount, beast::Journal j, WaiveTransferFee waiveFee) { - assert(saAmount >= beast::zero && saAmount.isMPT()); + assert(saAmount >= beast::zero); /* If we aren't sending anything or if the sender is the same as the * receiver then we don't need to do anything. @@ -1405,9 +1404,10 @@ accountSendMPT( if (!saAmount || (uSenderID == uReceiverID)) return tesSUCCESS; - STAmount saActual{saAmount.asset()}; + // STAmount saActual{saAmount.asset()}; + STMPTAmount saActual{}; - return rippleSendMPT( + return rippleSend( view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee); } @@ -1440,7 +1440,8 @@ updateTrustLine( flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != static_cast(sle->getFlags() & lsfDefaultRipple) && !(flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) && - !state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit) + !get( + state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)) // Sender trust limit is 0. && !state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) // Sender quality in is 0. @@ -1489,7 +1490,8 @@ issueIOU( if (auto state = view.peek(index)) { - STAmount final_balance = state->getFieldAmount(sfBalance); + STAmount final_balance = + get(state->getFieldAmount(sfBalance)); if (bSenderHigh) final_balance.negate(); // Put balance in sender terms. @@ -1532,7 +1534,7 @@ issueIOU( // NIKB TODO: The limit uses the receiver's account as the issuer and // this is unnecessarily inefficient as copying which could be avoided // is now required. Consider available options. - STAmount const limit(Issue{issue.currency, account}); + STAmount const limit({issue.currency, account}); STAmount final_balance = amount; final_balance.setIssuer(noAccount()); @@ -1584,7 +1586,8 @@ redeemIOU( if (auto state = view.peek(keylet::line(account, issue.account, issue.currency))) { - STAmount final_balance = state->getFieldAmount(sfBalance); + STAmount final_balance = + get(state->getFieldAmount(sfBalance)); if (bSenderHigh) final_balance.negate(); // Put balance in sender terms. @@ -1651,7 +1654,7 @@ transferXRP( JLOG(j.trace()) << "transferXRP: " << to_string(from) << " -> " << to_string(to) << ") : " << amount.getFullText(); - if (sender->getFieldAmount(sfBalance) < amount) + if (get(sender->getFieldAmount(sfBalance)) < amount) { // VFALCO Its unfortunate we have to keep // mutating these TER everywhere @@ -1662,11 +1665,11 @@ transferXRP( // Decrement XRP balance. sender->setFieldAmount( - sfBalance, sender->getFieldAmount(sfBalance) - amount); + sfBalance, get(sender->getFieldAmount(sfBalance)) - amount); view.update(sender); receiver->setFieldAmount( - sfBalance, receiver->getFieldAmount(sfBalance) + amount); + sfBalance, get(receiver->getFieldAmount(sfBalance)) + amount); view.update(receiver); return tesSUCCESS; @@ -1851,14 +1854,14 @@ deleteAMMTrustLine( } TER -rippleMPTCredit( +rippleCredit( ApplyView& view, AccountID const& uSenderID, AccountID const& uReceiverID, - STAmount saAmount, + STMPTAmount saAmount, beast::Journal j) { - auto const mptID = keylet::mptIssuance(saAmount.mptIssue().mpt()); + auto const mptID = keylet::mptIssuance(saAmount.issue().mpt()); auto const issuer = saAmount.getIssuer(); if (uSenderID == issuer) { @@ -1866,7 +1869,7 @@ rippleMPTCredit( { sle->setFieldU64( sfOutstandingAmount, - sle->getFieldU64(sfOutstandingAmount) + saAmount.mpt().mpt()); + sle->getFieldU64(sfOutstandingAmount) + saAmount.value()); if (sle->getFieldU64(sfOutstandingAmount) > (*sle)[~sfMaximumAmount].value_or(maxMPTokenAmount)) @@ -1883,7 +1886,7 @@ rippleMPTCredit( if (auto sle = view.peek(mptokenID)) { auto const amt = sle->getFieldU64(sfMPTAmount); - auto const pay = saAmount.mpt().mpt(); + auto const pay = saAmount.value(); if (amt >= pay) { if (amt == pay) @@ -1902,7 +1905,7 @@ rippleMPTCredit( if (auto sle = view.peek(mptID)) { auto const outstanding = sle->getFieldU64(sfOutstandingAmount); - auto const redeem = saAmount.mpt().mpt(); + auto const redeem = saAmount.value(); if (outstanding >= redeem) { sle->setFieldU64(sfOutstandingAmount, outstanding - redeem); @@ -1920,8 +1923,7 @@ rippleMPTCredit( if (auto sle = view.peek(mptokenID)) { sle->setFieldU64( - sfMPTAmount, - sle->getFieldU64(sfMPTAmount) + saAmount.mpt().mpt()); + sfMPTAmount, sle->getFieldU64(sfMPTAmount) + saAmount.value()); view.update(sle); } } diff --git a/src/xrpld/rpc/BookChanges.h b/src/xrpld/rpc/BookChanges.h index 7d7978d3fe2..e7b9b026bc5 100644 --- a/src/xrpld/rpc/BookChanges.h +++ b/src/xrpld/rpc/BookChanges.h @@ -107,10 +107,12 @@ computeBookChanges(std::shared_ptr const& lpAccepted) // compute the difference in gets and pays actually // affected onto the offer - STAmount deltaGets = finalFields.getFieldAmount(sfTakerGets) - - previousFields.getFieldAmount(sfTakerGets); - STAmount deltaPays = finalFields.getFieldAmount(sfTakerPays) - - previousFields.getFieldAmount(sfTakerPays); + STAmount deltaGets = + get(finalFields.getFieldAmount(sfTakerGets)) - + get(previousFields.getFieldAmount(sfTakerGets)); + STAmount deltaPays = + get(finalFields.getFieldAmount(sfTakerPays)) - + get(previousFields.getFieldAmount(sfTakerPays)); std::string g{to_string(deltaGets.issue())}; std::string p{to_string(deltaPays.issue())}; diff --git a/src/xrpld/rpc/DeliveredAmount.h b/src/xrpld/rpc/DeliveredAmount.h index 2ebadd38752..63e11b707a1 100644 --- a/src/xrpld/rpc/DeliveredAmount.h +++ b/src/xrpld/rpc/DeliveredAmount.h @@ -72,7 +72,7 @@ insertDeliveredAmount( std::shared_ptr const&, TxMeta const&); -std::optional +std::optional getDeliveredAmount( RPC::Context const& context, std::shared_ptr const& serializedTx, diff --git a/src/xrpld/rpc/detail/DeliveredAmount.cpp b/src/xrpld/rpc/detail/DeliveredAmount.cpp index 7874997e24f..4396fb291c7 100644 --- a/src/xrpld/rpc/detail/DeliveredAmount.cpp +++ b/src/xrpld/rpc/detail/DeliveredAmount.cpp @@ -44,7 +44,7 @@ namespace RPC { std::optional */ template -std::optional +std::optional getDeliveredAmount( GetLedgerIndex const& getLedgerIndex, GetCloseTime const& getCloseTime, @@ -174,7 +174,7 @@ insertDeliveredAmount( } template -static std::optional +static std::optional getDeliveredAmount( RPC::Context const& context, std::shared_ptr const& serializedTx, @@ -195,7 +195,7 @@ getDeliveredAmount( return {}; } -std::optional +std::optional getDeliveredAmount( RPC::Context const& context, std::shared_ptr const& serializedTx, diff --git a/src/xrpld/rpc/detail/TransactionSign.cpp b/src/xrpld/rpc/detail/TransactionSign.cpp index 1fee84c683b..c5077a01a13 100644 --- a/src/xrpld/rpc/detail/TransactionSign.cpp +++ b/src/xrpld/rpc/detail/TransactionSign.cpp @@ -1202,7 +1202,7 @@ transactionSubmitMultiSigned( return rpcError(rpcSIGNING_MALFORMED); // The Fee field must be in XRP and greater than zero. - auto const fee = stpTrans->getFieldAmount(sfFee); + auto const fee = get(stpTrans->getFieldAmount(sfFee)); if (!isLegalNet(fee)) { diff --git a/src/xrpld/rpc/handlers/AMMInfo.cpp b/src/xrpld/rpc/handlers/AMMInfo.cpp index aad8faea213..3b4f03a7384 100644 --- a/src/xrpld/rpc/handlers/AMMInfo.cpp +++ b/src/xrpld/rpc/handlers/AMMInfo.cpp @@ -189,7 +189,7 @@ doAMMInfo(RPC::JsonContext& context) context.j); auto const lptAMMBalance = accountID ? ammLPHolds(*ledger, *amm, *accountID, context.j) - : (*amm)[sfLPTokenBalance]; + : get((*amm)[sfLPTokenBalance]); Json::Value ammResult; asset1Balance.setJson(ammResult[jss::amount]); @@ -226,7 +226,7 @@ doAMMInfo(RPC::JsonContext& context) auctionSlot); auction[jss::time_interval] = timeSlot ? *timeSlot : AUCTION_SLOT_TIME_INTERVALS; - auctionSlot[sfPrice].setJson(auction[jss::price]); + get(auctionSlot[sfPrice]).setJson(auction[jss::price]); auction[jss::discounted_fee] = auctionSlot[sfDiscountedFee]; auction[jss::account] = to_string(auctionSlot.getAccountID(sfAccount)); diff --git a/src/xrpld/rpc/handlers/AccountOffers.cpp b/src/xrpld/rpc/handlers/AccountOffers.cpp index 1a3b5227ed8..75d02cdf59c 100644 --- a/src/xrpld/rpc/handlers/AccountOffers.cpp +++ b/src/xrpld/rpc/handlers/AccountOffers.cpp @@ -37,8 +37,10 @@ appendOfferJson(std::shared_ptr const& offer, Json::Value& offers) STAmount dirRate = amountFromQuality(getQuality(offer->getFieldH256(sfBookDirectory))); Json::Value& obj(offers.append(Json::objectValue)); - offer->getFieldAmount(sfTakerPays).setJson(obj[jss::taker_pays]); - offer->getFieldAmount(sfTakerGets).setJson(obj[jss::taker_gets]); + get(offer->getFieldAmount(sfTakerPays)) + .setJson(obj[jss::taker_pays]); + get(offer->getFieldAmount(sfTakerGets)) + .setJson(obj[jss::taker_gets]); obj[jss::seq] = offer->getFieldU32(sfSequence); obj[jss::flags] = offer->getFieldU32(sfFlags); obj[jss::quality] = dirRate.getText(); diff --git a/src/xrpld/rpc/handlers/NoRippleCheck.cpp b/src/xrpld/rpc/handlers/NoRippleCheck.cpp index e4012468434..ba533498494 100644 --- a/src/xrpld/rpc/handlers/NoRippleCheck.cpp +++ b/src/xrpld/rpc/handlers/NoRippleCheck.cpp @@ -169,18 +169,20 @@ doNoRippleCheck(RPC::JsonContext& context) if (needFix) { AccountID peer = - ownedItem - ->getFieldAmount(bLow ? sfHighLimit : sfLowLimit) + get(ownedItem->getFieldAmount( + bLow ? sfHighLimit : sfLowLimit)) .getIssuer(); - STAmount peerLimit = ownedItem->getFieldAmount( - bLow ? sfHighLimit : sfLowLimit); + STAmount peerLimit = + get(ownedItem->getFieldAmount( + bLow ? sfHighLimit : sfLowLimit)); problem += to_string(peerLimit.getCurrency()); problem += " line to "; problem += to_string(peerLimit.getIssuer()); problems.append(problem); - STAmount limitAmount(ownedItem->getFieldAmount( - bLow ? sfLowLimit : sfHighLimit)); + STAmount limitAmount( + get(ownedItem->getFieldAmount( + bLow ? sfLowLimit : sfHighLimit))); limitAmount.setIssuer(peer); Json::Value& tx = jvTransactions.append(Json::objectValue);