feat: Move VaultDelete invariants from ValidVault to VaultDelete

This commit is contained in:
Vito
2026-06-09 14:50:59 +02:00
parent d0a54d1159
commit c8b66c8961
5 changed files with 97 additions and 50 deletions

View File

@@ -67,10 +67,15 @@ public:
[[nodiscard]] std::optional<Shares>
findShares(uint192 const& mptID) const;
/** Find deleted shares in beforeMPTs_ whose mptID matches. */
[[nodiscard]] std::optional<Shares>
findDeletedShares(uint192 const& mptID) const;
private:
std::vector<Vault> afterVault_;
std::vector<Vault> beforeVault_;
std::vector<Shares> afterMPTs_;
std::vector<Shares> beforeMPTs_;
};
} // namespace xrpl

View File

@@ -1,6 +1,7 @@
#pragma once
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/invariants/VaultInvariantData.h>
namespace xrpl {
@@ -32,6 +33,9 @@ public:
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
private:
VaultInvariantData data_;
};
} // namespace xrpl

View File

@@ -5,7 +5,6 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Issue.h>
@@ -339,51 +338,8 @@ ValidVault::finalize(
return !enforce; // That's all we can do here
}
// Note, if afterVault_ is empty then we know that beforeVault_ is not
// empty, as enforced at the top of this function
auto const& beforeVault = beforeVault_[0];
// At this moment we only know a vault is being deleted and there
// might be some MPTokenIssuance objects which are deleted in the
// same transaction. Find the one matching this vault.
auto const deletedShares = [&]() -> std::optional<Shares> {
for (auto const& e : beforeMPTs_)
{
if (e.share.getMptID() == beforeVault.shareMPTID)
return e;
}
return std::nullopt;
}();
if (!deletedShares)
{
JLOG(j.fatal()) << "Invariant failed: deleted vault must also "
"delete shares";
XRPL_ASSERT(enforce, "xrpl::ValidVault::finalize : shares deletion invariant");
return !enforce; // That's all we can do here
}
bool result = true;
if (deletedShares->sharesTotal != 0)
{
JLOG(j.fatal()) << "Invariant failed: deleted vault must have no "
"shares outstanding";
result = false;
}
if (beforeVault.assetsTotal != kZero)
{
JLOG(j.fatal()) << "Invariant failed: deleted vault must have no "
"assets outstanding";
result = false;
}
if (beforeVault.assetsAvailable != kZero)
{
JLOG(j.fatal()) << "Invariant failed: deleted vault must have no "
"assets available";
result = false;
}
return result;
// Delete-specific checks now live in VaultDelete::finalizeInvariants.
return true;
}
if (txnType == ttVAULT_DELETE)
{

View File

@@ -5,8 +5,11 @@
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STNumber.h> // IWYU pragma: keep
#include <optional>
namespace xrpl {
VaultInvariantData::Vault
@@ -51,6 +54,9 @@ VaultInvariantData::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_
if (before && before->getType() == ltVAULT)
beforeVault_.push_back(Vault::make(*before));
if (before && before->getType() == ltMPTOKEN_ISSUANCE)
beforeMPTs_.push_back(Shares::make(*before));
if (!isDelete && after)
{
switch (after->getType())
@@ -77,4 +83,15 @@ VaultInvariantData::findShares(uint192 const& mptID) const
return std::nullopt;
}
std::optional<VaultInvariantData::Shares>
VaultInvariantData::findDeletedShares(uint192 const& mptID) const
{
for (auto const& s : beforeMPTs_)
{
if (s.share.getMptID() == mptID)
return s;
}
return std::nullopt;
}
} // namespace xrpl

View File

@@ -1,12 +1,16 @@
#include <xrpl/tx/transactors/vault/VaultDelete.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/Number.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/SField.h>
@@ -211,15 +215,76 @@ VaultDelete::doApply()
}
void
VaultDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
VaultDelete::visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after)
{
// No transaction-specific invariants yet (future work).
data_.visitEntry(isDelete, before, after);
}
bool
VaultDelete::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
VaultDelete::finalizeInvariants(
STTx const&,
TER result,
XRPAmount,
ReadView const& view,
beast::Journal const& j)
{
// No transaction-specific invariants yet (future work).
bool const enforce = view.rules().enabled(featureSingleAssetVault);
if (!isTesSuccess(result))
return true;
static constexpr Number kZero{};
// VaultDelete must have deleted the vault; if it still exists, something
// went wrong.
if (!data_.afterVaults().empty())
{
JLOG(j.fatal()) << "Invariant failed: vault not deleted by VaultDelete";
XRPL_ASSERT(enforce, "xrpl::VaultDelete::finalizeInvariants : vault not deleted invariant");
return !enforce;
}
// Nothing to check if the vault was never in the before-set (e.g. a
// test that omits the vault entry entirely).
if (data_.beforeVaults().empty())
return true;
auto const& beforeVault = data_.beforeVaults()[0];
auto const deletedShares = data_.findDeletedShares(beforeVault.shareMPTID);
if (!deletedShares)
{
JLOG(j.fatal()) << "Invariant failed: deleted vault must also delete shares";
XRPL_ASSERT(enforce, "xrpl::VaultDelete::finalizeInvariants : shares deletion invariant");
return !enforce;
}
bool result2 = true;
if (deletedShares->sharesTotal != 0)
{
JLOG(j.fatal()) << "Invariant failed: deleted vault must have no shares outstanding";
result2 = false;
}
if (beforeVault.assetsTotal != kZero)
{
JLOG(j.fatal()) << "Invariant failed: deleted vault must have no assets outstanding";
result2 = false;
}
if (beforeVault.assetsAvailable != kZero)
{
JLOG(j.fatal()) << "Invariant failed: deleted vault must have no assets available";
result2 = false;
}
if (!result2)
{
XRPL_ASSERT(enforce, "xrpl::VaultDelete::finalizeInvariants : vault delete invariants");
return !enforce;
}
return true;
}