From 01ac2aa441a0068f52c0f3719c34cc1a90cf6323 Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Mon, 1 Jul 2024 15:01:24 -0400 Subject: [PATCH] Use STEitherAmount as a variant of STAmount and MPTAmount --- Builds/levelization/results/loops.txt | 2 +- include/xrpl/basics/MPTAmount.h | 188 ++---- include/xrpl/basics/Number.h | 2 +- include/xrpl/protocol/Asset.h | 138 ---- include/xrpl/protocol/Issue.h | 8 +- include/xrpl/protocol/MPTIssue.h | 18 + include/xrpl/protocol/Rate.h | 4 + include/xrpl/protocol/SField.h | 4 +- include/xrpl/protocol/SOTemplate.h | 20 +- include/xrpl/protocol/STAmount.h | 322 +--------- include/xrpl/protocol/STEitherAmount.h | 319 ++++++++++ include/xrpl/protocol/STMPTAmount.h | 123 ++++ include/xrpl/protocol/STObject.h | 11 +- include/xrpl/protocol/STTx.h | 4 +- include/xrpl/protocol/TER.h | 3 +- include/xrpl/protocol/TxMeta.h | 6 +- include/xrpl/protocol/UintTypes.h | 6 - src/libxrpl/basics/MPTAmount.cpp | 85 +++ src/libxrpl/protocol/Asset.cpp | 118 ---- src/libxrpl/protocol/Issue.cpp | 4 - src/libxrpl/protocol/MPTIssue.cpp | 17 + src/libxrpl/protocol/Quality.cpp | 10 +- src/libxrpl/protocol/Rate2.cpp | 17 +- src/libxrpl/protocol/STAmount.cpp | 557 ++++++---------- src/libxrpl/protocol/STEitherAmount.cpp | 442 +++++++++++++ src/libxrpl/protocol/STMPTAmount.cpp | 233 +++++++ src/libxrpl/protocol/STObject.cpp | 22 +- src/libxrpl/protocol/STParsedJSON.cpp | 4 +- src/libxrpl/protocol/STTx.cpp | 32 +- src/libxrpl/protocol/STVar.cpp | 4 +- src/libxrpl/protocol/TER.cpp | 1 + src/libxrpl/protocol/TxFormats.cpp | 6 +- src/libxrpl/protocol/TxMeta.cpp | 2 +- src/libxrpl/protocol/XChainAttestations.cpp | 26 +- src/test/app/AMMExtended_test.cpp | 8 +- src/test/app/Check_test.cpp | 26 +- src/test/app/Flow_test.cpp | 5 +- src/test/app/MPToken_test.cpp | 3 +- src/test/app/Offer_test.cpp | 67 +- src/test/app/Path_test.cpp | 15 +- src/test/app/PayChan_test.cpp | 47 +- src/test/app/PayStrand_test.cpp | 6 +- src/test/app/ReducedOffer_test.cpp | 34 +- src/test/app/Regression_test.cpp | 6 +- src/test/app/SetAuth_test.cpp | 4 +- src/test/app/TheoreticalQuality_test.cpp | 5 +- src/test/app/TxQ_test.cpp | 17 +- src/test/app/XChain_test.cpp | 4 +- src/test/jtx/PathSet.h | 10 +- src/test/jtx/amount.h | 88 +-- src/test/jtx/impl/AMM.cpp | 9 +- src/test/jtx/impl/AMMTest.cpp | 9 +- src/test/jtx/impl/Env.cpp | 4 +- src/test/jtx/impl/TestHelpers.cpp | 16 +- src/test/jtx/impl/amount.cpp | 45 +- src/test/jtx/impl/balance.cpp | 5 +- src/test/jtx/impl/envconfig.cpp | 4 +- src/test/jtx/impl/mpt.cpp | 37 +- src/test/jtx/impl/offer.cpp | 4 +- src/test/jtx/impl/paths.cpp | 3 +- src/test/jtx/impl/trust.cpp | 2 +- src/test/jtx/impl/xchain_bridge.cpp | 6 +- src/test/jtx/mpt.h | 20 +- src/test/jtx/offer.h | 4 +- src/test/jtx/trust.h | 2 +- src/test/ledger/Invariants_test.cpp | 2 +- src/test/nodestore/DatabaseShard_test.cpp | 4 +- src/test/protocol/Quality_test.cpp | 2 +- src/test/protocol/STObject_test.cpp | 4 +- src/test/protocol/STTx_test.cpp | 32 +- src/test/rpc/AccountSet_test.cpp | 4 +- src/xrpld/app/ledger/AcceptedLedgerTx.cpp | 2 +- src/xrpld/app/ledger/Ledger.cpp | 7 +- src/xrpld/app/ledger/OrderBookDB.cpp | 6 +- src/xrpld/app/ledger/detail/LedgerToJson.cpp | 2 +- src/xrpld/app/misc/FeeVoteImpl.cpp | 2 +- src/xrpld/app/misc/NetworkOPs.cpp | 23 +- src/xrpld/app/misc/detail/AMMUtils.cpp | 13 +- src/xrpld/app/misc/detail/TxQ.cpp | 4 +- src/xrpld/app/paths/Credit.cpp | 10 +- src/xrpld/app/paths/PathRequest.cpp | 2 +- src/xrpld/app/paths/Pathfinder.cpp | 7 +- src/xrpld/app/paths/TrustLine.cpp | 6 +- src/xrpld/app/tx/detail/AMMBid.cpp | 22 +- src/xrpld/app/tx/detail/AMMCreate.cpp | 16 +- src/xrpld/app/tx/detail/AMMDelete.cpp | 2 +- src/xrpld/app/tx/detail/AMMDeposit.cpp | 33 +- src/xrpld/app/tx/detail/AMMVote.cpp | 2 +- src/xrpld/app/tx/detail/AMMWithdraw.cpp | 38 +- src/xrpld/app/tx/detail/CashCheck.cpp | 37 +- src/xrpld/app/tx/detail/Change.cpp | 11 +- src/xrpld/app/tx/detail/Clawback.cpp | 152 +++-- src/xrpld/app/tx/detail/CreateCheck.cpp | 7 +- src/xrpld/app/tx/detail/CreateOffer.cpp | 18 +- src/xrpld/app/tx/detail/DID.cpp | 2 +- src/xrpld/app/tx/detail/DeleteAccount.cpp | 6 +- src/xrpld/app/tx/detail/Escrow.cpp | 17 +- src/xrpld/app/tx/detail/InvariantCheck.cpp | 43 +- .../app/tx/detail/NFTokenAcceptOffer.cpp | 36 +- .../app/tx/detail/NFTokenCreateOffer.cpp | 9 +- src/xrpld/app/tx/detail/NFTokenMint.cpp | 9 +- src/xrpld/app/tx/detail/NFTokenUtils.cpp | 2 +- src/xrpld/app/tx/detail/Offer.h | 8 +- src/xrpld/app/tx/detail/PayChan.cpp | 54 +- src/xrpld/app/tx/detail/Payment.cpp | 596 +++++++++++------- src/xrpld/app/tx/detail/SetOracle.cpp | 2 +- src/xrpld/app/tx/detail/SetTrust.cpp | 25 +- src/xrpld/app/tx/detail/Transactor.cpp | 19 +- src/xrpld/app/tx/detail/XChainBridge.cpp | 80 ++- src/xrpld/app/tx/detail/applySteps.cpp | 5 +- src/xrpld/ledger/View.h | 14 +- src/xrpld/ledger/detail/PaymentSandbox.cpp | 16 +- src/xrpld/ledger/detail/View.cpp | 100 +-- src/xrpld/rpc/BookChanges.h | 10 +- src/xrpld/rpc/DeliveredAmount.h | 2 +- src/xrpld/rpc/detail/DeliveredAmount.cpp | 6 +- src/xrpld/rpc/detail/TransactionSign.cpp | 2 +- src/xrpld/rpc/handlers/AMMInfo.cpp | 4 +- src/xrpld/rpc/handlers/AccountOffers.cpp | 6 +- src/xrpld/rpc/handlers/NoRippleCheck.cpp | 14 +- src/xrpld/rpc/handlers/Submit.cpp | 3 +- 121 files changed, 2851 insertions(+), 1982 deletions(-) delete mode 100644 include/xrpl/protocol/Asset.h create mode 100644 include/xrpl/protocol/STEitherAmount.h create mode 100644 include/xrpl/protocol/STMPTAmount.h create mode 100644 src/libxrpl/basics/MPTAmount.cpp delete mode 100644 src/libxrpl/protocol/Asset.cpp create mode 100644 src/libxrpl/protocol/STEitherAmount.cpp create mode 100644 src/libxrpl/protocol/STMPTAmount.cpp 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..e9cebcf21bf 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -26,10 +26,11 @@ #include #include #include +#include #include -#include #include #include +#include #include #include #include @@ -119,7 +120,9 @@ class STObject : public STBase, public CountedObject reserve(std::size_t n); void - applyTemplate(const SOTemplate& type); + applyTemplate( + const SOTemplate& type, + std::optional const& rules = std::nullopt); void applyTemplateFromSField(SField const&); @@ -235,7 +238,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 +373,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/STTx.h b/include/xrpl/protocol/STTx.h index 08b9a1bad10..8ff61349687 100644 --- a/include/xrpl/protocol/STTx.h +++ b/include/xrpl/protocol/STTx.h @@ -66,7 +66,9 @@ class STTx final : public STObject, public CountedObject STTx& operator=(STTx const& other) = delete; - explicit STTx(SerialIter& sit); + explicit STTx( + SerialIter& sit, + std::optional const& rules = std::nullopt); explicit STTx(SerialIter&& sit); explicit STTx(STObject&& object); 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..39a5e9ec26f 100644 --- a/src/libxrpl/protocol/STObject.cpp +++ b/src/libxrpl/protocol/STObject.cpp @@ -138,7 +138,9 @@ STObject::set(const SOTemplate& type) } void -STObject::applyTemplate(const SOTemplate& type) +STObject::applyTemplate( + const SOTemplate& type, + std::optional const& rules) { auto throwFieldErr = [](std::string const& field, char const* description) { std::stringstream ss; @@ -165,6 +167,16 @@ 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 (rules && rules->enabled(featureMPTokensV1)) + throwFieldErr( + e.sField().fieldName, "doesn't support MPT"); + } + } v.emplace_back(std::move(*iter)); v_.erase(iter); } @@ -630,11 +642,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 +760,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..f33fc178148 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -64,7 +64,8 @@ STTx::STTx(STObject&& object) : STObject(std::move(object)) tid_ = getHash(HashPrefix::transactionID); } -STTx::STTx(SerialIter& sit) : STObject(sfTransaction) +STTx::STTx(SerialIter& sit, std::optional const& rules) + : STObject(sfTransaction) { int length = sit.getBytesLeft(); @@ -76,7 +77,7 @@ STTx::STTx(SerialIter& sit) : STObject(sfTransaction) tx_type_ = safe_cast(getFieldU16(sfTransactionType)); - applyTemplate(getTxFormat(tx_type_)->getSOTemplate()); // May throw + applyTemplate(getTxFormat(tx_type_)->getSOTemplate(), rules); // May throw tid_ = getHash(HashPrefix::transactionID); } @@ -143,14 +144,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); } } @@ -534,23 +530,15 @@ isMemoOkay(STObject const& st, std::string& reason) return true; } -// Ensure all account fields are 160-bits and that MPT amount is only passed -// to Payment or Clawback tx (until MPT is supported in more tx) +// Ensure all account fields are 160-bits static bool -isAccountAndMPTFieldOkay(STObject const& st) +isAccountFieldOkay(STObject const& st) { - auto const txType = st[~sfTransactionType]; - static std::unordered_set const mptAmountTx{ttPAYMENT, ttCLAWBACK}; - bool const isMPTAmountAllowed = txType && - (mptAmountTx.find(safe_cast(*txType)) != mptAmountTx.end()); for (int i = 0; i < st.getCount(); ++i) { 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; } return true; @@ -562,9 +550,9 @@ passesLocalChecks(STObject const& st, std::string& reason) if (!isMemoOkay(st, reason)) return false; - if (!isAccountAndMPTFieldOkay(st)) + if (!isAccountFieldOkay(st)) { - reason = "An account or MPT field is invalid."; + reason = "An account field is invalid."; return false; } 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..305b76aefc9 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}); 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..934bd8050c0 100644 --- a/src/test/protocol/STTx_test.cpp +++ b/src/test/protocol/STTx_test.cpp @@ -1344,22 +1344,22 @@ class STTx_test : public beast::unit_test::suite 0x73, 0x00, 0x81, 0x14, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe, 0xf3, 0xe7, 0xe5, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x1e, 0x00, 0x6f, 0x00, 0x00, 0x20, 0x1f, 0x03, 0xf6, 0x00, - 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x59, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x12, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x1e, 0x00, 0x4f, 0x00, 0x00, 0x20, 0x1f, 0x03, 0xf6, 0x00, - 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x24, 0x59, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x54, 0x72, 0x61, 0x6e, 0x00, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xe5, 0xfe, 0xf3, 0xe7, 0xe5, 0x65, 0x24, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x6f, 0x00, 0x00, 0x20, 0xf6, - 0x00, 0x00, 0x03, 0x1f, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, - 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x73, 0x00, 0x81, 0x14, 0x00, - 0x10, 0x00, 0x73, 0x00, 0x81, 0x14, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe}; + 0x20, 0x1e, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x20, 0x1f, 0x03, 0xf6, + 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x59, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x12, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x1e, 0x00, 0x4f, 0x00, 0x00, 0x20, 0x1f, 0x03, 0xf6, + 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x24, 0x59, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x54, 0x72, 0x61, 0x6e, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe, 0xf3, 0xe7, 0xe5, 0x65, 0x24, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x6f, 0x00, 0x00, 0x20, + 0xf6, 0x00, 0x00, 0x03, 0x1f, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x35, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x73, 0x00, 0x81, 0x14, + 0x00, 0x10, 0x00, 0x73, 0x00, 0x81, 0x14, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe}; // Construct an STObject with 11 levels of object nesting so the // maximum nesting level exception is thrown. 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); diff --git a/src/xrpld/rpc/handlers/Submit.cpp b/src/xrpld/rpc/handlers/Submit.cpp index 73fdc3822c2..1a237636dd7 100644 --- a/src/xrpld/rpc/handlers/Submit.cpp +++ b/src/xrpld/rpc/handlers/Submit.cpp @@ -87,7 +87,8 @@ doSubmit(RPC::JsonContext& context) try { - stpTrans = std::make_shared(std::ref(sitTrans)); + stpTrans = std::make_shared( + std::ref(sitTrans), context.ledgerMaster.getValidatedRules()); } catch (std::exception& e) {