mirror of
https://github.com/XRPLF/rippled.git
synced 2026-09-27 15:28:03 +00:00
WIP
This commit is contained in:
@@ -365,6 +365,11 @@ public:
|
||||
class ValidPseudoAccounts
|
||||
{
|
||||
std::vector<std::string> errors_;
|
||||
// Live pseudo-account entries touched by the transaction. Populated in
|
||||
// visitEntry and consumed by finalize to cross-check owner-field
|
||||
// resolution (e.g. VaultID -> vault whose sfAccount matches the
|
||||
// pseudo-account).
|
||||
std::vector<SLE::const_pointer> pseudoAccounts_;
|
||||
|
||||
public:
|
||||
void
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -34,24 +39,55 @@ class ValidLoanBroker
|
||||
};
|
||||
// Collect all the LoanBrokers found directly or indirectly through
|
||||
// pseudo-accounts. Key is the brokerID / index. It will be used to find the
|
||||
// LoanBroker object if brokerBefore and brokerAfter are nullptr
|
||||
// LoanBroker object if brokerBefore and brokerAfter are nullptr. Populated
|
||||
// for live (created or modified) brokers only; deletions go to
|
||||
// deletedBrokers_ below so the live-broker check loop does not have to
|
||||
// exempt the delete case.
|
||||
std::map<uint256, BrokerInfo> brokers_;
|
||||
// Pre-deletion snapshots of brokers erased by this transaction. Deletion
|
||||
// preconditions (e.g. first-loss capital was returned to the owner) are
|
||||
// stated positively against this collection, rather than by exempting the
|
||||
// delete case from the live-broker checks.
|
||||
std::vector<SLE::const_pointer> deletedBrokers_;
|
||||
// Collect all the modified trust lines. Their high and low accounts will be
|
||||
// loaded to look for LoanBroker pseudo-accounts.
|
||||
std::vector<SLE::const_pointer> lines_;
|
||||
// Collect all the modified MPTokens. Their accounts will be loaded to look
|
||||
// for LoanBroker pseudo-accounts.
|
||||
std::vector<SLE::const_pointer> mpts_;
|
||||
// Every touched (created or modified) balance-bearing entry, keyed by its
|
||||
// ledger key. Used by the broker-deletion checks to compute the change in
|
||||
// the owner's vault-asset balance and verify it matches the returned
|
||||
// first-loss capital. Deletion-side entries are not stored: their
|
||||
// after-state balance is zero by construction.
|
||||
std::unordered_map<uint256, std::pair<SLE::const_pointer, SLE::const_pointer>>
|
||||
touchedBalances_;
|
||||
|
||||
static bool
|
||||
goodZeroDirectory(ReadView const& view, SLE::const_ref dir, beast::Journal const& j);
|
||||
|
||||
// Return the balance of @p id in @p asset held by @p sle. Handles XRP
|
||||
// (ACCOUNT_ROOT), IOU (RIPPLE_STATE, sign-flipped per which side is @p id)
|
||||
// and MPT (MPTOKEN). Returns 0 when @p sle is null, so callers can compute
|
||||
// a delta uniformly across create / modify / delete transitions.
|
||||
[[nodiscard]] static Number
|
||||
balanceOf(SLE::const_ref sle, AccountID const& id, Asset const& asset);
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, SLE::const_ref, SLE::const_ref);
|
||||
|
||||
// The TER parameter is named because some checks (deltas, deletion
|
||||
// post-conditions, participant-flow identities) are class-2 and must gate
|
||||
// on isTesSuccess(result) to avoid firing against the fee-claim-only state
|
||||
// the framework re-runs in InvariantScope::ProtocolOnly.
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
finalize(
|
||||
STTx const&,
|
||||
TER const result,
|
||||
XRPAmount const,
|
||||
ReadView const&,
|
||||
beast::Journal const&);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
@@ -36,22 +37,29 @@ namespace xrpl {
|
||||
* - vault withdrawal and clawback reduce assets and share issuance, and
|
||||
* subtracts from: total assets, assets available, shares outstanding
|
||||
* - vault set must not alter the vault assets or shares balance
|
||||
* - every lending transaction touches exactly one loan: loan set creates one,
|
||||
* loan manage and loan pay each modify one
|
||||
* - loan set moves the requested principal out of the vault: it must create
|
||||
* exactly one loan, and decreases assets available (and the vault balance)
|
||||
* by the principal
|
||||
* - loan manage never removes assets from the vault: assets available may only
|
||||
* grow (and the vault balance grows with it, by the returned first-loss
|
||||
* capital on a default), and assets outstanding may only shrink (realized
|
||||
* loss); a loan manage with none of the sub-operation flags (impair,
|
||||
* capital on a default, which leaves the loan-broker pseudo-account by the
|
||||
* same amount), and assets outstanding may only shrink (realized loss); loss
|
||||
* unrealized moves in the direction of the sub-operation (up on impair, down
|
||||
* on unimpair and on default, as the paper loss is either reversed or
|
||||
* realized); a loan manage with none of the sub-operation flags (impair,
|
||||
* unimpair, default) is a no-op and must not modify the vault
|
||||
* - loan pay adds the paid principal and interest to the vault: assets
|
||||
* available (and the vault balance) increase by the same amount, which is at
|
||||
* most the amount paid; the combined inflow to the vault pseudo-account, the
|
||||
* loan-broker pseudo-account and the loan-broker owner never exceeds the
|
||||
* amount paid (no value is manufactured); and assets outstanding move in
|
||||
* lock-step: their change equals the cash received plus the change in the
|
||||
* paid loan's claim on the vault, which verifies the payment was split
|
||||
* correctly between principal and interest
|
||||
* amount paid (no value is manufactured); the vault's claim on the paid loan
|
||||
* may only shrink; and assets outstanding move in lock-step: their change
|
||||
* equals the cash received plus the change in the paid loan's claim on the
|
||||
* vault (the claim being the exposure the vault recognizes under its
|
||||
* accounting basis), which verifies the payment was split correctly between
|
||||
* principal and interest
|
||||
* - shares outstanding may only change through deposit, withdraw, or clawback
|
||||
* - no vault transaction can change loss unrealized (it's updated by loan
|
||||
* transactions)
|
||||
@@ -82,8 +90,12 @@ class ValidVault
|
||||
Number assetsAvailable = 0;
|
||||
Number assetsMaximum = 0;
|
||||
Number lossUnrealized = 0;
|
||||
std::uint32_t flags = 0;
|
||||
std::uint8_t withdrawalPolicy = 0;
|
||||
std::uint8_t scale = 0;
|
||||
// Recognition model (accrual vs. cash-basis) this vault was created
|
||||
// with; absent sfLEVersion means the legacy, accrual-basis model.
|
||||
VaultVersion version = VaultVersion::Legacy;
|
||||
std::optional<std::uint8_t> vaultKind;
|
||||
std::optional<std::uint32_t> subscriptionDate;
|
||||
std::optional<std::uint32_t> redemptionDate;
|
||||
@@ -96,31 +108,86 @@ class ValidVault
|
||||
MPTIssue share;
|
||||
std::uint64_t sharesTotal = 0;
|
||||
std::uint64_t sharesMaximum = 0;
|
||||
// Static share MPTokenIssuance fields snapshotted for post-creation
|
||||
// invariants (Items 3, 4). These are all set at VaultCreate time and
|
||||
// must not drift on any subsequent transaction.
|
||||
std::uint16_t transferFee = 0;
|
||||
std::uint8_t assetScale = 0;
|
||||
std::uint32_t flags = 0;
|
||||
|
||||
Shares static make(SLE const&);
|
||||
};
|
||||
|
||||
// Change in an MPToken holder's balance for a single share issuance. The
|
||||
// holder is preserved so a future check can point at the offending
|
||||
// account rather than just reporting an aggregate mismatch.
|
||||
struct ShareHoldingDelta final
|
||||
{
|
||||
AccountID holder;
|
||||
Number delta = kNumZero;
|
||||
};
|
||||
|
||||
struct Loan final
|
||||
{
|
||||
uint256 key = beast::kZero;
|
||||
uint256 loanBrokerID = beast::kZero;
|
||||
// Borrower of the loan. Snapshotted so the loan-set funding checks can
|
||||
// verify the principal, net of the origination fee, was credited to
|
||||
// this account.
|
||||
AccountID borrower;
|
||||
// Origination fee routed to the broker owner when the loan is funded.
|
||||
// Absent on the ledger entry when zero; the snapshot normalizes to
|
||||
// Number{0} in that case.
|
||||
Number originationFee = 0;
|
||||
Number principalOutstanding = 0;
|
||||
Number totalValueOutstanding = 0;
|
||||
Number managementFeeOutstanding = 0;
|
||||
// Whether lsfLoanImpaired was set on the ledger entry. Required by the
|
||||
// LossUnrealized magnitude checks in finalizeLoanManage, which switch
|
||||
// on the pre-transaction impairment state of a defaulted loan.
|
||||
bool impaired = false;
|
||||
|
||||
// Interest booked to the vault at loan creation: the portion of the
|
||||
// total value owed that is neither principal nor broker management fee.
|
||||
[[nodiscard]] Number
|
||||
interestDue() const;
|
||||
|
||||
// The vault's claim on the loan: the total value owed less the broker's
|
||||
// management fee (which belongs to the broker, not the vault).
|
||||
// The vault's claim on the loan, i.e. its exposure to the loan. This is
|
||||
// accounting-basis dependent: under accrual it is the total value owed
|
||||
// less the broker's management fee (which belongs to the broker, not
|
||||
// the vault); under cash-basis, where interest is only recognised once
|
||||
// received, it is the outstanding principal alone. Mirrors
|
||||
// loanVaultExposure in LendingHelpers.cpp.
|
||||
[[nodiscard]] Number
|
||||
claim() const;
|
||||
claim(VaultVersion version) const;
|
||||
|
||||
// The vault's exposure to the loan, used by the LossUnrealized
|
||||
// bookkeeping checks. Numerically identical to claim() under either
|
||||
// basis (see the note above), but named separately so that
|
||||
// impair / unimpair / default / pay checks read as "exposure" while
|
||||
// the LoanPay assets-outstanding identity reads as "claim".
|
||||
[[nodiscard]] Number
|
||||
exposure(VaultVersion version) const;
|
||||
|
||||
Loan static make(SLE const&);
|
||||
};
|
||||
|
||||
// Snapshot of a LoanBroker ledger entry. Populated by visitEntry whenever a
|
||||
// broker is touched by the transaction, so the lending-side finalizers can
|
||||
// compute deltas on DebtTotal, CoverAvailable and OwnerCount without a
|
||||
// separate read of the after-state.
|
||||
struct Broker final
|
||||
{
|
||||
uint256 key = beast::kZero;
|
||||
AccountID owner;
|
||||
uint256 vaultID = beast::kZero;
|
||||
Number debtTotal = 0;
|
||||
Number coverAvailable = 0;
|
||||
std::uint32_t ownerCount = 0;
|
||||
|
||||
Broker static make(SLE const&);
|
||||
};
|
||||
|
||||
public:
|
||||
struct DeltaInfo final
|
||||
{
|
||||
@@ -136,10 +203,17 @@ private:
|
||||
std::vector<Vault> afterVault_;
|
||||
std::vector<Shares> afterMPTs_;
|
||||
std::vector<Loan> afterLoan_;
|
||||
std::vector<Broker> afterBroker_;
|
||||
std::vector<Vault> beforeVault_;
|
||||
std::vector<Shares> beforeMPTs_;
|
||||
std::vector<Loan> beforeLoan_;
|
||||
std::vector<Broker> beforeBroker_;
|
||||
std::unordered_map<uint256, DeltaInfo> deltas_;
|
||||
// Per-issuance holder-side share deltas, populated for every touched
|
||||
// ltMPTOKEN. The universal share-conservation check in finalize sums
|
||||
// these for the vault's own share issuance and matches the total against
|
||||
// the issuance's OutstandingAmount delta.
|
||||
std::unordered_map<uint192, std::vector<ShareHoldingDelta>> shareHoldings_;
|
||||
|
||||
/**
|
||||
* @brief Compute the minimum STAmount scale for rounding invariant
|
||||
@@ -149,6 +223,12 @@ private:
|
||||
* @c assetsTotal scale. Pre-amendment it is the coarsest scale across
|
||||
* @p vaultDelta and both asset-field deltas.
|
||||
*
|
||||
* @pre @c afterVault_ is non-empty. Under the pre-amendment branch also
|
||||
* @c beforeVault_ is non-empty. Both preconditions are asserted at
|
||||
* runtime and hold for every current caller; the assert catches a
|
||||
* future reuse (e.g. from a new transactor via @c checkLoanFunding)
|
||||
* that reaches this helper without a snapshot.
|
||||
*
|
||||
* @param vaultDelta Delta of the vault's asset balance for this transaction.
|
||||
* @param rules Active ledger rules (used to check the amendment).
|
||||
* @return The minimum scale to apply when rounding vault-related amounts.
|
||||
@@ -208,21 +288,85 @@ private:
|
||||
[[nodiscard]] static bool
|
||||
isVaultEmpty(Vault const& vault);
|
||||
|
||||
/**
|
||||
* @brief Verify that the transaction touched exactly one loan.
|
||||
*
|
||||
* Every lending transaction operates on a single loan: @c ttLOAN_SET creates one, while
|
||||
* @c ttLOAN_MANAGE and @c ttLOAN_PAY each modify one. The per-transaction checks below index
|
||||
* the loan snapshots directly, so this must hold before they run.
|
||||
*
|
||||
* @param isCreate Whether the loan is expected to be created (rather than modified).
|
||||
* @param j Journal for logging invariant failures.
|
||||
* @return @c true when exactly one loan was created, respectively modified.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
exactlyOneLoan(bool isCreate, beast::Journal const& j) const;
|
||||
|
||||
/**
|
||||
* @brief Creation-side invariants of a loan-origination transaction.
|
||||
*
|
||||
* Enforces the phase gate (closed-ended vaults must be in the Investment phase to originate a
|
||||
* loan; open-ended vaults fall through) and, under @c fixCleanup3_4_0, the exactly-one-loan
|
||||
* cardinality expected of a creation. Extracted so the checks are callable from any transactor
|
||||
* that ends up creating a loan (see the eventual @c LoanAccept split); reads the transaction's
|
||||
* effect on the vault and loan snapshots directly from @c this.
|
||||
*
|
||||
* @param tx The transaction being applied.
|
||||
* @param view Active ledger view (used for rules).
|
||||
* @param j Journal for logging invariant failures.
|
||||
* @return @c true when the creation-side invariants hold.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
checkLoanCreation(STTx const& tx, ReadView const& view, beast::Journal const& j) const;
|
||||
|
||||
/**
|
||||
* @brief Funding-side invariants of a loan-origination transaction.
|
||||
*
|
||||
* Verifies that the transaction moved the requested principal out of the vault
|
||||
* pseudo-account, and that the created loan records exactly that principal. Under
|
||||
* @c featureLendingProtocolV1_1 also verifies the participant-side accounting: the broker's
|
||||
* @c DebtTotal grows by the new loan's exposure (basis-aware), the borrower and broker owner
|
||||
* receive their respective portions of the principal, and the vault's @c AssetsTotal /
|
||||
* @c AssetsAvailable / claim identity holds at origination. Assumes @c checkLoanCreation has
|
||||
* already run and returned @c true (in particular that the loan cardinality is one), so it may
|
||||
* index @c afterLoan_[0] directly. Extracted so a future @c LoanAccept transactor can reuse
|
||||
* the funding checks independently of the creation ones.
|
||||
*
|
||||
* @param tx The transaction being applied.
|
||||
* @param fee Fee charged by this transaction; added back when the fee-payer is one
|
||||
* of the participants whose vault-asset flow we compare.
|
||||
* @param view Active ledger view (used for rules).
|
||||
* @param j Journal for logging invariant failures.
|
||||
* @return @c true when the funding-side invariants hold.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
checkLoanFunding(
|
||||
STTx const& tx,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) const;
|
||||
|
||||
/**
|
||||
* @brief Invariant check for @c ttLOAN_SET.
|
||||
*
|
||||
* For a closed-ended vault, a loan may only be originated while the vault is in the Investment
|
||||
* phase (strictly past @c SubscriptionDate and before @c RedemptionDate). Open-ended vaults (@c
|
||||
* NoPhase) are unaffected. The complementary maturity bound (final payment strictly precedes @c
|
||||
* RedemptionDate) is enforced by @c ValidLoan.
|
||||
* NoPhase) are exempt from the phase gate only; the funding checks apply to every vault kind.
|
||||
* The complementary maturity bound (final payment strictly precedes @c RedemptionDate) is
|
||||
* enforced by @c ValidLoan.
|
||||
*
|
||||
* @param tx The transaction being applied.
|
||||
* @param fee Fee charged by this transaction.
|
||||
* @param view Active ledger view (used for rules).
|
||||
* @param j Journal for logging invariant failures.
|
||||
* @return @c true when all @c ttLOAN_SET invariants hold.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
finalizeLoanSet(STTx const& tx, ReadView const& view, beast::Journal const& j) const;
|
||||
finalizeLoanSet(
|
||||
STTx const& tx,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) const;
|
||||
|
||||
/**
|
||||
* @brief Enforce the invariants specific to a @c ttLOAN_MANAGE
|
||||
|
||||
Reference in New Issue
Block a user