refactor: collapse transactions.macro settings into a TxSettings struct

This commit is contained in:
Mayukha Vadari
2026-08-10 22:26:54 -04:00
parent 9c292fbe4f
commit 90a202cf14
9 changed files with 396 additions and 346 deletions

View File

@@ -8,6 +8,7 @@ Uses pcpp to preprocess the macro file and pyparsing to parse the DSL.
import io
import argparse
import re
from pathlib import Path
import pyparsing as pp
@@ -53,28 +54,80 @@ def create_transaction_parser():
return macro_parser
# Defaults for xrpl::TxSettings members, mirroring
# include/xrpl/protocol/TxSettings.h. A transaction's settings blob only names
# the members that differ from these.
SETTING_DEFAULTS = {
"delegable": "Delegation::Delegable",
"amendment": "uint256{}",
"privileges": "NoPriv",
}
def parse_settings(settings_str):
"""Parse a TxSettings blob into a dict, filling in defaults.
Args:
settings_str: A string like '({.delegable = Delegation::NotDelegable,
.privileges = CreateAcct | MayCreateMpt})', or '({})'.
Returns:
A dict with a value for every key in SETTING_DEFAULTS.
"""
body = settings_str.strip()
if not (body.startswith("(") and body.endswith(")")):
raise ValueError(
f"Malformed settings blob, expected '({{...}})': {settings_str!r}"
)
body = body[1:-1].strip()
if not (body.startswith("{") and body.endswith("}")):
raise ValueError(
f"Malformed settings blob, expected '({{...}})': {settings_str!r}"
)
body = body[1:-1]
# Strip comments, which may be interleaved with the designated initializers.
body = re.sub(r"//[^\n]*", "", body)
settings = dict(SETTING_DEFAULTS)
seen = set()
# Each entry runs from '.key =' up to the next '.key =' or the end.
for key, value in re.findall(
r"\.(\w+)\s*=\s*(.*?)(?=,\s*\.\w+\s*=|,?\s*$)", body, re.S
):
if key not in SETTING_DEFAULTS:
raise ValueError(f"Unknown TxSettings member '.{key}' in {settings_str!r}")
settings[key] = " ".join(value.split()).rstrip(",")
seen.add(key)
# Catch a typo'd or unparsed initializer rather than silently defaulting it.
# Every '.member' in the blob must have been consumed above.
if len(re.findall(r"\.\w+", body)) != len(seen):
raise ValueError(f"Could not parse every setting in {settings_str!r}")
return settings
def parse_transaction_args(args_list):
"""Parse the arguments of a TRANSACTION macro call.
Args:
args_list: A list of parsed arguments from pyparsing, e.g.,
['ttPAYMENT', '0', 'Payment', 'Delegation::delegable',
'uint256{}', 'createAcct', '({...})']
['ttPAYMENT', '0', 'Payment',
'({.privileges = CreateAcct | MayCreateMpt})', '({...})']
Returns:
A dict with parsed transaction information.
"""
if len(args_list) < 7:
if len(args_list) < 5:
raise ValueError(
f"Expected at least 7 parts in TRANSACTION, got {len(args_list)}: {args_list}"
f"Expected at least 5 parts in TRANSACTION, got {len(args_list)}: {args_list}"
)
tag = args_list[0]
value = args_list[1]
name = args_list[2]
delegable = args_list[3]
amendments = args_list[4]
privileges = args_list[5]
settings = parse_settings(args_list[3])
fields_str = args_list[-1]
# Parse fields: ({field1, field2, ...})
@@ -84,9 +137,9 @@ def parse_transaction_args(args_list):
"tag": tag,
"value": value,
"name": name,
"delegable": delegable,
"amendments": amendments,
"privileges": privileges,
"delegable": settings["delegable"],
"amendments": settings["amendment"],
"privileges": settings["privileges"],
"fields": fields,
}

View File

