Files
rippled/include/xrpl/protocol/LedgerFormats.h
Denis Angell d8febb71bd part 1
2026-05-13 23:01:44 +02:00

391 lines
23 KiB
C++

/** @file
* Authoritative registry for every object type that can live in the XRP Ledger.
*
* Defines three tightly-coupled, protocol-level artifacts:
*
* 1. `LedgerEntryType` — the `uint16_t` wire discriminants stored inside every
* serialized ledger object.
* 2. `LedgerSpecificFlags` / per-object flag accessor functions / `getAllLedgerFlags()`
* — flag bitmasks that modify ledger object behavior, together with Meyer's-singleton
* accessors consumed by the `server_definitions` RPC endpoint.
* 3. `LedgerFormats` — the singleton registry that maps each `LedgerEntryType` to its
* `SOTemplate` (field presence schema).
*
* The `ledger_entries.macro` X-macro file is the single source of truth for all
* per-type data; this header and `LedgerFormats.cpp` each include it with a different
* macro definition to derive the enum and the format registration from the same table.
*
* @warning All numeric values defined here are embedded in serialized ledger objects
* and transmitted over the wire. Changing them without corresponding amendment
* machinery causes a hard fork.
*
* @ingroup protocol
*/
#pragma once
// NOLINTBEGIN(readability-identifier-naming)
#include <xrpl/protocol/KnownFormats.h>
#include <map>
#include <string>
#include <vector>
namespace xrpl {
/** Numeric type identifiers for every object type that can exist in the XRP Ledger.
*
* Each ledger object embeds its `LedgerEntryType` in the serialized form; this allows
* the ledger layer to determine an object's type during iteration and to verify that
* a hash lookup returned the expected kind of object.
*
* The concrete values are generated by the `ledger_entries.macro` X-macro, which is
* the single source of truth for (tag, value, name, fields) tuples across the enum,
* the `LedgerFormats` constructor, and any auto-generated protocol bindings.
*
* Beyond the macro-generated members, two sentinel pseudo-types (`ltANY`, `ltCHILD`)
* are defined manually for use in keylet lookups where the precise object type is
* unknown or irrelevant.
*
* @warning These values are stored in serialized ledger objects and are part of the
* protocol. Changing them without special amendment handling causes a hard fork.
*
* @note Values outside the known range may be used internally, but passing them to
* ledger-object APIs will result in an invariant failure.
*
* @note When retiring an entry type, mark its enumerator `[[deprecated]]` rather than
* removing it. Removal would free the numeric slot for accidental reuse.
*
* @todo C++ enums cannot enforce uniqueness of values at compile time; duplicate IDs
* can silently coexist. If the language gains that capability it should be used here.
*
* @ingroup protocol
*/
// Protocol-critical, hundreds of usages
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
enum LedgerEntryType : std::uint16_t {
#pragma push_macro("LEDGER_ENTRY")
#undef LEDGER_ENTRY
#define LEDGER_ENTRY(tag, value, ...) tag = value,
#include <xrpl/protocol/detail/ledger_entries.macro>
#undef LEDGER_ENTRY
#pragma pop_macro("LEDGER_ENTRY")
//---------------------------------------------------------------------------
/** 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.
Objects with this special type cannot be created or stored on the ledger.
\sa keylet::unchecked
*/
ltANY = 0,
/** 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.
Objects with this special type cannot be created or stored on the ledger.
\sa keylet::child
*/
ltCHILD = 0x1CD2,
//---------------------------------------------------------------------------
/** A legacy, deprecated type.
\deprecated **This object type is not supported and should not be used.**
Support for this type of object was never implemented.
No objects of this type were ever created.
*/
ltNICKNAME [[deprecated("This object type is not supported and should not be used.")]] = 0x006e,
/** A legacy, deprecated type.
\deprecated **This object type is not supported and should not be used.**
Support for this type of object was never implemented.
No objects of this type were ever created.
*/
ltCONTRACT [[deprecated("This object type is not supported and should not be used.")]] = 0x0063,
/** A legacy, deprecated type.
\deprecated **This object type is not supported and should not be used.**
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,
};
/** Flat enum of all per-object flag bitmasks across every ledger entry type.
*
* Each enumerator is a named bit constant (e.g. `lsfRequireDestTag`, `lsfGlobalFreeze`)
* that modifies the behavior of a specific ledger object type. The constants are
* generated via the `XMACRO` / `TO_VALUE` pass below, which strips object-type grouping
* and collects every flag name and value into a single flat enum.
*
* @note `LSF_FLAG2` is used when the same bit value appears in more than one object
* type (currently `lsfMPTLocked = 0x00000001` shared between `MPTokenIssuance` and
* `MPToken`). The second occurrence is silently omitted from this enum via the
* `NULL_OUTPUT` helper to avoid a duplicate-enumerator warning, while still appearing
* in the per-object flag maps returned by the getter functions below.
*
* @note Most object types use flag bits starting at `0x00010000`, reserving the low 16
* bits for future use. `DirNode`, `NFTokenOffer`, and the MPToken family deviate
* from this convention and use the low-order bits — a legacy of their original
* feature designs.
*
* @warning These values are stored in serialized ledger objects and form part of the
* protocol. Changing them without amendment machinery causes a hard fork.
*
* @ingroup protocol
*/
#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) \
LSF_FLAG(lsfMPTAMM, 0x00000004)) \
\
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)
// Bitwise flag enum
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
enum LedgerSpecificFlags : std::uint32_t { XMACRO(NULL_NAME, TO_VALUE, NULL_OUTPUT) };
/** Map from flag name string to its `uint32_t` bitmask value for a single ledger object type.
*
* Each entry has the form `{"lsfFlagName", 0xXXXXXXXX}`. Per-object maps are returned
* by the `get<ObjectType>Flags()` inline functions generated below and are aggregated by
* `getAllLedgerFlags()` for the `server_definitions` RPC response.
*/
using LedgerFlagMap = std::map<std::string, std::uint32_t>;
// Per-object flag getter functions: getAccountRootFlags(), getOfferFlags(), etc.
// Each returns a const LedgerFlagMap& initialized once via Meyer's singleton.
// See getAllLedgerFlags() for the aggregating accessor.
#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)
/** Return the flags for all ledger object types, keyed by object type name.
*
* Aggregates every per-object `LedgerFlagMap` (produced by the `get<ObjectType>Flags()`
* inline functions above) into a single vector, where each element is a pair of
* `(object-type-name, flag-map)`. The vector is initialized once via Meyer's singleton.
*
* This function is the sole data source for the `server_definitions` RPC endpoint,
* which exposes the complete ledger flag catalogue to external API consumers.
*
* @return A stable `const` reference to the process-wide flag catalogue.
*/
#define ALL_LEDGER_FLAGS(name, values) {#name, get##name##Flags()},
inline std::vector<std::pair<std::string, LedgerFlagMap>> const&
getAllLedgerFlags()
{
static std::vector<std::pair<std::string, LedgerFlagMap>> 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")
//------------------------------------------------------------------------------
/** Singleton registry mapping every `LedgerEntryType` to its canonical field schema.
*
* Inherits from `KnownFormats<LedgerEntryType, LedgerFormats>` (CRTP), which provides
* O(log n) lookup by type and by name, duplicate-registration detection, and stable
* `Item` pointer identity.
*
* The registry is populated once, during static initialization, via the private
* constructor's X-macro pass over `ledger_entries.macro`. Every registered entry
* receives an `SOTemplate` built from its type-specific fields plus the three
* common fields returned by `getCommonFields()` (`sfLedgerIndex`, `sfLedgerEntryType`,
* `sfFlags`).
*
* Callers in the serialization, deserialization, and invariant-checking layers
* access the registry through `getInstance()` to look up schemas by type.
*
* @see KnownFormats, LedgerEntryType, SOTemplate
* @ingroup protocol
*/
class LedgerFormats : public KnownFormats<LedgerEntryType, LedgerFormats>
{
private:
/** Populate the registry with all known ledger entry formats.
*
* Uses an X-macro pass over `ledger_entries.macro`, registering each entry type
* by calling `KnownFormats::add()` with the entry's name, `LedgerEntryType`
* discriminant, type-specific fields, and the common fields from `getCommonFields()`.
*
* If `ledger_entries.macro` contains a duplicate numeric type ID, `add()` calls
* `logicError()` (process abort) during static initialization rather than silently
* corrupting the registry.
*/
LedgerFormats();
public:
/** Return the process-wide `LedgerFormats` singleton.
*
* Uses a function-local static (Meyer's singleton) for thread-safe, once-only
* initialization guaranteed by C++11. The first call constructs the registry and
* registers every known ledger entry type; subsequent calls return the same instance.
*
* @return A `const` reference to the global `LedgerFormats` instance.
*/
static LedgerFormats const&
getInstance();
/** Return the three fields that every ledger entry must carry.
*
* The common fields are:
* - `sfLedgerIndex` (`soeOPTIONAL`) — key of the entry in the SHAMap.
* - `sfLedgerEntryType` (`soeREQUIRED`) — wire discriminant; must always be present.
* - `sfFlags` (`soeREQUIRED`) — object flag bitmask; must always be present.
*
* These fields are injected into every `SOTemplate` by the constructor, so they do
* not need to be listed in each entry type's individual field set in `ledger_entries.macro`.
* The vector is initialized once on first call (function-local static).
*
* @return A stable `const` reference to the common-fields vector.
*/
static std::vector<SOElement> const&
getCommonFields();
};
} // namespace xrpl
// NOLINTEND(readability-identifier-naming)