adds BLockDeposit flagsto VaultSet

This commit is contained in:
Vito
2026-02-12 17:42:27 +01:00
parent c808c46049
commit a2198146a8
6 changed files with 155 additions and 2 deletions

View File

@@ -296,7 +296,7 @@ constexpr std::uint32_t const tfLoanManageMask = ~(tfUniversal | tfLoanDefault |
// VaultSet flags:
constexpr std::uint32_t const tfVaultDepositBlock = 0x00010000;
constexpr std::uint32_t const tfVaultDepositUnblock = 0x00020000;
constexpr std::uint32_t const tfVaultManageMask = ~(tfUniversal | tfVaultDepositBlock | tfVaultDepositUnblock);
constexpr std::uint32_t const tfVaultSetMask = ~(tfUniversal | tfVaultDepositBlock | tfVaultDepositUnblock);
// clang-format on

View File

@@ -4990,6 +4990,97 @@ class Vault_test : public beast::unit_test::suite
}
}
void
testVaultSetVaultDepositFlagValidation()
{
using namespace test::jtx;
auto const all = testable_amendments();
{
testcase("VaultSet VaultDepositBlock fixLendingProtocolV1_1 disabled");
Env env{*this, all - fixLendingProtocolV1_1};
Account owner{"owner"};
env.fund(XRP(1'000'000), owner);
env.close();
PrettyAsset asset = xrpIssue();
Vault vault{env};
auto const [tx, keylet] = vault.create({.owner = owner, .asset = asset, .flags = tfVaultPrivate});
env(tx, ter(tesSUCCESS), THISLINE);
env.close();
env(vault.set({.owner = owner, .id = keylet.key, .flags = tfVaultDepositBlock}),
ter(temDISABLED),
THISLINE);
env.close();
}
{
std::string const prefix = "VaultSet(VaultDepositBlock): ";
Env env{*this, all | fixLendingProtocolV1_1};
Account owner{"owner"};
env.fund(XRP(1'000'000), owner);
env.close();
PrettyAsset asset = xrpIssue();
Vault vault{env};
auto const [tx, keylet] = vault.create({.owner = owner, .asset = asset, .flags = tfVaultPrivate});
env(tx, ter(tesSUCCESS), THISLINE);
env.close();
{
testcase(prefix + "invalid flags");
env(vault.set({.owner = owner, .id = keylet.key, .flags = tfVaultDepositBlock | tfVaultDepositUnblock}),
ter(temMALFORMED),
THISLINE);
env.close();
}
{
testcase(prefix + "unblock already unblocked vault");
// Cannot unblock an already unblocked vault
env(vault.set({.owner = owner, .id = keylet.key, .flags = tfVaultDepositUnblock}),
ter(tecNO_PERMISSION),
THISLINE);
}
{
testcase(prefix + "set and clear vault flag");
env(vault.set({.owner = owner, .id = keylet.key, .flags = tfVaultDepositBlock}),
ter(tesSUCCESS),
THISLINE);
auto sleVault = env.le(keylet);
if (!BEAST_EXPECT(sleVault))
return;
if (!BEAST_EXPECT(sleVault->isFlag(lsfVaultDepositBlocked)))
return;
// Cannot block an already blocked vault
env(vault.set({.owner = owner, .id = keylet.key, .flags = tfVaultDepositBlock}),
ter(tecNO_PERMISSION),
THISLINE);
// Cannot unblock an already unblocked vault
env(vault.set({.owner = owner, .id = keylet.key, .flags = tfVaultDepositUnblock}),
ter(tesSUCCESS),
THISLINE);
sleVault = env.le(keylet);
if (!BEAST_EXPECT(sleVault))
return;
BEAST_EXPECT(!sleVault->isFlag(lsfVaultDepositBlocked));
}
}
}
public:
void
run() override
@@ -5011,6 +5102,7 @@ public:
testVaultClawbackBurnShares();
testVaultClawbackAssets();
testAssetsMaximum();
testVaultSetVaultDepositFlagValidation();
}
};

View File