@@ -4,6 +4,7 @@
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/SOTemplate.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/TxSettings.h>
#include <cstdint>
#include <functional>
@@ -38,11 +39,6 @@ enum GranularPermissionType : std::uint32_t {
#pragma pop_macro("GRANULAR_PERMISSION")
};
// Injected bare enumerators (xrpl::delegable / xrpl::notDelegable) are required by preprocessor
// tricks in tests and macro-generated code; enum class would break that.
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
enum Delegation { Delegable, NotDelegable };
class Permission
{
private:

View File

@@ -0,0 +1,82 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/safe_cast.h>
#include <type_traits>
namespace xrpl {
// Injected bare enumerators (xrpl::Delegable / xrpl::NotDelegable) are required by preprocessor
// tricks in tests and macro-generated code; enum class would break that.
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
enum Delegation { Delegable, NotDelegable };
/**
* Operations a transaction is permitted to perform, as a bitfield.
*
* These are declared per-transaction in transactions.macro (via
* TxSettings::privileges) and enforced in InvariantCheck.cpp.
*/
// Bitwise flags, 86 files, used in macros files
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
enum Privilege {
NoPriv = 0x0000, // The transaction can not do any of the enumerated operations
CreateAcct = 0x0001, // The transaction can create a new ACCOUNT_ROOT object.
CreatePseudoAcct = 0x0002, // The transaction can create a pseudo account,
// which implies createAcct
MustDeleteAcct = 0x0004, // The transaction must delete an ACCOUNT_ROOT object
MayDeleteAcct = 0x0008, // The transaction may delete an ACCOUNT_ROOT
// object, but does not have to
OverrideFreeze = 0x0010, // The transaction can override some freeze rules
ChangeNftCounts = 0x0020, // The transaction can mint or burn an NFT
CreateMptIssuance = 0x0040, // The transaction can create a new MPT issuance
DestroyMptIssuance = 0x0080, // The transaction can destroy an MPT issuance
MustAuthorizeMpt = 0x0100, // The transaction MUST create or delete an MPT
// object (except by issuer)
MayAuthorizeMpt = 0x0200, // The transaction MAY create or delete an MPT
// object (except by issuer)
MayDeleteMpt = 0x0400, // The transaction MAY delete an MPT object. May not create.
MustModifyVault = 0x0800, // The transaction must modify, delete or create, a vault
MayModifyVault = 0x1000, // The transaction MAY modify, delete or create, a vault
MayCreateMpt = 0x2000, // The transaction MAY create an MPT object, except for issuer.
};
constexpr Privilege
operator|(Privilege lhs, Privilege rhs)
{
return safeCast<Privilege>(
safeCast<std::underlying_type_t<Privilege>>(lhs) |
safeCast<std::underlying_type_t<Privilege>>(rhs));
}
/**
* Per-transaction metadata declared in transactions.macro.
*
* Every member has a default, so a transaction only needs to name the settings
* that differ from the common case. See the documentation at the top of
* transactions.macro for the authoring syntax.
*
* This is deliberately not a constexpr-friendly type: amendment identifiers are
* runtime-initialized `extern uint256 const` globals (see Feature.h), so a
* TxSettings can only be built at runtime.
*/
struct TxSettings
{
/**
* Whether an account may delegate this transaction to another account.
*/
Delegation delegable = Delegable;
/**
* The amendment gating this transaction, or uint256{} if always available.
*/
uint256 amendment{};
/**
* Operations this transaction is permitted to perform.
*/
Privilege privileges = NoPriv;
};
} // namespace xrpl

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,7 @@
#pragma once
#include <xrpl/basics/safe_cast.h>
#include <xrpl/protocol/STTx.h>
#include <type_traits>
#include <xrpl/protocol/TxSettings.h> // IWYU pragma: export
namespace xrpl {
@@ -26,37 +24,8 @@ not have the relevant amendments enabled_. It's intentionally a pain in the neck
so that bad code gets caught and fixed as early as possible.
*/
// Bitwise flags, 86 files, used in macros files
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
enum Privilege {
NoPriv = 0x0000, // The transaction can not do any of the enumerated operations
CreateAcct = 0x0001, // The transaction can create a new ACCOUNT_ROOT object.
CreatePseudoAcct = 0x0002, // The transaction can create a pseudo account,
// which implies createAcct
MustDeleteAcct = 0x0004, // The transaction must delete an ACCOUNT_ROOT object
MayDeleteAcct = 0x0008, // The transaction may delete an ACCOUNT_ROOT
// object, but does not have to
OverrideFreeze = 0x0010, // The transaction can override some freeze rules
ChangeNftCounts = 0x0020, // The transaction can mint or burn an NFT
CreateMptIssuance = 0x0040, // The transaction can create a new MPT issuance
DestroyMptIssuance = 0x0080, // The transaction can destroy an MPT issuance
MustAuthorizeMpt = 0x0100, // The transaction MUST create or delete an MPT
// object (except by issuer)
MayAuthorizeMpt = 0x0200, // The transaction MAY create or delete an MPT
// object (except by issuer)
MayDeleteMpt = 0x0400, // The transaction MAY delete an MPT object. May not create.
MustModifyVault = 0x0800, // The transaction must modify, delete or create, a vault
MayModifyVault = 0x1000, // The transaction MAY modify, delete or create, a vault
MayCreateMpt = 0x2000, // The transaction MAY create an MPT object, except for issuer.
};
constexpr Privilege
operator|(Privilege lhs, Privilege rhs)
{
return safeCast<Privilege>(
safeCast<std::underlying_type_t<Privilege>>(lhs) |
safeCast<std::underlying_type_t<Privilege>>(rhs));
}
// `enum Privilege` and its `operator|` live in <xrpl/protocol/TxSettings.h>,
// alongside the TxSettings struct that carries them out of transactions.macro.
bool
hasPrivilege(STTx const& tx, Privilege priv);

View File

@@ -10,6 +10,7 @@
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TxFlags.h> // IWYU pragma: keep
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/TxSettings.h>
#include <algorithm>
#include <cstdint>
@@ -40,16 +41,24 @@ Permission::GranularPermissionEntry::GranularPermissionEntry(
Permission::Permission()
{
{
#pragma push_macro("UNWRAP")
#undef UNWRAP
#pragma push_macro("TRANSACTION")
#undef TRANSACTION
#define TRANSACTION(tag, value, name, delegable, amendment, ...) \
txDelegationMap_[static_cast<TxType>(value)] = {amendment, delegable};
#define UNWRAP(...) __VA_ARGS__
#define TRANSACTION(tag, value, name, settings, ...) \
{ \
TxSettings const s = UNWRAP settings; \
txDelegationMap_[static_cast<TxType>(value)] = {s.amendment, s.delegable}; \
}
#include <xrpl/protocol/detail/transactions.macro>
#undef TRANSACTION
#pragma pop_macro("TRANSACTION")
#undef UNWRAP
#pragma pop_macro("UNWRAP")
}
granularPermissionsByName_ = {

View File

@@ -45,7 +45,7 @@ TxFormats::TxFormats()
#undef TRANSACTION
#define UNWRAP(...) __VA_ARGS__
#define TRANSACTION(tag, value, name, delegable, amendment, privileges, fields) \
#define TRANSACTION(tag, value, name, settings, fields) \
add(jss::name, tag, UNWRAP fields, getCommonFields());
#include <xrpl/protocol/detail/transactions.macro>

View File

@@ -25,6 +25,7 @@
#include <xrpl/protocol/SystemParameters.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/TxSettings.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/invariants/InvariantCheckPrivilege.h>
@@ -40,12 +41,15 @@
namespace xrpl {
#pragma push_macro("UNWRAP")
#undef UNWRAP
#pragma push_macro("TRANSACTION")
#undef TRANSACTION
#define TRANSACTION(tag, value, name, delegable, amendment, privileges, ...) \
case tag: { \
return (privileges) & priv; \
#define UNWRAP(...) __VA_ARGS__
#define TRANSACTION(tag, value, name, settings, ...) \
case tag: { \
return (TxSettings UNWRAP settings).privileges & priv; \
}
bool
@@ -63,6 +67,8 @@ hasPrivilege(STTx const& tx, Privilege priv)
#undef TRANSACTION
#pragma pop_macro("TRANSACTION")
#undef UNWRAP
#pragma pop_macro("UNWRAP")
// Returns the human-readable name of a ledger entry's type, falling back to
// the numeric type if the format is somehow unknown.

View File

@@ -47,6 +47,7 @@
#include <xrpl/protocol/SecretKey.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/TxSettings.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/protocol/jss.h>
@@ -2718,19 +2719,24 @@ class Delegate_test : public beast::unit_test::Suite
std::size_t delegableCount = 0;
#pragma push_macro("UNWRAP")
#undef UNWRAP
#pragma push_macro("TRANSACTION")
#undef TRANSACTION
#define TRANSACTION(tag, value, name, txDelegable, ...) \
if (txDelegable == xrpl::Delegable) \
{ \
delegableCount++; \
#define UNWRAP(...) __VA_ARGS__
#define TRANSACTION(tag, value, name, settings, ...) \
if ((xrpl::TxSettings UNWRAP settings).delegable == xrpl::Delegable) \
{ \
delegableCount++; \
}
#include <xrpl/protocol/detail/transactions.macro>
#undef TRANSACTION
#pragma pop_macro("TRANSACTION")
#undef UNWRAP
#pragma pop_macro("UNWRAP")
// ====================================================================
// IMPORTANT NOTICE: