mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-22 06:40:53 +00:00
refactor: Extract invariant invocation into free checkInvariants runner (#7404)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/OpenView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
@@ -56,6 +57,7 @@
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
#include <xrpl/tx/invariants/AMMInvariant.h>
|
||||
#include <xrpl/tx/invariants/DirectoryInvariant.h>
|
||||
#include <xrpl/tx/invariants/InvariantRunner.h>
|
||||
#include <xrpl/tx/invariants/PermissionedDEXInvariant.h>
|
||||
#include <xrpl/tx/invariants/VaultInvariant.h>
|
||||
|
||||
@@ -68,6 +70,7 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <source_location>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
@@ -217,7 +220,8 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
TER terActual = tesSUCCESS;
|
||||
for (TER const& terExpect : ters)
|
||||
{
|
||||
terActual = transactor->checkInvariants(terActual, fee);
|
||||
terActual =
|
||||
transactor->checkInvariants(terActual, fee, Transactor::InvariantScope::Full);
|
||||
expect(
|
||||
terExpect == terActual,
|
||||
"expected: " + transToken(terExpect) + " got: " + transToken(terActual),
|
||||
@@ -6379,12 +6383,137 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
auto transactor = makeTransactor(ac);
|
||||
if (!BEAST_EXPECT(transactor))
|
||||
return;
|
||||
TER const result = transactor->checkInvariants(tesSUCCESS, XRPAmount{});
|
||||
TER const result = transactor->checkInvariants(
|
||||
tesSUCCESS, XRPAmount{}, Transactor::InvariantScope::Full);
|
||||
BEAST_EXPECT(result == tecINVARIANT_FAILED);
|
||||
BEAST_EXPECT(sink.messages().str().contains("is missing pseudo-account field"));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testTxCheckException()
|
||||
{
|
||||
testcase << "txCheck exception";
|
||||
using namespace jtx;
|
||||
|
||||
// A TxInvariantCheck that throws from the requested hook, so we can
|
||||
// exercise checkInvariantsHelper's catch block via the
|
||||
// transaction-specific layer (as opposed to the protocol layer,
|
||||
// which testObjectHasPseudoAccount's last case already covers via a
|
||||
// real Transactor's finalizeInvariants).
|
||||
enum class ThrowFrom { VisitEntry, Finalize };
|
||||
|
||||
struct ThrowingTxInvariantCheck : TxInvariantCheck
|
||||
{
|
||||
ThrowFrom const throwFrom;
|
||||
|
||||
explicit ThrowingTxInvariantCheck(ThrowFrom throwFrom) : throwFrom(throwFrom)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
visitEntry(bool, SLE::const_ref, SLE::const_ref) override
|
||||
{
|
||||
if (throwFrom == ThrowFrom::VisitEntry)
|
||||
throw std::runtime_error("test-injected visitEntry exception");
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
finalize(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&) override
|
||||
{
|
||||
if (throwFrom == ThrowFrom::Finalize)
|
||||
throw std::runtime_error("test-injected finalize exception");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
for (auto const throwFrom : {ThrowFrom::VisitEntry, ThrowFrom::Finalize})
|
||||
{
|
||||
Env env{*this};
|
||||
Account const alice{"alice"};
|
||||
env.fund(XRP(1000), alice);
|
||||
env.close();
|
||||
|
||||
OpenView ov{*env.current()};
|
||||
STTx const tx{ttACCOUNT_SET, [](STObject&) {}};
|
||||
test::StreamSink sink{beast::Severity::Warning};
|
||||
beast::Journal const jlog{sink};
|
||||
ApplyContext ac{
|
||||
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
|
||||
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
|
||||
|
||||
// visitEntry only runs for entries the transaction touched, so
|
||||
// make a modification for the traversal to report.
|
||||
auto sle = ac.view().peek(keylet::account(alice.id()));
|
||||
if (!BEAST_EXPECT(sle))
|
||||
return;
|
||||
sle->at(sfSequence) = sle->at(sfSequence) + 1;
|
||||
ac.view().update(sle);
|
||||
|
||||
ThrowingTxInvariantCheck throwing{throwFrom};
|
||||
TER terActual = tesSUCCESS;
|
||||
for (TER const& terExpect : {TER(tecINVARIANT_FAILED), TER(tefINVARIANT_FAILED)})
|
||||
{
|
||||
terActual = checkInvariants(ac, terActual, XRPAmount{}, throwing);
|
||||
BEAST_EXPECT(terExpect == terActual);
|
||||
BEAST_EXPECT(sink.messages().str().contains(
|
||||
"Transaction caused an exception during invariant checks"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testTxCheckFinalizeFalse()
|
||||
{
|
||||
testcase << "txCheck finalize returns false";
|
||||
using namespace jtx;
|
||||
|
||||
// A TxInvariantCheck whose finalize returns false, so we can exercise
|
||||
// the "Transaction has failed one or more transaction invariants"
|
||||
// log path in checkInvariantsHelper independently of any real
|
||||
// transactor. This is the transaction-layer analogue of the
|
||||
// protocol-layer coverage in testObjectHasPseudoAccount / others.
|
||||
struct FailingTxInvariantCheck : TxInvariantCheck
|
||||
{
|
||||
void
|
||||
visitEntry(bool, SLE::const_ref, SLE::const_ref) override
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
finalize(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Env env{*this};
|
||||
Account const alice{"alice"};
|
||||
env.fund(XRP(1000), alice);
|
||||
env.close();
|
||||
|
||||
OpenView ov{*env.current()};
|
||||
STTx const tx{ttACCOUNT_SET, [](STObject&) {}};
|
||||
test::StreamSink sink{beast::Severity::Warning};
|
||||
beast::Journal const jlog{sink};
|
||||
ApplyContext ac{env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
|
||||
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
|
||||
|
||||
FailingTxInvariantCheck failing;
|
||||
TER terActual = tesSUCCESS;
|
||||
for (TER const& terExpect : {TER(tecINVARIANT_FAILED), TER(tefINVARIANT_FAILED)})
|
||||
{
|
||||
terActual = checkInvariants(ac, terActual, XRPAmount{}, failing);
|
||||
BEAST_EXPECT(terExpect == terActual);
|
||||
BEAST_EXPECT(sink.messages().str().contains(
|
||||
"Transaction has failed one or more transaction invariants"));
|
||||
// The protocol-layer log must not appear: only the tx-layer
|
||||
// finalize failed here.
|
||||
BEAST_EXPECT(!sink.messages().str().contains(
|
||||
"Transaction has failed one or more global invariants"));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testConfidentialMPTTransfer()
|
||||
{
|
||||
@@ -6670,6 +6799,8 @@ public:
|
||||
testAMM();
|
||||
testObjectHasPseudoAccount();
|
||||
testSponsorship();
|
||||
testTxCheckException();
|
||||
testTxCheckFinalizeFalse();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/protocol/nft.h>
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
#include <xrpl/tx/invariants/InvariantRunner.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
@@ -794,7 +795,7 @@ class NFTokenBurn_test : public beast::unit_test::Suite
|
||||
TER terActual = tesSUCCESS;
|
||||
for (TER const& terExpect : {TER(tecINVARIANT_FAILED), TER(tefINVARIANT_FAILED)})
|
||||
{
|
||||
terActual = ac.checkInvariants(terActual, XRPAmount{});
|
||||
terActual = xrpl::checkInvariants(ac, terActual, XRPAmount{});
|
||||
BEAST_EXPECT(terExpect == terActual);
|
||||
BEAST_EXPECT(sink.messages().str().starts_with("Invariant failed:"));
|
||||
// uncomment to log the invariant failure message
|
||||
@@ -830,7 +831,7 @@ class NFTokenBurn_test : public beast::unit_test::Suite
|
||||
TER terActual = tesSUCCESS;
|
||||
for (TER const& terExpect : {TER(tecINVARIANT_FAILED), TER(tefINVARIANT_FAILED)})
|
||||
{
|
||||
terActual = ac.checkInvariants(terActual, XRPAmount{});
|
||||
terActual = xrpl::checkInvariants(ac, terActual, XRPAmount{});
|
||||
BEAST_EXPECT(terExpect == terActual);
|
||||
BEAST_EXPECT(sink.messages().str().starts_with("Invariant failed:"));
|
||||
// uncomment to log the invariant failure message
|
||||
|
||||
Reference in New Issue
Block a user