@@ -31,6 +31,8 @@ Vault::set(SetArgs const& args)
jv[jss::TransactionType] = jss::VaultSet;
jv[jss::Account] = args.owner.human();
jv[sfVaultID] = to_string(args.id);
if (args.flags)
jv[jss::Flags] = *args.flags;
return jv;
}

View File

@@ -36,6 +36,7 @@ struct Vault
{
Account owner;
uint256 id;
std::optional<std::uint32_t> flags{};
};
Json::Value

View File

@@ -21,9 +21,34 @@ VaultSet::checkExtraFeatures(PreflightContext const& ctx)
return true;
}
std::uint32_t
VaultSet::getFlagsMask(PreflightContext const& ctx)
{
return tfVaultSetMask;
}
static bool
isValidVaultUpdate(PreflightContext const& ctx)
{
auto const checkFlags = ctx.rules.enabled(fixLendingProtocolV1_1);
auto const atLeastOneFieldPresent =
ctx.tx.isFieldPresent(sfDomainID) || ctx.tx.isFieldPresent(sfAssetsMaximum) || ctx.tx.isFieldPresent(sfData);
return atLeastOneFieldPresent ||
(checkFlags && (ctx.tx.isFlag(tfVaultDepositBlock) || ctx.tx.isFlag(tfVaultDepositUnblock)));
}
NotTEC
VaultSet::preflight(PreflightContext const& ctx)
{
if ((ctx.tx.isFlag(tfVaultDepositBlock) || ctx.tx.isFlag(tfVaultDepositUnblock)) &&
!ctx.rules.enabled(fixLendingProtocolV1_1))
{
JLOG(ctx.j.debug()) << "VaultSet: flags not supported without fixLendingProtocolV1_1.";
return temDISABLED;
}
if (ctx.tx[sfVaultID] == beast::zero)
{
JLOG(ctx.j.debug()) << "VaultSet: zero/empty vault ID.";
@@ -48,12 +73,18 @@ VaultSet::preflight(PreflightContext const& ctx)
}
}
if (!ctx.tx.isFieldPresent(sfDomainID) && !ctx.tx.isFieldPresent(sfAssetsMaximum) && !ctx.tx.isFieldPresent(sfData))
if (!isValidVaultUpdate(ctx))
{
JLOG(ctx.j.debug()) << "VaultSet: nothing is being updated.";
return temMALFORMED;
}
if (ctx.tx.isFlag(tfVaultDepositBlock) && ctx.tx.isFlag(tfVaultDepositUnblock))
{
JLOG(ctx.j.debug()) << "VaultSet: cannot set tfVaultDepositBlock and tfVaultDepositUnblock simultaneously.";
return temMALFORMED;
}
return tesSUCCESS;
}
@@ -107,6 +138,21 @@ VaultSet::preclaim(PreclaimContext const& ctx)
}
}
if (ctx.view.rules().enabled(fixLendingProtocolV1_1))
{
if (vault->isFlag(lsfVaultDepositBlocked) && ctx.tx.isFlag(tfVaultDepositBlock))
{
JLOG(ctx.j.debug()) << "VaultSet: vault deposit is already blocked";
return tecNO_PERMISSION;
}
if (!vault->isFlag(lsfVaultDepositBlocked) && ctx.tx.isFlag(tfVaultDepositUnblock))
{
JLOG(ctx.j.debug()) << "VaultSet: vault deposit is already unblocked";
return tecNO_PERMISSION;
}
}
return tesSUCCESS;
}
@@ -164,6 +210,15 @@ VaultSet::doApply()
view().update(sleIssuance);
}
if (view().rules().enabled(fixLendingProtocolV1_1))
{
if (tx.isFlag(tfVaultDepositBlock))
vault->setFlag(lsfVaultDepositBlocked);
if (tx.isFlag(tfVaultDepositUnblock))
vault->clearFlag(lsfVaultDepositBlocked);
}
// Note, we must update Vault object even if only DomainID is being updated
// in Issuance object. Otherwise it's really difficult for Vault invariants
// to verify the operation.

View File

@@ -13,6 +13,9 @@ public:
{
}
static std::uint32_t
getFlagsMask(PreflightContext const& ctx);
static bool
checkExtraFeatures(PreflightContext const& ctx);