Compare commits

...

3 Commits

Author SHA1 Message Date
Gregory Tsipenyuk
beaebd7932 Fix skip isVaultPseudoAccountFrozen() for non-vault shares. 2026-04-30 14:44:32 -04:00
Gregory Tsipenyuk
fe582c1379 Skip isVaultPseudoAccountFrozen() for non-vault shares. 2026-04-30 13:57:01 -04:00
Gregory Tsipenyuk
017256e235 ammAccountHolds MPT optimization 2026-04-29 10:33:08 -04:00
8 changed files with 88 additions and 15 deletions

View File

@@ -58,6 +58,12 @@ isVaultPseudoAccountFrozen(
AccountID const& account,
MPTIssue const& mptShare,
int depth);
[[nodiscard]] bool
isVaultPseudoAccountFrozen(
ReadView const& view,
AccountID const& account,
SLE const& mptSLE,
int depth);
[[nodiscard]] bool
isLPTokenFrozen(

View File

@@ -26,9 +26,15 @@ isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue);
[[nodiscard]] bool
isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue);
[[nodiscard]] bool
isIndividualFrozen(ReadView const& view, AccountID const& account, SLE const& mptSLE);
[[nodiscard]] bool
isFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth = 0);
[[nodiscard]] bool
isFrozen(ReadView const& view, AccountID const& account, SLE const& mptSLE, int depth = 0);
[[nodiscard]] bool
isAnyFrozen(
ReadView const& view,
@@ -102,7 +108,8 @@ enforceMPTokenAuthorization(
MPTID const& mptIssuanceID,
AccountID const& account,
XRPAmount const& priorBalance,
beast::Journal j);
beast::Journal j,
std::uint32_t flags = 0);
/** Check if the destination account is allowed
* to receive MPT. Return tecNO_AUTH if it doesn't

View File

@@ -188,7 +188,8 @@ enum LedgerEntryType : std::uint16_t {
LEDGER_OBJECT(MPToken, \
LSF_FLAG2(lsfMPTLocked, 0x00000001) \
LSF_FLAG(lsfMPTAuthorized, 0x00000002) \
LSF_FLAG(lsfMPTAMM, 0x00000004)) \
LSF_FLAG(lsfMPTAMM, 0x00000004) \
LSF_FLAG(lsfMPTVault, 0x00000008)) \
\
LEDGER_OBJECT(Credential, \
LSF_FLAG(lsfAccepted, 0x00010000)) \

View File

@@ -52,11 +52,13 @@ hasExpired(ReadView const& view, std::optional<std::uint32_t> const& exp)
return exp && (view.parentCloseTime() >= tp{d{*exp}});
}
template <typename T>
requires(std::is_same_v<T, MPTIssue> || std::is_same_v<T, SLE>)
bool
isVaultPseudoAccountFrozen(
ReadView const& view,
T const& mptShare,
AccountID const& account,
MPTIssue const& mptShare,
int depth)
{
if (!view.rules().enabled(featureSingleAssetVault))
@@ -65,11 +67,18 @@ isVaultPseudoAccountFrozen(
if (depth >= maxAssetCheckDepth)
return true; // LCOV_EXCL_LINE
auto const mptIssuance = view.read(keylet::mptIssuance(mptShare.getMptID()));
if (mptIssuance == nullptr)
return false; // zero MPToken won't block deletion of MPTokenIssuance
auto const issuer = [&]() {
if constexpr (std::is_same_v<T, MPTIssue>)
{
return mptShare.getIssuer();
}
if constexpr (std::is_same_v<T, SLE>)
{
MPTID const mptID = mptShare[sfMPTokenIssuanceID];
return getMPTIssuer(mptID);
}
}();
auto const issuer = mptIssuance->getAccountID(sfIssuer);
auto const mptIssuer = view.read(keylet::account(issuer));
if (mptIssuer == nullptr)
{
@@ -82,6 +91,13 @@ isVaultPseudoAccountFrozen(
if (!mptIssuer->isFieldPresent(sfVaultID))
return false; // not a Vault pseudo-account, common case
if constexpr (std::is_same_v<T, MPTIssue>)
{
auto const mptIssuance = view.read(keylet::mptIssuance(mptShare.getMptID()));
if (mptIssuance == nullptr)
return false; // zero MPToken won't block deletion of MPTokenIssuance
}
auto const vault = view.read(keylet::vault(mptIssuer->getFieldH256(sfVaultID)));
if (vault == nullptr)
{ // LCOV_EXCL_START
@@ -93,6 +109,26 @@ isVaultPseudoAccountFrozen(
return isAnyFrozen(view, {issuer, account}, vault->at(sfAsset), depth + 1);
}
bool
isVaultPseudoAccountFrozen(
ReadView const& view,
AccountID const& account,
MPTIssue const& mptShare,
int depth)
{
return isVaultPseudoAccountFrozen(view, mptShare, account, depth);
}
bool
isVaultPseudoAccountFrozen(
ReadView const& view,
AccountID const& account,
SLE const& mptShareSLE,
int depth)
{
return isVaultPseudoAccountFrozen(view, mptShareSLE, account, depth);
}
bool
isLPTokenFrozen(
ReadView const& view,

View File

@@ -12,6 +12,7 @@
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AMMCore.h>
@@ -598,7 +599,7 @@ ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Asset const
return asset.visit(
[&](MPTIssue const& issue) {
if (auto const sle = view.read(keylet::mptoken(issue, ammAccountID));
sle && !isFrozen(view, ammAccountID, issue))
sle && !isFrozen(view, ammAccountID, *sle.get()))
return STAmount{issue, (*sle)[sfMPTAmount]};
return STAmount{asset};
},

View File

@@ -54,6 +54,12 @@ isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue cons
return false;
}
bool
isIndividualFrozen(ReadView const& view, AccountID const& account, SLE const& mptSLE)
{
return mptSLE.isFlag(lsfMPTLocked);
}
bool
isFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth)
{
@@ -61,6 +67,15 @@ isFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssu
isVaultPseudoAccountFrozen(view, account, mptIssue, depth);
}
bool
isFrozen(ReadView const& view, AccountID const& account, SLE const& mptSLE, int depth)
{
MPTIssue const mptIssue{mptSLE[sfMPTokenIssuanceID]};
bool const isVaultShares = mptSLE.isFlag(lsfMPTVault);
return isGlobalFrozen(view, mptIssue) || isIndividualFrozen(view, account, mptSLE) ||
(isVaultShares && isVaultPseudoAccountFrozen(view, account, mptSLE, depth));
}
[[nodiscard]] bool
isAnyFrozen(
ReadView const& view,
@@ -395,7 +410,8 @@ enforceMPTokenAuthorization(
MPTID const& mptIssuanceID,
AccountID const& account,
XRPAmount const& priorBalance, // for MPToken authorization
beast::Journal j)
beast::Journal j,
std::uint32_t flags)
{
auto const sleIssuance = view.read(keylet::mptIssuance(mptIssuanceID));
if (!sleIssuance)
@@ -476,7 +492,8 @@ enforceMPTokenAuthorization(
priorBalance, // priorBalance
mptIssuanceID, // mptIssuanceID
account, // account
j);
j,
flags);
!isTesSuccess(err))
return err;

View File

@@ -174,7 +174,7 @@ VaultCreate::doApply()
: ctx_.tx[~sfScale].value_or(vaultDefaultIOUScale);
auto txFlags = tx.getFlags();
std::uint32_t mptFlags = 0;
std::uint32_t mptFlags = lsfMPTVault;
if ((txFlags & tfVaultShareNonTransferable) == 0)
mptFlags |= (lsfMPTCanEscrow | lsfMPTCanTrade | lsfMPTCanTransfer);
if ((txFlags & tfVaultPrivate) != 0u)
@@ -230,8 +230,8 @@ VaultCreate::doApply()
view().insert(vault);
// Explicitly create MPToken for the vault owner
if (auto const err =
authorizeMPToken(view(), preFeeBalance_, mptIssuanceID, account_, ctx_.journal);
if (auto const err = authorizeMPToken(
view(), preFeeBalance_, mptIssuanceID, account_, ctx_.journal, lsfMPTVault);
!isTesSuccess(err))
return err;

View File

@@ -159,7 +159,7 @@ VaultDeposit::doApply()
if (vault->isFlag(lsfVaultPrivate) && account_ != vault->at(sfOwner))
{
if (auto const err = enforceMPTokenAuthorization(
ctx_.view(), mptIssuanceID, account_, preFeeBalance_, j_);
ctx_.view(), mptIssuanceID, account_, preFeeBalance_, j_, lsfMPTVault);
!isTesSuccess(err))
return err;
}
@@ -169,7 +169,12 @@ VaultDeposit::doApply()
if (!view().exists(keylet::mptoken(mptIssuanceID, account_)))
{
if (auto const err = authorizeMPToken(
view(), preFeeBalance_, mptIssuanceID->value(), account_, ctx_.journal);
view(),
preFeeBalance_,
mptIssuanceID->value(),
account_,
ctx_.journal,
lsfMPTVault);
!isTesSuccess(err))
return err;
}