diff --git a/include/xrpl/protocol/Asset.h b/include/xrpl/protocol/Asset.h index 760b2439557..0688049e119 100644 --- a/include/xrpl/protocol/Asset.h +++ b/include/xrpl/protocol/Asset.h @@ -26,10 +26,16 @@ namespace ripple { +class Asset; + template concept ValidIssueType = std::is_same_v || std::is_same_v; +template +concept AssetType = std::is_same_v || + std::is_convertible_v || std::is_convertible_v; + class Asset { private: @@ -78,6 +84,9 @@ class Asset friend constexpr bool operator!=(Asset const& lhs, Asset const& rhs); + + friend constexpr bool + operator<(Asset const& lhs, Asset const& rhs); }; template @@ -132,6 +141,21 @@ operator!=(Asset const& lhs, Asset const& rhs) return !(lhs == rhs); } +constexpr bool +operator<(Asset const& lhs, Asset const& rhs) +{ + return std::visit( + [&]( + TLhs const& issLhs, TRhs const& issRhs) { + if constexpr (std::is_same_v) + return issLhs < issRhs; + else + return false; + }, + lhs.issue_, + rhs.issue_); +} + inline bool isXRP(Asset const& asset) { diff --git a/include/xrpl/protocol/Indexes.h b/include/xrpl/protocol/Indexes.h index e9f4100008e..87374bb5be4 100644 --- a/include/xrpl/protocol/Indexes.h +++ b/include/xrpl/protocol/Indexes.h @@ -267,7 +267,7 @@ nft_sells(uint256 const& id) noexcept; /** AMM entry */ Keylet -amm(Issue const& issue1, Issue const& issue2) noexcept; +amm(Asset const& issue1, Asset const& issue2) noexcept; Keylet amm(uint256 const& amm) noexcept; diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index 2f037a1c285..2e1c493bb09 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -34,10 +34,6 @@ namespace ripple { -template -concept AssetType = 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: diff --git a/include/xrpl/protocol/STIssue.h b/include/xrpl/protocol/STIssue.h index a0dfbd4faec..e97c2ab38ae 100644 --- a/include/xrpl/protocol/STIssue.h +++ b/include/xrpl/protocol/STIssue.h @@ -21,7 +21,7 @@ #define RIPPLE_PROTOCOL_STISSUE_H_INCLUDED #include -#include +#include #include #include #include @@ -31,31 +31,40 @@ namespace ripple { class STIssue final : public STBase, CountedObject { private: - Issue issue_{xrpIssue()}; + Asset asset_{xrpIssue()}; public: - using value_type = Issue; + using value_type = Asset; STIssue() = default; explicit STIssue(SerialIter& sit, SField const& name); - explicit STIssue(SField const& name, Issue const& issue); + template + explicit STIssue(SField const& name, A const& issue); explicit STIssue(SField const& name); - Issue const& - issue() const; + template + TIss const& + get() const; - Issue const& + template + bool + holds() const; + + value_type const& value() const noexcept; void - setIssue(Issue const& issue); + setIssue(Asset const& issue); SerializedTypeID getSType() const override; + std::string + getText() const override; + Json::Value getJson(JsonOptions) const override; void @@ -76,35 +85,54 @@ class STIssue final : public STBase, CountedObject friend class detail::STVar; }; +template +STIssue::STIssue(SField const& name, A const& asset) + : STBase{name}, asset_{asset} +{ + if (holds() && !isConsistent(asset_.get())) + Throw( + "Invalid asset: currency and account native mismatch"); +} + STIssue issueFromJson(SField const& name, Json::Value const& v); -inline Issue const& -STIssue::issue() const +template +bool +STIssue::holds() const +{ + return std::holds_alternative(asset_.value()); +} + +template +TIss const& +STIssue::get() const { - return issue_; + if (!holds(asset_)) + Throw("Asset doesn't hold the requested issue"); + return std::get(asset_); } -inline Issue const& +inline STIssue::value_type const& STIssue::value() const noexcept { - return issue_; + return asset_; } inline void -STIssue::setIssue(Issue const& issue) +STIssue::setIssue(Asset const& asset) { - if (isXRP(issue_.currency) != isXRP(issue_.account)) + if (holds() && !isConsistent(asset_.get())) Throw( - "invalid issue: currency and account native mismatch"); + "Invalid asset: currency and account native mismatch"); - issue_ = issue; + asset_ = asset; } inline bool operator==(STIssue const& lhs, STIssue const& rhs) { - return lhs.issue() == rhs.issue(); + return lhs.value() == rhs.value(); } inline bool @@ -116,19 +144,19 @@ operator!=(STIssue const& lhs, STIssue const& rhs) inline bool operator<(STIssue const& lhs, STIssue const& rhs) { - return lhs.issue() < rhs.issue(); + return lhs.value() < rhs.value(); } inline bool -operator==(STIssue const& lhs, Issue const& rhs) +operator==(STIssue const& lhs, Asset const& rhs) { - return lhs.issue() == rhs; + return lhs.value() == rhs; } inline bool -operator<(STIssue const& lhs, Issue const& rhs) +operator<(STIssue const& lhs, Asset const& rhs) { - return lhs.issue() < rhs; + return lhs.value() < rhs; } } // namespace ripple diff --git a/include/xrpl/protocol/STXChainBridge.h b/include/xrpl/protocol/STXChainBridge.h index 38db1912c70..813bcc44437 100644 --- a/include/xrpl/protocol/STXChainBridge.h +++ b/include/xrpl/protocol/STXChainBridge.h @@ -170,7 +170,7 @@ STXChainBridge::lockingChainDoor() const inline Issue const& STXChainBridge::lockingChainIssue() const { - return lockingChainIssue_.value(); + return lockingChainIssue_.value().get(); }; inline AccountID const& @@ -182,7 +182,7 @@ STXChainBridge::issuingChainDoor() const inline Issue const& STXChainBridge::issuingChainIssue() const { - return issuingChainIssue_.value(); + return issuingChainIssue_.value().get(); }; inline STXChainBridge::value_type const& diff --git a/src/libxrpl/protocol/Asset.cpp b/src/libxrpl/protocol/Asset.cpp index dcd4a4f0b19..cf7b55a5c76 100644 --- a/src/libxrpl/protocol/Asset.cpp +++ b/src/libxrpl/protocol/Asset.cpp @@ -67,10 +67,7 @@ Asset::setJson(Json::Value& jv) const if (holds()) jv[jss::mpt_issuance_id] = to_string(get().getMptID()); else - { - jv[jss::currency] = to_string(get().currency); - jv[jss::issuer] = to_string(get().account); - } + jv = to_json(get()); } std::string diff --git a/src/libxrpl/protocol/Indexes.cpp b/src/libxrpl/protocol/Indexes.cpp index 8014d8d4dcd..86d77aae3cc 100644 --- a/src/libxrpl/protocol/Indexes.cpp +++ b/src/libxrpl/protocol/Indexes.cpp @@ -17,6 +17,7 @@ */ //============================================================================== +#include #include #include #include @@ -393,9 +394,12 @@ nft_sells(uint256 const& id) noexcept } Keylet -amm(Issue const& issue1, Issue const& issue2) noexcept +amm(Asset const& issue1, Asset const& issue2) noexcept { - auto const& [minI, maxI] = std::minmax(issue1, issue2); + if (!issue1.holds() || !issue2.holds()) + Throw("Asset doesn't hold issue"); + auto const& [minI, maxI] = + std::minmax(issue1.get(), issue2.get()); return amm(indexHash( LedgerNameSpace::AMM, minI.account, diff --git a/src/libxrpl/protocol/STIssue.cpp b/src/libxrpl/protocol/STIssue.cpp index 9dcee6498ab..03aaa54fb93 100644 --- a/src/libxrpl/protocol/STIssue.cpp +++ b/src/libxrpl/protocol/STIssue.cpp @@ -40,23 +40,44 @@ STIssue::STIssue(SField const& name) : STBase{name} STIssue::STIssue(SerialIter& sit, SField const& name) : STBase{name} { - issue_.currency = sit.get160(); - if (!isXRP(issue_.currency)) - issue_.account = sit.get160(); - else - issue_.account = xrpAccount(); - - if (isXRP(issue_.currency) != isXRP(issue_.account)) - Throw( - "invalid issue: currency and account native mismatch"); -} + auto const currencyOrAccount = sit.get160(); -STIssue::STIssue(SField const& name, Issue const& issue) - : STBase{name}, issue_{issue} -{ - if (isXRP(issue_.currency) != isXRP(issue_.account)) - Throw( - "invalid issue: currency and account native mismatch"); + if (isXRP(static_cast(currencyOrAccount))) + { + asset_ = xrpIssue(); + } + // Check if MPT + else + { + // MPT is serialized as: + // - 160 bits MPT issuer account + // - 160 bits black hole account + // - 32 bits sequence + AccountID account = static_cast(sit.get160()); + // MPT + if (noAccount() == account) + { + uint192 mptID; + std::uint32_t sequence = sit.get32(); + memcpy(mptID.data(), &sequence, sizeof(sequence)); + memcpy( + mptID.data() + sizeof(sequence), + account.data(), + sizeof(account)); + MPTIssue issue{mptID}; + asset_ = issue; + } + else + { + Issue issue; + issue.currency = currencyOrAccount; + issue.account = account; + if (!isConsistent(issue)) + Throw( + "invalid issue: currency and account native mismatch"); + asset_ = issue; + } + } } SerializedTypeID @@ -65,17 +86,39 @@ STIssue::getSType() const return STI_ISSUE; } +std::string +STIssue::getText() const +{ + return asset_.getText(); +} + Json::Value STIssue::getJson(JsonOptions) const { - return to_json(issue_); + Json::Value jv; + asset_.setJson(jv); + return jv; } void STIssue::add(Serializer& s) const { - s.addBitString(issue_.currency); - if (!isXRP(issue_.currency)) - s.addBitString(issue_.account); + if (holds()) + { + s.addBitString(asset_.get().currency); + if (!isXRP(asset_.get().currency)) + s.addBitString(asset_.get().account); + } + else + { + s.addBitString(asset_.get().getIssuer()); + s.addBitString(noAccount()); + std::uint32_t sequence; + memcpy( + &sequence, + asset_.get().getMptID().data(), + sizeof(sequence)); + s.add32(sequence); + } } bool @@ -88,7 +131,7 @@ STIssue::isEquivalent(const STBase& t) const bool STIssue::isDefault() const { - return issue_ == xrpIssue(); + return holds() && asset_.get() == xrpIssue(); } STBase* diff --git a/src/xrpld/app/ledger/OrderBookDB.cpp b/src/xrpld/app/ledger/OrderBookDB.cpp index d0eddadbacb..802d1507f31 100644 --- a/src/xrpld/app/ledger/OrderBookDB.cpp +++ b/src/xrpld/app/ledger/OrderBookDB.cpp @@ -129,8 +129,8 @@ OrderBookDB::update(std::shared_ptr const& ledger) } else if (sle->getType() == ltAMM) { - auto const issue1 = (*sle)[sfAsset]; - auto const issue2 = (*sle)[sfAsset2]; + auto const issue1 = (*sle)[sfAsset].get(); + auto const issue2 = (*sle)[sfAsset2].get(); auto addBook = [&](Issue const& in, Issue const& out) { allBooks[in].insert(out); diff --git a/src/xrpld/app/misc/detail/AMMUtils.cpp b/src/xrpld/app/misc/detail/AMMUtils.cpp index efc80cf17b6..e305db53067 100644 --- a/src/xrpld/app/misc/detail/AMMUtils.cpp +++ b/src/xrpld/app/misc/detail/AMMUtils.cpp @@ -51,8 +51,8 @@ ammHolds( beast::Journal const j) { auto const issues = [&]() -> std::optional> { - auto const issue1 = ammSle[sfAsset]; - auto const issue2 = ammSle[sfAsset2]; + auto const issue1 = ammSle[sfAsset].get(); + auto const issue2 = ammSle[sfAsset2].get(); if (optIssue1 && optIssue2) { if (invalidAMMAssetPair( @@ -134,8 +134,8 @@ ammLPHolds( { return ammLPHolds( view, - ammSle[sfAsset].currency, - ammSle[sfAsset2].currency, + ammSle[sfAsset].get().currency, + ammSle[sfAsset2].get().currency, ammSle[sfAccount], lpAccount, j); diff --git a/src/xrpld/app/tx/detail/AMMBid.cpp b/src/xrpld/app/tx/detail/AMMBid.cpp index 9de3762d2e3..7b5a7e002e2 100644 --- a/src/xrpld/app/tx/detail/AMMBid.cpp +++ b/src/xrpld/app/tx/detail/AMMBid.cpp @@ -46,7 +46,8 @@ AMMBid::preflight(PreflightContext const& ctx) return temINVALID_FLAG; } - if (auto const res = invalidAMMAssetPair(ctx.tx[sfAsset], ctx.tx[sfAsset2])) + if (auto const res = invalidAMMAssetPair( + ctx.tx[sfAsset].get(), ctx.tx[sfAsset2].get())) { JLOG(ctx.j.debug()) << "AMM Bid: Invalid asset pair."; return res; diff --git a/src/xrpld/app/tx/detail/AMMDelete.cpp b/src/xrpld/app/tx/detail/AMMDelete.cpp index 89ce34052d2..430ac17e87b 100644 --- a/src/xrpld/app/tx/detail/AMMDelete.cpp +++ b/src/xrpld/app/tx/detail/AMMDelete.cpp @@ -72,8 +72,8 @@ AMMDelete::doApply() // as we go on processing transactions. Sandbox sb(&ctx_.view()); - auto const ter = - deleteAMMAccount(sb, ctx_.tx[sfAsset], ctx_.tx[sfAsset2], j_); + auto const ter = deleteAMMAccount( + sb, ctx_.tx[sfAsset].get(), ctx_.tx[sfAsset2].get(), j_); if (ter == tesSUCCESS || ter == tecINCOMPLETE) sb.apply(ctx_.rawView()); diff --git a/src/xrpld/app/tx/detail/AMMDeposit.cpp b/src/xrpld/app/tx/detail/AMMDeposit.cpp index 9bbf5b4a60a..60d6a0e794a 100644 --- a/src/xrpld/app/tx/detail/AMMDeposit.cpp +++ b/src/xrpld/app/tx/detail/AMMDeposit.cpp @@ -100,8 +100,8 @@ AMMDeposit::preflight(PreflightContext const& ctx) return temMALFORMED; } - auto const asset = ctx.tx[sfAsset]; - auto const asset2 = ctx.tx[sfAsset2]; + auto const asset = ctx.tx[sfAsset].get(); + auto const asset2 = ctx.tx[sfAsset2].get(); if (auto const res = invalidAMMAssetPair(asset, asset2)) { JLOG(ctx.j.debug()) << "AMM Deposit: invalid asset pair."; diff --git a/src/xrpld/app/tx/detail/AMMVote.cpp b/src/xrpld/app/tx/detail/AMMVote.cpp index c4b6c612c63..31b3e8a04e7 100644 --- a/src/xrpld/app/tx/detail/AMMVote.cpp +++ b/src/xrpld/app/tx/detail/AMMVote.cpp @@ -38,7 +38,8 @@ AMMVote::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - if (auto const res = invalidAMMAssetPair(ctx.tx[sfAsset], ctx.tx[sfAsset2])) + if (auto const res = invalidAMMAssetPair( + ctx.tx[sfAsset].get(), ctx.tx[sfAsset2].get())) { JLOG(ctx.j.debug()) << "AMM Vote: invalid asset pair."; return res; diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.cpp b/src/xrpld/app/tx/detail/AMMWithdraw.cpp index 51b512aba0a..e877824a102 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/xrpld/app/tx/detail/AMMWithdraw.cpp @@ -101,8 +101,8 @@ AMMWithdraw::preflight(PreflightContext const& ctx) return temMALFORMED; } - auto const asset = ctx.tx[sfAsset]; - auto const asset2 = ctx.tx[sfAsset2]; + auto const asset = ctx.tx[sfAsset].get(); + auto const asset2 = ctx.tx[sfAsset2].get(); if (auto const res = invalidAMMAssetPair(asset, asset2)) { JLOG(ctx.j.debug()) << "AMM Withdraw: Invalid asset pair."; @@ -409,8 +409,11 @@ AMMWithdraw::applyGuts(Sandbox& sb) bool updateBalance = true; if (newLPTokenBalance == beast::zero) { - if (auto const ter = - deleteAMMAccount(sb, ctx_.tx[sfAsset], ctx_.tx[sfAsset2], j_); + if (auto const ter = deleteAMMAccount( + sb, + ctx_.tx[sfAsset].get(), + ctx_.tx[sfAsset2].get(), + j_); ter != tesSUCCESS && ter != tecINCOMPLETE) return {ter, false}; else diff --git a/src/xrpld/rpc/handlers/AMMInfo.cpp b/src/xrpld/rpc/handlers/AMMInfo.cpp index aad8faea213..fec7819b472 100644 --- a/src/xrpld/rpc/handlers/AMMInfo.cpp +++ b/src/xrpld/rpc/handlers/AMMInfo.cpp @@ -160,8 +160,8 @@ doAMMInfo(RPC::JsonContext& context) return Unexpected(rpcACT_NOT_FOUND); if (!issue1 && !issue2) { - issue1 = (*amm)[sfAsset]; - issue2 = (*amm)[sfAsset2]; + issue1 = (*amm)[sfAsset].get(); + issue2 = (*amm)[sfAsset2].get(); } return ValuesFromContextParams{