feat: Move VaultClawback invariants from ValidVault to VaultClawback

Port the full ttVAULT_CLAWBACK invariant block from ValidVault::finalize
into VaultClawback::finalizeInvariants, following the pattern established
for VaultCreate. Extend VaultInvariantData with the balance-delta tracking
infrastructure (DeltaInfo, visitEntry, deltaAssets, deltaShares, etc.)
previously duplicated in ValidVault, so both per-transaction invariant
classes can share it without depending on ValidVault internals.
This commit is contained in:
Vito
2026-06-09 15:07:25 +02:00
parent d0a54d1159
commit 3a678f17fa
5 changed files with 503 additions and 112 deletions

View File

@@ -2,22 +2,28 @@
#include <xrpl/basics/Number.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/XRPAmount.h>
#include <cstdint>
#include <optional>
#include <unordered_map>
#include <vector>
namespace xrpl {
/**
* @brief Collects vault and share-issuance snapshots from ledger entry visits.
* @brief Collects vault and share-issuance snapshots from ledger entry visits,
* including full balance-delta tracking.
*
* Used by per-transaction invariant checks (e.g. VaultCreate) that need
* vault and MPTokenIssuance state without the full balance-delta tracking
* that ValidVault maintains.
* Used by per-transaction invariant checks (e.g. VaultCreate, VaultClawback)
* that need vault, MPTokenIssuance state and balance-delta information.
*/
class VaultInvariantData
{
@@ -48,6 +54,19 @@ public:
make(SLE const&);
};
struct DeltaInfo
{
Number delta = kNumZero;
std::optional<int> scale;
/**
* @brief Compute the delta between two Numbers, taking the coarsest
* scale.
*/
[[nodiscard]] static DeltaInfo
makeDelta(Number const& before, Number const& after, Asset const& asset);
};
void
visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after);
@@ -67,10 +86,92 @@ public:
[[nodiscard]] std::optional<Shares>
findShares(uint192 const& mptID) const;
/**
* @brief Find a deleted MPTokenIssuance (beforeMPTs_) whose mptID matches.
*
* Returns the Shares snapshot captured before the entry was deleted,
* or nullopt if no matching entry was found.
*/
[[nodiscard]] std::optional<Shares>
findDeletedShares(uint192 const& mptID) const;
/**
* @brief Return the before-MPT issuances vector (for deleted entries).
*/
[[nodiscard]] std::vector<Shares> const&
beforeMPTIssuances() const
{
return beforeMPTs_;
}
/**
* @brief Return the vault-asset balance-change delta for an account.
*
* Looks up the ledger-entry delta for the account entry (XRP), trust line
* (IOU), or MPToken (MPT) that corresponds to the vault asset held by
* @p id.
*
* @param id Account whose asset delta is requested.
* @returns The delta, or nullopt if the entry was not touched.
*/
[[nodiscard]] std::optional<DeltaInfo>
deltaAssets(AccountID const& id) const;
/**
* @brief Return the vault-asset delta for the transaction's sending
* account, adjusted for the fee.
*
* Calls deltaAssets for tx[sfAccount] and, for non-delegated XRP
* transactions, adds the consumed fee back so the invariant sees the net
* asset movement rather than the fee-reduced balance change.
*
* @param tx The transaction being applied.
* @param fee Fee charged by this transaction.
* @returns The fee-adjusted delta, or nullopt if the net delta is zero or
* the account entry was not touched.
*/
[[nodiscard]] std::optional<DeltaInfo>
deltaAssetsTxAccount(STTx const& tx, XRPAmount fee) const;
/**
* @brief Return the vault-share balance-change delta for an account.
*
* For the vault's pseudo-account the MPTokenIssuance outstanding-amount
* delta is returned; for all other accounts the MPToken delta is returned.
*
* @param id Account whose share delta is requested.
* @returns The delta, or nullopt if the entry was not touched.
*/
[[nodiscard]] std::optional<DeltaInfo>
deltaShares(AccountID const& id) const;
/**
* @brief Compute the coarsest scale required to represent all numbers.
*/
[[nodiscard]] static std::int32_t
computeCoarsestScale(std::vector<DeltaInfo> const& numbers);
/**
* @brief Compute the minimum STAmount scale for rounding invariant
* calculations.
*
* Post-amendment (fixCleanup3_2_0) this is simply the posterior
* assetsTotal scale. Pre-amendment it is the coarsest scale across
* vaultDelta and both asset-field deltas.
*
* @param vaultDelta Delta of the vault's asset balance for this transaction.
* @param rules Active ledger rules (used to check the amendment).
* @returns The minimum scale to apply when rounding vault-related amounts.
*/
[[nodiscard]] std::int32_t
computeVaultMinScale(DeltaInfo const& vaultDelta, Rules const& rules) const;
private:
std::vector<Vault> afterVault_;
std::vector<Vault> beforeVault_;
std::vector<Shares> afterMPTs_;
std::vector<Shares> beforeMPTs_;
std::unordered_map<uint256, DeltaInfo> deltas_;
};
} // namespace xrpl

View File

@@ -1,6 +1,7 @@
#pragma once
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/invariants/VaultInvariantData.h>
namespace xrpl {
@@ -34,6 +35,8 @@ public:
beast::Journal const& j) override;
private:
VaultInvariantData data_;
Expected<std::pair<STAmount, STAmount>, TER>
assetsToClawback(
SLE::ref vault,