mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-09 11:46:49 +00:00
Compare commits
2 Commits
a1q123456/
...
tapanito/c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11e33560fd | ||
|
|
67037782af |
@@ -103,23 +103,7 @@ public:
|
||||
view_->rawDestroyXRP(fee);
|
||||
}
|
||||
|
||||
/** Applies all invariant checkers one by one.
|
||||
|
||||
@param result the result generated by processing this transaction.
|
||||
@param fee the fee charged for this transaction
|
||||
@return the result code that should be returned for this transaction.
|
||||
*/
|
||||
TER
|
||||
checkInvariants(TER const result, XRPAmount const fee);
|
||||
|
||||
private:
|
||||
static TER
|
||||
failInvariantCheck(TER const result);
|
||||
|
||||
template <std::size_t... Is>
|
||||
TER
|
||||
checkInvariantsHelper(TER const result, XRPAmount const fee, std::index_sequence<Is...>);
|
||||
|
||||
OpenView& base_;
|
||||
ApplyFlags flags_;
|
||||
std::optional<ApplyViewImpl> view_;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
#include <xrpl/tx/invariants/CheckInvariants.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
@@ -142,19 +143,30 @@ public:
|
||||
return ctx_.view();
|
||||
}
|
||||
|
||||
/** Whether to run the transaction-specific invariant check.
|
||||
*
|
||||
* After a fee-claim reset the transaction's effects have been rolled back,
|
||||
* so only the protocol invariants are meaningful; pass @c Yes to skip the
|
||||
* transaction-specific check in that case.
|
||||
*/
|
||||
enum class SkipTxInvariants : bool { No = false, Yes = true };
|
||||
|
||||
/** Check all invariants for the current transaction.
|
||||
*
|
||||
* Runs transaction-specific invariants first (visitInvariantEntry +
|
||||
* finalizeInvariants), then protocol-level invariants. Both layers
|
||||
* always run; the worst failure code is returned.
|
||||
* Delegates to the free @c xrpl::checkInvariants runner. Unless @p skip is
|
||||
* @c SkipTxInvariants::Yes, the transaction-specific adapter is passed so
|
||||
* both layers share a single walk of the modified ledger entries.
|
||||
* Protocol faults (tefINVARIANT_FAILED) take priority over transaction
|
||||
* faults (tecINVARIANT_FAILED).
|
||||
*
|
||||
* @param result the tentative TER from transaction processing.
|
||||
* @param fee the fee consumed by the transaction.
|
||||
* @param skip whether to skip the transaction-specific invariant check.
|
||||
*
|
||||
* @return the final TER after all invariant checks.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkInvariants(TER result, XRPAmount fee);
|
||||
checkInvariants(TER result, XRPAmount fee, SkipTxInvariants skip);
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/*
|
||||
@@ -287,7 +299,7 @@ protected:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) = 0;
|
||||
beast::Journal const& j) const = 0;
|
||||
|
||||
/** Compute the minimum fee required to process a transaction
|
||||
with a given baseFee based on the current server load.
|
||||
@@ -404,20 +416,34 @@ private:
|
||||
static NotTEC
|
||||
preflightUniversal(PreflightContext const& ctx);
|
||||
|
||||
/** Check transaction-specific invariants only.
|
||||
*
|
||||
* Walks every modified ledger entry via visitInvariantEntry, then
|
||||
* calls finalizeInvariants on the derived transactor. Returns
|
||||
* tecINVARIANT_FAILED if any transaction invariant is violated.
|
||||
*
|
||||
* @param result the tentative TER from transaction processing.
|
||||
* @param fee the fee consumed by the transaction.
|
||||
*
|
||||
* @return the original result if all invariants pass, or
|
||||
* tecINVARIANT_FAILED otherwise.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkTransactionInvariants(TER result, XRPAmount fee);
|
||||
/** Bridges the transaction-specific two-phase invariant hooks
|
||||
* (visitInvariantEntry + finalizeInvariants) into the InvariantCheck
|
||||
* interface consumed by the free xrpl::checkInvariants runner. */
|
||||
class InvariantCheckAdapter : public InvariantCheck
|
||||
{
|
||||
Transactor& self_;
|
||||
|
||||
public:
|
||||
explicit InvariantCheckAdapter(Transactor& self) : self_(self)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
|
||||
{
|
||||
self_.visitInvariantEntry(isDelete, before, after);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
finalize(
|
||||
STTx const& tx,
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
InvariantCheckAdapter invariantCheck_{*this};
|
||||
};
|
||||
|
||||
inline bool
|
||||
|
||||
129
include/xrpl/tx/invariants/CheckInvariants.h
Normal file
129
include/xrpl/tx/invariants/CheckInvariants.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Runtime interface for a transaction-specific invariant check.
|
||||
*
|
||||
* The free @c checkInvariants runner drives two layers of checks over a single
|
||||
* walk of the modified ledger entries:
|
||||
*
|
||||
* - **Protocol checks** are the concrete types in @c InvariantChecks, held in
|
||||
* a @c std::tuple and dispatched statically by a compile-time fold (no
|
||||
* virtual calls). They are duck-typed against the two-phase contract
|
||||
* described below; see @c InvariantChecker_PROTOTYPE in InvariantCheck.h.
|
||||
* - **The transaction-specific check** is injected at runtime through this
|
||||
* interface, so the runner can call it without depending on the concrete
|
||||
* transactor type.
|
||||
*
|
||||
* Both layers honour the same two-phase protocol:
|
||||
*
|
||||
* **Phase 1 — state collection** (`visitEntry`)
|
||||
* Called once for each ledger entry created, modified, or deleted by the
|
||||
* transaction. Implementations accumulate whatever state they need to
|
||||
* evaluate their post-conditions. Must not throw.
|
||||
*
|
||||
* **Phase 2 — condition evaluation** (`finalize`)
|
||||
* Called once after every modified entry has been visited. Returns true if
|
||||
* all post-conditions hold, false to fail the transaction.
|
||||
*
|
||||
* ## Rules for implementing `finalize`
|
||||
*
|
||||
* ### Invariants must run regardless of transaction result
|
||||
*
|
||||
* `finalize` MUST perform meaningful checks even when the transaction has
|
||||
* failed (`!isTesSuccess(result)`). A bug or exploit could cause a failed
|
||||
* transaction to mutate ledger state in unexpected ways; invariants are the
|
||||
* last line of defense.
|
||||
*
|
||||
* The typical pattern: an invariant that expects a domain-specific state
|
||||
* change (e.g. a Vault being created) should expect that change only when
|
||||
* the transaction succeeded. A failed VaultCreate must not have created a
|
||||
* Vault.
|
||||
*
|
||||
* ### Privilege-gated checks apply to failed transactions too
|
||||
*
|
||||
* Failed transactions carry no privileges. Any privilege-gated assertion
|
||||
* must therefore also be enforced for failed transactions.
|
||||
*/
|
||||
class InvariantCheck
|
||||
{
|
||||
public:
|
||||
virtual ~InvariantCheck() = default;
|
||||
|
||||
/**
|
||||
* @brief Called for each ledger entry modified by the transaction.
|
||||
*
|
||||
* @param isDelete true if the SLE is being deleted.
|
||||
* @param before the entry's state before the transaction (nullptr for
|
||||
* newly created entries).
|
||||
* @param after the entry's state after the transaction. For deletions
|
||||
* this is the SLE being erased; use @p isDelete rather than
|
||||
* `after == nullptr` to detect deletions.
|
||||
*/
|
||||
virtual void
|
||||
visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) = 0;
|
||||
|
||||
/**
|
||||
* @brief Called after all entries have been visited.
|
||||
*
|
||||
* @param tx the transaction being applied.
|
||||
* @param result the tentative TER result of the transaction.
|
||||
* @param fee the fee consumed by the transaction.
|
||||
* @param view read-only view of the ledger after the transaction.
|
||||
* @param j journal for logging invariant failures.
|
||||
* @return true if all invariants hold; false to fail with
|
||||
* tecINVARIANT_FAILED / tefINVARIANT_FAILED.
|
||||
*/
|
||||
[[nodiscard]] virtual bool
|
||||
finalize(
|
||||
STTx const& tx,
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Run all protocol invariant checks plus the transaction-specific check
|
||||
* in a single pass over the modified entries.
|
||||
*
|
||||
* Both layers share one walk of the modified-entry set: @p txCheck's
|
||||
* `visitEntry` accumulates state on the same traversal that drives the
|
||||
* protocol checkers, then both layers' `finalize` run on the complete state.
|
||||
* Protocol faults (tefINVARIANT_FAILED) take precedence over transaction
|
||||
* faults (tecINVARIANT_FAILED).
|
||||
*
|
||||
* Every transaction is guaranteed to supply a @p txCheck (either a real check
|
||||
* or a no-op stub). The invariant contract requires @c finalize to handle
|
||||
* failed results gracefully, so passing the same @p txCheck after a fee-claim
|
||||
* reset is safe.
|
||||
*
|
||||
* @param ctx the apply context for the current transaction.
|
||||
* @param result the tentative TER from transaction processing.
|
||||
* @param fee the fee consumed by the transaction.
|
||||
* @param txCheck the transaction-specific invariant check.
|
||||
* @return the final TER after all invariant checks.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkInvariants(
|
||||
ApplyContext& ctx,
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
std::optional<std::reference_wrapper<InvariantCheck>> txCheck);
|
||||
|
||||
[[nodiscard]] inline TER
|
||||
checkInvariants(ApplyContext& ctx, TER result, XRPAmount fee)
|
||||
{
|
||||
return checkInvariants(ctx, result, fee, std::nullopt);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
// Interface used by AccountDelete
|
||||
static TER
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
class BridgeModify : public Transactor
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
using XChainModifyBridge = BridgeModify;
|
||||
@@ -113,7 +113,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -151,7 +151,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -191,7 +191,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -231,7 +231,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
class XChainAddAccountCreateAttestation : public Transactor
|
||||
@@ -262,7 +262,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -317,7 +317,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
using XChainAccountCreateCommit = XChainCreateAccountCommit;
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
// Interface used by AccountDelete
|
||||
static TER
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
private:
|
||||
TER
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
private:
|
||||
std::pair<TER, bool>
|
||||
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
/** Equal-asset withdrawal (LPTokens) of some AMM instance pools
|
||||
* shares represented by the number of LPTokens .
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
private:
|
||||
std::pair<TER, bool>
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
public:
|
||||
static constexpr std::uint32_t kMinPaymentTotal = 1;
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
// Public to support unit tests.
|
||||
static uint256
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
static TER
|
||||
deleteOracle(ApplyView& view, SLE::ref sle, AccountID const& account, beast::Journal j);
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
// Interface used by AccountDelete
|
||||
static TER
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
static constexpr auto kDisabledTxTypes = std::to_array<TxType>({
|
||||
ttVAULT_CREATE,
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
static Expected<MPTID, TER>
|
||||
create(ApplyView& view, beast::Journal journal, MPTCreateArgs const& args);
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
private:
|
||||
Expected<std::pair<STAmount, STAmount>, TER>
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -12,15 +12,10 @@
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxMeta.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/invariants/InvariantCheck.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -74,75 +69,4 @@ ApplyContext::visit(
|
||||
view_->visit(base_, func); // NOLINT(bugprone-unchecked-optional-access)
|
||||
}
|
||||
|
||||
TER
|
||||
ApplyContext::failInvariantCheck(TER const result)
|
||||
{
|
||||
// If we already failed invariant checks before and we are now attempting to
|
||||
// only charge a fee, and even that fails the invariant checks something is
|
||||
// very wrong. We switch to tefINVARIANT_FAILED, which does NOT get included
|
||||
// in a ledger.
|
||||
|
||||
return (result == tecINVARIANT_FAILED || result == tefINVARIANT_FAILED)
|
||||
? TER{tefINVARIANT_FAILED}
|
||||
: TER{tecINVARIANT_FAILED};
|
||||
}
|
||||
|
||||
template <std::size_t... Is>
|
||||
TER
|
||||
ApplyContext::checkInvariantsHelper(
|
||||
TER const result,
|
||||
XRPAmount const fee,
|
||||
std::index_sequence<Is...>)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto checkers = getInvariantChecks();
|
||||
|
||||
// call each check's per-entry method
|
||||
visit(
|
||||
[&checkers](
|
||||
uint256 const& index, bool isDelete, SLE::const_ref before, SLE::const_ref after) {
|
||||
(..., std::get<Is>(checkers).visitEntry(isDelete, before, after));
|
||||
});
|
||||
|
||||
// Note: do not replace this logic with a `...&&` fold expression.
|
||||
// The fold expression will only run until the first check fails (it
|
||||
// short-circuits). While the logic is still correct, the log
|
||||
// message won't be. Every failed invariant should write to the log,
|
||||
// not just the first one.
|
||||
std::array<bool, sizeof...(Is)> const finalizers{{std::get<Is>(checkers).finalize(
|
||||
tx, result, fee, *view_, journal)...}}; // NOLINT(bugprone-unchecked-optional-access)
|
||||
|
||||
// call each check's finalizer to see that it passes
|
||||
if (!std::all_of(finalizers.cbegin(), finalizers.cend(), [](auto const& b) { return b; }))
|
||||
{
|
||||
JLOG(journal.fatal()) << "Transaction has failed one or more global invariants: "
|
||||
<< to_string(tx.getJson(JsonOptions::Values::None));
|
||||
|
||||
return failInvariantCheck(result);
|
||||
}
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
JLOG(journal.fatal()) << "Transaction caused an exception in a global invariant"
|
||||
<< ", ex: " << ex.what()
|
||||
<< ", tx: " << to_string(tx.getJson(JsonOptions::Values::None));
|
||||
|
||||
return failInvariantCheck(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TER
|
||||
ApplyContext::checkInvariants(TER const result, XRPAmount const fee)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
isTesSuccess(result) || isTecClaim(result),
|
||||
"xrpl::ApplyContext::checkInvariants : is tesSUCCESS or tecCLAIM");
|
||||
|
||||
return checkInvariantsHelper(
|
||||
result, fee, std::make_index_sequence<std::tuple_size_v<InvariantChecks>>{});
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
@@ -1135,58 +1134,24 @@ Transactor::trapTransaction(uint256 txHash) const
|
||||
JLOG(j_.debug()) << "Transaction trapped: " << txHash;
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
Transactor::checkTransactionInvariants(TER result, XRPAmount fee)
|
||||
[[nodiscard]] bool
|
||||
Transactor::InvariantCheckAdapter::finalize(
|
||||
STTx const& tx,
|
||||
TER const result,
|
||||
XRPAmount const fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) const
|
||||
{
|
||||
try
|
||||
{
|
||||
// Phase 1: visit modified entries
|
||||
ctx_.visit(
|
||||
[this](uint256 const&, bool isDelete, SLE::const_ref before, SLE::const_ref after) {
|
||||
this->visitInvariantEntry(isDelete, before, after);
|
||||
});
|
||||
|
||||
// Phase 2: finalize
|
||||
if (!this->finalizeInvariants(ctx_.tx, result, fee, ctx_.view(), ctx_.journal))
|
||||
{
|
||||
JLOG(ctx_.journal.fatal()) << //
|
||||
"Transaction has failed one or more transaction invariants, tx: " << //
|
||||
to_string(ctx_.tx.getJson(JsonOptions::Values::None));
|
||||
return tecINVARIANT_FAILED;
|
||||
}
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
JLOG(ctx_.journal.fatal()) << //
|
||||
"Exception while checking transaction invariants: " << //
|
||||
ex.what() << //
|
||||
", tx: " << //
|
||||
to_string(ctx_.tx.getJson(JsonOptions::Values::None));
|
||||
|
||||
return tecINVARIANT_FAILED;
|
||||
}
|
||||
|
||||
return result;
|
||||
return self_.finalizeInvariants(tx, result, fee, view, j);
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
Transactor::checkInvariants(TER result, XRPAmount fee)
|
||||
Transactor::checkInvariants(TER result, XRPAmount fee, SkipTxInvariants skip)
|
||||
{
|
||||
// Transaction invariants first (more specific). These check post-conditions of the specific
|
||||
// transaction. If these fail, the transaction's core logic is wrong.
|
||||
auto const txResult = checkTransactionInvariants(result, fee);
|
||||
if (skip == SkipTxInvariants::Yes)
|
||||
return xrpl::checkInvariants(ctx_, result, fee);
|
||||
|
||||
// Protocol invariants second (broader). These check properties that must hold regardless of
|
||||
// transaction type.
|
||||
auto const protoResult = ctx_.checkInvariants(result, fee);
|
||||
|
||||
// Fail if either check failed. tef (fatal) takes priority over tec.
|
||||
if (protoResult == tefINVARIANT_FAILED)
|
||||
return tefINVARIANT_FAILED;
|
||||
if (txResult == tecINVARIANT_FAILED || protoResult == tecINVARIANT_FAILED)
|
||||
return tecINVARIANT_FAILED;
|
||||
|
||||
return result;
|
||||
return xrpl::checkInvariants(ctx_, result, fee, std::ref(invariantCheck_));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
ApplyResult
|
||||
@@ -1364,7 +1329,7 @@ Transactor::operator()()
|
||||
{
|
||||
// Check invariants: if `tecINVARIANT_FAILED` is not returned, we can
|
||||
// proceed to apply the tx
|
||||
result = checkInvariants(result, fee);
|
||||
result = checkInvariants(result, fee, SkipTxInvariants::No);
|
||||
if (result == tecINVARIANT_FAILED)
|
||||
{
|
||||
// Reset to fee-claim only
|
||||
@@ -1375,11 +1340,11 @@ Transactor::operator()()
|
||||
fee = resetResult.second;
|
||||
|
||||
// Check invariants again to ensure the fee claiming doesn't violate
|
||||
// invariants. After reset, only protocol invariants are re-checked.
|
||||
// Transaction invariants are not meaningful here — the transaction's
|
||||
// effects have been rolled back.
|
||||
// invariants. After reset, only protocol invariants are re-checked;
|
||||
// the transaction's effects have been rolled back, so the
|
||||
// transaction-specific invariants are no longer meaningful.
|
||||
if (isTesSuccess(result) || isTecClaim(result))
|
||||
result = ctx_.checkInvariants(result, fee);
|
||||
result = checkInvariants(result, fee, SkipTxInvariants::Yes);
|
||||
}
|
||||
|
||||
// We ran through the invariant checker, which can, in some cases,
|
||||
|
||||
184
src/libxrpl/tx/invariants/CheckInvariants.cpp
Normal file
184
src/libxrpl/tx/invariants/CheckInvariants.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
#include <xrpl/tx/invariants/CheckInvariants.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/json/to_string.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
#include <xrpl/tx/invariants/InvariantCheck.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
namespace {
|
||||
|
||||
TER
|
||||
failInvariantCheck(TER const result)
|
||||
{
|
||||
return (result == tecINVARIANT_FAILED || result == tefINVARIANT_FAILED)
|
||||
? TER{tefINVARIANT_FAILED}
|
||||
: TER{tecINVARIANT_FAILED};
|
||||
}
|
||||
|
||||
template <std::size_t... Is>
|
||||
TER
|
||||
checkInvariantsHelper(
|
||||
ApplyContext& ctx,
|
||||
TER const result,
|
||||
XRPAmount const fee,
|
||||
std::optional<std::reference_wrapper<InvariantCheck>> txCheck,
|
||||
std::index_sequence<Is...>)
|
||||
{
|
||||
auto checkers = getInvariantChecks();
|
||||
|
||||
// Phase 1 — state collection.
|
||||
// One walk feeds both layers. Per-layer try-catch isolates faults: a throw
|
||||
// in txCheck stops only txCheck from visiting further entries; the protocol
|
||||
// fold keeps going (and vice-versa). A layer that threw skips finalize.
|
||||
bool txCollectionOk = true;
|
||||
bool protoCollectionOk = true;
|
||||
std::string txCollectionEx;
|
||||
std::string protoCollectionEx;
|
||||
|
||||
ctx.visit([&](uint256 const&, bool isDelete, SLE::const_ref before, SLE::const_ref after) {
|
||||
if (txCheck && txCollectionOk)
|
||||
{
|
||||
try
|
||||
{
|
||||
txCheck->get().visitEntry(isDelete, before, after);
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
txCollectionOk = false;
|
||||
txCollectionEx = ex.what();
|
||||
}
|
||||
}
|
||||
|
||||
if (protoCollectionOk)
|
||||
{
|
||||
try
|
||||
{
|
||||
(..., std::get<Is>(checkers).visitEntry(isDelete, before, after));
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
protoCollectionOk = false;
|
||||
protoCollectionEx = ex.what();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Phase 2 — evaluate invariant conditions.
|
||||
auto const txResult = [&]() -> TER {
|
||||
if (!txCheck)
|
||||
return result;
|
||||
|
||||
if (!txCollectionOk)
|
||||
{
|
||||
JLOG(ctx.journal.fatal())
|
||||
<< "Transaction caused an exception while collecting transaction invariant state"
|
||||
<< ", ex: " << txCollectionEx
|
||||
<< ", tx: " << to_string(ctx.tx.getJson(JsonOptions::Values::None));
|
||||
return tecINVARIANT_FAILED;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!txCheck->get().finalize(ctx.tx, result, fee, ctx.view(), ctx.journal))
|
||||
{
|
||||
JLOG(ctx.journal.fatal())
|
||||
<< "Transaction has failed one or more transaction invariants: "
|
||||
<< to_string(ctx.tx.getJson(JsonOptions::Values::None));
|
||||
return tecINVARIANT_FAILED;
|
||||
}
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
JLOG(ctx.journal.fatal())
|
||||
<< "Transaction caused an exception in a transaction invariant"
|
||||
<< ", ex: " << ex.what()
|
||||
<< ", tx: " << to_string(ctx.tx.getJson(JsonOptions::Values::None));
|
||||
return tecINVARIANT_FAILED;
|
||||
}
|
||||
|
||||
return result;
|
||||
}();
|
||||
|
||||
auto const protoResult = [&]() -> TER {
|
||||
if (!protoCollectionOk)
|
||||
{
|
||||
JLOG(ctx.journal.fatal())
|
||||
<< "Transaction caused an exception while collecting global invariant state"
|
||||
<< ", ex: " << protoCollectionEx
|
||||
<< ", tx: " << to_string(ctx.tx.getJson(JsonOptions::Values::None));
|
||||
return failInvariantCheck(result);
|
||||
}
|
||||
|
||||
bool protoOk = true;
|
||||
try
|
||||
{
|
||||
// Note: do not replace this logic with a `...&&` fold expression.
|
||||
// The fold expression will only run until the first check fails (it
|
||||
// short-circuits). While the logic is still correct, the log
|
||||
// message won't be. Every failed invariant should write to the log,
|
||||
// not just the first one.
|
||||
std::array<bool, sizeof...(Is)> const finalizers{{std::get<Is>(checkers).finalize(
|
||||
ctx.tx,
|
||||
result,
|
||||
fee,
|
||||
ctx.view(),
|
||||
ctx.journal)...}}; // NOLINT(bugprone-unchecked-optional-access)
|
||||
|
||||
protoOk = std::all_of(
|
||||
finalizers.cbegin(), finalizers.cend(), [](auto const& b) { return b; });
|
||||
if (!protoOk)
|
||||
{
|
||||
JLOG(ctx.journal.fatal())
|
||||
<< "Transaction has failed one or more global invariants: "
|
||||
<< to_string(ctx.tx.getJson(JsonOptions::Values::None));
|
||||
}
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
JLOG(ctx.journal.fatal())
|
||||
<< "Transaction caused an exception in a global invariant"
|
||||
<< ", ex: " << ex.what()
|
||||
<< ", tx: " << to_string(ctx.tx.getJson(JsonOptions::Values::None));
|
||||
protoOk = false;
|
||||
}
|
||||
|
||||
return protoOk ? result : failInvariantCheck(result);
|
||||
}();
|
||||
|
||||
// Fail if either check failed. tef (fatal) takes priority over tec.
|
||||
if (protoResult == tefINVARIANT_FAILED)
|
||||
return tefINVARIANT_FAILED;
|
||||
if (txResult == tecINVARIANT_FAILED || protoResult == tecINVARIANT_FAILED)
|
||||
return tecINVARIANT_FAILED;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TER
|
||||
checkInvariants(
|
||||
ApplyContext& ctx,
|
||||
TER const result,
|
||||
XRPAmount const fee,
|
||||
std::optional<std::reference_wrapper<InvariantCheck>> txCheck)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
isTesSuccess(result) || isTecClaim(result),
|
||||
"xrpl::checkInvariants : is tesSUCCESS or tecCLAIM");
|
||||
|
||||
return checkInvariantsHelper(
|
||||
ctx, result, fee, txCheck, std::make_index_sequence<std::tuple_size_v<InvariantChecks>>{});
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -427,7 +427,7 @@ AccountDelete::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -649,6 +649,7 @@ AccountSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
AccountSet::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -90,7 +90,7 @@ SetRegularKey::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -417,7 +417,7 @@ SignerListSet::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -2236,7 +2236,7 @@ XChainCreateBridge::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
@@ -2254,7 +2254,7 @@ BridgeModify::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
@@ -2268,6 +2268,7 @@ XChainClaim::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
XChainClaim::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
@@ -2285,7 +2286,7 @@ XChainCommit::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
@@ -2303,7 +2304,7 @@ XChainCreateClaimID::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
@@ -2321,7 +2322,7 @@ XChainAddClaimAttestation::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
@@ -2339,7 +2340,7 @@ XChainAddAccountCreateAttestation::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
@@ -2357,7 +2358,7 @@ XChainCreateAccountCommit::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -107,6 +107,7 @@ CheckCancel::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
CheckCancel::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -598,6 +598,7 @@ CheckCash::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
CheckCash::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -265,6 +265,7 @@ CheckCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
CheckCreate::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -135,7 +135,7 @@ CredentialAccept::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -190,7 +190,7 @@ CredentialCreate::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -106,7 +106,7 @@ CredentialDelete::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -181,6 +181,7 @@ DelegateSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
DelegateSet::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -385,6 +385,7 @@ AMMBid::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
AMMBid::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -389,6 +389,7 @@ AMMClawback::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
AMMClawback::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -396,6 +396,7 @@ AMMCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
AMMCreate::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -72,6 +72,7 @@ AMMDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
AMMDelete::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -1020,6 +1020,7 @@ AMMDeposit::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
AMMDeposit::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -255,6 +255,7 @@ AMMVote::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
AMMVote::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -1143,6 +1143,7 @@ AMMWithdraw::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
AMMWithdraw::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -75,6 +75,7 @@ OfferCancel::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
OfferCancel::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -997,6 +997,7 @@ OfferCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
OfferCreate::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -72,6 +72,7 @@ DIDDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
DIDDelete::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -157,6 +157,7 @@ DIDSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
DIDSet::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -230,7 +230,7 @@ EscrowCancel::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -552,7 +552,7 @@ EscrowCreate::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -411,7 +411,7 @@ EscrowFinish::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -387,7 +387,7 @@ LoanBrokerCoverClawback::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -190,7 +190,7 @@ LoanBrokerCoverDeposit::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user