fix: Add checkDepositFreeze and relax vault deposit freeze semantics

Add checkDepositFreeze that checks the depositor for regular freeze and
the destination pseudo-account for deep freeze only. A regular freeze on
the vault account's trust line no longer blocks deposits.

Update checkWithdrawFreezes to skip the submitter freeze check when
submitter == destination (withdrawing to self).

Gate VaultDeposit with fixCleanup3_3_0; pre-fix path preserves the old
isFrozen-based checks. Use checkDepositFreeze in LoanBrokerCoverDeposit.
This commit is contained in:
Vito
2026-06-12 14:17:31 +02:00
parent 612c31df81
commit e42cdbea2a
4 changed files with 57 additions and 16 deletions

View File

@@ -150,6 +150,18 @@ checkWithdrawFreezes(
AccountID const& dstAcct,
Asset const& asset);
/**
* Checks freeze compliance for a pseudo-account deposit.
* - The source pseudo-account is not deep frozen for the asset
* - The submitting account is not individually frozen for the asset
*/
[[nodiscard]] TER
checkDepositFreeze(
ReadView const& view,
AccountID const& sourceAcct,
AccountID const& dstAcct,
Asset const& asset);
//------------------------------------------------------------------------------
//
// Account balance functions (Asset-based dispatchers)

View File

@@ -167,13 +167,13 @@ checkDeepFrozen(ReadView const& view, AccountID const& account, Asset const& ass
TER
checkWithdrawFreezes(
ReadView const& view,
AccountID const& sourceAcct,
AccountID const& srcAcct,
AccountID const& submitterAcct,
AccountID const& dstAcct,
Asset const& asset)
{
XRPL_ASSERT(
isPseudoAccount(view, sourceAcct),
isPseudoAccount(view, srcAcct),
"xrpl::checkWithdrawFreezes : source must be a pseudo-account");
// No matter what, funds can be sent to the issuer
@@ -182,16 +182,39 @@ checkWithdrawFreezes(
// The transfer is from Submitter to Destination via Source (pseudo-account)
// Both Source and Submitter must not be frozen to allow sending funds
if (auto const ret = checkFrozen(view, sourceAcct, asset))
if (auto const ret = checkFrozen(view, srcAcct, asset))
return ret;
if (auto const ret = checkFrozen(view, submitterAcct, asset))
return ret;
// Check submitter's individual freeze only when Submitter != Destination (a regular freeze
// should not block self-withdrawal).
if (submitterAcct != dstAcct)
{
if (auto const ret = checkFrozen(view, submitterAcct, asset))
return ret;
}
// The destination account must not be deep frozen to receive the funds
return checkDeepFrozen(view, dstAcct, asset);
}
[[nodiscard]] TER
checkDepositFreeze(
ReadView const& view,
AccountID const& srcAcct,
AccountID const& dstAcct,
Asset const& asset)
{
XRPL_ASSERT(
isPseudoAccount(view, dstAcct),
"xrpl::checkDepositFreeze : destination must be a pseudo-account");
if (auto const ret = checkFrozen(view, srcAcct, asset))
return ret;
// Pseudo-account cannot receive if asset is deep frozen
return checkDeepFrozen(view, dstAcct, asset);
}
//------------------------------------------------------------------------------
//
// Account balance functions

View File

@@ -77,12 +77,10 @@ LoanBrokerCoverDeposit::preclaim(PreclaimContext const& ctx)
// Cannot transfer a non-transferable Asset
if (auto const ret = canTransfer(ctx.view, vaultAsset, account, pseudoAccountID))
return ret;
// Cannot transfer a frozen Asset
if (auto const ret = checkFrozen(ctx.view, account, vaultAsset))
return ret;
// Pseudo-account cannot receive if asset is deep frozen
if (auto const ret = checkDeepFrozen(ctx.view, pseudoAccountID, vaultAsset))
if (auto const ret = checkDepositFreeze(ctx.view, account, pseudoAccountID, vaultAsset))
return ret;
// Cannot transfer unauthorized asset
if (auto const ret = requireAuth(ctx.view, vaultAsset, account, AuthType::StrongAuth))
return ret;

View File

@@ -107,13 +107,21 @@ VaultDeposit::preclaim(PreclaimContext const& ctx)
// LCOV_EXCL_STOP
}
// Cannot deposit inside Vault an Asset frozen for the depositor
if (isFrozen(ctx.view, account, vaultAsset))
return vaultAsset.holds<Issue>() ? tecFROZEN : tecLOCKED;
if (ctx.view.rules().enabled(fixCleanup3_3_0))
{
if (auto const ret = checkDepositFreeze(ctx.view, account, vaultAccount, vaultAsset))
return ret;
}
else
{
// Cannot deposit inside Vault an Asset frozen for the depositor
if (isFrozen(ctx.view, account, vaultAsset))
return vaultAsset.holds<Issue>() ? tecFROZEN : tecLOCKED;
// Cannot deposit if the shares of the vault are frozen
if (isFrozen(ctx.view, account, vaultShare))
return tecLOCKED;
// Cannot deposit if the shares of the vault are frozen
if (isFrozen(ctx.view, account, vaultShare))
return tecLOCKED;
}
if (vault->isFlag(lsfVaultPrivate) && account != vault->at(sfOwner))
{