mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-05 01:37:00 +00:00
fix: Use IgnoreFreeze in doApply for issuer-redemption withdrawals
Hoist dstAcct into doApply and use FreezeHandling::IgnoreFreeze for accountHolds when dstAcct == vaultAsset.getIssuer() under fixCleanup3_2_0. Without this the locked-share balance read as zero and the redemption failed with tecINSUFFICIENT_FUNDS despite preclaim passing. Update tests: issuer-redemption now succeeds end-to-end for both IOU and MPT vaults.
This commit is contained in:
@@ -277,8 +277,12 @@ VaultWithdraw::doApply()
|
||||
return tecPATH_DRY;
|
||||
}
|
||||
|
||||
if (accountHolds(
|
||||
view(), accountID_, share, FreezeHandling::ZeroIfFrozen, AuthHandling::IgnoreAuth, j_) <
|
||||
auto const dstAcct = ctx_.tx[~sfDestination].value_or(accountID_);
|
||||
bool const isIssuerRedemption =
|
||||
view().rules().enabled(fixCleanup3_2_0) && dstAcct == vaultAsset.getIssuer();
|
||||
auto const freezeHandling =
|
||||
isIssuerRedemption ? FreezeHandling::IgnoreFreeze : FreezeHandling::ZeroIfFrozen;
|
||||
if (accountHolds(view(), accountID_, share, freezeHandling, AuthHandling::IgnoreAuth, j_) <
|
||||
sharesRedeemed)
|
||||
{
|
||||
JLOG(j_.debug()) << "VaultWithdraw: account doesn't hold enough shares";
|
||||
@@ -381,8 +385,6 @@ VaultWithdraw::doApply()
|
||||
// else quietly ignore, account balance is not zero
|
||||
}
|
||||
|
||||
auto const dstAcct = ctx_.tx[~sfDestination].value_or(accountID_);
|
||||
|
||||
associateAsset(*vault, vaultAsset);
|
||||
|
||||
return doWithdraw(
|
||||
|
||||
@@ -1701,22 +1701,14 @@ class Vault_test : public beast::unit_test::Suite
|
||||
tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
|
||||
env(tx, Ter(tecLOCKED));
|
||||
|
||||
// Redemption to the issuer: preclaim's issuer guard skips all
|
||||
// freeze checks, but doApply::accountHolds(ZeroIfFrozen) still
|
||||
// returns 0 for the locked shares (isVaultPseudoAccountFrozen is
|
||||
// true while the asset is globally locked).
|
||||
// TODO: doApply needs a matching fix to use Normal freeze handling
|
||||
// for the issuer-destination path.
|
||||
// Redemption to the issuer bypasses freeze checks end-to-end:
|
||||
// preclaim's issuer guard skips all three checks, and doApply uses
|
||||
// FreezeHandling::IgnoreFreeze for the accountHolds balance check.
|
||||
tx[sfDestination] = issuer.human();
|
||||
env(tx, Ter(tecINSUFFICIENT_FUNDS));
|
||||
|
||||
// Clawback is still permitted, even with global lock
|
||||
tx = vault.clawback(
|
||||
{.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)});
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
// Clawback removed shares MPToken
|
||||
// Withdrawal burned all depositor shares — MPToken is removed.
|
||||
auto const mptSle = env.le(keylet::mptoken(share, depositor.id()));
|
||||
BEAST_EXPECT(mptSle == nullptr);
|
||||
|
||||
@@ -2953,27 +2945,25 @@ class Vault_test : public beast::unit_test::Suite
|
||||
}
|
||||
|
||||
{
|
||||
// Preclaim passes (issuer guard skips all three checks), but
|
||||
// doApply::accountHolds(ZeroIfFrozen) still returns 0 for the
|
||||
// owner's shares because isVaultPseudoAccountFrozen is true
|
||||
// while the vault's trust line is frozen.
|
||||
// TODO: doApply needs a matching fix to use Normal freeze
|
||||
// handling for the issuer-destination path.
|
||||
// Withdrawal to the IOU issuer succeeds end-to-end: the issuer
|
||||
// guard skips all preclaim checks, and doApply uses
|
||||
// FreezeHandling::IgnoreFreeze so accountHolds returns the
|
||||
// actual balance rather than zero.
|
||||
auto t =
|
||||
vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(50)});
|
||||
t[sfDestination] = issuer.human();
|
||||
env(t, Ter{tecINSUFFICIENT_FUNDS});
|
||||
env(t);
|
||||
env.close();
|
||||
}
|
||||
|
||||
// Vault unchanged (100 assets / 100'000'000 shares). Clear freeze
|
||||
// and drain before deletion.
|
||||
// vault now has 50 assets / owner holds 50'000'000 shares
|
||||
// Clear freeze and drain what remains.
|
||||
trustSet[jss::Flags] = tfClearFreeze;
|
||||
env(trustSet);
|
||||
env.close();
|
||||
|
||||
env(vault.withdraw(
|
||||
{.depositor = owner, .id = keylet.key, .amount = share(100'000'000)}));
|
||||
{.depositor = owner, .id = keylet.key, .amount = share(50'000'000)}));
|
||||
|
||||
env(vault.del({.owner = owner, .id = keylet.key}));
|
||||
env.close();
|
||||
|
||||
Reference in New Issue
Block a user