From c2a7281c0e004169558ec7e09ba93f6bd35c13ff Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Mon, 1 Jul 2024 15:01:24 -0400 Subject: [PATCH 01/11] 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 | 33 +- include/xrpl/protocol/STAmount.h | 322 +--------- include/xrpl/protocol/STEitherAmount.h | 318 ++++++++++ include/xrpl/protocol/STMPTAmount.h | 123 ++++ include/xrpl/protocol/STObject.h | 7 +- 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 | 16 +- src/libxrpl/protocol/STParsedJSON.cpp | 4 +- src/libxrpl/protocol/STTx.cpp | 27 +- src/libxrpl/protocol/STVar.cpp | 4 +- src/libxrpl/protocol/TER.cpp | 1 + src/libxrpl/protocol/TxFormats.cpp | 4 +- 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 | 33 +- 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 | 598 +++++++++++------- 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 +- 119 files changed, 2869 insertions(+), 1984 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..95f11f37658 100644 --- a/include/xrpl/protocol/SOTemplate.h +++ b/include/xrpl/protocol/SOTemplate.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -39,6 +40,8 @@ enum SOEStyle { // constructed with STObject::makeInnerObject() }; +enum SOESupportMPT : bool { soeMPTYes = true, soeMPTNo = false }; + //------------------------------------------------------------------------------ /** An element in a SOTemplate. */ @@ -47,10 +50,11 @@ 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) +private: + void + init(SField const& fieldName) const { if (!sField_.get().isUseful()) { @@ -62,6 +66,23 @@ class SOElement } } +public: + SOElement(SField const& fieldName, SOEStyle style) + : sField_(fieldName) + , style_(style) + , supportMpt_(SOESupportMPT::soeMPTNo) + { + init(fieldName); + } + SOElement( + TypedField const& fieldName, + SOEStyle style, + SOESupportMPT supportMpt = SOESupportMPT::soeMPTNo) + : sField_(fieldName), style_(style), supportMpt_(supportMpt) + { + init(fieldName); + } + SField const& sField() const { @@ -73,6 +94,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..a3ab1c4b2bc --- /dev/null +++ b/include/xrpl/protocol/STEitherAmount.h @@ -0,0 +1,318 @@ +//------------------------------------------------------------------------------ +/* + 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 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 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..8a6a651463e 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 @@ -235,7 +236,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 +371,7 @@ class STObject : public STBase, public CountedObject setAccountID(SField const& field, AccountID const&); void - setFieldAmount(SField const& field, STAmount const&); + setFieldAmount(SField const& field, STEitherAmount const&); void setFieldIssue(SField const& field, STIssue const&); void diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index d0f9275d69c..6d666a3fb55 100644 --- a/include/xrpl/protocol/TER.h +++ b/include/xrpl/protocol/TER.h @@ -140,7 +140,8 @@ enum TEMcodes : TERUnderlyingType { temARRAY_EMPTY, temARRAY_TOO_LARGE, - temMPT_NOT_SUPPORTED + temMPT_NOT_SUPPORTED, + temMPT_INVALID_USAGE }; //------------------------------------------------------------------------------ diff --git a/include/xrpl/protocol/TxMeta.h b/include/xrpl/protocol/TxMeta.h index 7932a4c55a3..92114230da7 100644 --- a/include/xrpl/protocol/TxMeta.h +++ b/include/xrpl/protocol/TxMeta.h @@ -108,12 +108,12 @@ class TxMeta } void - setDeliveredAmount(STAmount const& delivered) + setDeliveredAmount(STEitherAmount const& delivered) { mDelivered = delivered; } - STAmount + STEitherAmount getDeliveredAmount() const { assert(hasDeliveredAmount()); @@ -132,7 +132,7 @@ class TxMeta std::uint32_t mIndex; int mResult; - std::optional mDelivered; + std::optional mDelivered; STArray mNodes; }; diff --git a/include/xrpl/protocol/UintTypes.h b/include/xrpl/protocol/UintTypes.h index b574319b734..8ccca8743ae 100644 --- a/include/xrpl/protocol/UintTypes.h +++ b/include/xrpl/protocol/UintTypes.h @@ -46,12 +46,6 @@ class NodeIDTag explicit NodeIDTag() = default; }; -class MPTTag -{ -public: - explicit MPTTag() = default; -}; - } // namespace detail /** Directory is an index into the directory of offer books. diff --git a/src/libxrpl/basics/MPTAmount.cpp b/src/libxrpl/basics/MPTAmount.cpp new file mode 100644 index 00000000000..6c9a50e4730 --- /dev/null +++ b/src/libxrpl/basics/MPTAmount.cpp @@ -0,0 +1,85 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include + +namespace ripple { + +MPTAmount& +MPTAmount::operator+=(MPTAmount const& other) +{ + value_ += other.value(); + return *this; +} + +MPTAmount& +MPTAmount::operator-=(MPTAmount const& other) +{ + value_ -= other.value(); + return *this; +} + +MPTAmount +MPTAmount::operator-() const +{ + return MPTAmount{-value_}; +} + +bool +MPTAmount::operator==(MPTAmount const& other) const +{ + return value_ == other.value_; +} + +bool +MPTAmount::operator==(value_type other) const +{ + return value_ == other; +} + +bool +MPTAmount::operator<(MPTAmount const& other) const +{ + return value_ < other.value_; +} + +Json::Value +MPTAmount::jsonClipped() const +{ + static_assert( + std::is_signed_v && std::is_integral_v, + "Expected MPTAmount to be a signed integral type"); + + constexpr auto min = std::numeric_limits::min(); + constexpr auto max = std::numeric_limits::max(); + + if (value_ < min) + return min; + if (value_ > max) + return max; + return static_cast(value_); +} + +MPTAmount +MPTAmount::minPositiveAmount() +{ + return MPTAmount{1}; +} + +} // namespace ripple diff --git a/src/libxrpl/protocol/Asset.cpp b/src/libxrpl/protocol/Asset.cpp deleted file mode 100644 index d3bd567a1fd..00000000000 --- a/src/libxrpl/protocol/Asset.cpp +++ /dev/null @@ -1,118 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2024 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -namespace ripple { - -Asset::Asset(Issue const& issue) : asset_(issue) -{ -} - -Asset::Asset(MPTIssue const& mpt) : asset_(mpt) -{ -} - -Asset::Asset(MPT const& mpt) : asset_(MPTIssue{mpt}) -{ -} - -Asset::operator Issue() const -{ - return issue(); -} - -Asset::operator MPTIssue() const -{ - return mptIssue(); -} - -AccountID const& -Asset::getIssuer() const -{ - if (isIssue()) - return issue().getIssuer(); - return mptIssue().getIssuer(); -} - -Asset::Asset(uint192 const& u) -{ - std::uint32_t sequence; - AccountID account; - memcpy(&sequence, u.data(), sizeof(sequence)); - sequence = boost::endian::big_to_native(sequence); - memcpy(account.data(), u.data() + sizeof(sequence), sizeof(AccountID)); - asset_ = std::make_pair(sequence, account); -} - -Issue& -Asset::issue() -{ - if (!std::holds_alternative(asset_)) - Throw("Asset is not Issue"); - return std::get(asset_); -} - -MPTIssue& -Asset::mptIssue() -{ - if (!std::holds_alternative(asset_)) - Throw("Asset is not MPT"); - return std::get(asset_); -} - -std::string -Asset::getText() const -{ - if (isIssue()) - return issue().getText(); - return to_string(mptIssue().getMptID()); -} - -std::string -to_string(Asset const& asset) -{ - if (asset.isIssue()) - return to_string(asset.issue()); - return to_string(asset.mptIssue().getMptID()); -} - -std::string -to_string(MPTIssue const& mptIssue) -{ - return to_string(mptIssue.getMptID()); -} - -std::string -to_string(MPT const& mpt) -{ - return to_string(getMptID(mpt.second, mpt.first)); -} - -bool -validJSONAsset(Json::Value const& jv) -{ - return (jv.isMember(jss::currency) && !jv.isMember(jss::mpt_issuance_id)) || - (!jv.isMember(jss::currency) && !jv.isMember(jss::issuer) && - jv.isMember(jss::mpt_issuance_id)); -} - -} // namespace ripple \ No newline at end of file diff --git a/src/libxrpl/protocol/Issue.cpp b/src/libxrpl/protocol/Issue.cpp index 8aff535fde7..d860e7062bc 100644 --- a/src/libxrpl/protocol/Issue.cpp +++ b/src/libxrpl/protocol/Issue.cpp @@ -26,10 +26,6 @@ namespace ripple { -Issue::Issue(Currency const& c, AccountID const& a) : currency(c), account(a) -{ -} - AccountID const& Issue::getIssuer() const { diff --git a/src/libxrpl/protocol/MPTIssue.cpp b/src/libxrpl/protocol/MPTIssue.cpp index fd3b6bbfa90..28806dce3aa 100644 --- a/src/libxrpl/protocol/MPTIssue.cpp +++ b/src/libxrpl/protocol/MPTIssue.cpp @@ -26,6 +26,11 @@ MPTIssue::MPTIssue(MPT const& mpt) : mpt_(mpt) { } +MPTIssue::MPTIssue(uint192 const& id) +{ + mpt_ = getMPT(id); +} + AccountID const& MPTIssue::getIssuer() const { @@ -50,4 +55,16 @@ MPTIssue::getMptID() const return ripple::getMptID(mpt_.second, mpt_.first); } +MPT +getMPT(uint192 const& id) +{ + std::uint32_t sequence; + AccountID account; + + memcpy(&sequence, id.data(), sizeof(sequence)); + sequence = boost::endian::big_to_native(sequence); + memcpy(account.data(), id.data() + sizeof(sequence), sizeof(AccountID)); + return std::make_pair(sequence, account); +} + } // namespace ripple diff --git a/src/libxrpl/protocol/Quality.cpp b/src/libxrpl/protocol/Quality.cpp index c6464eba9d2..38b641328b0 100644 --- a/src/libxrpl/protocol/Quality.cpp +++ b/src/libxrpl/protocol/Quality.cpp @@ -65,7 +65,7 @@ Quality::operator--(int) } template + *DivRoundFunc)(STAmount const&, STAmount const&, Issue const&, bool)> static Amounts ceil_in_impl( Amounts const& amount, @@ -77,7 +77,7 @@ ceil_in_impl( { Amounts result( limit, - DivRoundFunc(limit, quality.rate(), amount.out.asset(), roundUp)); + DivRoundFunc(limit, quality.rate(), amount.out.issue(), roundUp)); // Clamp out if (result.out > amount.out) result.out = amount.out; @@ -104,7 +104,7 @@ Quality::ceil_in_strict( } template + *MulRoundFunc)(STAmount const&, STAmount const&, Issue const&, bool)> static Amounts ceil_out_impl( Amounts const& amount, @@ -115,7 +115,7 @@ ceil_out_impl( if (amount.out > limit) { Amounts result( - MulRoundFunc(limit, quality.rate(), amount.in.asset(), roundUp), + MulRoundFunc(limit, quality.rate(), amount.in.issue(), roundUp), limit); // Clamp in if (result.in > amount.in) @@ -151,7 +151,7 @@ composed_quality(Quality const& lhs, Quality const& rhs) STAmount const rhs_rate(rhs.rate()); assert(rhs_rate != beast::zero); - STAmount const rate(mulRound(lhs_rate, rhs_rate, lhs_rate.asset(), true)); + STAmount const rate(mulRound(lhs_rate, rhs_rate, lhs_rate.issue(), true)); std::uint64_t const stored_exponent(rate.exponent() + 100); std::uint64_t const stored_mantissa(rate.mantissa()); diff --git a/src/libxrpl/protocol/Rate2.cpp b/src/libxrpl/protocol/Rate2.cpp index 01a3e7deca5..ecf6de2cb77 100644 --- a/src/libxrpl/protocol/Rate2.cpp +++ b/src/libxrpl/protocol/Rate2.cpp @@ -51,7 +51,20 @@ multiply(STAmount const& amount, Rate const& rate) if (rate == parityRate) return amount; - return multiply(amount, detail::as_amount(rate), amount.asset()); + return multiply(amount, detail::as_amount(rate), amount.issue()); +} + +STMPTAmount +multiply(STMPTAmount const& amount, Rate const& rate) +{ + assert(rate.value != 0); + + if (rate == parityRate) + return amount; + + return STMPTAmount{ + amount.issue(), + static_cast(amount.value() * Number{rate.value, -9})}; } STAmount @@ -62,7 +75,7 @@ multiplyRound(STAmount const& amount, Rate const& rate, bool roundUp) if (rate == parityRate) return amount; - return mulRound(amount, detail::as_amount(rate), amount.asset(), roundUp); + return mulRound(amount, detail::as_amount(rate), amount.issue(), roundUp); } STAmount diff --git a/src/libxrpl/protocol/STAmount.cpp b/src/libxrpl/protocol/STAmount.cpp index 355518347a9..c4cf6d9c151 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -79,56 +78,25 @@ getSNValue(STAmount const& amount) return ret; } -static std::int64_t -getMPTValue(STAmount const& amount) -{ - if (!amount.isMPT()) - Throw("amount is not native!"); - - auto ret = static_cast(amount.mantissa()); - - assert(static_cast(ret) == amount.mantissa()); - - if (amount.negative()) - ret = -ret; - - return ret; -} - static bool areComparable(STAmount const& v1, STAmount const& v2) { - return (v1.isMPT() && v2.isMPT() && - v1.asset().mptIssue() == v2.asset().mptIssue()) || - (v1.isIssue() && v1.native() == v2.native() && - v1.issue().currency == v2.issue().currency); + return v1.native() == v2.native() && + v1.issue().currency == v2.issue().currency; } -STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name) +STAmount::STAmount(std::uint64_t value, SerialIter& sit, SField const& name) + : STBase(name) { - // TODO MPT make sure backward compatible - - std::uint64_t value = sit.get64(); - // TODO must fix serialization for IOU, it incorrectly sets cMPToken - bool isMPT = (value & cMPToken) && !(value & cIssuedCurrency); - - // native or MPT - if ((value & cIssuedCurrency) == 0 || isMPT) + // native + if ((value & cNotNative) == 0) { - if (isMPT) - { - // mAsset = std::make_pair( - // sit.get32(), static_cast(sit.get160())); - mAsset = sit.get192(); - } - else - mAsset = xrpIssue(); // positive - if ((value & cPositive) != 0) + if ((value & cPosNative) != 0) { - mValue = value & cValueMask; + mValue = value & ~cPosNative; mOffset = 0; - mIsNative = !isMPT; + mIsNative = true; mIsNegative = false; return; } @@ -137,9 +105,9 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name) if (value == 0) Throw("negative zero is not canonical"); - mValue = value & cValueMask; + mValue = value; mOffset = 0; - mIsNative = !isMPT; + mIsNative = true; mIsNegative = true; return; } @@ -171,7 +139,7 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name) Throw("invalid currency value"); } - mAsset = issue; + mIssue = issue; mValue = value; mOffset = offset; mIsNegative = isNegative; @@ -182,22 +150,75 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name) if (offset != 512) Throw("invalid currency value"); - mAsset = issue; + mIssue = issue; mValue = 0; mOffset = 0; mIsNegative = false; canonicalize(); } +STAmount::STAmount(ripple::SerialIter& sit, const ripple::SField& name) + : STAmount(sit.get64(), sit, name) +{ +} + +STAmount::STAmount( + SField const& name, + Issue const& issue, + mantissa_type mantissa, + exponent_type exponent, + bool native, + bool negative, + unchecked) + : STBase(name) + , mIssue(issue) + , mValue(mantissa) + , mOffset(exponent) + , mIsNative(native) + , mIsNegative(negative) +{ +} + +STAmount::STAmount( + Issue const& issue, + mantissa_type mantissa, + exponent_type exponent, + bool native, + bool negative, + unchecked) + : mIssue(issue) + , mValue(mantissa) + , mOffset(exponent) + , mIsNative(native) + , mIsNegative(negative) +{ +} + +STAmount::STAmount( + SField const& name, + Issue const& issue, + mantissa_type mantissa, + exponent_type exponent, + bool native, + bool negative) + : STBase(name) + , mIssue(issue) + , mValue(mantissa) + , mOffset(exponent) + , mIsNative(native) + , mIsNegative(negative) +{ + canonicalize(); +} + STAmount::STAmount(SField const& name, std::int64_t mantissa) - : STBase(name), mAsset(xrpIssue()), mOffset(0), mIsNative(true) + : STBase(name), mOffset(0), mIsNative(true) { set(mantissa); } STAmount::STAmount(SField const& name, std::uint64_t mantissa, bool negative) : STBase(name) - , mAsset(xrpIssue()) , mValue(mantissa) , mOffset(0) , mIsNative(true) @@ -206,9 +227,25 @@ STAmount::STAmount(SField const& name, std::uint64_t mantissa, bool negative) assert(mValue <= std::numeric_limits::max()); } +STAmount::STAmount( + SField const& name, + Issue const& issue, + std::uint64_t mantissa, + int exponent, + bool negative) + : STBase(name) + , mIssue(issue) + , mValue(mantissa) + , mOffset(exponent) + , mIsNegative(negative) +{ + assert(mValue <= std::numeric_limits::max()); + canonicalize(); +} + STAmount::STAmount(SField const& name, STAmount const& from) : STBase(name) - , mAsset(from.mAsset) + , mIssue(from.mIssue) , mValue(from.mValue) , mOffset(from.mOffset) , mIsNegative(from.mIsNegative) @@ -220,8 +257,7 @@ STAmount::STAmount(SField const& name, STAmount const& from) //------------------------------------------------------------------------------ STAmount::STAmount(std::uint64_t mantissa, bool negative) - : mAsset(xrpIssue()) - , mValue(mantissa) + : mValue(mantissa) , mOffset(0) , mIsNative(true) , mIsNegative(mantissa != 0 && negative) @@ -229,11 +265,54 @@ STAmount::STAmount(std::uint64_t mantissa, bool negative) assert(mValue <= std::numeric_limits::max()); } -STAmount::STAmount(XRPAmount const& amount) - : mAsset(xrpIssue()) - , mOffset(0) - , mIsNative(true) +STAmount::STAmount( + Issue const& issue, + std::uint64_t mantissa, + int exponent, + bool negative) + : mIssue(issue), mValue(mantissa), mOffset(exponent), mIsNegative(negative) +{ + canonicalize(); +} + +STAmount::STAmount(Issue const& issue, std::int64_t mantissa, int exponent) + : mIssue(issue), mOffset(exponent) +{ + set(mantissa); + canonicalize(); +} + +STAmount::STAmount( + Issue const& issue, + std::uint32_t mantissa, + int exponent, + bool negative) + : STAmount(issue, safe_cast(mantissa), exponent, negative) +{ +} + +STAmount::STAmount(Issue const& issue, int mantissa, int exponent) + : STAmount(issue, safe_cast(mantissa), exponent) +{ +} + +// Legacy support for new-style amounts +STAmount::STAmount(IOUAmount const& amount, Issue const& issue) + : mIssue(issue) + , mOffset(amount.exponent()) + , mIsNative(false) , mIsNegative(amount < beast::zero) +{ + if (mIsNegative) + mValue = static_cast(-amount.mantissa()); + else + mValue = static_cast(amount.mantissa()); + + canonicalize(); +} + +STAmount::STAmount(XRPAmount const& amount) + : mOffset(0), mIsNative(true), mIsNegative(amount < beast::zero) { if (mIsNegative) mValue = unsafe_cast(-amount.drops()); @@ -284,7 +363,7 @@ STAmount::xrp() const IOUAmount STAmount::iou() const { - if (mIsNative || isMPT()) + if (mIsNative) Throw("Cannot return native STAmount as IOUAmount"); auto mantissa = static_cast(mValue); @@ -296,20 +375,6 @@ STAmount::iou() const return {mantissa, exponent}; } -MPTAmount -STAmount::mpt() const -{ - if (!isMPT()) - Throw("Cannot return STAmount as MPTAmount"); - - auto value = static_cast(mValue); - - if (mIsNegative) - value = -value; - - return MPTAmount{value}; -} - STAmount& STAmount::operator=(IOUAmount const& iou) { @@ -357,17 +422,14 @@ operator+(STAmount const& v1, STAmount const& v2) // Result must be in terms of v1 currency and issuer. return { v1.getFName(), - v1.asset(), + v1.issue(), v2.mantissa(), v2.exponent(), v2.negative()}; } - // TODO if (v1.native()) return {v1.getFName(), getSNValue(v1) + getSNValue(v2)}; - if (v1.isMPT()) - return {v1.mAsset, v1.mpt().mpt() + v2.mpt().mpt()}; if (getSTNumberSwitchover()) { @@ -404,18 +466,18 @@ operator+(STAmount const& v1, STAmount const& v2) std::int64_t fv = vv1 + vv2; if ((fv >= -10) && (fv <= 10)) - return {v1.getFName(), v1.asset()}; + return {v1.getFName(), v1.issue()}; if (fv >= 0) return STAmount{ v1.getFName(), - v1.asset(), + v1.issue(), static_cast(fv), ov1, false}; return STAmount{ - v1.getFName(), v1.asset(), static_cast(-fv), ov1, true}; + v1.getFName(), v1.issue(), static_cast(-fv), ov1, true}; } STAmount @@ -431,7 +493,7 @@ std::uint64_t const STAmount::uRateOne = getRate(STAmount(1), STAmount(1)); void STAmount::setIssue(Issue const& issue) { - mAsset = issue; + mIssue = issue; mIsNative = isXRP(*this); } @@ -476,13 +538,8 @@ STAmount::setJson(Json::Value& elem) const // It is an error for currency or issuer not to be specified for valid // json. elem[jss::value] = getText(); - if (mAsset.isMPT()) - elem[jss::mpt_issuance_id] = to_string(mAsset.mptIssue()); - else - { - elem[jss::currency] = to_string(mAsset.issue().currency); - elem[jss::issuer] = to_string(mAsset.issue().account); - } + elem[jss::currency] = to_string(mIssue.currency); + elem[jss::issuer] = to_string(mIssue.account); } else { @@ -508,7 +565,7 @@ STAmount::getFullText() const std::string ret; ret.reserve(64); - ret = getText() + "/" + mAsset.getText(); + ret = getText() + "/" + mIssue.getText(); return ret; } @@ -528,7 +585,7 @@ STAmount::getText() const bool const scientific( (mOffset != 0) && ((mOffset < -25) || (mOffset > -5))); - if (mIsNative || mAsset.isMPT() || scientific) + if (mIsNative || scientific) { ret.append(raw_value); @@ -607,44 +664,31 @@ Json::Value STAmount::getJson(JsonOptions) const void STAmount::add(Serializer& s) const { - // TODO MPT make sure backward compatible if (mIsNative) { assert(mOffset == 0); if (!mIsNegative) - s.add64(mValue | cPositive); + s.add64(mValue | cPosNative); else s.add64(mValue); } else { - if (mAsset.isMPT()) - { - if (mIsNegative) - s.add64(mValue | cMPToken); - else - s.add64(mValue | cMPToken | cPositive); - auto const& mptIssue = mAsset.mptIssue(); - s.addBitString(mptIssue.getMptID()); - } - else - { - if (*this == beast::zero) - s.add64(cIssuedCurrency); - else if (mIsNegative) // 512 = not native - s.add64( - mValue | - (static_cast(mOffset + 512 + 97) - << (64 - 10))); - else // 256 = positive - s.add64( - mValue | - (static_cast(mOffset + 512 + 256 + 97) - << (64 - 10))); - s.addBitString(mAsset.issue().currency); - s.addBitString(mAsset.issue().account); - } + if (*this == beast::zero) + s.add64(cNotNative); + else if (mIsNegative) // 512 = not native + s.add64( + mValue | + (static_cast(mOffset + 512 + 97) << (64 - 10))); + else // 256 = positive + s.add64( + mValue | + (static_cast(mOffset + 512 + 256 + 97) + << (64 - 10))); + + s.addBitString(mIssue.currency); + s.addBitString(mIssue.account); } } @@ -682,10 +726,10 @@ STAmount::isDefault() const void STAmount::canonicalize() { - if (isXRP(*this) || mAsset.isMPT()) + if (isXRP(*this)) { // native currency amounts should always have an offset of zero - mIsNative = isXRP(*this); + mIsNative = true; // log(2^64,10) ~ 19.2 if (mValue == 0 || mOffset <= -20) @@ -708,18 +752,9 @@ STAmount::canonicalize() { Number num( mIsNegative ? -mValue : mValue, mOffset, Number::unchecked{}); - if (mIsNative) - { - XRPAmount xrp{num}; - mIsNegative = xrp.drops() < 0; - mValue = mIsNegative ? -xrp.drops() : xrp.drops(); - } - else - { - MPTAmount c{num}; - mIsNegative = c.mpt() < 0; - mValue = mIsNegative ? -c.mpt() : c.mpt(); - } + XRPAmount xrp{num}; + mIsNegative = xrp.drops() < 0; + mValue = mIsNegative ? -xrp.drops() : xrp.drops(); mOffset = 0; } else @@ -828,7 +863,7 @@ amountFromQuality(std::uint64_t rate) } STAmount -amountFromString(Asset const& asset, std::string const& amount) +amountFromString(Issue const& issue, std::string const& amount) { static boost::regex const reNumber( "^" // the beginning of the string @@ -861,7 +896,7 @@ amountFromString(Asset const& asset, std::string const& amount) bool negative = (match[1].matched && (match[1] == "-")); // Can't specify XRP using fractional representation - if (isXRP(asset) && match[3].matched) + if (isXRP(issue) && match[3].matched) Throw("XRP must be specified in integral drops."); std::uint64_t mantissa; @@ -889,152 +924,7 @@ amountFromString(Asset const& asset, std::string const& amount) exponent += beast::lexicalCastThrow(std::string(match[7])); } - return {asset, mantissa, exponent, negative}; -} - -STAmount -amountFromJson(SField const& name, Json::Value const& v) -{ - STAmount::mantissa_type mantissa = 0; - STAmount::exponent_type exponent = 0; - bool negative = false; - Asset asset; - - Json::Value value; - Json::Value currencyOrMPTID; - Json::Value issuer; - bool isMPT = false; - - if (v.isNull()) - { - Throw( - "XRP may not be specified with a null Json value"); - } - else if (v.isObject()) - { - if (!validJSONAsset(v)) - Throw("Invalid Asset's Json specification"); - - value = v[jss::value]; - if (v.isMember(jss::mpt_issuance_id)) - { - isMPT = true; - currencyOrMPTID = v[jss::mpt_issuance_id]; - } - else - { - currencyOrMPTID = v[jss::currency]; - issuer = v[jss::issuer]; - } - } - else if (v.isArray()) - { - value = v.get(Json::UInt(0), 0); - currencyOrMPTID = v.get(Json::UInt(1), Json::nullValue); - issuer = v.get(Json::UInt(2), Json::nullValue); - } - else if (v.isString()) - { - std::string val = v.asString(); - std::vector elements; - boost::split(elements, val, boost::is_any_of("\t\n\r ,/")); - - if (elements.size() > 3) - Throw("invalid amount string"); - - value = elements[0]; - - if (elements.size() > 1) - currencyOrMPTID = elements[1]; - - if (elements.size() > 2) - issuer = elements[2]; - } - else - { - value = v; - } - - bool const native = !currencyOrMPTID.isString() || - currencyOrMPTID.asString().empty() || - (currencyOrMPTID.asString() == systemCurrencyCode()); - - if (native) - { - if (v.isObjectOrNull()) - Throw("XRP may not be specified as an object"); - asset = xrpIssue(); - } - else - { - if (isMPT) - { - // sequence (32 bits) + account (160 bits) - uint192 u; - if (!u.parseHex(currencyOrMPTID.asString())) - Throw("invalid MPTokenIssuanceID"); - asset = u; - } - else - { - Issue issue; - if (!to_currency(issue.currency, currencyOrMPTID.asString())) - Throw("invalid currency"); - if (!issuer.isString() || - !to_issuer(issue.account, issuer.asString())) - Throw("invalid issuer"); - if (isXRP(issue)) - Throw("invalid issuer"); - asset = issue; - } - } - - if (value.isInt()) - { - if (value.asInt() >= 0) - { - mantissa = value.asInt(); - } - else - { - mantissa = -value.asInt(); - negative = true; - } - } - else if (value.isUInt()) - { - mantissa = v.asUInt(); - } - else if (value.isString()) - { - auto const ret = amountFromString(asset, value.asString()); - - mantissa = ret.mantissa(); - exponent = ret.exponent(); - negative = ret.negative(); - } - else - { - Throw("invalid amount type"); - } - - return {name, asset, mantissa, exponent, native, negative}; -} - -bool -amountFromJsonNoThrow(STAmount& result, Json::Value const& jvSource) -{ - try - { - result = amountFromJson(sfGeneric, jvSource); - return true; - } - catch (const std::exception& e) - { - JLOG(debugLog().warn()) - << "amountFromJsonNoThrow: caught: " << e.what(); - } - return false; + return STAmount{issue, mantissa, exponent, negative}; } //------------------------------------------------------------------------------ @@ -1092,7 +982,7 @@ operator-(STAmount const& value) return value; return STAmount( value.getFName(), - value.asset(), + value.issue(), value.mantissa(), value.exponent(), value.native(), @@ -1154,20 +1044,20 @@ muldiv_round( } STAmount -divide(STAmount const& num, STAmount const& den, Asset const& asset) +divide(STAmount const& num, STAmount const& den, Issue const& issue) { if (den == beast::zero) Throw("division by zero"); if (num == beast::zero) - return {asset}; + return {issue}; std::uint64_t numVal = num.mantissa(); std::uint64_t denVal = den.mantissa(); int numOffset = num.exponent(); int denOffset = den.exponent(); - if (num.native() || num.isMPT()) + if (num.native()) { while (numVal < STAmount::cMinValue) { @@ -1177,7 +1067,7 @@ divide(STAmount const& num, STAmount const& den, Asset const& asset) } } - if (den.native() || den.isMPT()) + if (den.native()) { while (denVal < STAmount::cMinValue) { @@ -1192,19 +1082,19 @@ divide(STAmount const& num, STAmount const& den, Asset const& asset) // 10^32 to 10^33) followed by a division, so the result // is in the range of 10^16 to 10^15. return STAmount( - asset, + issue, muldiv(numVal, tenTo17, denVal) + 5, numOffset - denOffset - 17, num.negative() != den.negative()); } STAmount -multiply(STAmount const& v1, STAmount const& v2, Asset const& asset) +multiply(STAmount const& v1, STAmount const& v2, Issue const& issue) { if (v1 == beast::zero || v2 == beast::zero) - return STAmount(asset); + return STAmount(issue); - if (v1.native() && v2.native() && isXRP(asset)) + if (v1.native() && v2.native() && isXRP(issue)) { std::uint64_t const minV = getSNValue(v1) < getSNValue(v2) ? getSNValue(v1) : getSNValue(v2); @@ -1219,34 +1109,16 @@ multiply(STAmount const& v1, STAmount const& v2, Asset const& asset) return STAmount(v1.getFName(), minV * maxV); } - if (v1.isMPT() && v2.isMPT() && asset.isMPT()) - { - std::uint64_t const minV = getMPTValue(v1) < getMPTValue(v2) - ? getMPTValue(v1) - : getMPTValue(v2); - std::uint64_t const maxV = getMPTValue(v1) < getMPTValue(v2) - ? getMPTValue(v2) - : getMPTValue(v1); - - if (minV > 3000000000ull) // sqrt(cMaxNative) - Throw("Asset value overflow"); - - if (((maxV >> 32) * minV) > 2095475792ull) // cMaxNative / 2^32 - Throw("Asset value overflow"); - - return STAmount(asset, minV * maxV); - } if (getSTNumberSwitchover()) - return {IOUAmount{Number{v1} * Number{v2}}, asset}; + return {IOUAmount{Number{v1} * Number{v2}}, issue}; std::uint64_t value1 = v1.mantissa(); std::uint64_t value2 = v2.mantissa(); int offset1 = v1.exponent(); int offset2 = v2.exponent(); - // TODO MPT - if (v1.native() || v1.isMPT()) + if (v1.native()) { while (value1 < STAmount::cMinValue) { @@ -1255,7 +1127,7 @@ multiply(STAmount const& v1, STAmount const& v2, Asset const& asset) } } - if (v2.native() || v2.isMPT()) + if (v2.native()) { while (value2 < STAmount::cMinValue) { @@ -1269,7 +1141,7 @@ multiply(STAmount const& v1, STAmount const& v2, Asset const& asset) // range. Dividing their product by 10^14 maintains the // precision, by scaling the result to 10^16 to 10^18. return STAmount( - asset, + issue, muldiv(value1, value2, tenTo14) + 7, offset1 + offset2 + 14, v1.negative() != v2.negative()); @@ -1406,15 +1278,14 @@ static STAmount mulRoundImpl( STAmount const& v1, STAmount const& v2, - Asset const& asset, + Issue const& issue, bool roundUp) { if (v1 == beast::zero || v2 == beast::zero) - return {asset}; + return {issue}; - bool const xrp = isXRP(asset); + bool const xrp = isXRP(issue); - // TODO MPT if (v1.native() && v2.native() && xrp) { std::uint64_t minV = @@ -1430,30 +1301,11 @@ mulRoundImpl( return STAmount(v1.getFName(), minV * maxV); } - // TODO MPT - if (v1.isMPT() && v2.isMPT() && asset.isMPT()) - { - std::uint64_t minV = (getMPTValue(v1) < getMPTValue(v2)) - ? getMPTValue(v1) - : getMPTValue(v2); - std::uint64_t maxV = (getMPTValue(v1) < getMPTValue(v2)) - ? getMPTValue(v2) - : getMPTValue(v1); - - if (minV > 3000000000ull) // sqrt(cMaxNative) - Throw("Asset value overflow"); - - if (((maxV >> 32) * minV) > 2095475792ull) // cMaxNative / 2^32 - Throw("Asset value overflow"); - - return STAmount(asset, minV * maxV); - } std::uint64_t value1 = v1.mantissa(), value2 = v2.mantissa(); int offset1 = v1.exponent(), offset2 = v2.exponent(); - // TODO MPT - if (v1.native() || v1.isMPT()) + if (v1.native()) { while (value1 < STAmount::cMinValue) { @@ -1462,8 +1314,7 @@ mulRoundImpl( } } - // TODO MPT - if (v2.native() || v2.isMPT()) + if (v2.native()) { while (value2 < STAmount::cMinValue) { @@ -1494,7 +1345,7 @@ mulRoundImpl( // If appropriate, tell Number to round down. This gives the desired // result from STAmount::canonicalize. MightSaveRound const savedRound(Number::towards_zero); - return STAmount(asset, amount, offset, resultNegative); + return STAmount(issue, amount, offset, resultNegative); }(); if (roundUp && !resultNegative && !result) @@ -1511,7 +1362,7 @@ mulRoundImpl( amount = STAmount::cMinValue; offset = STAmount::cMinOffset; } - return STAmount(asset, amount, offset, resultNegative); + return STAmount(issue, amount, offset, resultNegative); } return result; } @@ -1520,22 +1371,22 @@ STAmount mulRound( STAmount const& v1, STAmount const& v2, - Asset const& asset, + Issue const& issue, bool roundUp) { return mulRoundImpl( - v1, v2, asset, roundUp); + v1, v2, issue, roundUp); } STAmount mulRoundStrict( STAmount const& v1, STAmount const& v2, - Asset const& asset, + Issue const& issue, bool roundUp) { return mulRoundImpl( - v1, v2, asset, roundUp); + v1, v2, issue, roundUp); } // We might need to use NumberRoundModeGuard. Allow the caller @@ -1545,20 +1396,19 @@ static STAmount divRoundImpl( STAmount const& num, STAmount const& den, - Asset const& asset, + Issue const& issue, bool roundUp) { if (den == beast::zero) Throw("division by zero"); if (num == beast::zero) - return {asset}; + return {issue}; std::uint64_t numVal = num.mantissa(), denVal = den.mantissa(); int numOffset = num.exponent(), denOffset = den.exponent(); - // TODO MPT - if (num.native() || num.isMPT()) + if (num.native()) { while (numVal < STAmount::cMinValue) { @@ -1567,8 +1417,7 @@ divRoundImpl( } } - // TODO MPT - if (den.native() || den.isMPT()) + if (den.native()) { while (denVal < STAmount::cMinValue) { @@ -1592,10 +1441,8 @@ divRoundImpl( int offset = numOffset - denOffset - 17; - // TODO MPT if (resultNegative != roundUp) - canonicalizeRound( - isXRP(asset) /*|| issue.isMPT()*/, amount, offset, roundUp); + canonicalizeRound(isXRP(issue), amount, offset, roundUp); STAmount result = [&]() { // If appropriate, tell Number the rounding mode we are using. @@ -1604,12 +1451,12 @@ divRoundImpl( using enum Number::rounding_mode; MightSaveRound const savedRound( roundUp ^ resultNegative ? upward : downward); - return STAmount(asset, amount, offset, resultNegative); + return STAmount(issue, amount, offset, resultNegative); }(); if (roundUp && !resultNegative && !result) { - if (isXRP(asset) || asset.isMPT()) + if (isXRP(issue)) { // return the smallest value above zero amount = 1; @@ -1621,7 +1468,7 @@ divRoundImpl( amount = STAmount::cMinValue; offset = STAmount::cMinOffset; } - return STAmount(asset, amount, offset, resultNegative); + return STAmount(issue, amount, offset, resultNegative); } return result; } @@ -1630,20 +1477,20 @@ STAmount divRound( STAmount const& num, STAmount const& den, - Asset const& asset, + Issue const& issue, bool roundUp) { - return divRoundImpl(num, den, asset, roundUp); + return divRoundImpl(num, den, issue, roundUp); } STAmount divRoundStrict( STAmount const& num, STAmount const& den, - Asset const& asset, + Issue const& issue, bool roundUp) { - return divRoundImpl(num, den, asset, roundUp); + return divRoundImpl(num, den, issue, roundUp); } } // namespace ripple diff --git a/src/libxrpl/protocol/STEitherAmount.cpp b/src/libxrpl/protocol/STEitherAmount.cpp new file mode 100644 index 00000000000..dd18665d816 --- /dev/null +++ b/src/libxrpl/protocol/STEitherAmount.cpp @@ -0,0 +1,442 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include + +#include + +namespace ripple { + +STEitherAmount::STEitherAmount(SerialIter& sit, SField const& name) + : STBase(name) +{ + auto const value = sit.get64(); + if ((value & STAmount::cNotNative) == 0 && + (value & STMPTAmount::cMPToken) != 0) + amount_.emplace(value, sit, name); + else + amount_.emplace(value, sit, name); +} + +STEitherAmount::STEitherAmount(XRPAmount const& amount) : amount_{amount} +{ +} + +STEitherAmount::STEitherAmount(STAmount const& amount) + : STBase(amount.getFName()), amount_{amount} +{ +} + +STEitherAmount::STEitherAmount(SField const& name, STAmount const& amount) + : STBase(name), amount_{amount} +{ +} + +STEitherAmount::STEitherAmount(STMPTAmount const& amount) + : STBase(amount.getFName()), amount_{amount} +{ +} + +STEitherAmount& +STEitherAmount::operator=(STAmount const& amount) +{ + setFName(amount.getFName()); + amount_ = amount; + return *this; +} + +STEitherAmount& +STEitherAmount::operator=(STMPTAmount const& amount) +{ + setFName(amount.getFName()); + amount_ = amount; + return *this; +} + +STEitherAmount& +STEitherAmount::operator=(XRPAmount const& amount) +{ + amount_ = amount; + return *this; +} + +SerializedTypeID +STEitherAmount::getSType() const +{ + return STI_AMOUNT; +} + +std::string +STEitherAmount::getFullText() const +{ + return std::visit([&](auto&& a) { return a.getFullText(); }, amount_); +} + +std::string +STEitherAmount::getText() const +{ + return std::visit([&](auto&& a) { return a.getText(); }, amount_); +} + +Json::Value STEitherAmount::getJson(JsonOptions) const +{ + return std::visit( + [&](auto&& a) { return a.getJson(JsonOptions::none); }, amount_); +} + +void +STEitherAmount::setJson(Json::Value& jv) const +{ + std::visit([&](auto&& a) { a.setJson(jv); }, amount_); +} + +void +STEitherAmount::add(Serializer& s) const +{ + std::visit([&](auto&& a) { a.add(s); }, amount_); +} + +bool +STEitherAmount::isEquivalent(const STBase& t) const +{ + const STEitherAmount* v = dynamic_cast(&t); + return v && + std::visit( + [&](auto&& a, auto&& a1) { return a.isEquivalent(a1); }, + amount_, + v->amount_); +} + +bool +STEitherAmount::isDefault() const +{ + return std::visit([&](auto&& a) { return a.isDefault(); }, amount_); +} +//------------------------------------------------------------------------------ +bool +STEitherAmount::isMPT() const +{ + return std::holds_alternative(amount_); +} + +bool +STEitherAmount::isIssue() const +{ + return std::holds_alternative(amount_); +} + +bool +STEitherAmount::badAsset() const +{ + if (isIssue()) + return badCurrency() == std::get(amount_).getCurrency(); + return badMPT() == std::get(amount_).getCurrency(); +} + +bool +STEitherAmount::negative() const +{ + if (isIssue()) + return std::get(amount_).negative(); + return false; +} + +bool +STEitherAmount::native() const +{ + if (isIssue()) + return std::get(amount_).native(); + return false; +} + +STEitherAmount +STEitherAmount::zeroed() const +{ + return std::visit( + [&](auto&& a) { return STEitherAmount{a.zeroed()}; }, amount_); +} + +bool +STEitherAmount::sameAsset(STEitherAmount const& amount) const +{ + return std::visit( + [&](T1&& a1, T2&& a2) { + if constexpr (std::is_same_v) + return a1.getCurrency() == a2.getCurrency(); + else + return false; + }, + amount_, + amount.amount_); +} + +bool +STEitherAmount::sameIssue(STEitherAmount const& amount) const +{ + return std::visit( + [&](T1&& a1, T2&& a2) { + if constexpr (std::is_same_v) + return a1.issue() == a2.issue(); + else + return false; + }, + amount_, + amount.amount_); +} + +STEitherAmount const& +STEitherAmount::value() const +{ + return *this; +} + +std::variant const& +STEitherAmount::getValue() const +{ + return amount_; +} + +std::variant& +STEitherAmount::getValue() +{ + return amount_; +} + +AccountID const& +STEitherAmount::getIssuer() const +{ + if (isIssue()) + return get().getIssuer(); + return get().getIssuer(); +} + +STBase* +STEitherAmount::copy(std::size_t n, void* buf) const +{ + return emplace(n, buf, *this); +} + +STBase* +STEitherAmount::move(std::size_t n, void* buf) +{ + return emplace(n, buf, std::move(*this)); +} + +int +STEitherAmount::signum() const noexcept +{ + return std::visit([&](auto&& a) { return a.signum(); }, amount_); +} + +static bool +validJSONIssue(Json::Value const& jv) +{ + return (jv.isMember(jss::currency) && !jv.isMember(jss::mpt_issuance_id)) || + (!jv.isMember(jss::currency) && !jv.isMember(jss::issuer) && + jv.isMember(jss::mpt_issuance_id)); +} + +STEitherAmount +amountFromJson(SField const& name, Json::Value const& v) +{ + STAmount::mantissa_type mantissa = 0; + STAmount::exponent_type exponent = 0; + bool negative = false; + std::variant issue; + + Json::Value value; + Json::Value currencyOrMPTID; + Json::Value issuer; + bool isMPT = false; + + if (v.isNull()) + { + Throw( + "XRP may not be specified with a null Json value"); + } + else if (v.isObject()) + { + if (!validJSONIssue(v)) + Throw("Invalid Issue's Json specification"); + + value = v[jss::value]; + if (v.isMember(jss::mpt_issuance_id)) + { + isMPT = true; + currencyOrMPTID = v[jss::mpt_issuance_id]; + } + else + { + currencyOrMPTID = v[jss::currency]; + issuer = v[jss::issuer]; + } + } + else if (v.isArray()) + { + value = v.get(Json::UInt(0), 0); + currencyOrMPTID = v.get(Json::UInt(1), Json::nullValue); + issuer = v.get(Json::UInt(2), Json::nullValue); + } + else if (v.isString()) + { + std::string val = v.asString(); + std::vector elements; + boost::split(elements, val, boost::is_any_of("\t\n\r ,/")); + + if (elements.size() > 3) + Throw("invalid amount string"); + + value = elements[0]; + + if (elements.size() > 1) + currencyOrMPTID = elements[1]; + + if (elements.size() > 2) + issuer = elements[2]; + } + else + { + value = v; + } + + bool const native = !currencyOrMPTID.isString() || + currencyOrMPTID.asString().empty() || + (currencyOrMPTID.asString() == systemCurrencyCode()); + + if (native) + { + if (v.isObjectOrNull()) + Throw("XRP may not be specified as an object"); + issue = xrpIssue(); + } + else + { + if (isMPT) + { + // sequence (32 bits) + account (160 bits) + uint192 u; + if (!u.parseHex(currencyOrMPTID.asString())) + Throw("invalid MPTokenIssuanceID"); + issue = u; + } + else + { + issue = Issue{}; + if (!to_currency( + std::get(issue).currency, + currencyOrMPTID.asString())) + Throw("invalid currency"); + if (!issuer.isString() || + !to_issuer(std::get(issue).account, issuer.asString())) + Throw("invalid issuer"); + if (isXRP(std::get(issue))) + Throw("invalid issuer"); + } + } + + if (value.isInt()) + { + if (value.asInt() >= 0) + { + mantissa = value.asInt(); + } + else + { + mantissa = -value.asInt(); + negative = true; + } + } + else if (value.isUInt()) + { + mantissa = v.asUInt(); + } + else if (value.isString()) + { + if (std::holds_alternative(issue)) + { + STAmount const ret = + amountFromString(std::get(issue), value.asString()); + mantissa = ret.mantissa(); + exponent = ret.exponent(); + negative = ret.negative(); + } + else + { + STMPTAmount const ret = + amountFromString(std::get(issue), value.asString()); + mantissa = ret.value(); + exponent = 0; + negative = false; + } + } + else + { + Throw("invalid amount type"); + } + + if (std::holds_alternative(issue)) + return STAmount{ + name, std::get(issue), mantissa, exponent, native, negative}; + while (exponent-- > 0) + mantissa *= 10; + if (mantissa > 0x8000000000000000) + Throw("MPT amount out of range"); + return STMPTAmount{ + name, std::get(issue), static_cast(mantissa)}; +} + +bool +amountFromJsonNoThrow(STEitherAmount& result, Json::Value const& jvSource) +{ + try + { + result = amountFromJson(sfGeneric, jvSource); + return true; + } + catch (const std::exception& e) + { + JLOG(debugLog().warn()) + << "amountFromJsonNoThrow: caught: " << e.what(); + } + return false; +} + +bool +amountFromJsonNoThrow(STAmount& result, Json::Value const& jvSource) +{ + try + { + STEitherAmount amount; + const bool res = amountFromJsonNoThrow(amount, jvSource); + if (res) + result = get(amount); + return res; + } + catch (const std::exception& e) + { + JLOG(debugLog().warn()) + << "amountFromJsonNoThrow: caught: " << e.what(); + } + return false; +} + +} // namespace ripple diff --git a/src/libxrpl/protocol/STMPTAmount.cpp b/src/libxrpl/protocol/STMPTAmount.cpp new file mode 100644 index 00000000000..ffec54a1b3c --- /dev/null +++ b/src/libxrpl/protocol/STMPTAmount.cpp @@ -0,0 +1,233 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include + +#include + +namespace ripple { + +STMPTAmount::STMPTAmount( + std::uint64_t value, + SerialIter& sit, + SField const& name) + : STBase(name) +{ + assert(value & cMPToken); + value_ = (value << 8) | sit.get8(); + value_ &= ~cMPToken; + + issue_ = sit.get192(); +} + +STMPTAmount::STMPTAmount( + SField const& name, + MPTIssue const& issue, + value_type value) + : MPTAmount(value), STBase(name), issue_(issue) +{ +} + +STMPTAmount::STMPTAmount(MPTIssue const& issue, value_type value) + : MPTAmount(value), issue_(issue) +{ +} + +STMPTAmount::STMPTAmount(MPTIssue const& issue, std::uint64_t value) + : issue_(issue) +{ + if (value > cMaxMPTValue) + Throw("MPTAmount is out of range"); + value_ = static_cast(value); +} + +STMPTAmount::STMPTAmount(value_type value) : MPTAmount(value) +{ +} + +SerializedTypeID +STMPTAmount::getSType() const +{ + return STI_AMOUNT; +} + +std::string +STMPTAmount::getFullText() const +{ + std::string ret; + + ret.reserve(64); + ret = getText() + "/" + to_string(issue_.getMptID()); + return ret; +} + +std::string +STMPTAmount::getText() const +{ + return std::to_string(value_); +} + +Json::Value STMPTAmount::getJson(JsonOptions) const +{ + Json::Value elem; + setJson(elem); + return elem; +} + +void +STMPTAmount::setJson(Json::Value& elem) const +{ + elem[jss::mpt_issuance_id] = to_string(issue_.getMptID()); + elem[jss::value] = getText(); +} + +void +STMPTAmount::add(Serializer& s) const +{ + auto u8 = static_cast(cMPToken >> 56); + s.add8(u8); + s.add64(value_); + s.addBitString(issue_.getMptID()); +} + +bool +STMPTAmount::isEquivalent(const STBase& t) const +{ + const STMPTAmount* v = dynamic_cast(&t); + return v && operator==(*v); +} + +bool +STMPTAmount::isDefault() const +{ + return value_ == 0 && issue_ == badMPT(); +} + +AccountID const& +STMPTAmount::getIssuer() const +{ + return issue_.getIssuer(); +} + +uint192 +STMPTAmount::getCurrency() const +{ + return issue_.getMptID(); +} + +MPTIssue const& +STMPTAmount::issue() const +{ + return issue_; +} + +void +STMPTAmount::clear() +{ + value_ = 0; +} + +void +STMPTAmount::clear(MPTIssue const& issue) +{ + issue_ = issue; + value_ = 0; +} + +STMPTAmount +STMPTAmount::zeroed() const +{ + return STMPTAmount{issue_}; +} + +int +STMPTAmount::signum() const noexcept +{ + return value_ ? 1 : 0; +} + +STMPTAmount +amountFromString(MPTIssue const& issue, std::string const& amount) +{ + static boost::regex const reNumber( + "^" // the beginning of the string + "([+]?)" // (optional) + character (MPT is positive) + "(0|[1-9][0-9]*)" // a number (no leading zeroes, unless 0) + "(\\.([0-9]+))?" // (optional) period followed by any number + "([eE]([+-]?)([0-9]+))?" // (optional) E, optional + or -, any number + "$", + boost::regex_constants::optimize); + + boost::smatch match; + + if (!boost::regex_match(amount, match, reNumber)) + Throw("MPT '" + amount + "' is not valid"); + + // Match fields: + // 0 = whole input + // 1 = sign + // 2 = integer portion + // 3 = whole fraction (with '.') + // 4 = fraction (without '.') + // 5 = whole exponent (with 'e') + // 6 = exponent sign + // 7 = exponent number + + // CHECKME: Why 32? Shouldn't this be 16? + if ((match[2].length() + match[4].length()) > 32) + Throw("Number '" + amount + "' is overlong"); + + // Can't specify MPT using fractional representation + if (match[3].matched) + Throw("MPT must be specified as integral."); + + std::int64_t mantissa; + int exponent; + + if (!match[4].matched) // integer only + { + mantissa = + beast::lexicalCastThrow(std::string(match[2])); + exponent = 0; + } + else + { + // integer and fraction + mantissa = beast::lexicalCastThrow(match[2] + match[4]); + exponent = -(match[4].length()); + } + + if (match[5].matched) + { + // we have an exponent + if (match[6].matched && (match[6] == "-")) + exponent -= beast::lexicalCastThrow(std::string(match[7])); + else + exponent += beast::lexicalCastThrow(std::string(match[7])); + } + + while (exponent-- > 0) + mantissa *= 10; + + return {issue, mantissa}; +} + +} // namespace ripple \ No newline at end of file diff --git a/src/libxrpl/protocol/STObject.cpp b/src/libxrpl/protocol/STObject.cpp index 7e62fc25bd6..2984799277d 100644 --- a/src/libxrpl/protocol/STObject.cpp +++ b/src/libxrpl/protocol/STObject.cpp @@ -165,6 +165,14 @@ 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()) + { + throwFieldErr(e.sField().fieldName, "doesn't support MPT"); + } + } v.emplace_back(std::move(*iter)); v_.erase(iter); } @@ -630,11 +638,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 +756,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..149186d43ce 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -143,14 +143,9 @@ STTx::getMentionedAccounts() const } else if (auto samt = dynamic_cast(&it)) { - if (samt->isIssue()) - { - auto const& issuer = samt->getIssuer(); - if (!isXRP(issuer)) - list.insert(issuer); - } - else - list.insert(samt->getIssuer()); + auto const& issuer = samt->getIssuer(); + if (!isXRP(issuer)) + list.insert(issuer); } } @@ -534,23 +529,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 +549,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..3f76ccd6903 100644 --- a/src/libxrpl/protocol/TxFormats.cpp +++ b/src/libxrpl/protocol/TxFormats.cpp @@ -162,7 +162,7 @@ TxFormats::TxFormats() ttPAYMENT, { {sfDestination, soeREQUIRED}, - {sfAmount, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTYes}, {sfSendMax, soeOPTIONAL}, {sfPaths, soeDEFAULT}, {sfInvoiceID, soeOPTIONAL}, @@ -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..1000b255573 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}); @@ -853,17 +854,33 @@ class MPToken_test : public beast::unit_test::suite { testcase("MPT Amount Invalid in Transaction"); using namespace test::jtx; - Env env{*this, features}; - Account const alice("alice"); // issuer - MPTTester mptAlice(env, alice); + { + Env env{*this, features}; + Account const alice("alice"); // issuer - mptAlice.create(); + MPTTester mptAlice(env, alice); + + mptAlice.create(); + + env(offer(alice, mptAlice.mpt(100), XRP(100)), ter(temMALFORMED)); + env.close(); + + BEAST_EXPECT(expectOffers(env, alice, 0)); + } - env(offer(alice, mptAlice.mpt(100), XRP(100)), ter(telENV_RPC_FAILED)); - env.close(); + { + Env env{*this, features - featureMPTokensV1}; + Account const alice("alice"); - BEAST_EXPECT(expectOffers(env, alice, 0)); + env.fund(XRP(1'000), alice); + STMPTAmount mpt{MPTIssue{std::make_pair(1, alice.id())}, 100llu}; + + env(offer(alice, mpt, XRP(100)), ter(temMALFORMED)); + env.close(); + + BEAST_EXPECT(expectOffers(env, alice, 0)); + } } void 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..113df99eedb 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include #include @@ -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); From e557a8938e779f920ea81126d9a2a994bd6081eb Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Sat, 27 Jul 2024 19:50:26 -0400 Subject: [PATCH 02/11] Split AMOUNT fields into two types SF_AMOUNT (don't support MPT) and SF_EITHER_AMOUNT (support MPT). Add TypedFieldAmount to declare Amount fields. Extend STObject with getters/setters for SF_AMOUNT fields. --- include/xrpl/protocol/SField.h | 35 ++- include/xrpl/protocol/SOTemplate.h | 20 +- include/xrpl/protocol/STEitherAmount.h | 15 +- include/xrpl/protocol/STMPTAmount.h | 88 +++++- include/xrpl/protocol/STObject.h | 288 ++++++++++++++---- src/libxrpl/protocol/SField.cpp | 9 +- src/libxrpl/protocol/STEitherAmount.cpp | 85 +++--- src/libxrpl/protocol/STMPTAmount.cpp | 4 +- src/libxrpl/protocol/STObject.cpp | 7 + src/libxrpl/protocol/TxFormats.cpp | 4 +- src/libxrpl/protocol/XChainAttestations.cpp | 10 +- src/test/app/Check_test.cpp | 6 +- src/test/app/Flow_test.cpp | 3 +- src/test/app/MPToken_test.cpp | 53 +++- src/test/app/Offer_test.cpp | 30 +- src/test/app/PayStrand_test.cpp | 6 +- src/test/app/ReducedOffer_test.cpp | 34 +-- src/test/app/Regression_test.cpp | 6 +- src/test/app/TheoreticalQuality_test.cpp | 3 +- src/test/app/TxQ_test.cpp | 17 +- src/test/jtx/PathSet.h | 10 +- src/test/jtx/delivermin.h | 5 +- src/test/jtx/impl/AMM.cpp | 9 +- src/test/jtx/impl/Env.cpp | 4 +- src/test/jtx/impl/TestHelpers.cpp | 16 +- src/test/jtx/impl/balance.cpp | 5 +- src/test/jtx/sendmax.h | 5 +- src/test/ledger/Invariants_test.cpp | 2 +- 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 | 6 +- src/xrpld/app/paths/TrustLine.cpp | 6 +- src/xrpld/app/tx/detail/AMMBid.cpp | 22 +- src/xrpld/app/tx/detail/AMMCreate.cpp | 9 +- src/xrpld/app/tx/detail/AMMDelete.cpp | 2 +- src/xrpld/app/tx/detail/AMMDeposit.cpp | 28 +- src/xrpld/app/tx/detail/AMMVote.cpp | 2 +- src/xrpld/app/tx/detail/AMMWithdraw.cpp | 33 +- src/xrpld/app/tx/detail/CashCheck.cpp | 21 +- src/xrpld/app/tx/detail/Change.cpp | 11 +- src/xrpld/app/tx/detail/Clawback.cpp | 8 +- src/xrpld/app/tx/detail/CreateCheck.cpp | 4 +- src/xrpld/app/tx/detail/CreateOffer.cpp | 14 +- src/xrpld/app/tx/detail/DID.cpp | 2 +- src/xrpld/app/tx/detail/DeleteAccount.cpp | 6 +- src/xrpld/app/tx/detail/Escrow.cpp | 11 +- src/xrpld/app/tx/detail/InvariantCheck.cpp | 34 +-- .../app/tx/detail/NFTokenAcceptOffer.cpp | 13 +- src/xrpld/app/tx/detail/Offer.h | 8 +- src/xrpld/app/tx/detail/PayChan.cpp | 33 +- src/xrpld/app/tx/detail/Payment.cpp | 174 +++++------ 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 | 39 +-- src/xrpld/app/tx/detail/applySteps.cpp | 5 +- src/xrpld/ledger/detail/PaymentSandbox.cpp | 16 +- src/xrpld/ledger/detail/View.cpp | 33 +- src/xrpld/rpc/BookChanges.h | 10 +- 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 +- 69 files changed, 832 insertions(+), 628 deletions(-) diff --git a/include/xrpl/protocol/SField.h b/include/xrpl/protocol/SField.h index 4dedea748b6..4483f6bf3b4 100644 --- a/include/xrpl/protocol/SField.h +++ b/include/xrpl/protocol/SField.h @@ -68,6 +68,7 @@ class STCurrency; STYPE(STI_UINT128, 4) \ STYPE(STI_UINT256, 5) \ STYPE(STI_AMOUNT, 6) \ + STYPE(STI_EITHER_AMOUNT, 6) \ STYPE(STI_VL, 7) \ STYPE(STI_ACCOUNT, 8) \ \ @@ -301,6 +302,8 @@ class SField static std::map knownCodeToField; }; +enum class SFieldMPT { None, Yes, No }; + /** A field with a type known at compile time. */ template struct TypedField : SField @@ -329,6 +332,33 @@ operator~(TypedField const& f) return OptionaledField(f); } +// Amount fields + +/** A field with a type known at compile time. */ +template +struct TypedFieldAmount : public TypedField +{ + template + explicit TypedFieldAmount(private_access_tag_t pat, Args&&... args); +}; + +/** Indicate std::optional field semantics. */ +template +struct OptionaledFieldAmount : public OptionaledField +{ + explicit OptionaledFieldAmount(TypedFieldAmount const& f_) + : OptionaledField(f_) + { + } +}; + +template +inline OptionaledFieldAmount +operator~(TypedFieldAmount const& f) +{ + return OptionaledFieldAmount(f); +} + //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ @@ -346,7 +376,8 @@ using SF_UINT384 = TypedField>; using SF_UINT512 = TypedField>; using SF_ACCOUNT = TypedField; -using SF_AMOUNT = TypedField; +using SF_AMOUNT = TypedFieldAmount; +using SF_EITHER_AMOUNT = TypedFieldAmount; using SF_ISSUE = TypedField; using SF_CURRENCY = TypedField; using SF_VL = TypedField; @@ -520,7 +551,7 @@ extern SF_UINT256 const sfHookNamespace; extern SF_UINT256 const sfHookSetTxnID; // currency amount (common) -extern SF_AMOUNT const sfAmount; +extern SF_EITHER_AMOUNT const sfAmount; extern SF_AMOUNT const sfBalance; extern SF_AMOUNT const sfLimitAmount; extern SF_AMOUNT const sfTakerPays; diff --git a/include/xrpl/protocol/SOTemplate.h b/include/xrpl/protocol/SOTemplate.h index 95f11f37658..0ea420a483b 100644 --- a/include/xrpl/protocol/SOTemplate.h +++ b/include/xrpl/protocol/SOTemplate.h @@ -40,7 +40,8 @@ enum SOEStyle { // constructed with STObject::makeInnerObject() }; -enum SOESupportMPT : bool { soeMPTYes = true, soeMPTNo = false }; +/** Amount fields that can support MPT */ +enum SOETxMPTAmount { soeMPTNone, soeMPTSupported, soeMPTNotSupported }; //------------------------------------------------------------------------------ @@ -50,7 +51,7 @@ class SOElement // Use std::reference_wrapper so SOElement can be stored in a std::vector. std::reference_wrapper sField_; SOEStyle style_; - SOESupportMPT supportMpt_; + SOETxMPTAmount supportMpt_; private: void @@ -70,14 +71,19 @@ class SOElement SOElement(SField const& fieldName, SOEStyle style) : sField_(fieldName) , style_(style) - , supportMpt_(SOESupportMPT::soeMPTNo) + , supportMpt_(SOETxMPTAmount::soeMPTNone) + { + init(fieldName); + } + SOElement(TypedFieldAmount const& fieldName, SOEStyle style) + : sField_(fieldName), style_(style), supportMpt_(soeMPTNotSupported) { init(fieldName); } SOElement( - TypedField const& fieldName, + TypedFieldAmount const& fieldName, SOEStyle style, - SOESupportMPT supportMpt = SOESupportMPT::soeMPTNo) + SOETxMPTAmount supportMpt = soeMPTNotSupported) : sField_(fieldName), style_(style), supportMpt_(supportMpt) { init(fieldName); @@ -95,10 +101,10 @@ class SOElement return style_; } - SOESupportMPT + bool supportMPT() const { - return supportMpt_; + return supportMpt_ == soeMPTSupported; } }; diff --git a/include/xrpl/protocol/STEitherAmount.h b/include/xrpl/protocol/STEitherAmount.h index a3ab1c4b2bc..3c62b42986c 100644 --- a/include/xrpl/protocol/STEitherAmount.h +++ b/include/xrpl/protocol/STEitherAmount.h @@ -49,7 +49,8 @@ class STEitherAmount : public STBase, public CountedObject STEitherAmount(SerialIter& sit, SField const& name); STEitherAmount(XRPAmount const& amount); STEitherAmount(STAmount const& amount); - STEitherAmount(SField const& name, STAmount const& amount); + STEitherAmount(SField const& name, STAmount const& amount = STAmount{}); + STEitherAmount(SField const& name, STMPTAmount const& amount); STEitherAmount(STMPTAmount const& amount); STEitherAmount& @@ -102,15 +103,6 @@ class STEitherAmount : public STBase, public CountedObject AccountID const& getIssuer() const; - bool - badAsset() const; - - bool - sameAsset(STEitherAmount const& amount) const; - - bool - sameIssue(STEitherAmount const& amount) const; - bool negative() const; @@ -192,6 +184,9 @@ get(auto&& amount) STEitherAmount amountFromJson(SField const& name, Json::Value const& v); +STAmount +amountFromJson(SF_AMOUNT const& name, Json::Value const& v); + bool amountFromJsonNoThrow(STEitherAmount& result, Json::Value const& jvSource); diff --git a/include/xrpl/protocol/STMPTAmount.h b/include/xrpl/protocol/STMPTAmount.h index 822fb44de8b..0bdabcba19c 100644 --- a/include/xrpl/protocol/STMPTAmount.h +++ b/include/xrpl/protocol/STMPTAmount.h @@ -93,23 +93,95 @@ class STMPTAmount final : public MPTAmount, int signum() const noexcept; - bool - operator==(STMPTAmount const& rhs) const; + STMPTAmount& + operator+=(STMPTAmount const& other); - bool - operator!=(STMPTAmount const& rhs) const; + STMPTAmount& + operator-=(STMPTAmount const& other); + + STMPTAmount + operator-() const; + + STMPTAmount& operator=(beast::Zero); }; +inline STMPTAmount +operator+(STMPTAmount const& lhs, STMPTAmount const& rhs) +{ + if (lhs.issue() != rhs.issue()) + Throw("Can't add amounts that aren't comparable!"); + return {lhs.issue(), lhs.value() + rhs.value()}; +} + +inline STMPTAmount +operator-(STMPTAmount const& lhs, STMPTAmount const& rhs) +{ + return lhs + (-rhs); +} + +inline STMPTAmount& +STMPTAmount::operator+=(const ripple::STMPTAmount& other) +{ + *this = *this + other; + return *this; +} + +inline STMPTAmount& +STMPTAmount::operator-=(const ripple::STMPTAmount& other) +{ + *this = *this - other; + return *this; +} + +inline STMPTAmount +STMPTAmount::operator-() const +{ + return {issue_, -value_}; +} + +inline STMPTAmount& STMPTAmount::operator=(beast::Zero) +{ + clear(); + return *this; +} + +inline bool +operator==(STMPTAmount const& lhs, STMPTAmount const& rhs) +{ + return lhs.issue() == rhs.issue() && lhs.value() == rhs.value(); +} + +inline bool +operator<(STMPTAmount const& lhs, STMPTAmount const& rhs) +{ + if (lhs.issue() != rhs.issue()) + Throw( + "Can't compare amounts that are't comparable!"); + return lhs.value() < rhs.value(); +} + +inline bool +operator!=(STMPTAmount const& lhs, STMPTAmount const& rhs) +{ + return !(lhs == rhs); +} + +inline bool +operator>(STMPTAmount const& lhs, STMPTAmount const& rhs) +{ + return rhs < lhs; +} + inline bool -STMPTAmount::operator==(STMPTAmount const& rhs) const +operator<=(STMPTAmount const& lhs, STMPTAmount const& rhs) { - return value_ == rhs.value_ && issue_ == rhs.issue_; + return !(rhs < lhs); } inline bool -STMPTAmount::operator!=(STMPTAmount const& rhs) const +operator>=(STMPTAmount const& lhs, STMPTAmount const& rhs) { - return !operator==(rhs); + return !(lhs < rhs); } STMPTAmount diff --git a/include/xrpl/protocol/STObject.h b/include/xrpl/protocol/STObject.h index 8a6a651463e..fb7d15d3630 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -54,12 +54,15 @@ throwFieldNotFound(SField const& field) class STObject : public STBase, public CountedObject { + template + using ValueType = std::conditional::type; + // Proxy value for a STBase derived class - template + template class Proxy; - template + template class ValueProxy; - template + template class OptionalProxy; struct Transform @@ -238,6 +241,8 @@ class STObject : public STBase, public CountedObject getFieldVL(SField const& field) const; STEitherAmount const& getFieldAmount(SField const& field) const; + STAmount const& + getFieldAmount(TypedFieldAmount const& field) const; STPathSet const& getFieldPathSet(SField const& field) const; const STVector256& @@ -258,6 +263,12 @@ class STObject : public STBase, public CountedObject typename T::value_type operator[](TypedField const& f) const; + /** Overload for amount fields + */ + template + typename ValueType::value_type + operator[](TypedFieldAmount const& f) const; + /** Get the value of a field as a std::optional @param An OptionaledField built from an SField value representing the @@ -270,6 +281,13 @@ class STObject : public STBase, public CountedObject std::optional> operator[](OptionaledField const& of) const; + /** Overload for amount fields + */ + template + std::optional< + std::decay_t::value_type>> + operator[](OptionaledFieldAmount const& of) const; + /** Get a modifiable field value. @param A TypedField built from an SField value representing the desired object field. In typical use, the TypedField will be implicitly @@ -281,6 +299,12 @@ class STObject : public STBase, public CountedObject ValueProxy operator[](TypedField const& f); + /** Overload for amount fields + */ + template + ValueProxy + operator[](TypedFieldAmount const& f); + /** Return a modifiable field value as std::optional @param An OptionaledField built from an SField value representing the @@ -294,6 +318,12 @@ class STObject : public STBase, public CountedObject OptionalProxy operator[](OptionaledField const& of); + /** Overload for amount fields + */ + template + OptionalProxy + operator[](OptionaledFieldAmount const& of); + /** Get the value of a field. @param A TypedField built from an SField value representing the desired object field. In typical use, the TypedField will be implicitly @@ -305,6 +335,12 @@ class STObject : public STBase, public CountedObject typename T::value_type at(TypedField const& f) const; + /** Overload for amount fields + */ + template + typename ValueType::value_type + at(TypedFieldAmount const& f) const; + /** Get the value of a field as std::optional @param An OptionaledField built from an SField value representing the @@ -317,6 +353,13 @@ class STObject : public STBase, public CountedObject std::optional> at(OptionaledField const& of) const; + /** Overload for amount fields + */ + template + std::optional< + std::decay_t::value_type>> + at(OptionaledFieldAmount const& of) const; + /** Get a modifiable field value. @param A TypedField built from an SField value representing the desired object field. In typical use, the TypedField will be implicitly @@ -328,6 +371,12 @@ class STObject : public STBase, public CountedObject ValueProxy at(TypedField const& f); + /** Overload for amount fields that don't support MPT + */ + template + ValueProxy + at(TypedFieldAmount const& f); + /** Return a modifiable field value as std::optional @param An OptionaledField built from an SField value representing the @@ -341,6 +390,12 @@ class STObject : public STBase, public CountedObject OptionalProxy at(OptionaledField const& of); + /** Overload for amount fields that don't support MPT + */ + template + OptionalProxy + at(OptionaledFieldAmount const& of); + /** Set a field. if the field already exists, it is replaced. */ @@ -430,6 +485,14 @@ class STObject : public STBase, public CountedObject static std::vector getSortedFields(STObject const& objToSort, WhichFields whichFields); + template + typename T::value_type + atImpl(TypedField const& f) const; + + template + std::optional> + atImpl(OptionaledField const& of) const; + // Implementation for getting (most) fields that return by value. // // The remove_cv and remove_reference are necessitated by the STBitString @@ -476,11 +539,11 @@ class STObject : public STBase, public CountedObject //------------------------------------------------------------------------------ -template +template class STObject::Proxy { protected: - using value_type = typename T::value_type; + using value_type = ValueType::value_type; STObject* st_; SOEStyle style_; @@ -501,11 +564,11 @@ class STObject::Proxy assign(U&& u); }; -template -class STObject::ValueProxy : private Proxy +template +class STObject::ValueProxy : private Proxy { private: - using value_type = typename T::value_type; + using value_type = ValueType::value_type; public: ValueProxy(ValueProxy const&) = default; @@ -524,11 +587,11 @@ class STObject::ValueProxy : private Proxy ValueProxy(STObject* st, TypedField const* f); }; -template -class STObject::OptionalProxy : private Proxy +template +class STObject::OptionalProxy : private Proxy { private: - using value_type = typename T::value_type; + using value_type = ValueType::value_type; using optional_type = std::optional::type>; @@ -660,8 +723,9 @@ class STObject::FieldErr : public std::runtime_error using std::runtime_error::runtime_error; }; -template -STObject::Proxy::Proxy(STObject* st, TypedField const* f) : st_(st), f_(f) +template +STObject::Proxy::Proxy(STObject* st, TypedField const* f) + : st_(st), f_(f) { if (st_->mType) { @@ -677,9 +741,9 @@ STObject::Proxy::Proxy(STObject* st, TypedField const* f) : st_(st), f_(f) } } -template +template auto -STObject::Proxy::value() const -> value_type +STObject::Proxy::value() const -> value_type { auto const t = find(); if (t) @@ -696,17 +760,36 @@ STObject::Proxy::value() const -> value_type return value_type{}; } -template +template <> +inline auto +STObject::Proxy::value() const -> STAmount +{ + auto const t = find(); + if (t) + return get(t->value()); + if (style_ == soeINVALID) + { + Throw("Value requested from invalid STObject."); + } + if (style_ != soeDEFAULT) + { + Throw( + "Missing field '" + this->f_->getName() + "'"); + } + return STAmount{}; +} + +template inline T const* -STObject::Proxy::find() const +STObject::Proxy::find() const { return dynamic_cast(st_->peekAtPField(*f_)); } -template +template template void -STObject::Proxy::assign(U&& u) +STObject::Proxy::assign(U&& u) { if (style_ == soeDEFAULT && u == value_type{}) { @@ -724,67 +807,68 @@ STObject::Proxy::assign(U&& u) //------------------------------------------------------------------------------ -template +template template -std::enable_if_t, STObject::ValueProxy&> -STObject::ValueProxy::operator=(U&& u) +std::enable_if_t, STObject::ValueProxy&> +STObject::ValueProxy::operator=(U&& u) { this->assign(std::forward(u)); return *this; } -template -STObject::ValueProxy::operator value_type() const +template +STObject::ValueProxy::operator value_type() const { return this->value(); } -template -STObject::ValueProxy::ValueProxy(STObject* st, TypedField const* f) - : Proxy(st, f) +template +STObject::ValueProxy::ValueProxy(STObject* st, TypedField const* f) + : Proxy(st, f) { } //------------------------------------------------------------------------------ -template -STObject::OptionalProxy::operator bool() const noexcept +template +STObject::OptionalProxy::operator bool() const noexcept { return engaged(); } -template +template auto -STObject::OptionalProxy::operator*() const -> value_type +STObject::OptionalProxy::operator*() const -> value_type { return this->value(); } -template -STObject::OptionalProxy::operator typename STObject::OptionalProxy< - T>::optional_type() const +template +STObject::OptionalProxy::operator typename STObject::OptionalProxy:: + optional_type() const { return optional_value(); } -template -typename STObject::OptionalProxy::optional_type -STObject::OptionalProxy::operator~() const +template +typename STObject::OptionalProxy::optional_type +STObject::OptionalProxy::operator~() const { return optional_value(); } -template +template auto -STObject::OptionalProxy::operator=(std::nullopt_t const&) -> OptionalProxy& +STObject::OptionalProxy::operator=(std::nullopt_t const&) + -> OptionalProxy& { disengage(); return *this; } -template +template auto -STObject::OptionalProxy::operator=(optional_type&& v) -> OptionalProxy& +STObject::OptionalProxy::operator=(optional_type&& v) -> OptionalProxy& { if (v) this->assign(std::move(*v)); @@ -793,9 +877,10 @@ STObject::OptionalProxy::operator=(optional_type&& v) -> OptionalProxy& return *this; } -template +template auto -STObject::OptionalProxy::operator=(optional_type const& v) -> OptionalProxy& +STObject::OptionalProxy::operator=(optional_type const& v) + -> OptionalProxy& { if (v) this->assign(*v); @@ -804,31 +889,33 @@ STObject::OptionalProxy::operator=(optional_type const& v) -> OptionalProxy& return *this; } -template +template template -std::enable_if_t, STObject::OptionalProxy&> -STObject::OptionalProxy::operator=(U&& u) +std::enable_if_t, STObject::OptionalProxy&> +STObject::OptionalProxy::operator=(U&& u) { this->assign(std::forward(u)); return *this; } -template -STObject::OptionalProxy::OptionalProxy(STObject* st, TypedField const* f) - : Proxy(st, f) +template +STObject::OptionalProxy::OptionalProxy( + STObject* st, + TypedField const* f) + : Proxy(st, f) { } -template +template bool -STObject::OptionalProxy::engaged() const noexcept +STObject::OptionalProxy::engaged() const noexcept { return this->style_ == soeDEFAULT || this->find() != nullptr; } -template +template void -STObject::OptionalProxy::disengage() +STObject::OptionalProxy::disengage() { if (this->style_ == soeREQUIRED || this->style_ == soeDEFAULT) Throw( @@ -839,18 +926,18 @@ STObject::OptionalProxy::disengage() this->st_->makeFieldAbsent(*this->f_); } -template +template auto -STObject::OptionalProxy::optional_value() const -> optional_type +STObject::OptionalProxy::optional_value() const -> optional_type { if (!engaged()) return std::nullopt; return this->value(); } -template -typename STObject::OptionalProxy::value_type -STObject::OptionalProxy::value_or(value_type val) const +template +typename STObject::OptionalProxy::value_type +STObject::OptionalProxy::value_or(value_type val) const { return engaged() ? this->value() : val; } @@ -962,6 +1049,13 @@ STObject::operator[](TypedField const& f) const return at(f); } +template +typename STObject::ValueType::value_type +STObject::operator[](TypedFieldAmount const& f) const +{ + return at(f); +} + template std::optional> STObject::operator[](OptionaledField const& of) const @@ -969,6 +1063,14 @@ STObject::operator[](OptionaledField const& of) const return at(of); } +template +std::optional< + std::decay_t::value_type>> +STObject::operator[](OptionaledFieldAmount const& of) const +{ + return at(of); +} + template inline auto STObject::operator[](TypedField const& f) -> ValueProxy @@ -976,6 +1078,14 @@ STObject::operator[](TypedField const& f) -> ValueProxy return at(f); } +template +inline auto +STObject::operator[](TypedFieldAmount const& f) + -> ValueProxy +{ + return at(f); +} + template inline auto STObject::operator[](OptionaledField const& of) -> OptionalProxy @@ -983,9 +1093,17 @@ STObject::operator[](OptionaledField const& of) -> OptionalProxy return at(of); } +template +inline auto +STObject::operator[](OptionaledFieldAmount const& of) + -> OptionalProxy +{ + return at(of); +} + template typename T::value_type -STObject::at(TypedField const& f) const +STObject::atImpl(TypedField const& f) const { auto const b = peekAtPField(f); if (!b) @@ -1010,9 +1128,26 @@ STObject::at(TypedField const& f) const return dv; } +template +typename T::value_type +STObject::at(TypedField const& f) const +{ + return atImpl(f); +} + +template +typename STObject::ValueType::value_type +STObject::at(TypedFieldAmount const& f) const +{ + if constexpr (M == SFieldMPT::Yes) + return atImpl(f); + else + return get(atImpl(f)); +} + template std::optional> -STObject::at(OptionaledField const& of) const +STObject::atImpl(OptionaledField const& of) const { auto const b = peekAtPField(*of.f); if (!b) @@ -1030,6 +1165,24 @@ STObject::at(OptionaledField const& of) const return u->value(); } +template +std::optional> +STObject::at(OptionaledField const& of) const +{ + return atImpl(of); +} + +template +std::optional< + std::decay_t::value_type>> +STObject::at(OptionaledFieldAmount const& of) const +{ + if constexpr (M == SFieldMPT::Yes) + return atImpl(of); + else + return get(atImpl(of)); +} + template inline auto STObject::at(TypedField const& f) -> ValueProxy @@ -1037,6 +1190,13 @@ STObject::at(TypedField const& f) -> ValueProxy return ValueProxy(this, &f); } +template +inline auto +STObject::at(TypedFieldAmount const& f) -> ValueProxy +{ + return ValueProxy(this, &f); +} + template inline auto STObject::at(OptionaledField const& of) -> OptionalProxy @@ -1044,6 +1204,14 @@ STObject::at(OptionaledField const& of) -> OptionalProxy return OptionalProxy(this, of.f); } +template +inline auto +STObject::at(OptionaledFieldAmount const& of) + -> OptionalProxy +{ + return OptionalProxy(this, of.f); +} + template void STObject::setFieldH160(SField const& field, base_uint<160, Tag> const& v) diff --git a/src/libxrpl/protocol/SField.cpp b/src/libxrpl/protocol/SField.cpp index 2d7615c85c8..38c2148c980 100644 --- a/src/libxrpl/protocol/SField.cpp +++ b/src/libxrpl/protocol/SField.cpp @@ -45,6 +45,13 @@ TypedField::TypedField(private_access_tag_t pat, Args&&... args) { } +template +template +TypedFieldAmount::TypedFieldAmount(private_access_tag_t pat, Args&&... args) + : TypedField(pat, std::forward(args)...) +{ +} + // Construct all compile-time SFields, and register them in the knownCodeToField // database: @@ -247,7 +254,7 @@ CONSTRUCT_TYPED_SFIELD(sfHookNamespace, "HookNamespace", UINT256, CONSTRUCT_TYPED_SFIELD(sfHookSetTxnID, "HookSetTxnID", UINT256, 33); // currency amount (common) -CONSTRUCT_TYPED_SFIELD(sfAmount, "Amount", AMOUNT, 1); +CONSTRUCT_TYPED_SFIELD(sfAmount, "Amount", EITHER_AMOUNT, 1); CONSTRUCT_TYPED_SFIELD(sfBalance, "Balance", AMOUNT, 2); CONSTRUCT_TYPED_SFIELD(sfLimitAmount, "LimitAmount", AMOUNT, 3); CONSTRUCT_TYPED_SFIELD(sfTakerPays, "TakerPays", AMOUNT, 4); diff --git a/src/libxrpl/protocol/STEitherAmount.cpp b/src/libxrpl/protocol/STEitherAmount.cpp index dd18665d816..cde7bc30d29 100644 --- a/src/libxrpl/protocol/STEitherAmount.cpp +++ b/src/libxrpl/protocol/STEitherAmount.cpp @@ -51,6 +51,11 @@ STEitherAmount::STEitherAmount(SField const& name, STAmount const& amount) { } +STEitherAmount::STEitherAmount(SField const& name, STMPTAmount const& amount) + : STBase(name), amount_{amount} +{ +} + STEitherAmount::STEitherAmount(STMPTAmount const& amount) : STBase(amount.getFName()), amount_{amount} { @@ -59,7 +64,6 @@ STEitherAmount::STEitherAmount(STMPTAmount const& amount) STEitherAmount& STEitherAmount::operator=(STAmount const& amount) { - setFName(amount.getFName()); amount_ = amount; return *this; } @@ -67,7 +71,6 @@ STEitherAmount::operator=(STAmount const& amount) STEitherAmount& STEitherAmount::operator=(STMPTAmount const& amount) { - setFName(amount.getFName()); amount_ = amount; return *this; } @@ -144,14 +147,6 @@ 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 { @@ -175,34 +170,6 @@ STEitherAmount::zeroed() const [&](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 { @@ -255,7 +222,9 @@ validJSONIssue(Json::Value const& jv) jv.isMember(jss::mpt_issuance_id)); } -STEitherAmount +namespace detail { + +static STEitherAmount amountFromJson(SField const& name, Json::Value const& v) { STAmount::mantissa_type mantissa = 0; @@ -394,14 +363,42 @@ amountFromJson(SField const& name, Json::Value const& v) } if (std::holds_alternative(issue)) - return STAmount{ - name, std::get(issue), mantissa, exponent, native, negative}; + return STEitherAmount{ + name, + STAmount{ + name, + std::get(issue), + mantissa, + exponent, + native, + negative}}; while (exponent-- > 0) mantissa *= 10; - if (mantissa > 0x8000000000000000) + if (mantissa > STMPTAmount::cMaxMPTValue) Throw("MPT amount out of range"); - return STMPTAmount{ - name, std::get(issue), static_cast(mantissa)}; + return STEitherAmount{ + name, + STMPTAmount{ + name, + std::get(issue), + static_cast(mantissa)}}; +} + +} // namespace detail + +STEitherAmount +amountFromJson(SField const& name, Json::Value const& v) +{ + return detail::amountFromJson(name, v); +} + +STAmount +amountFromJson(SF_AMOUNT const& name, Json::Value const& v) +{ + auto res = detail::amountFromJson(name, v); + if (!res.isIssue()) + Throw("Amount is not STAmount"); + return get(res); } bool diff --git a/src/libxrpl/protocol/STMPTAmount.cpp b/src/libxrpl/protocol/STMPTAmount.cpp index ffec54a1b3c..2e5235f2500 100644 --- a/src/libxrpl/protocol/STMPTAmount.cpp +++ b/src/libxrpl/protocol/STMPTAmount.cpp @@ -112,7 +112,7 @@ bool STMPTAmount::isEquivalent(const STBase& t) const { const STMPTAmount* v = dynamic_cast(&t); - return v && operator==(*v); + return v && *this == (*v); } bool @@ -161,7 +161,7 @@ STMPTAmount::zeroed() const int STMPTAmount::signum() const noexcept { - return value_ ? 1 : 0; + return MPTAmount::signum(); } STMPTAmount diff --git a/src/libxrpl/protocol/STObject.cpp b/src/libxrpl/protocol/STObject.cpp index 2984799277d..6542424e372 100644 --- a/src/libxrpl/protocol/STObject.cpp +++ b/src/libxrpl/protocol/STObject.cpp @@ -645,6 +645,13 @@ STObject::getFieldAmount(SField const& field) const return getFieldByConstRef(field, empty); } +STAmount const& +STObject::getFieldAmount(TypedFieldAmount const& field) const +{ + static STEitherAmount const empty{}; + return get(getFieldByConstRef(field, empty)); +} + STPathSet const& STObject::getFieldPathSet(SField const& field) const { diff --git a/src/libxrpl/protocol/TxFormats.cpp b/src/libxrpl/protocol/TxFormats.cpp index 3f76ccd6903..0ab849f818a 100644 --- a/src/libxrpl/protocol/TxFormats.cpp +++ b/src/libxrpl/protocol/TxFormats.cpp @@ -162,7 +162,7 @@ TxFormats::TxFormats() ttPAYMENT, { {sfDestination, soeREQUIRED}, - {sfAmount, soeREQUIRED, soeMPTYes}, + {sfAmount, soeREQUIRED, soeMPTSupported}, {sfSendMax, soeOPTIONAL}, {sfPaths, soeDEFAULT}, {sfInvoiceID, soeOPTIONAL}, @@ -377,7 +377,7 @@ TxFormats::TxFormats() add(jss::Clawback, ttCLAWBACK, { - {sfAmount, soeREQUIRED, soeMPTYes}, + {sfAmount, soeREQUIRED, soeMPTSupported}, {sfMPTokenHolder, soeOPTIONAL}, }, commonFields); diff --git a/src/libxrpl/protocol/XChainAttestations.cpp b/src/libxrpl/protocol/XChainAttestations.cpp index 10a5aaf0549..d72075e02c7 100644 --- a/src/libxrpl/protocol/XChainAttestations.cpp +++ b/src/libxrpl/protocol/XChainAttestations.cpp @@ -276,7 +276,7 @@ AttestationCreateAccount::AttestationCreateAccount(STObject const& o) : AttestationBase(o) , createCount{o[sfXChainAccountCreateCount]} , toCreate{o[sfDestination]} - , rewardAmount{get(o[sfSignatureReward])} + , rewardAmount{o[sfSignatureReward]} { } @@ -352,7 +352,7 @@ AttestationCreateAccount::toSTObject() const o[sfXChainAccountCreateCount] = createCount; o[sfDestination] = toCreate; - o[sfSignatureReward] = STEitherAmount{rewardAmount}; + o[sfSignatureReward] = rewardAmount; return o; } @@ -372,7 +372,7 @@ AttestationCreateAccount::message( // Serialize in SField order to make python serializers easier to write o[sfXChainAccountCreateCount] = createCount; o[sfAmount] = STEitherAmount{sendingAmount}; - o[sfSignatureReward] = STEitherAmount{rewardAmount}; + o[sfSignatureReward] = rewardAmount; o[sfDestination] = dst; o[sfOtherChainSource] = sendingAccount; o[sfAttestationRewardAccount] = rewardAccount; @@ -577,7 +577,7 @@ XChainCreateAccountAttestation::XChainCreateAccountAttestation( o[sfAttestationSignerAccount], PublicKey{o[sfPublicKey]}, get(o[sfAmount]), - get(o[sfSignatureReward]), + o[sfSignatureReward], o[sfAttestationRewardAccount], o[sfWasLockingChainSend] != 0, o[sfDestination]} {}; @@ -617,7 +617,7 @@ XChainCreateAccountAttestation::toSTObject() const STAccount{sfAttestationSignerAccount, keyAccount}; o[sfPublicKey] = publicKey; o[sfAmount] = STEitherAmount{sfAmount, amount}; - o[sfSignatureReward] = STEitherAmount{sfSignatureReward, rewardAmount}; + o[sfSignatureReward] = STAmount{sfSignatureReward, rewardAmount}; o[sfAttestationRewardAccount] = STAccount{sfAttestationRewardAccount, rewardAccount}; o[sfWasLockingChainSend] = wasLockingChainSend; diff --git a/src/test/app/Check_test.cpp b/src/test/app/Check_test.cpp index 0a899c82671..cb82bef9b59 100644 --- a/src/test/app/Check_test.cpp +++ b/src/test/app/Check_test.cpp @@ -2147,10 +2147,8 @@ class Check_test : public beast::unit_test::suite // without comparing the currency. auto cmpReqAmount = [this, offerLine, checkLine](SF_AMOUNT const& sfield) { - STAmount const offerAmount = - get(offerLine->at(sfield)); - STAmount const checkAmount = - get(checkLine->at(sfield)); + STAmount const offerAmount = offerLine->at(sfield); + STAmount const checkAmount = 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 250871747c4..ee11f5e4747 100644 --- a/src/test/app/Flow_test.cpp +++ b/src/test/app/Flow_test.cpp @@ -551,8 +551,7 @@ struct Flow_test : public beast::unit_test::suite return std::stoull(bookDirStr, nullptr, 16); }(); std::uint64_t const actualRate = getRate( - get(usdOffer->at(sfTakerGets)), - get(usdOffer->at(sfTakerPays))); + usdOffer->at(sfTakerGets), usdOffer->at(sfTakerPays)); // We expect the actual rate of the offer to be worse // (larger) than the rate of the book page holding the diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index 1000b255573..94cbe5a616b 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -847,6 +847,56 @@ class MPToken_test : public beast::unit_test::suite // bob can send back to alice(issuer) just fine mptAlice.pay(bob, alice, 10); } + + // MPT is disabled + { + Env env{*this, features - featureMPTokensV1}; + Account const alice("alice"); + Account const bob("bob"); + + env.fund(XRP(1'000), alice); + env.fund(XRP(1'000), bob); + STMPTAmount mpt{ + MPTIssue{std::make_pair(1, alice.id())}, UINT64_C(100)}; + + env(pay(alice, bob, mpt), ter(temDISABLED)); + } + + // Invalid combination of send, sendMax, deliverMin + { + Env env{*this, features}; + Account const alice("alice"); + Account const carol("carol"); + Account const bob("bob"); + + MPTTester mptAlice(env, alice, {.holders = {&bob, &carol}}); + + mptAlice.create({.ownerCount = 1, .holderCount = 0}); + + mptAlice.authorize({.account = &bob}); + + mptAlice.authorize({.account = &carol}); + + env(pay(alice, bob, mptAlice.mpt(100)), + sendmax(XRP(100)), + delivermin(XRP(100)), + ter(temMALFORMED)); + + env(pay(alice, bob, mptAlice.mpt(100)), + sendmax(mptAlice.mpt(100)), + delivermin(XRP(100)), + ter(temMALFORMED)); + + env(pay(alice, bob, XRP(100)), + sendmax(mptAlice.mpt(100)), + delivermin(XRP(100)), + ter(temMALFORMED)); + + env(pay(alice, bob, XRP(100)), + sendmax(XRP(100)), + delivermin(mptAlice.mpt(100)), + ter(temMALFORMED)); + } } void @@ -874,7 +924,8 @@ class MPToken_test : public beast::unit_test::suite Account const alice("alice"); env.fund(XRP(1'000), alice); - STMPTAmount mpt{MPTIssue{std::make_pair(1, alice.id())}, 100llu}; + STMPTAmount mpt{ + MPTIssue{std::make_pair(1, alice.id())}, UINT64_C(100)}; env(offer(alice, mpt, XRP(100)), ter(temMALFORMED)); env.close(); diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp index 76019741c34..01608b5b805 100644 --- a/src/test/app/Offer_test.cpp +++ b/src/test/app/Offer_test.cpp @@ -2315,13 +2315,10 @@ class OfferBaseUtil_test : public beast::unit_test::suite low.setIssuer(accountLow ? account.id() : issue.account); high.setIssuer(accountLow ? issue.account : account.id()); - BEAST_EXPECT( - get(sleTrust->getFieldAmount(sfLowLimit)) == low); - BEAST_EXPECT( - get(sleTrust->getFieldAmount(sfHighLimit)) == high); + BEAST_EXPECT(sleTrust->getFieldAmount(sfLowLimit) == low); + BEAST_EXPECT(sleTrust->getFieldAmount(sfHighLimit) == high); - STAmount actualBalance{ - get(sleTrust->getFieldAmount(sfBalance))}; + STAmount actualBalance{sleTrust->getFieldAmount(sfBalance)}; if (!accountLow) actualBalance.negate(); @@ -2955,10 +2952,8 @@ class OfferBaseUtil_test : public beast::unit_test::suite auto const& acctOffer = *(acctOffers.front()); BEAST_EXPECT(acctOffer[sfLedgerEntryType] == ltOFFER); - BEAST_EXPECT( - get(acctOffer[sfTakerGets]) == t.takerGets); - BEAST_EXPECT( - get(acctOffer[sfTakerPays]) == t.takerPays); + BEAST_EXPECT(acctOffer[sfTakerGets] == t.takerGets); + BEAST_EXPECT(acctOffer[sfTakerPays] == t.takerPays); } } @@ -3853,7 +3848,7 @@ class OfferBaseUtil_test : public beast::unit_test::suite auto const& offer = *offerPtr; BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER); BEAST_EXPECT( - get(offer[sfTakerGets]) == + offer[sfTakerGets] == STAmount(JPY.issue(), std::uint64_t(2230682446713524ul), -12)); BEAST_EXPECT(offer[sfTakerPays] == BTC(0.035378)); } @@ -3918,10 +3913,10 @@ class OfferBaseUtil_test : public beast::unit_test::suite auto const& offer = *offerPtr; BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER); BEAST_EXPECT( - get(offer[sfTakerGets]) == + offer[sfTakerGets] == STAmount(USD.issue(), std::uint64_t(2185847305256635), -14)); BEAST_EXPECT( - get(offer[sfTakerPays]) == + offer[sfTakerPays] == STAmount(JPY.issue(), std::uint64_t(2286608293434156), -12)); } } @@ -4109,8 +4104,7 @@ class OfferBaseUtil_test : public beast::unit_test::suite actorOffers.begin(), actorOffers.end(), [](std::shared_ptr& offer) { - return get((*offer)[sfTakerGets]) - .signum() == 0; + return (*offer)[sfTakerGets].signum() == 0; })); BEAST_EXPECT(offerCount == actor.offers); @@ -4267,8 +4261,7 @@ class OfferBaseUtil_test : public beast::unit_test::suite actorOffers.begin(), actorOffers.end(), [](std::shared_ptr& offer) { - return get((*offer)[sfTakerGets]) - .signum() == 0; + return (*offer)[sfTakerGets].signum() == 0; })); BEAST_EXPECT(offerCount == actor.offers); @@ -4817,8 +4810,7 @@ class OfferBaseUtil_test : public beast::unit_test::suite offers.emplace( (*sle)[sfSequence], std::make_pair( - get((*sle)[sfTakerPays]), - get((*sle)[sfTakerGets]))); + (*sle)[sfTakerPays], (*sle)[sfTakerGets])); }); // first offer diff --git a/src/test/app/PayStrand_test.cpp b/src/test/app/PayStrand_test.cpp index ec3a9a9b605..f00a7361292 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 = get((*sle)[sfBalance]); + auto const b = (*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 get((*sle)[sfBalance]); + return (*sle)[sfBalance]; }; auto lineBalance = [](ReadView const& v, ripple::Keylet const& k) { auto const sle = v.read(k); if (!sle) return STAmount{}; - return get((*sle)[sfBalance]); + return (*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 a59d0dcd534..a070051e435 100644 --- a/src/test/app/ReducedOffer_test.cpp +++ b/src/test/app/ReducedOffer_test.cpp @@ -134,14 +134,10 @@ class ReducedOffer_test : public beast::unit_test::suite Json::Value bobOffer = ledgerEntryOffer(env, bob, bobOfferSeq); - STAmount const reducedTakerGets = - get(amountFromJson( - sfTakerGets, - bobOffer[jss::node][sfTakerGets.jsonName])); - STAmount const reducedTakerPays = - get(amountFromJson( - sfTakerPays, - bobOffer[jss::node][sfTakerPays.jsonName])); + STAmount const reducedTakerGets = amountFromJson( + sfTakerGets, bobOffer[jss::node][sfTakerGets.jsonName]); + STAmount const reducedTakerPays = amountFromJson( + sfTakerPays, bobOffer[jss::node][sfTakerPays.jsonName]); STAmount const bobGot = env.balance(bob) + bobsFee - bobInitialBalance; BEAST_EXPECT(reducedTakerPays < newOffer.in); @@ -294,14 +290,12 @@ class ReducedOffer_test : public beast::unit_test::suite Json::Value aliceOffer = ledgerEntryOffer(env, alice, aliceOfferSeq); - STAmount const reducedTakerGets = - get(amountFromJson( - sfTakerGets, - aliceOffer[jss::node][sfTakerGets.jsonName])); - STAmount const reducedTakerPays = - get(amountFromJson( - sfTakerPays, - aliceOffer[jss::node][sfTakerPays.jsonName])); + STAmount const reducedTakerGets = amountFromJson( + sfTakerGets, + aliceOffer[jss::node][sfTakerGets.jsonName]); + STAmount const reducedTakerPays = amountFromJson( + sfTakerPays, + aliceOffer[jss::node][sfTakerPays.jsonName]); STAmount const aliceGot = env.balance(alice) - aliceInitialBalance; BEAST_EXPECT(reducedTakerPays < inLedger.in); @@ -618,10 +612,10 @@ class ReducedOffer_test : public beast::unit_test::suite Amounts jsonOfferToAmounts(Json::Value const& json) { - STAmount const in = get( - amountFromJson(sfTakerPays, json[sfTakerPays.jsonName])); - STAmount const out = get( - amountFromJson(sfTakerGets, json[sfTakerGets.jsonName])); + STAmount const in = + amountFromJson(sfTakerPays, json[sfTakerPays.jsonName]); + STAmount const out = + 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 609d425f1e9..f54a88ace00 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 = get(sle->getFieldAmount(sfBalance)); + auto balance = 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 = get(sle->getFieldAmount(sfBalance)); + auto balance = 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 = get(tx->getFieldAmount(sfFee)); + auto const fee = tx->getFieldAmount(sfFee); BEAST_EXPECT(fee == drops(expectedFees[i])); } } diff --git a/src/test/app/TheoreticalQuality_test.cpp b/src/test/app/TheoreticalQuality_test.cpp index cc52a1484c3..95e2dd0d9ab 100644 --- a/src/test/app/TheoreticalQuality_test.cpp +++ b/src/test/app/TheoreticalQuality_test.cpp @@ -51,8 +51,7 @@ struct RippleCalcTestParams , dstAmt{get(amountFromJson(sfAmount, jv[jss::Amount]))} { if (jv.isMember(jss::SendMax)) - sendMax = - get(amountFromJson(sfSendMax, jv[jss::SendMax])); + sendMax = 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 a1df4d997b1..e7b70203c91 100644 --- a/src/test/app/TxQ_test.cpp +++ b/src/test/app/TxQ_test.cpp @@ -1257,10 +1257,8 @@ 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 = get(env.le(alice)->getFieldAmount(sfBalance)) - .xrp() - .drops() - - (62); + aliceFee = + env.le(alice)->getFieldAmount(sfBalance).xrp().drops() - (62); env(noop(alice), seq(aliceSeq), fee(aliceFee), @@ -1320,10 +1318,7 @@ 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 = - get(env.le(bob)->getFieldAmount(sfBalance)) - .xrp() - .drops() - - (9 * 10 - 1); + env.le(bob)->getFieldAmount(sfBalance).xrp().drops() - (9 * 10 - 1); env(noop(bob), seq(bobSeq + 5), fee(bobFee), @@ -1336,10 +1331,8 @@ 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 = get(env.le(bob)->getFieldAmount(sfBalance)) - .xrp() - .drops() - - (9 * 10); + bobFee = + env.le(bob)->getFieldAmount(sfBalance).xrp().drops() - (9 * 10); env(noop(bob), seq(bobSeq + 5), fee(bobFee), diff --git a/src/test/jtx/PathSet.h b/src/test/jtx/PathSet.h index 55a084501e5..0f4c4ddd3dd 100644 --- a/src/test/jtx/PathSet.h +++ b/src/test/jtx/PathSet.h @@ -40,10 +40,8 @@ countOffers( forEachItem( *env.current(), account, [&](std::shared_ptr const& sle) { if (sle->getType() == ltOFFER && - get(sle->getFieldAmount(sfTakerPays)).issue() == - takerPays && - get(sle->getFieldAmount(sfTakerGets)).issue() == - takerGets) + sle->getFieldAmount(sfTakerPays).issue() == takerPays && + sle->getFieldAmount(sfTakerGets).issue() == takerGets) ++count; }); return count; @@ -60,8 +58,8 @@ countOffers( forEachItem( *env.current(), account, [&](std::shared_ptr const& sle) { if (sle->getType() == ltOFFER && - get(sle->getFieldAmount(sfTakerPays)) == takerPays && - get(sle->getFieldAmount(sfTakerGets)) == takerGets) + sle->getFieldAmount(sfTakerPays) == takerPays && + sle->getFieldAmount(sfTakerGets) == takerGets) ++count; }); return count; diff --git a/src/test/jtx/delivermin.h b/src/test/jtx/delivermin.h index 46e633dab20..95ca676c292 100644 --- a/src/test/jtx/delivermin.h +++ b/src/test/jtx/delivermin.h @@ -31,12 +31,15 @@ namespace jtx { class delivermin { private: - STAmount amount_; + STEitherAmount amount_; public: delivermin(STAmount const& amount) : amount_(amount) { } + delivermin(STMPTAmount const& amount) : amount_(amount) + { + } void operator()(Env&, JTx& jtx) const; diff --git a/src/test/jtx/impl/AMM.cpp b/src/test/jtx/impl/AMM.cpp index 9c5e17f7b75..c083b6df35c 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) - : get(amm->getFieldAmount(sfLPTokenBalance)); + : 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 get(amm->getFieldAmount(sfLPTokenBalance)).iou(); + return 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_ = get(auctionSlot[sfPrice]).iou(); + lastPurchasePrice_ = auctionSlot[sfPrice].iou(); } } bidMin_ = std::nullopt; @@ -777,8 +777,7 @@ AMM::expectAuctionSlot(auto&& cb) const auto const slotInterval = ammAuctionTimeSlot( env_.app().timeKeeper().now().time_since_epoch().count(), auctionSlot); - auto const slotPrice = - get(auctionSlot[sfPrice]).iou(); + auto const slotPrice = auctionSlot[sfPrice].iou(); auto const authAccounts = auctionSlot.getFieldArray(sfAuthAccounts); return cb(slotFee, slotInterval, slotPrice, authAccounts); diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index b070d64f6e1..6f0f9e3fc73 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 {get(sle->getFieldAmount(sfBalance)), ""}; + return {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 = get(sle->getFieldAmount(sfBalance)); + auto amount = 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 b7104820228..b8105b1a631 100644 --- a/src/test/jtx/impl/TestHelpers.cpp +++ b/src/test/jtx/impl/TestHelpers.cpp @@ -117,12 +117,11 @@ expectLine( low.setIssuer(accountLow ? account : issue.account); high.setIssuer(accountLow ? issue.account : account); - expectDefaultTrustLine = - get(sle->getFieldAmount(sfLowLimit)) == low && - get(sle->getFieldAmount(sfHighLimit)) == high; + expectDefaultTrustLine = sle->getFieldAmount(sfLowLimit) == low && + sle->getFieldAmount(sfHighLimit) == high; } - auto amount = get(sle->getFieldAmount(sfBalance)); + auto amount = sle->getFieldAmount(sfBalance); amount.setIssuer(value.issue().account); if (!accountLow) amount.negate(); @@ -155,11 +154,8 @@ expectOffers( ++cnt; if (std::find_if( toMatch.begin(), toMatch.end(), [&](auto const& a) { - return a.in == - get( - sle->getFieldAmount(sfTakerPays)) && - a.out == - get(sle->getFieldAmount(sfTakerGets)); + return a.in == sle->getFieldAmount(sfTakerPays) && + a.out == sle->getFieldAmount(sfTakerGets); }) != toMatch.end()) ++matched; } @@ -334,7 +330,7 @@ channelBalance(ReadView const& view, uint256 const& chan) auto const slep = view.read({ltPAYCHAN, chan}); if (!slep) return XRPAmount{-1}; - return get((*slep)[sfBalance]); + return (*slep)[sfBalance]; } bool diff --git a/src/test/jtx/impl/balance.cpp b/src/test/jtx/impl/balance.cpp index f2c67db512c..42330658eb0 100644 --- a/src/test/jtx/impl/balance.cpp +++ b/src/test/jtx/impl/balance.cpp @@ -35,8 +35,7 @@ balance::operator()(Env& env) const } else if (env.test.expect(sle)) { - env.test.expect( - get(sle->getFieldAmount(sfBalance)) == value_); + env.test.expect(sle->getFieldAmount(sfBalance) == value_); } } else @@ -48,7 +47,7 @@ balance::operator()(Env& env) const } else if (env.test.expect(sle)) { - auto amount = get(sle->getFieldAmount(sfBalance)); + auto amount = sle->getFieldAmount(sfBalance); amount.setIssuer(value_.issue().account); if (account_.id() > value_.issue().account) amount.negate(); diff --git a/src/test/jtx/sendmax.h b/src/test/jtx/sendmax.h index 495c61c33b8..86f23dc6894 100644 --- a/src/test/jtx/sendmax.h +++ b/src/test/jtx/sendmax.h @@ -31,12 +31,15 @@ namespace jtx { class sendmax { private: - STAmount amount_; + STEitherAmount amount_; public: sendmax(STAmount const& amount) : amount_(amount) { } + sendmax(STMPTAmount const& amount) : amount_(amount) + { + } void operator()(Env&, JTx& jtx) const; diff --git a/src/test/ledger/Invariants_test.cpp b/src/test/ledger/Invariants_test.cpp index 3b748b9998a..1fd1543f18b 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 = get(sle->getFieldAmount(sfBalance)); + auto amt = sle->getFieldAmount(sfBalance); sle->setFieldAmount(sfBalance, amt + STAmount{500}); ac.view().update(sle); return true; diff --git a/src/xrpld/app/ledger/AcceptedLedgerTx.cpp b/src/xrpld/app/ledger/AcceptedLedgerTx.cpp index 3a60f0d5eb6..e1ad68dff37 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 = get(mTxn->getFieldAmount(sfTakerGets)); + auto const amount = 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 1c76745b1c6..bcd3b6d4ba7 100644 --- a/src/xrpld/app/ledger/Ledger.cpp +++ b/src/xrpld/app/ledger/Ledger.cpp @@ -643,11 +643,10 @@ Ledger::setup() oldFees = baseFee || reserveBase || reserveIncrement; } { - auto const baseFeeXRP = get(sle->at(~sfBaseFeeDrops)); - auto const reserveBaseXRP = - get(sle->at(~sfReserveBaseDrops)); + auto const baseFeeXRP = sle->at(~sfBaseFeeDrops); + auto const reserveBaseXRP = sle->at(~sfReserveBaseDrops); auto const reserveIncrementXRP = - get(sle->at(~sfReserveIncrementDrops)); + 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 ff088d080c2..d0eddadbacb 100644 --- a/src/xrpld/app/ledger/OrderBookDB.cpp +++ b/src/xrpld/app/ledger/OrderBookDB.cpp @@ -274,10 +274,8 @@ OrderBookDB::processTxn( data->isFieldPresent(sfTakerGets)) { auto listeners = getBookListeners( - {get(data->getFieldAmount(sfTakerGets)) - .issue(), - get(data->getFieldAmount(sfTakerPays)) - .issue()}); + {data->getFieldAmount(sfTakerGets).issue(), + 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 25d7ae7b203..11f42cd66ec 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 = get(txn->getFieldAmount(sfTakerGets)); + auto const amount = 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 8a03f877351..af57314ef6d 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 = get(~val->at(~xrpField)); + if (auto const field = ~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 0755cf1ae63..f7cdfa212a2 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -2198,17 +2198,15 @@ NetworkOPsImp::pubValidation(std::shared_ptr const& val) // (The ~ operator converts the Proxy to a std::optional, which // simplifies later operations) - if (auto const baseFeeXRP = get(~val->at(~sfBaseFeeDrops)); + if (auto const baseFeeXRP = ~val->at(~sfBaseFeeDrops); baseFeeXRP && baseFeeXRP->native()) jvObj[jss::base_fee] = baseFeeXRP->xrp().jsonClipped(); - if (auto const reserveBaseXRP = - get(~val->at(~sfReserveBaseDrops)); + if (auto const reserveBaseXRP = ~val->at(~sfReserveBaseDrops); reserveBaseXRP && reserveBaseXRP->native()) jvObj[jss::reserve_base] = reserveBaseXRP->xrp().jsonClipped(); - if (auto const reserveIncXRP = - get(~val->at(~sfReserveIncrementDrops)); + if (auto const reserveIncXRP = ~val->at(~sfReserveIncrementDrops); reserveIncXRP && reserveIncXRP->native()) jvObj[jss::reserve_inc] = reserveIncXRP->xrp().jsonClipped(); @@ -3155,8 +3153,7 @@ NetworkOPsImp::transJson( if (transaction->getTxnType() == ttOFFER_CREATE) { auto const account = transaction->getAccountID(sfAccount); - auto const amount = - get(transaction->getFieldAmount(sfTakerGets)); + auto const amount = transaction->getFieldAmount(sfTakerGets); // If the offer create is not self funded then add the owner balance if (account != amount.issue().account) @@ -4393,10 +4390,8 @@ NetworkOPsImp::getBookPage( if (sleOffer) { auto const uOfferOwnerID = sleOffer->getAccountID(sfAccount); - auto const& saTakerGets = - get(sleOffer->getFieldAmount(sfTakerGets)); - auto const& saTakerPays = - get(sleOffer->getFieldAmount(sfTakerPays)); + auto const& saTakerGets = sleOffer->getFieldAmount(sfTakerGets); + auto const& saTakerPays = sleOffer->getFieldAmount(sfTakerPays); STAmount saOwnerFunds; bool firstOwnerOffer(true); @@ -4548,10 +4543,8 @@ NetworkOPsImp::getBookPage( if (sleOffer) { auto const uOfferOwnerID = sleOffer->getAccountID(sfAccount); - auto const& saTakerGets = - get(sleOffer->getFieldAmount(sfTakerGets)); - auto const& saTakerPays = - get(sleOffer->getFieldAmount(sfTakerPays)); + auto const& saTakerGets = sleOffer->getFieldAmount(sfTakerGets); + auto const& saTakerPays = 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 dd6f3d6a141..efc80cf17b6 100644 --- a/src/xrpld/app/misc/detail/AMMUtils.cpp +++ b/src/xrpld/app/misc/detail/AMMUtils.cpp @@ -104,8 +104,7 @@ ammHolds( issues->second, freezeHandling, j); - return std::make_tuple( - asset1, asset2, get(ammSle[sfLPTokenBalance])); + return std::make_tuple(asset1, asset2, ammSle[sfLPTokenBalance]); } STAmount @@ -182,14 +181,14 @@ ammAccountHolds( if (isXRP(issue)) { if (auto const sle = view.read(keylet::account(ammAccountID))) - return get((*sle)[sfBalance]); + return (*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 = get((*sle)[sfBalance]); + auto amount = (*sle)[sfBalance]; if (ammAccountID > issue.account) amount.negate(); amount.setIssuer(issue.account); @@ -393,10 +392,8 @@ isOnlyLiquidityProvider( } if (sle->getFieldU16(sfLedgerEntryType) != ltRIPPLE_STATE) return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE - auto const lowLimit = - get(sle->getFieldAmount(sfLowLimit)); - auto const highLimit = - get(sle->getFieldAmount(sfHighLimit)); + auto const lowLimit = sle->getFieldAmount(sfLowLimit); + auto const highLimit = 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 e0b386af754..159a700cc3f 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 = get(tx[sfFee]).xrp(); + XRPAmount feePaid = 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 = get((*sleAccount)[sfBalance]).xrp(); + auto const balance = (*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 5e632fd4d7d..c11f628a11d 100644 --- a/src/xrpld/app/paths/Credit.cpp +++ b/src/xrpld/app/paths/Credit.cpp @@ -37,8 +37,8 @@ creditLimit( if (sleRippleState) { - result = get(sleRippleState->getFieldAmount( - account < issuer ? sfLowLimit : sfHighLimit)); + result = sleRippleState->getFieldAmount( + account < issuer ? sfLowLimit : sfHighLimit); result.setIssuer(account); } @@ -70,7 +70,7 @@ creditBalance( if (sleRippleState) { - result = get(sleRippleState->getFieldAmount(sfBalance)); + result = sleRippleState->getFieldAmount(sfBalance); if (account < issuer) result.negate(); result.setIssuer(account); diff --git a/src/xrpld/app/paths/TrustLine.cpp b/src/xrpld/app/paths/TrustLine.cpp index 949a02a7ed8..6390c8d2117 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(get(sle->getFieldAmount(sfLowLimit))) - , mHighLimit(get(sle->getFieldAmount(sfHighLimit))) - , mBalance(get(sle->getFieldAmount(sfBalance))) + , mLowLimit(sle->getFieldAmount(sfLowLimit)) + , mHighLimit(sle->getFieldAmount(sfHighLimit)) + , mBalance(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 308f140791e..9de3762d2e3 100644 --- a/src/xrpld/app/tx/detail/AMMBid.cpp +++ b/src/xrpld/app/tx/detail/AMMBid.cpp @@ -40,10 +40,6 @@ 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."; @@ -56,7 +52,7 @@ AMMBid::preflight(PreflightContext const& ctx) return res; } - if (auto const bidMin = get(ctx.tx[~sfBidMin])) + if (auto const bidMin = ctx.tx[~sfBidMin]) { if (auto const res = invalidAMMAmount(*bidMin)) { @@ -65,7 +61,7 @@ AMMBid::preflight(PreflightContext const& ctx) } } - if (auto const bidMax = get(ctx.tx[~sfBidMax])) + if (auto const bidMax = ctx.tx[~sfBidMax]) { if (auto const res = invalidAMMAmount(*bidMax)) { @@ -98,7 +94,7 @@ AMMBid::preclaim(PreclaimContext const& ctx) return terNO_AMM; } - auto const lpTokensBalance = get((*ammSle)[sfLPTokenBalance]); + auto const lpTokensBalance = (*ammSle)[sfLPTokenBalance]; if (lpTokensBalance == beast::zero) return tecAMM_EMPTY; @@ -123,7 +119,7 @@ AMMBid::preclaim(PreclaimContext const& ctx) return tecAMM_INVALID_TOKENS; } - auto const bidMin = get(ctx.tx[~sfBidMin]); + auto const bidMin = ctx.tx[~sfBidMin]; if (bidMin) { @@ -139,7 +135,7 @@ AMMBid::preclaim(PreclaimContext const& ctx) } } - auto const bidMax = get(ctx.tx[~sfBidMax]); + auto const bidMax = ctx.tx[~sfBidMax]; if (bidMax) { if (bidMax->issue() != lpTokens.issue()) @@ -175,7 +171,7 @@ applyBid( sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2])); if (!ammSle) return {tecINTERNAL, false}; - STAmount const lptAMMBalance = get((*ammSle)[sfLPTokenBalance]); + STAmount const lptAMMBalance = (*ammSle)[sfLPTokenBalance]; auto const lpTokens = ammLPHolds(sb, *ammSle, account_, ctx_.journal); auto const& rules = ctx_.view().rules(); if (!rules.enabled(fixInnerObjTemplate)) @@ -257,8 +253,8 @@ applyBid( TER res = tesSUCCESS; - auto const bidMin = get(ctx_.tx[~sfBidMin]); - auto const bidMax = get(ctx_.tx[~sfBidMax]); + auto const bidMin = ctx_.tx[~sfBidMin]; + auto const bidMax = ctx_.tx[~sfBidMax]; auto getPayPrice = [&](Number const& computedPrice) -> Expected { @@ -307,7 +303,7 @@ applyBid( else { // Price the slot was purchased at. - STAmount const pricePurchased = get(auctionSlot[sfPrice]); + STAmount const pricePurchased = 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 46dd9e583ab..35c4563e4d3 100644 --- a/src/xrpld/app/tx/detail/AMMCreate.cpp +++ b/src/xrpld/app/tx/detail/AMMCreate.cpp @@ -41,8 +41,7 @@ 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]))) + if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) return temMPT_NOT_SUPPORTED; if (ctx.tx.getFlags() & tfUniversalMask) @@ -52,7 +51,7 @@ AMMCreate::preflight(PreflightContext const& ctx) } auto const amount = get(ctx.tx[sfAmount]); - auto const amount2 = get(ctx.tx[sfAmount2]); + auto const amount2 = ctx.tx[sfAmount2]; if (amount.issue() == amount2.issue()) { @@ -94,7 +93,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx) { auto const accountID = ctx.tx[sfAccount]; auto const amount = get(ctx.tx[sfAmount]); - auto const amount2 = get(ctx.tx[sfAmount2]); + auto const amount2 = ctx.tx[sfAmount2]; // Check if AMM already exists for the token pair if (auto const ammKeylet = keylet::amm(amount.issue(), amount2.issue()); @@ -213,7 +212,7 @@ applyCreate( beast::Journal j_) { auto const amount = get(ctx_.tx[sfAmount]); - auto const amount2 = get(ctx_.tx[sfAmount2]); + auto const amount2 = 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 43cc05c3168..89ce34052d2 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 = get((*ammSle)[sfLPTokenBalance]); + auto const lpTokensBalance = (*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 7f9b0c093e7..a83a0b101c0 100644 --- a/src/xrpld/app/tx/detail/AMMDeposit.cpp +++ b/src/xrpld/app/tx/detail/AMMDeposit.cpp @@ -41,13 +41,8 @@ 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; - } + if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[~sfAmount])) + return temMPT_NOT_SUPPORTED; auto const flags = ctx.tx.getFlags(); if (flags & tfDepositMask) @@ -57,9 +52,9 @@ AMMDeposit::preflight(PreflightContext const& ctx) } 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 amount2 = ctx.tx[~sfAmount2]; + auto const ePrice = ctx.tx[~sfEPrice]; + auto const lpTokens = ctx.tx[~sfLPTokenOut]; auto const tradingFee = ctx.tx[~sfTradingFee]; // Valid options for the flags are: // tfLPTokens: LPTokenOut, [Amount, Amount2] @@ -231,8 +226,7 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) auto balance = [&](auto const& deposit) -> TER { if (isXRP(deposit)) { - auto const lpIssue = - get((*ammSle)[sfLPTokenBalance]).issue(); + auto const lpIssue = (*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)); @@ -254,7 +248,7 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) }; auto const amount = get(ctx.tx[~sfAmount]); - auto const amount2 = get(ctx.tx[~sfAmount2]); + auto const amount2 = ctx.tx[~sfAmount2]; auto const ammAccountID = ammSle->getAccountID(sfAccount); auto checkAmount = [&](std::optional const& amount, @@ -322,7 +316,7 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) } // Equal deposit lp tokens - if (auto const lpTokens = get(ctx.tx[~sfLPTokenOut]); + if (auto const lpTokens = ctx.tx[~sfLPTokenOut]; lpTokens && lpTokens->issue() != lptAMMBalance.issue()) { JLOG(ctx.j.debug()) << "AMM Deposit: invalid LPTokens."; @@ -349,9 +343,9 @@ std::pair AMMDeposit::applyGuts(Sandbox& sb) { 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 const amount2 = ctx_.tx[~sfAmount2]; + auto const ePrice = ctx_.tx[~sfEPrice]; + auto const lpTokensDeposit = 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 dff94bf0d51..c4b6c612c63 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 = get((*ammSle)[sfLPTokenBalance]); + STAmount const lptAMMBalance = (*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 afc17690219..c6a70584a01 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/xrpld/app/tx/detail/AMMWithdraw.cpp @@ -41,13 +41,8 @@ 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; - } + if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[~sfAmount])) + return temMPT_NOT_SUPPORTED; auto const flags = ctx.tx.getFlags(); if (flags & tfWithdrawMask) @@ -57,9 +52,9 @@ AMMWithdraw::preflight(PreflightContext const& ctx) } 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]); + auto const amount2 = ctx.tx[~sfAmount2]; + auto const ePrice = ctx.tx[~sfEPrice]; + auto const lpTokens = ctx.tx[~sfLPTokenIn]; // Valid combinations are: // LPTokens // tfWithdrawAll @@ -190,7 +185,7 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx) } auto const amount = get(ctx.tx[~sfAmount]); - auto const amount2 = get(ctx.tx[~sfAmount2]); + auto const amount2 = ctx.tx[~sfAmount2]; auto const expected = ammHolds( ctx.view, @@ -261,8 +256,8 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx) auto const lpTokens = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j); - auto const lpTokensWithdraw = tokensWithdraw( - lpTokens, get(ctx.tx[~sfLPTokenIn]), ctx.tx.getFlags()); + auto const lpTokensWithdraw = + tokensWithdraw(lpTokens, ctx.tx[~sfLPTokenIn], ctx.tx.getFlags()); if (lpTokens <= beast::zero) { @@ -282,7 +277,7 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx) return tecAMM_INVALID_TOKENS; } - if (auto const ePrice = get(ctx.tx[~sfEPrice]); + if (auto const ePrice = ctx.tx[~sfEPrice]; ePrice && ePrice->issue() != lpTokens.issue()) { JLOG(ctx.j.debug()) << "AMM Withdraw: invalid EPrice."; @@ -304,8 +299,8 @@ std::pair AMMWithdraw::applyGuts(Sandbox& sb) { auto const amount = get(ctx_.tx[~sfAmount]); - auto const amount2 = get(ctx_.tx[~sfAmount2]); - auto const ePrice = get(ctx_.tx[~sfEPrice]); + auto const amount2 = ctx_.tx[~sfAmount2]; + auto const ePrice = ctx_.tx[~sfEPrice]; auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2])); if (!ammSle) return {tecINTERNAL, false}; // LCOV_EXCL_LINE @@ -315,8 +310,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, get(ctx_.tx[~sfLPTokenIn]), ctx_.tx.getFlags()); + auto const lpTokensWithdraw = + tokensWithdraw(lpTokens, ctx_.tx[~sfLPTokenIn], ctx_.tx.getFlags()); // Due to rounding, the LPTokenBalance of the last LP // might not match the LP's trustline balance @@ -330,7 +325,7 @@ AMMWithdraw::applyGuts(Sandbox& sb) { if (withinRelativeDistance( lpTokens, - get(ammSle->getFieldAmount(sfLPTokenBalance)), + 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 4e537432053..37b9c1348b9 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -42,9 +42,8 @@ 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.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[~sfAmount])) + return temMALFORMED; if (ctx.tx.getFlags() & tfUniversalMask) { @@ -55,7 +54,7 @@ CashCheck::preflight(PreflightContext const& ctx) // Exactly one of Amount or DeliverMin must be present. auto const optAmount = get(ctx.tx[~sfAmount]); - auto const optDeliverMin = get(ctx.tx[~sfDeliverMin]); + auto const optDeliverMin = ctx.tx[~sfDeliverMin]; if (static_cast(optAmount) == static_cast(optDeliverMin)) { @@ -141,10 +140,10 @@ CashCheck::preclaim(PreclaimContext const& ctx) // Make sure the requested amount is reasonable. STAmount const value{[](STTx const& tx) { auto const optAmount = get(tx[~sfAmount]); - return optAmount ? *optAmount : get(tx[sfDeliverMin]); + return optAmount ? *optAmount : tx[sfDeliverMin]; }(ctx.tx)}; - STAmount const sendMax = get(sleCheck->at(sfSendMax)); + STAmount const sendMax = sleCheck->at(sfSendMax); Currency const currency{value.getCurrency()}; if (currency != sendMax.getCurrency()) { @@ -287,12 +286,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 = get(ctx_.tx[~sfDeliverMin]); + auto const optDeliverMin = ctx_.tx[~sfDeliverMin]; bool const doFix1623{psb.rules().enabled(fix1623)}; if (srcId != account_) { - STAmount const sendMax = get(sleCheck->at(sfSendMax)); + STAmount const sendMax = sleCheck->at(sfSendMax); // Flow() doesn't do XRP to XRP transfers. if (sendMax.native()) @@ -424,7 +423,7 @@ CashCheck::doApply() return tecNO_LINE; SF_AMOUNT const& tweakedLimit = destLow ? sfLowLimit : sfHighLimit; - STEitherAmount const savedLimit = sleTrustLine->at(tweakedLimit); + STAmount const savedLimit = sleTrustLine->at(tweakedLimit); // Make sure the tweaked limits are restored when we leave scope. scope_exit fixup( @@ -439,7 +438,7 @@ CashCheck::doApply() // while flow runs. STAmount const bigAmount( trustLineIssue, STAmount::cMaxValue, STAmount::cMaxOffset); - sleTrustLine->at(tweakedLimit) = STEitherAmount{bigAmount}; + sleTrustLine->at(tweakedLimit) = bigAmount; } // Let flow() do the heavy lifting on a check for an IOU. @@ -454,7 +453,7 @@ CashCheck::doApply() true, // owner pays transfer fee OfferCrossing::no, std::nullopt, - get(sleCheck->getFieldAmount(sfSendMax)), + 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 56d005f7429..909f35fc799 100644 --- a/src/xrpld/app/tx/detail/Change.cpp +++ b/src/xrpld/app/tx/detail/Change.cpp @@ -38,11 +38,6 @@ 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) { @@ -51,7 +46,7 @@ Change::preflight(PreflightContext const& ctx) } // No point in going any further if the transaction fee is malformed. - auto const fee = get(ctx.tx.getFieldAmount(sfFee)); + auto const fee = ctx.tx.getFieldAmount(sfFee); if (!fee.native() || fee != beast::zero) { JLOG(ctx.j.warn()) << "Change: invalid fee"; @@ -186,8 +181,8 @@ Change::activateTrustLinesToSelfFix() return true; } - auto const& lo = get(tl->getFieldAmount(sfLowLimit)); - auto const& hi = get(tl->getFieldAmount(sfHighLimit)); + auto const& lo = tl->getFieldAmount(sfLowLimit); + auto const& hi = 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 d2cb619a84b..8dab279f29a 100644 --- a/src/xrpld/app/tx/detail/Clawback.cpp +++ b/src/xrpld/app/tx/detail/Clawback.cpp @@ -29,7 +29,7 @@ namespace ripple { template -NotTEC +static NotTEC preflightHelper(PreflightContext const& ctx) { if (!ctx.rules.enabled(featureClawback)) @@ -78,7 +78,7 @@ preflightHelper(PreflightContext const& ctx) } template -TER +static TER preclaimHelper(PreclaimContext const& ctx) { AccountID const issuer = ctx.tx[sfAccount]; @@ -137,7 +137,7 @@ preclaimHelper(PreclaimContext const& ctx) if (!sleRippleState) return tecNO_LINE; - STAmount const& balance = get((*sleRippleState)[sfBalance]); + STAmount const& balance = (*sleRippleState)[sfBalance]; // If balance is positive, issuer must have higher address than holder if (balance > beast::zero && issuer < holder) @@ -166,7 +166,7 @@ preclaimHelper(PreclaimContext const& ctx) } template -TER +static TER applyHelper(ApplyContext& ctx) { AccountID const& issuer = ctx.tx[sfAccount]; diff --git a/src/xrpld/app/tx/detail/CreateCheck.cpp b/src/xrpld/app/tx/detail/CreateCheck.cpp index 46e38ee2976..2fa1f323339 100644 --- a/src/xrpld/app/tx/detail/CreateCheck.cpp +++ b/src/xrpld/app/tx/detail/CreateCheck.cpp @@ -55,7 +55,7 @@ CreateCheck::preflight(PreflightContext const& ctx) } { - STAmount const sendMax{get(ctx.tx.getFieldAmount(sfSendMax))}; + STAmount const sendMax{ctx.tx.getFieldAmount(sfSendMax)}; if (!isLegalNet(sendMax) || sendMax.signum() <= 0) { JLOG(ctx.j.warn()) << "Malformed transaction: bad sendMax amount: " @@ -113,7 +113,7 @@ CreateCheck::preclaim(PreclaimContext const& ctx) } { - STAmount const sendMax{get(ctx.tx[sfSendMax])}; + STAmount const sendMax{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 5c5ed56dbf1..7a6a723bac8 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{get(tx[sfTakerGets])}; + auto const& amount{tx[sfTakerGets]}; return amount.native() ? amount.xrp() : beast::zero; }; @@ -84,8 +84,8 @@ CreateOffer::preflight(PreflightContext const& ctx) return temBAD_SEQUENCE; } - STAmount saTakerPays = get(tx[sfTakerPays]); - STAmount saTakerGets = get(tx[sfTakerGets]); + STAmount saTakerPays = tx[sfTakerPays]; + STAmount saTakerGets = tx[sfTakerGets]; if (!isLegalNet(saTakerPays) || !isLegalNet(saTakerGets)) return temBAD_AMOUNT; @@ -134,8 +134,8 @@ CreateOffer::preclaim(PreclaimContext const& ctx) { auto const id = ctx.tx[sfAccount]; - auto saTakerPays = get(ctx.tx[sfTakerPays]); - auto saTakerGets = get(ctx.tx[sfTakerGets]); + auto saTakerPays = ctx.tx[sfTakerPays]; + auto saTakerGets = ctx.tx[sfTakerGets]; auto const& uPaysIssuerID = saTakerPays.getIssuer(); auto const& uPaysCurrency = saTakerPays.getCurrency(); @@ -928,8 +928,8 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) bool const bFillOrKill(uTxFlags & tfFillOrKill); bool const bSell(uTxFlags & tfSell); - auto saTakerPays = get(ctx_.tx[sfTakerPays]); - auto saTakerGets = get(ctx_.tx[sfTakerGets]); + auto saTakerPays = ctx_.tx[sfTakerPays]; + auto saTakerGets = 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 c772f77223b..6e5a3108c72 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 = get((*sleAccount)[sfBalance]).xrp(); + auto const balance = STAmount((*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 80136cca832..fb2f3fc507f 100644 --- a/src/xrpld/app/tx/detail/DeleteAccount.cpp +++ b/src/xrpld/app/tx/detail/DeleteAccount.cpp @@ -347,10 +347,8 @@ DeleteAccount::doApply() return ter; // Transfer any XRP remaining after the fee is paid to the destination: - (*dst)[sfBalance] = - STEitherAmount{get((*dst)[sfBalance]) + mSourceBalance}; - (*src)[sfBalance] = - STEitherAmount{get((*src)[sfBalance]) - mSourceBalance}; + (*dst)[sfBalance] = (*dst)[sfBalance] + mSourceBalance; + (*src)[sfBalance] = (*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 3d45095954b..bcf187262c5 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -212,7 +212,7 @@ EscrowCreate::doApply() // Check reserve and funds availability { - auto const balance = get((*sle)[sfBalance]).xrp(); + auto const balance = STAmount((*sle)[sfBalance]).xrp(); auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1); @@ -276,8 +276,7 @@ EscrowCreate::doApply() } // Deduct owner's balance, increment owner count - (*sle)[sfBalance] = STEitherAmount{ - get((*sle)[sfBalance]) - get(ctx_.tx[sfAmount])}; + (*sle)[sfBalance] = (*sle)[sfBalance] - get(ctx_.tx[sfAmount]); adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); ctx_.view().update(sle); @@ -497,8 +496,7 @@ EscrowFinish::doApply() } // Transfer amount to destination - (*sled)[sfBalance] = STEitherAmount{ - get((*sled)[sfBalance]) + get((*slep)[sfAmount])}; + (*sled)[sfBalance] = (*sled)[sfBalance] + get((*slep)[sfAmount]); ctx_.view().update(sled); // Adjust source owner count @@ -584,8 +582,7 @@ EscrowCancel::doApply() // Transfer amount back to owner, decrement owner count auto const sle = ctx_.view().peek(keylet::account(account)); - (*sle)[sfBalance] = STEitherAmount{ - get((*sle)[sfBalance]) + get((*slep)[sfAmount])}; + (*sle)[sfBalance] = (*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 bb23bbc4f8a..a19f4517343 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 > get(tx.getFieldAmount(sfFee)).xrp()) + if (fee > tx.getFieldAmount(sfFee).xrp()) { JLOG(j.fatal()) << "Invariant failed: fee paid is " << fee.drops() << " exceeds fee specified in transaction."; @@ -98,13 +98,13 @@ XRPNotCreated::visitEntry( switch (before->getType()) { case ltACCOUNT_ROOT: - drops_ -= get((*before)[sfBalance]).xrp().drops(); + drops_ -= (*before)[sfBalance].xrp().drops(); break; case ltPAYCHAN: - drops_ -= (get((*before)[sfAmount]) - - get((*before)[sfBalance])) - .xrp() - .drops(); + drops_ -= + (get((*before)[sfAmount]) - (*before)[sfBalance]) + .xrp() + .drops(); break; case ltESCROW: drops_ -= get((*before)[sfAmount]).xrp().drops(); @@ -119,12 +119,12 @@ XRPNotCreated::visitEntry( switch (after->getType()) { case ltACCOUNT_ROOT: - drops_ += get((*after)[sfBalance]).xrp().drops(); + drops_ += (*after)[sfBalance].xrp().drops(); break; case ltPAYCHAN: if (!isDelete) drops_ += (get((*after)[sfAmount]) - - get((*after)[sfBalance])) + (*after)[sfBalance]) .xrp() .drops(); break; @@ -193,10 +193,10 @@ XRPBalanceChecks::visitEntry( }; if (before && before->getType() == ltACCOUNT_ROOT) - bad_ |= isBad(get((*before)[sfBalance])); + bad_ |= isBad((*before)[sfBalance]); if (after && after->getType() == ltACCOUNT_ROOT) - bad_ |= isBad(get((*after)[sfBalance])); + bad_ |= isBad((*after)[sfBalance]); } bool @@ -237,14 +237,10 @@ NoBadOffers::visitEntry( }; if (before && before->getType() == ltOFFER) - bad_ |= isBad( - get((*before)[sfTakerPays]), - get((*before)[sfTakerGets])); + bad_ |= isBad((*before)[sfTakerPays], (*before)[sfTakerGets]); if (after && after->getType() == ltOFFER) - bad_ |= isBad( - get((*after)[sfTakerPays]), - get((*after)[sfTakerGets])); + bad_ |= isBad((*after)[sfTakerPays], (*after)[sfTakerGets]); } bool @@ -448,10 +444,8 @@ NoXRPTrustLines::visitEntry( // relying on .native() just in case native somehow // were systematically incorrect xrpTrustLine_ = - get(after->getFieldAmount(sfLowLimit)).issue() == - xrpIssue() || - get(after->getFieldAmount(sfHighLimit)).issue() == - xrpIssue(); + after->getFieldAmount(sfLowLimit).issue() == xrpIssue() || + after->getFieldAmount(sfHighLimit).issue() == xrpIssue(); } } diff --git a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp index 782d8d5a23b..258a96a412d 100644 --- a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp @@ -36,10 +36,6 @@ 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; @@ -52,7 +48,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 = get(ctx.tx[~sfNFTokenBrokerFee])) + if (auto const bf = ctx.tx[~sfNFTokenBrokerFee]) { if (!bo || !so) return temMALFORMED; @@ -155,7 +151,7 @@ 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 = get(ctx.tx[~sfNFTokenBrokerFee])) + if (auto const brokerFee = ctx.tx[~sfNFTokenBrokerFee]) { if (brokerFee->issue() != get((*bo)[sfAmount]).issue()) return tecNFTOKEN_BUY_SELL_MISMATCH; @@ -368,8 +364,7 @@ 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 = - get(sleBuyer->getFieldAmount(sfBalance)); + auto const buyerBalance = sleBuyer->getFieldAmount(sfBalance); auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount); if (buyerOwnerCountAfter > buyerOwnerCountBefore) @@ -472,7 +467,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 = get(ctx_.tx[~sfNFTokenBrokerFee]); + if (auto const cut = 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/Offer.h b/src/xrpld/app/tx/detail/Offer.h index 27dd4fd5fe0..a6f707ba561 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 = get(m_entry->getFieldAmount(sfTakerPays)); - auto const tg = get(m_entry->getFieldAmount(sfTakerGets)); + auto const tp = m_entry->getFieldAmount(sfTakerPays); + auto const tg = 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( - get(m_entry->getFieldAmount(sfTakerPays)), - get(m_entry->getFieldAmount(sfTakerGets))) + m_entry->getFieldAmount(sfTakerPays), + m_entry->getFieldAmount(sfTakerGets)) { } diff --git a/src/xrpld/app/tx/detail/PayChan.cpp b/src/xrpld/app/tx/detail/PayChan.cpp index c7f20093302..e3a2e02e02d 100644 --- a/src/xrpld/app/tx/detail/PayChan.cpp +++ b/src/xrpld/app/tx/detail/PayChan.cpp @@ -149,11 +149,9 @@ closeChannel( if (!sle) return tefINTERNAL; - assert( - get((*slep)[sfAmount]) >= get((*slep)[sfBalance])); - (*sle)[sfBalance] = STEitherAmount{ - get((*sle)[sfBalance]) + get((*slep)[sfAmount]) - - get((*slep)[sfBalance])}; + assert(get((*slep)[sfAmount]) >= (*slep)[sfBalance]); + (*sle)[sfBalance] = (*sle)[sfBalance] + get((*slep)[sfAmount]) - + (*slep)[sfBalance]; adjustOwnerCount(view, sle, -1, j); view.update(sle); @@ -202,7 +200,7 @@ PayChanCreate::preclaim(PreclaimContext const& ctx) // Check reserve and funds availability { - auto const balance = get((*sle)[sfBalance]); + auto const balance = (*sle)[sfBalance]; auto const reserve = ctx.view.fees().accountReserve((*sle)[sfOwnerCount] + 1); @@ -265,7 +263,7 @@ PayChanCreate::doApply() // Funds held in this channel (*slep)[sfAmount] = ctx_.tx[sfAmount]; // Amount channel has already paid - (*slep)[sfBalance] = ctx_.tx[sfAmount].zeroed(); + (*slep)[sfBalance] = get(ctx_.tx[sfAmount]).zeroed(); (*slep)[sfAccount] = account; (*slep)[sfDestination] = dst; (*slep)[sfSettleDelay] = ctx_.tx[sfSettleDelay]; @@ -298,8 +296,7 @@ PayChanCreate::doApply() } // Deduct owner's balance, increment owner count - (*sle)[sfBalance] = STEitherAmount{ - get((*sle)[sfBalance]) - get(ctx_.tx[sfAmount])}; + (*sle)[sfBalance] = (*sle)[sfBalance] - get(ctx_.tx[sfAmount]); adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); ctx_.view().update(sle); @@ -376,7 +373,7 @@ PayChanFund::doApply() { // Check reserve and funds availability - auto const balance = get((*sle)[sfBalance]); + auto const balance = (*sle)[sfBalance]; auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount]); @@ -398,8 +395,7 @@ PayChanFund::doApply() get((*slep)[sfAmount]) + get(ctx_.tx[sfAmount])}; ctx_.view().update(slep); - (*sle)[sfBalance] = STEitherAmount{ - get((*sle)[sfBalance]) - get(ctx_.tx[sfAmount])}; + (*sle)[sfBalance] = (*sle)[sfBalance] - get(ctx_.tx[sfAmount]); ctx_.view().update(sle); return tesSUCCESS; @@ -413,7 +409,7 @@ PayChanClaim::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - auto const bal = get(ctx.tx[~sfBalance]); + auto const bal = ctx.tx[~sfBalance]; if (bal && (!isXRP(*bal) || *bal <= beast::zero)) return temBAD_AMOUNT; @@ -491,11 +487,10 @@ PayChanClaim::doApply() if (ctx_.tx[~sfBalance]) { - auto const chanBalance = - get(slep->getFieldAmount(sfBalance)).xrp(); + auto const chanBalance = slep->getFieldAmount(sfBalance).xrp(); auto const chanFunds = get(slep->getFieldAmount(sfAmount)).xrp(); - auto const reqBalance = get(ctx_.tx[sfBalance]).xrp(); + auto const reqBalance = ctx_.tx[sfBalance].xrp(); if (txAccount == dst && !ctx_.tx[~sfSignature]) return temBAD_SIGNATURE; @@ -542,8 +537,7 @@ PayChanClaim::doApply() (*slep)[sfBalance] = ctx_.tx[sfBalance]; XRPAmount const reqDelta = reqBalance - chanBalance; assert(reqDelta >= beast::zero); - (*sled)[sfBalance] = - STEitherAmount{get((*sled)[sfBalance]) + reqDelta}; + (*sled)[sfBalance] = (*sled)[sfBalance] + reqDelta; ctx_.view().update(sled); ctx_.view().update(slep); } @@ -560,8 +554,7 @@ PayChanClaim::doApply() { // Channel will close immediately if dry or the receiver closes if (dst == txAccount || - get((*slep)[sfBalance]) == - get((*slep)[sfAmount])) + (*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 113df99eedb..ea18e79e935 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -28,17 +28,17 @@ namespace ripple { -template -TxConsequences +template +static TxConsequences makeTxConsequencesHelper(PreflightContext const& ctx); template <> TxConsequences -makeTxConsequencesHelper(PreflightContext const& ctx) +makeTxConsequencesHelper(PreflightContext const& ctx) { auto calculateMaxXRPSpend = [](STTx const& tx) -> XRPAmount { auto const maxAmount = tx.isFieldPresent(sfSendMax) - ? get(tx[sfSendMax]) + ? tx[sfSendMax] : get(tx[sfAmount]); // If there's no sfSendMax in XRP, and the sfAmount isn't @@ -49,62 +49,57 @@ makeTxConsequencesHelper(PreflightContext const& ctx) return TxConsequences{ctx.tx, calculateMaxXRPSpend(ctx.tx)}; } -template +template <> TxConsequences -makeTxConsequencesHelper(PreflightContext const& ctx) +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 +template +static TDel getMaxSourceAmount( TDel const& dstAmount, - std::optional const& max, + std::optional const& max, AccountID const& account) { - if (max) - return *max; - - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { + return dstAmount; + } + else + { + if (max) + return *max; + if (isNative(dstAmount)) return dstAmount; else - return getMaxAmount(dstAmount, account); + return STAmount( + {dstAmount.getCurrency(), account}, + dstAmount.mantissa(), + dstAmount.exponent(), + dstAmount < beast::zero); } - else - Throw("Invalid sfAmount and sfSendMax combination"); } -template -NotTEC +template +static NotTEC 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]))) + if (!ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) return temDISABLED; + if constexpr (std::is_same_v) + { + if (ctx.tx.isFieldPresent(sfDeliverMin) || + ctx.tx.isFieldPresent(sfSendMax)) + return temMALFORMED; + } + auto& tx = ctx.tx; auto& j = ctx.j; @@ -127,8 +122,8 @@ preflightHelper(PreflightContext const& ctx) auto const account = tx.getAccountID(sfAccount); - auto const maxSourceAmount = getMaxSourceAmount( - saDstAmount, get(tx[~sfSendMax]), account); + auto const maxSourceAmount = + getMaxSourceAmount(saDstAmount, tx[~sfSendMax], account); auto const& uSrcCurrency = maxSourceAmount.getCurrency(); auto const& uDstCurrency = saDstAmount.getCurrency(); @@ -221,7 +216,7 @@ preflightHelper(PreflightContext const& ctx) return temBAD_SEND_XRP_NO_DIRECT; } - auto const deliverMin = get(ctx.tx[~sfDeliverMin]); + auto const deliverMin = ctx.tx[~sfDeliverMin]; if (deliverMin) { if (!partialPaymentAllowed) @@ -240,28 +235,32 @@ preflightHelper(PreflightContext const& ctx) << " amount. " << dMin.getFullText(); return temBAD_AMOUNT; } - if (dMin.issue() != saDstAmount.issue()) - { - JLOG(j.trace()) - << "Malformed transaction: Dst issue differs " - "from " - << jss::DeliverMin.c_str() << ". " << dMin.getFullText(); - return temBAD_AMOUNT; - } - if (dMin > saDstAmount) + // DeliverMin is invalid if MPT and is always the same type as dstAmount + if constexpr (std::is_same_v) { - JLOG(j.trace()) - << "Malformed transaction: Dst amount less than " - << jss::DeliverMin.c_str() << ". " << dMin.getFullText(); - return temBAD_AMOUNT; + if (dMin.issue() != saDstAmount.issue()) + { + JLOG(j.trace()) + << "Malformed transaction: Dst issue differs " + "from " + << jss::DeliverMin.c_str() << ". " << dMin.getFullText(); + return temBAD_AMOUNT; + } + if (dMin > saDstAmount) + { + JLOG(j.trace()) + << "Malformed transaction: Dst amount less than " + << jss::DeliverMin.c_str() << ". " << dMin.getFullText(); + return temBAD_AMOUNT; + } } } return preflight2(ctx); } -template -TER +template +static TER preclaimHelper( PreclaimContext const& ctx, std::size_t maxPathSize, @@ -271,7 +270,7 @@ preclaimHelper( std::uint32_t const uTxFlags = ctx.tx.getFlags(); bool const partialPaymentAllowed = uTxFlags & tfPartialPayment; auto const paths = ctx.tx.isFieldPresent(sfPaths); - auto const sendMax = get(ctx.tx[~sfSendMax]); + auto const sendMax = ctx.tx[~sfSendMax]; AccountID const uDstAccountID(ctx.tx[sfDestination]); TDel const saDstAmount(get(ctx.tx.getFieldAmount(sfAmount))); @@ -360,14 +359,14 @@ preclaimHelper( return tesSUCCESS; } -template -TER +template +static TER applyHelper( ApplyContext& ctx, XRPAmount const& priorBalance, XRPAmount const& sourceBalance) { - auto const deliverMin = get(ctx.tx[~sfDeliverMin]); + auto const deliverMin = ctx.tx[~sfDeliverMin]; // Ripple if source or destination is non-native or if there are paths. std::uint32_t const uTxFlags = ctx.tx.getFlags(); @@ -375,13 +374,13 @@ applyHelper( bool const limitQuality = uTxFlags & tfLimitQuality; bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect); auto const paths = ctx.tx.isFieldPresent(sfPaths); - auto const sendMax = get(ctx.tx[~sfSendMax]); + auto const sendMax = ctx.tx[~sfSendMax]; auto const account = ctx.tx[sfAccount]; AccountID const uDstAccountID(ctx.tx.getAccountID(sfDestination)); auto const saDstAmount(get(ctx.tx.getFieldAmount(sfAmount))); auto const maxSourceAmount = - getMaxSourceAmount(saDstAmount, sendMax, account); + getMaxSourceAmount(saDstAmount, sendMax, account); JLOG(ctx.journal.trace()) << "maxSourceAmount=" << maxSourceAmount.getFullText() @@ -421,15 +420,14 @@ applyHelper( ctx.view().rules().enabled(featureDepositPreauth); bool const bRipple = - paths || sendMax || !(isNative(saDstAmount) || isMPT(saDstAmount)); + (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 constexpr ( - std::is_same_v && std::is_same_v) + if constexpr (std::is_same_v) { if (bRipple) { @@ -497,8 +495,7 @@ applyHelper( return terResult; } } - else if constexpr ( - std::is_same_v && std::is_same_v) + else { if (auto const ter = requireAuth(ctx.view(), saDstAmount.issue(), account); @@ -518,9 +515,9 @@ applyHelper( 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 + // - can't send between holders + // - holder can send back to issuer + // - issuer can send to holder if (account != issuer && uDstAccountID != issuer && (isFrozen(ctx.view(), account, mpt) || isFrozen(ctx.view(), uDstAccountID, mpt))) @@ -553,8 +550,7 @@ applyHelper( // 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()); + auto const mmm = std::max(reserve, ctx.tx.getFieldAmount(sfFee).xrp()); if (priorBalance < saDstAmount.xrp() + mmm) { @@ -607,8 +603,7 @@ applyHelper( ctx.view().fees().accountReserve(0)}; if (saDstAmount > dstReserve || - get(sleDst->getFieldAmount(sfBalance)) > - dstReserve) + sleDst->getFieldAmount(sfBalance) > dstReserve) return tecNO_PERMISSION; } } @@ -617,14 +612,13 @@ applyHelper( // Do the arithmetic for the transfer and make the ledger change. sleSrc->setFieldAmount(sfBalance, sourceBalance - saDstAmount); sleDst->setFieldAmount( - sfBalance, - get(sleDst->getFieldAmount(sfBalance)) + saDstAmount); + sfBalance, sleDst->getFieldAmount(sfBalance) + saDstAmount); // 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) + else { Throw("Payment: expected native amount"); } @@ -636,44 +630,38 @@ TxConsequences Payment::makeTxConsequences(PreflightContext const& ctx) { return std::visit( - [&](TDel const&, TMax const&) { - return makeTxConsequencesHelper(ctx); + [&](TDel const&) { + return makeTxConsequencesHelper(ctx); }, - ctx.tx[sfAmount].getValue(), - ctx.tx[~sfSendMax].value_or(ctx.tx[sfAmount]).getValue()); + 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()); + [&](TDel const&) { return preflightHelper(ctx); }, + ctx.tx[sfAmount].getValue()); } TER Payment::preclaim(PreclaimContext const& ctx) { return std::visit( - [&](TDel const&, TMax const&) { - return preclaimHelper(ctx, MaxPathSize, MaxPathLength); + [&](TDel const&) { + return preclaimHelper(ctx, MaxPathSize, MaxPathLength); }, - ctx.tx[sfAmount].getValue(), - ctx.tx[~sfSendMax].value_or(ctx.tx[sfAmount]).getValue()); + ctx.tx[sfAmount].getValue()); } TER Payment::doApply() { return std::visit( - [&](TDel const&, TMax const&) { - return applyHelper(ctx_, mPriorBalance, mSourceBalance); + [&](TDel const&) { + return applyHelper(ctx_, mPriorBalance, mSourceBalance); }, - ctx_.tx[sfAmount].getValue(), - ctx_.tx[~sfSendMax].value_or(ctx_.tx[sfAmount]).getValue()); + 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 4d8ddaa6e5b..055143cc6fd 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 = get(sleSetter->getFieldAmount(sfBalance)); + auto const& balance = 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 173ed35003d..954fc6543f1 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -34,9 +34,6 @@ 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; @@ -48,8 +45,7 @@ SetTrust::preflight(PreflightContext const& ctx) return temINVALID_FLAG; } - STAmount const saLimitAmount( - get(tx.getFieldAmount(sfLimitAmount))); + STAmount const saLimitAmount(tx.getFieldAmount(sfLimitAmount)); if (!isLegalNet(saLimitAmount)) return temBAD_AMOUNT; @@ -104,7 +100,7 @@ SetTrust::preclaim(PreclaimContext const& ctx) return tefNO_AUTH_REQUIRED; } - auto const saLimitAmount = get(ctx.tx[sfLimitAmount]); + auto const saLimitAmount = ctx.tx[sfLimitAmount]; auto const currency = saLimitAmount.getCurrency(); auto const uDstAccountID = saLimitAmount.getIssuer(); @@ -175,7 +171,7 @@ SetTrust::preclaim(PreclaimContext const& ctx) ctx.view.read({ltAMM, sleDst->getFieldH256(sfAMMID)})) { if (auto const lpTokens = - get(ammSle->getFieldAmount(sfLPTokenBalance)); + ammSle->getFieldAmount(sfLPTokenBalance); lpTokens == beast::zero) return tecAMM_EMPTY; else if (lpTokens.getCurrency() != saLimitAmount.getCurrency()) @@ -194,8 +190,7 @@ SetTrust::doApply() { TER terResult = tesSUCCESS; - STAmount const saLimitAmount( - get(ctx_.tx.getFieldAmount(sfLimitAmount))); + STAmount const saLimitAmount(ctx_.tx.getFieldAmount(sfLimitAmount)); bool const bQualityIn(ctx_.tx.isFieldPresent(sfQualityIn)); bool const bQualityOut(ctx_.tx.isFieldPresent(sfQualityOut)); @@ -299,7 +294,7 @@ SetTrust::doApply() // Balances // - saLowBalance = get(sleRippleState->getFieldAmount(sfBalance)); + saLowBalance = sleRippleState->getFieldAmount(sfBalance); saHighBalance = -saLowBalance; // @@ -309,12 +304,10 @@ SetTrust::doApply() sleRippleState->setFieldAmount( !bHigh ? sfLowLimit : sfHighLimit, saLimitAllow); - saLowLimit = !bHigh - ? saLimitAllow - : get(sleRippleState->getFieldAmount(sfLowLimit)); - saHighLimit = bHigh - ? saLimitAllow - : get(sleRippleState->getFieldAmount(sfHighLimit)); + saLowLimit = + !bHigh ? saLimitAllow : sleRippleState->getFieldAmount(sfLowLimit); + saHighLimit = + bHigh ? saLimitAllow : sleRippleState->getFieldAmount(sfHighLimit); // // Quality in diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 4889e51d9c3..42e9f0677ab 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 = get(ctx.tx.getFieldAmount(sfFee)); + auto const fee = 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 = get(ctx.tx[sfFee]).xrp(); + auto const feePaid = 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 = get((*sle)[sfBalance]).xrp(); + auto const balance = (*sle)[sfBalance].xrp(); if (balance < feePaid) { @@ -245,7 +245,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) TER Transactor::payFee() { - auto const feePaid = get(ctx_.tx[sfFee]).xrp(); + auto const feePaid = ctx_.tx[sfFee].xrp(); auto const sle = view().peek(keylet::account(account_)); if (!sle) @@ -457,7 +457,7 @@ Transactor::apply() if (sle) { - mPriorBalance = STAmount{get((*sle)[sfBalance])}.xrp(); + mPriorBalance = STAmount{(*sle)[sfBalance]}.xrp(); mSourceBalance = mPriorBalance; TER result = consumeSeqProxy(sle); @@ -799,8 +799,7 @@ 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 = - get(txnAcct->getFieldAmount(sfBalance)).xrp(); + auto const balance = txnAcct->getFieldAmount(sfBalance).xrp(); // balance should have already been checked in checkFee / preFlight. assert(balance != beast::zero && (!view().open() || balance >= fee)); @@ -873,7 +872,7 @@ Transactor::operator()() stream << "preclaim result: " << transToken(result); bool applied = isTesSuccess(result); - auto fee = get(ctx_.tx.getFieldAmount(sfFee)).xrp(); + auto fee = ctx_.tx.getFieldAmount(sfFee).xrp(); if (ctx_.size() > oversizeMetaDataCap) result = tecOVERSIZE; @@ -922,8 +921,8 @@ Transactor::operator()() assert(before && after); if (doOffers && before && after && (before->getType() == ltOFFER) && - (get(before->getFieldAmount(sfTakerPays)) == - get(after->getFieldAmount(sfTakerPays)))) + (before->getFieldAmount(sfTakerPays) == + 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 0e0ab3079aa..9b45f692274 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 = get((*sleSrc)[sfBalance]); + STAmount const curBal = (*sleSrc)[sfBalance]; // Checking that account == src and postFeeBalance == curBal is // not strictly nessisary, but helps protect against future // changes @@ -488,10 +488,8 @@ transferHelper( psb.insert(sleDst); } - (*sleSrc)[sfBalance] = - STEitherAmount{get((*sleSrc)[sfBalance]) - amt}; - (*sleDst)[sfBalance] = STEitherAmount{ - sfBalance, get((*sleDst)[sfBalance]) + amt}; + (*sleSrc)[sfBalance] = (*sleSrc)[sfBalance] - amt; + (*sleDst)[sfBalance] = STAmount{sfBalance, (*sleDst)[sfBalance] + amt}; psb.update(sleSrc); psb.update(sleDst); @@ -935,7 +933,7 @@ applyClaimAttestations( return ScopeResult{ newAttResult, - get((*sleClaimID)[sfSignatureReward]), + (*sleClaimID)[sfSignatureReward], (*sleClaimID)[sfAccount]}; }(); @@ -1060,7 +1058,7 @@ applyCreateAccountAttestations( return Unexpected(tecINTERNAL); // Check reserve - auto const balance = get((*sleDoor)[sfBalance]); + auto const balance = (*sleDoor)[sfBalance]; auto const reserve = psb.fees().accountReserve((*sleDoor)[sfOwnerCount] + 1); @@ -1216,8 +1214,7 @@ 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]))) + if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) return temMPT_NOT_SUPPORTED; if (ctx.tx.getFlags() & tfUniversalMask) @@ -1387,11 +1384,6 @@ 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; @@ -1507,7 +1499,7 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx) if (!sleAcc) return terNO_ACCOUNT; - auto const balance = get((*sleAcc)[sfBalance]); + auto const balance = (*sleAcc)[sfBalance]; auto const reserve = ctx.view.fees().accountReserve((*sleAcc)[sfOwnerCount] + 1); @@ -1573,11 +1565,6 @@ 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; @@ -1871,7 +1858,7 @@ XChainClaim::doApply() (*sleClaimID)[sfAccount], sendingAmount, srcChain, - get((*sleClaimID)[sfSignatureReward]), + (*sleClaimID)[sfSignatureReward], }; }(); @@ -2089,7 +2076,7 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx) if (!sleAcc) return terNO_ACCOUNT; - auto const balance = get((*sleAcc)[sfBalance]); + auto const balance = (*sleAcc)[sfBalance]; auto const reserve = ctx.view.fees().accountReserve((*sleAcc)[sfOwnerCount] + 1); @@ -2219,7 +2206,7 @@ XChainCreateAccountCommit::preflight(PreflightContext const& ctx) if (amount.signum() <= 0 || !amount.native()) return temBAD_AMOUNT; - auto const reward = get(ctx.tx[sfSignatureReward]); + auto const reward = ctx.tx[sfSignatureReward]; if (reward.signum() < 0 || !reward.native()) return temBAD_AMOUNT; @@ -2234,7 +2221,7 @@ XChainCreateAccountCommit::preclaim(PreclaimContext const& ctx) { STXChainBridge const bridgeSpec = ctx.tx[sfXChainBridge]; STAmount const amount = get(ctx.tx[sfAmount]); - STEitherAmount const reward = ctx.tx[sfSignatureReward]; + STAmount const reward = ctx.tx[sfSignatureReward]; auto const sleBridge = readBridge(ctx.view, bridgeSpec); if (!sleBridge) @@ -2248,7 +2235,7 @@ XChainCreateAccountCommit::preclaim(PreclaimContext const& ctx) } std::optional const minCreateAmount = - get((*sleBridge)[~sfMinAccountCreateAmount]); + (*sleBridge)[~sfMinAccountCreateAmount]; if (!minCreateAmount) return tecXCHAIN_CREATE_ACCOUNT_DISABLED; @@ -2295,7 +2282,7 @@ XChainCreateAccountCommit::doApply() AccountID const account = ctx_.tx[sfAccount]; STAmount const amount = get(ctx_.tx[sfAmount]); - STAmount const reward = get(ctx_.tx[sfSignatureReward]); + STAmount const reward = 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 3552b7a3574..48d19070eae 100644 --- a/src/xrpld/app/tx/detail/applySteps.cpp +++ b/src/xrpld/app/tx/detail/applySteps.cpp @@ -323,9 +323,8 @@ TxConsequences::TxConsequences(NotTEC pfresult) TxConsequences::TxConsequences(STTx const& tx) : isBlocker_(false) , fee_( - tx[sfFee].native() && !tx[sfFee].negative() - ? get(tx[sfFee]).xrp() - : beast::zero) + tx[sfFee].native() && !tx[sfFee].negative() ? tx[sfFee].xrp() + : beast::zero) , potentialSpend_(beast::zero) , seqProx_(tx.getSeqProxy()) , sequencesConsumed_(tx.getSeqProxy().isSeq() ? 1 : 0) diff --git a/src/xrpld/ledger/detail/PaymentSandbox.cpp b/src/xrpld/ledger/detail/PaymentSandbox.cpp index a25907626b4..d182d22b56c 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 = get((*before)[sfBalance]); + oldBalance = (*before)[sfBalance]; newBalance = oldBalance.zeroed(); break; case ltRIPPLE_STATE: lowID = (*before)[sfLowLimit].getIssuer(); highID = (*before)[sfHighLimit].getIssuer(); - oldBalance = get((*before)[sfBalance]); + oldBalance = (*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 = get((*after)[sfBalance]); + newBalance = (*after)[sfBalance]; oldBalance = newBalance.zeroed(); break; case ltRIPPLE_STATE: lowID = (*after)[sfLowLimit].getIssuer(); highID = (*after)[sfHighLimit].getIssuer(); - newBalance = get((*after)[sfBalance]); + newBalance = (*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 = get((*before)[sfBalance]); - newBalance = get((*after)[sfBalance]); + oldBalance = (*before)[sfBalance]; + newBalance = (*after)[sfBalance]; break; case ltRIPPLE_STATE: lowID = (*after)[sfLowLimit].getIssuer(); highID = (*after)[sfHighLimit].getIssuer(); - oldBalance = get((*before)[sfBalance]); - newBalance = get((*after)[sfBalance]); + oldBalance = (*before)[sfBalance]; + newBalance = (*after)[sfBalance]; break; case ltOFFER: // TBD diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index 403294d775d..6021ca75688 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -278,7 +278,7 @@ accountHolds( } else { - amount = get(sle->getFieldAmount(sfBalance)); + amount = sle->getFieldAmount(sfBalance); if (account > issuer) { // Put balance in account terms. @@ -430,7 +430,7 @@ xrpLiquid( ? XRPAmount{0} : view.fees().accountReserve(ownerCount); - auto const fullBalance = get(sle->getFieldAmount(sfBalance)); + auto const fullBalance = sle->getFieldAmount(sfBalance); auto const balance = view.balanceHook(id, xrpAccount(), fullBalance); @@ -1054,8 +1054,7 @@ rippleCredit( // If the line exists, modify it accordingly. if (auto const sleRippleState = view.peek(index)) { - STAmount saBalance = - get(sleRippleState->getFieldAmount(sfBalance)); + STAmount saBalance = sleRippleState->getFieldAmount(sfBalance); if (bSenderHigh) saBalance.negate(); // Put balance in sender terms. @@ -1090,8 +1089,8 @@ rippleCredit( view.read(keylet::account(uSenderID))->getFlags() & lsfDefaultRipple) && !(uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) && - !get(sleRippleState->getFieldAmount( - !bSenderHigh ? sfLowLimit : sfHighLimit)) + !sleRippleState->getFieldAmount( + !bSenderHigh ? sfLowLimit : sfHighLimit) // Sender trust limit is 0. && !sleRippleState->getFieldU32( !bSenderHigh ? sfLowQualityIn : sfHighQualityIn) @@ -1290,7 +1289,7 @@ accountSend( if (sender) { - if (get(sender->getFieldAmount(sfBalance)) < saAmount) + if (sender->getFieldAmount(sfBalance) < saAmount) { // VFALCO Its laborious to have to mutate the // TER based on params everywhere @@ -1299,8 +1298,7 @@ accountSend( } else { - auto const sndBal = - get(sender->getFieldAmount(sfBalance)); + auto const sndBal = sender->getFieldAmount(sfBalance); view.creditHook(uSenderID, xrpAccount(), saAmount, sndBal); // Decrement XRP balance. @@ -1312,7 +1310,7 @@ accountSend( if (tesSUCCESS == terResult && receiver) { // Increment XRP balance. - auto const rcvBal = get(receiver->getFieldAmount(sfBalance)); + auto const rcvBal = receiver->getFieldAmount(sfBalance); receiver->setFieldAmount(sfBalance, rcvBal + saAmount); view.creditHook(xrpAccount(), uReceiverID, saAmount, -rcvBal); @@ -1440,8 +1438,7 @@ updateTrustLine( flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != static_cast(sle->getFlags() & lsfDefaultRipple) && !(flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) && - !get( - state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)) + !state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit) // Sender trust limit is 0. && !state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) // Sender quality in is 0. @@ -1490,8 +1487,7 @@ issueIOU( if (auto state = view.peek(index)) { - STAmount final_balance = - get(state->getFieldAmount(sfBalance)); + STAmount final_balance = state->getFieldAmount(sfBalance); if (bSenderHigh) final_balance.negate(); // Put balance in sender terms. @@ -1586,8 +1582,7 @@ redeemIOU( if (auto state = view.peek(keylet::line(account, issue.account, issue.currency))) { - STAmount final_balance = - get(state->getFieldAmount(sfBalance)); + STAmount final_balance = state->getFieldAmount(sfBalance); if (bSenderHigh) final_balance.negate(); // Put balance in sender terms. @@ -1654,7 +1649,7 @@ transferXRP( JLOG(j.trace()) << "transferXRP: " << to_string(from) << " -> " << to_string(to) << ") : " << amount.getFullText(); - if (get(sender->getFieldAmount(sfBalance)) < amount) + if (sender->getFieldAmount(sfBalance) < amount) { // VFALCO Its unfortunate we have to keep // mutating these TER everywhere @@ -1665,11 +1660,11 @@ transferXRP( // Decrement XRP balance. sender->setFieldAmount( - sfBalance, get(sender->getFieldAmount(sfBalance)) - amount); + sfBalance, sender->getFieldAmount(sfBalance) - amount); view.update(sender); receiver->setFieldAmount( - sfBalance, get(receiver->getFieldAmount(sfBalance)) + amount); + sfBalance, receiver->getFieldAmount(sfBalance) + amount); view.update(receiver); return tesSUCCESS; diff --git a/src/xrpld/rpc/BookChanges.h b/src/xrpld/rpc/BookChanges.h index e7b9b026bc5..7d7978d3fe2 100644 --- a/src/xrpld/rpc/BookChanges.h +++ b/src/xrpld/rpc/BookChanges.h @@ -107,12 +107,10 @@ computeBookChanges(std::shared_ptr const& lpAccepted) // compute the difference in gets and pays actually // affected onto the offer - STAmount deltaGets = - get(finalFields.getFieldAmount(sfTakerGets)) - - get(previousFields.getFieldAmount(sfTakerGets)); - STAmount deltaPays = - get(finalFields.getFieldAmount(sfTakerPays)) - - get(previousFields.getFieldAmount(sfTakerPays)); + STAmount deltaGets = finalFields.getFieldAmount(sfTakerGets) - + previousFields.getFieldAmount(sfTakerGets); + STAmount deltaPays = finalFields.getFieldAmount(sfTakerPays) - + previousFields.getFieldAmount(sfTakerPays); std::string g{to_string(deltaGets.issue())}; std::string p{to_string(deltaPays.issue())}; diff --git a/src/xrpld/rpc/detail/TransactionSign.cpp b/src/xrpld/rpc/detail/TransactionSign.cpp index c5077a01a13..1fee84c683b 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 = get(stpTrans->getFieldAmount(sfFee)); + auto const fee = stpTrans->getFieldAmount(sfFee); if (!isLegalNet(fee)) { diff --git a/src/xrpld/rpc/handlers/AMMInfo.cpp b/src/xrpld/rpc/handlers/AMMInfo.cpp index 3b4f03a7384..aad8faea213 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) - : get((*amm)[sfLPTokenBalance]); + : (*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; - get(auctionSlot[sfPrice]).setJson(auction[jss::price]); + 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 75d02cdf59c..1a3b5227ed8 100644 --- a/src/xrpld/rpc/handlers/AccountOffers.cpp +++ b/src/xrpld/rpc/handlers/AccountOffers.cpp @@ -37,10 +37,8 @@ appendOfferJson(std::shared_ptr const& offer, Json::Value& offers) STAmount dirRate = amountFromQuality(getQuality(offer->getFieldH256(sfBookDirectory))); Json::Value& obj(offers.append(Json::objectValue)); - get(offer->getFieldAmount(sfTakerPays)) - .setJson(obj[jss::taker_pays]); - get(offer->getFieldAmount(sfTakerGets)) - .setJson(obj[jss::taker_gets]); + offer->getFieldAmount(sfTakerPays).setJson(obj[jss::taker_pays]); + 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 ba533498494..e4012468434 100644 --- a/src/xrpld/rpc/handlers/NoRippleCheck.cpp +++ b/src/xrpld/rpc/handlers/NoRippleCheck.cpp @@ -169,20 +169,18 @@ doNoRippleCheck(RPC::JsonContext& context) if (needFix) { AccountID peer = - get(ownedItem->getFieldAmount( - bLow ? sfHighLimit : sfLowLimit)) + ownedItem + ->getFieldAmount(bLow ? sfHighLimit : sfLowLimit) .getIssuer(); - STAmount peerLimit = - get(ownedItem->getFieldAmount( - bLow ? sfHighLimit : sfLowLimit)); + STAmount peerLimit = ownedItem->getFieldAmount( + bLow ? sfHighLimit : sfLowLimit); problem += to_string(peerLimit.getCurrency()); problem += " line to "; problem += to_string(peerLimit.getIssuer()); problems.append(problem); - STAmount limitAmount( - get(ownedItem->getFieldAmount( - bLow ? sfLowLimit : sfHighLimit))); + STAmount limitAmount(ownedItem->getFieldAmount( + bLow ? sfLowLimit : sfHighLimit)); limitAmount.setIssuer(peer); Json::Value& tx = jvTransactions.append(Json::objectValue); From 4f6ddfe239fc8caef6837f7534544c730cfa54bc Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Thu, 1 Aug 2024 14:02:06 -0400 Subject: [PATCH 03/11] Remove STBase and CountedObject inheritance from STAmount and STMPTAmount --- include/xrpl/protocol/STAmount.h | 63 +++------- include/xrpl/protocol/STMPTAmount.h | 26 ++-- src/libxrpl/protocol/STAmount.cpp | 132 ++++---------------- src/libxrpl/protocol/STEitherAmount.cpp | 27 ++-- src/libxrpl/protocol/STMPTAmount.cpp | 21 +--- src/libxrpl/protocol/XChainAttestations.cpp | 8 +- src/test/protocol/STAmount_test.cpp | 2 +- src/xrpld/app/tx/detail/XChainBridge.cpp | 2 +- 8 files changed, 62 insertions(+), 219 deletions(-) diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index e630dd1c159..01520834301 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -26,13 +26,16 @@ #include #include #include -#include #include #include #include namespace ripple { +struct int64_tag_t +{ +}; + // Internal form: // 1: If amount is zero, then value is zero and offset is -100 // 2: Otherwise: @@ -43,7 +46,7 @@ namespace ripple { // Wire form: // High 8 bits are (offset+142), legal range is, 80 to 22 inclusive // Low 56 bits are value, legal range is 10^15 to (10^16 - 1) inclusive -class STAmount final : public STBase, public CountedObject +class STAmount final { public: using mantissa_type = std::uint64_t; @@ -76,24 +79,14 @@ class STAmount final : public STBase, public CountedObject static std::uint64_t const uRateOne; //-------------------------------------------------------------------------- - STAmount(std::uint64_t value, SerialIter& sit, SField const& name); - STAmount(SerialIter& sit, SField const& name); + STAmount(std::uint64_t value, SerialIter& sit); + STAmount(SerialIter& sit); struct unchecked { explicit unchecked() = default; }; - // Do not call canonicalize - STAmount( - SField const& name, - Issue const& issue, - mantissa_type mantissa, - exponent_type exponent, - bool native, - bool negative, - unchecked); - STAmount( Issue const& issue, mantissa_type mantissa, @@ -104,31 +97,16 @@ class STAmount final : public STBase, public CountedObject // Call canonicalize STAmount( - SField const& name, Issue const& issue, mantissa_type mantissa, exponent_type exponent, bool native, bool negative); - STAmount(SField const& name, std::int64_t mantissa); - - STAmount( - SField const& name, - std::uint64_t mantissa = 0, - bool negative = false); - - STAmount( - SField const& name, - Issue const& issue, - std::uint64_t mantissa = 0, - int exponent = 0, - bool negative = false); + STAmount(std::int64_t mantissa, int64_tag_t); explicit STAmount(std::uint64_t mantissa = 0, bool negative = false); - explicit STAmount(SField const& name, STAmount const& amt); - STAmount( Issue const& issue, std::uint64_t mantissa = 0, @@ -243,24 +221,21 @@ class STAmount final : public STBase, public CountedObject //-------------------------------------------------------------------------- SerializedTypeID - getSType() const override; + getSType() const; std::string - getFullText() const override; + getFullText() const; std::string - getText() const override; + getText() const; - Json::Value getJson(JsonOptions) const override; + Json::Value getJson(JsonOptions) const; void - add(Serializer& s) const override; - - bool - isEquivalent(const STBase& t) const override; + add(Serializer& s) const; bool - isDefault() const override; + isDefault() const; XRPAmount xrp() const; @@ -269,18 +244,13 @@ class STAmount final : public STBase, public CountedObject private: static std::unique_ptr - construct(SerialIter&, SField const& name); + construct(SerialIter&); void set(std::int64_t v); void canonicalize(); - STBase* - copy(std::size_t n, void* buf) const override; - STBase* - move(std::size_t n, void* buf) override; - STAmount& operator=(IOUAmount const& iou); @@ -478,6 +448,9 @@ operator>=(STAmount const& lhs, STAmount const& rhs) STAmount operator-(STAmount const& value); +std::ostream& +operator<<(std::ostream& out, const STAmount& t); + //------------------------------------------------------------------------------ // // Arithmetic diff --git a/include/xrpl/protocol/STMPTAmount.h b/include/xrpl/protocol/STMPTAmount.h index 0bdabcba19c..fab1d21a95c 100644 --- a/include/xrpl/protocol/STMPTAmount.h +++ b/include/xrpl/protocol/STMPTAmount.h @@ -23,16 +23,13 @@ #include #include #include -#include #include namespace ripple { struct Rate; -class STMPTAmount final : public MPTAmount, - public STBase, - public CountedObject +class STMPTAmount final : public MPTAmount { private: MPTIssue issue_; @@ -40,37 +37,30 @@ class STMPTAmount final : public MPTAmount, 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(std::uint64_t value, SerialIter& sit); 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; + getSType() const; std::string - getFullText() const override; + getFullText() const; std::string - getText() const override; + getText() const; - Json::Value getJson(JsonOptions) const override; + Json::Value getJson(JsonOptions) const; void - add(Serializer& s) const override; + add(Serializer& s) const; void setJson(Json::Value& elem) const; bool - isEquivalent(const STBase& t) const override; - - bool - isDefault() const override; + isDefault() const; AccountID const& getIssuer() const; diff --git a/src/libxrpl/protocol/STAmount.cpp b/src/libxrpl/protocol/STAmount.cpp index c4cf6d9c151..024b98ff817 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -85,8 +85,7 @@ areComparable(STAmount const& v1, STAmount const& v2) v1.issue().currency == v2.issue().currency; } -STAmount::STAmount(std::uint64_t value, SerialIter& sit, SField const& name) - : STBase(name) +STAmount::STAmount(std::uint64_t value, SerialIter& sit) { // native if ((value & cNotNative) == 0) @@ -157,25 +156,7 @@ STAmount::STAmount(std::uint64_t value, SerialIter& sit, SField const& name) 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(ripple::SerialIter& sit) : STAmount(sit.get64(), sit) { } @@ -195,14 +176,12 @@ STAmount::STAmount( } STAmount::STAmount( - SField const& name, Issue const& issue, mantissa_type mantissa, exponent_type exponent, bool native, bool negative) - : STBase(name) - , mIssue(issue) + : mIssue(issue) , mValue(mantissa) , mOffset(exponent) , mIsNative(native) @@ -211,49 +190,12 @@ STAmount::STAmount( canonicalize(); } -STAmount::STAmount(SField const& name, std::int64_t mantissa) - : STBase(name), mOffset(0), mIsNative(true) +STAmount::STAmount(std::int64_t mantissa, int64_tag_t) + : mOffset(0), mIsNative(true) { set(mantissa); } -STAmount::STAmount(SField const& name, std::uint64_t mantissa, bool negative) - : STBase(name) - , mValue(mantissa) - , mOffset(0) - , mIsNative(true) - , mIsNegative(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) - , mIssue(from.mIssue) - , mValue(from.mValue) - , mOffset(from.mOffset) - , mIsNegative(from.mIsNegative) -{ - assert(mValue <= std::numeric_limits::max()); - canonicalize(); -} - //------------------------------------------------------------------------------ STAmount::STAmount(std::uint64_t mantissa, bool negative) @@ -323,21 +265,9 @@ STAmount::STAmount(XRPAmount const& amount) } std::unique_ptr -STAmount::construct(SerialIter& sit, SField const& name) -{ - return std::make_unique(sit, name); -} - -STBase* -STAmount::copy(std::size_t n, void* buf) const -{ - return emplace(n, buf, *this); -} - -STBase* -STAmount::move(std::size_t n, void* buf) +STAmount::construct(SerialIter& sit) { - return emplace(n, buf, std::move(*this)); + return std::make_unique(sit); } //------------------------------------------------------------------------------ @@ -420,16 +350,11 @@ operator+(STAmount const& v1, STAmount const& v2) if (v1 == beast::zero) { // Result must be in terms of v1 currency and issuer. - return { - v1.getFName(), - v1.issue(), - v2.mantissa(), - v2.exponent(), - v2.negative()}; + return {v1.issue(), v2.mantissa(), v2.exponent(), v2.negative()}; } if (v1.native()) - return {v1.getFName(), getSNValue(v1) + getSNValue(v2)}; + return {getSNValue(v1) + getSNValue(v2), int64_tag_t{}}; if (getSTNumberSwitchover()) { @@ -466,18 +391,12 @@ operator+(STAmount const& v1, STAmount const& v2) std::int64_t fv = vv1 + vv2; if ((fv >= -10) && (fv <= 10)) - return {v1.getFName(), v1.issue()}; + return {v1.issue()}; if (fv >= 0) - return STAmount{ - v1.getFName(), - v1.issue(), - static_cast(fv), - ov1, - false}; - - return STAmount{ - v1.getFName(), v1.issue(), static_cast(-fv), ov1, true}; + return STAmount{v1.issue(), static_cast(fv), ov1, false}; + + return STAmount{v1.issue(), static_cast(-fv), ov1, true}; } STAmount @@ -547,12 +466,6 @@ STAmount::setJson(Json::Value& elem) const } } -//------------------------------------------------------------------------------ -// -// STBase -// -//------------------------------------------------------------------------------ - SerializedTypeID STAmount::getSType() const { @@ -692,13 +605,6 @@ STAmount::add(Serializer& s) const } } -bool -STAmount::isEquivalent(const STBase& t) const -{ - const STAmount* v = dynamic_cast(&t); - return v && (*v == *this); -} - bool STAmount::isDefault() const { @@ -981,7 +887,6 @@ operator-(STAmount const& value) if (value.mantissa() == 0) return value; return STAmount( - value.getFName(), value.issue(), value.mantissa(), value.exponent(), @@ -1107,7 +1012,7 @@ multiply(STAmount const& v1, STAmount const& v2, Issue const& issue) if (((maxV >> 32) * minV) > 2095475792ull) // cMaxNative / 2^32 Throw("Native value overflow"); - return STAmount(v1.getFName(), minV * maxV); + return STAmount(minV * maxV); } if (getSTNumberSwitchover()) @@ -1299,7 +1204,7 @@ mulRoundImpl( if (((maxV >> 32) * minV) > 2095475792ull) // cMaxNative / 2^32 Throw("Native value overflow"); - return STAmount(v1.getFName(), minV * maxV); + return STAmount(minV * maxV); } std::uint64_t value1 = v1.mantissa(), value2 = v2.mantissa(); @@ -1493,4 +1398,11 @@ divRoundStrict( return divRoundImpl(num, den, issue, roundUp); } +std::ostream& +operator<<(std::ostream& out, const STAmount& t) +{ + out << t.getFullText(); + return out; +} + } // namespace ripple diff --git a/src/libxrpl/protocol/STEitherAmount.cpp b/src/libxrpl/protocol/STEitherAmount.cpp index cde7bc30d29..eac706a8729 100644 --- a/src/libxrpl/protocol/STEitherAmount.cpp +++ b/src/libxrpl/protocol/STEitherAmount.cpp @@ -32,17 +32,16 @@ STEitherAmount::STEitherAmount(SerialIter& sit, SField const& name) auto const value = sit.get64(); if ((value & STAmount::cNotNative) == 0 && (value & STMPTAmount::cMPToken) != 0) - amount_.emplace(value, sit, name); + amount_.emplace(value, sit); else - amount_.emplace(value, sit, name); + amount_.emplace(value, sit); } STEitherAmount::STEitherAmount(XRPAmount const& amount) : amount_{amount} { } -STEitherAmount::STEitherAmount(STAmount const& amount) - : STBase(amount.getFName()), amount_{amount} +STEitherAmount::STEitherAmount(STAmount const& amount) : amount_{amount} { } @@ -56,8 +55,7 @@ STEitherAmount::STEitherAmount(SField const& name, STMPTAmount const& amount) { } -STEitherAmount::STEitherAmount(STMPTAmount const& amount) - : STBase(amount.getFName()), amount_{amount} +STEitherAmount::STEitherAmount(STMPTAmount const& amount) : amount_{amount} { } @@ -122,11 +120,7 @@ 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_); + return v && *this == *v; } bool @@ -366,12 +360,7 @@ amountFromJson(SField const& name, Json::Value const& v) return STEitherAmount{ name, STAmount{ - name, - std::get(issue), - mantissa, - exponent, - native, - negative}}; + std::get(issue), mantissa, exponent, native, negative}}; while (exponent-- > 0) mantissa *= 10; if (mantissa > STMPTAmount::cMaxMPTValue) @@ -379,9 +368,7 @@ amountFromJson(SField const& name, Json::Value const& v) return STEitherAmount{ name, STMPTAmount{ - name, - std::get(issue), - static_cast(mantissa)}}; + std::get(issue), static_cast(mantissa)}}; } } // namespace detail diff --git a/src/libxrpl/protocol/STMPTAmount.cpp b/src/libxrpl/protocol/STMPTAmount.cpp index 2e5235f2500..d5348e89e86 100644 --- a/src/libxrpl/protocol/STMPTAmount.cpp +++ b/src/libxrpl/protocol/STMPTAmount.cpp @@ -25,11 +25,7 @@ namespace ripple { -STMPTAmount::STMPTAmount( - std::uint64_t value, - SerialIter& sit, - SField const& name) - : STBase(name) +STMPTAmount::STMPTAmount(std::uint64_t value, SerialIter& sit) { assert(value & cMPToken); value_ = (value << 8) | sit.get8(); @@ -38,14 +34,6 @@ STMPTAmount::STMPTAmount( 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) { @@ -108,13 +96,6 @@ STMPTAmount::add(Serializer& s) const s.addBitString(issue_.getMptID()); } -bool -STMPTAmount::isEquivalent(const STBase& t) const -{ - const STMPTAmount* v = dynamic_cast(&t); - return v && *this == (*v); -} - bool STMPTAmount::isDefault() const { diff --git a/src/libxrpl/protocol/XChainAttestations.cpp b/src/libxrpl/protocol/XChainAttestations.cpp index d72075e02c7..2b0c1004a3a 100644 --- a/src/libxrpl/protocol/XChainAttestations.cpp +++ b/src/libxrpl/protocol/XChainAttestations.cpp @@ -438,7 +438,7 @@ XChainClaimAttestation::XChainClaimAttestation( std::optional const& dst_) : keyAccount(keyAccount_) , publicKey(publicKey_) - , amount(sfAmount, amount_) + , amount(amount_) , rewardAccount(rewardAccount_) , wasLockingChainSend(wasLockingChainSend_) , dst(dst_) @@ -563,8 +563,8 @@ XChainCreateAccountAttestation::XChainCreateAccountAttestation( AccountID const& dst_) : keyAccount(keyAccount_) , publicKey(publicKey_) - , amount(sfAmount, amount_) - , rewardAmount(sfSignatureReward, rewardAmount_) + , amount(amount_) + , rewardAmount(rewardAmount_) , rewardAccount(rewardAccount_) , wasLockingChainSend(wasLockingChainSend_) , dst(dst_) @@ -617,7 +617,7 @@ XChainCreateAccountAttestation::toSTObject() const STAccount{sfAttestationSignerAccount, keyAccount}; o[sfPublicKey] = publicKey; o[sfAmount] = STEitherAmount{sfAmount, amount}; - o[sfSignatureReward] = STAmount{sfSignatureReward, rewardAmount}; + o[sfSignatureReward] = STAmount{rewardAmount}; o[sfAttestationRewardAccount] = STAccount{sfAttestationRewardAccount, rewardAccount}; o[sfWasLockingChainSend] = wasLockingChainSend; diff --git a/src/test/protocol/STAmount_test.cpp b/src/test/protocol/STAmount_test.cpp index e48d0500ba6..532a0b984a3 100644 --- a/src/test/protocol/STAmount_test.cpp +++ b/src/test/protocol/STAmount_test.cpp @@ -34,7 +34,7 @@ class STAmount_test : public beast::unit_test::suite s.add(ser); SerialIter sit(ser.slice()); - return STAmount(sit, sfGeneric); + return STAmount(sit); } //-------------------------------------------------------------------------- diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/xrpld/app/tx/detail/XChainBridge.cpp index 9b45f692274..e7f0253e462 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/xrpld/app/tx/detail/XChainBridge.cpp @@ -489,7 +489,7 @@ transferHelper( } (*sleSrc)[sfBalance] = (*sleSrc)[sfBalance] - amt; - (*sleDst)[sfBalance] = STAmount{sfBalance, (*sleDst)[sfBalance] + amt}; + (*sleDst)[sfBalance] = (*sleDst)[sfBalance] + amt; psb.update(sleSrc); psb.update(sleDst); From 749a80ab37a953f6863aa9d119f1fe7e115b122c Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Mon, 5 Aug 2024 19:39:06 -0400 Subject: [PATCH 04/11] Consolidate errors. Remove redundant checks. --- include/xrpl/protocol/TER.h | 4 +--- src/libxrpl/protocol/TER.cpp | 2 -- src/xrpld/app/tx/detail/AMMCreate.cpp | 2 +- src/xrpld/app/tx/detail/AMMDeposit.cpp | 2 +- src/xrpld/app/tx/detail/AMMWithdraw.cpp | 2 +- src/xrpld/app/tx/detail/CreateCheck.cpp | 3 --- src/xrpld/app/tx/detail/CreateOffer.cpp | 4 ---- src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp | 2 +- src/xrpld/app/tx/detail/NFTokenMint.cpp | 2 +- src/xrpld/app/tx/detail/Payment.cpp | 15 +++++++++------ src/xrpld/app/tx/detail/XChainBridge.cpp | 15 +++++---------- 11 files changed, 20 insertions(+), 33 deletions(-) diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index 6d666a3fb55..afc915bc0a3 100644 --- a/include/xrpl/protocol/TER.h +++ b/include/xrpl/protocol/TER.h @@ -139,9 +139,7 @@ enum TEMcodes : TERUnderlyingType { temEMPTY_DID, temARRAY_EMPTY, - temARRAY_TOO_LARGE, - temMPT_NOT_SUPPORTED, - temMPT_INVALID_USAGE + temARRAY_TOO_LARGE }; //------------------------------------------------------------------------------ diff --git a/src/libxrpl/protocol/TER.cpp b/src/libxrpl/protocol/TER.cpp index 38842c34758..795ab74ca7d 100644 --- a/src/libxrpl/protocol/TER.cpp +++ b/src/libxrpl/protocol/TER.cpp @@ -189,8 +189,6 @@ transResults() MAKE_ERROR(temEMPTY_DID, "Malformed: No DID data provided."), 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/xrpld/app/tx/detail/AMMCreate.cpp b/src/xrpld/app/tx/detail/AMMCreate.cpp index 35c4563e4d3..12fee14e6ec 100644 --- a/src/xrpld/app/tx/detail/AMMCreate.cpp +++ b/src/xrpld/app/tx/detail/AMMCreate.cpp @@ -42,7 +42,7 @@ AMMCreate::preflight(PreflightContext const& ctx) return ret; if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) - return temMPT_NOT_SUPPORTED; + return temMALFORMED; if (ctx.tx.getFlags() & tfUniversalMask) { diff --git a/src/xrpld/app/tx/detail/AMMDeposit.cpp b/src/xrpld/app/tx/detail/AMMDeposit.cpp index a83a0b101c0..be6f2303e52 100644 --- a/src/xrpld/app/tx/detail/AMMDeposit.cpp +++ b/src/xrpld/app/tx/detail/AMMDeposit.cpp @@ -42,7 +42,7 @@ AMMDeposit::preflight(PreflightContext const& ctx) return ret; if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[~sfAmount])) - return temMPT_NOT_SUPPORTED; + return temMALFORMED; auto const flags = ctx.tx.getFlags(); if (flags & tfDepositMask) diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.cpp b/src/xrpld/app/tx/detail/AMMWithdraw.cpp index c6a70584a01..110291e9ac7 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/xrpld/app/tx/detail/AMMWithdraw.cpp @@ -42,7 +42,7 @@ AMMWithdraw::preflight(PreflightContext const& ctx) return ret; if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[~sfAmount])) - return temMPT_NOT_SUPPORTED; + return temMALFORMED; auto const flags = ctx.tx.getFlags(); if (flags & tfWithdrawMask) diff --git a/src/xrpld/app/tx/detail/CreateCheck.cpp b/src/xrpld/app/tx/detail/CreateCheck.cpp index 2fa1f323339..3a278eed738 100644 --- a/src/xrpld/app/tx/detail/CreateCheck.cpp +++ b/src/xrpld/app/tx/detail/CreateCheck.cpp @@ -38,9 +38,6 @@ 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. diff --git a/src/xrpld/app/tx/detail/CreateOffer.cpp b/src/xrpld/app/tx/detail/CreateOffer.cpp index 7a6a723bac8..2a5145594a1 100644 --- a/src/xrpld/app/tx/detail/CreateOffer.cpp +++ b/src/xrpld/app/tx/detail/CreateOffer.cpp @@ -45,10 +45,6 @@ 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; diff --git a/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp b/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp index 0f42fadb316..4321253204d 100644 --- a/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp @@ -37,7 +37,7 @@ NFTokenCreateOffer::preflight(PreflightContext const& ctx) return ret; if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) - return temMPT_NOT_SUPPORTED; + return temMALFORMED; auto const txFlags = ctx.tx.getFlags(); diff --git a/src/xrpld/app/tx/detail/NFTokenMint.cpp b/src/xrpld/app/tx/detail/NFTokenMint.cpp index 30834df70f0..bea1fee4b7e 100644 --- a/src/xrpld/app/tx/detail/NFTokenMint.cpp +++ b/src/xrpld/app/tx/detail/NFTokenMint.cpp @@ -54,7 +54,7 @@ NFTokenMint::preflight(PreflightContext const& ctx) return ret; if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[~sfAmount])) - return temMPT_NOT_SUPPORTED; + return temMALFORMED; // Prior to fixRemoveNFTokenAutoTrustLine, transfer of an NFToken between // accounts allowed a TrustLine to be added to the issuer of that token diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index ea18e79e935..5e9293c164f 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -185,14 +185,17 @@ preflightHelper(PreflightContext const& ctx) JLOG(j.trace()) << "Malformed transaction: " << "Paths specified for XRP to XRP or MPT to MPT."; if (bMPTDirect) - return temMPT_NOT_SUPPORTED; + return temMALFORMED; return temBAD_SEND_XRP_PATHS; } - if (bXRPDirect && partialPaymentAllowed) + if (bDirect && partialPaymentAllowed) { // Consistent but redundant transaction. - JLOG(j.trace()) << "Malformed transaction: " - << "Partial payment specified for XRP to XRP."; + JLOG(j.trace()) + << "Malformed transaction: " + << "Partial payment specified for XRP to XRP or MPT to MPT."; + if (bMPTDirect) + return temMALFORMED; return temBAD_SEND_XRP_PARTIAL; } if (bDirect && limitQuality) @@ -202,7 +205,7 @@ preflightHelper(PreflightContext const& ctx) << "Malformed transaction: " << "Limit quality specified for XRP to XRP or MPT to MPT."; if (bMPTDirect) - return temMPT_NOT_SUPPORTED; + return temMALFORMED; return temBAD_SEND_XRP_LIMIT; } if (bDirect && !defaultPathsAllowed) @@ -212,7 +215,7 @@ preflightHelper(PreflightContext const& ctx) << "Malformed transaction: " << "No ripple direct specified for XRP to XRP or MPT to MPT."; if (bMPTDirect) - return temMPT_NOT_SUPPORTED; + return temMALFORMED; return temBAD_SEND_XRP_NO_DIRECT; } diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/xrpld/app/tx/detail/XChainBridge.cpp index e7f0253e462..f611aed7d48 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/xrpld/app/tx/detail/XChainBridge.cpp @@ -1215,7 +1215,7 @@ attestationPreflight(PreflightContext const& ctx) return ret; if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) - return temMPT_NOT_SUPPORTED; + return temMALFORMED; if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; @@ -1676,7 +1676,7 @@ XChainClaim::preflight(PreflightContext const& ctx) return ret; if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) - return temMPT_NOT_SUPPORTED; + return temMALFORMED; if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; @@ -1917,7 +1917,7 @@ XChainCommit::preflight(PreflightContext const& ctx) return ret; if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) - return temMPT_NOT_SUPPORTED; + return temMALFORMED; if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; @@ -2035,10 +2035,6 @@ 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; @@ -2194,9 +2190,8 @@ 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.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) + return temMALFORMED; if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; From 5b77635dbd5c4709d0726a6f3c8f9b517ec8b36f Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Mon, 5 Aug 2024 19:39:06 -0400 Subject: [PATCH 05/11] Add invalid MPT amount check in STTx::passesLocalChecks() to provide one single check for all tx. Update unit-tests. Fix payment for unauthorized holder --- include/xrpl/protocol/MPTIssue.h | 3 + include/xrpl/protocol/SOTemplate.h | 8 +- include/xrpl/protocol/STEitherAmount.h | 3 - include/xrpl/protocol/STObject.h | 2 + include/xrpl/protocol/XChainAttestations.h | 6 +- src/libxrpl/protocol/MPTIssue.cpp | 9 + src/libxrpl/protocol/STObject.cpp | 15 +- src/libxrpl/protocol/STTx.cpp | 33 ++ src/libxrpl/protocol/XChainAttestations.cpp | 8 +- src/test/app/MPToken_test.cpp | 407 ++++++++++++++++-- src/test/jtx/attester.h | 6 +- src/test/jtx/impl/attester.cpp | 6 +- src/test/jtx/impl/xchain_bridge.cpp | 8 +- src/test/jtx/xchain_bridge.h | 2 +- src/xrpld/app/tx/detail/AMMCreate.cpp | 3 - src/xrpld/app/tx/detail/AMMDeposit.cpp | 3 - src/xrpld/app/tx/detail/AMMWithdraw.cpp | 3 - src/xrpld/app/tx/detail/CashCheck.cpp | 3 - .../app/tx/detail/NFTokenCreateOffer.cpp | 3 - src/xrpld/app/tx/detail/NFTokenMint.cpp | 3 - src/xrpld/app/tx/detail/Transactor.cpp | 2 +- src/xrpld/app/tx/detail/XChainBridge.cpp | 12 - src/xrpld/ledger/detail/View.cpp | 4 + src/xrpld/rpc/detail/TransactionSign.cpp | 9 +- 24 files changed, 453 insertions(+), 108 deletions(-) diff --git a/include/xrpl/protocol/MPTIssue.h b/include/xrpl/protocol/MPTIssue.h index dc16d207a87..6c01baccf08 100644 --- a/include/xrpl/protocol/MPTIssue.h +++ b/include/xrpl/protocol/MPTIssue.h @@ -84,6 +84,9 @@ isXRP(uint192 const&) return false; } +Json::Value +to_json(MPTIssue const& issue); + } // namespace ripple #endif // RIPPLE_PROTOCOL_MPTISSUE_H_INCLUDED diff --git a/include/xrpl/protocol/SOTemplate.h b/include/xrpl/protocol/SOTemplate.h index 0ea420a483b..56a0d471150 100644 --- a/include/xrpl/protocol/SOTemplate.h +++ b/include/xrpl/protocol/SOTemplate.h @@ -69,9 +69,7 @@ class SOElement public: SOElement(SField const& fieldName, SOEStyle style) - : sField_(fieldName) - , style_(style) - , supportMpt_(SOETxMPTAmount::soeMPTNone) + : sField_(fieldName), style_(style), supportMpt_(soeMPTNone) { init(fieldName); } @@ -101,10 +99,10 @@ class SOElement return style_; } - bool + SOETxMPTAmount supportMPT() const { - return supportMpt_ == soeMPTSupported; + return supportMpt_; } }; diff --git a/include/xrpl/protocol/STEitherAmount.h b/include/xrpl/protocol/STEitherAmount.h index 3c62b42986c..c16c8a1d6a1 100644 --- a/include/xrpl/protocol/STEitherAmount.h +++ b/include/xrpl/protocol/STEitherAmount.h @@ -205,9 +205,6 @@ operator==(STEitherAmount const& lhs, STEitherAmount const& rhs) }, lhs.getValue(), rhs.getValue()); - if (lhs.isIssue() == rhs.isIssue()) - return lhs.getValue() == rhs.getValue(); - return false; } inline bool diff --git a/include/xrpl/protocol/STObject.h b/include/xrpl/protocol/STObject.h index fb7d15d3630..26cdf5a55ea 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -240,6 +240,8 @@ class STObject : public STBase, public CountedObject Blob getFieldVL(SField const& field) const; STEitherAmount const& + getFieldEitherAmount(SField const& field) const; + STEitherAmount const& getFieldAmount(SField const& field) const; STAmount const& getFieldAmount(TypedFieldAmount const& field) const; diff --git a/include/xrpl/protocol/XChainAttestations.h b/include/xrpl/protocol/XChainAttestations.h index 721950ca9c1..ea715aa30ab 100644 --- a/include/xrpl/protocol/XChainAttestations.h +++ b/include/xrpl/protocol/XChainAttestations.h @@ -143,7 +143,7 @@ struct AttestationClaim : AttestationBase message( STXChainBridge const& bridge, AccountID const& sendingAccount, - STAmount const& sendingAmount, + STEitherAmount const& sendingAmount, AccountID const& rewardAccount, bool wasLockingChainSend, std::uint64_t claimID, @@ -226,8 +226,8 @@ struct AttestationCreateAccount : AttestationBase message( STXChainBridge const& bridge, AccountID const& sendingAccount, - STAmount const& sendingAmount, - STAmount const& rewardAmount, + STEitherAmount const& sendingAmount, + STEitherAmount const& rewardAmount, AccountID const& rewardAccount, bool wasLockingChainSend, std::uint64_t createCount, diff --git a/src/libxrpl/protocol/MPTIssue.cpp b/src/libxrpl/protocol/MPTIssue.cpp index 28806dce3aa..4737f46a938 100644 --- a/src/libxrpl/protocol/MPTIssue.cpp +++ b/src/libxrpl/protocol/MPTIssue.cpp @@ -19,6 +19,7 @@ #include #include +#include namespace ripple { @@ -67,4 +68,12 @@ getMPT(uint192 const& id) return std::make_pair(sequence, account); } +Json::Value +to_json(MPTIssue const& issue) +{ + Json::Value jv; + jv[jss::mpt_issuance_id] = to_string(issue.getMptID()); + return jv; +} + } // namespace ripple diff --git a/src/libxrpl/protocol/STObject.cpp b/src/libxrpl/protocol/STObject.cpp index 6542424e372..ec23b16793d 100644 --- a/src/libxrpl/protocol/STObject.cpp +++ b/src/libxrpl/protocol/STObject.cpp @@ -165,14 +165,6 @@ 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()) - { - throwFieldErr(e.sField().fieldName, "doesn't support MPT"); - } - } v.emplace_back(std::move(*iter)); v_.erase(iter); } @@ -638,6 +630,13 @@ STObject::getFieldVL(SField const& field) const return Blob(b.data(), b.data() + b.size()); } +STEitherAmount const& +STObject::getFieldEitherAmount(SField const& field) const +{ + static STEitherAmount const empty{}; + return getFieldByConstRef(field, empty); +} + STEitherAmount const& STObject::getFieldAmount(SField const& field) const { diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index 149186d43ce..d9726ccbd06 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -543,6 +543,32 @@ isAccountFieldOkay(STObject const& st) return true; } +static bool +invalidMPTAmountInTx(STObject const& tx) +{ + auto const txType = tx[~sfTransactionType]; + if (!txType) + return false; + if (auto const* item = + TxFormats::getInstance().findByType(safe_cast(*txType))) + { + for (auto const& e : item->getSOTemplate()) + { + if (tx.isFieldPresent(e.sField()) && e.supportMPT() != soeMPTNone) + { + if (auto const& field = tx.peekAtField(e.sField()); + field.getSType() == STI_AMOUNT && + static_cast(field).isMPT()) + { + if (e.supportMPT() == soeMPTNotSupported) + return true; + } + } + } + } + return false; +} + bool passesLocalChecks(STObject const& st, std::string& reason) { @@ -560,6 +586,13 @@ passesLocalChecks(STObject const& st, std::string& reason) reason = "Cannot submit pseudo transactions."; return false; } + + if (invalidMPTAmountInTx(st)) + { + reason = "Amount can not be MPT."; + return false; + } + return true; } diff --git a/src/libxrpl/protocol/XChainAttestations.cpp b/src/libxrpl/protocol/XChainAttestations.cpp index 2b0c1004a3a..2d736ebff43 100644 --- a/src/libxrpl/protocol/XChainAttestations.cpp +++ b/src/libxrpl/protocol/XChainAttestations.cpp @@ -216,7 +216,7 @@ std::vector AttestationClaim::message( STXChainBridge const& bridge, AccountID const& sendingAccount, - STAmount const& sendingAmount, + STEitherAmount const& sendingAmount, AccountID const& rewardAccount, bool wasLockingChainSend, std::uint64_t claimID, @@ -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] = STEitherAmount{sendingAmount}; + o[sfAmount] = sendingAmount; if (dst) o[sfDestination] = *dst; o[sfOtherChainSource] = sendingAccount; @@ -361,8 +361,8 @@ std::vector AttestationCreateAccount::message( STXChainBridge const& bridge, AccountID const& sendingAccount, - STAmount const& sendingAmount, - STAmount const& rewardAmount, + STEitherAmount const& sendingAmount, + STEitherAmount const& rewardAmount, AccountID const& rewardAccount, bool wasLockingChainSend, std::uint64_t createCount, diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index 94cbe5a616b..f68f1c65bcf 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -640,6 +641,25 @@ class MPToken_test : public beast::unit_test::suite mptAlice.pay(bob, carol, 50); } + // Holder is not authorized + { + Env env{*this, features}; + + MPTTester mptAlice(env, alice, {.holders = {&bob, &carol}}); + + mptAlice.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer}); + + // issuer to holder + mptAlice.pay(alice, bob, 100, tecNO_AUTH); + + // holder to issuer + mptAlice.pay(bob, alice, 100, tecNO_AUTH); + + // holder to holder + mptAlice.pay(bob, carol, 50, tecNO_AUTH); + } + // If allowlisting is enabled, Payment fails if the receiver is not // authorized { @@ -862,40 +882,70 @@ class MPToken_test : public beast::unit_test::suite env(pay(alice, bob, mpt), ter(temDISABLED)); } + // MPT is disabled, unsigned request + { + Env env{*this, features - featureMPTokensV1}; + Account const alice("alice"); // issuer + Account const carol("carol"); + auto const USD = alice["USD"]; + + env.fund(XRP(1'000), alice); + env.fund(XRP(1'000), carol); + STMPTAmount mpt{ + MPTIssue{std::make_pair(1, alice.id())}, UINT64_C(100)}; + + Json::Value jv; + jv[jss::secret] = alice.name(); + jv[jss::tx_json][jss::Fee] = to_string(env.current()->fees().base); + jv[jss::tx_json] = pay(alice, carol, mpt); + auto const jrr = env.rpc("json", "submit", to_string(jv)); + BEAST_EXPECT(jrr[jss::result][jss::engine_result] == "temDISABLED"); + } + // Invalid combination of send, sendMax, deliverMin { Env env{*this, features}; Account const alice("alice"); Account const carol("carol"); - Account const bob("bob"); - MPTTester mptAlice(env, alice, {.holders = {&bob, &carol}}); + MPTTester mptAlice(env, alice, {.holders = {&carol}}); mptAlice.create({.ownerCount = 1, .holderCount = 0}); - mptAlice.authorize({.account = &bob}); - mptAlice.authorize({.account = &carol}); - env(pay(alice, bob, mptAlice.mpt(100)), + // sendMax and DeliverMin are valid XRP amount, + // but is invalid combination with MPT amount + env(pay(alice, carol, mptAlice.mpt(100)), sendmax(XRP(100)), - delivermin(XRP(100)), ter(temMALFORMED)); - - env(pay(alice, bob, mptAlice.mpt(100)), - sendmax(mptAlice.mpt(100)), + env(pay(alice, carol, mptAlice.mpt(100)), delivermin(XRP(100)), ter(temMALFORMED)); + } - env(pay(alice, bob, XRP(100)), - sendmax(mptAlice.mpt(100)), - delivermin(XRP(100)), - ter(temMALFORMED)); + // build_path is invalid if MPT + { + Env env{*this, features}; + Account const alice("alice"); + Account const carol("carol"); - env(pay(alice, bob, XRP(100)), - sendmax(XRP(100)), - delivermin(mptAlice.mpt(100)), - ter(temMALFORMED)); + MPTTester mptAlice(env, alice, {.holders = {&bob, &carol}}); + + mptAlice.create({.ownerCount = 1, .holderCount = 0}); + + mptAlice.authorize({.account = &carol}); + + Json::Value payment; + payment[jss::secret] = alice.name(); + payment[jss::tx_json] = pay(alice, carol, mptAlice.mpt(100)); + + payment[jss::build_path] = true; + auto jrr = env.rpc("json", "submit", to_string(payment)); + BEAST_EXPECT(jrr[jss::result][jss::error] == "invalidParams"); + BEAST_EXPECT( + jrr[jss::result][jss::error_message] == + "Field 'build_path' not allowed in this context."); } } @@ -905,33 +955,312 @@ class MPToken_test : public beast::unit_test::suite testcase("MPT Amount Invalid in Transaction"); using namespace test::jtx; + std::set txWithAmounts; + for (auto const& format : TxFormats::getInstance()) { - Env env{*this, features}; - Account const alice("alice"); // issuer - - MPTTester mptAlice(env, alice); - - mptAlice.create(); - - env(offer(alice, mptAlice.mpt(100), XRP(100)), ter(temMALFORMED)); - env.close(); - - BEAST_EXPECT(expectOffers(env, alice, 0)); + for (auto const& e : format.getSOTemplate()) + { + // Transaction has amount fields. + // Exclude Clawback, which only supports sfAmount and is checked + // in the transactor for amendment enable/disable. Exclude + // pseudo-transaction SetFee. Don't consider the Fee field since + // it's included in every transaction. + if (e.supportMPT() != soeMPTNone && + e.sField().getName() != jss::Fee && + format.getName() != jss::Clawback && + format.getName() != jss::SetFee) + { + txWithAmounts.insert(format.getName()); + break; + } + } } + Account const alice("alice"); + auto const USD = alice["USD"]; + Account const carol("carol"); + MPTIssue issue(std::make_pair(1, alice.id())); + STMPTAmount mpt{issue, UINT64_C(100)}; + auto const jvb = bridge(alice, USD, alice, USD); + for (auto const& feature : {features, features - featureMPTokensV1}) { - Env env{*this, features - featureMPTokensV1}; - Account const alice("alice"); - + Env env{*this, feature}; env.fund(XRP(1'000), alice); - STMPTAmount mpt{ - MPTIssue{std::make_pair(1, alice.id())}, UINT64_C(100)}; - - env(offer(alice, mpt, XRP(100)), ter(temMALFORMED)); - env.close(); - - BEAST_EXPECT(expectOffers(env, alice, 0)); + env.fund(XRP(1'000), carol); + auto test = [&](Json::Value const& jv) { + txWithAmounts.erase(jv[jss::TransactionType].asString()); + + // tx is signed + auto jtx = env.jt(jv); + Serializer s; + jtx.stx->add(s); + auto jrr = env.rpc("submit", strHex(s.slice())); + BEAST_EXPECT( + jrr[jss::result][jss::error] == "invalidTransaction"); + + // tx is unsigned + Json::Value jv1; + jv1[jss::secret] = alice.name(); + jv1[jss::tx_json] = jv; + jrr = env.rpc("json", "submit", to_string(jv1)); + BEAST_EXPECT(jrr[jss::result][jss::error] == "invalidParams"); + }; + // All transactions with sfAmount, which don't support MPT + // and transactions with amount fields, which can't be MPT + + // AMMCreate + auto ammCreate = [&](SField const& field) { + Json::Value jv; + jv[jss::TransactionType] = jss::AMMCreate; + jv[jss::Account] = alice.human(); + jv[jss::Amount] = (field.fieldName == sfAmount.fieldName) + ? mpt.getJson(JsonOptions::none) + : "100000000"; + jv[jss::Amount2] = (field.fieldName == sfAmount2.fieldName) + ? mpt.getJson(JsonOptions::none) + : "100000000"; + jv[jss::TradingFee] = 0; + test(jv); + }; + ammCreate(sfAmount); + ammCreate(sfAmount2); + // AMMDeposit + auto ammDeposit = [&](SField const& field) { + Json::Value jv; + jv[jss::TransactionType] = jss::AMMDeposit; + jv[jss::Account] = alice.human(); + jv[jss::Asset] = to_json(xrpIssue()); + jv[jss::Asset2] = to_json(USD.issue()); + jv[field.fieldName] = mpt.getJson(JsonOptions::none); + jv[jss::Flags] = tfSingleAsset; + test(jv); + }; + ammDeposit(sfAmount); + for (SField const& field : + {std::ref(sfAmount2), + std::ref(sfEPrice), + std::ref(sfLPTokenOut)}) + ammDeposit(field); + // AMMWithdraw + auto ammWithdraw = [&](SField const& field) { + Json::Value jv; + jv[jss::TransactionType] = jss::AMMWithdraw; + jv[jss::Account] = alice.human(); + jv[jss::Asset] = to_json(xrpIssue()); + jv[jss::Asset2] = to_json(USD.issue()); + jv[jss::Flags] = tfSingleAsset; + jv[field.fieldName] = mpt.getJson(JsonOptions::none); + test(jv); + }; + ammWithdraw(sfAmount); + for (SField const& field : + {std::ref(sfAmount2), + std::ref(sfEPrice), + std::ref(sfLPTokenIn)}) + ammWithdraw(field); + // AMMBid + auto ammBid = [&](SField const& field) { + Json::Value jv; + jv[jss::TransactionType] = jss::AMMBid; + jv[jss::Account] = alice.human(); + jv[jss::Asset] = to_json(xrpIssue()); + jv[jss::Asset2] = to_json(USD.issue()); + jv[field.fieldName] = mpt.getJson(JsonOptions::none); + test(jv); + }; + ammBid(sfBidMin); + ammBid(sfBidMax); + // CheckCash + auto checkCash = [&](SField const& field) { + Json::Value jv; + jv[jss::TransactionType] = jss::CheckCash; + jv[jss::Account] = alice.human(); + jv[sfCheckID.fieldName] = to_string(uint256{1}); + jv[field.fieldName] = mpt.getJson(JsonOptions::none); + test(jv); + }; + checkCash(sfAmount); + checkCash(sfDeliverMin); + // CheckCreate + { + Json::Value jv; + jv[jss::TransactionType] = jss::CheckCreate; + jv[jss::Account] = alice.human(); + jv[jss::Destination] = carol.human(); + jv[jss::SendMax] = mpt.getJson(JsonOptions::none); + test(jv); + } + // Clawback + if (!feature[featureMPTokensV1]) + { + } + // EscrowCreate + { + Json::Value jv; + jv[jss::TransactionType] = jss::EscrowCreate; + jv[jss::Account] = alice.human(); + jv[jss::Destination] = carol.human(); + jv[jss::Amount] = mpt.getJson(JsonOptions::none); + test(jv); + } + // OfferCreate + { + Json::Value const jv = offer(alice, USD(100), mpt); + test(jv); + } + // PaymentChannelCreate + { + Json::Value jv; + jv[jss::TransactionType] = jss::PaymentChannelCreate; + jv[jss::Account] = alice.human(); + jv[jss::Destination] = carol.human(); + jv[jss::SettleDelay] = 1; + jv[sfPublicKey.fieldName] = strHex(alice.pk().slice()); + jv[jss::Amount] = mpt.getJson(JsonOptions::none); + test(jv); + } + // PaymentChannelFund + { + Json::Value jv; + jv[jss::TransactionType] = jss::PaymentChannelFund; + jv[jss::Account] = alice.human(); + jv[sfChannel.fieldName] = to_string(uint256{1}); + jv[jss::Amount] = mpt.getJson(JsonOptions::none); + test(jv); + } + // PaymentChannelClaim + { + Json::Value jv; + jv[jss::TransactionType] = jss::PaymentChannelClaim; + jv[jss::Account] = alice.human(); + jv[sfChannel.fieldName] = to_string(uint256{1}); + jv[jss::Amount] = mpt.getJson(JsonOptions::none); + test(jv); + } + // Payment + auto payment = [&](SField const& field) { + Json::Value jv; + jv[jss::TransactionType] = jss::Payment; + jv[jss::Account] = alice.human(); + jv[jss::Destination] = carol.human(); + jv[jss::Amount] = mpt.getJson(JsonOptions::none); + if (field == sfSendMax) + jv[jss::SendMax] = mpt.getJson(JsonOptions::none); + else + jv[jss::DeliverMin] = mpt.getJson(JsonOptions::none); + test(jv); + }; + payment(sfSendMax); + payment(sfDeliverMin); + // NFTokenCreateOffer + { + Json::Value jv; + jv[jss::TransactionType] = jss::NFTokenCreateOffer; + jv[jss::Account] = alice.human(); + jv[sfNFTokenID.fieldName] = to_string(uint256{1}); + jv[jss::Amount] = mpt.getJson(JsonOptions::none); + test(jv); + } + // NFTokenAcceptOffer + { + Json::Value jv; + jv[jss::TransactionType] = jss::NFTokenAcceptOffer; + jv[jss::Account] = alice.human(); + jv[sfNFTokenBrokerFee.fieldName] = + mpt.getJson(JsonOptions::none); + test(jv); + } + // NFTokenMint + { + Json::Value jv; + jv[jss::TransactionType] = jss::NFTokenMint; + jv[jss::Account] = alice.human(); + jv[sfNFTokenTaxon.fieldName] = 1; + jv[jss::Amount] = mpt.getJson(JsonOptions::none); + test(jv); + } + // TrustSet + auto trustSet = [&](SField const& field) { + Json::Value jv; + jv[jss::TransactionType] = jss::TrustSet; + jv[jss::Account] = alice.human(); + jv[jss::Flags] = 0; + jv[field.fieldName] = mpt.getJson(JsonOptions::none); + test(jv); + }; + trustSet(sfLimitAmount); + trustSet(sfFee); + // XChainCommit + { + Json::Value const jv = xchain_commit(alice, jvb, 1, mpt); + test(jv); + } + // XChainClaim + { + Json::Value const jv = xchain_claim(alice, jvb, 1, mpt, alice); + test(jv); + } + // XChainCreateClaimID + { + Json::Value const jv = + xchain_create_claim_id(alice, jvb, mpt, alice); + test(jv); + } + // XChainAddClaimAttestation + { + Json::Value const jv = claim_attestation( + alice, + jvb, + alice, + mpt, + alice, + true, + 1, + alice, + signer(alice)); + test(jv); + } + // XChainAddAccountCreateAttestation + { + Json::Value const jv = create_account_attestation( + alice, + jvb, + alice, + mpt, + XRP(10), + alice, + false, + 1, + alice, + signer(alice)); + test(jv); + } + // XChainAccountCreateCommit + { + Json::Value const jv = sidechain_xchain_account_create( + alice, jvb, alice, mpt, XRP(10)); + test(jv); + } + // XChain[Create|Modify]Bridge + auto bridgeTx = [&](Json::StaticString const& tt, + bool minAmount = false) { + Json::Value jv; + jv[jss::TransactionType] = tt; + jv[jss::Account] = alice.human(); + jv[sfXChainBridge.fieldName] = jvb; + jv[sfSignatureReward.fieldName] = + mpt.getJson(JsonOptions::none); + if (minAmount) + jv[sfMinAccountCreateAmount.fieldName] = + mpt.getJson(JsonOptions::none); + test(jv); + }; + bridgeTx(jss::XChainCreateBridge); + bridgeTx(jss::XChainCreateBridge, true); + bridgeTx(jss::XChainModifyBridge); + bridgeTx(jss::XChainModifyBridge, true); } + BEAST_EXPECT(txWithAmounts.empty()); } void @@ -1231,7 +1560,7 @@ class MPToken_test : public beast::unit_test::suite // Test Direct Payment testPayment(all); - // Test MPT Amount is invalid in non-Payment Tx + // Test MPT Amount is invalid in Tx, which don't support MPT testMPTInvalidInTx(all); // Test parsed MPTokenIssuanceID in API response metadata diff --git a/src/test/jtx/attester.h b/src/test/jtx/attester.h index 327fb2e4873..8bb5d57196a 100644 --- a/src/test/jtx/attester.h +++ b/src/test/jtx/attester.h @@ -42,7 +42,7 @@ sign_claim_attestation( SecretKey const& sk, STXChainBridge const& bridge, AccountID const& sendingAccount, - STAmount const& sendingAmount, + STEitherAmount const& sendingAmount, AccountID const& rewardAccount, bool wasLockingChainSend, std::uint64_t claimID, @@ -54,8 +54,8 @@ sign_create_account_attestation( SecretKey const& sk, STXChainBridge const& bridge, AccountID const& sendingAccount, - STAmount const& sendingAmount, - STAmount const& rewardAmount, + STEitherAmount const& sendingAmount, + STEitherAmount const& rewardAmount, AccountID const& rewardAccount, bool wasLockingChainSend, std::uint64_t createCount, diff --git a/src/test/jtx/impl/attester.cpp b/src/test/jtx/impl/attester.cpp index 66be9da83b3..02a61ffe9bd 100644 --- a/src/test/jtx/impl/attester.cpp +++ b/src/test/jtx/impl/attester.cpp @@ -35,7 +35,7 @@ sign_claim_attestation( SecretKey const& sk, STXChainBridge const& bridge, AccountID const& sendingAccount, - STAmount const& sendingAmount, + STEitherAmount const& sendingAmount, AccountID const& rewardAccount, bool wasLockingChainSend, std::uint64_t claimID, @@ -58,8 +58,8 @@ sign_create_account_attestation( SecretKey const& sk, STXChainBridge const& bridge, AccountID const& sendingAccount, - STAmount const& sendingAmount, - STAmount const& rewardAmount, + STEitherAmount const& sendingAmount, + STEitherAmount const& rewardAmount, AccountID const& rewardAccount, bool wasLockingChainSend, std::uint64_t createCount, diff --git a/src/test/jtx/impl/xchain_bridge.cpp b/src/test/jtx/impl/xchain_bridge.cpp index 53b972288a6..32d24c0027a 100644 --- a/src/test/jtx/impl/xchain_bridge.cpp +++ b/src/test/jtx/impl/xchain_bridge.cpp @@ -115,7 +115,7 @@ Json::Value xchain_create_claim_id( Account const& acc, Json::Value const& bridge, - STAmount const& reward, + STEitherAmount const& reward, Account const& otherChainSource) { Json::Value jv; @@ -216,7 +216,7 @@ claim_attestation( sk, stBridge, sendingAccount, - get(sendingAmount.value), + sendingAmount.value, rewardAccount, wasLockingChainSend, claimID, @@ -269,8 +269,8 @@ create_account_attestation( sk, stBridge, sendingAccount, - get(sendingAmount.value), - get(rewardAmount.value), + sendingAmount.value, + rewardAmount.value, rewardAccount, wasLockingChainSend, createCount, diff --git a/src/test/jtx/xchain_bridge.h b/src/test/jtx/xchain_bridge.h index 9968317c8de..dd219d0dfcd 100644 --- a/src/test/jtx/xchain_bridge.h +++ b/src/test/jtx/xchain_bridge.h @@ -61,7 +61,7 @@ Json::Value xchain_create_claim_id( Account const& acc, Json::Value const& bridge, - STAmount const& reward, + STEitherAmount const& reward, Account const& otherChainSource); Json::Value diff --git a/src/xrpld/app/tx/detail/AMMCreate.cpp b/src/xrpld/app/tx/detail/AMMCreate.cpp index 12fee14e6ec..3ed35d72656 100644 --- a/src/xrpld/app/tx/detail/AMMCreate.cpp +++ b/src/xrpld/app/tx/detail/AMMCreate.cpp @@ -41,9 +41,6 @@ AMMCreate::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) - return temMALFORMED; - if (ctx.tx.getFlags() & tfUniversalMask) { JLOG(ctx.j.debug()) << "AMM Instance: invalid flags."; diff --git a/src/xrpld/app/tx/detail/AMMDeposit.cpp b/src/xrpld/app/tx/detail/AMMDeposit.cpp index be6f2303e52..75993ad4189 100644 --- a/src/xrpld/app/tx/detail/AMMDeposit.cpp +++ b/src/xrpld/app/tx/detail/AMMDeposit.cpp @@ -41,9 +41,6 @@ AMMDeposit::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[~sfAmount])) - return temMALFORMED; - auto const flags = ctx.tx.getFlags(); if (flags & tfDepositMask) { diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.cpp b/src/xrpld/app/tx/detail/AMMWithdraw.cpp index 110291e9ac7..394fd79dd8c 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/xrpld/app/tx/detail/AMMWithdraw.cpp @@ -41,9 +41,6 @@ AMMWithdraw::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[~sfAmount])) - return temMALFORMED; - auto const flags = ctx.tx.getFlags(); if (flags & tfWithdrawMask) { diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/xrpld/app/tx/detail/CashCheck.cpp index 37b9c1348b9..585bf9dcc99 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -42,9 +42,6 @@ CashCheck::preflight(PreflightContext const& ctx) if (!isTesSuccess(ret)) return ret; - if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[~sfAmount])) - return temMALFORMED; - if (ctx.tx.getFlags() & tfUniversalMask) { // There are no flags (other than universal) for CashCheck yet. diff --git a/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp b/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp index 4321253204d..c132df1ffda 100644 --- a/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp @@ -36,9 +36,6 @@ 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 temMALFORMED; - auto const txFlags = ctx.tx.getFlags(); if (txFlags & tfNFTokenCreateOfferMask) diff --git a/src/xrpld/app/tx/detail/NFTokenMint.cpp b/src/xrpld/app/tx/detail/NFTokenMint.cpp index bea1fee4b7e..06ff1932f3f 100644 --- a/src/xrpld/app/tx/detail/NFTokenMint.cpp +++ b/src/xrpld/app/tx/detail/NFTokenMint.cpp @@ -53,9 +53,6 @@ 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 temMALFORMED; - // 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 diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 42e9f0677ab..bf52a66debe 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -835,7 +835,7 @@ Transactor::operator()() // fixSTAmountCanonicalize predate the rulesGuard and should be replaced. STAmountSO stAmountSO{view().rules().enabled(fixSTAmountCanonicalize)}; NumberSO stNumberSO{view().rules().enabled(fixUniversalNumber)}; - CurrentTransactionRulesGuard currentTransctionRulesGuard(view().rules()); + CurrentTransactionRulesGuard currentTransactionRulesGuard(view().rules()); #ifdef DEBUG { diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/xrpld/app/tx/detail/XChainBridge.cpp index f611aed7d48..3154d605ca1 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/xrpld/app/tx/detail/XChainBridge.cpp @@ -1214,9 +1214,6 @@ attestationPreflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) - return temMALFORMED; - if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; @@ -1675,9 +1672,6 @@ 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 temMALFORMED; - if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; @@ -1916,9 +1910,6 @@ 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 temMALFORMED; - if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; @@ -2190,9 +2181,6 @@ XChainCreateAccountCommit::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - if (ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) - return temMALFORMED; - if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index 6021ca75688..98d8d5b59ee 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -1893,6 +1893,8 @@ rippleCredit( else return tecINSUFFICIENT_FUNDS; } + else + return tecNO_AUTH; } if (uReceiverID == issuer) @@ -1921,6 +1923,8 @@ rippleCredit( sfMPTAmount, sle->getFieldU64(sfMPTAmount) + saAmount.value()); view.update(sle); } + else + return tecNO_AUTH; } return tesSUCCESS; } diff --git a/src/xrpld/rpc/detail/TransactionSign.cpp b/src/xrpld/rpc/detail/TransactionSign.cpp index 1fee84c683b..77938b4924a 100644 --- a/src/xrpld/rpc/detail/TransactionSign.cpp +++ b/src/xrpld/rpc/detail/TransactionSign.cpp @@ -200,7 +200,7 @@ checkPayment( if (!tx_json.isMember(jss::Amount)) return RPC::missing_field_error("tx_json.Amount"); - STAmount amount; + STEitherAmount amount; if (!amountFromJsonNoThrow(amount, tx_json[jss::Amount])) return RPC::invalid_field_error("tx_json.Amount"); @@ -213,7 +213,8 @@ checkPayment( if (!dstAccountID) return RPC::invalid_field_error("tx_json.Destination"); - if ((doPath == false) && params.isMember(jss::build_path)) + if (((doPath == false) && params.isMember(jss::build_path)) || + (params.isMember(jss::build_path) && !amount.isIssue())) return RPC::make_error( rpcINVALID_PARAMS, "Field 'build_path' not allowed in this context."); @@ -235,7 +236,7 @@ checkPayment( else { // If no SendMax, default to Amount with sender as issuer. - sendMax = amount; + sendMax = get(amount); sendMax.setIssuer(srcAddressID); } @@ -259,7 +260,7 @@ checkPayment( *dstAccountID, sendMax.issue().currency, sendMax.issue().account, - amount, + get(amount), std::nullopt, app); if (pf.findPaths(app.config().PATH_SEARCH_OLD)) From 39e890674b35cee718bc9a112fcbeef3d052d4d4 Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Wed, 14 Aug 2024 14:09:05 -0400 Subject: [PATCH 06/11] Fix serialization. Extend unit-tests. --- include/xrpl/basics/MPTAmount.h | 1 - include/xrpl/protocol/STAmount.h | 1 - include/xrpl/protocol/STMPTAmount.h | 12 ++-- include/xrpl/protocol/Serializer.h | 4 ++ src/libxrpl/protocol/STAmount.cpp | 7 +- src/libxrpl/protocol/STEitherAmount.cpp | 21 +++--- src/libxrpl/protocol/STMPTAmount.cpp | 36 +++++++--- src/libxrpl/protocol/Serializer.cpp | 9 +++ src/test/app/MPToken_test.cpp | 90 ++++++++++++++++++------- 9 files changed, 124 insertions(+), 57 deletions(-) diff --git a/include/xrpl/basics/MPTAmount.h b/include/xrpl/basics/MPTAmount.h index d0060489b4f..0aaa3a2853f 100644 --- a/include/xrpl/basics/MPTAmount.h +++ b/include/xrpl/basics/MPTAmount.h @@ -42,7 +42,6 @@ class MPTAmount : private boost::totally_ordered, { public: using value_type = std::int64_t; - static constexpr std::uint64_t cMaxMPTValue = 0x8000000000000000; protected: value_type value_; diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index 01520834301..309a5ad2e2c 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -79,7 +79,6 @@ class STAmount final static std::uint64_t const uRateOne; //-------------------------------------------------------------------------- - STAmount(std::uint64_t value, SerialIter& sit); STAmount(SerialIter& sit); struct unchecked diff --git a/include/xrpl/protocol/STMPTAmount.h b/include/xrpl/protocol/STMPTAmount.h index fab1d21a95c..212a0c3b77b 100644 --- a/include/xrpl/protocol/STMPTAmount.h +++ b/include/xrpl/protocol/STMPTAmount.h @@ -35,10 +35,14 @@ class STMPTAmount final : public MPTAmount MPTIssue issue_; public: - static constexpr std::uint64_t cMPToken = 0x2000000000000000ull; - - STMPTAmount(std::uint64_t value, SerialIter& sit); - STMPTAmount(MPTIssue const& issue, std::uint64_t value); + static constexpr std::uint8_t cMPToken = 0x20; + static constexpr std::uint8_t cPositive = 0x40; + + STMPTAmount(SerialIter& sit); + STMPTAmount( + MPTIssue const& issue, + std::uint64_t value, + bool negative = false); STMPTAmount(MPTIssue const& issue, std::int64_t value = 0); explicit STMPTAmount(value_type value = 0); diff --git a/include/xrpl/protocol/Serializer.h b/include/xrpl/protocol/Serializer.h index d8d0b9222e3..dac1cc6132b 100644 --- a/include/xrpl/protocol/Serializer.h +++ b/include/xrpl/protocol/Serializer.h @@ -344,6 +344,10 @@ class SerialIter return static_cast(remain_); } + // peek function, throw on error + unsigned char + peek8(); + // get functions throw on error unsigned char get8(); diff --git a/src/libxrpl/protocol/STAmount.cpp b/src/libxrpl/protocol/STAmount.cpp index 024b98ff817..c2eee78d462 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -85,8 +85,9 @@ areComparable(STAmount const& v1, STAmount const& v2) v1.issue().currency == v2.issue().currency; } -STAmount::STAmount(std::uint64_t value, SerialIter& sit) +STAmount::STAmount(SerialIter& sit) { + std::uint64_t value = sit.get64(); // native if ((value & cNotNative) == 0) { @@ -156,10 +157,6 @@ STAmount::STAmount(std::uint64_t value, SerialIter& sit) canonicalize(); } -STAmount::STAmount(ripple::SerialIter& sit) : STAmount(sit.get64(), sit) -{ -} - STAmount::STAmount( Issue const& issue, mantissa_type mantissa, diff --git a/src/libxrpl/protocol/STEitherAmount.cpp b/src/libxrpl/protocol/STEitherAmount.cpp index eac706a8729..74eebcff487 100644 --- a/src/libxrpl/protocol/STEitherAmount.cpp +++ b/src/libxrpl/protocol/STEitherAmount.cpp @@ -18,6 +18,7 @@ //============================================================================== #include +#include #include #include #include @@ -29,12 +30,12 @@ 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); + auto const u8 = sit.peek8(); + if (((static_cast(u8) << 56) & STAmount::cNotNative) == 0 && + (u8 & STMPTAmount::cMPToken) != 0) + amount_.emplace(sit); else - amount_.emplace(value, sit); + amount_.emplace(sit); } STEitherAmount::STEitherAmount(XRPAmount const& amount) : amount_{amount} @@ -346,9 +347,9 @@ amountFromJson(SField const& name, Json::Value const& v) { STMPTAmount const ret = amountFromString(std::get(issue), value.asString()); - mantissa = ret.value(); + negative = ret.value() < 0; + mantissa = !negative ? ret.value() : -ret.value(); exponent = 0; - negative = false; } } else @@ -363,12 +364,10 @@ amountFromJson(SField const& name, Json::Value const& v) std::get(issue), mantissa, exponent, native, negative}}; while (exponent-- > 0) mantissa *= 10; - if (mantissa > STMPTAmount::cMaxMPTValue) + if (mantissa > maxMPTokenAmount) Throw("MPT amount out of range"); return STEitherAmount{ - name, - STMPTAmount{ - std::get(issue), static_cast(mantissa)}}; + name, STMPTAmount{std::get(issue), mantissa, negative}}; } } // namespace detail diff --git a/src/libxrpl/protocol/STMPTAmount.cpp b/src/libxrpl/protocol/STMPTAmount.cpp index d5348e89e86..554e3d7c713 100644 --- a/src/libxrpl/protocol/STMPTAmount.cpp +++ b/src/libxrpl/protocol/STMPTAmount.cpp @@ -18,6 +18,7 @@ //============================================================================== #include +#include #include #include @@ -25,12 +26,16 @@ namespace ripple { -STMPTAmount::STMPTAmount(std::uint64_t value, SerialIter& sit) +STMPTAmount::STMPTAmount(SerialIter& sit) { - assert(value & cMPToken); - value_ = (value << 8) | sit.get8(); - value_ &= ~cMPToken; + auto const mask = sit.get8(); + assert((mask & cMPToken)); + if (((mask & cMPToken) == 0)) + Throw("Not MPT Amount."); + value_ = sit.get64(); + if ((mask & cPositive) == 0) + value_ = -value_; issue_ = sit.get192(); } @@ -39,12 +44,17 @@ STMPTAmount::STMPTAmount(MPTIssue const& issue, value_type value) { } -STMPTAmount::STMPTAmount(MPTIssue const& issue, std::uint64_t value) +STMPTAmount::STMPTAmount( + MPTIssue const& issue, + std::uint64_t value, + bool negative) : issue_(issue) { - if (value > cMaxMPTValue) + if (value > maxMPTokenAmount) Throw("MPTAmount is out of range"); value_ = static_cast(value); + if (negative) + value_ = -value_; } STMPTAmount::STMPTAmount(value_type value) : MPTAmount(value) @@ -90,9 +100,11 @@ STMPTAmount::setJson(Json::Value& elem) const void STMPTAmount::add(Serializer& s) const { - auto u8 = static_cast(cMPToken >> 56); + auto u8 = cMPToken; + if (value_ >= 0) + u8 |= cPositive; s.add8(u8); - s.add64(value_); + s.add64(value_ >= 0 ? value_ : -value_); s.addBitString(issue_.getMptID()); } @@ -150,7 +162,7 @@ amountFromString(MPTIssue const& issue, std::string const& amount) { static boost::regex const reNumber( "^" // the beginning of the string - "([+]?)" // (optional) + character (MPT is positive) + "([+-]?)" // (optional) + character "(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 @@ -180,9 +192,11 @@ amountFromString(MPTIssue const& issue, std::string const& amount) if (match[3].matched) Throw("MPT must be specified as integral."); - std::int64_t mantissa; + std::uint64_t mantissa; int exponent; + bool negative = (match[1].matched && (match[1] == "-")); + if (!match[4].matched) // integer only { mantissa = @@ -208,7 +222,7 @@ amountFromString(MPTIssue const& issue, std::string const& amount) while (exponent-- > 0) mantissa *= 10; - return {issue, mantissa}; + return {issue, mantissa, negative}; } } // namespace ripple \ No newline at end of file diff --git a/src/libxrpl/protocol/Serializer.cpp b/src/libxrpl/protocol/Serializer.cpp index b99375f80dd..d9dddab74c1 100644 --- a/src/libxrpl/protocol/Serializer.cpp +++ b/src/libxrpl/protocol/Serializer.cpp @@ -358,6 +358,15 @@ SerialIter::skip(int length) remain_ -= length; } +unsigned char +SerialIter::peek8() +{ + if (remain_ < 1) + Throw("invalid SerialIter peek8"); + unsigned char t = *p_; + return t; +} + unsigned char SerialIter::get8() { diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index f68f1c65bcf..c1d00cf6679 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -787,25 +787,53 @@ class MPToken_test : public beast::unit_test::suite mptAlice.pay(alice, bob, 1, tecMPT_MAX_AMOUNT_EXCEEDED); } - // TODO: This test is currently failing! Modify the STAmount to change - // the range // Issuer fails trying to send more than the default maximum // amount allowed - // { - // Env env{*this, features}; + { + Env env{*this, features}; + + MPTTester mptAlice(env, alice, {.holders = {&bob}}); + + mptAlice.create({.ownerCount = 1, .holderCount = 0}); + + mptAlice.authorize({.account = &bob}); + + // issuer sends holder the default max amount allowed + mptAlice.pay(alice, bob, maxMPTokenAmount); + + // issuer tries to exceed max amount + mptAlice.pay(alice, bob, 1, tecMPT_MAX_AMOUNT_EXCEEDED); + } - // MPTTester mptAlice(env, alice, {.holders = {&bob}}); + // Can't pay negative amount + { + Env env{*this, features}; - // mptAlice.create({.ownerCount = 1, .holderCount = 0}); + MPTTester mptAlice(env, alice, {.holders = {&bob}}); - // mptAlice.authorize({.account = &bob}); + mptAlice.create({.ownerCount = 1, .holderCount = 0}); - // // issuer sends holder the default max amount allowed - // mptAlice.pay(alice, bob, maxMPTokenAmount); + mptAlice.authorize({.account = &bob}); - // // issuer tries to exceed max amount - // mptAlice.pay(alice, bob, 1, tecMPT_MAX_AMOUNT_EXCEEDED); - // } + mptAlice.pay(alice, bob, -1, temBAD_AMOUNT); + } + + // pay more than max amount + // fails in the json parser before + // transactor is called + { + Env env{*this, features}; + env.fund(XRP(1'000), alice, bob); + STMPTAmount mpt{ + MPTIssue{std::make_pair(1, alice.id())}, UINT64_C(100)}; + Json::Value jv; + jv[jss::secret] = alice.name(); + jv[jss::tx_json] = pay(alice, bob, mpt); + jv[jss::tx_json][jss::Amount][jss::value] = + to_string(maxMPTokenAmount + 1); + auto const jrr = env.rpc("json", "submit", to_string(jv)); + BEAST_EXPECT(jrr[jss::result][jss::error] == "invalidParams"); + } // Transfer fee { @@ -1090,10 +1118,6 @@ class MPToken_test : public beast::unit_test::suite jv[jss::SendMax] = mpt.getJson(JsonOptions::none); test(jv); } - // Clawback - if (!feature[featureMPTokensV1]) - { - } // EscrowCreate { Json::Value jv; @@ -1348,9 +1372,9 @@ class MPToken_test : public beast::unit_test::suite env(claw(alice, mpt(5), alice), ter(temMALFORMED)); env.close(); - // TODO: uncomment after stamount changes - // env(claw(alice, mpt(maxMPTokenAmount), bob), ter(temBAD_AMOUNT)); - // env.close(); + // can't clawback negative amount + env(claw(alice, mpt(-1), bob), ter(temBAD_AMOUNT)); + env.close(); } // Preclaim - clawback fails when MPTCanClawback is disabled on issuance @@ -1415,6 +1439,29 @@ class MPToken_test : public beast::unit_test::suite // issuer mptAlice.claw(carol, bob, 1, tecNO_PERMISSION); } + + // clawback more than max amount + // fails in the json parser before + // transactor is called + { + Env env(*this, features); + Account const alice{"alice"}; + Account const bob{"bob"}; + + env.fund(XRP(1000), alice, bob); + env.close(); + + auto const mpt = ripple::test::jtx::MPT( + alice.name(), std::make_pair(env.seq(alice), alice.id())); + + Json::Value jv = claw(alice, mpt(1), bob); + jv[jss::Amount][jss::value] = to_string(maxMPTokenAmount + 1); + Json::Value jv1; + jv1[jss::secret] = alice.name(); + jv1[jss::tx_json] = jv; + auto const jrr = env.rpc("json", "submit", to_string(jv1)); + BEAST_EXPECT(jrr[jss::result][jss::error] == "invalidParams"); + } } void @@ -1564,11 +1611,6 @@ class MPToken_test : public beast::unit_test::suite testMPTInvalidInTx(all); // Test parsed MPTokenIssuanceID in API response metadata - // TODO: This test exercises the parsing logic of mptID in `tx`, - // but, - // mptID is also parsed in different places like `account_tx`, - // `subscribe`, `ledger`. We should create test for these - // occurances (lower prioirity). testTxJsonMetaFields(all); } }; From 927abd52d1ad2c9ad12aa24f0e6b2d0ee4fc9afc Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Thu, 15 Aug 2024 13:10:34 -0400 Subject: [PATCH 07/11] Refactor Payment and Clawback transactors to have separate functions for MPT/IOU handling since there is little overlap between them. Remove the check for DeletableAccounts --- include/xrpl/protocol/SField.h | 1 - include/xrpl/protocol/STAmount.h | 6 + include/xrpl/protocol/STEitherAmount.h | 41 -- include/xrpl/protocol/STMPTAmount.h | 6 + src/test/app/MPToken_test.cpp | 2 +- src/xrpld/app/tx/detail/Clawback.cpp | 323 ++++++------ src/xrpld/app/tx/detail/Payment.cpp | 678 ++++++++++++++----------- src/xrpld/ledger/detail/View.cpp | 2 +- 8 files changed, 571 insertions(+), 488 deletions(-) diff --git a/include/xrpl/protocol/SField.h b/include/xrpl/protocol/SField.h index 4483f6bf3b4..4d977060dba 100644 --- a/include/xrpl/protocol/SField.h +++ b/include/xrpl/protocol/SField.h @@ -691,7 +691,6 @@ extern SField const sfXChainClaimProofSig; extern SField const sfXChainCreateAccountProofSig; extern SField const sfXChainClaimAttestationCollectionElement; extern SField const sfXChainCreateAccountAttestationCollectionElement; -extern SField const MPToken; // array of objects (common) // ARRAY/1 is reserved for end of array diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index 309a5ad2e2c..63738966b95 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -409,6 +409,12 @@ STAmount::value() const noexcept return *this; } +inline bool +isLegalNet(STAmount const& value) +{ + return !value.native() || (value.mantissa() <= STAmount::cMaxNativeN); +} + //------------------------------------------------------------------------------ // // Operators diff --git a/include/xrpl/protocol/STEitherAmount.h b/include/xrpl/protocol/STEitherAmount.h index c16c8a1d6a1..c2c8172cb9b 100644 --- a/include/xrpl/protocol/STEitherAmount.h +++ b/include/xrpl/protocol/STEitherAmount.h @@ -213,47 +213,6 @@ 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) diff --git a/include/xrpl/protocol/STMPTAmount.h b/include/xrpl/protocol/STMPTAmount.h index 212a0c3b77b..02ea3837d33 100644 --- a/include/xrpl/protocol/STMPTAmount.h +++ b/include/xrpl/protocol/STMPTAmount.h @@ -178,6 +178,12 @@ operator>=(STMPTAmount const& lhs, STMPTAmount const& rhs) return !(lhs < rhs); } +inline bool +isLegalNet(STMPTAmount const& value) +{ + return true; +} + STMPTAmount amountFromString(MPTIssue const& issue, std::string const& amount); diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index c1d00cf6679..e23352cafa0 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -1333,7 +1333,7 @@ class MPToken_test : public beast::unit_test::suite auto const mpt = ripple::test::jtx::MPT( alice.name(), std::make_pair(env.seq(alice), alice.id())); - env(claw(alice, bob["USD"](5), bob), ter(temDISABLED)); + env(claw(alice, bob["USD"](5), bob), ter(temMALFORMED)); env.close(); env(claw(alice, mpt(5)), ter(temDISABLED)); diff --git a/src/xrpld/app/tx/detail/Clawback.cpp b/src/xrpld/app/tx/detail/Clawback.cpp index 8dab279f29a..8cecb1208b7 100644 --- a/src/xrpld/app/tx/detail/Clawback.cpp +++ b/src/xrpld/app/tx/detail/Clawback.cpp @@ -30,62 +30,79 @@ namespace ripple { template static NotTEC -preflightHelper(PreflightContext const& ctx) +preflightHelper(PreflightContext const& ctx); + +template <> +NotTEC +preflightHelper(PreflightContext const& ctx) +{ + if (!ctx.rules.enabled(featureClawback)) + return temDISABLED; + + if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) + return ret; + + if (ctx.tx.getFlags() & tfClawbackMask) + return temINVALID_FLAG; + + if (ctx.tx.isFieldPresent(sfMPTokenHolder)) + return temMALFORMED; + + AccountID const issuer = ctx.tx[sfAccount]; + STAmount const clawAmount = get(ctx.tx[sfAmount]); + + // The issuer field is used for the token holder instead + AccountID const& holder = clawAmount.getIssuer(); + + if (issuer == holder || isXRP(clawAmount) || clawAmount <= beast::zero) + return temBAD_AMOUNT; + + return preflight2(ctx); +} + +template <> +NotTEC +preflightHelper(PreflightContext const& ctx) { if (!ctx.rules.enabled(featureClawback)) return temDISABLED; auto const mptHolder = ctx.tx[~sfMPTokenHolder]; - auto const clawAmount = get(ctx.tx.getFieldAmount(sfAmount)); - bool constexpr isMPT = std::is_same_v; + auto const clawAmount = get(ctx.tx[sfAmount]); - if (!ctx.rules.enabled(featureMPTokensV1) && (mptHolder || isMPT)) + if (!ctx.rules.enabled(featureMPTokensV1)) return temDISABLED; if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - if (!mptHolder && isMPT) - return temMALFORMED; - - if (mptHolder && !isMPT) + if (!mptHolder) return temMALFORMED; if (ctx.tx.getFlags() & tfClawbackMask) return temINVALID_FLAG; - AccountID const issuer = ctx.tx[sfAccount]; - - // The issuer field is used for the token holder if asset is IOU - AccountID const& holder = isMPT ? *mptHolder : clawAmount.getIssuer(); - - if constexpr (isMPT) - { - if (issuer == holder) - return temMALFORMED; + // issuer is the same as holder + if (ctx.tx[sfAccount] == *mptHolder) + return temMALFORMED; - if (clawAmount > MPTAmount{maxMPTokenAmount} || - clawAmount <= beast::zero) - return temBAD_AMOUNT; - } - else - { - if (issuer == holder || isXRP(clawAmount) || clawAmount <= beast::zero) - return temBAD_AMOUNT; - } + if (clawAmount > MPTAmount{maxMPTokenAmount} || clawAmount <= beast::zero) + return temBAD_AMOUNT; return preflight2(ctx); } template static TER -preclaimHelper(PreclaimContext const& ctx) +preclaimHelper(PreclaimContext const& ctx); + +template <> +TER +preclaimHelper(PreclaimContext const& ctx) { AccountID const issuer = ctx.tx[sfAccount]; - auto const clawAmount = get(ctx.tx.getFieldAmount(sfAmount)); - bool constexpr isMPT = std::is_same_v; - AccountID const& holder = - isMPT ? ctx.tx[sfMPTokenHolder] : clawAmount.getIssuer(); + STAmount const clawAmount = get(ctx.tx[sfAmount]); + AccountID const& holder = clawAmount.getIssuer(); auto const sleIssuer = ctx.view.read(keylet::account(issuer)); auto const sleHolder = ctx.view.read(keylet::account(holder)); @@ -95,130 +112,150 @@ preclaimHelper(PreclaimContext const& ctx) if (sleHolder->isFieldPresent(sfAMMID)) return tecAMM_ACCOUNT; - if constexpr (isMPT) - { - 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; - - if (!((*sleIssuance)[sfFlags] & lsfMPTCanClawback)) - return tecNO_PERMISSION; - - if (sleIssuance->getAccountID(sfIssuer) != issuer) - return tecNO_PERMISSION; - - if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, holder))) - return tecOBJECT_NOT_FOUND; - - if (accountHolds( - ctx.view, - holder, - mptIssue, - fhIGNORE_FREEZE, - ahIGNORE_AUTH, - ctx.j) <= beast::zero) - return tecINSUFFICIENT_FUNDS; - } - else - { - std::uint32_t const issuerFlagsIn = sleIssuer->getFieldU32(sfFlags); - - // If AllowTrustLineClawback is not set or NoFreeze is set, return no - // permission - if (!(issuerFlagsIn & lsfAllowTrustLineClawback) || - (issuerFlagsIn & lsfNoFreeze)) - return tecNO_PERMISSION; - - 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]; - - // If balance is positive, issuer must have higher address than holder - if (balance > beast::zero && issuer < holder) - return tecNO_PERMISSION; - - // If balance is negative, issuer must have lower address than holder - if (balance < beast::zero && issuer > holder) - return tecNO_PERMISSION; - - // At this point, we know that issuer and holder accounts - // are correct and a trustline exists between them. - // - // Must now explicitly check the balance to make sure - // available balance is non-zero. - // - // We can't directly check the balance of trustline because - // the available balance of a trustline is prone to new changes (eg. - // XLS-34). So we must use `accountHolds`. - if (accountHolds( - ctx.view, holder, currency, issuer, fhIGNORE_FREEZE, ctx.j) <= - beast::zero) - return tecINSUFFICIENT_FUNDS; - } + std::uint32_t const issuerFlagsIn = sleIssuer->getFieldU32(sfFlags); + + // If AllowTrustLineClawback is not set or NoFreeze is set, return no + // permission + if (!(issuerFlagsIn & lsfAllowTrustLineClawback) || + (issuerFlagsIn & lsfNoFreeze)) + return tecNO_PERMISSION; + + auto const sleRippleState = + ctx.view.read(keylet::line(holder, issuer, clawAmount.getCurrency())); + if (!sleRippleState) + return tecNO_LINE; + + STAmount const balance = (*sleRippleState)[sfBalance]; + + // If balance is positive, issuer must have higher address than holder + if (balance > beast::zero && issuer < holder) + return tecNO_PERMISSION; + + // If balance is negative, issuer must have lower address than holder + if (balance < beast::zero && issuer > holder) + return tecNO_PERMISSION; + + // At this point, we know that issuer and holder accounts + // are correct and a trustline exists between them. + // + // Must now explicitly check the balance to make sure + // available balance is non-zero. + // + // We can't directly check the balance of trustline because + // 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) + return tecINSUFFICIENT_FUNDS; return tesSUCCESS; } -template +template <> static TER -applyHelper(ApplyContext& ctx) +preclaimHelper(PreclaimContext const& ctx) { - 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 below - - if constexpr (isMPT) - { - // Get the spendable balance. Must use `accountHolds`. - STMPTAmount const spendableAmount = accountHolds( - ctx.view(), + AccountID const issuer = ctx.tx[sfAccount]; + auto const clawAmount = get(ctx.tx[sfAmount]); + AccountID const& holder = ctx.tx[sfMPTokenHolder]; + + auto const sleIssuer = ctx.view.read(keylet::account(issuer)); + auto const sleHolder = ctx.view.read(keylet::account(holder)); + if (!sleIssuer || !sleHolder) + return terNO_ACCOUNT; + + if (sleHolder->isFieldPresent(sfAMMID)) + return tecAMM_ACCOUNT; + + auto const issuanceKey = keylet::mptIssuance(clawAmount.issue().mpt()); + auto const sleIssuance = ctx.view.read(issuanceKey); + if (!sleIssuance) + return tecOBJECT_NOT_FOUND; + + if (!((*sleIssuance)[sfFlags] & lsfMPTCanClawback)) + return tecNO_PERMISSION; + + if (sleIssuance->getAccountID(sfIssuer) != issuer) + return tecNO_PERMISSION; + + if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, holder))) + return tecOBJECT_NOT_FOUND; + + if (accountHolds( + ctx.view, holder, clawAmount.issue(), fhIGNORE_FREEZE, ahIGNORE_AUTH, - ctx.journal); + ctx.j) <= beast::zero) + return tecINSUFFICIENT_FUNDS; - 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); + return tesSUCCESS; +} - return rippleCredit( - ctx.view(), - holder, - issuer, - std::min(spendableAmount, clawAmount), - true, - ctx.journal); - } +template +static TER +applyHelper(ApplyContext& ctx); + +template <> +TER +applyHelper(ApplyContext& ctx) +{ + AccountID const& issuer = ctx.tx[sfAccount]; + STAmount clawAmount = get(ctx.tx[sfAmount]); + AccountID const holder = clawAmount.getIssuer(); // cannot be reference + + // Replace the `issuer` field with issuer's account + 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); + + return rippleCredit( + ctx.view(), + holder, + issuer, + std::min(spendableAmount, clawAmount), + true, + ctx.journal); +} + +template <> +TER +applyHelper(ApplyContext& ctx) +{ + AccountID const& issuer = ctx.tx[sfAccount]; + auto clawAmount = get(ctx.tx[sfAmount]); + AccountID const holder = ctx.tx[sfMPTokenHolder]; + + // Get the spendable balance. Must use `accountHolds`. + STMPTAmount const spendableAmount = accountHolds( + ctx.view(), + holder, + clawAmount.issue(), + fhIGNORE_FREEZE, + ahIGNORE_AUTH, + ctx.journal); + + return rippleCredit( + ctx.view(), + holder, + issuer, + std::min(spendableAmount, clawAmount), + ctx.journal); } NotTEC diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 5e9293c164f..6cc88a8107a 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -56,50 +56,17 @@ makeTxConsequencesHelper(PreflightContext const& ctx) return TxConsequences{ctx.tx, beast::zero}; } -template -static TDel -getMaxSourceAmount( - TDel const& dstAmount, - std::optional const& max, - AccountID const& account) -{ - if constexpr (std::is_same_v) - { - return dstAmount; - } - else - { - if (max) - return *max; - - if (isNative(dstAmount)) - return dstAmount; - else - return STAmount( - {dstAmount.getCurrency(), account}, - dstAmount.mantissa(), - dstAmount.exponent(), - dstAmount < beast::zero); - } -} - template static NotTEC -preflightHelper(PreflightContext const& ctx) +preflightHelper(PreflightContext const& ctx); + +template <> +NotTEC +preflightHelper(PreflightContext const& ctx) { if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - if (!ctx.rules.enabled(featureMPTokensV1) && isMPT(ctx.tx[sfAmount])) - return temDISABLED; - - if constexpr (std::is_same_v) - { - if (ctx.tx.isFieldPresent(sfDeliverMin) || - ctx.tx.isFieldPresent(sfSendMax)) - return temMALFORMED; - } - auto& tx = ctx.tx; auto& j = ctx.j; @@ -118,19 +85,27 @@ preflightHelper(PreflightContext const& ctx) bool const bPaths = tx.isFieldPresent(sfPaths); bool const bMax = tx.isFieldPresent(sfSendMax); - TDel const saDstAmount(get(tx.getFieldAmount(sfAmount))); + STAmount const saDstAmount(get(tx.getFieldAmount(sfAmount))); + STAmount maxSourceAmount; auto const account = tx.getAccountID(sfAccount); - auto const maxSourceAmount = - getMaxSourceAmount(saDstAmount, tx[~sfSendMax], account); + if (bMax) + maxSourceAmount = tx.getFieldAmount(sfSendMax); + else if (saDstAmount.native()) + maxSourceAmount = saDstAmount; + else + maxSourceAmount = STAmount( + {saDstAmount.getCurrency(), account}, + saDstAmount.mantissa(), + saDstAmount.exponent(), + saDstAmount < beast::zero); auto const& uSrcCurrency = maxSourceAmount.getCurrency(); auto const& uDstCurrency = saDstAmount.getCurrency(); - bool const bXRPDirect = isXRP(uSrcCurrency) && isXRP(uDstCurrency); - bool constexpr bMPTDirect = std::is_same_v; - bool const bDirect = bXRPDirect || bMPTDirect; + // isZero() is XRP. FIX! + bool const bXRPDirect = uSrcCurrency.isZero() && uDstCurrency.isZero(); if (!isLegalNet(saDstAmount) || !isLegalNet(maxSourceAmount)) return temBAD_AMOUNT; @@ -155,14 +130,13 @@ preflightHelper(PreflightContext const& ctx) << "bad dst amount: " << saDstAmount.getFullText(); return temBAD_AMOUNT; } - if (badAsset(uSrcCurrency) || badAsset(uDstCurrency)) + if (badCurrency() == uSrcCurrency || badCurrency() == uDstCurrency) { JLOG(j.trace()) << "Malformed transaction: " - << "Bad asset."; + << "Bad currency."; return temBAD_CURRENCY; } - if (account == uDstAccountID && sameAsset(uSrcCurrency, uDstCurrency) && - !bPaths) + if (account == uDstAccountID && uSrcCurrency == uDstCurrency && !bPaths) { // You're signing yourself a payment. // If bPaths is true, you might be trying some arbitrage. @@ -179,47 +153,36 @@ preflightHelper(PreflightContext const& ctx) << "SendMax specified for XRP to XRP."; return temBAD_SEND_XRP_MAX; } - if (bDirect && bPaths) + if (bXRPDirect && bPaths) { // XRP is sent without paths. JLOG(j.trace()) << "Malformed transaction: " - << "Paths specified for XRP to XRP or MPT to MPT."; - if (bMPTDirect) - return temMALFORMED; + << "Paths specified for XRP to XRP."; 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."; - if (bMPTDirect) - return temMALFORMED; + JLOG(j.trace()) << "Malformed transaction: " + << "Partial payment specified for XRP to XRP."; return temBAD_SEND_XRP_PARTIAL; } - if (bDirect && limitQuality) + if (bXRPDirect && limitQuality) { // Consistent but redundant transaction. - JLOG(j.trace()) - << "Malformed transaction: " - << "Limit quality specified for XRP to XRP or MPT to MPT."; - if (bMPTDirect) - return temMALFORMED; + JLOG(j.trace()) << "Malformed transaction: " + << "Limit quality specified for XRP to XRP."; return temBAD_SEND_XRP_LIMIT; } - if (bDirect && !defaultPathsAllowed) + if (bXRPDirect && !defaultPathsAllowed) { // Consistent but redundant transaction. - JLOG(j.trace()) - << "Malformed transaction: " - << "No ripple direct specified for XRP to XRP or MPT to MPT."; - if (bMPTDirect) - return temMALFORMED; + JLOG(j.trace()) << "Malformed transaction: " + << "No ripple direct specified for XRP to XRP."; return temBAD_SEND_XRP_NO_DIRECT; } - auto const deliverMin = ctx.tx[~sfDeliverMin]; + auto const deliverMin = tx[~sfDeliverMin]; if (deliverMin) { if (!partialPaymentAllowed) @@ -238,33 +201,108 @@ preflightHelper(PreflightContext const& ctx) << " amount. " << dMin.getFullText(); return temBAD_AMOUNT; } - // DeliverMin is invalid if MPT and is always the same type as dstAmount - if constexpr (std::is_same_v) + if (dMin.issue() != saDstAmount.issue()) { - if (dMin.issue() != saDstAmount.issue()) - { - JLOG(j.trace()) - << "Malformed transaction: Dst issue differs " - "from " - << jss::DeliverMin.c_str() << ". " << dMin.getFullText(); - return temBAD_AMOUNT; - } - if (dMin > saDstAmount) - { - JLOG(j.trace()) - << "Malformed transaction: Dst amount less than " - << jss::DeliverMin.c_str() << ". " << dMin.getFullText(); - return temBAD_AMOUNT; - } + JLOG(j.trace()) + << "Malformed transaction: Dst issue differs " + "from " + << jss::DeliverMin.c_str() << ". " << dMin.getFullText(); + return temBAD_AMOUNT; + } + if (dMin > saDstAmount) + { + JLOG(j.trace()) + << "Malformed transaction: Dst amount less than " + << jss::DeliverMin.c_str() << ". " << dMin.getFullText(); + return temBAD_AMOUNT; } } return preflight2(ctx); } +template <> +NotTEC +preflightHelper(PreflightContext const& ctx) +{ + if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) + return ret; + + if (!ctx.rules.enabled(featureMPTokensV1)) + return temDISABLED; + + if (ctx.tx.isFieldPresent(sfDeliverMin) || + ctx.tx.isFieldPresent(sfSendMax) || ctx.tx.isFieldPresent(sfPaths)) + return temMALFORMED; + + auto& tx = ctx.tx; + auto& j = ctx.j; + + std::uint32_t const uTxFlags = tx.getFlags(); + + if (uTxFlags & tfPaymentMask) + { + JLOG(j.trace()) << "Malformed transaction: " + << "Invalid flags set."; + return temINVALID_FLAG; + } + + STMPTAmount const saDstAmount( + get(tx.getFieldAmount(sfAmount))); + + auto const account = tx.getAccountID(sfAccount); + + auto const& uDstCurrency = saDstAmount.getCurrency(); + + auto const uDstAccountID = tx.getAccountID(sfDestination); + + if (!uDstAccountID) + { + JLOG(j.trace()) << "Malformed transaction: " + << "Payment destination account not specified."; + return temDST_NEEDED; + } + if (saDstAmount <= beast::zero) + { + JLOG(j.trace()) << "Malformed transaction: " + << "bad dst amount: " << saDstAmount.getFullText(); + return temBAD_AMOUNT; + } + if (badMPT() == uDstCurrency) + { + JLOG(j.trace()) << "Malformed transaction: " + << "Bad asset."; + return temBAD_CURRENCY; + } + if (account == uDstAccountID) + { + // You're signing yourself a payment. + JLOG(j.trace()) << "Malformed transaction: " + << "Redundant payment from " << to_string(account) + << " to self without path for " + << to_string(uDstCurrency); + return temREDUNDANT; + } + if (uTxFlags & (tfPartialPayment | tfLimitQuality | tfNoRippleDirect)) + { + JLOG(j.trace()) << "Malformed transaction: invalid MPT flags: " + << uTxFlags; + return temMALFORMED; + } + + return preflight2(ctx); +} + template static TER preclaimHelper( + PreclaimContext const& ctx, + std::size_t maxPathSize, + std::size_t maxPathLength); + +template <> +TER +preclaimHelper( PreclaimContext const& ctx, std::size_t maxPathSize, std::size_t maxPathLength) @@ -276,14 +314,15 @@ preclaimHelper( auto const sendMax = ctx.tx[~sfSendMax]; AccountID const uDstAccountID(ctx.tx[sfDestination]); - TDel const saDstAmount(get(ctx.tx.getFieldAmount(sfAmount))); + STAmount const saDstAmount(get(ctx.tx[sfAmount])); auto const k = keylet::account(uDstAccountID); auto const sleDst = ctx.view.read(k); if (!sleDst) { - if constexpr (std::is_same_v) + // Destination account does not exist. + if (!saDstAmount.native()) { JLOG(ctx.j.trace()) << "Delay transaction: Destination account does not exist."; @@ -292,42 +331,29 @@ preclaimHelper( // transaction would succeed. return tecNO_DST; } - else if constexpr (std::is_same_v) + else if (ctx.view.open() && partialPaymentAllowed) { - // Destination account does not exist. - if (!saDstAmount.native()) - { - JLOG(ctx.j.trace()) - << "Delay transaction: Destination account does not exist."; + // 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 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; - } + // 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 ( @@ -346,7 +372,7 @@ preclaimHelper( } // Payment with at least one intermediate step and uses transitive balances. - if ((paths || sendMax || !isNative(saDstAmount)) && ctx.view.open()) + if ((paths || sendMax || !saDstAmount.native()) && ctx.view.open()) { STPathSet const& paths = ctx.tx.getFieldPathSet(sfPaths); @@ -362,13 +388,60 @@ preclaimHelper( return tesSUCCESS; } +template <> +TER +preclaimHelper( + PreclaimContext const& ctx, + std::size_t, + std::size_t) +{ + AccountID const uDstAccountID(ctx.tx[sfDestination]); + + auto const k = keylet::account(uDstAccountID); + auto const sleDst = ctx.view.read(k); + + if (!sleDst) + { + JLOG(ctx.j.trace()) + << "Delay transaction: Destination account does not exist."; + + // Another transaction could create the account and then this + // transaction would succeed. + return tecNO_DST; + } + else if ( + (sleDst->getFlags() & lsfRequireDestTag) && + !ctx.tx.isFieldPresent(sfDestinationTag)) + { + // The tag is basically account-specific information we don't + // understand, but we can require someone to fill it in. + + // We didn't make this test for a newly-formed account because there's + // no way for this field to be set. + JLOG(ctx.j.trace()) + << "Malformed transaction: DestinationTag required."; + + return tecDST_TAG_NEEDED; + } + + return tesSUCCESS; +} + template static TER applyHelper( + ApplyContext& ctx, + XRPAmount const& priorBalance, + XRPAmount const& sourceBalance); + +template <> +TER +applyHelper( ApplyContext& ctx, XRPAmount const& priorBalance, XRPAmount const& sourceBalance) { + AccountID const account = ctx.tx[sfAccount]; auto const deliverMin = ctx.tx[~sfDeliverMin]; // Ripple if source or destination is non-native or if there are paths. @@ -378,12 +451,20 @@ applyHelper( bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect); auto const paths = ctx.tx.isFieldPresent(sfPaths); auto const sendMax = ctx.tx[~sfSendMax]; - auto const account = ctx.tx[sfAccount]; AccountID const uDstAccountID(ctx.tx.getAccountID(sfDestination)); - auto const saDstAmount(get(ctx.tx.getFieldAmount(sfAmount))); - auto const maxSourceAmount = - getMaxSourceAmount(saDstAmount, sendMax, account); + STAmount const saDstAmount(get(ctx.tx.getFieldAmount(sfAmount))); + STAmount maxSourceAmount; + if (sendMax) + maxSourceAmount = *sendMax; + else if (saDstAmount.native()) + maxSourceAmount = saDstAmount; + else + maxSourceAmount = STAmount( + {saDstAmount.getCurrency(), account}, + saDstAmount.mantissa(), + saDstAmount.exponent(), + saDstAmount < beast::zero); JLOG(ctx.journal.trace()) << "maxSourceAmount=" << maxSourceAmount.getFullText() @@ -422,213 +503,208 @@ applyHelper( bool const depositPreauth = ctx.view().rules().enabled(featureDepositPreauth); - bool const bRipple = - (paths || sendMax || !isNative(saDstAmount)) && !isMPT(saDstAmount); + bool const bRipple = paths || sendMax || !saDstAmount.native(); // 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 constexpr (std::is_same_v) + if (bRipple) { - if (bRipple) - { - // Ripple payment with at least one intermediate step and uses - // transitive balances. + // Ripple payment with at least one intermediate step and uses + // transitive balances. - if (depositPreauth && reqDepositAuth) + if (depositPreauth && reqDepositAuth) + { + // 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 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; - } + 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 = ctx.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(&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()); - } + 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; - } - } - else - { - if (auto const ter = - requireAuth(ctx.view(), saDstAmount.issue(), account); - ter != tesSUCCESS) - return ter; - - if (auto const ter = - requireAuth(ctx.view(), saDstAmount.issue(), uDstAccountID); - ter != tesSUCCESS) - return ter; - - if (auto const ter = canTransfer( - ctx.view(), saDstAmount.issue(), account, uDstAccountID); - ter != tesSUCCESS) - return ter; - - 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(ctx.view(), account, mpt) || - isFrozen(ctx.view(), uDstAccountID, mpt))) - return tecMPT_LOCKED; - - PaymentSandbox pv(&ctx.view()); - auto const res = - accountSend(pv, account, uDstAccountID, saDstAmount, ctx.journal); - pv.apply(ctx.rawView()); - return res; + // 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; } - if constexpr (std::is_same_v) - { - assert(saDstAmount.native()); + assert(saDstAmount.native()); - // Direct XRP payment. + // Direct XRP payment. - auto const sleSrc = ctx.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 = ctx.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, ctx.tx.getFieldAmount(sfFee).xrp()); - 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; - } + 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) + // 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) { - // 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) + if (!ctx.view().exists( + keylet::depositPreauth(uDstAccountID, account))) { - if (!ctx.view().exists( - keylet::depositPreauth(uDstAccountID, account))) - { - // Get the base reserve. - XRPAmount const dstReserve{ - ctx.view().fees().accountReserve(0)}; - - if (saDstAmount > dstReserve || - sleDst->getFieldAmount(sfBalance) > dstReserve) - return tecNO_PERMISSION; - } + // Get the base reserve. + XRPAmount const dstReserve{ctx.view().fees().accountReserve(0)}; + + if (saDstAmount > dstReserve || + sleDst->getFieldAmount(sfBalance) > dstReserve) + return tecNO_PERMISSION; } } + } - // Do the arithmetic for the transfer and make the ledger change. - sleSrc->setFieldAmount(sfBalance, sourceBalance - 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, sleDst->getFieldAmount(sfBalance) + saDstAmount); - // Re-arm the password change fee if we can and need to. - if ((sleDst->getFlags() & lsfPasswordSpent)) - sleDst->clearFlag(lsfPasswordSpent); - } - else - { - Throw("Payment: expected native amount"); - } + // Re-arm the password change fee if we can and need to. + if ((sleDst->getFlags() & lsfPasswordSpent)) + sleDst->clearFlag(lsfPasswordSpent); return tesSUCCESS; } +template <> +TER +applyHelper(ApplyContext& ctx, XRPAmount const&, XRPAmount const&) +{ + auto const account = ctx.tx[sfAccount]; + + AccountID const uDstAccountID(ctx.tx.getAccountID(sfDestination)); + auto const saDstAmount(get(ctx.tx.getFieldAmount(sfAmount))); + + JLOG(ctx.journal.trace()) << " saDstAmount=" << saDstAmount.getFullText(); + + if (auto const ter = requireAuth(ctx.view(), saDstAmount.issue(), account); + ter != tesSUCCESS) + return ter; + + if (auto const ter = + requireAuth(ctx.view(), saDstAmount.issue(), uDstAccountID); + ter != tesSUCCESS) + return ter; + + if (auto const ter = canTransfer( + ctx.view(), saDstAmount.issue(), account, uDstAccountID); + ter != tesSUCCESS) + return ter; + + 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(ctx.view(), account, mpt) || + isFrozen(ctx.view(), uDstAccountID, mpt))) + return tecMPT_LOCKED; + + PaymentSandbox pv(&ctx.view()); + auto const res = + accountSend(pv, account, uDstAccountID, saDstAmount, ctx.journal); + pv.apply(ctx.rawView()); + return res; +} + TxConsequences Payment::makeTxConsequences(PreflightContext const& ctx) { diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index 98d8d5b59ee..f5303c3bb7e 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -1356,7 +1356,7 @@ rippleSend( // Direct send: redeeming IOUs and/or sending own IOUs. auto const ter = rippleCredit(view, uSenderID, uReceiverID, saAmount, j); - if (view.rules().enabled(featureDeletableAccounts) && ter != tesSUCCESS) + if (ter != tesSUCCESS) return ter; saActual = saAmount; return tesSUCCESS; From f39eeb102a1c051a0465f8f60a66ce58a014e1be Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Tue, 20 Aug 2024 13:15:43 -0400 Subject: [PATCH 08/11] Fix Linux build --- src/xrpld/app/tx/detail/Clawback.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xrpld/app/tx/detail/Clawback.cpp b/src/xrpld/app/tx/detail/Clawback.cpp index 8cecb1208b7..fa8f2a3843f 100644 --- a/src/xrpld/app/tx/detail/Clawback.cpp +++ b/src/xrpld/app/tx/detail/Clawback.cpp @@ -157,7 +157,7 @@ preclaimHelper(PreclaimContext const& ctx) } template <> -static TER +TER preclaimHelper(PreclaimContext const& ctx) { AccountID const issuer = ctx.tx[sfAccount]; From db124b0a64fd973468616fa604862b2bd6503589 Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Sat, 24 Aug 2024 14:15:55 -0400 Subject: [PATCH 09/11] Address reviewer's feedback * Replace MPT:pair with MPTID:uint192 * Remove SFieldMPT * Replated TypedFieldAmount with generic TypedFieldVariant to handle STEitherAmount * Update comments --- include/xrpl/basics/base_uint.h | 1 + include/xrpl/protocol/Indexes.h | 12 +- include/xrpl/protocol/MPTIssue.h | 32 +-- include/xrpl/protocol/SField.h | 143 ++++++----- include/xrpl/protocol/SOTemplate.h | 6 +- include/xrpl/protocol/STAmount.h | 6 - include/xrpl/protocol/STEitherAmount.h | 7 +- include/xrpl/protocol/STMPTAmount.h | 4 +- include/xrpl/protocol/STObject.h | 270 ++++++++++----------- include/xrpl/protocol/UintTypes.h | 10 +- src/libxrpl/protocol/Indexes.cpp | 23 +- src/libxrpl/protocol/MPTIssue.cpp | 44 +--- src/libxrpl/protocol/SField.cpp | 8 +- src/libxrpl/protocol/STAmount.cpp | 13 +- src/libxrpl/protocol/STEitherAmount.cpp | 4 +- src/libxrpl/protocol/STMPTAmount.cpp | 6 +- src/libxrpl/protocol/STObject.cpp | 10 +- src/libxrpl/protocol/UintTypes.cpp | 7 +- src/test/app/MPToken_test.cpp | 19 +- src/test/jtx/amount.h | 8 +- src/test/jtx/impl/mpt.cpp | 14 +- src/test/jtx/mpt.h | 11 +- src/xrpld/app/tx/detail/Clawback.cpp | 2 +- src/xrpld/app/tx/detail/Payment.cpp | 2 +- src/xrpld/ledger/View.h | 2 +- src/xrpld/ledger/detail/View.cpp | 20 +- src/xrpld/rpc/MPTokenIssuanceID.h | 2 +- src/xrpld/rpc/detail/MPTokenIssuanceID.cpp | 4 +- src/xrpld/rpc/handlers/LedgerEntry.cpp | 4 +- 29 files changed, 307 insertions(+), 387 deletions(-) diff --git a/include/xrpl/basics/base_uint.h b/include/xrpl/basics/base_uint.h index 2bb74791444..0518ee37ea5 100644 --- a/include/xrpl/basics/base_uint.h +++ b/include/xrpl/basics/base_uint.h @@ -634,6 +634,7 @@ operator<<(std::ostream& out, base_uint const& u) #ifndef __INTELLISENSE__ static_assert(sizeof(uint128) == 128 / 8, "There should be no padding bytes"); static_assert(sizeof(uint160) == 160 / 8, "There should be no padding bytes"); +static_assert(sizeof(uint192) == 192 / 8, "There should be no padding bytes"); static_assert(sizeof(uint256) == 256 / 8, "There should be no padding bytes"); #endif diff --git a/include/xrpl/protocol/Indexes.h b/include/xrpl/protocol/Indexes.h index 0f6e78d9cee..64328ce5f52 100644 --- a/include/xrpl/protocol/Indexes.h +++ b/include/xrpl/protocol/Indexes.h @@ -290,10 +290,7 @@ Keylet mptIssuance(AccountID const& issuer, std::uint32_t seq) noexcept; Keylet -mptIssuance(uint192 const& mpt) noexcept; - -Keylet -mptIssuance(ripple::MPT const& mpt) noexcept; +mptIssuance(MPTID const& mpt) noexcept; inline Keylet mptIssuance(uint256 const& issuance) @@ -302,10 +299,7 @@ mptIssuance(uint256 const& issuance) } Keylet -mptoken(MPT const& issuanceID, AccountID const& holder) noexcept; - -Keylet -mptoken(uint192 const& issuanceID, AccountID const& holder) noexcept; +mptoken(MPTID const& issuanceID, AccountID const& holder) noexcept; inline Keylet mptoken(uint256 const& mptokenKey) @@ -336,7 +330,7 @@ getTicketIndex(AccountID const& account, std::uint32_t uSequence); uint256 getTicketIndex(AccountID const& account, SeqProxy ticketSeq); -uint192 +MPTID getMptID(AccountID const& account, std::uint32_t sequence); } // namespace ripple diff --git a/include/xrpl/protocol/MPTIssue.h b/include/xrpl/protocol/MPTIssue.h index 6c01baccf08..7bc34adcf53 100644 --- a/include/xrpl/protocol/MPTIssue.h +++ b/include/xrpl/protocol/MPTIssue.h @@ -28,25 +28,17 @@ namespace ripple { class MPTIssue { private: - MPT mpt_; + MPTID mptID_; public: MPTIssue() = default; - MPTIssue(MPT const& mpt); + MPTIssue(MPTID const& id); - MPTIssue(uint192 const& id); - - AccountID const& + AccountID getIssuer() const; - MPT const& - mpt() const; - - MPT& - mpt(); - - uint192 + MPTID const& getMptID() const; friend constexpr bool @@ -59,27 +51,17 @@ class MPTIssue constexpr bool operator==(MPTIssue const& lhs, MPTIssue const& rhs) { - return lhs.mpt_ == rhs.mpt_; + return lhs.mptID_ == rhs.mptID_; } constexpr bool 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; + return !(lhs.mptID_ == rhs.mptID_); } inline bool -isXRP(uint192 const&) +isXRP(MPTID const&) { return false; } diff --git a/include/xrpl/protocol/SField.h b/include/xrpl/protocol/SField.h index 4d977060dba..ae7d38627ec 100644 --- a/include/xrpl/protocol/SField.h +++ b/include/xrpl/protocol/SField.h @@ -43,6 +43,7 @@ Some fields have a different meaning for their // Forwards class STAccount; class STEitherAmount; +class STAmount; class STIssue; class STBlob; template @@ -56,44 +57,49 @@ class STCurrency; #pragma push_macro("XMACRO") #undef XMACRO -#define XMACRO(STYPE) \ - /* special types */ \ - STYPE(STI_UNKNOWN, -2) \ - STYPE(STI_NOTPRESENT, 0) \ - STYPE(STI_UINT16, 1) \ - \ - /* types (common) */ \ - STYPE(STI_UINT32, 2) \ - STYPE(STI_UINT64, 3) \ - STYPE(STI_UINT128, 4) \ - STYPE(STI_UINT256, 5) \ - STYPE(STI_AMOUNT, 6) \ - STYPE(STI_EITHER_AMOUNT, 6) \ - STYPE(STI_VL, 7) \ - STYPE(STI_ACCOUNT, 8) \ - \ - /* 9-13 are reserved */ \ - STYPE(STI_OBJECT, 14) \ - STYPE(STI_ARRAY, 15) \ - \ - /* types (uncommon) */ \ - STYPE(STI_UINT8, 16) \ - STYPE(STI_UINT160, 17) \ - STYPE(STI_PATHSET, 18) \ - STYPE(STI_VECTOR256, 19) \ - STYPE(STI_UINT96, 20) \ - STYPE(STI_UINT192, 21) \ - STYPE(STI_UINT384, 22) \ - STYPE(STI_UINT512, 23) \ - STYPE(STI_ISSUE, 24) \ - STYPE(STI_XCHAIN_BRIDGE, 25) \ - STYPE(STI_CURRENCY, 26) \ - \ - /* high-level types */ \ - /* cannot be serialized inside other types */ \ - STYPE(STI_TRANSACTION, 10001) \ - STYPE(STI_LEDGERENTRY, 10002) \ - STYPE(STI_VALIDATION, 10003) \ +#define XMACRO(STYPE) \ + /* special types */ \ + STYPE(STI_UNKNOWN, -2) \ + STYPE(STI_NOTPRESENT, 0) \ + STYPE(STI_UINT16, 1) \ + \ + /* types (common) */ \ + STYPE(STI_UINT32, 2) \ + STYPE(STI_UINT64, 3) \ + STYPE(STI_UINT128, 4) \ + STYPE(STI_UINT256, 5) \ + /* Need two enumerators with the same value */ \ + /* so that SF_AMOUNT and SF_EITHER_AMOUNT */ \ + /* map to the same serialization id. */ \ + /* This is an artifact of */ \ + /* CONSTRUCT_TYPED_SFIELD */ \ + STYPE(STI_AMOUNT, 6) \ + STYPE(STI_EITHER_AMOUNT, 6) \ + STYPE(STI_VL, 7) \ + STYPE(STI_ACCOUNT, 8) \ + \ + /* 9-13 are reserved */ \ + STYPE(STI_OBJECT, 14) \ + STYPE(STI_ARRAY, 15) \ + \ + /* types (uncommon) */ \ + STYPE(STI_UINT8, 16) \ + STYPE(STI_UINT160, 17) \ + STYPE(STI_PATHSET, 18) \ + STYPE(STI_VECTOR256, 19) \ + STYPE(STI_UINT96, 20) \ + STYPE(STI_UINT192, 21) \ + STYPE(STI_UINT384, 22) \ + STYPE(STI_UINT512, 23) \ + STYPE(STI_ISSUE, 24) \ + STYPE(STI_XCHAIN_BRIDGE, 25) \ + STYPE(STI_CURRENCY, 26) \ + \ + /* high-level types */ \ + /* cannot be serialized inside other types */ \ + STYPE(STI_TRANSACTION, 10001) \ + STYPE(STI_LEDGERENTRY, 10002) \ + STYPE(STI_VALIDATION, 10003) \ STYPE(STI_METADATA, 10004) #pragma push_macro("TO_ENUM") @@ -302,8 +308,6 @@ class SField static std::map knownCodeToField; }; -enum class SFieldMPT { None, Yes, No }; - /** A field with a type known at compile time. */ template struct TypedField : SField @@ -325,38 +329,49 @@ struct OptionaledField } }; -template -inline OptionaledField -operator~(TypedField const& f) -{ - return OptionaledField(f); -} - -// Amount fields - -/** A field with a type known at compile time. */ -template -struct TypedFieldAmount : public TypedField +/** A field representing a variant with a type known at compile time. + * First template parameter is the variant type, the second + * template parameter is one of its alternative types. A variant field + * enables STObject::operator[]() overload to return the specified + * alternative type. For instance, STEitherAmount is a variant of STAmount + * and STMPTAmount. Some Amount fields, like SFee, don't support MPT + * and are declared as TypedVariantField. + * Conversely, sfAmount field supports MPT and is declared as + * TypedVariantField. Then tx[sfFee] always returns STAmount, + * while tx[sfAmount] returns STEitherAmount and the caller has to get + * the specific type that STEitherAmount holds. + */ +template +struct TypedVariantField : TypedField { template - explicit TypedFieldAmount(private_access_tag_t pat, Args&&... args); + explicit TypedVariantField( + SField::private_access_tag_t pat, + Args&&... args); }; -/** Indicate std::optional field semantics. */ -template -struct OptionaledFieldAmount : public OptionaledField +/** Indicate std::optional variant field semantics. */ +template +struct OptionaledVariantField : OptionaledField { - explicit OptionaledFieldAmount(TypedFieldAmount const& f_) - : OptionaledField(f_) + explicit OptionaledVariantField(TypedVariantField const& f_) + : OptionaledField(f_) { } }; -template -inline OptionaledFieldAmount -operator~(TypedFieldAmount const& f) +template +inline OptionaledField +operator~(TypedField const& f) +{ + return OptionaledField(f); +} + +template +inline OptionaledVariantField +operator~(TypedVariantField const& f) { - return OptionaledFieldAmount(f); + return OptionaledVariantField(f); } //------------------------------------------------------------------------------ @@ -376,8 +391,8 @@ using SF_UINT384 = TypedField>; using SF_UINT512 = TypedField>; using SF_ACCOUNT = TypedField; -using SF_AMOUNT = TypedFieldAmount; -using SF_EITHER_AMOUNT = TypedFieldAmount; +using SF_AMOUNT = TypedVariantField; +using SF_EITHER_AMOUNT = TypedVariantField; 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 56a0d471150..f6c231d1310 100644 --- a/include/xrpl/protocol/SOTemplate.h +++ b/include/xrpl/protocol/SOTemplate.h @@ -73,13 +73,15 @@ class SOElement { init(fieldName); } - SOElement(TypedFieldAmount const& fieldName, SOEStyle style) + SOElement( + TypedVariantField const& fieldName, + SOEStyle style) : sField_(fieldName), style_(style), supportMpt_(soeMPTNotSupported) { init(fieldName); } SOElement( - TypedFieldAmount const& fieldName, + TypedVariantField const& fieldName, SOEStyle style, SOETxMPTAmount supportMpt = soeMPTNotSupported) : sField_(fieldName), style_(style), supportMpt_(supportMpt) diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index 63738966b95..6cd5cc7a557 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -32,10 +32,6 @@ namespace ripple { -struct int64_tag_t -{ -}; - // Internal form: // 1: If amount is zero, then value is zero and offset is -100 // 2: Otherwise: @@ -102,8 +98,6 @@ class STAmount final bool native, bool negative); - STAmount(std::int64_t mantissa, int64_tag_t); - explicit STAmount(std::uint64_t mantissa = 0, bool negative = false); STAmount( diff --git a/include/xrpl/protocol/STEitherAmount.h b/include/xrpl/protocol/STEitherAmount.h index c2c8172cb9b..d4b5756451f 100644 --- a/include/xrpl/protocol/STEitherAmount.h +++ b/include/xrpl/protocol/STEitherAmount.h @@ -29,11 +29,6 @@ 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>; @@ -100,7 +95,7 @@ class STEitherAmount : public STBase, public CountedObject std::variant& getValue(); - AccountID const& + AccountID getIssuer() const; bool diff --git a/include/xrpl/protocol/STMPTAmount.h b/include/xrpl/protocol/STMPTAmount.h index 02ea3837d33..8e467b6453b 100644 --- a/include/xrpl/protocol/STMPTAmount.h +++ b/include/xrpl/protocol/STMPTAmount.h @@ -66,13 +66,13 @@ class STMPTAmount final : public MPTAmount bool isDefault() const; - AccountID const& + AccountID getIssuer() const; MPTIssue const& issue() const; - uint192 + MPTID const& getCurrency() const; void diff --git a/include/xrpl/protocol/STObject.h b/include/xrpl/protocol/STObject.h index 26cdf5a55ea..4d2b061f88a 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -54,15 +54,12 @@ throwFieldNotFound(SField const& field) class STObject : public STBase, public CountedObject { - template - using ValueType = std::conditional::type; - // Proxy value for a STBase derived class - template + template class Proxy; - template + template class ValueProxy; - template + template class OptionalProxy; struct Transform @@ -240,11 +237,10 @@ class STObject : public STBase, public CountedObject Blob getFieldVL(SField const& field) const; STEitherAmount const& - getFieldEitherAmount(SField const& field) const; - STEitherAmount const& getFieldAmount(SField const& field) const; STAmount const& - getFieldAmount(TypedFieldAmount const& field) const; + getFieldAmount( + TypedVariantField const& field) const; STPathSet const& getFieldPathSet(SField const& field) const; const STVector256& @@ -265,11 +261,11 @@ class STObject : public STBase, public CountedObject typename T::value_type operator[](TypedField const& f) const; - /** Overload for amount fields + /** Overload for amount field */ - template - typename ValueType::value_type - operator[](TypedFieldAmount const& f) const; + template + H::value_type + operator[](TypedVariantField const& f) const; /** Get the value of a field as a std::optional @@ -283,12 +279,11 @@ class STObject : public STBase, public CountedObject std::optional> operator[](OptionaledField const& of) const; - /** Overload for amount fields + /** Overload for a variant field */ - template - std::optional< - std::decay_t::value_type>> - operator[](OptionaledFieldAmount const& of) const; + template + std::optional> + operator[](OptionaledVariantField const& of) const; /** Get a modifiable field value. @param A TypedField built from an SField value representing the desired @@ -301,11 +296,11 @@ class STObject : public STBase, public CountedObject ValueProxy operator[](TypedField const& f); - /** Overload for amount fields + /** Overload for a variant field */ - template - ValueProxy - operator[](TypedFieldAmount const& f); + template + ValueProxy + operator[](TypedVariantField const& f); /** Return a modifiable field value as std::optional @@ -320,11 +315,11 @@ class STObject : public STBase, public CountedObject OptionalProxy operator[](OptionaledField const& of); - /** Overload for amount fields + /** Overload for a variant field */ - template - OptionalProxy - operator[](OptionaledFieldAmount const& of); + template + OptionalProxy + operator[](OptionaledVariantField const& of); /** Get the value of a field. @param A TypedField built from an SField value representing the desired @@ -337,11 +332,11 @@ class STObject : public STBase, public CountedObject typename T::value_type at(TypedField const& f) const; - /** Overload for amount fields + /** Overload for a variant field */ - template - typename ValueType::value_type - at(TypedFieldAmount const& f) const; + template + H::value_type + at(TypedVariantField const& f) const; /** Get the value of a field as std::optional @@ -355,12 +350,11 @@ class STObject : public STBase, public CountedObject std::optional> at(OptionaledField const& of) const; - /** Overload for amount fields + /** Overload for a variant field */ - template - std::optional< - std::decay_t::value_type>> - at(OptionaledFieldAmount const& of) const; + template + std::optional> + at(OptionaledVariantField const& of) const; /** Get a modifiable field value. @param A TypedField built from an SField value representing the desired @@ -373,11 +367,11 @@ class STObject : public STBase, public CountedObject ValueProxy at(TypedField const& f); - /** Overload for amount fields that don't support MPT + /** Overload for a variant field */ - template - ValueProxy - at(TypedFieldAmount const& f); + template + ValueProxy + at(TypedVariantField const& f); /** Return a modifiable field value as std::optional @@ -392,11 +386,11 @@ class STObject : public STBase, public CountedObject OptionalProxy at(OptionaledField const& of); - /** Overload for amount fields that don't support MPT + /** Overload for a variant field */ - template - OptionalProxy - at(OptionaledFieldAmount const& of); + template + OptionalProxy + at(OptionaledVariantField const& of); /** Set a field. if the field already exists, it is replaced. @@ -541,11 +535,11 @@ class STObject : public STBase, public CountedObject //------------------------------------------------------------------------------ -template +template class STObject::Proxy { protected: - using value_type = ValueType::value_type; + using value_type = H::value_type; STObject* st_; SOEStyle style_; @@ -566,11 +560,11 @@ class STObject::Proxy assign(U&& u); }; -template -class STObject::ValueProxy : private Proxy +template +class STObject::ValueProxy : private Proxy { private: - using value_type = ValueType::value_type; + using value_type = H::value_type; public: ValueProxy(ValueProxy const&) = default; @@ -589,11 +583,11 @@ class STObject::ValueProxy : private Proxy ValueProxy(STObject* st, TypedField const* f); }; -template -class STObject::OptionalProxy : private Proxy +template +class STObject::OptionalProxy : private Proxy { private: - using value_type = ValueType::value_type; + using value_type = H::value_type; using optional_type = std::optional::type>; @@ -725,8 +719,8 @@ class STObject::FieldErr : public std::runtime_error using std::runtime_error::runtime_error; }; -template -STObject::Proxy::Proxy(STObject* st, TypedField const* f) +template +STObject::Proxy::Proxy(STObject* st, TypedField const* f) : st_(st), f_(f) { if (st_->mType) @@ -743,32 +737,18 @@ STObject::Proxy::Proxy(STObject* st, TypedField const* f) } } -template +template auto -STObject::Proxy::value() const -> value_type +STObject::Proxy::value() const -> value_type { auto const t = find(); if (t) - return t->value(); - if (style_ == soeINVALID) { - Throw("Value requested from invalid STObject."); - } - if (style_ != soeDEFAULT) - { - Throw( - "Missing field '" + this->f_->getName() + "'"); + if constexpr (std::is_same_v) + return t->value(); + else + return get(t->value()); } - return value_type{}; -} - -template <> -inline auto -STObject::Proxy::value() const -> STAmount -{ - auto const t = find(); - if (t) - return get(t->value()); if (style_ == soeINVALID) { Throw("Value requested from invalid STObject."); @@ -778,20 +758,20 @@ STObject::Proxy::value() const -> STAmount Throw( "Missing field '" + this->f_->getName() + "'"); } - return STAmount{}; + return value_type{}; } -template +template inline T const* -STObject::Proxy::find() const +STObject::Proxy::find() const { return dynamic_cast(st_->peekAtPField(*f_)); } -template +template template void -STObject::Proxy::assign(U&& u) +STObject::Proxy::assign(U&& u) { if (style_ == soeDEFAULT && u == value_type{}) { @@ -809,68 +789,68 @@ STObject::Proxy::assign(U&& u) //------------------------------------------------------------------------------ -template +template template -std::enable_if_t, STObject::ValueProxy&> -STObject::ValueProxy::operator=(U&& u) +std::enable_if_t, STObject::ValueProxy&> +STObject::ValueProxy::operator=(U&& u) { this->assign(std::forward(u)); return *this; } -template -STObject::ValueProxy::operator value_type() const +template +STObject::ValueProxy::operator value_type() const { return this->value(); } -template -STObject::ValueProxy::ValueProxy(STObject* st, TypedField const* f) - : Proxy(st, f) +template +STObject::ValueProxy::ValueProxy(STObject* st, TypedField const* f) + : Proxy(st, f) { } //------------------------------------------------------------------------------ -template -STObject::OptionalProxy::operator bool() const noexcept +template +STObject::OptionalProxy::operator bool() const noexcept { return engaged(); } -template +template auto -STObject::OptionalProxy::operator*() const -> value_type +STObject::OptionalProxy::operator*() const -> value_type { return this->value(); } -template -STObject::OptionalProxy::operator typename STObject::OptionalProxy:: +template +STObject::OptionalProxy::operator typename STObject::OptionalProxy:: optional_type() const { return optional_value(); } -template -typename STObject::OptionalProxy::optional_type -STObject::OptionalProxy::operator~() const +template +typename STObject::OptionalProxy::optional_type +STObject::OptionalProxy::operator~() const { return optional_value(); } -template +template auto -STObject::OptionalProxy::operator=(std::nullopt_t const&) +STObject::OptionalProxy::operator=(std::nullopt_t const&) -> OptionalProxy& { disengage(); return *this; } -template +template auto -STObject::OptionalProxy::operator=(optional_type&& v) -> OptionalProxy& +STObject::OptionalProxy::operator=(optional_type&& v) -> OptionalProxy& { if (v) this->assign(std::move(*v)); @@ -879,9 +859,9 @@ STObject::OptionalProxy::operator=(optional_type&& v) -> OptionalProxy& return *this; } -template +template auto -STObject::OptionalProxy::operator=(optional_type const& v) +STObject::OptionalProxy::operator=(optional_type const& v) -> OptionalProxy& { if (v) @@ -891,33 +871,33 @@ STObject::OptionalProxy::operator=(optional_type const& v) return *this; } -template +template template -std::enable_if_t, STObject::OptionalProxy&> -STObject::OptionalProxy::operator=(U&& u) +std::enable_if_t, STObject::OptionalProxy&> +STObject::OptionalProxy::operator=(U&& u) { this->assign(std::forward(u)); return *this; } -template -STObject::OptionalProxy::OptionalProxy( +template +STObject::OptionalProxy::OptionalProxy( STObject* st, TypedField const* f) - : Proxy(st, f) + : Proxy(st, f) { } -template +template bool -STObject::OptionalProxy::engaged() const noexcept +STObject::OptionalProxy::engaged() const noexcept { return this->style_ == soeDEFAULT || this->find() != nullptr; } -template +template void -STObject::OptionalProxy::disengage() +STObject::OptionalProxy::disengage() { if (this->style_ == soeREQUIRED || this->style_ == soeDEFAULT) Throw( @@ -928,18 +908,18 @@ STObject::OptionalProxy::disengage() this->st_->makeFieldAbsent(*this->f_); } -template +template auto -STObject::OptionalProxy::optional_value() const -> optional_type +STObject::OptionalProxy::optional_value() const -> optional_type { if (!engaged()) return std::nullopt; return this->value(); } -template -typename STObject::OptionalProxy::value_type -STObject::OptionalProxy::value_or(value_type val) const +template +typename STObject::OptionalProxy::value_type +STObject::OptionalProxy::value_or(value_type val) const { return engaged() ? this->value() : val; } @@ -1051,9 +1031,9 @@ STObject::operator[](TypedField const& f) const return at(f); } -template -typename STObject::ValueType::value_type -STObject::operator[](TypedFieldAmount const& f) const +template +inline H::value_type +STObject::operator[](TypedVariantField const& f) const { return at(f); } @@ -1065,10 +1045,9 @@ STObject::operator[](OptionaledField const& of) const return at(of); } -template -std::optional< - std::decay_t::value_type>> -STObject::operator[](OptionaledFieldAmount const& of) const +template +inline std::optional> +STObject::operator[](OptionaledVariantField const& of) const { return at(of); } @@ -1080,10 +1059,9 @@ STObject::operator[](TypedField const& f) -> ValueProxy return at(f); } -template +template inline auto -STObject::operator[](TypedFieldAmount const& f) - -> ValueProxy +STObject::operator[](TypedVariantField const& f) -> ValueProxy { return at(f); } @@ -1095,10 +1073,10 @@ STObject::operator[](OptionaledField const& of) -> OptionalProxy return at(of); } -template +template inline auto -STObject::operator[](OptionaledFieldAmount const& of) - -> OptionalProxy +STObject::operator[](OptionaledVariantField const& of) + -> OptionalProxy { return at(of); } @@ -1137,14 +1115,14 @@ STObject::at(TypedField const& f) const return atImpl(f); } -template -typename STObject::ValueType::value_type -STObject::at(TypedFieldAmount const& f) const +template +inline H::value_type +STObject::at(TypedVariantField const& f) const { - if constexpr (M == SFieldMPT::Yes) + if constexpr (std::is_same_v) return atImpl(f); else - return get(atImpl(f)); + return get(atImpl(f)); } template @@ -1174,15 +1152,14 @@ STObject::at(OptionaledField const& of) const return atImpl(of); } -template -std::optional< - std::decay_t::value_type>> -STObject::at(OptionaledFieldAmount const& of) const +template +inline std::optional> +STObject::at(OptionaledVariantField const& of) const { - if constexpr (M == SFieldMPT::Yes) + if constexpr (std::is_same_v) return atImpl(of); else - return get(atImpl(of)); + return get(atImpl(of)); } template @@ -1192,11 +1169,11 @@ STObject::at(TypedField const& f) -> ValueProxy return ValueProxy(this, &f); } -template +template inline auto -STObject::at(TypedFieldAmount const& f) -> ValueProxy +STObject::at(TypedVariantField const& f) -> ValueProxy { - return ValueProxy(this, &f); + return ValueProxy(this, &f); } template @@ -1206,12 +1183,11 @@ STObject::at(OptionaledField const& of) -> OptionalProxy return OptionalProxy(this, of.f); } -template +template inline auto -STObject::at(OptionaledFieldAmount const& of) - -> OptionalProxy +STObject::at(OptionaledVariantField const& of) -> OptionalProxy { - return OptionalProxy(this, of.f); + return OptionalProxy(this, of.f); } template diff --git a/include/xrpl/protocol/UintTypes.h b/include/xrpl/protocol/UintTypes.h index 8ccca8743ae..cf34366262f 100644 --- a/include/xrpl/protocol/UintTypes.h +++ b/include/xrpl/protocol/UintTypes.h @@ -58,8 +58,12 @@ using Currency = base_uint<160, detail::CurrencyTag>; /** NodeID is a 160-bit hash representing one node. */ using NodeID = base_uint<160, detail::NodeIDTag>; -/** MPT is a 192-bit hash representing MPTID. */ -using MPT = std::pair; +/** MPT is a 192-bit hash representing MPTID. + * Currently MPTID is the only 192-bit field. + * If other 192-bit fields with different semantics + * are added then MPTID must change to a unique tag. + */ +using MPTID = base_uint<192>; /** XRP currency. */ Currency const& @@ -70,7 +74,7 @@ Currency const& noCurrency(); /** A placeholder for empty MPTID. */ -MPT const& +MPTID const& noMPT(); /** We deliberately disallow the currency that looks like "XRP" because too diff --git a/src/libxrpl/protocol/Indexes.cpp b/src/libxrpl/protocol/Indexes.cpp index b402111972c..8014d8d4dcd 100644 --- a/src/libxrpl/protocol/Indexes.cpp +++ b/src/libxrpl/protocol/Indexes.cpp @@ -137,10 +137,10 @@ getTicketIndex(AccountID const& account, SeqProxy ticketSeq) return getTicketIndex(account, ticketSeq.value()); } -uint192 +MPTID getMptID(AccountID const& account, std::uint32_t sequence) { - uint192 u; + MPTID u; sequence = boost::endian::native_to_big(sequence); memcpy(u.data(), &sequence, sizeof(sequence)); memcpy(u.data() + sizeof(sequence), account.data(), sizeof(account)); @@ -470,31 +470,18 @@ mptIssuance(AccountID const& issuer, std::uint32_t seq) noexcept } Keylet -mptIssuance(ripple::MPT const& mpt) noexcept -{ - return mptIssuance(mpt.second, mpt.first); -} - -Keylet -mptIssuance(uint192 const& mpt) noexcept +mptIssuance(MPTID const& id) noexcept { return { - ltMPTOKEN_ISSUANCE, indexHash(LedgerNameSpace::MPTOKEN_ISSUANCE, mpt)}; + ltMPTOKEN_ISSUANCE, indexHash(LedgerNameSpace::MPTOKEN_ISSUANCE, id)}; } Keylet -mptoken(uint192 const& issuanceID, AccountID const& holder) noexcept +mptoken(MPTID const& issuanceID, AccountID const& holder) noexcept { return mptoken(mptIssuance(issuanceID).key, holder); } -Keylet -mptoken(MPT const& mptID, AccountID const& holder) noexcept -{ - return mptoken( - mptIssuance(getMptID(mptID.second, mptID.first)).key, holder); -} - Keylet mptoken(uint256 const& issuanceKey, AccountID const& holder) noexcept { diff --git a/src/libxrpl/protocol/MPTIssue.cpp b/src/libxrpl/protocol/MPTIssue.cpp index 4737f46a938..629f00be2d2 100644 --- a/src/libxrpl/protocol/MPTIssue.cpp +++ b/src/libxrpl/protocol/MPTIssue.cpp @@ -23,49 +23,27 @@ namespace ripple { -MPTIssue::MPTIssue(MPT const& mpt) : mpt_(mpt) +MPTIssue::MPTIssue(MPTID const& id) : mptID_(id) { } -MPTIssue::MPTIssue(uint192 const& id) -{ - mpt_ = getMPT(id); -} - -AccountID const& +AccountID MPTIssue::getIssuer() const { - return mpt_.second; -} - -MPT const& -MPTIssue::mpt() const -{ - return mpt_; -} + AccountID account; -MPT& -MPTIssue::mpt() -{ - return mpt_; + // copy from id skipping the sequence + memcpy( + account.data(), + mptID_.data() + sizeof(std::uint32_t), + sizeof(AccountID)); + return account; } -uint192 +MPTID const& 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); + return mptID_; } Json::Value diff --git a/src/libxrpl/protocol/SField.cpp b/src/libxrpl/protocol/SField.cpp index 38c2148c980..19b55607435 100644 --- a/src/libxrpl/protocol/SField.cpp +++ b/src/libxrpl/protocol/SField.cpp @@ -45,10 +45,12 @@ TypedField::TypedField(private_access_tag_t pat, Args&&... args) { } -template +template template -TypedFieldAmount::TypedFieldAmount(private_access_tag_t pat, Args&&... args) - : TypedField(pat, std::forward(args)...) +TypedVariantField::TypedVariantField( + SField::private_access_tag_t pat, + Args&&... args) + : TypedField(pat, std::forward(args)...) { } diff --git a/src/libxrpl/protocol/STAmount.cpp b/src/libxrpl/protocol/STAmount.cpp index c2eee78d462..99eb4b829c5 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -187,12 +187,6 @@ STAmount::STAmount( canonicalize(); } -STAmount::STAmount(std::int64_t mantissa, int64_tag_t) - : mOffset(0), mIsNative(true) -{ - set(mantissa); -} - //------------------------------------------------------------------------------ STAmount::STAmount(std::uint64_t mantissa, bool negative) @@ -351,7 +345,12 @@ operator+(STAmount const& v1, STAmount const& v2) } if (v1.native()) - return {getSNValue(v1) + getSNValue(v2), int64_tag_t{}}; + { + auto const res = getSNValue(v1) + getSNValue(v2); + auto const negative = res < 0; + return STAmount{ + static_cast(negative ? -res : res), negative}; + } if (getSTNumberSwitchover()) { diff --git a/src/libxrpl/protocol/STEitherAmount.cpp b/src/libxrpl/protocol/STEitherAmount.cpp index 74eebcff487..2fc4d3f00de 100644 --- a/src/libxrpl/protocol/STEitherAmount.cpp +++ b/src/libxrpl/protocol/STEitherAmount.cpp @@ -183,7 +183,7 @@ STEitherAmount::getValue() return amount_; } -AccountID const& +AccountID STEitherAmount::getIssuer() const { if (isIssue()) @@ -297,7 +297,7 @@ amountFromJson(SField const& name, Json::Value const& v) if (isMPT) { // sequence (32 bits) + account (160 bits) - uint192 u; + MPTID u; if (!u.parseHex(currencyOrMPTID.asString())) Throw("invalid MPTokenIssuanceID"); issue = u; diff --git a/src/libxrpl/protocol/STMPTAmount.cpp b/src/libxrpl/protocol/STMPTAmount.cpp index 554e3d7c713..c4e40e3ce36 100644 --- a/src/libxrpl/protocol/STMPTAmount.cpp +++ b/src/libxrpl/protocol/STMPTAmount.cpp @@ -111,16 +111,16 @@ STMPTAmount::add(Serializer& s) const bool STMPTAmount::isDefault() const { - return value_ == 0 && issue_ == badMPT(); + return value_ == 0 && issue_ == noMPT(); } -AccountID const& +AccountID STMPTAmount::getIssuer() const { return issue_.getIssuer(); } -uint192 +MPTID const& STMPTAmount::getCurrency() const { return issue_.getMptID(); diff --git a/src/libxrpl/protocol/STObject.cpp b/src/libxrpl/protocol/STObject.cpp index ec23b16793d..ccd34d16d33 100644 --- a/src/libxrpl/protocol/STObject.cpp +++ b/src/libxrpl/protocol/STObject.cpp @@ -630,13 +630,6 @@ STObject::getFieldVL(SField const& field) const return Blob(b.data(), b.data() + b.size()); } -STEitherAmount const& -STObject::getFieldEitherAmount(SField const& field) const -{ - static STEitherAmount const empty{}; - return getFieldByConstRef(field, empty); -} - STEitherAmount const& STObject::getFieldAmount(SField const& field) const { @@ -645,7 +638,8 @@ STObject::getFieldAmount(SField const& field) const } STAmount const& -STObject::getFieldAmount(TypedFieldAmount const& field) const +STObject::getFieldAmount( + TypedVariantField const& field) const { static STEitherAmount const empty{}; return get(getFieldByConstRef(field, empty)); diff --git a/src/libxrpl/protocol/UintTypes.cpp b/src/libxrpl/protocol/UintTypes.cpp index 9487e5dcbf7..e50e52c1c30 100644 --- a/src/libxrpl/protocol/UintTypes.cpp +++ b/src/libxrpl/protocol/UintTypes.cpp @@ -18,6 +18,7 @@ //============================================================================== #include +#include #include #include #include @@ -125,11 +126,11 @@ noCurrency() return currency; } -MPT const& +MPTID const& noMPT() { - static MPT const mpt{0, noAccount()}; - return mpt; + static MPTID const id = getMptID(noAccount(), 0); + return id; } Currency const& diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index e23352cafa0..757e9e9db60 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -824,8 +824,7 @@ class MPToken_test : public beast::unit_test::suite { Env env{*this, features}; env.fund(XRP(1'000), alice, bob); - STMPTAmount mpt{ - MPTIssue{std::make_pair(1, alice.id())}, UINT64_C(100)}; + STMPTAmount mpt{MPTIssue{getMptID(alice.id(), 1)}, UINT64_C(100)}; Json::Value jv; jv[jss::secret] = alice.name(); jv[jss::tx_json] = pay(alice, bob, mpt); @@ -904,8 +903,7 @@ class MPToken_test : public beast::unit_test::suite env.fund(XRP(1'000), alice); env.fund(XRP(1'000), bob); - STMPTAmount mpt{ - MPTIssue{std::make_pair(1, alice.id())}, UINT64_C(100)}; + STMPTAmount mpt{MPTIssue{getMptID(alice.id(), 1)}, UINT64_C(100)}; env(pay(alice, bob, mpt), ter(temDISABLED)); } @@ -919,8 +917,7 @@ class MPToken_test : public beast::unit_test::suite env.fund(XRP(1'000), alice); env.fund(XRP(1'000), carol); - STMPTAmount mpt{ - MPTIssue{std::make_pair(1, alice.id())}, UINT64_C(100)}; + STMPTAmount mpt{MPTIssue{getMptID(alice.id(), 1)}, UINT64_C(100)}; Json::Value jv; jv[jss::secret] = alice.name(); @@ -1007,7 +1004,7 @@ class MPToken_test : public beast::unit_test::suite Account const alice("alice"); auto const USD = alice["USD"]; Account const carol("carol"); - MPTIssue issue(std::make_pair(1, alice.id())); + MPTIssue issue(getMptID(alice.id(), 1)); STMPTAmount mpt{issue, UINT64_C(100)}; auto const jvb = bridge(alice, USD, alice, USD); for (auto const& feature : {features, features - featureMPTokensV1}) @@ -1331,7 +1328,7 @@ class MPToken_test : public beast::unit_test::suite auto const USD = alice["USD"]; auto const mpt = ripple::test::jtx::MPT( - alice.name(), std::make_pair(env.seq(alice), alice.id())); + alice.name(), getMptID(alice.id(), env.seq(alice))); env(claw(alice, bob["USD"](5), bob), ter(temMALFORMED)); env.close(); @@ -1354,7 +1351,7 @@ class MPToken_test : public beast::unit_test::suite auto const USD = alice["USD"]; auto const mpt = ripple::test::jtx::MPT( - alice.name(), std::make_pair(env.seq(alice), alice.id())); + alice.name(), getMptID(alice.id(), env.seq(alice))); // clawing back IOU from a MPT holder fails env(claw(alice, bob["USD"](5), bob), ter(temMALFORMED)); @@ -1413,7 +1410,7 @@ class MPToken_test : public beast::unit_test::suite MPTTester mptAlice(env, alice, {.holders = {&bob}}); auto const fakeMpt = ripple::test::jtx::MPT( - alice.name(), std::make_pair(env.seq(alice), alice.id())); + alice.name(), getMptID(alice.id(), env.seq(alice))); // issuer tries to clawback MPT where issuance doesn't exist env(claw(alice, fakeMpt(5), bob), ter(tecOBJECT_NOT_FOUND)); @@ -1452,7 +1449,7 @@ class MPToken_test : public beast::unit_test::suite env.close(); auto const mpt = ripple::test::jtx::MPT( - alice.name(), std::make_pair(env.seq(alice), alice.id())); + alice.name(), getMptID(alice.id(), env.seq(alice))); Json::Value jv = claw(alice, mpt(1), bob); jv[jss::Amount][jss::value] = to_string(maxMPTokenAmount + 1); diff --git a/src/test/jtx/amount.h b/src/test/jtx/amount.h index 70a0bf51f34..fb5cef102ef 100644 --- a/src/test/jtx/amount.h +++ b/src/test/jtx/amount.h @@ -377,14 +377,14 @@ class MPT { public: std::string name; - ripple::MPT mptID; + ripple::MPTID mptID; - MPT(std::string const& n, ripple::MPT const& mptID_) + MPT(std::string const& n, ripple::MPTID const& mptID_) : name(n), mptID(mptID_) { } - ripple::MPT const& + ripple::MPTID const& mpt() const { return mptID; @@ -395,7 +395,7 @@ class MPT This allows passing an MPT value where an Issue is expected. */ - operator ripple::MPT() const + operator ripple::MPTID() const { return mpt(); } diff --git a/src/test/jtx/impl/mpt.cpp b/src/test/jtx/impl/mpt.cpp index 909154fb720..88f206796bf 100644 --- a/src/test/jtx/impl/mpt.cpp +++ b/src/test/jtx/impl/mpt.cpp @@ -94,8 +94,7 @@ MPTTester::create(const MPTCreate& arg) { if (issuanceKey_) Throw("MPT can't be reused"); - mpt_ = std::make_pair(env_.seq(issuer_), issuer_.id()); - id_ = getMptID(issuer_.id(), mpt_->first); + id_ = getMptID(issuer_.id(), env_.seq(issuer_)); issuanceKey_ = keylet::mptIssuance(*id_).key; Json::Value jv; jv[sfAccount.jsonName] = issuer_.human(); @@ -119,7 +118,6 @@ MPTTester::create(const MPTCreate& arg) id_.reset(); issuanceKey_.reset(); - mpt_.reset(); } else if (arg.flags) env_.require(mptflags(*this, *arg.flags)); @@ -306,7 +304,7 @@ MPTTester::pay( std::int64_t amount, std::optional err) { - assert(mpt_); + assert(id_); auto const srcAmt = getAmount(src); auto const destAmt = getAmount(dest); auto const outstnAmt = getAmount(issuer_); @@ -331,7 +329,7 @@ MPTTester::pay( else { auto const actual = static_cast( - amount * Number{transferRate(*env_.current(), *mpt_).value, -9}); + amount * Number{transferRate(*env_.current(), *id_).value, -9}); // Sender pays the transfer fee if any env_.require(mptpay(*this, src, srcAmt - actual)); env_.require(mptpay(*this, dest, destAmt + amount)); @@ -347,7 +345,7 @@ MPTTester::claw( std::int64_t amount, std::optional err) { - assert(mpt_); + assert(id_); auto const issuerAmt = getAmount(issuer); auto const holderAmt = getAmount(holder); if (err) @@ -368,8 +366,8 @@ MPTTester::claw( STMPTAmount MPTTester::mpt(std::int64_t amount) const { - assert(mpt_); - return ripple::test::jtx::MPT(issuer_.name(), *mpt_)(amount); + assert(id_); + return ripple::test::jtx::MPT(issuer_.name(), *id_)(amount); } std::int64_t diff --git a/src/test/jtx/mpt.h b/src/test/jtx/mpt.h index e3ebb69d658..e96b73f039a 100644 --- a/src/test/jtx/mpt.h +++ b/src/test/jtx/mpt.h @@ -111,7 +111,7 @@ struct MPTCreate struct MPTDestroy { AccountP issuer = nullptr; - std::optional id = std::nullopt; + std::optional id = std::nullopt; std::optional ownerCount = std::nullopt; std::optional holderCount = std::nullopt; std::optional flags = std::nullopt; @@ -122,7 +122,7 @@ struct MPTAuthorize { AccountP account = nullptr; AccountP holder = nullptr; - std::optional id = std::nullopt; + std::optional id = std::nullopt; std::optional ownerCount = std::nullopt; std::optional holderCount = std::nullopt; std::optional flags = std::nullopt; @@ -133,7 +133,7 @@ struct MPTSet { AccountP account = nullptr; AccountP holder = nullptr; - std::optional id = std::nullopt; + std::optional id = std::nullopt; std::optional ownerCount = std::nullopt; std::optional holderCount = std::nullopt; std::optional flags = std::nullopt; @@ -145,9 +145,8 @@ class MPTTester Env& env_; Account const& issuer_; std::unordered_map const holders_; - std::optional id_; + std::optional id_; std::optional issuanceKey_; - std::optional mpt_; bool close_; public: @@ -206,7 +205,7 @@ class MPTTester return *issuanceKey_; } - uint192 const& + MPTID const& issuanceID() const { assert(id_); diff --git a/src/xrpld/app/tx/detail/Clawback.cpp b/src/xrpld/app/tx/detail/Clawback.cpp index fa8f2a3843f..a6ca307bb3d 100644 --- a/src/xrpld/app/tx/detail/Clawback.cpp +++ b/src/xrpld/app/tx/detail/Clawback.cpp @@ -172,7 +172,7 @@ preclaimHelper(PreclaimContext const& ctx) if (sleHolder->isFieldPresent(sfAMMID)) return tecAMM_ACCOUNT; - auto const issuanceKey = keylet::mptIssuance(clawAmount.issue().mpt()); + auto const issuanceKey = keylet::mptIssuance(clawAmount.issue().getMptID()); auto const sleIssuance = ctx.view.read(issuanceKey); if (!sleIssuance) return tecOBJECT_NOT_FOUND; diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 6cc88a8107a..98ef572b548 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -268,7 +268,7 @@ preflightHelper(PreflightContext const& ctx) << "bad dst amount: " << saDstAmount.getFullText(); return temBAD_AMOUNT; } - if (badMPT() == uDstCurrency) + if (noMPT() == uDstCurrency) { JLOG(j.trace()) << "Malformed transaction: " << "Bad asset."; diff --git a/src/xrpld/ledger/View.h b/src/xrpld/ledger/View.h index 3ee99713dcc..b413a775bea 100644 --- a/src/xrpld/ledger/View.h +++ b/src/xrpld/ledger/View.h @@ -235,7 +235,7 @@ forEachItemAfter( transferRate(ReadView const& view, AccountID const& issuer); [[nodiscard]] Rate -transferRate(ReadView const& view, MPT const& id); +transferRate(ReadView const& view, MPTID const& id); /** Returns `true` if the directory is empty @param key The key of the directory diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index f5303c3bb7e..22a8249758e 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -180,7 +180,7 @@ isGlobalFrozen(ReadView const& view, AccountID const& issuer) bool isGlobalFrozen(ReadView const& view, MPTIssue const& mpt) { - if (auto const sle = view.read(keylet::mptIssuance(mpt.mpt()))) + if (auto const sle = view.read(keylet::mptIssuance(mpt.getMptID()))) return sle->getFlags() & lsfMPTLocked; return false; } @@ -211,7 +211,7 @@ isIndividualFrozen( AccountID const& account, MPTIssue const& mpt) { - if (auto const sle = view.read(keylet::mptoken(mpt.mpt(), account))) + if (auto const sle = view.read(keylet::mptoken(mpt.getMptID(), account))) return sle->getFlags() & lsfMPTLocked; return false; } @@ -316,7 +316,7 @@ accountHolds( { STMPTAmount amount; - auto const sleMpt = view.read(keylet::mptoken(issue.mpt(), account)); + auto const sleMpt = view.read(keylet::mptoken(issue.getMptID(), account)); if (!sleMpt) amount.clear(issue); else if (zeroIfFrozen == fhZERO_IF_FROZEN && isFrozen(view, account, issue)) @@ -564,7 +564,7 @@ transferRate(ReadView const& view, AccountID const& issuer) } Rate -transferRate(ReadView const& view, MPT const& id) +transferRate(ReadView const& view, MPTID const& id) { auto const sle = view.read(keylet::mptIssuance(id)); @@ -1363,11 +1363,13 @@ rippleSend( } // Sending 3rd party MPTs: transit. - if (auto const sle = view.read(keylet::mptIssuance(saAmount.issue().mpt()))) + if (auto const sle = + view.read(keylet::mptIssuance(saAmount.issue().getMptID()))) { saActual = (waiveFee == WaiveTransferFee::Yes) ? saAmount - : multiply(saAmount, transferRate(view, saAmount.issue().mpt())); + : multiply( + saAmount, transferRate(view, saAmount.issue().getMptID())); JLOG(j.debug()) << "rippleSend> " << to_string(uSenderID) << " - > " << to_string(uReceiverID) @@ -1693,7 +1695,7 @@ requireAuth(ReadView const& view, Issue const& issue, AccountID const& account) TER requireAuth(ReadView const& view, MPTIssue const& mpt, AccountID const& account) { - auto const mptID = keylet::mptIssuance(mpt.mpt()); + auto const mptID = keylet::mptIssuance(mpt.getMptID()); if (auto const sle = view.read(mptID); sle && sle->getFieldU32(sfFlags) & lsfMPTRequireAuth) { @@ -1713,7 +1715,7 @@ canTransfer( AccountID const& from, AccountID const& to) { - auto const mptID = keylet::mptIssuance(mpt.mpt()); + auto const mptID = keylet::mptIssuance(mpt.getMptID()); if (auto const sle = view.read(mptID); sle && !(sle->getFieldU32(sfFlags) & lsfMPTCanTransfer)) { @@ -1856,7 +1858,7 @@ rippleCredit( STMPTAmount saAmount, beast::Journal j) { - auto const mptID = keylet::mptIssuance(saAmount.issue().mpt()); + auto const mptID = keylet::mptIssuance(saAmount.issue().getMptID()); auto const issuer = saAmount.getIssuer(); if (uSenderID == issuer) { diff --git a/src/xrpld/rpc/MPTokenIssuanceID.h b/src/xrpld/rpc/MPTokenIssuanceID.h index f7f45fded3b..4f94e61fa86 100644 --- a/src/xrpld/rpc/MPTokenIssuanceID.h +++ b/src/xrpld/rpc/MPTokenIssuanceID.h @@ -37,7 +37,7 @@ canHaveMPTokenIssuanceID( std::shared_ptr const& serializedTx, TxMeta const& transactionMeta); -std::optional +std::optional getIDFromCreatedIssuance(TxMeta const& transactionMeta); void diff --git a/src/xrpld/rpc/detail/MPTokenIssuanceID.cpp b/src/xrpld/rpc/detail/MPTokenIssuanceID.cpp index 8de1e8f3344..c2aa3eea02a 100644 --- a/src/xrpld/rpc/detail/MPTokenIssuanceID.cpp +++ b/src/xrpld/rpc/detail/MPTokenIssuanceID.cpp @@ -47,7 +47,7 @@ canHaveMPTokenIssuanceID( return true; } -std::optional +std::optional getIDFromCreatedIssuance(TxMeta const& transactionMeta) { for (STObject const& node : transactionMeta.getNodes()) @@ -74,7 +74,7 @@ insertMPTokenIssuanceID( if (!canHaveMPTokenIssuanceID(transaction, transactionMeta)) return; - std::optional result = getIDFromCreatedIssuance(transactionMeta); + std::optional result = getIDFromCreatedIssuance(transactionMeta); if (result.has_value()) response[jss::mpt_issuance_id] = to_string(result.value()); } diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index 30b1113cd05..b8f5ffe104a 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -651,7 +651,7 @@ doLedgerEntry(RPC::JsonContext& context) context.params[jss::mpt_issuance]; if (unparsedMPTIssuanceID.isString()) { - uint192 mptIssuanceID; + MPTID mptIssuanceID; if (!mptIssuanceID.parseHex(unparsedMPTIssuanceID.asString())) { uNodeIndex = beast::zero; @@ -691,7 +691,7 @@ doLedgerEntry(RPC::JsonContext& context) context.params[jss::mptoken][jss::mpt_issuance_id] .asString(); - uint192 mptIssuanceID; + MPTID mptIssuanceID; if (!mptIssuanceID.parseHex(mptIssuanceIdStr)) Throw( "Cannot parse mpt_issuance_id"); From 496f600c301959afd2f9fa74152b8ef201bd7ca6 Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Mon, 26 Aug 2024 13:15:37 -0400 Subject: [PATCH 10/11] Fix clang-format --- include/xrpl/protocol/jss.h | 224 ++++++++++++++++++------------------ 1 file changed, 112 insertions(+), 112 deletions(-) diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index 2137681fa7d..8db6f722541 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -41,123 +41,123 @@ namespace jss { error: Common properties of RPC error responses. */ -JSS(AL_size); // out: GetCounts -JSS(AL_hit_rate); // out: GetCounts -JSS(Account); // in: TransactionSign; field. -JSS(AccountDelete); // transaction type. -JSS(AccountRoot); // ledger type. -JSS(AccountSet); // transaction type. -JSS(AMM); // ledger type -JSS(AMMBid); // transaction type -JSS(AMMID); // field -JSS(AMMCreate); // transaction type -JSS(AMMDeposit); // transaction type -JSS(AMMDelete); // transaction type -JSS(AMMVote); // transaction type -JSS(AMMWithdraw); // transaction type -JSS(Amendments); // ledger type. -JSS(Amount); // in: TransactionSign; field. -JSS(Amount2); // in/out: AMM IOU/XRP pool, deposit, withdraw amount -JSS(Asset); // in: AMM Asset1 -JSS(Asset2); // in: AMM Asset2 -JSS(AssetClass); // in: Oracle -JSS(AssetPrice); // in: Oracle -JSS(AuthAccount); // in: AMM Auction Slot -JSS(AuthAccounts); // in: AMM Auction Slot -JSS(BaseAsset); // in: Oracle -JSS(Bridge); // ledger type. -JSS(MPToken); // ledger type. -JSS(MPTokenIssuance); // ledger type. +JSS(AL_size); // out: GetCounts +JSS(AL_hit_rate); // out: GetCounts +JSS(Account); // in: TransactionSign; field. +JSS(AccountDelete); // transaction type. +JSS(AccountRoot); // ledger type. +JSS(AccountSet); // transaction type. +JSS(AMM); // ledger type +JSS(AMMBid); // transaction type +JSS(AMMID); // field +JSS(AMMCreate); // transaction type +JSS(AMMDeposit); // transaction type +JSS(AMMDelete); // transaction type +JSS(AMMVote); // transaction type +JSS(AMMWithdraw); // transaction type +JSS(Amendments); // ledger type. +JSS(Amount); // in: TransactionSign; field. +JSS(Amount2); // in/out: AMM IOU/XRP pool, deposit, withdraw amount +JSS(Asset); // in: AMM Asset1 +JSS(Asset2); // in: AMM Asset2 +JSS(AssetClass); // in: Oracle +JSS(AssetPrice); // in: Oracle +JSS(AuthAccount); // in: AMM Auction Slot +JSS(AuthAccounts); // in: AMM Auction Slot +JSS(BaseAsset); // in: Oracle +JSS(Bridge); // ledger type. +JSS(MPToken); // ledger type. +JSS(MPTokenIssuance); // ledger type. JSS(MPTokenIssuanceCreate); // transaction type. JSS(MPTokenIssuanceDestroy); // transaction type. JSS(MPTokenAuthorize); // transaction type. JSS(MPTokenIssuanceSet); // transaction type. JSS(MPTokenIssuanceID); // in: MPTokenIssuanceDestroy, MPTokenAuthorize -JSS(Check); // ledger type. -JSS(CheckCancel); // transaction type. -JSS(CheckCash); // transaction type. -JSS(CheckCreate); // transaction type. -JSS(Clawback); // transaction type. -JSS(ClearFlag); // field. -JSS(DID); // ledger type. -JSS(DIDDelete); // transaction type. -JSS(DIDSet); // transaction type. -JSS(DeliverMax); // out: alias to Amount -JSS(DeliverMin); // in: TransactionSign -JSS(DepositPreauth); // transaction and ledger type. -JSS(Destination); // in: TransactionSign; field. -JSS(DirectoryNode); // ledger type. -JSS(EnableAmendment); // transaction type. -JSS(EPrice); // in: AMM Deposit option -JSS(Escrow); // ledger type. -JSS(EscrowCancel); // transaction type. -JSS(EscrowCreate); // transaction type. -JSS(EscrowFinish); // transaction type. -JSS(Fee); // in/out: TransactionSign; field. -JSS(FeeSettings); // ledger type. -JSS(Flags); // in/out: TransactionSign; field. -JSS(Invalid); // -JSS(LastLedgerSequence); // in: TransactionSign; field -JSS(LastUpdateTime); // field. -JSS(LedgerHashes); // ledger type. -JSS(LimitAmount); // field. -JSS(BidMax); // in: AMM Bid -JSS(BidMin); // in: AMM Bid -JSS(NetworkID); // field. -JSS(NFTokenBurn); // transaction type. -JSS(NFTokenMint); // transaction type. -JSS(NFTokenOffer); // ledger type. -JSS(NFTokenAcceptOffer); // transaction type. -JSS(NFTokenCancelOffer); // transaction type. -JSS(NFTokenCreateOffer); // transaction type. -JSS(NFTokenPage); // ledger type. -JSS(LedgerStateFix); // transaction type. -JSS(LPTokenOut); // in: AMM Liquidity Provider deposit tokens -JSS(LPTokenIn); // in: AMM Liquidity Provider withdraw tokens -JSS(LPToken); // out: AMM Liquidity Provider tokens info -JSS(Offer); // ledger type. -JSS(OfferCancel); // transaction type. -JSS(OfferCreate); // transaction type. -JSS(OfferSequence); // field. -JSS(Oracle); // ledger type. -JSS(OracleDelete); // transaction type. -JSS(OracleDocumentID); // field -JSS(OracleSet); // transaction type. -JSS(Owner); // field -JSS(Paths); // in/out: TransactionSign -JSS(PayChannel); // ledger type. -JSS(Payment); // transaction type. -JSS(PaymentChannelClaim); // transaction type. -JSS(PaymentChannelCreate); // transaction type. -JSS(PaymentChannelFund); // transaction type. -JSS(PriceDataSeries); // field. -JSS(PriceData); // field. -JSS(Provider); // field. -JSS(QuoteAsset); // in: Oracle. -JSS(RippleState); // ledger type. -JSS(SLE_hit_rate); // out: GetCounts. -JSS(SetFee); // transaction type. -JSS(UNLModify); // transaction type. -JSS(Scale); // field. -JSS(SettleDelay); // in: TransactionSign -JSS(SendMax); // in: TransactionSign -JSS(Sequence); // in/out: TransactionSign; field. -JSS(SetFlag); // field. -JSS(SetRegularKey); // transaction type. -JSS(SignerList); // ledger type. -JSS(SignerListSet); // transaction type. -JSS(SigningPubKey); // field. -JSS(TakerGets); // field. -JSS(TakerPays); // field. -JSS(Ticket); // ledger type. -JSS(TicketCreate); // transaction type. -JSS(TxnSignature); // field. -JSS(TradingFee); // in/out: AMM trading fee -JSS(TransactionType); // in: TransactionSign. -JSS(TransferRate); // in: TransferRate. -JSS(TrustSet); // transaction type. -JSS(URI); // field. -JSS(VoteSlots); // out: AMM Vote +JSS(Check); // ledger type. +JSS(CheckCancel); // transaction type. +JSS(CheckCash); // transaction type. +JSS(CheckCreate); // transaction type. +JSS(Clawback); // transaction type. +JSS(ClearFlag); // field. +JSS(DID); // ledger type. +JSS(DIDDelete); // transaction type. +JSS(DIDSet); // transaction type. +JSS(DeliverMax); // out: alias to Amount +JSS(DeliverMin); // in: TransactionSign +JSS(DepositPreauth); // transaction and ledger type. +JSS(Destination); // in: TransactionSign; field. +JSS(DirectoryNode); // ledger type. +JSS(EnableAmendment); // transaction type. +JSS(EPrice); // in: AMM Deposit option +JSS(Escrow); // ledger type. +JSS(EscrowCancel); // transaction type. +JSS(EscrowCreate); // transaction type. +JSS(EscrowFinish); // transaction type. +JSS(Fee); // in/out: TransactionSign; field. +JSS(FeeSettings); // ledger type. +JSS(Flags); // in/out: TransactionSign; field. +JSS(Invalid); // +JSS(LastLedgerSequence); // in: TransactionSign; field +JSS(LastUpdateTime); // field. +JSS(LedgerHashes); // ledger type. +JSS(LimitAmount); // field. +JSS(BidMax); // in: AMM Bid +JSS(BidMin); // in: AMM Bid +JSS(NetworkID); // field. +JSS(NFTokenBurn); // transaction type. +JSS(NFTokenMint); // transaction type. +JSS(NFTokenOffer); // ledger type. +JSS(NFTokenAcceptOffer); // transaction type. +JSS(NFTokenCancelOffer); // transaction type. +JSS(NFTokenCreateOffer); // transaction type. +JSS(NFTokenPage); // ledger type. +JSS(LedgerStateFix); // transaction type. +JSS(LPTokenOut); // in: AMM Liquidity Provider deposit tokens +JSS(LPTokenIn); // in: AMM Liquidity Provider withdraw tokens +JSS(LPToken); // out: AMM Liquidity Provider tokens info +JSS(Offer); // ledger type. +JSS(OfferCancel); // transaction type. +JSS(OfferCreate); // transaction type. +JSS(OfferSequence); // field. +JSS(Oracle); // ledger type. +JSS(OracleDelete); // transaction type. +JSS(OracleDocumentID); // field +JSS(OracleSet); // transaction type. +JSS(Owner); // field +JSS(Paths); // in/out: TransactionSign +JSS(PayChannel); // ledger type. +JSS(Payment); // transaction type. +JSS(PaymentChannelClaim); // transaction type. +JSS(PaymentChannelCreate); // transaction type. +JSS(PaymentChannelFund); // transaction type. +JSS(PriceDataSeries); // field. +JSS(PriceData); // field. +JSS(Provider); // field. +JSS(QuoteAsset); // in: Oracle. +JSS(RippleState); // ledger type. +JSS(SLE_hit_rate); // out: GetCounts. +JSS(SetFee); // transaction type. +JSS(UNLModify); // transaction type. +JSS(Scale); // field. +JSS(SettleDelay); // in: TransactionSign +JSS(SendMax); // in: TransactionSign +JSS(Sequence); // in/out: TransactionSign; field. +JSS(SetFlag); // field. +JSS(SetRegularKey); // transaction type. +JSS(SignerList); // ledger type. +JSS(SignerListSet); // transaction type. +JSS(SigningPubKey); // field. +JSS(TakerGets); // field. +JSS(TakerPays); // field. +JSS(Ticket); // ledger type. +JSS(TicketCreate); // transaction type. +JSS(TxnSignature); // field. +JSS(TradingFee); // in/out: AMM trading fee +JSS(TransactionType); // in: TransactionSign. +JSS(TransferRate); // in: TransferRate. +JSS(TrustSet); // transaction type. +JSS(URI); // field. +JSS(VoteSlots); // out: AMM Vote JSS(XChainAddAccountCreateAttestation); // transaction type. JSS(XChainAddClaimAttestation); // transaction type. JSS(XChainAccountCreateCommit); // transaction type. From 632373bfa04aeb90537970e2c259bd3de3d2336f Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Mon, 26 Aug 2024 14:36:23 -0400 Subject: [PATCH 11/11] Fix clang-format --- include/xrpl/protocol/jss.h | 114 ++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index e2a8d6df5c5..f317968eeb7 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -41,63 +41,63 @@ namespace jss { error: Common properties of RPC error responses. */ -JSS(AL_size); // out: GetCounts -JSS(AL_hit_rate); // out: GetCounts -JSS(Account); // in: TransactionSign; field. -JSS(AccountDelete); // transaction type. -JSS(AccountRoot); // ledger type. -JSS(AccountSet); // transaction type. -JSS(AMM); // ledger type -JSS(AMMBid); // transaction type -JSS(AMMID); // field -JSS(AMMCreate); // transaction type -JSS(AMMDeposit); // transaction type -JSS(AMMDelete); // transaction type -JSS(AMMVote); // transaction type -JSS(AMMWithdraw); // transaction type -JSS(Amendments); // ledger type. -JSS(Amount); // in: TransactionSign; field. -JSS(Amount2); // in/out: AMM IOU/XRP pool, deposit, withdraw amount -JSS(Asset); // in: AMM Asset1 -JSS(Asset2); // in: AMM Asset2 -JSS(AssetClass); // in: Oracle -JSS(AssetPrice); // in: Oracle -JSS(AuthAccount); // in: AMM Auction Slot -JSS(AuthAccounts); // in: AMM Auction Slot -JSS(BaseAsset); // in: Oracle -JSS(Bridge); // ledger type. -JSS(Check); // ledger type. -JSS(CheckCancel); // transaction type. -JSS(CheckCash); // transaction type. -JSS(CheckCreate); // transaction type. -JSS(Clawback); // transaction type. -JSS(ClearFlag); // field. -JSS(DID); // ledger type. -JSS(DIDDelete); // transaction type. -JSS(DIDSet); // transaction type. -JSS(DeliverMax); // out: alias to Amount -JSS(DeliverMin); // in: TransactionSign -JSS(DepositPreauth); // transaction and ledger type. -JSS(Destination); // in: TransactionSign; field. -JSS(DirectoryNode); // ledger type. -JSS(EnableAmendment); // transaction type. -JSS(EPrice); // in: AMM Deposit option -JSS(Escrow); // ledger type. -JSS(EscrowCancel); // transaction type. -JSS(EscrowCreate); // transaction type. -JSS(EscrowFinish); // transaction type. -JSS(Fee); // in/out: TransactionSign; field. -JSS(FeeSettings); // ledger type. -JSS(Flags); // in/out: TransactionSign; field. -JSS(Invalid); // -JSS(LastLedgerSequence); // in: TransactionSign; field -JSS(LastUpdateTime); // field. -JSS(LedgerHashes); // ledger type. -JSS(LimitAmount); // field. -JSS(BidMax); // in: AMM Bid -JSS(BidMin); // in: AMM Bid -JSS(MPToken); // ledger type. -JSS(MPTokenIssuance); // ledger type. +JSS(AL_size); // out: GetCounts +JSS(AL_hit_rate); // out: GetCounts +JSS(Account); // in: TransactionSign; field. +JSS(AccountDelete); // transaction type. +JSS(AccountRoot); // ledger type. +JSS(AccountSet); // transaction type. +JSS(AMM); // ledger type +JSS(AMMBid); // transaction type +JSS(AMMID); // field +JSS(AMMCreate); // transaction type +JSS(AMMDeposit); // transaction type +JSS(AMMDelete); // transaction type +JSS(AMMVote); // transaction type +JSS(AMMWithdraw); // transaction type +JSS(Amendments); // ledger type. +JSS(Amount); // in: TransactionSign; field. +JSS(Amount2); // in/out: AMM IOU/XRP pool, deposit, withdraw amount +JSS(Asset); // in: AMM Asset1 +JSS(Asset2); // in: AMM Asset2 +JSS(AssetClass); // in: Oracle +JSS(AssetPrice); // in: Oracle +JSS(AuthAccount); // in: AMM Auction Slot +JSS(AuthAccounts); // in: AMM Auction Slot +JSS(BaseAsset); // in: Oracle +JSS(Bridge); // ledger type. +JSS(Check); // ledger type. +JSS(CheckCancel); // transaction type. +JSS(CheckCash); // transaction type. +JSS(CheckCreate); // transaction type. +JSS(Clawback); // transaction type. +JSS(ClearFlag); // field. +JSS(DID); // ledger type. +JSS(DIDDelete); // transaction type. +JSS(DIDSet); // transaction type. +JSS(DeliverMax); // out: alias to Amount +JSS(DeliverMin); // in: TransactionSign +JSS(DepositPreauth); // transaction and ledger type. +JSS(Destination); // in: TransactionSign; field. +JSS(DirectoryNode); // ledger type. +JSS(EnableAmendment); // transaction type. +JSS(EPrice); // in: AMM Deposit option +JSS(Escrow); // ledger type. +JSS(EscrowCancel); // transaction type. +JSS(EscrowCreate); // transaction type. +JSS(EscrowFinish); // transaction type. +JSS(Fee); // in/out: TransactionSign; field. +JSS(FeeSettings); // ledger type. +JSS(Flags); // in/out: TransactionSign; field. +JSS(Invalid); // +JSS(LastLedgerSequence); // in: TransactionSign; field +JSS(LastUpdateTime); // field. +JSS(LedgerHashes); // ledger type. +JSS(LimitAmount); // field. +JSS(BidMax); // in: AMM Bid +JSS(BidMin); // in: AMM Bid +JSS(MPToken); // ledger type. +JSS(MPTokenIssuance); // ledger type. JSS(MPTokenIssuanceCreate); // transaction type. JSS(MPTokenIssuanceDestroy); // transaction type. JSS(MPTokenAuthorize); // transaction type.