Compare commits

..

13 Commits

Author SHA1 Message Date
Michael Legleux
b7f16c341c feat: Build packages in GitHub 2026-03-05 11:29:07 -08:00
Alex Kremer
dde450784d Add Formats and Flags to server_definitions (#6321)
This change implements https://github.com/XRPLF/XRPL-Standards/discussions/418: "System XLS: Add Formats and Flags to server_definitions".
2026-03-05 16:11:27 +00:00
Michael Legleux
08e734457f fix: Fix docs deployment for pull requests (#6482) 2026-03-05 00:12:41 -08:00
Michael Legleux
77518394e8 fix: Stop committing generated docs to prevent repo bloat (#6474) 2026-03-04 19:19:57 -08:00
Ayaz Salikhov
c69091bded chore: Add Git information compile-time info to only one file (#6464)
The existing code added the git commit info (`GIT_COMMIT_HASH` and `GIT_BRANCH`) to every file, which was a problem for leveraging `ccache` to cache build objects. This change adds a separate C++ file from where these compile-time variables are propagated to wherever they are needed. A new CMake file is added to set the commit info if the `git` binary is available.
2026-03-04 19:45:28 +00:00
Alex Kremer
595f0dd461 chore: Enable clang-tidy bugprone-sizeof-expression check (#6466) 2026-03-04 19:15:22 +00:00
Alex Kremer
b451d5e412 chore: Enable clang-tidy bugprone-return-const-ref-from-parameter check (#6459) 2026-03-04 18:10:10 +00:00
Alex Kremer
af97df5a63 chore: Enable clang-tidy bugprone-move-forwarding-reference check (#6457) 2026-03-04 17:03:27 +00:00
Peter Chen
e39954d128 fix: Gateway balance with MPT (#6143)
When `gateway_balances` gets called on an account that is involved in the `EscrowCreate` transaction (with MPT being escrowed), the method returns internal error. This change fixes this case by excluding the MPT type when totaling escrow amount.
2026-03-04 15:50:51 +00:00
tequ
3cd1e3d94e refactor: Update PermissionedDomainDelete to use keylet for sle access (#6063) 2026-03-04 04:11:58 +01:00
Ayaz Salikhov
fcec31ed20 chore: Update pre-commit hooks (#6460) 2026-03-03 20:23:22 +00:00
dependabot[bot]
0abd762781 ci: [DEPENDABOT] bump actions/upload-artifact from 6.0.0 to 7.0.0 (#6450) 2026-03-03 17:17:08 +00:00
Sergey Kuznetsov
5300e65686 tests: Improve stability of Subscribe tests (#6420)
The `Subscribe` tests were flaky, because each test performs some operations (e.g. sends transactions) and waits for messages to appear in subscription with a 100ms timeout. If tests are slow (e.g. compiled in debug mode or a slow machine) then some of them could fail. This change adds an attempt to synchronize the background Env's thread and the test's thread by ensuring that all the scheduled operations are started before the test's thread starts to wait for a websocket message. This is done by limiting I/O threads of the app inside Env to 1 and adding a synchronization barrier after closing the ledger.
2026-03-03 08:46:55 -05:00
74 changed files with 1699 additions and 721 deletions

View File

@@ -24,6 +24,7 @@ Checks: "-*,
bugprone-misplaced-operator-in-strlen-in-alloc,
bugprone-misplaced-pointer-arithmetic-in-alloc,
bugprone-misplaced-widening-cast,
bugprone-move-forwarding-reference,
bugprone-multi-level-implicit-pointer-conversion,
bugprone-multiple-new-in-one-expression,
bugprone-multiple-statement-macro,
@@ -32,10 +33,12 @@ Checks: "-*,
bugprone-parent-virtual-call,
bugprone-posix-return,
bugprone-redundant-branch-condition,
bugprone-return-const-ref-from-parameter,
bugprone-shared-ptr-array-mismatch,
bugprone-signal-handler,
bugprone-signed-char-misuse,
bugprone-sizeof-container,
bugprone-sizeof-expression,
bugprone-spuriously-wake-up-functions,
bugprone-standalone-empty,
bugprone-string-constructor,
@@ -82,16 +85,14 @@ Checks: "-*,
performance-trivially-destructible
"
# ---
# checks that have some issues that need to be resolved:
# more checks that have some issues that need to be resolved:
#
# bugprone-crtp-constructor-accessibility,
# bugprone-inc-dec-in-conditions,
# bugprone-reserved-identifier,
# bugprone-move-forwarding-reference,
# bugprone-unused-local-non-trivial-variable,
# bugprone-return-const-ref-from-parameter,
# bugprone-switch-missing-default-case,
# bugprone-sizeof-expression,
# bugprone-suspicious-stringview-data-usage,
# bugprone-suspicious-missing-comma,
# bugprone-pointer-arithmetic-on-polymorphic-object,

View File

@@ -40,15 +40,18 @@ env:
NPROC_SUBTRACT: ${{ github.event.repository.private && '1' || '2' }}
jobs:
publish:
build:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/ci/tools-rippled-documentation:sha-a8c7be1
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@2cbf481018d930656e9276fcc20dc0e3a0be5b6d
with:
enable_ccache: false
- name: Get number of processors
uses: XRPLF/actions/get-nproc@cf0433aa74563aead044a1e395610c96d65a37cf
id: nproc
@@ -78,9 +81,23 @@ jobs:
cmake -Donly_docs=ON ..
cmake --build . --target docs --parallel ${BUILD_NPROC}
- name: Publish documentation
- name: Create documentation artifact
if: ${{ github.event_name == 'push' }}
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ${{ env.BUILD_DIR }}/docs/html
path: ${{ env.BUILD_DIR }}/docs/html
deploy:
if: ${{ github.event_name == 'push' }}
needs: build
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deploy
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5

View File

@@ -177,7 +177,7 @@ jobs:
- name: Upload the binary (Linux)
if: ${{ github.repository_owner == 'XRPLF' && runner.os == 'Linux' }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: xrpld-${{ inputs.config_name }}
path: ${{ env.BUILD_DIR }}/xrpld

View File

@@ -84,7 +84,7 @@ jobs:
- name: Upload clang-tidy output
if: steps.run_clang_tidy.outcome != 'success'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: clang-tidy-results
path: clang-tidy-output.txt

View File

@@ -20,7 +20,7 @@ repos:
args: [--assume-in-merge]
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: 75ca4ad908dc4a99f57921f29b7e6c1521e10b26 # frozen: v21.1.8
rev: cd481d7b0bfb5c7b3090c21846317f9a8262e891 # frozen: v22.1.0
hooks:
- id: clang-format
args: [--style=file]
@@ -33,17 +33,17 @@ repos:
additional_dependencies: [PyYAML]
- repo: https://github.com/rbubley/mirrors-prettier
rev: 5ba47274f9b181bce26a5150a725577f3c336011 # frozen: v3.6.2
rev: c2bc67fe8f8f549cc489e00ba8b45aa18ee713b1 # frozen: v3.8.1
hooks:
- id: prettier
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 831207fd435b47aeffdf6af853097e64322b4d44 # frozen: v25.12.0
rev: ea488cebbfd88a5f50b8bd95d5c829d0bb76feb8 # frozen: 26.1.0
hooks:
- id: black
- repo: https://github.com/streetsidesoftware/cspell-cli
rev: 1cfa010f078c354f3ffb8413616280cc28f5ba21 # frozen: v9.4.0
rev: a42085ade523f591dca134379a595e7859986445 # frozen: v9.7.0
hooks:
- id: cspell # Spell check changed files
exclude: .config/cspell.config.yaml

View File

@@ -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.
## Unreleased
This section contains changes targeting a future version.
### Additions
- `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.
@@ -75,10 +88,6 @@ This release contains bug fixes only and no API changes.
[Version 2.5.0](https://github.com/XRPLF/rippled/releases/tag/2.5.0) was released on Jun 24, 2025.
### Breaking changes in 2.5.0
- `feature`: In admin-mode responses, the `vetoed` field is now always a boolean. Disabled obsolete amendments now have `"vetoed": true` and a new `"obsolete": true` field, instead of the previous `"vetoed": "Obsolete"` string value. This change improves type safety for API clients while avoiding changes to non-admin responses.
### Additions and bugfixes in 2.5.0
- `tx`: Added `ctid` field to the response and improved error handling. ([#4738](https://github.com/XRPLF/rippled/pull/4738))

View File

@@ -36,26 +36,6 @@ endif ()
# Enable ccache to speed up builds.
include(Ccache)
# make GIT_COMMIT_HASH define available to all sources
find_package(Git)
if (Git_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git rev-parse
HEAD OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gch)
if (gch)
set(GIT_COMMIT_HASH "${gch}")
message(STATUS gch: ${GIT_COMMIT_HASH})
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
endif ()
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git rev-parse
--abbrev-ref HEAD OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gb)
if (gb)
set(GIT_BRANCH "${gb}")
message(STATUS gb: ${GIT_BRANCH})
add_definitions(-DGIT_BRANCH="${GIT_BRANCH}")
endif ()
endif () # git
if (thread_safety_analysis)
add_compile_options(-Wthread-safety -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS
-DXRPL_ENABLE_THREAD_SAFETY_ANNOTATIONS)

View File

@@ -0,0 +1,21 @@
Source: rippled
Section: net
Priority: optional
Maintainer: Michael Legleux <mlegleux@ripple.com>
Rules-Requires-Root: no
Build-Depends:
debhelper-compat (= 13),
Standards-Version: 4.7.0
Homepage: https://github.com/XRPLF/rippled
Vcs-Git: https://github.com/XRPLF/rippled.git
Vcs-Browser: https://github.com/XRPLF/rippled
Package: rippled
Section: net
Priority: optional
Architecture: any
Depends:
${shlibs:Depends},
${misc:Depends}
Description: XRP Ledger server daemon
XRPL server daemon providing ledger validation and p2p network services.

View File

@@ -0,0 +1,11 @@
opt/ripple/bin/rippled
opt/ripple/etc/rippled.cfg
opt/ripple/etc/validators.txt
# systemd unit (if you keep it in debian/ as a source file, do NOT list it here)
# If instead you stage it into debian/tmp, uncomment:
# lib/systemd/system/rippled.service
usr/share/doc/rippled/README.md
usr/share/doc/rippled/LICENSE.md

View File

@@ -0,0 +1,8 @@
/opt/ripple/bin/rippled /usr/bin/rippled
/opt/ripple/etc/rippled.cfg /etc/opt/ripple/xrpld.cfg
/opt/ripple/etc/validators.txt /etc/opt/ripple/validators.txt
# TODO: Remove when rippled deprecated
/opt/ripple/bin/rippled /opt/ripple/bin/xrpld
/opt/ripple/etc/rippled.cfg /etc/opt/ripple/xrpld.cfg
/opt/ripple /opt/xrpld

View File

@@ -0,0 +1,15 @@
/var/log/rippled/*.log {
daily
minsize 200M
rotate 7
nocreate
missingok
notifempty
compress
compresscmd /usr/bin/nice
compressoptions -n19 ionice -c3 gzip
compressext .gz
postrotate
/opt/ripple/bin/rippled --conf /opt/ripple/etc/rippled.cfg logrotate
endscript
}

View File

@@ -0,0 +1,40 @@
[Unit]
Description=XRPL daemon
After=network-online.target
Wants=network-online.target
[Service]
Type=notify
User=rippled
Group=rippled
# Canonical config location (as you described)
ExecStart=/opt/ripple/bin/rippled --conf /etc/opt/ripple/rippled.cfg
# Reasonable hardening defaults (trim if they break your runtime)
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectControlGroups=true
ProtectKernelTunables=true
ProtectKernelModules=true
LockPersonality=true
RestrictRealtime=true
RestrictSUIDSGID=true
MemoryDenyWriteExecute=true
SystemCallArchitectures=native
# Allow writes only where you actually need them
ReadWritePaths=/var/lib/rippled /var/log/rippled
StateDirectory=rippled
LogsDirectory=rippled
Restart=on-failure
RestartSec=2s
TimeoutStopSec=30s
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,5 @@
u rippled - "XRPL Daemon" /var/lib/ripled
# Type Name ID GECOS Home Shell
g rippled - - - -
u rippled - "XRPL rippled" /var/lib/rippled /usr/sbin/nologin

View File

@@ -0,0 +1,3 @@
# StateDirectory/LogsDirectory/RuntimeDirectory in systemd service makes this redundant but this enables apt purge.
d /var/lib/rippled 0750 rippled rippled -
d /var/log/rippled 0750 rippled rippled -

42
ci/packaging/debian/rules Normal file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/make -f
export DH_VERBOSE = 1
%:
dh $@
override_dh_auto_configure override_dh_auto_build override_dh_auto_test:
@:
override_dh_auto_install:
rm -rf debian/tmp
mkdir -p debian/tmp/opt/ripple/bin
mkdir -p debian/tmp/opt/ripple/etc
mkdir -p debian/tmp/usr
cp -a "$(INSTALL_TREE)/bin" debian/tmp/opt/ripple
cp -a "$(INSTALL_TREE)/etc" debian/tmp/opt/ripple
cp -a "$(INSTALL_TREE)/usr" debian/tmp
rm -rf debian/tmp/usr/include
rm -rf debian/tmp/usr/lib
install -Dm0644 README.md debian/tmp/usr/share/doc/rippled/README.md
install -Dm0644 LICENSE.md debian/tmp/usr/share/doc/rippled/LICENSE.md
install -Dm0644 cfg/rippled-example.cfg debian/tmp/opt/ripple/etc/rippled.cfg
install -Dm0644 cfg/validators-example.txt debian/tmp/opt/ripple/etc/validators.txt
override_dh_installsystemd:
dh_installsystemd
override_dh_installsysusers:
dh_installsysusers
override_dh_installtmpfiles:
dh_installtmpfiles
override_dh_install:
dh_install
override_dh_dwz:
@:
override_dh_strip:
dh_strip --no-automatic-dbgsym

21
cmake/GitInfo.cmake Normal file
View File

@@ -0,0 +1,21 @@
include_guard()
set(GIT_BUILD_BRANCH "")
set(GIT_COMMIT_HASH "")
find_package(Git)
if (NOT Git_FOUND)
message(WARNING "Git not found. Git branch and commit hash will be empty.")
return()
endif ()
set(GIT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/.git)
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${GIT_DIRECTORY} rev-parse --abbrev-ref HEAD
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE GIT_BUILD_BRANCH)
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${GIT_DIRECTORY} rev-parse HEAD
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE GIT_COMMIT_HASH)
message(STATUS "Git branch: ${GIT_BUILD_BRANCH}")
message(STATUS "Git commit hash: ${GIT_COMMIT_HASH}")

View File

@@ -58,6 +58,12 @@ include(target_link_modules)
add_module(xrpl beast)
target_link_libraries(xrpl.libxrpl.beast PUBLIC xrpl.imports.main)
include(GitInfo)
add_module(xrpl git)
target_compile_definitions(xrpl.libxrpl.git PRIVATE GIT_COMMIT_HASH="${GIT_COMMIT_HASH}"
GIT_BUILD_BRANCH="${GIT_BUILD_BRANCH}")
target_link_libraries(xrpl.libxrpl.git PUBLIC xrpl.imports.main)
# Level 02
add_module(xrpl basics)
target_link_libraries(xrpl.libxrpl.basics PUBLIC xrpl.libxrpl.beast)
@@ -71,7 +77,8 @@ target_link_libraries(xrpl.libxrpl.crypto PUBLIC xrpl.libxrpl.basics)
# Level 04
add_module(xrpl protocol)
target_link_libraries(xrpl.libxrpl.protocol PUBLIC xrpl.libxrpl.crypto xrpl.libxrpl.json)
target_link_libraries(xrpl.libxrpl.protocol PUBLIC xrpl.libxrpl.crypto xrpl.libxrpl.git
xrpl.libxrpl.json)
# Level 05
add_module(xrpl core)
@@ -135,6 +142,7 @@ target_link_modules(
conditions
core
crypto
git
json
ledger
net

View File

@@ -23,6 +23,7 @@ install(TARGETS common
xrpl.libxrpl.conditions
xrpl.libxrpl.core
xrpl.libxrpl.crypto
xrpl.libxrpl.git
xrpl.libxrpl.json
xrpl.libxrpl.rdb
xrpl.libxrpl.ledger

View File

@@ -1,6 +1,7 @@
#pragma once
#include <string>
#include <string_view>
#include <vector>
namespace beast {
@@ -26,14 +27,14 @@ public:
SemanticVersion();
SemanticVersion(std::string const& version);
SemanticVersion(std::string_view version);
/** Parse a semantic version string.
The parsing is as strict as possible.
@return `true` if the string was parsed.
*/
bool
parse(std::string const& input);
parse(std::string_view input);
/** Produce a string from semantic version components. */
std::string

13
include/xrpl/git/Git.h Normal file
View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
namespace xrpl::git {
std::string const&
getCommitHash();
std::string const&
getBuildBranch();
} // namespace xrpl::git

View File

@@ -23,13 +23,13 @@ public:
static constexpr size_t initialBufferSize = kilobytes(256);
RawStateTable()
: monotonic_resource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
initialBufferSize)}
: monotonic_resource_{
std::make_unique<boost::container::pmr::monotonic_buffer_resource>(initialBufferSize)}
, items_{monotonic_resource_.get()} {};
RawStateTable(RawStateTable const& rhs)
: monotonic_resource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
initialBufferSize)}
: monotonic_resource_{
std::make_unique<boost::container::pmr::monotonic_buffer_resource>(initialBufferSize)}
, items_{rhs.items_, monotonic_resource_.get()}
, dropsDestroyed_{rhs.dropsDestroyed_} {};

View File

@@ -49,7 +49,7 @@ getFullVersionString();
@return the encoded version in a 64-bit integer
*/
std::uint64_t
encodeSoftwareVersion(char const* const versionStr);
encodeSoftwareVersion(std::string_view versionStr);
/** Returns this server's version packed in a 64-bit integer. */
std::uint64_t

View File

@@ -30,9 +30,11 @@ public:
Item(
char const* name,
KeyType type,
std::initializer_list<SOElement> uniqueFields,
std::initializer_list<SOElement> commonFields)
: soTemplate_(uniqueFields, commonFields), name_(name), type_(type)
std::vector<SOElement> uniqueFields,
std::vector<SOElement> commonFields)
: soTemplate_(std::move(uniqueFields), std::move(commonFields))
, name_(name)
, type_(type)
{
// Verify that KeyType is appropriate.
static_assert(
@@ -142,16 +144,16 @@ protected:
@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<SOElement> uniqueFields,
std::initializer_list<SOElement> commonFields = {})
std::vector<SOElement> uniqueFields,
std::vector<SOElement> commonFields = {})
{
if (auto const item = findByType(type))
{
@@ -160,7 +162,7 @@ protected:
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;

View File

@@ -2,36 +2,34 @@
#include <xrpl/protocol/KnownFormats.h>
namespace xrpl {
#include <map>
#include <string>
#include <vector>
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
#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")
// ltOFFER
lsfPassive = 0x00010000,
lsfSell = 0x00020000, // True, offer was placed as a sell.
lsfHybrid = 0x00040000, // True, offer is hybrid.
#undef XMACRO
#undef TO_VALUE
#undef VALUE_TO_MAP
#undef NULL_NAME
#undef TO_MAP
// 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.
#undef ALL_LEDGER_FLAGS
// ltSIGNER_LIST
lsfOneOwnerCount = 0x00010000, // True, uses only one OwnerCount
// clang-format off
// ltDIR_NODE
lsfNFTokenBuyOffers = 0x00000001,
lsfNFTokenSellOffers = 0x00000002,
#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 */
// ltNFTOKEN_OFFER
lsfSellNFToken = 0x00000001,
// clang-format on
// ltMPTOKEN_ISSUANCE
lsfMPTLocked = 0x00000001, // Also used in ltMPTOKEN
lsfMPTCanLock = 0x00000002,
lsfMPTRequireAuth = 0x00000004,
lsfMPTCanEscrow = 0x00000008,
lsfMPTCanTrade = 0x00000010,
lsfMPTCanTransfer = 0x00000020,
lsfMPTCanClawback = 0x00000040,
// 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) };
lsmfMPTCanMutateCanLock = 0x00000002,
lsmfMPTCanMutateRequireAuth = 0x00000004,
lsmfMPTCanMutateCanEscrow = 0x00000008,
lsmfMPTCanMutateCanTrade = 0x00000010,
lsmfMPTCanMutateCanTransfer = 0x00000020,
lsmfMPTCanMutateCanClawback = 0x00000040,
lsmfMPTCanMutateMetadata = 0x00010000,
lsmfMPTCanMutateTransferFee = 0x00020000,
// 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<std::string, std::uint32_t>;
#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)
// ltMPTOKEN
lsfMPTAuthorized = 0x00000002,
// 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<std::pair<std::string, LedgerFlagMap>> const& getAllLedgerFlags() {
// static std::vector<std::pair<std::string, LedgerFlagMap>> const flags = {
// {"AccountRoot", getAccountRootFlags()},
// ...};
// return flags;
// }
#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;
}
// ltCREDENTIAL
lsfAccepted = 0x00010000,
#undef XMACRO
#undef TO_VALUE
#undef VALUE_TO_MAP
#undef NULL_NAME
#undef NULL_OUTPUT
#undef TO_MAP
#undef ALL_LEDGER_FLAGS
// ltVAULT
lsfVaultPrivate = 0x00010000,
// ltLOAN
lsfLoanDefault = 0x00010000,
lsfLoanImpaired = 0x00020000,
lsfLoanOverpayment = 0x00040000, // True, loan allows overpayments
};
#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 @@ private:
public:
static LedgerFormats const&
getInstance();
// Fields shared by all ledger entry formats:
static std::vector<SOElement> const&
getCommonFields();
};
} // namespace xrpl

View File

@@ -6,6 +6,7 @@
#include <functional>
#include <initializer_list>
#include <stdexcept>
#include <vector>
namespace xrpl {
@@ -97,8 +98,12 @@ public:
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<SOElement> uniqueFields, std::vector<SOElement> commonFields = {});
/** Create a template populated with all fields.
Note: Defers to the vector constructor above.
*/
SOTemplate(
std::initializer_list<SOElement> uniqueFields,

View File

@@ -3,294 +3,444 @@
#include <xrpl/protocol/LedgerFormats.h>
#include <cstdint>
#include <map>
#include <string>
#include <utility>
#include <vector>
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;
inline constexpr FlagValue tfFullyCanonicalSig = 0x80000000;
inline constexpr FlagValue tfInnerBatchTxn = 0x40000000;
inline constexpr FlagValue tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn;
inline constexpr FlagValue 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);
#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")
// 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;
#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
// 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);
// clang-format off
#undef ALL_TX_FLAGS
// 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);
// 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);
// 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.
// 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)
//
// 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.
// 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).
//
// 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 =
~(tfUniversal | tfBurnable | tfOnlyXRP | tfTransferable);
constexpr std::uint32_t const tfNFTokenMintOldMask =
~( ~tfNFTokenMintMask | 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);
// 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<std::string, FlagValue>;
#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<std::pair<std::string, FlagMap>>;
#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.
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.
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.
//
// 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);
inline constexpr FlagValue tfNFTokenMintOldMask = ~(~tfNFTokenMintMaskWithoutMutable | tfTrustLine);
// if featureDynamicNFT enabled then new flag allowing mutable URI available.
inline constexpr FlagValue tfNFTokenMintOldMaskWithMutable = ~(~tfNFTokenMintOldMask | tfMutable);
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<std::string, FlagValue> const&
getAsfFlagMap()
{
static std::map<std::string, FlagValue> 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

View File

@@ -2,6 +2,8 @@
#include <xrpl/protocol/KnownFormats.h>
#include <vector>
namespace xrpl {
/** Transaction type identifiers.
@@ -73,6 +75,9 @@ private:
public:
static TxFormats const&
getInstance();
static std::vector<SOElement> const&
getCommonFields();
};
} // namespace xrpl

View File

@@ -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
@@ -449,7 +453,6 @@ JSS(node_write_retries); // out: GetCounts
JSS(node_writes_delayed); // out::GetCounts
JSS(nth); // out: RPC server_definitions
JSS(obligations); // out: GatewayBalances
JSS(obsolete); // out: AmendmentTableImpl
JSS(offers); // out: NetworkOPs, AccountOffers, Subscribe
JSS(offer_id); // out: insertNFTokenOfferID
JSS(offline); // in: TransactionSign
@@ -458,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
@@ -617,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)

View File

@@ -13,9 +13,6 @@ public:
{
}
static std::uint32_t
getFlagsMask(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);

View File

@@ -13,9 +13,6 @@ public:
{
}
static std::uint32_t
getFlagsMask(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);

View File

@@ -26,9 +26,6 @@ public:
{
}
static std::uint32_t
getFlagsMask(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);

View File

@@ -13,9 +13,6 @@ public:
{
}
static std::uint32_t
getFlagsMask(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);

View File

@@ -138,14 +138,14 @@ SemanticVersion::SemanticVersion() : majorVersion(0), minorVersion(0), patchVers
{
}
SemanticVersion::SemanticVersion(std::string const& version) : SemanticVersion()
SemanticVersion::SemanticVersion(std::string_view version) : SemanticVersion()
{
if (!parse(version))
throw std::invalid_argument("invalid version string");
}
bool
SemanticVersion::parse(std::string const& input)
SemanticVersion::parse(std::string_view input)
{
// May not have leading or trailing whitespace
auto left_iter = std::find_if_not(input.begin(), input.end(), [](std::string::value_type c) {

31
src/libxrpl/git/Git.cpp Normal file
View File

@@ -0,0 +1,31 @@
#include "xrpl/git/Git.h"
#include <string>
#ifndef GIT_COMMIT_HASH
#error "GIT_COMMIT_HASH must be defined"
#endif
#ifndef GIT_BUILD_BRANCH
#error "GIT_BUILD_BRANCH must be defined"
#endif
namespace xrpl::git {
static constexpr char kGIT_COMMIT_HASH[] = GIT_COMMIT_HASH;
static constexpr char kGIT_BUILD_BRANCH[] = GIT_BUILD_BRANCH;
std::string const&
getCommitHash()
{
static std::string const kVALUE = kGIT_COMMIT_HASH;
return kVALUE;
}
std::string const&
getBuildBranch()
{
static std::string const kVALUE = kGIT_BUILD_BRANCH;
return kVALUE;
}
} // namespace xrpl::git

View File

@@ -72,8 +72,8 @@ OpenView::OpenView(
ReadView const* base,
Rules const& rules,
std::shared_ptr<void const> hold)
: monotonic_resource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
initialBufferSize)}
: monotonic_resource_{
std::make_unique<boost::container::pmr::monotonic_buffer_resource>(initialBufferSize)}
, txs_{monotonic_resource_.get()}
, rules_(rules)
, header_(base->header())
@@ -88,8 +88,8 @@ OpenView::OpenView(
}
OpenView::OpenView(ReadView const* base, std::shared_ptr<void const> hold)
: monotonic_resource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
initialBufferSize)}
: monotonic_resource_{
std::make_unique<boost::container::pmr::monotonic_buffer_resource>(initialBufferSize)}
, txs_{monotonic_resource_.get()}
, rules_(base->rules())
, header_(base->header())

View File

@@ -1,6 +1,7 @@
#include <xrpl/basics/contract.h>
#include <xrpl/beast/core/LexicalCast.h>
#include <xrpl/beast/core/SemanticVersion.h>
#include <xrpl/git/Git.h>
#include <xrpl/protocol/BuildInfo.h>
#include <boost/preprocessor/stringize.hpp>
@@ -14,44 +15,60 @@ namespace xrpl {
namespace BuildInfo {
namespace {
//--------------------------------------------------------------------------
// The build version number. You must edit this for each release
// and follow the format described at http://semver.org/
//------------------------------------------------------------------------------
// clang-format off
char const* const versionString = "3.2.0-b0"
// clang-format on
#if defined(DEBUG) || defined(SANITIZERS)
"+"
#ifdef GIT_COMMIT_HASH
GIT_COMMIT_HASH
"."
#endif
#ifdef DEBUG
"DEBUG"
#ifdef SANITIZERS
"."
#endif
#endif
#ifdef SANITIZERS
BOOST_PP_STRINGIZE(SANITIZERS) // cspell: disable-line
#endif
#endif
//--------------------------------------------------------------------------
// clang-format on
;
//
// Don't touch anything below this line
//
std::string
buildVersionString()
{
std::string version = versionString;
#if defined(DEBUG) || defined(SANITIZERS)
std::string metadata;
std::string const& commitHash = xrpl::git::getCommitHash();
if (!commitHash.empty())
metadata += commitHash + ".";
#ifdef DEBUG
metadata += "DEBUG";
#endif
#if defined(DEBUG) && defined(SANITIZERS)
metadata += ".";
#endif
#ifdef SANITIZERS
metadata += BOOST_PP_STRINGIZE(SANITIZERS); // cspell: disable-line
#endif
if (!metadata.empty())
version += "+" + metadata;
#endif
return version;
}
} // namespace
std::string const&
getVersionString()
{
static std::string const value = [] {
std::string const s = versionString;
std::string const s = buildVersionString();
beast::SemanticVersion v;
if (!v.parse(s) || v.print() != s)
LogicError(s + ": Bad server version string");
@@ -71,13 +88,13 @@ static constexpr std::uint64_t implementationVersionIdentifier = 0x183B'0000'000
static constexpr std::uint64_t implementationVersionIdentifierMask = 0xFFFF'0000'0000'0000LLU;
std::uint64_t
encodeSoftwareVersion(char const* const versionStr)
encodeSoftwareVersion(std::string_view versionStr)
{
std::uint64_t c = implementationVersionIdentifier;
beast::SemanticVersion v;
if (v.parse(std::string(versionStr)))
if (v.parse(versionStr))
{
if (v.majorVersion >= 0 && v.majorVersion <= 255)
c |= static_cast<std::uint64_t>(v.majorVersion) << 40;
@@ -137,7 +154,7 @@ encodeSoftwareVersion(char const* const versionStr)
std::uint64_t
getEncodedVersion()
{
static std::uint64_t const cookie = {encodeSoftwareVersion(versionString)};
static std::uint64_t const cookie = {encodeSoftwareVersion(getVersionString())};
return cookie;
}

View File

@@ -3,19 +3,23 @@
#include <xrpl/protocol/SOTemplate.h>
#include <xrpl/protocol/jss.h>
#include <initializer_list>
#include <vector>
namespace xrpl {
LedgerFormats::LedgerFormats()
std::vector<SOElement> const&
LedgerFormats::getCommonFields()
{
// Fields shared by all ledger formats:
static std::initializer_list<SOElement> const commonFields{
static auto const commonFields = std::vector<SOElement>{
{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 <xrpl/protocol/detail/ledger_entries.macro>

View File

@@ -2,23 +2,32 @@
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/SOTemplate.h>
#include <algorithm>
#include <cstddef>
#include <initializer_list>
#include <iterator>
#include <stdexcept>
#include <vector>
namespace xrpl {
SOTemplate::SOTemplate(
std::initializer_list<SOElement> uniqueFields,
std::initializer_list<SOElement> commonFields)
: SOTemplate(std::vector(uniqueFields), std::vector(commonFields))
{
}
SOTemplate::SOTemplate(std::vector<SOElement> uniqueFields, std::vector<SOElement> 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()};

View File

@@ -133,9 +133,9 @@ STVar::constructST(SerializedTypeID id, int depth, Args&&... args)
{
construct<T>(std::forward<Args>(args)...);
}
else if constexpr (std::is_same_v<
std::tuple<std::remove_cvref_t<Args>...>,
std::tuple<SerialIter, SField>>)
else if constexpr (
std::
is_same_v<std::tuple<std::remove_cvref_t<Args>...>, std::tuple<SerialIter, SField>>)
{
construct<T>(std::forward<Args>(args)..., depth);
}

View File

@@ -3,14 +3,14 @@
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/jss.h>
#include <initializer_list>
#include <vector>
namespace xrpl {
TxFormats::TxFormats()
std::vector<SOElement> const&
TxFormats::getCommonFields()
{
// Fields shared by all txFormats:
static std::initializer_list<SOElement> const commonFields{
static auto const commonFields = std::vector<SOElement>{
{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 <xrpl/protocol/detail/transactions.macro>

View File

@@ -19,7 +19,7 @@ std::uint32_t
AMMDeposit::getFlagsMask(PreflightContext const& ctx)
{
return tfDepositMask;
return tfAMMDepositMask;
}
NotTEC

View File

@@ -180,8 +180,9 @@ ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Issue const
if (auto const sle = view.read(keylet::account(ammAccountID)))
return (*sle)[sfBalance];
}
else if (auto const sle = view.read(keylet::line(ammAccountID, issue.account, issue.currency));
sle && !isFrozen(view, ammAccountID, issue.currency, issue.account))
else if (
auto const sle = view.read(keylet::line(ammAccountID, issue.account, issue.currency));
sle && !isFrozen(view, ammAccountID, issue.currency, issue.account))
{
auto amount = (*sle)[sfBalance];
if (ammAccountID > issue.account)

View File

@@ -42,8 +42,9 @@ AMMVote::preclaim(PreclaimContext const& ctx)
}
else if (ammSle->getFieldAmount(sfLPTokenBalance) == beast::zero)
return tecAMM_EMPTY;
else if (auto const lpTokensNew = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j);
lpTokensNew == beast::zero)
else if (
auto const lpTokensNew = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j);
lpTokensNew == beast::zero)
{
JLOG(ctx.j.debug()) << "AMM Vote: account is not LP.";
return tecAMM_INVALID_TOKENS;

View File

@@ -17,7 +17,7 @@ AMMWithdraw::checkExtraFeatures(PreflightContext const& ctx)
std::uint32_t
AMMWithdraw::getFlagsMask(PreflightContext const& ctx)
{
return tfWithdrawMask;
return tfAMMWithdrawMask;
}
NotTEC

View File

@@ -16,11 +16,11 @@ NotTEC
Transactor::invokePreflight<Change>(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);

View File

@@ -54,12 +54,6 @@ preflightHelper<MPTIssue>(PreflightContext const& ctx)
return tesSUCCESS;
}
std::uint32_t
Clawback::getFlagsMask(PreflightContext const& ctx)
{
return tfClawbackMask;
}
NotTEC
Clawback::preflight(PreflightContext const& ctx)
{

View File

@@ -84,11 +84,12 @@ LoanSet::preflight(PreflightContext const& ctx)
!validNumericMinimum(paymentInterval, LoanSet::minPaymentInterval))
return temINVALID;
// Grace period is between min default value and payment interval
else if (auto const gracePeriod = tx[~sfGracePeriod]; //
!validNumericRange(
gracePeriod,
paymentInterval.value_or(LoanSet::defaultPaymentInterval),
defaultGracePeriod))
else if (
auto const gracePeriod = tx[~sfGracePeriod]; //
!validNumericRange(
gracePeriod,
paymentInterval.value_or(LoanSet::defaultPaymentInterval),
defaultGracePeriod))
return temINVALID;
// Copied from preflight2

View File

@@ -5,12 +5,6 @@
namespace xrpl {
std::uint32_t
MPTokenIssuanceDestroy::getFlagsMask(PreflightContext const& ctx)
{
return tfMPTokenIssuanceDestroyMask;
}
NotTEC
MPTokenIssuanceDestroy::preflight(PreflightContext const& ctx)
{

View File

@@ -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<GranularPermissionType> granularPermissions;

View File

@@ -7,12 +7,6 @@
namespace xrpl {
std::uint32_t
NFTokenAcceptOffer::getFlagsMask(PreflightContext const& ctx)
{
return tfNFTokenAcceptOfferMask;
}
NotTEC
NFTokenAcceptOffer::preflight(PreflightContext const& ctx)
{

View File

@@ -8,12 +8,6 @@
namespace xrpl {
std::uint32_t
NFTokenCancelOffer::getFlagsMask(PreflightContext const& ctx)
{
return tfNFTokenCancelOfferMask;
}
NotTEC
NFTokenCancelOffer::preflight(PreflightContext const& ctx)
{

View File

@@ -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;

View File

@@ -378,7 +378,7 @@ PayChanClaim::checkExtraFeatures(PreflightContext const& ctx)
std::uint32_t
PayChanClaim::getFlagsMask(PreflightContext const&)
{
return tfPayChanClaimMask;
return tfPaymentChannelClaimMask;
}
NotTEC

View File

@@ -18,7 +18,7 @@ TER
PermissionedDomainDelete::preclaim(PreclaimContext const& ctx)
{
auto const domain = ctx.tx.getFieldH256(sfDomainID);
auto const sleDomain = ctx.view.read({ltPERMISSIONED_DOMAIN, domain});
auto const sleDomain = ctx.view.read(keylet::permissionedDomain(domain));
if (!sleDomain)
return tecNO_ENTRY;
@@ -40,7 +40,7 @@ PermissionedDomainDelete::doApply()
ctx_.tx.isFieldPresent(sfDomainID),
"xrpl::PermissionedDomainDelete::doApply : required field present");
auto const slePd = view().peek({ltPERMISSIONED_DOMAIN, ctx_.tx.at(sfDomainID)});
auto const slePd = view().peek(keylet::permissionedDomain(ctx_.tx.at(sfDomainID)));
auto const page = (*slePd)[sfOwnerNode];
if (!view().dirRemove(keylet::ownerDir(account_), page, slePd->key(), true))

View File

@@ -1448,7 +1448,7 @@ XChainCreateBridge::doApply()
std::uint32_t
BridgeModify::getFlagsMask(PreflightContext const& ctx)
{
return tfBridgeModifyMask;
return tfXChainModifyBridgeMask;
}
NotTEC

View File

@@ -575,6 +575,6 @@ public:
}
};
BEAST_DEFINE_TESTSUITE_PRIO(AccountSet, rpc, xrpl, 1);
BEAST_DEFINE_TESTSUITE_PRIO(AccountSet, app, xrpl, 1);
} // namespace xrpl

View File

@@ -4355,6 +4355,7 @@ public:
run() override
{
using namespace test::jtx;
auto const sa = testable_amendments();
testWithFeats(sa - fixBatchInnerSigs);
testWithFeats(sa);

View File

@@ -227,7 +227,7 @@ public:
static typename Base::Key const&
extract(Value const& value)
{
return value;
return value; // NOLINT(bugprone-return-const-ref-from-parameter)
}
static Values

View File

@@ -31,6 +31,7 @@
#include <xrpl/protocol/STTx.h>
#include <functional>
#include <future>
#include <source_location>
#include <string>
#include <tuple>
@@ -393,6 +394,48 @@ public:
return close(std::chrono::seconds(5));
}
/** Close and advance the ledger, then synchronize with the server's
io_context to ensure all async operations initiated by the close have
been started.
This function performs the same ledger close as close(), but additionally
ensures that all tasks posted to the server's io_context (such as
WebSocket subscription message sends) have been initiated before returning.
What it guarantees:
- All async operations posted before syncClose() have been STARTED
- For WebSocket sends: async_write_some() has been called
- The actual I/O completion may still be pending (async)
What it does NOT guarantee:
- Async operations have COMPLETED
- WebSocket messages have been received by clients
- However, for localhost connections, the remaining latency is typically
microseconds, making tests reliable
Use this instead of close() when:
- Test code immediately checks for subscription messages
- Race conditions between test and worker threads must be avoided
- Deterministic test behavior is required
@param timeout Maximum time to wait for the barrier task to execute
@return true if close succeeded and barrier executed within timeout,
false otherwise
*/
[[nodiscard]] bool
syncClose(std::chrono::steady_clock::duration timeout = std::chrono::seconds{1})
{
XRPL_ASSERT(
app().getNumberOfThreads() == 1,
"syncClose() is only useful on an application with a single thread");
auto const result = close();
auto serverBarrier = std::make_shared<std::promise<void>>();
auto future = serverBarrier->get_future();
boost::asio::post(app().getIOContext(), [serverBarrier]() { serverBarrier->set_value(); });
auto const status = future.wait_for(timeout);
return result && status == std::future_status::ready;
}
/** Turn on JSON tracing.
With no arguments, trace all
*/

View File

@@ -73,6 +73,8 @@ std::unique_ptr<Config> admin_localnet(std::unique_ptr<Config>);
std::unique_ptr<Config> secure_gateway_localnet(std::unique_ptr<Config>);
std::unique_ptr<Config> single_thread_io(std::unique_ptr<Config>);
/// @brief adjust configuration with params needed to be a validator
///
/// this is intended for use with envconfig, as in

View File

@@ -87,6 +87,12 @@ secure_gateway_localnet(std::unique_ptr<Config> cfg)
(*cfg)[PORT_WS].set("secure_gateway", "127.0.0.0/8");
return cfg;
}
std::unique_ptr<Config>
single_thread_io(std::unique_ptr<Config> cfg)
{
cfg->IO_WORKERS = 1;
return cfg;
}
auto constexpr defaultseed = "shUwVw52ofnCUX5m7kPTKzJdr4HEH";

View File

@@ -556,7 +556,7 @@ struct MultiApiJson_test : beast::unit_test::suite
static_assert([](auto&& v) {
return !requires {
v.visitor(
std::move(v), // cannot bind rvalue
decltype(v){}, // cannot bind rvalue
1,
[](Json::Value&, auto) {});
};

View File

@@ -148,20 +148,12 @@ class Feature_test : public beast::unit_test::suite
feature.isMember(jss::enabled) && !feature[jss::enabled].asBool(),
feature[jss::name].asString() + " enabled");
BEAST_EXPECTS(
feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool(),
feature[jss::name].asString() + " vetoed is bool");
BEAST_EXPECTS(
feature[jss::vetoed].asBool() == (expectVeto || expectObsolete),
feature[jss::name].asString() + " vetoed value");
if (expectObsolete)
BEAST_EXPECTS(
feature.isMember(jss::obsolete) && feature[jss::obsolete].isBool() &&
feature[jss::obsolete].asBool() == true,
feature[jss::name].asString() + " obsolete");
else
BEAST_EXPECTS(
!feature.isMember(jss::obsolete),
feature[jss::name].asString() + " no obsolete");
feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool() == !expectObsolete &&
(!feature[jss::vetoed].isBool() ||
feature[jss::vetoed].asBool() == expectVeto) &&
(feature[jss::vetoed].isBool() ||
feature[jss::vetoed].asString() == "Obsolete"),
feature[jss::name].asString() + " vetoed");
BEAST_EXPECTS(
feature.isMember(jss::supported) && feature[jss::supported].asBool(),
feature[jss::name].asString() + " supported");
@@ -261,7 +253,6 @@ class Feature_test : public beast::unit_test::suite
(*it)[jss::supported].asBool() == expectSupported,
(*it)[jss::name].asString() + " supported");
BEAST_EXPECT(!(*it).isMember(jss::vetoed));
BEAST_EXPECT(!(*it).isMember(jss::obsolete));
BEAST_EXPECT(!(*it).isMember(jss::majority));
BEAST_EXPECT(!(*it).isMember(jss::count));
BEAST_EXPECT(!(*it).isMember(jss::validations));
@@ -324,23 +315,13 @@ class Feature_test : public beast::unit_test::suite
BEAST_EXPECTS(
!(*it).isMember(jss::vetoed), (*it)[jss::name].asString() + " vetoed");
else
{
BEAST_EXPECTS(
(*it).isMember(jss::vetoed) && (*it)[jss::vetoed].isBool(),
(*it)[jss::name].asString() + " vetoed is bool");
BEAST_EXPECTS(
(*it)[jss::vetoed].asBool() == (expectVeto || expectObsolete),
(*it)[jss::name].asString() + " vetoed value");
if (expectObsolete)
BEAST_EXPECTS(
(*it).isMember(jss::obsolete) && (*it)[jss::obsolete].isBool() &&
(*it)[jss::obsolete].asBool() == true,
(*it)[jss::name].asString() + " obsolete");
else
BEAST_EXPECTS(
!(*it).isMember(jss::obsolete),
(*it)[jss::name].asString() + " no obsolete");
}
(*it).isMember(jss::vetoed) && (*it)[jss::vetoed].isBool() == !expectObsolete &&
(!(*it)[jss::vetoed].isBool() ||
(*it)[jss::vetoed].asBool() == expectVeto) &&
((*it)[jss::vetoed].isBool() ||
(*it)[jss::vetoed].asString() == "Obsolete"),
(*it)[jss::name].asString() + " vetoed");
BEAST_EXPECTS(
(*it).isMember(jss::supported) && (*it)[jss::supported].asBool() == expectSupported,
(*it)[jss::name].asString() + " supported");
@@ -408,20 +389,12 @@ class Feature_test : public beast::unit_test::suite
(expectVeto || expectObsolete) ^ feature.isMember(jss::majority),
feature[jss::name].asString() + " majority");
BEAST_EXPECTS(
feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool(),
feature[jss::name].asString() + " vetoed is bool");
BEAST_EXPECTS(
feature[jss::vetoed].asBool() == (expectVeto || expectObsolete),
feature[jss::name].asString() + " vetoed value");
if (expectObsolete)
BEAST_EXPECTS(
feature.isMember(jss::obsolete) && feature[jss::obsolete].isBool() &&
feature[jss::obsolete].asBool() == true,
feature[jss::name].asString() + " obsolete");
else
BEAST_EXPECTS(
!feature.isMember(jss::obsolete),
feature[jss::name].asString() + " no obsolete");
feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool() == !expectObsolete &&
(!feature[jss::vetoed].isBool() ||
feature[jss::vetoed].asBool() == expectVeto) &&
(feature[jss::vetoed].isBool() ||
feature[jss::vetoed].asString() == "Obsolete"),
feature[jss::name].asString() + " vetoed");
BEAST_EXPECTS(feature.isMember(jss::count), feature[jss::name].asString() + " count");
BEAST_EXPECTS(
feature.isMember(jss::threshold), feature[jss::name].asString() + " threshold");
@@ -512,9 +485,8 @@ class Feature_test : public beast::unit_test::suite
auto feature = *(jrr.begin());
BEAST_EXPECTS(feature[jss::name] == featureName, "name");
BEAST_EXPECTS(
feature[jss::vetoed].isBool() && feature[jss::vetoed].asBool() == true, "vetoed");
BEAST_EXPECTS(
feature[jss::obsolete].isBool() && feature[jss::obsolete].asBool() == true, "obsolete");
feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete",
"vetoed");
jrr = env.rpc("feature", featureName, "reject")[jss::result];
if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
@@ -525,9 +497,8 @@ class Feature_test : public beast::unit_test::suite
feature = *(jrr.begin());
BEAST_EXPECTS(feature[jss::name] == featureName, "name");
BEAST_EXPECTS(
feature[jss::vetoed].isBool() && feature[jss::vetoed].asBool() == true, "vetoed");
BEAST_EXPECTS(
feature[jss::obsolete].isBool() && feature[jss::obsolete].asBool() == true, "obsolete");
feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete",
"vetoed");
jrr = env.rpc("feature", featureName, "accept")[jss::result];
if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
@@ -538,9 +509,8 @@ class Feature_test : public beast::unit_test::suite
feature = *(jrr.begin());
BEAST_EXPECTS(feature[jss::name] == featureName, "name");
BEAST_EXPECTS(
feature[jss::vetoed].isBool() && feature[jss::vetoed].asBool() == true, "vetoed");
BEAST_EXPECTS(
feature[jss::obsolete].isBool() && feature[jss::obsolete].asBool() == true, "obsolete");
feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete",
"vetoed");
// anything other than accept or reject is an error
jrr = env.rpc("feature", featureName, "maybe");

View File

@@ -223,6 +223,45 @@ public:
expect(jv[jss::result][jss::obligations]["USD"] == maxUSD.getText());
}
void
testGWBWithMPT()
{
testcase("Gateway Balances with MPT Escrow");
using namespace std::chrono_literals;
using namespace jtx;
// Ensure MPT is enabled
FeatureBitset features = testable_amendments() | featureMPTokensV1;
Env env(*this, features);
Account const alice{"alice"};
Account const bob{"bob"};
env.fund(XRP(10000), alice, bob);
env.close();
// Create MPT issuance (Alice) with Escrow capability
MPTTester mpt(env, alice, {.holders = {bob}, .fund = false});
mpt.create({.flags = tfMPTCanEscrow});
// Authorize Bob and fund him
mpt.authorize({.account = bob, .holderCount = 1});
mpt.pay(alice, bob, 1000);
// Bob creates an escrow of MPT to Alice.
auto const MPT = mpt["MPT"];
env(escrow::create(bob, alice, MPT(100)), escrow::finish_time(env.now() + 10s));
env.close();
// Query gateway_balances for Bob.
auto wsc = makeWSClient(env.app().config());
Json::Value qry;
qry[jss::account] = bob.human();
auto jv = wsc->invoke("gateway_balances", qry);
expect(jv[jss::status] == "success");
}
void
run() override
{
@@ -233,7 +272,7 @@ public:
testGWB(feature);
testGWBApiVersions(feature);
}
testGWBWithMPT();
testGWBOverflow();
}
};

View File

@@ -1,6 +1,9 @@
#include <test/jtx.h>
#include <xrpl/beast/unit_test.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SOTemplate.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/jss.h>
namespace xrpl {
@@ -81,43 +84,371 @@ public:
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));
}
}
}

View File

@@ -26,7 +26,7 @@ public:
{
using namespace std::chrono_literals;
using namespace jtx;
Env env(*this);
Env env{*this, single_thread_io(envconfig())};
auto wsc = makeWSClient(env.app().config());
Json::Value stream;
@@ -92,7 +92,7 @@ public:
{
using namespace std::chrono_literals;
using namespace jtx;
Env env(*this);
Env env{*this, single_thread_io(envconfig())};
auto wsc = makeWSClient(env.app().config());
Json::Value stream;
@@ -114,7 +114,7 @@ public:
{
// Accept a ledger
env.close();
BEAST_EXPECT(env.syncClose());
// Check stream update
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
@@ -125,7 +125,7 @@ public:
{
// Accept another ledger
env.close();
BEAST_EXPECT(env.syncClose());
// Check stream update
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
@@ -150,7 +150,7 @@ public:
{
using namespace std::chrono_literals;
using namespace jtx;
Env env(*this);
Env env(*this, single_thread_io(envconfig()));
auto baseFee = env.current()->fees().base.drops();
auto wsc = makeWSClient(env.app().config());
Json::Value stream;
@@ -171,7 +171,7 @@ public:
{
env.fund(XRP(10000), "alice");
env.close();
BEAST_EXPECT(env.syncClose());
// Check stream update for payment transaction
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
@@ -195,7 +195,7 @@ public:
}));
env.fund(XRP(10000), "bob");
env.close();
BEAST_EXPECT(env.syncClose());
// Check stream update for payment transaction
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
@@ -249,12 +249,12 @@ public:
{
// Transaction that does not affect stream
env.fund(XRP(10000), "carol");
env.close();
BEAST_EXPECT(env.syncClose());
BEAST_EXPECT(!wsc->getMsg(10ms));
// Transactions concerning alice
env.trust(Account("bob")["USD"](100), "alice");
env.close();
BEAST_EXPECT(env.syncClose());
// Check stream updates
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
@@ -288,6 +288,7 @@ public:
using namespace jtx;
Env env(*this, envconfig([](std::unique_ptr<Config> cfg) {
cfg->FEES.reference_fee = 10;
cfg = single_thread_io(std::move(cfg));
return cfg;
}));
auto wsc = makeWSClient(env.app().config());
@@ -310,7 +311,7 @@ public:
{
env.fund(XRP(10000), "alice");
env.close();
BEAST_EXPECT(env.syncClose());
// Check stream update for payment transaction
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
@@ -360,7 +361,7 @@ public:
testManifests()
{
using namespace jtx;
Env env(*this);
Env env(*this, single_thread_io(envconfig()));
auto wsc = makeWSClient(env.app().config());
Json::Value stream;
@@ -394,7 +395,7 @@ public:
{
using namespace jtx;
Env env{*this, envconfig(validator, ""), features};
Env env{*this, single_thread_io(envconfig(validator, "")), features};
auto& cfg = env.app().config();
if (!BEAST_EXPECT(cfg.section(SECTION_VALIDATION_SEED).empty()))
return;
@@ -483,7 +484,7 @@ public:
// at least one flag ledger.
while (env.closed()->header().seq < 300)
{
env.close();
BEAST_EXPECT(env.syncClose());
using namespace std::chrono_literals;
BEAST_EXPECT(wsc->findMsg(5s, validValidationFields));
}
@@ -505,7 +506,7 @@ public:
{
using namespace jtx;
testcase("Subscribe by url");
Env env{*this};
Env env{*this, single_thread_io(envconfig())};
Json::Value jv;
jv[jss::url] = "http://localhost/events";
@@ -536,7 +537,7 @@ public:
auto const method = subscribe ? "subscribe" : "unsubscribe";
testcase << "Error cases for " << method;
Env env{*this};
Env env{*this, single_thread_io(envconfig())};
auto wsc = makeWSClient(env.app().config());
{
@@ -572,7 +573,7 @@ public:
}
{
Env env_nonadmin{*this, no_admin(envconfig())};
Env env_nonadmin{*this, single_thread_io(no_admin(envconfig()))};
Json::Value jv;
jv[jss::url] = "no-url";
auto jr = env_nonadmin.rpc("json", method, to_string(jv))[jss::result];
@@ -834,12 +835,13 @@ public:
* send payments between the two accounts a and b,
* and close ledgersToClose ledgers
*/
auto sendPayments = [](Env& env,
Account const& a,
Account const& b,
int newTxns,
std::uint32_t ledgersToClose,
int numXRP = 10) {
auto sendPayments = [this](
Env& env,
Account const& a,
Account const& b,
int newTxns,
std::uint32_t ledgersToClose,
int numXRP = 10) {
env.memoize(a);
env.memoize(b);
for (int i = 0; i < newTxns; ++i)
@@ -852,7 +854,7 @@ public:
jtx::sig(jtx::autofill));
}
for (int i = 0; i < ledgersToClose; ++i)
env.close();
BEAST_EXPECT(env.syncClose());
return newTxns;
};
@@ -945,7 +947,7 @@ public:
*
* also test subscribe to the account before it is created
*/
Env env(*this);
Env env(*this, single_thread_io(envconfig()));
auto wscTxHistory = makeWSClient(env.app().config());
Json::Value request;
request[jss::account_history_tx_stream] = Json::objectValue;
@@ -988,7 +990,7 @@ public:
* subscribe genesis account tx history without txns
* subscribe to bob's account after it is created
*/
Env env(*this);
Env env(*this, single_thread_io(envconfig()));
auto wscTxHistory = makeWSClient(env.app().config());
Json::Value request;
request[jss::account_history_tx_stream] = Json::objectValue;
@@ -998,6 +1000,7 @@ public:
if (!BEAST_EXPECT(goodSubRPC(jv)))
return;
IdxHashVec genesisFullHistoryVec;
BEAST_EXPECT(env.syncClose());
if (!BEAST_EXPECT(!getTxHash(*wscTxHistory, genesisFullHistoryVec, 1).first))
return;
@@ -1016,6 +1019,7 @@ public:
if (!BEAST_EXPECT(goodSubRPC(jv)))
return;
IdxHashVec bobFullHistoryVec;
BEAST_EXPECT(env.syncClose());
r = getTxHash(*wscTxHistory, bobFullHistoryVec, 1);
if (!BEAST_EXPECT(r.first && r.second))
return;
@@ -1050,6 +1054,7 @@ public:
"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
jv = wscTxHistory->invoke("subscribe", request);
genesisFullHistoryVec.clear();
BEAST_EXPECT(env.syncClose());
BEAST_EXPECT(getTxHash(*wscTxHistory, genesisFullHistoryVec, 31).second);
jv = wscTxHistory->invoke("unsubscribe", request);
@@ -1062,13 +1067,13 @@ public:
* subscribe account and subscribe account tx history
* and compare txns streamed
*/
Env env(*this);
Env env(*this, single_thread_io(envconfig()));
auto wscAccount = makeWSClient(env.app().config());
auto wscTxHistory = makeWSClient(env.app().config());
std::array<Account, 2> accounts = {alice, bob};
env.fund(XRP(222222), accounts);
env.close();
BEAST_EXPECT(env.syncClose());
// subscribe account
Json::Value stream = Json::objectValue;
@@ -1131,18 +1136,18 @@ public:
* alice issues USD to carol
* mix USD and XRP payments
*/
Env env(*this);
Env env(*this, single_thread_io(envconfig()));
auto const USD_a = alice["USD"];
std::array<Account, 2> accounts = {alice, carol};
env.fund(XRP(333333), accounts);
env.trust(USD_a(20000), carol);
env.close();
BEAST_EXPECT(env.syncClose());
auto mixedPayments = [&]() -> int {
sendPayments(env, alice, carol, 1, 0);
env(pay(alice, carol, USD_a(100)));
env.close();
BEAST_EXPECT(env.syncClose());
return 2;
};
@@ -1152,6 +1157,7 @@ public:
request[jss::account_history_tx_stream][jss::account] = carol.human();
auto ws = makeWSClient(env.app().config());
auto jv = ws->invoke("subscribe", request);
BEAST_EXPECT(env.syncClose());
{
// take out existing txns from the stream
IdxHashVec tempVec;
@@ -1169,10 +1175,10 @@ public:
/*
* long transaction history
*/
Env env(*this);
Env env(*this, single_thread_io(envconfig()));
std::array<Account, 2> accounts = {alice, carol};
env.fund(XRP(444444), accounts);
env.close();
BEAST_EXPECT(env.syncClose());
// many payments, and close lots of ledgers
auto oneRound = [&](int numPayments) {
@@ -1185,6 +1191,7 @@ public:
request[jss::account_history_tx_stream][jss::account] = carol.human();
auto wscLong = makeWSClient(env.app().config());
auto jv = wscLong->invoke("subscribe", request);
BEAST_EXPECT(env.syncClose());
{
// take out existing txns from the stream
IdxHashVec tempVec;
@@ -1222,7 +1229,7 @@ public:
jtx::testable_amendments() | featurePermissionedDomains | featureCredentials |
featurePermissionedDEX};
Env env(*this, all);
Env env(*this, single_thread_io(envconfig()), all);
PermissionedDEX permDex(env);
auto const alice = permDex.alice;
auto const bob = permDex.bob;
@@ -1241,10 +1248,10 @@ public:
if (!BEAST_EXPECT(jv[jss::status] == "success"))
return;
env(offer(alice, XRP(10), USD(10)), domain(domainID), txflags(tfHybrid));
env.close();
BEAST_EXPECT(env.syncClose());
env(pay(bob, carol, USD(5)), path(~USD), sendmax(XRP(5)), domain(domainID));
env.close();
BEAST_EXPECT(env.syncClose());
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
if (jv[jss::changes].size() != 1)
@@ -1284,9 +1291,9 @@ public:
Account const bob{"bob"};
Account const broker{"broker"};
Env env{*this, features};
Env env{*this, single_thread_io(envconfig()), features};
env.fund(XRP(10000), alice, bob, broker);
env.close();
BEAST_EXPECT(env.syncClose());
auto wsc = test::makeWSClient(env.app().config());
Json::Value stream;
@@ -1350,12 +1357,12 @@ public:
// Verify the NFTokenIDs are correct in the NFTokenMint tx meta
uint256 const nftId1{token::getNextID(env, alice, 0u, tfTransferable)};
env(token::mint(alice, 0u), txflags(tfTransferable));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenID(nftId1);
uint256 const nftId2{token::getNextID(env, alice, 0u, tfTransferable)};
env(token::mint(alice, 0u), txflags(tfTransferable));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenID(nftId2);
// Alice creates one sell offer for each NFT
@@ -1363,32 +1370,32 @@ public:
// meta
uint256 const aliceOfferIndex1 = keylet::nftoffer(alice, env.seq(alice)).key;
env(token::createOffer(alice, nftId1, drops(1)), txflags(tfSellNFToken));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenOfferID(aliceOfferIndex1);
uint256 const aliceOfferIndex2 = keylet::nftoffer(alice, env.seq(alice)).key;
env(token::createOffer(alice, nftId2, drops(1)), txflags(tfSellNFToken));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenOfferID(aliceOfferIndex2);
// Alice cancels two offers she created
// Verify the NFTokenIDs are correct in the NFTokenCancelOffer tx
// meta
env(token::cancelOffer(alice, {aliceOfferIndex1, aliceOfferIndex2}));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenIDsInCancelOffer({nftId1, nftId2});
// Bobs creates a buy offer for nftId1
// Verify the offer id is correct in the NFTokenCreateOffer tx meta
auto const bobBuyOfferIndex = keylet::nftoffer(bob, env.seq(bob)).key;
env(token::createOffer(bob, nftId1, drops(1)), token::owner(alice));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenOfferID(bobBuyOfferIndex);
// Alice accepts bob's buy offer
// Verify the NFTokenID is correct in the NFTokenAcceptOffer tx meta
env(token::acceptBuyOffer(alice, bobBuyOfferIndex));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenID(nftId1);
}
@@ -1397,7 +1404,7 @@ public:
// Alice mints a NFT
uint256 const nftId{token::getNextID(env, alice, 0u, tfTransferable)};
env(token::mint(alice, 0u), txflags(tfTransferable));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenID(nftId);
// Alice creates sell offer and set broker as destination
@@ -1405,18 +1412,18 @@ public:
env(token::createOffer(alice, nftId, drops(1)),
token::destination(broker),
txflags(tfSellNFToken));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenOfferID(offerAliceToBroker);
// Bob creates buy offer
uint256 const offerBobToBroker = keylet::nftoffer(bob, env.seq(bob)).key;
env(token::createOffer(bob, nftId, drops(1)), token::owner(alice));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenOfferID(offerBobToBroker);
// Check NFTokenID meta for NFTokenAcceptOffer in brokered mode
env(token::brokerOffers(broker, offerBobToBroker, offerAliceToBroker));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenID(nftId);
}
@@ -1426,24 +1433,24 @@ public:
// Alice mints a NFT
uint256 const nftId{token::getNextID(env, alice, 0u, tfTransferable)};
env(token::mint(alice, 0u), txflags(tfTransferable));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenID(nftId);
// Alice creates 2 sell offers for the same NFT
uint256 const aliceOfferIndex1 = keylet::nftoffer(alice, env.seq(alice)).key;
env(token::createOffer(alice, nftId, drops(1)), txflags(tfSellNFToken));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenOfferID(aliceOfferIndex1);
uint256 const aliceOfferIndex2 = keylet::nftoffer(alice, env.seq(alice)).key;
env(token::createOffer(alice, nftId, drops(1)), txflags(tfSellNFToken));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenOfferID(aliceOfferIndex2);
// Make sure the metadata only has 1 nft id, since both offers are
// for the same nft
env(token::cancelOffer(alice, {aliceOfferIndex1, aliceOfferIndex2}));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenIDsInCancelOffer({nftId});
}
@@ -1451,7 +1458,7 @@ public:
{
uint256 const aliceMintWithOfferIndex1 = keylet::nftoffer(alice, env.seq(alice)).key;
env(token::mint(alice), token::amount(XRP(0)));
env.close();
BEAST_EXPECT(env.syncClose());
verifyNFTokenOfferID(aliceMintWithOfferIndex1);
}
}

View File

@@ -1072,6 +1072,12 @@ public:
return trapTxID_;
}
size_t
getNumberOfThreads() const override
{
return get_number_of_threads();
}
private:
// For a newly-started validator, this is the greatest persisted ledger
// and new validations must be greater than this.

View File

@@ -157,6 +157,10 @@ public:
* than the last ledger it persisted. */
virtual LedgerIndex
getMaxDisallowedLedger() = 0;
/** Returns the number of io_context (I/O worker) threads used by the application. */
virtual size_t
getNumberOfThreads() const = 0;
};
std::unique_ptr<Application>

View File

@@ -23,4 +23,10 @@ public:
{
return io_context_;
}
size_t
get_number_of_threads() const
{
return threads_.size();
}
};

View File

@@ -6,6 +6,7 @@
#include <xrpl/basics/Log.h>
#include <xrpl/beast/core/CurrentThreadName.h>
#include <xrpl/git/Git.h>
#include <xrpl/protocol/BuildInfo.h>
#include <xrpl/server/Vacuum.h>
@@ -476,12 +477,8 @@ run(int argc, char** argv)
if (vm.count("version"))
{
std::cout << "rippled version " << BuildInfo::getVersionString() << std::endl;
#ifdef GIT_COMMIT_HASH
std::cout << "Git commit hash: " << GIT_COMMIT_HASH << std::endl;
#endif
#ifdef GIT_BRANCH
std::cout << "Git build branch: " << GIT_BRANCH << std::endl;
#endif
std::cout << "Git commit hash: " << xrpl::git::getCommitHash() << std::endl;
std::cout << "Git build branch: " << xrpl::git::getBuildBranch() << std::endl;
return 0;
}

View File

@@ -40,6 +40,7 @@
#include <xrpl/core/PerfLog.h>
#include <xrpl/crypto/RFC1751.h>
#include <xrpl/crypto/csprng.h>
#include <xrpl/git/Git.h>
#include <xrpl/ledger/AmendmentTable.h>
#include <xrpl/ledger/OrderBookDB.h>
#include <xrpl/protocol/BuildInfo.h>
@@ -2593,17 +2594,14 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters)
}
}
#if defined(GIT_COMMIT_HASH) || defined(GIT_BRANCH)
if (!xrpl::git::getCommitHash().empty() || !xrpl::git::getBuildBranch().empty())
{
auto& x = (info[jss::git] = Json::objectValue);
#ifdef GIT_COMMIT_HASH
x[jss::hash] = GIT_COMMIT_HASH;
#endif
#ifdef GIT_BRANCH
x[jss::branch] = GIT_BRANCH;
#endif
if (!xrpl::git::getCommitHash().empty())
x[jss::hash] = xrpl::git::getCommitHash();
if (!xrpl::git::getBuildBranch().empty())
x[jss::branch] = xrpl::git::getBuildBranch();
}
#endif
}
info[jss::io_latency_ms] = static_cast<Json::UInt>(registry_.app().getIOLatency().count());

View File

@@ -931,10 +931,7 @@ AmendmentTableImpl::injectJson(
if (!fs.enabled && isAdmin)
{
if (fs.vote == AmendmentVote::obsolete)
{
v[jss::vetoed] = true;
v[jss::obsolete] = true;
}
v[jss::vetoed] = "Obsolete";
else
v[jss::vetoed] = fs.vote == AmendmentVote::down;
}

View File

@@ -131,6 +131,10 @@ doGatewayBalances(RPC::JsonContext& context)
if (sle->getType() == ltESCROW)
{
auto const& escrow = sle->getFieldAmount(sfAmount);
// Gateway Balance should not include MPTs
if (escrow.holds<MPTIssue>())
return;
auto& bal = locked[escrow.getCurrency()];
if (bal == beast::zero)
{

View File

@@ -6,12 +6,15 @@
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/jss.h>
#include <boost/algorithm/string.hpp>
#include <set>
#include <string_view>
#include <unordered_map>
namespace xrpl {
@@ -47,13 +50,14 @@ public:
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<std::string, std::string> replacements{
static std::unordered_map<std::string_view, std::string_view> 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<std::string>();
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<std::string>();
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_);