diff --git a/include/xrpl/tx/invariants/AMMInvariant.h b/include/xrpl/tx/invariants/AMMInvariant.h index ee2fb66a1c..b0c35a3070 100644 --- a/include/xrpl/tx/invariants/AMMInvariant.h +++ b/include/xrpl/tx/invariants/AMMInvariant.h @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -20,6 +21,9 @@ class ValidAMM public: enum class ZeroAllowed : bool { No = false, Yes = true }; + static constexpr auto kRelevantLedgerEntryTypes = + VisitLedgerEntryTypes{}; + ValidAMM() = default; void visitEntry(bool, SLE::const_ref, SLE::const_ref); diff --git a/include/xrpl/tx/invariants/DirectoryInvariant.h b/include/xrpl/tx/invariants/DirectoryInvariant.h index 96643ea465..15def95d88 100644 --- a/include/xrpl/tx/invariants/DirectoryInvariant.h +++ b/include/xrpl/tx/invariants/DirectoryInvariant.h @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -17,6 +18,8 @@ class ValidBookDirectory hash_set rootIndexes_; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitLedgerEntryTypes{}; + void visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); diff --git a/include/xrpl/tx/invariants/FreezeInvariant.h b/include/xrpl/tx/invariants/FreezeInvariant.h index a76eb66497..5afab12fff 100644 --- a/include/xrpl/tx/invariants/FreezeInvariant.h +++ b/include/xrpl/tx/invariants/FreezeInvariant.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,9 @@ class TransfersNotFrozen std::map possibleIssuers_; public: + static constexpr auto kRelevantLedgerEntryTypes = + VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); diff --git a/include/xrpl/tx/invariants/InvariantCheck.h b/include/xrpl/tx/invariants/InvariantCheck.h index 9378062726..ce1246ab28 100644 --- a/include/xrpl/tx/invariants/InvariantCheck.h +++ b/include/xrpl/tx/invariants/InvariantCheck.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -107,6 +108,8 @@ public: class TransactionFeeCheck { public: + static constexpr auto kRelevantLedgerEntryTypes = VisitNoLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -127,6 +130,9 @@ class XRPNotCreated std::int64_t drops_ = 0; public: + static constexpr auto kRelevantLedgerEntryTypes = + VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -147,6 +153,8 @@ class AccountRootsNotDeleted std::uint32_t accountsDeleted_ = 0; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -174,6 +182,8 @@ class AccountRootsDeletedClean std::vector> accountsDeleted_; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -193,6 +203,8 @@ class XRPBalanceChecks bool bad_ = false; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -210,6 +222,8 @@ class LedgerEntryTypesMatch bool invalidTypeAdded_ = false; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitAllLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -228,6 +242,8 @@ class NoXRPTrustLines bool xrpTrustLine_ = false; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -247,6 +263,8 @@ class NoDeepFreezeTrustLinesWithoutFreeze bool deepFreezeWithoutFreeze_ = false; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -266,6 +284,8 @@ class NoBadOffers bool bad_ = false; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -282,6 +302,9 @@ class NoZeroEscrow bool bad_ = false; public: + static constexpr auto kRelevantLedgerEntryTypes = + VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -302,6 +325,8 @@ class ValidNewAccountRoot std::uint32_t flags_ = 0; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -323,6 +348,9 @@ class ValidClawback std::uint32_t mptokensChanged_ = 0; public: + static constexpr auto kRelevantLedgerEntryTypes = + VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -343,6 +371,8 @@ class ValidPseudoAccounts std::vector errors_; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -363,6 +393,8 @@ class NoModifiedUnmodifiableFields std::set> changedEntries_; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitAllLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -378,6 +410,8 @@ class ValidAmounts std::vector> afterEntries_; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitAllLedgerEntryTypes{}; + void visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); diff --git a/include/xrpl/tx/invariants/InvariantEntryTypes.h b/include/xrpl/tx/invariants/InvariantEntryTypes.h new file mode 100644 index 0000000000..c08b269ff7 --- /dev/null +++ b/include/xrpl/tx/invariants/InvariantEntryTypes.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace xrpl { + +struct VisitAllLedgerEntryTypes +{ + static constexpr bool visitsAll = true; + static constexpr bool visitsNone = false; + static constexpr bool empty = false; + + template + static consteval bool + contains() + { + return true; + } +}; + +struct VisitNoLedgerEntryTypes +{ + static constexpr bool visitsAll = false; + static constexpr bool visitsNone = true; + static constexpr bool empty = true; + + template + static consteval bool + contains() + { + return false; + } +}; + +template +struct VisitLedgerEntryTypes +{ + static constexpr bool visitsAll = false; + static constexpr bool visitsNone = false; + static constexpr bool empty = sizeof...(Types) == 0; + + template + static consteval bool + contains() + { + return ((Type == Types) || ...); + } +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/invariants/LoanBrokerInvariant.h b/include/xrpl/tx/invariants/LoanBrokerInvariant.h index 684bbff423..e57fc23568 100644 --- a/include/xrpl/tx/invariants/LoanBrokerInvariant.h +++ b/include/xrpl/tx/invariants/LoanBrokerInvariant.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,9 @@ class ValidLoanBroker goodZeroDirectory(ReadView const& view, SLE::const_ref dir, beast::Journal const& j); public: + static constexpr auto kRelevantLedgerEntryTypes = + VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); diff --git a/include/xrpl/tx/invariants/LoanInvariant.h b/include/xrpl/tx/invariants/LoanInvariant.h index 3f408d169a..6f0af5eb1d 100644 --- a/include/xrpl/tx/invariants/LoanInvariant.h +++ b/include/xrpl/tx/invariants/LoanInvariant.h @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -22,6 +23,8 @@ class ValidLoan std::vector> loans_; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); diff --git a/include/xrpl/tx/invariants/MPTInvariant.h b/include/xrpl/tx/invariants/MPTInvariant.h index b4b76a290f..89e3293e23 100644 --- a/include/xrpl/tx/invariants/MPTInvariant.h +++ b/include/xrpl/tx/invariants/MPTInvariant.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -36,6 +37,9 @@ class ValidMPTIssuance std::vector> deletedHoldings_; public: + static constexpr auto kRelevantLedgerEntryTypes = + VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -64,6 +68,9 @@ class ValidMPTPayment hash_map data_; public: + static constexpr auto kRelevantLedgerEntryTypes = + VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -85,6 +92,8 @@ class ValidMPTTransfer hash_map deletedAuthorized_; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitLedgerEntryTypes{}; + void visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); diff --git a/include/xrpl/tx/invariants/NFTInvariant.h b/include/xrpl/tx/invariants/NFTInvariant.h index 698df05247..9b746e6bf6 100644 --- a/include/xrpl/tx/invariants/NFTInvariant.h +++ b/include/xrpl/tx/invariants/NFTInvariant.h @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -32,6 +33,8 @@ class ValidNFTokenPage bool deletedLink_ = false; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); @@ -60,6 +63,8 @@ class NFTokenCountTracking std::uint32_t afterBurnedTotal_ = 0; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); diff --git a/include/xrpl/tx/invariants/PermissionedDEXInvariant.h b/include/xrpl/tx/invariants/PermissionedDEXInvariant.h index 2ec22ded88..ec279cc8b5 100644 --- a/include/xrpl/tx/invariants/PermissionedDEXInvariant.h +++ b/include/xrpl/tx/invariants/PermissionedDEXInvariant.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace xrpl { @@ -17,6 +18,8 @@ class ValidPermissionedDEX hash_set domains_; public: + static constexpr auto kRelevantLedgerEntryTypes = VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); diff --git a/include/xrpl/tx/invariants/PermissionedDomainInvariant.h b/include/xrpl/tx/invariants/PermissionedDomainInvariant.h index 19edcc0b39..84a443d4ab 100644 --- a/include/xrpl/tx/invariants/PermissionedDomainInvariant.h +++ b/include/xrpl/tx/invariants/PermissionedDomainInvariant.h @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -31,6 +32,9 @@ class ValidPermissionedDomain std::vector sleStatus_; public: + static constexpr auto kRelevantLedgerEntryTypes = + VisitLedgerEntryTypes{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); diff --git a/include/xrpl/tx/invariants/VaultInvariant.h b/include/xrpl/tx/invariants/VaultInvariant.h index 2a9ffc8282..370722cd49 100644 --- a/include/xrpl/tx/invariants/VaultInvariant.h +++ b/include/xrpl/tx/invariants/VaultInvariant.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -153,6 +154,13 @@ public: [[nodiscard]] static std::int32_t computeCoarsestScale(std::vector const& numbers); + static constexpr auto kRelevantLedgerEntryTypes = VisitLedgerEntryTypes< + ltVAULT, + ltMPTOKEN_ISSUANCE, + ltMPTOKEN, + ltACCOUNT_ROOT, + ltRIPPLE_STATE>{}; + void visitEntry(bool, SLE::const_ref, SLE::const_ref); diff --git a/src/libxrpl/tx/ApplyContext.cpp b/src/libxrpl/tx/ApplyContext.cpp index 9047587300..b634453314 100644 --- a/src/libxrpl/tx/ApplyContext.cpp +++ b/src/libxrpl/tx/ApplyContext.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -38,79 +39,68 @@ namespace xrpl { namespace { -template -struct InvariantCheckTypes -{ -}; +template +using RelevantLedgerEntryTypes = std::remove_cvref_t; -using RoutedInvariantCheckTypes = InvariantCheckTypes< - TransactionFeeCheck, - AccountRootsNotDeleted, - AccountRootsDeletedClean, - LedgerEntryTypesMatch, - XRPBalanceChecks, - XRPNotCreated, - NoXRPTrustLines, - NoDeepFreezeTrustLinesWithoutFreeze, - TransfersNotFrozen, - NoBadOffers, - NoZeroEscrow, - ValidNewAccountRoot, - ValidNFTokenPage, - NFTokenCountTracking, - ValidClawback, - ValidMPTIssuance, - ValidPermissionedDomain, - ValidPermissionedDEX, - ValidBookDirectory, - ValidAMM, - NoModifiedUnmodifiableFields, - ValidPseudoAccounts, - ValidLoanBroker, - ValidLoan, - ValidVault, - ValidMPTPayment, - ValidAmounts, - ValidMPTTransfer>; +template +concept HasRelevantLedgerEntryTypes = requires { Checker::kRelevantLedgerEntryTypes; }; -template -consteval std::size_t -countInvariantCheck(InvariantCheckTypes) +template +consteval bool +hasInvariantVisitRoute() { - return (std::size_t{0} + ... + (std::is_same_v ? 1 : 0)); + if constexpr (HasRelevantLedgerEntryTypes) + { + using Types = RelevantLedgerEntryTypes; + return Types::visitsAll || Types::visitsNone || !Types::empty; + } + return false; } template consteval bool -allInvariantChecksAreRouted(std::index_sequence) +allInvariantChecksHaveVisitRoutes(std::index_sequence) { - return ( - (countInvariantCheck>( - RoutedInvariantCheckTypes{}) == 1) && - ...); + return (hasInvariantVisitRoute>() && ...); } static_assert( - allInvariantChecksAreRouted(std::make_index_sequence>{}), - "Every invariant check must be routed exactly once."); + allInvariantChecksHaveVisitRoutes( + std::make_index_sequence>{}), + "Every invariant check must declare ledger-entry visit routing."); -template +template void -visitInvariantChecks( +visitAllEntryInvariantCheck( InvariantChecks& checkers, bool isDelete, SLE::const_ref before, SLE::const_ref after) { - (..., std::get(checkers).visitEntry(isDelete, before, after)); + if constexpr (RelevantLedgerEntryTypes::visitsAll) + std::get(checkers).visitEntry(isDelete, before, after); +} + +template +void +visitAllEntryInvariantChecks( + InvariantChecks& checkers, + bool isDelete, + SLE::const_ref before, + SLE::const_ref after, + std::index_sequence) +{ + (..., + visitAllEntryInvariantCheck>( + checkers, isDelete, before, after)); } [[nodiscard]] std::optional -entryTypeForSpecificInvariants(SLE::const_ref before, SLE::const_ref after) +entryTypeForMappedInvariants(SLE::const_ref before, SLE::const_ref after) { if (before && after && before->getType() != after->getType()) { - // LedgerEntryTypesMatch runs as a broad invariant and reports this. + // LedgerEntryTypesMatch runs as an all-entry invariant and reports this. return std::nullopt; } @@ -121,102 +111,68 @@ entryTypeForSpecificInvariants(SLE::const_ref before, SLE::const_ref after) return std::nullopt; } +template void -visitTypeSpecificInvariantChecks( +visitLedgerTypeInvariantCheck( InvariantChecks& checkers, - LedgerEntryType type, bool isDelete, SLE::const_ref before, SLE::const_ref after) { - switch (type) + using Types = RelevantLedgerEntryTypes; + if constexpr (!Types::visitsAll && !Types::visitsNone && Types::template contains()) { - case ltACCOUNT_ROOT: - visitInvariantChecks< - AccountRootsNotDeleted, - AccountRootsDeletedClean, - XRPBalanceChecks, - TransfersNotFrozen, - ValidNewAccountRoot, - NFTokenCountTracking, - ValidAMM, - ValidPseudoAccounts, - ValidLoanBroker, - ValidVault>(checkers, isDelete, before, after); - break; - case ltRIPPLE_STATE: - visitInvariantChecks< - NoXRPTrustLines, - NoDeepFreezeTrustLinesWithoutFreeze, - TransfersNotFrozen, - ValidMPTIssuance, - ValidAMM, - ValidLoanBroker, - ValidVault>(checkers, isDelete, before, after); - break; - case ltOFFER: - visitInvariantChecks( - checkers, isDelete, before, after); - break; - case ltESCROW: - visitInvariantChecks(checkers, isDelete, before, after); - break; - case ltNFTOKEN_PAGE: - visitInvariantChecks(checkers, isDelete, before, after); - break; - case ltMPTOKEN_ISSUANCE: - visitInvariantChecks( - checkers, isDelete, before, after); - break; - case ltMPTOKEN: - visitInvariantChecks< - NoZeroEscrow, - ValidClawback, - ValidMPTIssuance, - ValidAMM, - ValidLoanBroker, - ValidVault, - ValidMPTPayment, - ValidMPTTransfer>(checkers, isDelete, before, after); - break; - case ltPERMISSIONED_DOMAIN: - visitInvariantChecks(checkers, isDelete, before, after); - break; - case ltDIR_NODE: - visitInvariantChecks( - checkers, isDelete, before, after); - break; - case ltAMM: - visitInvariantChecks(checkers, isDelete, before, after); - break; - case ltLOAN_BROKER: - visitInvariantChecks(checkers, isDelete, before, after); - break; - case ltLOAN: - visitInvariantChecks(checkers, isDelete, before, after); - break; - case ltVAULT: - visitInvariantChecks(checkers, isDelete, before, after); - break; - default: - break; + std::get(checkers).visitEntry(isDelete, before, after); } } +template void -visitBroadInvariantChecks( +visitLedgerTypeInvariantChecks( + InvariantChecks& checkers, + bool isDelete, + SLE::const_ref before, + SLE::const_ref after, + std::index_sequence) +{ + (..., + visitLedgerTypeInvariantCheck>( + checkers, isDelete, before, after)); +} + +using LedgerTypeInvariantVisitor = void (*)(InvariantChecks&, bool, SLE::const_ref, SLE::const_ref); + +template +void +visitMappedLedgerTypeInvariantChecks( InvariantChecks& checkers, bool isDelete, SLE::const_ref before, SLE::const_ref after) { - visitInvariantChecks< - LedgerEntryTypesMatch, - XRPNotCreated, - NoModifiedUnmodifiableFields, - ValidAmounts>(checkers, isDelete, before, after); + visitLedgerTypeInvariantChecks( + checkers, + isDelete, + before, + after, + std::make_index_sequence>{}); } +#pragma push_macro("LEDGER_ENTRY") +#undef LEDGER_ENTRY + +#define LEDGER_ENTRY(tag, ...) \ + std::pair{ \ + tag, &visitMappedLedgerTypeInvariantChecks}, + +static constexpr auto kLedgerTypeInvariantVisitors = + std::to_array>({ +#include + }); + +#undef LEDGER_ENTRY +#pragma pop_macro("LEDGER_ENTRY") + } // namespace ApplyContext::ApplyContext( @@ -296,10 +252,22 @@ ApplyContext::checkInvariantsHelper( // call each check's per-entry method visit([&checkers]( uint256 const&, bool isDelete, SLE::const_ref before, SLE::const_ref after) { - visitBroadInvariantChecks(checkers, isDelete, before, after); + visitAllEntryInvariantChecks( + checkers, + isDelete, + before, + after, + std::make_index_sequence>{}); - if (auto const type = entryTypeForSpecificInvariants(before, after)) - visitTypeSpecificInvariantChecks(checkers, *type, isDelete, before, after); + if (auto const type = entryTypeForMappedInvariants(before, after)) + { + auto const iter = std::find_if( + kLedgerTypeInvariantVisitors.cbegin(), + kLedgerTypeInvariantVisitors.cend(), + [type](auto const& visitor) { return visitor.first == *type; }); + if (iter != kLedgerTypeInvariantVisitors.cend()) + iter->second(checkers, isDelete, before, after); + } }); // Note: do not replace this logic with a `...&&` fold expression.