diff --git a/API-CHANGELOG.md b/API-CHANGELOG.md index c7a31d27fa4..2e3d1903957 100644 --- a/API-CHANGELOG.md +++ b/API-CHANGELOG.md @@ -22,6 +22,19 @@ API version 2 is available in `rippled` version 2.0.0 and later. See [API-VERSIO This version is supported by all `rippled` versions. For WebSocket and HTTP JSON-RPC requests, it is currently the default API version used when no `api_version` is specified. +## XRP Ledger server version 3.2.0 + +[Version 3.2.0](https://github.com/XRPLF/rippled/releases/tag/3.2.0) + +### Additions in 3.2.0 + +- `server_definitions`: Added the following new sections to the response ([#6321](https://github.com/XRPLF/rippled/pull/6321)): + - `TRANSACTION_FORMATS`: Describes the fields and their optionality for each transaction type, including common fields shared across all transactions. + - `LEDGER_ENTRY_FORMATS`: Describes the fields and their optionality for each ledger entry type, including common fields shared across all ledger entries. + - `TRANSACTION_FLAGS`: Maps transaction type names to their supported flags and flag values. + - `LEDGER_ENTRY_FLAGS`: Maps ledger entry type names to their flags and flag values. + - `ACCOUNT_SET_FLAGS`: Maps AccountSet flag names (asf flags) to their numeric values. + ## XRP Ledger server version 3.1.0 [Version 3.1.0](https://github.com/XRPLF/rippled/releases/tag/3.1.0) was released on Jan 27, 2026. diff --git a/include/xrpl/protocol/KnownFormats.h b/include/xrpl/protocol/KnownFormats.h index ce14a210ab0..c454683e191 100644 --- a/include/xrpl/protocol/KnownFormats.h +++ b/include/xrpl/protocol/KnownFormats.h @@ -30,9 +30,11 @@ class KnownFormats Item( char const* name, KeyType type, - std::initializer_list uniqueFields, - std::initializer_list commonFields) - : soTemplate_(uniqueFields, commonFields), name_(name), type_(type) + std::vector uniqueFields, + std::vector commonFields) + : soTemplate_(std::move(uniqueFields), std::move(commonFields)) + , name_(name) + , type_(type) { // Verify that KeyType is appropriate. static_assert( @@ -142,16 +144,16 @@ class KnownFormats @param name The name of this format. @param type The type of this format. - @param uniqueFields An std::initializer_list of unique fields - @param commonFields An std::initializer_list of common fields + @param uniqueFields A std::vector of unique fields + @param commonFields A std::vector of common fields @return The created format. */ Item const& add(char const* name, KeyType type, - std::initializer_list uniqueFields, - std::initializer_list commonFields = {}) + std::vector uniqueFields, + std::vector commonFields = {}) { if (auto const item = findByType(type)) { @@ -160,7 +162,7 @@ class KnownFormats item->getName()); } - formats_.emplace_front(name, type, uniqueFields, commonFields); + formats_.emplace_front(name, type, std::move(uniqueFields), std::move(commonFields)); Item const& item{formats_.front()}; names_[name] = &item; diff --git a/include/xrpl/protocol/LedgerFormats.h b/include/xrpl/protocol/LedgerFormats.h index fa39c158438..a43f6a71340 100644 --- a/include/xrpl/protocol/LedgerFormats.h +++ b/include/xrpl/protocol/LedgerFormats.h @@ -2,36 +2,34 @@ #include -namespace xrpl { +#include +#include +#include +namespace xrpl { /** Identifiers for on-ledger objects. - Each ledger object requires a unique type identifier, which is stored - within the object itself; this makes it possible to iterate the entire - ledger and determine each object's type and verify that the object you - retrieved from a given hash matches the expected type. + Each ledger object requires a unique type identifier, which is stored within the object itself; + this makes it possible to iterate the entire ledger and determine each object's type and verify + that the object you retrieved from a given hash matches the expected type. - @warning Since these values are stored inside objects stored on the ledger - they are part of the protocol. **Changing them should be avoided - because without special handling, this will result in a hard + @warning Since these values are stored inside objects stored on the ledger they are part of the + protocol. + **Changing them should be avoided because without special handling, this will result in a hard fork.** - @note Values outside this range may be used internally by the code for - various purposes, but attempting to use such values to identify - on-ledger objects will results in an invariant failure. + @note Values outside this range may be used internally by the code for various purposes, but + attempting to use such values to identify on-ledger objects will result in an invariant failure. - @note When retiring types, the specific values should not be removed but - should be marked as [[deprecated]]. This is to avoid accidental - reuse of identifiers. + @note When retiring types, the specific values should not be removed but should be marked as + [[deprecated]]. This is to avoid accidental reuse of identifiers. - @todo The C++ language does not enable checking for duplicate values - here. If it becomes possible then we should do this. + @todo The C++ language does not enable checking for duplicate values here. + If it becomes possible then we should do this. @ingroup protocol */ -// clang-format off -enum LedgerEntryType : std::uint16_t -{ +enum LedgerEntryType : std::uint16_t { #pragma push_macro("LEDGER_ENTRY") #undef LEDGER_ENTRY @@ -46,12 +44,10 @@ enum LedgerEntryType : std::uint16_t //--------------------------------------------------------------------------- /** A special type, matching any ledger entry type. - The value does not represent a concrete type, but rather is used in - contexts where the specific type of a ledger object is unimportant, - unknown or unavailable. + The value does not represent a concrete type, but rather is used in contexts where the + specific type of a ledger object is unimportant, unknown or unavailable. - Objects with this special type cannot be created or stored on the - ledger. + Objects with this special type cannot be created or stored on the ledger. \sa keylet::unchecked */ @@ -59,12 +55,11 @@ enum LedgerEntryType : std::uint16_t /** A special type, matching any ledger type except directory nodes. - The value does not represent a concrete type, but rather is used in - contexts where the ledger object must not be a directory node but - its specific type is otherwise unimportant, unknown or unavailable. + The value does not represent a concrete type, but rather is used in contexts where the + ledger object must not be a directory node but its specific type is otherwise unimportant, + unknown or unavailable. - Objects with this special type cannot be created or stored on the - ledger. + Objects with this special type cannot be created or stored on the ledger. \sa keylet::child */ @@ -93,104 +88,188 @@ enum LedgerEntryType : std::uint16_t Support for this type of object was never implemented. No objects of this type were ever created. */ - ltGENERATOR_MAP [[deprecated("This object type is not supported and should not be used.")]] = 0x0067, + ltGENERATOR_MAP [[deprecated("This object type is not supported and should not be used.")]] = + 0x0067, }; -// clang-format off -/** +/** Ledger object flags. + + These flags are specified in ledger objects and modify their behavior. + + @warning Ledger object flags form part of the protocol. + **Changing them should be avoided because without special handling, this will result in a hard + fork.** + @ingroup protocol */ -enum LedgerSpecificFlags { - // ltACCOUNT_ROOT - lsfPasswordSpent = 0x00010000, // True, if password set fee is spent. - lsfRequireDestTag = - 0x00020000, // True, to require a DestinationTag for payments. - lsfRequireAuth = - 0x00040000, // True, to require a authorization to hold IOUs. - lsfDisallowXRP = 0x00080000, // True, to disallow sending XRP. - lsfDisableMaster = 0x00100000, // True, force regular key - lsfNoFreeze = 0x00200000, // True, cannot freeze ripple states - lsfGlobalFreeze = 0x00400000, // True, all assets frozen - lsfDefaultRipple = - 0x00800000, // True, incoming trust lines allow rippling by default - lsfDepositAuth = 0x01000000, // True, all deposits require authorization -/* // reserved for Hooks amendment - lsfTshCollect = 0x02000000, // True, allow TSH collect-calls to acc hooks -*/ - lsfDisallowIncomingNFTokenOffer = - 0x04000000, // True, reject new incoming NFT offers - lsfDisallowIncomingCheck = - 0x08000000, // True, reject new checks - lsfDisallowIncomingPayChan = - 0x10000000, // True, reject new paychans - lsfDisallowIncomingTrustline = - 0x20000000, // True, reject new trustlines (only if no issued assets) - lsfAllowTrustLineLocking = - 0x40000000, // True, enable trustline locking - lsfAllowTrustLineClawback = - 0x80000000, // True, enable clawback - - // ltOFFER - lsfPassive = 0x00010000, - lsfSell = 0x00020000, // True, offer was placed as a sell. - lsfHybrid = 0x00040000, // True, offer is hybrid. - - // ltRIPPLE_STATE - lsfLowReserve = 0x00010000, // True, if entry counts toward reserve. - lsfHighReserve = 0x00020000, - lsfLowAuth = 0x00040000, - lsfHighAuth = 0x00080000, - lsfLowNoRipple = 0x00100000, - lsfHighNoRipple = 0x00200000, - lsfLowFreeze = 0x00400000, // True, low side has set freeze flag - lsfHighFreeze = 0x00800000, // True, high side has set freeze flag - lsfLowDeepFreeze = 0x02000000, // True, low side has set deep freeze flag - lsfHighDeepFreeze = 0x04000000, // True, high side has set deep freeze flag - lsfAMMNode = 0x01000000, // True, trust line to AMM. Used by client - // apps to identify payments via AMM. - - // ltSIGNER_LIST - lsfOneOwnerCount = 0x00010000, // True, uses only one OwnerCount - - // ltDIR_NODE - lsfNFTokenBuyOffers = 0x00000001, - lsfNFTokenSellOffers = 0x00000002, - - // ltNFTOKEN_OFFER - lsfSellNFToken = 0x00000001, - - // ltMPTOKEN_ISSUANCE - lsfMPTLocked = 0x00000001, // Also used in ltMPTOKEN - lsfMPTCanLock = 0x00000002, - lsfMPTRequireAuth = 0x00000004, - lsfMPTCanEscrow = 0x00000008, - lsfMPTCanTrade = 0x00000010, - lsfMPTCanTransfer = 0x00000020, - lsfMPTCanClawback = 0x00000040, - - lsmfMPTCanMutateCanLock = 0x00000002, - lsmfMPTCanMutateRequireAuth = 0x00000004, - lsmfMPTCanMutateCanEscrow = 0x00000008, - lsmfMPTCanMutateCanTrade = 0x00000010, - lsmfMPTCanMutateCanTransfer = 0x00000020, - lsmfMPTCanMutateCanClawback = 0x00000040, - lsmfMPTCanMutateMetadata = 0x00010000, - lsmfMPTCanMutateTransferFee = 0x00020000, - - // ltMPTOKEN - lsfMPTAuthorized = 0x00000002, - - // ltCREDENTIAL - lsfAccepted = 0x00010000, - - // ltVAULT - lsfVaultPrivate = 0x00010000, - - // ltLOAN - lsfLoanDefault = 0x00010000, - lsfLoanImpaired = 0x00020000, - lsfLoanOverpayment = 0x00040000, // True, loan allows overpayments -}; +#pragma push_macro("XMACRO") +#pragma push_macro("TO_VALUE") +#pragma push_macro("VALUE_TO_MAP") +#pragma push_macro("NULL_NAME") +#pragma push_macro("TO_MAP") +#pragma push_macro("ALL_LEDGER_FLAGS") + +#undef XMACRO +#undef TO_VALUE +#undef VALUE_TO_MAP +#undef NULL_NAME +#undef TO_MAP + +#undef ALL_LEDGER_FLAGS + +// clang-format off + +#define XMACRO(LEDGER_OBJECT, LSF_FLAG, LSF_FLAG2) \ + LEDGER_OBJECT(AccountRoot, \ + LSF_FLAG(lsfPasswordSpent, 0x00010000) /* True, if password set fee is spent. */ \ + LSF_FLAG(lsfRequireDestTag, 0x00020000) /* True, to require a DestinationTag for payments. */ \ + LSF_FLAG(lsfRequireAuth, 0x00040000) /* True, to require a authorization to hold IOUs. */ \ + LSF_FLAG(lsfDisallowXRP, 0x00080000) /* True, to disallow sending XRP. */ \ + LSF_FLAG(lsfDisableMaster, 0x00100000) /* True, force regular key */ \ + LSF_FLAG(lsfNoFreeze, 0x00200000) /* True, cannot freeze ripple states */ \ + LSF_FLAG(lsfGlobalFreeze, 0x00400000) /* True, all assets frozen */ \ + LSF_FLAG(lsfDefaultRipple, 0x00800000) /* True, incoming trust lines allow rippling by default */ \ + LSF_FLAG(lsfDepositAuth, 0x01000000) /* True, all deposits require authorization */ \ + LSF_FLAG(lsfDisallowIncomingNFTokenOffer, 0x04000000) /* True, reject new incoming NFT offers */ \ + LSF_FLAG(lsfDisallowIncomingCheck, 0x08000000) /* True, reject new checks */ \ + LSF_FLAG(lsfDisallowIncomingPayChan, 0x10000000) /* True, reject new paychans */ \ + LSF_FLAG(lsfDisallowIncomingTrustline, 0x20000000) /* True, reject new trustlines (only if no issued assets) */ \ + LSF_FLAG(lsfAllowTrustLineLocking, 0x40000000) /* True, enable trustline locking */ \ + LSF_FLAG(lsfAllowTrustLineClawback, 0x80000000)) /* True, enable clawback */ \ + \ + LEDGER_OBJECT(Offer, \ + LSF_FLAG(lsfPassive, 0x00010000) \ + LSF_FLAG(lsfSell, 0x00020000) /* True, offer was placed as a sell. */ \ + LSF_FLAG(lsfHybrid, 0x00040000)) /* True, offer is hybrid. */ \ + \ + LEDGER_OBJECT(RippleState, \ + LSF_FLAG(lsfLowReserve, 0x00010000) /* True, if entry counts toward reserve. */ \ + LSF_FLAG(lsfHighReserve, 0x00020000) \ + LSF_FLAG(lsfLowAuth, 0x00040000) \ + LSF_FLAG(lsfHighAuth, 0x00080000) \ + LSF_FLAG(lsfLowNoRipple, 0x00100000) \ + LSF_FLAG(lsfHighNoRipple, 0x00200000) \ + LSF_FLAG(lsfLowFreeze, 0x00400000) /* True, low side has set freeze flag */ \ + LSF_FLAG(lsfHighFreeze, 0x00800000) /* True, high side has set freeze flag */ \ + LSF_FLAG(lsfAMMNode, 0x01000000) /* True, trust line to AMM. */ \ + /* Used by client apps to identify payments via AMM. */ \ + LSF_FLAG(lsfLowDeepFreeze, 0x02000000) /* True, low side has set deep freeze flag */ \ + LSF_FLAG(lsfHighDeepFreeze, 0x04000000)) /* True, high side has set deep freeze flag */ \ + \ + LEDGER_OBJECT(SignerList, \ + LSF_FLAG(lsfOneOwnerCount, 0x00010000)) /* True, uses only one OwnerCount */ \ + \ + LEDGER_OBJECT(DirNode, \ + LSF_FLAG(lsfNFTokenBuyOffers, 0x00000001) \ + LSF_FLAG(lsfNFTokenSellOffers, 0x00000002)) \ + \ + LEDGER_OBJECT(NFTokenOffer, \ + LSF_FLAG(lsfSellNFToken, 0x00000001)) \ + \ + LEDGER_OBJECT(MPTokenIssuance, \ + LSF_FLAG(lsfMPTLocked, 0x00000001) /* Also used in ltMPTOKEN */ \ + LSF_FLAG(lsfMPTCanLock, 0x00000002) \ + LSF_FLAG(lsfMPTRequireAuth, 0x00000004) \ + LSF_FLAG(lsfMPTCanEscrow, 0x00000008) \ + LSF_FLAG(lsfMPTCanTrade, 0x00000010) \ + LSF_FLAG(lsfMPTCanTransfer, 0x00000020) \ + LSF_FLAG(lsfMPTCanClawback, 0x00000040)) \ + \ + LEDGER_OBJECT(MPTokenIssuanceMutable, \ + LSF_FLAG(lsmfMPTCanMutateCanLock, 0x00000002) \ + LSF_FLAG(lsmfMPTCanMutateRequireAuth, 0x00000004) \ + LSF_FLAG(lsmfMPTCanMutateCanEscrow, 0x00000008) \ + LSF_FLAG(lsmfMPTCanMutateCanTrade, 0x00000010) \ + LSF_FLAG(lsmfMPTCanMutateCanTransfer, 0x00000020) \ + LSF_FLAG(lsmfMPTCanMutateCanClawback, 0x00000040) \ + LSF_FLAG(lsmfMPTCanMutateMetadata, 0x00010000) \ + LSF_FLAG(lsmfMPTCanMutateTransferFee, 0x00020000)) \ + \ + LEDGER_OBJECT(MPToken, \ + LSF_FLAG2(lsfMPTLocked, 0x00000001) \ + LSF_FLAG(lsfMPTAuthorized, 0x00000002)) \ + \ + LEDGER_OBJECT(Credential, \ + LSF_FLAG(lsfAccepted, 0x00010000)) \ + \ + LEDGER_OBJECT(Vault, \ + LSF_FLAG(lsfVaultPrivate, 0x00010000)) \ + \ + LEDGER_OBJECT(Loan, \ + LSF_FLAG(lsfLoanDefault, 0x00010000) \ + LSF_FLAG(lsfLoanImpaired, 0x00020000) \ + LSF_FLAG(lsfLoanOverpayment, 0x00040000)) /* True, loan allows overpayments */ + +// clang-format on + +// Create all the flag values as an enum. +// +// example: +// enum LedgerSpecificFlags { +// lsfPasswordSpent = 0x00010000, +// lsfRequireDestTag = 0x00020000, +// ... +// }; +#define TO_VALUE(name, value) name = value, +#define NULL_NAME(name, values) values +#define NULL_OUTPUT(name, value) +enum LedgerSpecificFlags : std::uint32_t { XMACRO(NULL_NAME, TO_VALUE, NULL_OUTPUT) }; + +// Create getter functions for each set of flags using Meyer's singleton pattern. +// This avoids static initialization order fiasco while still providing efficient access. +// This is used below in `getAllLedgerFlags()` to generate the server_definitions RPC output. +// +// example: +// inline LedgerFlagMap const& getAccountRootFlags() { +// static LedgerFlagMap const flags = { +// {"lsfPasswordSpent", 0x00010000}, +// {"lsfRequireDestTag", 0x00020000}, +// ...}; +// return flags; +// } +using LedgerFlagMap = std::map; +#define VALUE_TO_MAP(name, value) {#name, value}, +#define TO_MAP(name, values) \ + inline LedgerFlagMap const& get##name##Flags() \ + { \ + static LedgerFlagMap const flags = {values}; \ + return flags; \ + } +XMACRO(TO_MAP, VALUE_TO_MAP, VALUE_TO_MAP) + +// Create a getter function for all ledger flag maps using Meyer's singleton pattern. +// This is used to generate the server_definitions RPC output. +// +// example: +// inline std::vector> const& getAllLedgerFlags() { +// static std::vector> const flags = { +// {"AccountRoot", getAccountRootFlags()}, +// ...}; +// return flags; +// } +#define ALL_LEDGER_FLAGS(name, values) {#name, get##name##Flags()}, +inline std::vector> const& +getAllLedgerFlags() +{ + static std::vector> const flags = { + XMACRO(ALL_LEDGER_FLAGS, NULL_OUTPUT, NULL_OUTPUT)}; + return flags; +} + +#undef XMACRO +#undef TO_VALUE +#undef VALUE_TO_MAP +#undef NULL_NAME +#undef NULL_OUTPUT +#undef TO_MAP +#undef ALL_LEDGER_FLAGS + +#pragma pop_macro("XMACRO") +#pragma pop_macro("TO_VALUE") +#pragma pop_macro("VALUE_TO_MAP") +#pragma pop_macro("NULL_NAME") +#pragma pop_macro("TO_MAP") +#pragma pop_macro("ALL_LEDGER_FLAGS") //------------------------------------------------------------------------------ @@ -207,6 +286,10 @@ class LedgerFormats : public KnownFormats public: static LedgerFormats const& getInstance(); + + // Fields shared by all ledger entry formats: + static std::vector const& + getCommonFields(); }; } // namespace xrpl diff --git a/include/xrpl/protocol/SOTemplate.h b/include/xrpl/protocol/SOTemplate.h index 576bd8c34a2..41cea7936c8 100644 --- a/include/xrpl/protocol/SOTemplate.h +++ b/include/xrpl/protocol/SOTemplate.h @@ -6,6 +6,7 @@ #include #include #include +#include namespace xrpl { @@ -97,8 +98,12 @@ class SOTemplate operator=(SOTemplate&& other) = default; /** Create a template populated with all fields. - After creating the template fields cannot be - added, modified, or removed. + After creating the template fields cannot be added, modified, or removed. + */ + SOTemplate(std::vector uniqueFields, std::vector commonFields = {}); + + /** Create a template populated with all fields. + Note: Defers to the vector constructor above. */ SOTemplate( std::initializer_list uniqueFields, diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index 6c6ef5e3698..7c2085109ff 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -3,294 +3,444 @@ #include #include +#include +#include +#include +#include namespace xrpl { /** Transaction flags. - These flags are specified in a transaction's 'Flags' field and modify the - behavior of that transaction. + These flags are specified in a transaction's 'Flags' field and modify + the behavior of that transaction. There are two types of flags: - (1) Universal flags: these are flags which apply to, and are interpreted - the same way by, all transactions, except, perhaps, - to special pseudo-transactions. + (1) Universal flags: these are flags which apply to, and are interpreted the same way by, + all transactions, except, perhaps, to special pseudo-transactions. - (2) Tx-Specific flags: these are flags which are interpreted according - to the type of the transaction being executed. - That is, the same numerical flag value may have - different effects, depending on the transaction - being executed. + (2) Tx-Specific flags: these are flags which are interpreted according to the type of the + transaction being executed. That is, the same numerical flag value may have different + effects, depending on the transaction being executed. - @note The universal transaction flags occupy the high-order 8 bits. The - tx-specific flags occupy the remaining 24 bits. + @note The universal transaction flags occupy the high-order 8 bits. + The tx-specific flags occupy the remaining 24 bits. - @warning Transaction flags form part of the protocol. **Changing them - should be avoided because without special handling, this will - result in a hard fork.** + @warning Transaction flags form part of the protocol. + **Changing them should be avoided because without special handling, this will result in + a hard fork.** @ingroup protocol */ -// Formatting equals sign aligned 4 spaces after longest prefix, except for -// wrapped lines -// clang-format off +using FlagValue = std::uint32_t; + // Universal Transaction flags: -constexpr std::uint32_t tfFullyCanonicalSig = 0x80000000; -constexpr std::uint32_t tfInnerBatchTxn = 0x40000000; -constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn; -constexpr std::uint32_t tfUniversalMask = ~tfUniversal; - -// AccountSet flags: -constexpr std::uint32_t tfRequireDestTag = 0x00010000; -constexpr std::uint32_t tfOptionalDestTag = 0x00020000; -constexpr std::uint32_t tfRequireAuth = 0x00040000; -constexpr std::uint32_t tfOptionalAuth = 0x00080000; -constexpr std::uint32_t tfDisallowXRP = 0x00100000; -constexpr std::uint32_t tfAllowXRP = 0x00200000; -constexpr std::uint32_t tfAccountSetMask = - ~(tfUniversal | tfRequireDestTag | tfOptionalDestTag | tfRequireAuth | - tfOptionalAuth | tfDisallowXRP | tfAllowXRP); +inline constexpr FlagValue tfFullyCanonicalSig = 0x80000000; +inline constexpr FlagValue tfInnerBatchTxn = 0x40000000; +inline constexpr FlagValue tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn; +inline constexpr FlagValue tfUniversalMask = ~tfUniversal; + +#pragma push_macro("XMACRO") +#pragma push_macro("TO_VALUE") +#pragma push_macro("VALUE_TO_MAP") +#pragma push_macro("NULL_NAME") +#pragma push_macro("NULL_OUTPUT") +#pragma push_macro("TO_MAP") +#pragma push_macro("TO_MASK") +#pragma push_macro("VALUE_TO_MASK") +#pragma push_macro("ALL_TX_FLAGS") +#pragma push_macro("NULL_MASK_ADJ") +#pragma push_macro("MASK_ADJ_TO_MASK") + +#undef XMACRO +#undef TO_VALUE +#undef VALUE_TO_MAP +#undef NULL_NAME +#undef NULL_OUTPUT +#undef TO_MAP +#undef TO_MASK +#undef VALUE_TO_MASK +#undef NULL_MASK_ADJ +#undef MASK_ADJ_TO_MASK -// AccountSet SetFlag/ClearFlag values -constexpr std::uint32_t asfRequireDest = 1; -constexpr std::uint32_t asfRequireAuth = 2; -constexpr std::uint32_t asfDisallowXRP = 3; -constexpr std::uint32_t asfDisableMaster = 4; -constexpr std::uint32_t asfAccountTxnID = 5; -constexpr std::uint32_t asfNoFreeze = 6; -constexpr std::uint32_t asfGlobalFreeze = 7; -constexpr std::uint32_t asfDefaultRipple = 8; -constexpr std::uint32_t asfDepositAuth = 9; -constexpr std::uint32_t asfAuthorizedNFTokenMinter = 10; -/* // reserved for Hooks amendment -constexpr std::uint32_t asfTshCollect = 11; -*/ -constexpr std::uint32_t asfDisallowIncomingNFTokenOffer = 12; -constexpr std::uint32_t asfDisallowIncomingCheck = 13; -constexpr std::uint32_t asfDisallowIncomingPayChan = 14; -constexpr std::uint32_t asfDisallowIncomingTrustline = 15; -constexpr std::uint32_t asfAllowTrustLineClawback = 16; -constexpr std::uint32_t asfAllowTrustLineLocking = 17; - -// OfferCreate flags: -constexpr std::uint32_t tfPassive = 0x00010000; -constexpr std::uint32_t tfImmediateOrCancel = 0x00020000; -constexpr std::uint32_t tfFillOrKill = 0x00040000; -constexpr std::uint32_t tfSell = 0x00080000; -constexpr std::uint32_t tfHybrid = 0x00100000; -constexpr std::uint32_t tfOfferCreateMask = - ~(tfUniversal | tfPassive | tfImmediateOrCancel | tfFillOrKill | tfSell | tfHybrid); - -// Payment flags: -constexpr std::uint32_t tfNoRippleDirect = 0x00010000; -constexpr std::uint32_t tfPartialPayment = 0x00020000; -constexpr std::uint32_t tfLimitQuality = 0x00040000; -constexpr std::uint32_t tfPaymentMask = - ~(tfUniversal | tfPartialPayment | tfLimitQuality | tfNoRippleDirect); -constexpr std::uint32_t tfMPTPaymentMask = ~(tfUniversal | tfPartialPayment); - -// TrustSet flags: -constexpr std::uint32_t tfSetfAuth = 0x00010000; -constexpr std::uint32_t tfSetNoRipple = 0x00020000; -constexpr std::uint32_t tfClearNoRipple = 0x00040000; -constexpr std::uint32_t tfSetFreeze = 0x00100000; -constexpr std::uint32_t tfClearFreeze = 0x00200000; -constexpr std::uint32_t tfSetDeepFreeze = 0x00400000; -constexpr std::uint32_t tfClearDeepFreeze = 0x00800000; -constexpr std::uint32_t tfTrustSetMask = - ~(tfUniversal | tfSetfAuth | tfSetNoRipple | tfClearNoRipple | tfSetFreeze | - tfClearFreeze | tfSetDeepFreeze | tfClearDeepFreeze); -constexpr std::uint32_t tfTrustSetPermissionMask = ~(tfUniversal | tfSetfAuth | tfSetFreeze | tfClearFreeze); - -// EnableAmendment flags: -constexpr std::uint32_t tfGotMajority = 0x00010000; -constexpr std::uint32_t tfLostMajority = 0x00020000; -constexpr std::uint32_t tfChangeMask = - ~( tfUniversal | tfGotMajority | tfLostMajority); - -// PaymentChannelClaim flags: -constexpr std::uint32_t tfRenew = 0x00010000; -constexpr std::uint32_t tfClose = 0x00020000; -constexpr std::uint32_t tfPayChanClaimMask = ~(tfUniversal | tfRenew | tfClose); - -// NFTokenMint flags: -constexpr std::uint32_t const tfBurnable = 0x00000001; -constexpr std::uint32_t const tfOnlyXRP = 0x00000002; -constexpr std::uint32_t const tfTrustLine = 0x00000004; -constexpr std::uint32_t const tfTransferable = 0x00000008; -constexpr std::uint32_t const tfMutable = 0x00000010; - -// MPTokenIssuanceCreate flags: -// Note: tf/lsfMPTLocked is intentionally omitted, since this transaction -// is not allowed to modify it. -constexpr std::uint32_t const tfMPTCanLock = lsfMPTCanLock; -constexpr std::uint32_t const tfMPTRequireAuth = lsfMPTRequireAuth; -constexpr std::uint32_t const tfMPTCanEscrow = lsfMPTCanEscrow; -constexpr std::uint32_t const tfMPTCanTrade = lsfMPTCanTrade; -constexpr std::uint32_t const tfMPTCanTransfer = lsfMPTCanTransfer; -constexpr std::uint32_t const tfMPTCanClawback = lsfMPTCanClawback; -constexpr std::uint32_t const tfMPTokenIssuanceCreateMask = - ~(tfUniversal | tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | tfMPTCanClawback); +// clang-format off +#undef ALL_TX_FLAGS + +// XMACRO parameters: +// - TRANSACTION: handles the transaction name, its flags, and mask adjustment +// - TF_FLAG: defines a new flag constant +// - TF_FLAG2: references an existing flag constant (no new definition) +// - MASK_ADJ: specifies flags to add back to the mask (making them invalid for this tx type) +// +// Note: MASK_ADJ is used when a universal flag should be invalid for a specific transaction. +// For example, Batch uses MASK_ADJ(tfInnerBatchTxn) because the outer Batch transaction +// must not have tfInnerBatchTxn set (only inner transactions should have it). +// +// TODO: Consider rewriting this using reflection in C++26 or later. Alternatively this could be a DSL processed by a script at build time. +#define XMACRO(TRANSACTION, TF_FLAG, TF_FLAG2, MASK_ADJ) \ + TRANSACTION(AccountSet, \ + TF_FLAG(tfRequireDestTag, 0x00010000) \ + TF_FLAG(tfOptionalDestTag, 0x00020000) \ + TF_FLAG(tfRequireAuth, 0x00040000) \ + TF_FLAG(tfOptionalAuth, 0x00080000) \ + TF_FLAG(tfDisallowXRP, 0x00100000) \ + TF_FLAG(tfAllowXRP, 0x00200000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(OfferCreate, \ + TF_FLAG(tfPassive, 0x00010000) \ + TF_FLAG(tfImmediateOrCancel, 0x00020000) \ + TF_FLAG(tfFillOrKill, 0x00040000) \ + TF_FLAG(tfSell, 0x00080000) \ + TF_FLAG(tfHybrid, 0x00100000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(Payment, \ + TF_FLAG(tfNoRippleDirect, 0x00010000) \ + TF_FLAG(tfPartialPayment, 0x00020000) \ + TF_FLAG(tfLimitQuality, 0x00040000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(TrustSet, \ + TF_FLAG(tfSetfAuth, 0x00010000) \ + TF_FLAG(tfSetNoRipple, 0x00020000) \ + TF_FLAG(tfClearNoRipple, 0x00040000) \ + TF_FLAG(tfSetFreeze, 0x00100000) \ + TF_FLAG(tfClearFreeze, 0x00200000) \ + TF_FLAG(tfSetDeepFreeze, 0x00400000) \ + TF_FLAG(tfClearDeepFreeze, 0x00800000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(EnableAmendment, \ + TF_FLAG(tfGotMajority, 0x00010000) \ + TF_FLAG(tfLostMajority, 0x00020000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(PaymentChannelClaim, \ + TF_FLAG(tfRenew, 0x00010000) \ + TF_FLAG(tfClose, 0x00020000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(NFTokenMint, \ + TF_FLAG(tfBurnable, 0x00000001) \ + TF_FLAG(tfOnlyXRP, 0x00000002) \ + /* deprecated TF_FLAG(tfTrustLine, 0x00000004) */ \ + TF_FLAG(tfTransferable, 0x00000008) \ + TF_FLAG(tfMutable, 0x00000010), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(MPTokenIssuanceCreate, \ + /* Note: tf/lsfMPTLocked is intentionally omitted since this transaction is not allowed to modify it. */ \ + TF_FLAG(tfMPTCanLock, lsfMPTCanLock) \ + TF_FLAG(tfMPTRequireAuth, lsfMPTRequireAuth) \ + TF_FLAG(tfMPTCanEscrow, lsfMPTCanEscrow) \ + TF_FLAG(tfMPTCanTrade, lsfMPTCanTrade) \ + TF_FLAG(tfMPTCanTransfer, lsfMPTCanTransfer) \ + TF_FLAG(tfMPTCanClawback, lsfMPTCanClawback), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(MPTokenAuthorize, \ + TF_FLAG(tfMPTUnauthorize, 0x00000001), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(MPTokenIssuanceSet, \ + TF_FLAG(tfMPTLock, 0x00000001) \ + TF_FLAG(tfMPTUnlock, 0x00000002), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(NFTokenCreateOffer, \ + TF_FLAG(tfSellNFToken, 0x00000001), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(AMMDeposit, \ + TF_FLAG(tfLPToken, 0x00010000) \ + TF_FLAG(tfSingleAsset, 0x00080000) \ + TF_FLAG(tfTwoAsset, 0x00100000) \ + TF_FLAG(tfOneAssetLPToken, 0x00200000) \ + TF_FLAG(tfLimitLPToken, 0x00400000) \ + TF_FLAG(tfTwoAssetIfEmpty, 0x00800000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(AMMWithdraw, \ + TF_FLAG2(tfLPToken, 0x00010000) \ + TF_FLAG(tfWithdrawAll, 0x00020000) \ + TF_FLAG(tfOneAssetWithdrawAll, 0x00040000) \ + TF_FLAG2(tfSingleAsset, 0x00080000) \ + TF_FLAG2(tfTwoAsset, 0x00100000) \ + TF_FLAG2(tfOneAssetLPToken, 0x00200000) \ + TF_FLAG2(tfLimitLPToken, 0x00400000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(AMMClawback, \ + TF_FLAG(tfClawTwoAssets, 0x00000001), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(XChainModifyBridge, \ + TF_FLAG(tfClearAccountCreateAmount, 0x00010000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(VaultCreate, \ + TF_FLAG(tfVaultPrivate, lsfVaultPrivate) \ + TF_FLAG(tfVaultShareNonTransferable, 0x00020000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(Batch, \ + TF_FLAG(tfAllOrNothing, 0x00010000) \ + TF_FLAG(tfOnlyOne, 0x00020000) \ + TF_FLAG(tfUntilFailure, 0x00040000) \ + TF_FLAG(tfIndependent, 0x00080000), \ + MASK_ADJ(tfInnerBatchTxn)) /* Batch must reject tfInnerBatchTxn - only inner transactions should have this flag */ \ + \ + TRANSACTION(LoanSet, /* True indicates the loan supports overpayments */ \ + TF_FLAG(tfLoanOverpayment, 0x00010000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(LoanPay, /* True indicates any excess in this payment can be used as an overpayment. */ \ + /* False: no overpayments will be taken. */ \ + TF_FLAG2(tfLoanOverpayment, 0x00010000) \ + TF_FLAG(tfLoanFullPayment, 0x00020000) /* True indicates that the payment is an early full payment. */ \ + /* It must pay the entire loan including close interest and fees, or it will fail. */ \ + /* False: Not a full payment. */ \ + TF_FLAG(tfLoanLatePayment, 0x00040000), /* True indicates that the payment is late, and includes late interest and fees. */ \ + /* If the loan is not late, it will fail. */ \ + /* False: not a late payment. If the current payment is overdue, the transaction will fail.*/ \ + MASK_ADJ(0)) \ + \ + TRANSACTION(LoanManage, \ + TF_FLAG(tfLoanDefault, 0x00010000) \ + TF_FLAG(tfLoanImpair, 0x00020000) \ + TF_FLAG(tfLoanUnimpair, 0x00040000), \ + MASK_ADJ(0)) + +// clang-format on + +// Create all the flag values. +// +// example: +// inline constexpr FlagValue tfAccountSetRequireDestTag = 0x00010000; +#define TO_VALUE(name, value) inline constexpr FlagValue name = value; +#define NULL_NAME(name, values, maskAdj) values +#define NULL_OUTPUT(name, value) +#define NULL_MASK_ADJ(value) +XMACRO(NULL_NAME, TO_VALUE, NULL_OUTPUT, NULL_MASK_ADJ) + +// Create masks for each transaction type that has flags. +// +// example: +// inline constexpr FlagValue tfAccountSetMask = ~(tfUniversal | tfRequireDestTag | +// tfOptionalDestTag | tfRequireAuth | tfOptionalAuth | tfDisallowXRP | tfAllowXRP); +// +// The mask adjustment (maskAdj) allows adding flags back to the mask, making them invalid. +// For example, Batch uses MASK_ADJ(tfInnerBatchTxn) to reject tfInnerBatchTxn on outer Batch. +#define TO_MASK(name, values, maskAdj) \ + inline constexpr FlagValue tf##name##Mask = ~(tfUniversal values) | maskAdj; +#define VALUE_TO_MASK(name, value) | name +#define MASK_ADJ_TO_MASK(value) value +XMACRO(TO_MASK, VALUE_TO_MASK, VALUE_TO_MASK, MASK_ADJ_TO_MASK) + +// Verify that tfBatchMask correctly rejects tfInnerBatchTxn. +// The outer Batch transaction must NOT have tfInnerBatchTxn set; only inner transactions should +// have it. +static_assert( + (tfBatchMask & tfInnerBatchTxn) == tfInnerBatchTxn, + "tfBatchMask must include tfInnerBatchTxn to reject it on outer Batch"); + +// Verify that other transaction masks correctly allow tfInnerBatchTxn. +// Inner transactions need tfInnerBatchTxn to be valid, so these masks must not reject it. +static_assert( + (tfPaymentMask & tfInnerBatchTxn) == 0, + "tfPaymentMask must not reject tfInnerBatchTxn"); +static_assert( + (tfAccountSetMask & tfInnerBatchTxn) == 0, + "tfAccountSetMask must not reject tfInnerBatchTxn"); + +// Create getter functions for each set of flags using Meyer's singleton pattern. +// This avoids static initialization order fiasco while still providing efficient access. +// This is used below in `getAllTxFlags()` to generate the server_definitions RPC +// output. +// +// example: +// inline FlagMap const& getAccountSetFlags() { +// static FlagMap const flags = { +// {"tfRequireDestTag", 0x00010000}, +// {"tfOptionalDestTag", 0x00020000}, +// ...}; +// return flags; +// } +using FlagMap = std::map; +#define VALUE_TO_MAP(name, value) {#name, value}, +#define TO_MAP(name, values, maskAdj) \ + inline FlagMap const& get##name##Flags() \ + { \ + static FlagMap const flags = {values}; \ + return flags; \ + } +XMACRO(TO_MAP, VALUE_TO_MAP, VALUE_TO_MAP, NULL_MASK_ADJ) + +inline FlagMap const& +getUniversalFlags() +{ + static FlagMap const flags = { + {"tfFullyCanonicalSig", tfFullyCanonicalSig}, {"tfInnerBatchTxn", tfInnerBatchTxn}}; + return flags; +} + +// Create a getter function for all transaction flag maps using Meyer's singleton pattern. +// This is used to generate the server_definitions RPC output. +// +// example: +// inline FlagMapPairList const& getAllTxFlags() { +// static FlagMapPairList const flags = { +// {"AccountSet", getAccountSetFlags()}, +// ...}; +// return flags; +// } +using FlagMapPairList = std::vector>; +#define ALL_TX_FLAGS(name, values, maskAdj) {#name, get##name##Flags()}, +inline FlagMapPairList const& +getAllTxFlags() +{ + static FlagMapPairList const flags = { + {"universal", getUniversalFlags()}, + XMACRO(ALL_TX_FLAGS, NULL_OUTPUT, NULL_OUTPUT, NULL_MASK_ADJ)}; + return flags; +} + +#undef XMACRO +#undef TO_VALUE +#undef VALUE_TO_MAP +#undef NULL_NAME +#undef NULL_OUTPUT +#undef TO_MAP +#undef TO_MASK +#undef VALUE_TO_MASK +#undef ALL_TX_FLAGS +#undef NULL_MASK_ADJ +#undef MASK_ADJ_TO_MASK + +#pragma pop_macro("XMACRO") +#pragma pop_macro("TO_VALUE") +#pragma pop_macro("VALUE_TO_MAP") +#pragma pop_macro("NULL_NAME") +#pragma pop_macro("NULL_OUTPUT") +#pragma pop_macro("TO_MAP") +#pragma pop_macro("TO_MASK") +#pragma pop_macro("VALUE_TO_MASK") +#pragma pop_macro("ALL_TX_FLAGS") +#pragma pop_macro("NULL_MASK_ADJ") +#pragma pop_macro("MASK_ADJ_TO_MASK") + +// Additional transaction masks and combos +inline constexpr FlagValue tfMPTPaymentMask = ~(tfUniversal | tfPartialPayment); +inline constexpr FlagValue tfTrustSetPermissionMask = + ~(tfUniversal | tfSetfAuth | tfSetFreeze | tfClearFreeze); // MPTokenIssuanceCreate MutableFlags: // Indicating specific fields or flags may be changed after issuance. -constexpr std::uint32_t const tmfMPTCanMutateCanLock = lsmfMPTCanMutateCanLock; -constexpr std::uint32_t const tmfMPTCanMutateRequireAuth = lsmfMPTCanMutateRequireAuth; -constexpr std::uint32_t const tmfMPTCanMutateCanEscrow = lsmfMPTCanMutateCanEscrow; -constexpr std::uint32_t const tmfMPTCanMutateCanTrade = lsmfMPTCanMutateCanTrade; -constexpr std::uint32_t const tmfMPTCanMutateCanTransfer = lsmfMPTCanMutateCanTransfer; -constexpr std::uint32_t const tmfMPTCanMutateCanClawback = lsmfMPTCanMutateCanClawback; -constexpr std::uint32_t const tmfMPTCanMutateMetadata = lsmfMPTCanMutateMetadata; -constexpr std::uint32_t const tmfMPTCanMutateTransferFee = lsmfMPTCanMutateTransferFee; -constexpr std::uint32_t const tmfMPTokenIssuanceCreateMutableMask = - ~(tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow | tmfMPTCanMutateCanTrade - | tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback | tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee); - -// MPTokenAuthorize flags: -constexpr std::uint32_t const tfMPTUnauthorize = 0x00000001; -constexpr std::uint32_t const tfMPTokenAuthorizeMask = ~(tfUniversal | tfMPTUnauthorize); - -// MPTokenIssuanceSet flags: -constexpr std::uint32_t const tfMPTLock = 0x00000001; -constexpr std::uint32_t const tfMPTUnlock = 0x00000002; -constexpr std::uint32_t const tfMPTokenIssuanceSetMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock); -constexpr std::uint32_t const tfMPTokenIssuanceSetPermissionMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock); +inline constexpr FlagValue tmfMPTCanMutateCanLock = lsmfMPTCanMutateCanLock; +inline constexpr FlagValue tmfMPTCanMutateRequireAuth = lsmfMPTCanMutateRequireAuth; +inline constexpr FlagValue tmfMPTCanMutateCanEscrow = lsmfMPTCanMutateCanEscrow; +inline constexpr FlagValue tmfMPTCanMutateCanTrade = lsmfMPTCanMutateCanTrade; +inline constexpr FlagValue tmfMPTCanMutateCanTransfer = lsmfMPTCanMutateCanTransfer; +inline constexpr FlagValue tmfMPTCanMutateCanClawback = lsmfMPTCanMutateCanClawback; +inline constexpr FlagValue tmfMPTCanMutateMetadata = lsmfMPTCanMutateMetadata; +inline constexpr FlagValue tmfMPTCanMutateTransferFee = lsmfMPTCanMutateTransferFee; +inline constexpr FlagValue tmfMPTokenIssuanceCreateMutableMask = + ~(tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow | + tmfMPTCanMutateCanTrade | tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback | + tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee); // MPTokenIssuanceSet MutableFlags: // Set or Clear flags. -constexpr std::uint32_t const tmfMPTSetCanLock = 0x00000001; -constexpr std::uint32_t const tmfMPTClearCanLock = 0x00000002; -constexpr std::uint32_t const tmfMPTSetRequireAuth = 0x00000004; -constexpr std::uint32_t const tmfMPTClearRequireAuth = 0x00000008; -constexpr std::uint32_t const tmfMPTSetCanEscrow = 0x00000010; -constexpr std::uint32_t const tmfMPTClearCanEscrow = 0x00000020; -constexpr std::uint32_t const tmfMPTSetCanTrade = 0x00000040; -constexpr std::uint32_t const tmfMPTClearCanTrade = 0x00000080; -constexpr std::uint32_t const tmfMPTSetCanTransfer = 0x00000100; -constexpr std::uint32_t const tmfMPTClearCanTransfer = 0x00000200; -constexpr std::uint32_t const tmfMPTSetCanClawback = 0x00000400; -constexpr std::uint32_t const tmfMPTClearCanClawback = 0x00000800; -constexpr std::uint32_t const tmfMPTokenIssuanceSetMutableMask = ~(tmfMPTSetCanLock | tmfMPTClearCanLock | - tmfMPTSetRequireAuth | tmfMPTClearRequireAuth | tmfMPTSetCanEscrow | tmfMPTClearCanEscrow | - tmfMPTSetCanTrade | tmfMPTClearCanTrade | tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | - tmfMPTSetCanClawback | tmfMPTClearCanClawback); - -// MPTokenIssuanceDestroy flags: -constexpr std::uint32_t const tfMPTokenIssuanceDestroyMask = ~tfUniversal; - -// Prior to fixRemoveNFTokenAutoTrustLine, transfer of an NFToken between -// accounts allowed a TrustLine to be added to the issuer of that token -// without explicit permission from that issuer. This was enabled by -// minting the NFToken with the tfTrustLine flag set. + +inline constexpr FlagValue tmfMPTSetCanLock = 0x00000001; +inline constexpr FlagValue tmfMPTClearCanLock = 0x00000002; +inline constexpr FlagValue tmfMPTSetRequireAuth = 0x00000004; +inline constexpr FlagValue tmfMPTClearRequireAuth = 0x00000008; +inline constexpr FlagValue tmfMPTSetCanEscrow = 0x00000010; +inline constexpr FlagValue tmfMPTClearCanEscrow = 0x00000020; +inline constexpr FlagValue tmfMPTSetCanTrade = 0x00000040; +inline constexpr FlagValue tmfMPTClearCanTrade = 0x00000080; +inline constexpr FlagValue tmfMPTSetCanTransfer = 0x00000100; +inline constexpr FlagValue tmfMPTClearCanTransfer = 0x00000200; +inline constexpr FlagValue tmfMPTSetCanClawback = 0x00000400; +inline constexpr FlagValue tmfMPTClearCanClawback = 0x00000800; +inline constexpr FlagValue tmfMPTokenIssuanceSetMutableMask = ~( + tmfMPTSetCanLock | tmfMPTClearCanLock | tmfMPTSetRequireAuth | tmfMPTClearRequireAuth | + tmfMPTSetCanEscrow | tmfMPTClearCanEscrow | tmfMPTSetCanTrade | tmfMPTClearCanTrade | + tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | tmfMPTSetCanClawback | tmfMPTClearCanClawback); + +// Prior to fixRemoveNFTokenAutoTrustLine, transfer of an NFToken between accounts allowed a +// TrustLine to be added to the issuer of that token without explicit permission from that issuer. +// This was enabled by minting the NFToken with the tfTrustLine flag set. // -// That capability could be used to attack the NFToken issuer. It -// would be possible for two accounts to trade the NFToken back and forth -// building up any number of TrustLines on the issuer, increasing the -// issuer's reserve without bound. +// That capability could be used to attack the NFToken issuer. +// It would be possible for two accounts to trade the NFToken back and forth building up any number +// of TrustLines on the issuer, increasing the issuer's reserve without bound. // -// The fixRemoveNFTokenAutoTrustLine amendment disables minting with the -// tfTrustLine flag as a way to prevent the attack. But until the -// amendment passes we still need to keep the old behavior available. -constexpr std::uint32_t const tfNFTokenMintMask = +// The fixRemoveNFTokenAutoTrustLine amendment disables minting with the tfTrustLine flag as a way +// to prevent the attack. But until the amendment passes we still need to keep the old behavior +// available. +inline constexpr FlagValue tfTrustLine = 0x00000004; // needed for backwards compatibility +inline constexpr FlagValue tfNFTokenMintMaskWithoutMutable = ~(tfUniversal | tfBurnable | tfOnlyXRP | tfTransferable); -constexpr std::uint32_t const tfNFTokenMintOldMask = - ~( ~tfNFTokenMintMask | tfTrustLine); +inline constexpr FlagValue tfNFTokenMintOldMask = ~(~tfNFTokenMintMaskWithoutMutable | tfTrustLine); // if featureDynamicNFT enabled then new flag allowing mutable URI available. -constexpr std::uint32_t const tfNFTokenMintOldMaskWithMutable = - ~( ~tfNFTokenMintOldMask | tfMutable); - -constexpr std::uint32_t const tfNFTokenMintMaskWithMutable = - ~( ~tfNFTokenMintMask | tfMutable); - -// NFTokenCreateOffer flags: -constexpr std::uint32_t const tfSellNFToken = 0x00000001; -constexpr std::uint32_t const tfNFTokenCreateOfferMask = - ~(tfUniversal | tfSellNFToken); - -// NFTokenCancelOffer flags: -constexpr std::uint32_t const tfNFTokenCancelOfferMask = ~tfUniversal; - -// NFTokenAcceptOffer flags: -constexpr std::uint32_t const tfNFTokenAcceptOfferMask = ~tfUniversal; - -// Clawback flags: -constexpr std::uint32_t const tfClawbackMask = ~tfUniversal; - -// AMM Flags: -constexpr std::uint32_t tfLPToken = 0x00010000; -constexpr std::uint32_t tfWithdrawAll = 0x00020000; -constexpr std::uint32_t tfOneAssetWithdrawAll = 0x00040000; -constexpr std::uint32_t tfSingleAsset = 0x00080000; -constexpr std::uint32_t tfTwoAsset = 0x00100000; -constexpr std::uint32_t tfOneAssetLPToken = 0x00200000; -constexpr std::uint32_t tfLimitLPToken = 0x00400000; -constexpr std::uint32_t tfTwoAssetIfEmpty = 0x00800000; -constexpr std::uint32_t tfWithdrawSubTx = - tfLPToken | tfSingleAsset | tfTwoAsset | tfOneAssetLPToken | - tfLimitLPToken | tfWithdrawAll | tfOneAssetWithdrawAll; -constexpr std::uint32_t tfDepositSubTx = - tfLPToken | tfSingleAsset | tfTwoAsset | tfOneAssetLPToken | - tfLimitLPToken | tfTwoAssetIfEmpty; -constexpr std::uint32_t tfWithdrawMask = ~(tfUniversal | tfWithdrawSubTx); -constexpr std::uint32_t tfDepositMask = ~(tfUniversal | tfDepositSubTx); - -// AMMClawback flags: -constexpr std::uint32_t tfClawTwoAssets = 0x00000001; -constexpr std::uint32_t tfAMMClawbackMask = ~(tfUniversal | tfClawTwoAssets); - -// BridgeModify flags: -constexpr std::uint32_t tfClearAccountCreateAmount = 0x00010000; -constexpr std::uint32_t tfBridgeModifyMask = ~(tfUniversal | tfClearAccountCreateAmount); - -// VaultCreate flags: -constexpr std::uint32_t const tfVaultPrivate = 0x00010000; -static_assert(tfVaultPrivate == lsfVaultPrivate); -constexpr std::uint32_t const tfVaultShareNonTransferable = 0x00020000; -constexpr std::uint32_t const tfVaultCreateMask = ~(tfUniversal | tfVaultPrivate | tfVaultShareNonTransferable); - -// Batch Flags: -constexpr std::uint32_t tfAllOrNothing = 0x00010000; -constexpr std::uint32_t tfOnlyOne = 0x00020000; -constexpr std::uint32_t tfUntilFailure = 0x00040000; -constexpr std::uint32_t tfIndependent = 0x00080000; -/** - * @note If nested Batch transactions are supported in the future, the tfInnerBatchTxn flag - * will need to be removed from this mask to allow Batch transaction to be inside - * the sfRawTransactions array. - */ -constexpr std::uint32_t const tfBatchMask = - ~(tfUniversal | tfAllOrNothing | tfOnlyOne | tfUntilFailure | tfIndependent) | tfInnerBatchTxn; - -// LoanSet and LoanPay flags: -// LoanSet: True, indicates the loan supports overpayments -// LoanPay: True, indicates any excess in this payment can be used -// as an overpayment. False, no overpayments will be taken. -constexpr std::uint32_t const tfLoanOverpayment = 0x00010000; -// LoanPay exclusive flags: -// tfLoanFullPayment: True, indicates that the payment is an early -// full payment. It must pay the entire loan including close -// interest and fees, or it will fail. False: Not a full payment. -constexpr std::uint32_t const tfLoanFullPayment = 0x00020000; -// tfLoanLatePayment: True, indicates that the payment is late, -// and includes late interest and fees. If the loan is not late, -// it will fail. False: not a late payment. If the current payment -// is overdue, the transaction will fail. -constexpr std::uint32_t const tfLoanLatePayment = 0x00040000; -constexpr std::uint32_t const tfLoanSetMask = ~(tfUniversal | - tfLoanOverpayment); -constexpr std::uint32_t const tfLoanPayMask = ~(tfUniversal | - tfLoanOverpayment | tfLoanFullPayment | tfLoanLatePayment); - -// LoanManage flags: -constexpr std::uint32_t const tfLoanDefault = 0x00010000; -constexpr std::uint32_t const tfLoanImpair = 0x00020000; -constexpr std::uint32_t const tfLoanUnimpair = 0x00040000; -constexpr std::uint32_t const tfLoanManageMask = ~(tfUniversal | tfLoanDefault | tfLoanImpair | tfLoanUnimpair); +inline constexpr FlagValue tfNFTokenMintOldMaskWithMutable = ~(~tfNFTokenMintOldMask | tfMutable); -// clang-format on +inline constexpr FlagValue tfWithdrawSubTx = tfLPToken | tfSingleAsset | tfTwoAsset | + tfOneAssetLPToken | tfLimitLPToken | tfWithdrawAll | tfOneAssetWithdrawAll; +inline constexpr FlagValue tfDepositSubTx = + tfLPToken | tfSingleAsset | tfTwoAsset | tfOneAssetLPToken | tfLimitLPToken | tfTwoAssetIfEmpty; + +#pragma push_macro("ACCOUNTSET_FLAGS") +#pragma push_macro("ACCOUNTSET_FLAG_TO_VALUE") +#pragma push_macro("ACCOUNTSET_FLAG_TO_MAP") + +// AccountSet SetFlag/ClearFlag values +#define ACCOUNTSET_FLAGS(ASF_FLAG) \ + ASF_FLAG(asfRequireDest, 1) \ + ASF_FLAG(asfRequireAuth, 2) \ + ASF_FLAG(asfDisallowXRP, 3) \ + ASF_FLAG(asfDisableMaster, 4) \ + ASF_FLAG(asfAccountTxnID, 5) \ + ASF_FLAG(asfNoFreeze, 6) \ + ASF_FLAG(asfGlobalFreeze, 7) \ + ASF_FLAG(asfDefaultRipple, 8) \ + ASF_FLAG(asfDepositAuth, 9) \ + ASF_FLAG(asfAuthorizedNFTokenMinter, 10) \ + /* 11 is reserved for Hooks amendment */ \ + /* ASF_FLAG(asfTshCollect, 11) */ \ + ASF_FLAG(asfDisallowIncomingNFTokenOffer, 12) \ + ASF_FLAG(asfDisallowIncomingCheck, 13) \ + ASF_FLAG(asfDisallowIncomingPayChan, 14) \ + ASF_FLAG(asfDisallowIncomingTrustline, 15) \ + ASF_FLAG(asfAllowTrustLineClawback, 16) \ + ASF_FLAG(asfAllowTrustLineLocking, 17) + +#define ACCOUNTSET_FLAG_TO_VALUE(name, value) inline constexpr FlagValue name = value; +#define ACCOUNTSET_FLAG_TO_MAP(name, value) {#name, value}, + +ACCOUNTSET_FLAGS(ACCOUNTSET_FLAG_TO_VALUE) + +inline std::map const& +getAsfFlagMap() +{ + static std::map const flags = { + ACCOUNTSET_FLAGS(ACCOUNTSET_FLAG_TO_MAP)}; + return flags; +} + +#undef ACCOUNTSET_FLAG_TO_VALUE +#undef ACCOUNTSET_FLAG_TO_MAP +#undef ACCOUNTSET_FLAGS + +#pragma pop_macro("ACCOUNTSET_FLAG_TO_VALUE") +#pragma pop_macro("ACCOUNTSET_FLAG_TO_MAP") +#pragma pop_macro("ACCOUNTSET_FLAGS") } // namespace xrpl diff --git a/include/xrpl/protocol/TxFormats.h b/include/xrpl/protocol/TxFormats.h index b76eef7585f..563a28f39f3 100644 --- a/include/xrpl/protocol/TxFormats.h +++ b/include/xrpl/protocol/TxFormats.h @@ -2,6 +2,8 @@ #include +#include + namespace xrpl { /** Transaction type identifiers. @@ -73,6 +75,9 @@ class TxFormats : public KnownFormats public: static TxFormats const& getInstance(); + + static std::vector const& + getCommonFields(); }; } // namespace xrpl diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index 0264f625eb2..5c605dde043 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -25,6 +25,7 @@ namespace jss { JSS(AL_size); // out: GetCounts JSS(AL_hit_rate); // out: GetCounts JSS(AcceptedCredentials); // out: AccountObjects +JSS(ACCOUNT_SET_FLAGS); // out: RPC server_definitions JSS(Account); // in: TransactionSign; field. JSS(AMMID); // field JSS(Amount); // in: TransactionSign; field. @@ -187,6 +188,7 @@ JSS(closed_ledger); // out: NetworkOPs JSS(cluster); // out: PeerImp JSS(code); // out: errors JSS(command); // in: RPCHandler +JSS(common); // out: RPC server_definitions JSS(complete); // out: NetworkOPs, InboundLedger JSS(complete_ledgers); // out: NetworkOPs, PeerImp JSS(consensus); // out: NetworkOPs, LedgerConsensus @@ -356,6 +358,8 @@ JSS(ledger_min); // in, out: AccountTx* JSS(ledger_time); // out: NetworkOPs JSS(LEDGER_ENTRY_TYPES); // out: RPC server_definitions // matches definitions.json format +JSS(LEDGER_ENTRY_FLAGS); // out: RPC server_definitions +JSS(LEDGER_ENTRY_FORMATS); // out: RPC server_definitions JSS(levels); // LogLevels JSS(limit); // in/out: AccountTx*, AccountOffers, // AccountLines, AccountObjects @@ -457,6 +461,7 @@ JSS(open); // out: handlers/Ledger JSS(open_ledger_cost); // out: SubmitTransaction JSS(open_ledger_fee); // out: TxQ JSS(open_ledger_level); // out: TxQ +JSS(optionality); // out: server_definitions JSS(oracles); // in: get_aggregate_price JSS(oracle_document_id); // in: get_aggregate_price JSS(owner); // in: LedgerEntry, out: NetworkOPs @@ -616,6 +621,8 @@ JSS(TRANSACTION_RESULTS); // out: RPC server_definitions // matches definitions.json format JSS(TRANSACTION_TYPES); // out: RPC server_definitions // matches definitions.json format +JSS(TRANSACTION_FLAGS); // out: RPC server_definitions +JSS(TRANSACTION_FORMATS); // out: RPC server_definitions JSS(TYPES); // out: RPC server_definitions // matches definitions.json format JSS(transfer_rate); // out: nft_info (clio) diff --git a/include/xrpl/tx/transactors/Clawback.h b/include/xrpl/tx/transactors/Clawback.h index 74512664618..a795115f7ac 100644 --- a/include/xrpl/tx/transactors/Clawback.h +++ b/include/xrpl/tx/transactors/Clawback.h @@ -13,9 +13,6 @@ class Clawback : public Transactor { } - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - static NotTEC preflight(PreflightContext const& ctx); diff --git a/include/xrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.h b/include/xrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.h index c4a448032ac..416708565aa 100644 --- a/include/xrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.h +++ b/include/xrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.h @@ -13,9 +13,6 @@ class MPTokenIssuanceDestroy : public Transactor { } - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - static NotTEC preflight(PreflightContext const& ctx); diff --git a/include/xrpl/tx/transactors/NFT/NFTokenAcceptOffer.h b/include/xrpl/tx/transactors/NFT/NFTokenAcceptOffer.h index d876a703621..60962fc9ca2 100644 --- a/include/xrpl/tx/transactors/NFT/NFTokenAcceptOffer.h +++ b/include/xrpl/tx/transactors/NFT/NFTokenAcceptOffer.h @@ -26,9 +26,6 @@ class NFTokenAcceptOffer : public Transactor { } - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - static NotTEC preflight(PreflightContext const& ctx); diff --git a/include/xrpl/tx/transactors/NFT/NFTokenCancelOffer.h b/include/xrpl/tx/transactors/NFT/NFTokenCancelOffer.h index bb8cd4c2164..6d60a0bebd6 100644 --- a/include/xrpl/tx/transactors/NFT/NFTokenCancelOffer.h +++ b/include/xrpl/tx/transactors/NFT/NFTokenCancelOffer.h @@ -13,9 +13,6 @@ class NFTokenCancelOffer : public Transactor { } - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - static NotTEC preflight(PreflightContext const& ctx); diff --git a/src/libxrpl/protocol/LedgerFormats.cpp b/src/libxrpl/protocol/LedgerFormats.cpp index 2056cfab7be..30725f44a99 100644 --- a/src/libxrpl/protocol/LedgerFormats.cpp +++ b/src/libxrpl/protocol/LedgerFormats.cpp @@ -3,19 +3,23 @@ #include #include -#include +#include namespace xrpl { -LedgerFormats::LedgerFormats() +std::vector const& +LedgerFormats::getCommonFields() { - // Fields shared by all ledger formats: - static std::initializer_list const commonFields{ + static auto const commonFields = std::vector{ {sfLedgerIndex, soeOPTIONAL}, {sfLedgerEntryType, soeREQUIRED}, {sfFlags, soeREQUIRED}, }; + return commonFields; +} +LedgerFormats::LedgerFormats() +{ #pragma push_macro("UNWRAP") #undef UNWRAP #pragma push_macro("LEDGER_ENTRY") @@ -23,7 +27,7 @@ LedgerFormats::LedgerFormats() #define UNWRAP(...) __VA_ARGS__ #define LEDGER_ENTRY(tag, value, name, rpcName, fields) \ - add(jss::name, tag, UNWRAP fields, commonFields); + add(jss::name, tag, UNWRAP fields, getCommonFields()); #include diff --git a/src/libxrpl/protocol/SOTemplate.cpp b/src/libxrpl/protocol/SOTemplate.cpp index 46edf987109..b90bff6192e 100644 --- a/src/libxrpl/protocol/SOTemplate.cpp +++ b/src/libxrpl/protocol/SOTemplate.cpp @@ -2,23 +2,32 @@ #include #include +#include #include #include +#include #include +#include namespace xrpl { SOTemplate::SOTemplate( std::initializer_list uniqueFields, std::initializer_list commonFields) + : SOTemplate(std::vector(uniqueFields), std::vector(commonFields)) +{ +} + +SOTemplate::SOTemplate(std::vector uniqueFields, std::vector commonFields) : indices_(SField::getNumFields() + 1, -1) // Unmapped indices == -1 { // Add all SOElements. - elements_.reserve(uniqueFields.size() + commonFields.size()); - elements_.assign(uniqueFields); - elements_.insert(elements_.end(), commonFields); + // + elements_ = std::move(uniqueFields); + std::ranges::move(commonFields, std::back_inserter(elements_)); // Validate and index elements_. + // for (std::size_t i = 0; i < elements_.size(); ++i) { SField const& sField{elements_[i].sField()}; diff --git a/src/libxrpl/protocol/TxFormats.cpp b/src/libxrpl/protocol/TxFormats.cpp index 00a560de1b1..4492ae271b7 100644 --- a/src/libxrpl/protocol/TxFormats.cpp +++ b/src/libxrpl/protocol/TxFormats.cpp @@ -3,14 +3,14 @@ #include #include -#include +#include namespace xrpl { -TxFormats::TxFormats() +std::vector const& +TxFormats::getCommonFields() { - // Fields shared by all txFormats: - static std::initializer_list const commonFields{ + static auto const commonFields = std::vector{ {sfTransactionType, soeREQUIRED}, {sfFlags, soeOPTIONAL}, {sfSourceTag, soeOPTIONAL}, @@ -29,7 +29,11 @@ TxFormats::TxFormats() {sfNetworkID, soeOPTIONAL}, {sfDelegate, soeOPTIONAL}, }; + return commonFields; +} +TxFormats::TxFormats() +{ #pragma push_macro("UNWRAP") #undef UNWRAP #pragma push_macro("TRANSACTION") @@ -37,7 +41,7 @@ TxFormats::TxFormats() #define UNWRAP(...) __VA_ARGS__ #define TRANSACTION(tag, value, name, delegable, amendment, privileges, fields) \ - add(jss::name, tag, UNWRAP fields, commonFields); + add(jss::name, tag, UNWRAP fields, getCommonFields()); #include diff --git a/src/libxrpl/tx/transactors/AMM/AMMDeposit.cpp b/src/libxrpl/tx/transactors/AMM/AMMDeposit.cpp index ba482958516..dad1bc536a9 100644 --- a/src/libxrpl/tx/transactors/AMM/AMMDeposit.cpp +++ b/src/libxrpl/tx/transactors/AMM/AMMDeposit.cpp @@ -19,7 +19,7 @@ std::uint32_t AMMDeposit::getFlagsMask(PreflightContext const& ctx) { - return tfDepositMask; + return tfAMMDepositMask; } NotTEC diff --git a/src/libxrpl/tx/transactors/AMM/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/AMM/AMMWithdraw.cpp index 3c974ee4a4b..f58dbaefc6b 100644 --- a/src/libxrpl/tx/transactors/AMM/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/AMM/AMMWithdraw.cpp @@ -17,7 +17,7 @@ AMMWithdraw::checkExtraFeatures(PreflightContext const& ctx) std::uint32_t AMMWithdraw::getFlagsMask(PreflightContext const& ctx) { - return tfWithdrawMask; + return tfAMMWithdrawMask; } NotTEC diff --git a/src/libxrpl/tx/transactors/Change.cpp b/src/libxrpl/tx/transactors/Change.cpp index 315aeb89bdf..9ae65047187 100644 --- a/src/libxrpl/tx/transactors/Change.cpp +++ b/src/libxrpl/tx/transactors/Change.cpp @@ -16,11 +16,11 @@ NotTEC Transactor::invokePreflight(PreflightContext const& ctx) { // 0 means "Allow any flags" - // The check for tfChangeMask is gated by LendingProtocol because that - // feature introduced this parameter, and it's not worth adding another + // The check for tfEnableAmendmentMask is gated by LendingProtocol because + // that feature introduced this parameter, and it's not worth adding another // amendment just for this. if (auto const ret = - preflight0(ctx, ctx.rules.enabled(featureLendingProtocol) ? tfChangeMask : 0)) + preflight0(ctx, ctx.rules.enabled(featureLendingProtocol) ? tfEnableAmendmentMask : 0)) return ret; auto account = ctx.tx.getAccountID(sfAccount); diff --git a/src/libxrpl/tx/transactors/Clawback.cpp b/src/libxrpl/tx/transactors/Clawback.cpp index ac13cc10daa..789017e3ecb 100644 --- a/src/libxrpl/tx/transactors/Clawback.cpp +++ b/src/libxrpl/tx/transactors/Clawback.cpp @@ -54,12 +54,6 @@ preflightHelper(PreflightContext const& ctx) return tesSUCCESS; } -std::uint32_t -Clawback::getFlagsMask(PreflightContext const& ctx) -{ - return tfClawbackMask; -} - NotTEC Clawback::preflight(PreflightContext const& ctx) { diff --git a/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.cpp b/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.cpp index acdd004bae7..4b4d7c53f54 100644 --- a/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.cpp +++ b/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.cpp @@ -5,12 +5,6 @@ namespace xrpl { -std::uint32_t -MPTokenIssuanceDestroy::getFlagsMask(PreflightContext const& ctx) -{ - return tfMPTokenIssuanceDestroyMask; -} - NotTEC MPTokenIssuanceDestroy::preflight(PreflightContext const& ctx) { diff --git a/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceSet.cpp b/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceSet.cpp index bacd4585d7b..2ae6bde083d 100644 --- a/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceSet.cpp +++ b/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceSet.cpp @@ -130,7 +130,7 @@ MPTokenIssuanceSet::checkPermission(ReadView const& view, STTx const& tx) // this is added in case more flags will be added for MPTokenIssuanceSet // in the future. Currently unreachable. - if (txFlags & tfMPTokenIssuanceSetPermissionMask) + if (txFlags & tfMPTokenIssuanceSetMask) return terNO_DELEGATE_PERMISSION; // LCOV_EXCL_LINE std::unordered_set granularPermissions; diff --git a/src/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp b/src/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp index 45668d4273f..90a28a2e7c6 100644 --- a/src/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp +++ b/src/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp @@ -7,12 +7,6 @@ namespace xrpl { -std::uint32_t -NFTokenAcceptOffer::getFlagsMask(PreflightContext const& ctx) -{ - return tfNFTokenAcceptOfferMask; -} - NotTEC NFTokenAcceptOffer::preflight(PreflightContext const& ctx) { diff --git a/src/libxrpl/tx/transactors/NFT/NFTokenCancelOffer.cpp b/src/libxrpl/tx/transactors/NFT/NFTokenCancelOffer.cpp index 9a3e36b7c88..ce2cdf83c51 100644 --- a/src/libxrpl/tx/transactors/NFT/NFTokenCancelOffer.cpp +++ b/src/libxrpl/tx/transactors/NFT/NFTokenCancelOffer.cpp @@ -8,12 +8,6 @@ namespace xrpl { -std::uint32_t -NFTokenCancelOffer::getFlagsMask(PreflightContext const& ctx) -{ - return tfNFTokenCancelOfferMask; -} - NotTEC NFTokenCancelOffer::preflight(PreflightContext const& ctx) { diff --git a/src/libxrpl/tx/transactors/NFT/NFTokenMint.cpp b/src/libxrpl/tx/transactors/NFT/NFTokenMint.cpp index d8c063571b2..a32a659bbd8 100644 --- a/src/libxrpl/tx/transactors/NFT/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/NFT/NFTokenMint.cpp @@ -48,9 +48,8 @@ NFTokenMint::getFlagsMask(PreflightContext const& ctx) // tfTrustLine flag as a way to prevent the attack. But until the // amendment passes we still need to keep the old behavior available. std::uint32_t const nfTokenMintMask = ctx.rules.enabled(fixRemoveNFTokenAutoTrustLine) - // if featureDynamicNFT enabled then new flag allowing mutable URI - // available - ? ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintMaskWithMutable : tfNFTokenMintMask + // if featureDynamicNFT enabled then new flag allowing mutable URI available + ? ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintMask : tfNFTokenMintMaskWithoutMutable : ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintOldMaskWithMutable : tfNFTokenMintOldMask; diff --git a/src/libxrpl/tx/transactors/PayChan.cpp b/src/libxrpl/tx/transactors/PayChan.cpp index 3b29169af63..265030bebf9 100644 --- a/src/libxrpl/tx/transactors/PayChan.cpp +++ b/src/libxrpl/tx/transactors/PayChan.cpp @@ -378,7 +378,7 @@ PayChanClaim::checkExtraFeatures(PreflightContext const& ctx) std::uint32_t PayChanClaim::getFlagsMask(PreflightContext const&) { - return tfPayChanClaimMask; + return tfPaymentChannelClaimMask; } NotTEC diff --git a/src/libxrpl/tx/transactors/XChainBridge.cpp b/src/libxrpl/tx/transactors/XChainBridge.cpp index 30fc9f59e14..3473df4a53e 100644 --- a/src/libxrpl/tx/transactors/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/XChainBridge.cpp @@ -1448,7 +1448,7 @@ XChainCreateBridge::doApply() std::uint32_t BridgeModify::getFlagsMask(PreflightContext const& ctx) { - return tfBridgeModifyMask; + return tfXChainModifyBridgeMask; } NotTEC diff --git a/src/test/rpc/AccountSet_test.cpp b/src/test/app/AccountSet_test.cpp similarity index 99% rename from src/test/rpc/AccountSet_test.cpp rename to src/test/app/AccountSet_test.cpp index d342bf366aa..87c6474be2d 100644 --- a/src/test/rpc/AccountSet_test.cpp +++ b/src/test/app/AccountSet_test.cpp @@ -575,6 +575,6 @@ class AccountSet_test : public beast::unit_test::suite } }; -BEAST_DEFINE_TESTSUITE_PRIO(AccountSet, rpc, xrpl, 1); +BEAST_DEFINE_TESTSUITE_PRIO(AccountSet, app, xrpl, 1); } // namespace xrpl diff --git a/src/test/app/Batch_test.cpp b/src/test/app/Batch_test.cpp index bd1d701381a..f429299f019 100644 --- a/src/test/app/Batch_test.cpp +++ b/src/test/app/Batch_test.cpp @@ -4355,6 +4355,7 @@ class Batch_test : public beast::unit_test::suite run() override { using namespace test::jtx; + auto const sa = testable_amendments(); testWithFeats(sa - fixBatchInnerSigs); testWithFeats(sa); diff --git a/src/test/rpc/ServerDefinitions_test.cpp b/src/test/rpc/ServerDefinitions_test.cpp index 69533939cd5..a2c45cfa650 100644 --- a/src/test/rpc/ServerDefinitions_test.cpp +++ b/src/test/rpc/ServerDefinitions_test.cpp @@ -1,6 +1,9 @@ #include #include +#include +#include +#include #include namespace xrpl { @@ -81,43 +84,371 @@ class ServerDefinitions_test : public beast::unit_test::suite BEAST_EXPECT(types["Hash384"].asUInt() == 22); BEAST_EXPECT(types["Hash512"].asUInt() == 23); } - } - // test providing the same hash - { - Env env(*this); - auto const firstResult = env.rpc("server_definitions"); - auto const hash = firstResult[jss::result][jss::hash].asString(); - auto const hashParam = std::string("{ ") + "\"hash\": \"" + hash + "\"}"; + // test the properties of the LEDGER_ENTRY_FLAGS section + { + BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_FLAGS)); + Json::Value const& leFlags = result[jss::result][jss::LEDGER_ENTRY_FLAGS]; - auto const result = env.rpc("json", "server_definitions", hashParam); - BEAST_EXPECT(!result[jss::result].isMember(jss::error)); - BEAST_EXPECT(result[jss::result][jss::status] == "success"); - BEAST_EXPECT(!result[jss::result].isMember(jss::FIELDS)); - BEAST_EXPECT(!result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES)); - BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_RESULTS)); - BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_TYPES)); - BEAST_EXPECT(!result[jss::result].isMember(jss::TYPES)); - BEAST_EXPECT(result[jss::result].isMember(jss::hash)); - } + // sanity test the mapped value of a few arbitrarily chosen flags + BEAST_EXPECT(leFlags["AccountRoot"]["lsfDisallowXRP"] == 0x00080000); + BEAST_EXPECT(leFlags["AccountRoot"]["lsfDepositAuth"] == 0x01000000); + BEAST_EXPECT(leFlags["AccountRoot"]["lsfAllowTrustLineClawback"] == 0x80000000); - // test providing a different hash - { - Env env(*this); - std::string const hash = - "54296160385A27154BFA70A239DD8E8FD4CC2DB7BA32D970BA3A5B132CF749" - "D1"; - auto const hashParam = std::string("{ ") + "\"hash\": \"" + hash + "\"}"; + BEAST_EXPECT(leFlags["RippleState"]["lsfHighFreeze"] == 0x00800000); + BEAST_EXPECT(leFlags["RippleState"]["lsfAMMNode"] == 0x01000000); - auto const result = env.rpc("json", "server_definitions", hashParam); - BEAST_EXPECT(!result[jss::result].isMember(jss::error)); - BEAST_EXPECT(result[jss::result][jss::status] == "success"); - BEAST_EXPECT(result[jss::result].isMember(jss::FIELDS)); - BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES)); - BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_RESULTS)); - BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_TYPES)); - BEAST_EXPECT(result[jss::result].isMember(jss::TYPES)); - BEAST_EXPECT(result[jss::result].isMember(jss::hash)); + BEAST_EXPECT(leFlags["DirNode"]["lsfNFTokenBuyOffers"] == 0x00000001); + BEAST_EXPECT(leFlags["MPTokenIssuance"]["lsfMPTCanTrade"] == 0x00000010); + BEAST_EXPECT(leFlags["Credential"]["lsfAccepted"] == 0x00010000); + BEAST_EXPECT(leFlags["Loan"]["lsfLoanImpaired"] == 0x00020000); + BEAST_EXPECT(leFlags["Vault"]["lsfVaultPrivate"] == 0x00010000); + BEAST_EXPECT(leFlags["MPToken"]["lsfMPTAuthorized"] == 0x00000002); + } + + // validate the correctness of few chosen transaction flags + { + BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_FLAGS)); + Json::Value const& txFlags = result[jss::result][jss::TRANSACTION_FLAGS]; + + BEAST_EXPECT(txFlags["universal"]["tfFullyCanonicalSig"] == 0x80000000); + BEAST_EXPECT(txFlags["universal"]["tfInnerBatchTxn"] == 0x40000000); + + BEAST_EXPECT(txFlags["AccountSet"]["tfRequireAuth"] == 0x00040000); + BEAST_EXPECT(txFlags["AccountSet"]["tfAllowXRP"] == 0x00200000); + + BEAST_EXPECT(txFlags["MPTokenIssuanceSet"]["tfMPTLock"] == 0x00000001); + BEAST_EXPECT(txFlags["MPTokenIssuanceSet"]["tfMPTUnlock"] == 0x00000002); + + BEAST_EXPECT(txFlags["AMMDeposit"]["tfLPToken"] == 0x00010000); + BEAST_EXPECT(txFlags["AMMDeposit"]["tfLimitLPToken"] == 0x00400000); + } + + // validate the correctness of the AccountSpecificFlags section + { + BEAST_EXPECT(result[jss::result].isMember(jss::ACCOUNT_SET_FLAGS)); + Json::Value const& asFlags = result[jss::result][jss::ACCOUNT_SET_FLAGS]; + + BEAST_EXPECT(asFlags["asfDisallowXRP"] == 3); + BEAST_EXPECT(asFlags["asfGlobalFreeze"] == 7); + BEAST_EXPECT(asFlags["asfDisallowIncomingNFTokenOffer"] == 12); + BEAST_EXPECT(asFlags["asfDisallowIncomingTrustline"] == 15); + } + + // test the response fields of the TRANSACTION_FORMATS section + { + BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_FORMATS)); + Json::Value const& txnFormats = result[jss::result][jss::TRANSACTION_FORMATS]; + + // first validate the contents of "common" + { + BEAST_EXPECT(txnFormats.isMember("common")); + Json::Value const& section = txnFormats["common"]; + + BEAST_EXPECT(section[0u][jss::name] == "TransactionType"); + BEAST_EXPECT(section[0u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[1u][jss::name] == "Flags"); + BEAST_EXPECT(section[1u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[2u][jss::name] == "SourceTag"); + BEAST_EXPECT(section[2u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[3u][jss::name] == "Account"); + BEAST_EXPECT(section[3u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[4u][jss::name] == "Sequence"); + BEAST_EXPECT(section[4u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[5u][jss::name] == "PreviousTxnID"); + BEAST_EXPECT(section[5u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[6u][jss::name] == "LastLedgerSequence"); + BEAST_EXPECT(section[6u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[7u][jss::name] == "AccountTxnID"); + BEAST_EXPECT(section[7u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[8u][jss::name] == "Fee"); + BEAST_EXPECT(section[8u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[9u][jss::name] == "OperationLimit"); + BEAST_EXPECT(section[9u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[10u][jss::name] == "Memos"); + BEAST_EXPECT(section[10u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[11u][jss::name] == "SigningPubKey"); + BEAST_EXPECT(section[11u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[12u][jss::name] == "TicketSequence"); + BEAST_EXPECT(section[12u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[13u][jss::name] == "TxnSignature"); + BEAST_EXPECT(section[13u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[14u][jss::name] == "Signers"); + BEAST_EXPECT(section[14u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[15u][jss::name] == "NetworkID"); + BEAST_EXPECT(section[15u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[16u][jss::name] == "Delegate"); + BEAST_EXPECT(section[16u][jss::optionality] == soeOPTIONAL); + } + + // validate the contents of four arbitrarily selected transactions validate the + // format of the OracleSet transaction + { + BEAST_EXPECT(txnFormats.isMember("OracleSet")); + Json::Value const& section = txnFormats["OracleSet"]; + + BEAST_EXPECT(section[0u][jss::name] == "OracleDocumentID"); + BEAST_EXPECT(section[0u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[1u][jss::name] == "Provider"); + BEAST_EXPECT(section[1u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[2u][jss::name] == "URI"); + BEAST_EXPECT(section[2u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[3u][jss::name] == "AssetClass"); + BEAST_EXPECT(section[3u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[4u][jss::name] == "LastUpdateTime"); + BEAST_EXPECT(section[4u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[5u][jss::name] == "PriceDataSeries"); + BEAST_EXPECT(section[5u][jss::optionality] == soeREQUIRED); + } + + // validate the format of the PermissionedDomainDelete transaction + { + BEAST_EXPECT(txnFormats.isMember("PermissionedDomainDelete")); + Json::Value const& section = txnFormats["PermissionedDomainDelete"]; + + BEAST_EXPECT(section[0u][jss::name] == "DomainID"); + BEAST_EXPECT(section[0u][jss::optionality] == soeREQUIRED); + } + + // validate the format of the Clawback transaction + { + BEAST_EXPECT(txnFormats.isMember("Clawback")); + Json::Value const& section = txnFormats["Clawback"]; + + BEAST_EXPECT(section[0u][jss::name] == "Amount"); + BEAST_EXPECT(section[0u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[1u][jss::name] == "Holder"); + BEAST_EXPECT(section[1u][jss::optionality] == soeOPTIONAL); + } + + // validate the format of the SetFee transaction + { + BEAST_EXPECT(txnFormats.isMember("SetFee")); + Json::Value const& section = txnFormats["SetFee"]; + + BEAST_EXPECT(section[0u][jss::name] == "LedgerSequence"); + BEAST_EXPECT(section[0u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[1u][jss::name] == "BaseFee"); + BEAST_EXPECT(section[1u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[2u][jss::name] == "ReferenceFeeUnits"); + BEAST_EXPECT(section[2u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[3u][jss::name] == "ReserveBase"); + BEAST_EXPECT(section[3u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[4u][jss::name] == "ReserveIncrement"); + BEAST_EXPECT(section[4u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[5u][jss::name] == "BaseFeeDrops"); + BEAST_EXPECT(section[5u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[6u][jss::name] == "ReserveBaseDrops"); + BEAST_EXPECT(section[6u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[7u][jss::name] == "ReserveIncrementDrops"); + BEAST_EXPECT(section[7u][jss::optionality] == soeOPTIONAL); + } + } + + // test the properties of the LEDGER_ENTRY_FORMATS section in server_definitions + // response + { + BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_FORMATS)); + + // Note: For the purposes of software maintenance, this test does not exhaustively + // validate all the LEDGER_ENTRY_FORMATS + + // check "common" first + { + Json::Value const& observedCommonLedgerEntry = + result[jss::result][jss::LEDGER_ENTRY_FORMATS]["common"]; + + BEAST_EXPECT(observedCommonLedgerEntry[0u][jss::name] == "LedgerIndex"); + BEAST_EXPECT(observedCommonLedgerEntry[0u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedCommonLedgerEntry[1u][jss::name] == "LedgerEntryType"); + BEAST_EXPECT(observedCommonLedgerEntry[1u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(observedCommonLedgerEntry[2u][jss::name] == "Flags"); + BEAST_EXPECT(observedCommonLedgerEntry[2u][jss::optionality] == soeREQUIRED); + } + + // test the contents of an arbitrary ledger-entry (DID) + { + Json::Value const& observedDIDLedgerEntry = + result[jss::result][jss::LEDGER_ENTRY_FORMATS]["DID"]; + + BEAST_EXPECT(observedDIDLedgerEntry[0u][jss::name] == "Account"); + BEAST_EXPECT(observedDIDLedgerEntry[0u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(observedDIDLedgerEntry[1u][jss::name] == "DIDDocument"); + BEAST_EXPECT(observedDIDLedgerEntry[1u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedDIDLedgerEntry[2u][jss::name] == "URI"); + BEAST_EXPECT(observedDIDLedgerEntry[2u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedDIDLedgerEntry[3u][jss::name] == "Data"); + BEAST_EXPECT(observedDIDLedgerEntry[3u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedDIDLedgerEntry[4u][jss::name] == "OwnerNode"); + BEAST_EXPECT(observedDIDLedgerEntry[4u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(observedDIDLedgerEntry[5u][jss::name] == "PreviousTxnID"); + BEAST_EXPECT(observedDIDLedgerEntry[5u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(observedDIDLedgerEntry[6u][jss::name] == "PreviousTxnLgrSeq"); + BEAST_EXPECT(observedDIDLedgerEntry[6u][jss::optionality] == soeREQUIRED); + } + + // test the contents of an arbitrary ledger-entry (NegativeUNL) + { + Json::Value const& observedNunlLedgerEntry = + result[jss::result][jss::LEDGER_ENTRY_FORMATS]["NegativeUNL"]; + + BEAST_EXPECT(observedNunlLedgerEntry[0u][jss::name] == "DisabledValidators"); + BEAST_EXPECT(observedNunlLedgerEntry[0u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedNunlLedgerEntry[1u][jss::name] == "ValidatorToDisable"); + BEAST_EXPECT(observedNunlLedgerEntry[1u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedNunlLedgerEntry[2u][jss::name] == "ValidatorToReEnable"); + BEAST_EXPECT(observedNunlLedgerEntry[2u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedNunlLedgerEntry[3u][jss::name] == "PreviousTxnID"); + BEAST_EXPECT(observedNunlLedgerEntry[3u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedNunlLedgerEntry[4u][jss::name] == "PreviousTxnLgrSeq"); + BEAST_EXPECT(observedNunlLedgerEntry[4u][jss::optionality] == soeOPTIONAL); + } + } + + // Exhaustive test: verify all transaction flags from getAllTxFlags() appear in the + // output + { + Json::Value const& txFlags = result[jss::result][jss::TRANSACTION_FLAGS]; + + for (auto const& [txName, flagMap] : getAllTxFlags()) + { + BEAST_EXPECT(txFlags.isMember(txName)); + if (txFlags.isMember(txName)) + { + for (auto const& [flagName, flagValue] : flagMap) + { + BEAST_EXPECT(txFlags[txName].isMember(flagName)); + if (txFlags[txName].isMember(flagName)) + { + BEAST_EXPECT(txFlags[txName][flagName].asUInt() == flagValue); + } + } + } + } + } + + // Exhaustive test: verify all ledger entry flags from getAllLedgerFlags() appear in the + // output + { + Json::Value const& leFlags = result[jss::result][jss::LEDGER_ENTRY_FLAGS]; + + for (auto const& [ledgerType, flagMap] : getAllLedgerFlags()) + { + BEAST_EXPECT(leFlags.isMember(ledgerType)); + if (leFlags.isMember(ledgerType)) + { + for (auto const& [flagName, flagValue] : flagMap) + { + BEAST_EXPECT(leFlags[ledgerType].isMember(flagName)); + if (leFlags[ledgerType].isMember(flagName)) + { + BEAST_EXPECT(leFlags[ledgerType][flagName].asUInt() == flagValue); + } + } + } + } + } + + // Exhaustive test: verify all AccountSet flags from getAsfFlagMap() appear in the + // output + { + Json::Value const& asFlags = result[jss::result][jss::ACCOUNT_SET_FLAGS]; + + for (auto const& [flagName, flagValue] : getAsfFlagMap()) + { + BEAST_EXPECT(asFlags.isMember(flagName)); + if (asFlags.isMember(flagName)) + { + BEAST_EXPECT(asFlags[flagName].asInt() == flagValue); + } + } + } + + // test providing the same hash + { + Env env(*this); + auto const firstResult = env.rpc("server_definitions"); + auto const hash = firstResult[jss::result][jss::hash].asString(); + auto const hashParam = std::string("{ ") + "\"hash\": \"" + hash + "\"}"; + + auto const result = env.rpc("json", "server_definitions", hashParam); + BEAST_EXPECT(!result[jss::result].isMember(jss::error)); + BEAST_EXPECT(result[jss::result][jss::status] == "success"); + BEAST_EXPECT(!result[jss::result].isMember(jss::FIELDS)); + BEAST_EXPECT(!result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES)); + BEAST_EXPECT(!result[jss::result].isMember(jss::LEDGER_ENTRY_FLAGS)); + BEAST_EXPECT(!result[jss::result].isMember(jss::LEDGER_ENTRY_FORMATS)); + BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_RESULTS)); + BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_TYPES)); + BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_FLAGS)); + BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_FORMATS)); + BEAST_EXPECT(!result[jss::result].isMember(jss::TYPES)); + BEAST_EXPECT(result[jss::result].isMember(jss::hash)); + } + + // test providing a different hash + { + Env env(*this); + std::string const hash = + "54296160385A27154BFA70A239DD8E8FD4CC2DB7BA32D970BA3A5B132CF749" + "D1"; + auto const hashParam = std::string("{ ") + "\"hash\": \"" + hash + "\"}"; + + auto const result = env.rpc("json", "server_definitions", hashParam); + BEAST_EXPECT(!result[jss::result].isMember(jss::error)); + BEAST_EXPECT(result[jss::result][jss::status] == "success"); + BEAST_EXPECT(result[jss::result].isMember(jss::FIELDS)); + BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES)); + BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_FLAGS)); + BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_FORMATS)); + BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_RESULTS)); + BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_TYPES)); + BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_FLAGS)); + BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_FORMATS)); + BEAST_EXPECT(result[jss::result].isMember(jss::TYPES)); + BEAST_EXPECT(result[jss::result].isMember(jss::hash)); + } } } diff --git a/src/xrpld/rpc/handlers/ServerDefinitions.cpp b/src/xrpld/rpc/handlers/ServerDefinitions.cpp index ea1912f66e9..e153065ea9d 100644 --- a/src/xrpld/rpc/handlers/ServerDefinitions.cpp +++ b/src/xrpld/rpc/handlers/ServerDefinitions.cpp @@ -6,12 +6,15 @@ #include #include #include +#include #include #include #include #include +#include +#include #include namespace xrpl { @@ -47,13 +50,14 @@ class ServerDefinitions std::string ServerDefinitions::translate(std::string const& inp) { - auto replace = [&](char const* oldStr, char const* newStr) -> std::string { + auto replace = [&](std::string_view oldStr, std::string_view newStr) -> std::string { std::string out = inp; boost::replace_all(out, oldStr, newStr); return out; }; - auto contains = [&](char const* s) -> bool { return inp.find(s) != std::string::npos; }; + // TODO: use string::contains with C++23 + auto contains = [&](std::string_view s) -> bool { return inp.find(s) != std::string::npos; }; if (contains("UINT")) { @@ -64,7 +68,7 @@ ServerDefinitions::translate(std::string const& inp) return replace("UINT", "UInt"); } - std::unordered_map replacements{ + static std::unordered_map const replacements{ {"OBJECT", "STObject"}, {"ARRAY", "STArray"}, {"ACCOUNT", "AccountID"}, @@ -77,7 +81,7 @@ ServerDefinitions::translate(std::string const& inp) if (auto const& it = replacements.find(inp); it != replacements.end()) { - return it->second; + return std::string(it->second); } std::string out; @@ -211,36 +215,35 @@ ServerDefinitions::ServerDefinitions() : defs_{Json::objectValue} defs_[jss::FIELDS][i++] = a; } - for (auto const& [code, f] : xrpl::SField::getKnownCodeToField()) + for (auto const& [code, field] : xrpl::SField::getKnownCodeToField()) { - if (f->fieldName == "") + if (field->fieldName == "") continue; Json::Value innerObj = Json::objectValue; - uint32_t type = f->fieldType; + uint32_t type = field->fieldType; - innerObj[jss::nth] = f->fieldValue; + innerObj[jss::nth] = field->fieldValue; - // whether the field is variable-length encoded - // this means that the length is included before the content + // whether the field is variable-length encoded this means that the length is included + // before the content innerObj[jss::isVLEncoded] = - (type == 7U /* Blob */ || type == 8U /* AccountID */ || - type == 19U /* Vector256 */); + (type == 7U /* Blob */ || type == 8U /* AccountID */ || type == 19U /* Vector256 */); // whether the field is included in serialization innerObj[jss::isSerialized] = - (type < 10000 && f->fieldName != "hash" && - f->fieldName != "index"); /* hash, index, TRANSACTION, - LEDGER_ENTRY, VALIDATION, METADATA */ + (type < 10000 && field->fieldName != "hash" && + field->fieldName != + "index"); // hash, index, TRANSACTION, LEDGER_ENTRY, VALIDATION, METADATA // whether the field is included in serialization when signing - innerObj[jss::isSigningField] = f->shouldInclude(false); + innerObj[jss::isSigningField] = field->shouldInclude(false); innerObj[jss::type] = typeMap[type]; Json::Value innerArray = Json::arrayValue; - innerArray[0U] = f->fieldName; + innerArray[0U] = field->fieldName; innerArray[1U] = innerObj; defs_[jss::FIELDS][i++] = innerArray; @@ -262,6 +265,92 @@ ServerDefinitions::ServerDefinitions() : defs_{Json::objectValue} defs_[jss::TRANSACTION_TYPES][f.getName()] = f.getType(); } + // populate TxFormats + defs_[jss::TRANSACTION_FORMATS] = Json::objectValue; + + defs_[jss::TRANSACTION_FORMATS][jss::common] = Json::arrayValue; + auto txCommonFields = std::set(); + for (auto const& element : TxFormats::getCommonFields()) + { + Json::Value elementObj = Json::objectValue; + elementObj[jss::name] = element.sField().getName(); + elementObj[jss::optionality] = element.style(); + defs_[jss::TRANSACTION_FORMATS][jss::common].append(elementObj); + txCommonFields.insert(element.sField().getName()); + } + + for (auto const& format : TxFormats::getInstance()) + { + auto const& soTemplate = format.getSOTemplate(); + Json::Value templateArray = Json::arrayValue; + for (auto const& element : soTemplate) + { + if (txCommonFields.contains(element.sField().getName())) + continue; // skip common fields, already added + Json::Value elementObj = Json::objectValue; + elementObj[jss::name] = element.sField().getName(); + elementObj[jss::optionality] = element.style(); + templateArray.append(elementObj); + } + defs_[jss::TRANSACTION_FORMATS][format.getName()] = templateArray; + } + + // populate LedgerFormats + defs_[jss::LEDGER_ENTRY_FORMATS] = Json::objectValue; + defs_[jss::LEDGER_ENTRY_FORMATS][jss::common] = Json::arrayValue; + auto ledgerCommonFields = std::set(); + for (auto const& element : LedgerFormats::getCommonFields()) + { + Json::Value elementObj = Json::objectValue; + elementObj[jss::name] = element.sField().getName(); + elementObj[jss::optionality] = element.style(); + defs_[jss::LEDGER_ENTRY_FORMATS][jss::common].append(elementObj); + ledgerCommonFields.insert(element.sField().getName()); + } + for (auto const& format : LedgerFormats::getInstance()) + { + auto const& soTemplate = format.getSOTemplate(); + Json::Value templateArray = Json::arrayValue; + for (auto const& element : soTemplate) + { + if (ledgerCommonFields.contains(element.sField().getName())) + continue; // skip common fields, already added + Json::Value elementObj = Json::objectValue; + elementObj[jss::name] = element.sField().getName(); + elementObj[jss::optionality] = element.style(); + templateArray.append(elementObj); + } + defs_[jss::LEDGER_ENTRY_FORMATS][format.getName()] = templateArray; + } + + defs_[jss::TRANSACTION_FLAGS] = Json::objectValue; + for (auto const& [name, value] : getAllTxFlags()) + { + Json::Value txObj = Json::objectValue; + for (auto const& [flagName, flagValue] : value) + { + txObj[flagName] = flagValue; + } + defs_[jss::TRANSACTION_FLAGS][name] = txObj; + } + + defs_[jss::LEDGER_ENTRY_FLAGS] = Json::objectValue; + for (auto const& [name, value] : getAllLedgerFlags()) + { + Json::Value ledgerObj = Json::objectValue; + for (auto const& [flagName, flagValue] : value) + { + ledgerObj[flagName] = flagValue; + } + defs_[jss::LEDGER_ENTRY_FLAGS][name] = ledgerObj; + } + + defs_[jss::ACCOUNT_SET_FLAGS] = Json::objectValue; + for (auto const& [name, value] : getAsfFlagMap()) + { + defs_[jss::ACCOUNT_SET_FLAGS][name] = value; + } + // generate hash { std::string const out = Json::FastWriter().write(defs_);