From 0abd76278139e72b0f34c3f12d8d8332d23046ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:17:08 +0000 Subject: [PATCH 01/11] ci: [DEPENDABOT] bump actions/upload-artifact from 6.0.0 to 7.0.0 (#6450) --- .github/workflows/reusable-build-test-config.yml | 2 +- .github/workflows/reusable-clang-tidy-files.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index dabcc737f8..75fe546b18 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -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 diff --git a/.github/workflows/reusable-clang-tidy-files.yml b/.github/workflows/reusable-clang-tidy-files.yml index d36dea747c..129726ec8f 100644 --- a/.github/workflows/reusable-clang-tidy-files.yml +++ b/.github/workflows/reusable-clang-tidy-files.yml @@ -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 From fcec31ed20be643d1a113b4d6fd0e50d02605ceb Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 3 Mar 2026 21:23:22 +0100 Subject: [PATCH 02/11] chore: Update pre-commit hooks (#6460) --- .pre-commit-config.yaml | 8 ++++---- include/xrpl/ledger/detail/RawStateTable.h | 8 ++++---- src/libxrpl/ledger/OpenView.cpp | 8 ++++---- src/libxrpl/protocol/STVar.cpp | 6 +++--- src/libxrpl/tx/transactors/AMM/AMMUtils.cpp | 5 +++-- src/libxrpl/tx/transactors/AMM/AMMVote.cpp | 5 +++-- src/libxrpl/tx/transactors/Lending/LoanSet.cpp | 11 ++++++----- 7 files changed, 27 insertions(+), 24 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c17eb92787..2d0ff63b38 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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 diff --git a/include/xrpl/ledger/detail/RawStateTable.h b/include/xrpl/ledger/detail/RawStateTable.h index 7a3e2077ff..499b9204c6 100644 --- a/include/xrpl/ledger/detail/RawStateTable.h +++ b/include/xrpl/ledger/detail/RawStateTable.h @@ -23,13 +23,13 @@ public: static constexpr size_t initialBufferSize = kilobytes(256); RawStateTable() - : monotonic_resource_{std::make_unique( - initialBufferSize)} + : monotonic_resource_{ + std::make_unique(initialBufferSize)} , items_{monotonic_resource_.get()} {}; RawStateTable(RawStateTable const& rhs) - : monotonic_resource_{std::make_unique( - initialBufferSize)} + : monotonic_resource_{ + std::make_unique(initialBufferSize)} , items_{rhs.items_, monotonic_resource_.get()} , dropsDestroyed_{rhs.dropsDestroyed_} {}; diff --git a/src/libxrpl/ledger/OpenView.cpp b/src/libxrpl/ledger/OpenView.cpp index d27d755c66..5b94be5da8 100644 --- a/src/libxrpl/ledger/OpenView.cpp +++ b/src/libxrpl/ledger/OpenView.cpp @@ -72,8 +72,8 @@ OpenView::OpenView( ReadView const* base, Rules const& rules, std::shared_ptr hold) - : monotonic_resource_{std::make_unique( - initialBufferSize)} + : monotonic_resource_{ + std::make_unique(initialBufferSize)} , txs_{monotonic_resource_.get()} , rules_(rules) , header_(base->header()) @@ -88,8 +88,8 @@ OpenView::OpenView( } OpenView::OpenView(ReadView const* base, std::shared_ptr hold) - : monotonic_resource_{std::make_unique( - initialBufferSize)} + : monotonic_resource_{ + std::make_unique(initialBufferSize)} , txs_{monotonic_resource_.get()} , rules_(base->rules()) , header_(base->header()) diff --git a/src/libxrpl/protocol/STVar.cpp b/src/libxrpl/protocol/STVar.cpp index 994077da56..6218bb6db6 100644 --- a/src/libxrpl/protocol/STVar.cpp +++ b/src/libxrpl/protocol/STVar.cpp @@ -133,9 +133,9 @@ STVar::constructST(SerializedTypeID id, int depth, Args&&... args) { construct(std::forward(args)...); } - else if constexpr (std::is_same_v< - std::tuple...>, - std::tuple>) + else if constexpr ( + std:: + is_same_v...>, std::tuple>) { construct(std::forward(args)..., depth); } diff --git a/src/libxrpl/tx/transactors/AMM/AMMUtils.cpp b/src/libxrpl/tx/transactors/AMM/AMMUtils.cpp index ac67e861f1..55d71ced4f 100644 --- a/src/libxrpl/tx/transactors/AMM/AMMUtils.cpp +++ b/src/libxrpl/tx/transactors/AMM/AMMUtils.cpp @@ -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) diff --git a/src/libxrpl/tx/transactors/AMM/AMMVote.cpp b/src/libxrpl/tx/transactors/AMM/AMMVote.cpp index f40015ac08..549d9705b5 100644 --- a/src/libxrpl/tx/transactors/AMM/AMMVote.cpp +++ b/src/libxrpl/tx/transactors/AMM/AMMVote.cpp @@ -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; diff --git a/src/libxrpl/tx/transactors/Lending/LoanSet.cpp b/src/libxrpl/tx/transactors/Lending/LoanSet.cpp index 82a64bc89b..5e45bd5a9a 100644 --- a/src/libxrpl/tx/transactors/Lending/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/Lending/LoanSet.cpp @@ -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 From 3cd1e3d94e3e2498f98352e720dbc9948c1213a4 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 4 Mar 2026 12:11:58 +0900 Subject: [PATCH 03/11] refactor: Update PermissionedDomainDelete to use keylet for sle access (#6063) --- .../PermissionedDomain/PermissionedDomainDelete.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libxrpl/tx/transactors/PermissionedDomain/PermissionedDomainDelete.cpp b/src/libxrpl/tx/transactors/PermissionedDomain/PermissionedDomainDelete.cpp index e013bd8d2b..861bb934be 100644 --- a/src/libxrpl/tx/transactors/PermissionedDomain/PermissionedDomainDelete.cpp +++ b/src/libxrpl/tx/transactors/PermissionedDomain/PermissionedDomainDelete.cpp @@ -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)) From e39954d12809042001e94ce212bd9533cce84fc3 Mon Sep 17 00:00:00 2001 From: Peter Chen <34582813+PeterChen13579@users.noreply.github.com> Date: Wed, 4 Mar 2026 07:50:51 -0800 Subject: [PATCH 04/11] 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. --- src/test/rpc/GatewayBalances_test.cpp | 41 +++++++++++++++++++++- src/xrpld/rpc/handlers/GatewayBalances.cpp | 4 +++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/test/rpc/GatewayBalances_test.cpp b/src/test/rpc/GatewayBalances_test.cpp index 6415f6a51f..0deb1fc627 100644 --- a/src/test/rpc/GatewayBalances_test.cpp +++ b/src/test/rpc/GatewayBalances_test.cpp @@ -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(); } }; diff --git a/src/xrpld/rpc/handlers/GatewayBalances.cpp b/src/xrpld/rpc/handlers/GatewayBalances.cpp index e5e95d6835..a2176ab388 100644 --- a/src/xrpld/rpc/handlers/GatewayBalances.cpp +++ b/src/xrpld/rpc/handlers/GatewayBalances.cpp @@ -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()) + return; + auto& bal = locked[escrow.getCurrency()]; if (bal == beast::zero) { From af97df5a63fce2c5db6ca557394d722c5c0d7cab Mon Sep 17 00:00:00 2001 From: Alex Kremer Date: Wed, 4 Mar 2026 17:03:27 +0000 Subject: [PATCH 05/11] chore: Enable clang-tidy bugprone-move-forwarding-reference check (#6457) --- .clang-tidy | 1 + src/test/protocol/MultiApiJson_test.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index 5971b5dd14..e7169e47fe 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -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, diff --git a/src/test/protocol/MultiApiJson_test.cpp b/src/test/protocol/MultiApiJson_test.cpp index ccf719e349..bbd6ec1a07 100644 --- a/src/test/protocol/MultiApiJson_test.cpp +++ b/src/test/protocol/MultiApiJson_test.cpp @@ -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) {}); }; From b451d5e412590f55426de91bacea0f3bdaf45183 Mon Sep 17 00:00:00 2001 From: Alex Kremer Date: Wed, 4 Mar 2026 18:10:10 +0000 Subject: [PATCH 06/11] chore: Enable clang-tidy `bugprone-return-const-ref-from-parameter` check (#6459) --- .clang-tidy | 2 +- src/test/beast/aged_associative_container_test.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index e7169e47fe..f6e826323a 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -33,6 +33,7 @@ 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, @@ -90,7 +91,6 @@ Checks: "-*, # 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, diff --git a/src/test/beast/aged_associative_container_test.cpp b/src/test/beast/aged_associative_container_test.cpp index 19927c2d35..690f03cd49 100644 --- a/src/test/beast/aged_associative_container_test.cpp +++ b/src/test/beast/aged_associative_container_test.cpp @@ -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 From 595f0dd4619a668f629a7bc8982924b9742134bc Mon Sep 17 00:00:00 2001 From: Alex Kremer Date: Wed, 4 Mar 2026 19:15:22 +0000 Subject: [PATCH 07/11] chore: Enable clang-tidy `bugprone-sizeof-expression` check (#6466) --- .clang-tidy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index f6e826323a..26c7995631 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -38,6 +38,7 @@ Checks: "-*, bugprone-signal-handler, bugprone-signed-char-misuse, bugprone-sizeof-container, + bugprone-sizeof-expression, bugprone-spuriously-wake-up-functions, bugprone-standalone-empty, bugprone-string-constructor, @@ -84,7 +85,7 @@ 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, @@ -92,7 +93,6 @@ Checks: "-*, # bugprone-move-forwarding-reference, # bugprone-unused-local-non-trivial-variable, # bugprone-switch-missing-default-case, -# bugprone-sizeof-expression, # bugprone-suspicious-stringview-data-usage, # bugprone-suspicious-missing-comma, # bugprone-pointer-arithmetic-on-polymorphic-object, From c69091bded554a0820b8b449b1d3d10bcaa2553c Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 4 Mar 2026 19:45:28 +0000 Subject: [PATCH 08/11] 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. --- .github/workflows/publish-docs.yml | 5 ++ CMakeLists.txt | 20 ------- cmake/GitInfo.cmake | 21 +++++++ cmake/XrplCore.cmake | 10 +++- cmake/XrplInstall.cmake | 1 + include/xrpl/beast/core/SemanticVersion.h | 5 +- include/xrpl/git/Git.h | 13 +++++ include/xrpl/protocol/BuildInfo.h | 2 +- src/libxrpl/beast/core/SemanticVersion.cpp | 4 +- src/libxrpl/git/Git.cpp | 31 ++++++++++ src/libxrpl/protocol/BuildInfo.cpp | 67 ++++++++++++++-------- src/xrpld/app/main/Main.cpp | 9 +-- src/xrpld/app/misc/NetworkOPs.cpp | 14 ++--- 13 files changed, 137 insertions(+), 65 deletions(-) create mode 100644 cmake/GitInfo.cmake create mode 100644 include/xrpl/git/Git.h create mode 100644 src/libxrpl/git/Git.cpp diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index acde57fd91..c4fc2e65ee 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -49,6 +49,11 @@ jobs: - 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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 764e917498..f0d8519327 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/cmake/GitInfo.cmake b/cmake/GitInfo.cmake new file mode 100644 index 0000000000..1281182977 --- /dev/null +++ b/cmake/GitInfo.cmake @@ -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}") diff --git a/cmake/XrplCore.cmake b/cmake/XrplCore.cmake index e54dd09953..cd44f29df9 100644 --- a/cmake/XrplCore.cmake +++ b/cmake/XrplCore.cmake @@ -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 diff --git a/cmake/XrplInstall.cmake b/cmake/XrplInstall.cmake index 4cbf381f87..666fc0712f 100644 --- a/cmake/XrplInstall.cmake +++ b/cmake/XrplInstall.cmake @@ -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 diff --git a/include/xrpl/beast/core/SemanticVersion.h b/include/xrpl/beast/core/SemanticVersion.h index 1d3525de25..f839ef8c53 100644 --- a/include/xrpl/beast/core/SemanticVersion.h +++ b/include/xrpl/beast/core/SemanticVersion.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include 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 diff --git a/include/xrpl/git/Git.h b/include/xrpl/git/Git.h new file mode 100644 index 0000000000..001c09aea7 --- /dev/null +++ b/include/xrpl/git/Git.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace xrpl::git { + +std::string const& +getCommitHash(); + +std::string const& +getBuildBranch(); + +} // namespace xrpl::git diff --git a/include/xrpl/protocol/BuildInfo.h b/include/xrpl/protocol/BuildInfo.h index 3c42a4323f..cc7633cfe1 100644 --- a/include/xrpl/protocol/BuildInfo.h +++ b/include/xrpl/protocol/BuildInfo.h @@ -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 diff --git a/src/libxrpl/beast/core/SemanticVersion.cpp b/src/libxrpl/beast/core/SemanticVersion.cpp index b18043d5f0..06cd622722 100644 --- a/src/libxrpl/beast/core/SemanticVersion.cpp +++ b/src/libxrpl/beast/core/SemanticVersion.cpp @@ -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) { diff --git a/src/libxrpl/git/Git.cpp b/src/libxrpl/git/Git.cpp new file mode 100644 index 0000000000..2992852632 --- /dev/null +++ b/src/libxrpl/git/Git.cpp @@ -0,0 +1,31 @@ +#include "xrpl/git/Git.h" + +#include + +#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 diff --git a/src/libxrpl/protocol/BuildInfo.cpp b/src/libxrpl/protocol/BuildInfo.cpp index 32b3431798..c7531f8376 100644 --- a/src/libxrpl/protocol/BuildInfo.cpp +++ b/src/libxrpl/protocol/BuildInfo.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -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(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; } diff --git a/src/xrpld/app/main/Main.cpp b/src/xrpld/app/main/Main.cpp index 83bdb51ec1..32d246f493 100644 --- a/src/xrpld/app/main/Main.cpp +++ b/src/xrpld/app/main/Main.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -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; } diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index 8178611c61..b8663a76fb 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -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(registry_.app().getIOLatency().count()); From 77518394e8ced1581460b3fb02f7ac8aa41a2683 Mon Sep 17 00:00:00 2001 From: Michael Legleux Date: Wed, 4 Mar 2026 19:19:57 -0800 Subject: [PATCH 09/11] fix: Stop committing generated docs to prevent repo bloat (#6474) --- .github/workflows/publish-docs.yml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index c4fc2e65ee..aab2f9ca7d 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -44,7 +44,11 @@ jobs: runs-on: ubuntu-latest container: ghcr.io/xrplf/ci/tools-rippled-documentation:sha-a8c7be1 permissions: - contents: write + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deploy.outputs.page_url }} steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -83,9 +87,13 @@ 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 + + - name: Deploy documentation to GitHub Pages + id: deploy + if: ${{ github.event_name == 'push' }} + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 From 08e734457fa16cb3a851ecfae34f9a4f8accd0eb Mon Sep 17 00:00:00 2001 From: Michael Legleux Date: Thu, 5 Mar 2026 00:12:41 -0800 Subject: [PATCH 10/11] fix: Fix docs deployment for pull requests (#6482) --- .github/workflows/publish-docs.yml | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index aab2f9ca7d..d4abd74363 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -40,15 +40,9 @@ 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: - pages: write - id-token: write - environment: - name: github-pages - url: ${{ steps.deploy.outputs.page_url }} steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -93,7 +87,17 @@ jobs: with: path: ${{ env.BUILD_DIR }}/docs/html - - name: Deploy documentation to GitHub Pages + 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 - if: ${{ github.event_name == 'push' }} uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 From dde450784dafe5bb84799cae3f7e4a4968e5496e Mon Sep 17 00:00:00 2001 From: Alex Kremer Date: Thu, 5 Mar 2026 16:11:27 +0000 Subject: [PATCH 11/11] 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". --- API-CHANGELOG.md | 13 + include/xrpl/protocol/KnownFormats.h | 18 +- include/xrpl/protocol/LedgerFormats.h | 309 +++++--- include/xrpl/protocol/SOTemplate.h | 9 +- include/xrpl/protocol/TxFlags.h | 680 +++++++++++------- include/xrpl/protocol/TxFormats.h | 5 + include/xrpl/protocol/jss.h | 7 + include/xrpl/tx/transactors/Clawback.h | 3 - .../transactors/MPT/MPTokenIssuanceDestroy.h | 3 - .../tx/transactors/NFT/NFTokenAcceptOffer.h | 3 - .../tx/transactors/NFT/NFTokenCancelOffer.h | 3 - src/libxrpl/protocol/LedgerFormats.cpp | 14 +- src/libxrpl/protocol/SOTemplate.cpp | 15 +- src/libxrpl/protocol/TxFormats.cpp | 14 +- src/libxrpl/tx/transactors/AMM/AMMDeposit.cpp | 2 +- .../tx/transactors/AMM/AMMWithdraw.cpp | 2 +- src/libxrpl/tx/transactors/Change.cpp | 6 +- src/libxrpl/tx/transactors/Clawback.cpp | 6 - .../MPT/MPTokenIssuanceDestroy.cpp | 6 - .../tx/transactors/MPT/MPTokenIssuanceSet.cpp | 2 +- .../tx/transactors/NFT/NFTokenAcceptOffer.cpp | 6 - .../tx/transactors/NFT/NFTokenCancelOffer.cpp | 6 - .../tx/transactors/NFT/NFTokenMint.cpp | 5 +- src/libxrpl/tx/transactors/PayChan.cpp | 2 +- src/libxrpl/tx/transactors/XChainBridge.cpp | 2 +- src/test/{rpc => app}/AccountSet_test.cpp | 2 +- src/test/app/Batch_test.cpp | 1 + src/test/rpc/ServerDefinitions_test.cpp | 397 +++++++++- src/xrpld/rpc/handlers/ServerDefinitions.cpp | 123 +++- 29 files changed, 1165 insertions(+), 499 deletions(-) rename src/test/{rpc => app}/AccountSet_test.cpp (99%) diff --git a/API-CHANGELOG.md b/API-CHANGELOG.md index c7a31d27fa..31aca39782 100644 --- a/API-CHANGELOG.md +++ b/API-CHANGELOG.md @@ -22,6 +22,19 @@ API version 2 is available in `rippled` version 2.0.0 and later. See [API-VERSIO This version is supported by all `rippled` versions. For WebSocket and HTTP JSON-RPC requests, it is currently the default API version used when no `api_version` is specified. +## 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. diff --git a/include/xrpl/protocol/KnownFormats.h b/include/xrpl/protocol/KnownFormats.h index ce14a210ab..c454683e19 100644 --- a/include/xrpl/protocol/KnownFormats.h +++ b/include/xrpl/protocol/KnownFormats.h @@ -30,9 +30,11 @@ public: Item( char const* name, KeyType type, - std::initializer_list uniqueFields, - std::initializer_list commonFields) - : soTemplate_(uniqueFields, commonFields), name_(name), type_(type) + std::vector uniqueFields, + std::vector commonFields) + : soTemplate_(std::move(uniqueFields), std::move(commonFields)) + , name_(name) + , type_(type) { // Verify that KeyType is appropriate. static_assert( @@ -142,16 +144,16 @@ 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 uniqueFields, - std::initializer_list commonFields = {}) + std::vector uniqueFields, + std::vector 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; diff --git a/include/xrpl/protocol/LedgerFormats.h b/include/xrpl/protocol/LedgerFormats.h index fa39c15843..a43f6a7134 100644 --- a/include/xrpl/protocol/LedgerFormats.h +++ b/include/xrpl/protocol/LedgerFormats.h @@ -2,36 +2,34 @@ #include -namespace xrpl { +#include +#include +#include +namespace xrpl { /** Identifiers for on-ledger objects. - Each ledger object requires a unique type identifier, which is stored - within the object itself; this makes it possible to iterate the entire - ledger and determine each object's type and verify that the object you - retrieved from a given hash matches the expected type. + Each ledger object requires a unique type identifier, which is stored within the object itself; + this makes it possible to iterate the entire ledger and determine each object's type and verify + that the object you retrieved from a given hash matches the expected type. - @warning Since these values are stored inside objects stored on the ledger - they are part of the protocol. **Changing them should be avoided - because without special handling, this will result in a hard + @warning Since these values are stored inside objects stored on the ledger they are part of the + protocol. + **Changing them should be avoided because without special handling, this will result in a hard fork.** - @note Values outside this range may be used internally by the code for - various purposes, but attempting to use such values to identify - on-ledger objects will results in an invariant failure. + @note Values outside this range may be used internally by the code for various purposes, but + attempting to use such values to identify on-ledger objects will result in an invariant failure. - @note When retiring types, the specific values should not be removed but - should be marked as [[deprecated]]. This is to avoid accidental - reuse of identifiers. + @note When retiring types, the specific values should not be removed but should be marked as + [[deprecated]]. This is to avoid accidental reuse of identifiers. - @todo The C++ language does not enable checking for duplicate values - here. If it becomes possible then we should do this. + @todo The C++ language does not enable checking for duplicate values here. + If it becomes possible then we should do this. @ingroup protocol */ -// clang-format off -enum LedgerEntryType : std::uint16_t -{ +enum LedgerEntryType : std::uint16_t { #pragma push_macro("LEDGER_ENTRY") #undef LEDGER_ENTRY @@ -46,12 +44,10 @@ enum LedgerEntryType : std::uint16_t //--------------------------------------------------------------------------- /** A special type, matching any ledger entry type. - The value does not represent a concrete type, but rather is used in - contexts where the specific type of a ledger object is unimportant, - unknown or unavailable. + The value does not represent a concrete type, but rather is used in contexts where the + specific type of a ledger object is unimportant, unknown or unavailable. - Objects with this special type cannot be created or stored on the - ledger. + Objects with this special type cannot be created or stored on the ledger. \sa keylet::unchecked */ @@ -59,12 +55,11 @@ enum LedgerEntryType : std::uint16_t /** A special type, matching any ledger type except directory nodes. - The value does not represent a concrete type, but rather is used in - contexts where the ledger object must not be a directory node but - its specific type is otherwise unimportant, unknown or unavailable. + The value does not represent a concrete type, but rather is used in contexts where the + ledger object must not be a directory node but its specific type is otherwise unimportant, + unknown or unavailable. - Objects with this special type cannot be created or stored on the - ledger. + Objects with this special type cannot be created or stored on the ledger. \sa keylet::child */ @@ -93,104 +88,188 @@ enum LedgerEntryType : std::uint16_t Support for this type of object was never implemented. No objects of this type were ever created. */ - ltGENERATOR_MAP [[deprecated("This object type is not supported and should not be used.")]] = 0x0067, + ltGENERATOR_MAP [[deprecated("This object type is not supported and should not be used.")]] = + 0x0067, }; -// clang-format off -/** +/** Ledger object flags. + + These flags are specified in ledger objects and modify their behavior. + + @warning Ledger object flags form part of the protocol. + **Changing them should be avoided because without special handling, this will result in a hard + fork.** + @ingroup protocol */ -enum LedgerSpecificFlags { - // ltACCOUNT_ROOT - lsfPasswordSpent = 0x00010000, // True, if password set fee is spent. - lsfRequireDestTag = - 0x00020000, // True, to require a DestinationTag for payments. - lsfRequireAuth = - 0x00040000, // True, to require a authorization to hold IOUs. - lsfDisallowXRP = 0x00080000, // True, to disallow sending XRP. - lsfDisableMaster = 0x00100000, // True, force regular key - lsfNoFreeze = 0x00200000, // True, cannot freeze ripple states - lsfGlobalFreeze = 0x00400000, // True, all assets frozen - lsfDefaultRipple = - 0x00800000, // True, incoming trust lines allow rippling by default - lsfDepositAuth = 0x01000000, // True, all deposits require authorization -/* // reserved for Hooks amendment - lsfTshCollect = 0x02000000, // True, allow TSH collect-calls to acc hooks -*/ - lsfDisallowIncomingNFTokenOffer = - 0x04000000, // True, reject new incoming NFT offers - lsfDisallowIncomingCheck = - 0x08000000, // True, reject new checks - lsfDisallowIncomingPayChan = - 0x10000000, // True, reject new paychans - lsfDisallowIncomingTrustline = - 0x20000000, // True, reject new trustlines (only if no issued assets) - lsfAllowTrustLineLocking = - 0x40000000, // True, enable trustline locking - lsfAllowTrustLineClawback = - 0x80000000, // True, enable clawback +#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; +#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> const& getAllLedgerFlags() { +// static std::vector> const flags = { +// {"AccountRoot", getAccountRootFlags()}, +// ...}; +// return flags; +// } +#define ALL_LEDGER_FLAGS(name, values) {#name, get##name##Flags()}, +inline std::vector> const& +getAllLedgerFlags() +{ + static std::vector> const flags = { + XMACRO(ALL_LEDGER_FLAGS, NULL_OUTPUT, NULL_OUTPUT)}; + return flags; +} - // 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 const& + getCommonFields(); }; } // namespace xrpl diff --git a/include/xrpl/protocol/SOTemplate.h b/include/xrpl/protocol/SOTemplate.h index 576bd8c34a..41cea7936c 100644 --- a/include/xrpl/protocol/SOTemplate.h +++ b/include/xrpl/protocol/SOTemplate.h @@ -6,6 +6,7 @@ #include #include #include +#include namespace xrpl { @@ -97,8 +98,12 @@ 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 uniqueFields, std::vector commonFields = {}); + + /** Create a template populated with all fields. + Note: Defers to the vector constructor above. */ SOTemplate( std::initializer_list uniqueFields, diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index 6c6ef5e369..7c2085109f 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -3,294 +3,444 @@ #include #include +#include +#include +#include +#include namespace xrpl { /** Transaction flags. - These flags are specified in a transaction's 'Flags' field and modify the - behavior of that transaction. + These flags are specified in a transaction's 'Flags' field and modify + the behavior of that transaction. There are two types of flags: - (1) Universal flags: these are flags which apply to, and are interpreted - the same way by, all transactions, except, perhaps, - to special pseudo-transactions. + (1) Universal flags: these are flags which apply to, and are interpreted the same way by, + all transactions, except, perhaps, to special pseudo-transactions. - (2) Tx-Specific flags: these are flags which are interpreted according - to the type of the transaction being executed. - That is, the same numerical flag value may have - different effects, depending on the transaction - being executed. + (2) Tx-Specific flags: these are flags which are interpreted according to the type of the + transaction being executed. That is, the same numerical flag value may have different + effects, depending on the transaction being executed. - @note The universal transaction flags occupy the high-order 8 bits. The - tx-specific flags occupy the remaining 24 bits. + @note The universal transaction flags occupy the high-order 8 bits. + The tx-specific flags occupy the remaining 24 bits. - @warning Transaction flags form part of the protocol. **Changing them - should be avoided because without special handling, this will - result in a hard fork.** + @warning Transaction flags form part of the protocol. + **Changing them should be avoided because without special handling, this will result in + a hard fork.** @ingroup protocol */ -// Formatting equals sign aligned 4 spaces after longest prefix, except for -// wrapped lines -// clang-format off +using FlagValue = std::uint32_t; + // Universal Transaction flags: -constexpr std::uint32_t tfFullyCanonicalSig = 0x80000000; -constexpr std::uint32_t tfInnerBatchTxn = 0x40000000; -constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn; -constexpr std::uint32_t tfUniversalMask = ~tfUniversal; +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; +#define VALUE_TO_MAP(name, value) {#name, value}, +#define TO_MAP(name, values, maskAdj) \ + inline FlagMap const& get##name##Flags() \ + { \ + static FlagMap const flags = {values}; \ + return flags; \ + } +XMACRO(TO_MAP, VALUE_TO_MAP, VALUE_TO_MAP, NULL_MASK_ADJ) + +inline FlagMap const& +getUniversalFlags() +{ + static FlagMap const flags = { + {"tfFullyCanonicalSig", tfFullyCanonicalSig}, {"tfInnerBatchTxn", tfInnerBatchTxn}}; + return flags; +} + +// Create a getter function for all transaction flag maps using Meyer's singleton pattern. +// This is used to generate the server_definitions RPC output. +// +// example: +// inline FlagMapPairList const& getAllTxFlags() { +// static FlagMapPairList const flags = { +// {"AccountSet", getAccountSetFlags()}, +// ...}; +// return flags; +// } +using FlagMapPairList = std::vector>; +#define ALL_TX_FLAGS(name, values, maskAdj) {#name, get##name##Flags()}, +inline FlagMapPairList const& +getAllTxFlags() +{ + static FlagMapPairList const flags = { + {"universal", getUniversalFlags()}, + XMACRO(ALL_TX_FLAGS, NULL_OUTPUT, NULL_OUTPUT, NULL_MASK_ADJ)}; + return flags; +} + +#undef XMACRO +#undef TO_VALUE +#undef VALUE_TO_MAP +#undef NULL_NAME +#undef NULL_OUTPUT +#undef TO_MAP +#undef TO_MASK +#undef VALUE_TO_MASK +#undef ALL_TX_FLAGS +#undef NULL_MASK_ADJ +#undef MASK_ADJ_TO_MASK + +#pragma pop_macro("XMACRO") +#pragma pop_macro("TO_VALUE") +#pragma pop_macro("VALUE_TO_MAP") +#pragma pop_macro("NULL_NAME") +#pragma pop_macro("NULL_OUTPUT") +#pragma pop_macro("TO_MAP") +#pragma pop_macro("TO_MASK") +#pragma pop_macro("VALUE_TO_MASK") +#pragma pop_macro("ALL_TX_FLAGS") +#pragma pop_macro("NULL_MASK_ADJ") +#pragma pop_macro("MASK_ADJ_TO_MASK") + +// Additional transaction masks and combos +inline constexpr FlagValue tfMPTPaymentMask = ~(tfUniversal | tfPartialPayment); +inline constexpr FlagValue tfTrustSetPermissionMask = + ~(tfUniversal | tfSetfAuth | tfSetFreeze | tfClearFreeze); + +// MPTokenIssuanceCreate MutableFlags: +// Indicating specific fields or flags may be changed after issuance. +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 const& +getAsfFlagMap() +{ + static std::map const flags = { + ACCOUNTSET_FLAGS(ACCOUNTSET_FLAG_TO_MAP)}; + return flags; +} + +#undef ACCOUNTSET_FLAG_TO_VALUE +#undef ACCOUNTSET_FLAG_TO_MAP +#undef ACCOUNTSET_FLAGS + +#pragma pop_macro("ACCOUNTSET_FLAG_TO_VALUE") +#pragma pop_macro("ACCOUNTSET_FLAG_TO_MAP") +#pragma pop_macro("ACCOUNTSET_FLAGS") + } // namespace xrpl diff --git a/include/xrpl/protocol/TxFormats.h b/include/xrpl/protocol/TxFormats.h index b76eef7585..563a28f39f 100644 --- a/include/xrpl/protocol/TxFormats.h +++ b/include/xrpl/protocol/TxFormats.h @@ -2,6 +2,8 @@ #include +#include + namespace xrpl { /** Transaction type identifiers. @@ -73,6 +75,9 @@ private: public: static TxFormats const& getInstance(); + + static std::vector const& + getCommonFields(); }; } // namespace xrpl diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index 0264f625eb..5c605dde04 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -25,6 +25,7 @@ namespace jss { JSS(AL_size); // out: GetCounts JSS(AL_hit_rate); // out: GetCounts JSS(AcceptedCredentials); // out: AccountObjects +JSS(ACCOUNT_SET_FLAGS); // out: RPC server_definitions JSS(Account); // in: TransactionSign; field. JSS(AMMID); // field JSS(Amount); // in: TransactionSign; field. @@ -187,6 +188,7 @@ JSS(closed_ledger); // out: NetworkOPs JSS(cluster); // out: PeerImp JSS(code); // out: errors JSS(command); // in: RPCHandler +JSS(common); // out: RPC server_definitions JSS(complete); // out: NetworkOPs, InboundLedger JSS(complete_ledgers); // out: NetworkOPs, PeerImp JSS(consensus); // out: NetworkOPs, LedgerConsensus @@ -356,6 +358,8 @@ JSS(ledger_min); // in, out: AccountTx* JSS(ledger_time); // out: NetworkOPs JSS(LEDGER_ENTRY_TYPES); // out: RPC server_definitions // matches definitions.json format +JSS(LEDGER_ENTRY_FLAGS); // out: RPC server_definitions +JSS(LEDGER_ENTRY_FORMATS); // out: RPC server_definitions JSS(levels); // LogLevels JSS(limit); // in/out: AccountTx*, AccountOffers, // AccountLines, AccountObjects @@ -457,6 +461,7 @@ JSS(open); // out: handlers/Ledger JSS(open_ledger_cost); // out: SubmitTransaction JSS(open_ledger_fee); // out: TxQ JSS(open_ledger_level); // out: TxQ +JSS(optionality); // out: server_definitions JSS(oracles); // in: get_aggregate_price JSS(oracle_document_id); // in: get_aggregate_price JSS(owner); // in: LedgerEntry, out: NetworkOPs @@ -616,6 +621,8 @@ JSS(TRANSACTION_RESULTS); // out: RPC server_definitions // matches definitions.json format JSS(TRANSACTION_TYPES); // out: RPC server_definitions // matches definitions.json format +JSS(TRANSACTION_FLAGS); // out: RPC server_definitions +JSS(TRANSACTION_FORMATS); // out: RPC server_definitions JSS(TYPES); // out: RPC server_definitions // matches definitions.json format JSS(transfer_rate); // out: nft_info (clio) diff --git a/include/xrpl/tx/transactors/Clawback.h b/include/xrpl/tx/transactors/Clawback.h index 7451266461..a795115f7a 100644 --- a/include/xrpl/tx/transactors/Clawback.h +++ b/include/xrpl/tx/transactors/Clawback.h @@ -13,9 +13,6 @@ public: { } - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - static NotTEC preflight(PreflightContext const& ctx); diff --git a/include/xrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.h b/include/xrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.h index c4a448032a..416708565a 100644 --- a/include/xrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.h +++ b/include/xrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.h @@ -13,9 +13,6 @@ public: { } - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - static NotTEC preflight(PreflightContext const& ctx); diff --git a/include/xrpl/tx/transactors/NFT/NFTokenAcceptOffer.h b/include/xrpl/tx/transactors/NFT/NFTokenAcceptOffer.h index d876a70362..60962fc9ca 100644 --- a/include/xrpl/tx/transactors/NFT/NFTokenAcceptOffer.h +++ b/include/xrpl/tx/transactors/NFT/NFTokenAcceptOffer.h @@ -26,9 +26,6 @@ public: { } - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - static NotTEC preflight(PreflightContext const& ctx); diff --git a/include/xrpl/tx/transactors/NFT/NFTokenCancelOffer.h b/include/xrpl/tx/transactors/NFT/NFTokenCancelOffer.h index bb8cd4c216..6d60a0bebd 100644 --- a/include/xrpl/tx/transactors/NFT/NFTokenCancelOffer.h +++ b/include/xrpl/tx/transactors/NFT/NFTokenCancelOffer.h @@ -13,9 +13,6 @@ public: { } - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - static NotTEC preflight(PreflightContext const& ctx); diff --git a/src/libxrpl/protocol/LedgerFormats.cpp b/src/libxrpl/protocol/LedgerFormats.cpp index 2056cfab7b..30725f44a9 100644 --- a/src/libxrpl/protocol/LedgerFormats.cpp +++ b/src/libxrpl/protocol/LedgerFormats.cpp @@ -3,19 +3,23 @@ #include #include -#include +#include namespace xrpl { -LedgerFormats::LedgerFormats() +std::vector const& +LedgerFormats::getCommonFields() { - // Fields shared by all ledger formats: - static std::initializer_list const commonFields{ + static auto const commonFields = std::vector{ {sfLedgerIndex, soeOPTIONAL}, {sfLedgerEntryType, soeREQUIRED}, {sfFlags, soeREQUIRED}, }; + return commonFields; +} +LedgerFormats::LedgerFormats() +{ #pragma push_macro("UNWRAP") #undef UNWRAP #pragma push_macro("LEDGER_ENTRY") @@ -23,7 +27,7 @@ LedgerFormats::LedgerFormats() #define UNWRAP(...) __VA_ARGS__ #define LEDGER_ENTRY(tag, value, name, rpcName, fields) \ - add(jss::name, tag, UNWRAP fields, commonFields); + add(jss::name, tag, UNWRAP fields, getCommonFields()); #include diff --git a/src/libxrpl/protocol/SOTemplate.cpp b/src/libxrpl/protocol/SOTemplate.cpp index 46edf98710..b90bff6192 100644 --- a/src/libxrpl/protocol/SOTemplate.cpp +++ b/src/libxrpl/protocol/SOTemplate.cpp @@ -2,23 +2,32 @@ #include #include +#include #include #include +#include #include +#include namespace xrpl { SOTemplate::SOTemplate( std::initializer_list uniqueFields, std::initializer_list commonFields) + : SOTemplate(std::vector(uniqueFields), std::vector(commonFields)) +{ +} + +SOTemplate::SOTemplate(std::vector uniqueFields, std::vector commonFields) : indices_(SField::getNumFields() + 1, -1) // Unmapped indices == -1 { // Add all SOElements. - elements_.reserve(uniqueFields.size() + commonFields.size()); - elements_.assign(uniqueFields); - elements_.insert(elements_.end(), commonFields); + // + elements_ = std::move(uniqueFields); + std::ranges::move(commonFields, std::back_inserter(elements_)); // Validate and index elements_. + // for (std::size_t i = 0; i < elements_.size(); ++i) { SField const& sField{elements_[i].sField()}; diff --git a/src/libxrpl/protocol/TxFormats.cpp b/src/libxrpl/protocol/TxFormats.cpp index 00a560de1b..4492ae271b 100644 --- a/src/libxrpl/protocol/TxFormats.cpp +++ b/src/libxrpl/protocol/TxFormats.cpp @@ -3,14 +3,14 @@ #include #include -#include +#include namespace xrpl { -TxFormats::TxFormats() +std::vector const& +TxFormats::getCommonFields() { - // Fields shared by all txFormats: - static std::initializer_list const commonFields{ + static auto const commonFields = std::vector{ {sfTransactionType, soeREQUIRED}, {sfFlags, soeOPTIONAL}, {sfSourceTag, soeOPTIONAL}, @@ -29,7 +29,11 @@ TxFormats::TxFormats() {sfNetworkID, soeOPTIONAL}, {sfDelegate, soeOPTIONAL}, }; + return commonFields; +} +TxFormats::TxFormats() +{ #pragma push_macro("UNWRAP") #undef UNWRAP #pragma push_macro("TRANSACTION") @@ -37,7 +41,7 @@ TxFormats::TxFormats() #define UNWRAP(...) __VA_ARGS__ #define TRANSACTION(tag, value, name, delegable, amendment, privileges, fields) \ - add(jss::name, tag, UNWRAP fields, commonFields); + add(jss::name, tag, UNWRAP fields, getCommonFields()); #include diff --git a/src/libxrpl/tx/transactors/AMM/AMMDeposit.cpp b/src/libxrpl/tx/transactors/AMM/AMMDeposit.cpp index ba48295851..dad1bc536a 100644 --- a/src/libxrpl/tx/transactors/AMM/AMMDeposit.cpp +++ b/src/libxrpl/tx/transactors/AMM/AMMDeposit.cpp @@ -19,7 +19,7 @@ std::uint32_t AMMDeposit::getFlagsMask(PreflightContext const& ctx) { - return tfDepositMask; + return tfAMMDepositMask; } NotTEC diff --git a/src/libxrpl/tx/transactors/AMM/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/AMM/AMMWithdraw.cpp index 3c974ee4a4..f58dbaefc6 100644 --- a/src/libxrpl/tx/transactors/AMM/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/AMM/AMMWithdraw.cpp @@ -17,7 +17,7 @@ AMMWithdraw::checkExtraFeatures(PreflightContext const& ctx) std::uint32_t AMMWithdraw::getFlagsMask(PreflightContext const& ctx) { - return tfWithdrawMask; + return tfAMMWithdrawMask; } NotTEC diff --git a/src/libxrpl/tx/transactors/Change.cpp b/src/libxrpl/tx/transactors/Change.cpp index 315aeb89bd..9ae6504718 100644 --- a/src/libxrpl/tx/transactors/Change.cpp +++ b/src/libxrpl/tx/transactors/Change.cpp @@ -16,11 +16,11 @@ NotTEC Transactor::invokePreflight(PreflightContext const& ctx) { // 0 means "Allow any flags" - // The check for tfChangeMask is gated by LendingProtocol because that - // feature introduced this parameter, and it's not worth adding another + // The check for tfEnableAmendmentMask is gated by LendingProtocol because + // that feature introduced this parameter, and it's not worth adding another // amendment just for this. if (auto const ret = - preflight0(ctx, ctx.rules.enabled(featureLendingProtocol) ? tfChangeMask : 0)) + preflight0(ctx, ctx.rules.enabled(featureLendingProtocol) ? tfEnableAmendmentMask : 0)) return ret; auto account = ctx.tx.getAccountID(sfAccount); diff --git a/src/libxrpl/tx/transactors/Clawback.cpp b/src/libxrpl/tx/transactors/Clawback.cpp index ac13cc10da..789017e3ec 100644 --- a/src/libxrpl/tx/transactors/Clawback.cpp +++ b/src/libxrpl/tx/transactors/Clawback.cpp @@ -54,12 +54,6 @@ preflightHelper(PreflightContext const& ctx) return tesSUCCESS; } -std::uint32_t -Clawback::getFlagsMask(PreflightContext const& ctx) -{ - return tfClawbackMask; -} - NotTEC Clawback::preflight(PreflightContext const& ctx) { diff --git a/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.cpp b/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.cpp index acdd004bae..4b4d7c53f5 100644 --- a/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.cpp +++ b/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.cpp @@ -5,12 +5,6 @@ namespace xrpl { -std::uint32_t -MPTokenIssuanceDestroy::getFlagsMask(PreflightContext const& ctx) -{ - return tfMPTokenIssuanceDestroyMask; -} - NotTEC MPTokenIssuanceDestroy::preflight(PreflightContext const& ctx) { diff --git a/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceSet.cpp b/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceSet.cpp index bacd4585d7..2ae6bde083 100644 --- a/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceSet.cpp +++ b/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceSet.cpp @@ -130,7 +130,7 @@ MPTokenIssuanceSet::checkPermission(ReadView const& view, STTx const& tx) // this is added in case more flags will be added for MPTokenIssuanceSet // in the future. Currently unreachable. - if (txFlags & tfMPTokenIssuanceSetPermissionMask) + if (txFlags & tfMPTokenIssuanceSetMask) return terNO_DELEGATE_PERMISSION; // LCOV_EXCL_LINE std::unordered_set granularPermissions; diff --git a/src/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp b/src/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp index 45668d4273..90a28a2e7c 100644 --- a/src/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp +++ b/src/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp @@ -7,12 +7,6 @@ namespace xrpl { -std::uint32_t -NFTokenAcceptOffer::getFlagsMask(PreflightContext const& ctx) -{ - return tfNFTokenAcceptOfferMask; -} - NotTEC NFTokenAcceptOffer::preflight(PreflightContext const& ctx) { diff --git a/src/libxrpl/tx/transactors/NFT/NFTokenCancelOffer.cpp b/src/libxrpl/tx/transactors/NFT/NFTokenCancelOffer.cpp index 9a3e36b7c8..ce2cdf83c5 100644 --- a/src/libxrpl/tx/transactors/NFT/NFTokenCancelOffer.cpp +++ b/src/libxrpl/tx/transactors/NFT/NFTokenCancelOffer.cpp @@ -8,12 +8,6 @@ namespace xrpl { -std::uint32_t -NFTokenCancelOffer::getFlagsMask(PreflightContext const& ctx) -{ - return tfNFTokenCancelOfferMask; -} - NotTEC NFTokenCancelOffer::preflight(PreflightContext const& ctx) { diff --git a/src/libxrpl/tx/transactors/NFT/NFTokenMint.cpp b/src/libxrpl/tx/transactors/NFT/NFTokenMint.cpp index d8c063571b..a32a659bbd 100644 --- a/src/libxrpl/tx/transactors/NFT/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/NFT/NFTokenMint.cpp @@ -48,9 +48,8 @@ NFTokenMint::getFlagsMask(PreflightContext const& ctx) // tfTrustLine flag as a way to prevent the attack. But until the // amendment passes we still need to keep the old behavior available. std::uint32_t const nfTokenMintMask = ctx.rules.enabled(fixRemoveNFTokenAutoTrustLine) - // if featureDynamicNFT enabled then new flag allowing mutable URI - // available - ? ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintMaskWithMutable : tfNFTokenMintMask + // if featureDynamicNFT enabled then new flag allowing mutable URI available + ? ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintMask : tfNFTokenMintMaskWithoutMutable : ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintOldMaskWithMutable : tfNFTokenMintOldMask; diff --git a/src/libxrpl/tx/transactors/PayChan.cpp b/src/libxrpl/tx/transactors/PayChan.cpp index 3b29169af6..265030bebf 100644 --- a/src/libxrpl/tx/transactors/PayChan.cpp +++ b/src/libxrpl/tx/transactors/PayChan.cpp @@ -378,7 +378,7 @@ PayChanClaim::checkExtraFeatures(PreflightContext const& ctx) std::uint32_t PayChanClaim::getFlagsMask(PreflightContext const&) { - return tfPayChanClaimMask; + return tfPaymentChannelClaimMask; } NotTEC diff --git a/src/libxrpl/tx/transactors/XChainBridge.cpp b/src/libxrpl/tx/transactors/XChainBridge.cpp index 64daa6d1ee..44a1974f93 100644 --- a/src/libxrpl/tx/transactors/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/XChainBridge.cpp @@ -1448,7 +1448,7 @@ XChainCreateBridge::doApply() std::uint32_t BridgeModify::getFlagsMask(PreflightContext const& ctx) { - return tfBridgeModifyMask; + return tfXChainModifyBridgeMask; } NotTEC diff --git a/src/test/rpc/AccountSet_test.cpp b/src/test/app/AccountSet_test.cpp similarity index 99% rename from src/test/rpc/AccountSet_test.cpp rename to src/test/app/AccountSet_test.cpp index d342bf366a..87c6474be2 100644 --- a/src/test/rpc/AccountSet_test.cpp +++ b/src/test/app/AccountSet_test.cpp @@ -575,6 +575,6 @@ public: } }; -BEAST_DEFINE_TESTSUITE_PRIO(AccountSet, rpc, xrpl, 1); +BEAST_DEFINE_TESTSUITE_PRIO(AccountSet, app, xrpl, 1); } // namespace xrpl diff --git a/src/test/app/Batch_test.cpp b/src/test/app/Batch_test.cpp index bd1d701381..f429299f01 100644 --- a/src/test/app/Batch_test.cpp +++ b/src/test/app/Batch_test.cpp @@ -4355,6 +4355,7 @@ public: run() override { using namespace test::jtx; + auto const sa = testable_amendments(); testWithFeats(sa - fixBatchInnerSigs); testWithFeats(sa); diff --git a/src/test/rpc/ServerDefinitions_test.cpp b/src/test/rpc/ServerDefinitions_test.cpp index 69533939cd..a2c45cfa65 100644 --- a/src/test/rpc/ServerDefinitions_test.cpp +++ b/src/test/rpc/ServerDefinitions_test.cpp @@ -1,6 +1,9 @@ #include #include +#include +#include +#include #include namespace xrpl { @@ -81,43 +84,371 @@ 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)); + } } } diff --git a/src/xrpld/rpc/handlers/ServerDefinitions.cpp b/src/xrpld/rpc/handlers/ServerDefinitions.cpp index ea1912f66e..e153065ea9 100644 --- a/src/xrpld/rpc/handlers/ServerDefinitions.cpp +++ b/src/xrpld/rpc/handlers/ServerDefinitions.cpp @@ -6,12 +6,15 @@ #include #include #include +#include #include #include #include #include +#include +#include #include namespace xrpl { @@ -47,13 +50,14 @@ 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 replacements{ + static std::unordered_map const replacements{ {"OBJECT", "STObject"}, {"ARRAY", "STArray"}, {"ACCOUNT", "AccountID"}, @@ -77,7 +81,7 @@ ServerDefinitions::translate(std::string const& inp) if (auto const& it = replacements.find(inp); it != replacements.end()) { - return it->second; + return std::string(it->second); } std::string out; @@ -211,36 +215,35 @@ ServerDefinitions::ServerDefinitions() : defs_{Json::objectValue} defs_[jss::FIELDS][i++] = a; } - for (auto const& [code, f] : xrpl::SField::getKnownCodeToField()) + for (auto const& [code, field] : xrpl::SField::getKnownCodeToField()) { - if (f->fieldName == "") + if (field->fieldName == "") continue; Json::Value innerObj = Json::objectValue; - uint32_t type = f->fieldType; + uint32_t type = field->fieldType; - innerObj[jss::nth] = f->fieldValue; + innerObj[jss::nth] = field->fieldValue; - // whether the field is variable-length encoded - // this means that the length is included before the content + // whether the field is variable-length encoded this means that the length is included + // before the content innerObj[jss::isVLEncoded] = - (type == 7U /* Blob */ || type == 8U /* AccountID */ || - type == 19U /* Vector256 */); + (type == 7U /* Blob */ || type == 8U /* AccountID */ || type == 19U /* Vector256 */); // whether the field is included in serialization innerObj[jss::isSerialized] = - (type < 10000 && f->fieldName != "hash" && - f->fieldName != "index"); /* hash, index, TRANSACTION, - LEDGER_ENTRY, VALIDATION, METADATA */ + (type < 10000 && field->fieldName != "hash" && + field->fieldName != + "index"); // hash, index, TRANSACTION, LEDGER_ENTRY, VALIDATION, METADATA // whether the field is included in serialization when signing - innerObj[jss::isSigningField] = f->shouldInclude(false); + innerObj[jss::isSigningField] = field->shouldInclude(false); innerObj[jss::type] = typeMap[type]; Json::Value innerArray = Json::arrayValue; - innerArray[0U] = f->fieldName; + innerArray[0U] = field->fieldName; innerArray[1U] = innerObj; defs_[jss::FIELDS][i++] = innerArray; @@ -262,6 +265,92 @@ ServerDefinitions::ServerDefinitions() : defs_{Json::objectValue} defs_[jss::TRANSACTION_TYPES][f.getName()] = f.getType(); } + // populate TxFormats + defs_[jss::TRANSACTION_FORMATS] = Json::objectValue; + + defs_[jss::TRANSACTION_FORMATS][jss::common] = Json::arrayValue; + auto txCommonFields = std::set(); + for (auto const& element : TxFormats::getCommonFields()) + { + Json::Value elementObj = Json::objectValue; + elementObj[jss::name] = element.sField().getName(); + elementObj[jss::optionality] = element.style(); + defs_[jss::TRANSACTION_FORMATS][jss::common].append(elementObj); + txCommonFields.insert(element.sField().getName()); + } + + for (auto const& format : TxFormats::getInstance()) + { + auto const& soTemplate = format.getSOTemplate(); + Json::Value templateArray = Json::arrayValue; + for (auto const& element : soTemplate) + { + if (txCommonFields.contains(element.sField().getName())) + continue; // skip common fields, already added + Json::Value elementObj = Json::objectValue; + elementObj[jss::name] = element.sField().getName(); + elementObj[jss::optionality] = element.style(); + templateArray.append(elementObj); + } + defs_[jss::TRANSACTION_FORMATS][format.getName()] = templateArray; + } + + // populate LedgerFormats + defs_[jss::LEDGER_ENTRY_FORMATS] = Json::objectValue; + defs_[jss::LEDGER_ENTRY_FORMATS][jss::common] = Json::arrayValue; + auto ledgerCommonFields = std::set(); + for (auto const& element : LedgerFormats::getCommonFields()) + { + Json::Value elementObj = Json::objectValue; + elementObj[jss::name] = element.sField().getName(); + elementObj[jss::optionality] = element.style(); + defs_[jss::LEDGER_ENTRY_FORMATS][jss::common].append(elementObj); + ledgerCommonFields.insert(element.sField().getName()); + } + for (auto const& format : LedgerFormats::getInstance()) + { + auto const& soTemplate = format.getSOTemplate(); + Json::Value templateArray = Json::arrayValue; + for (auto const& element : soTemplate) + { + if (ledgerCommonFields.contains(element.sField().getName())) + continue; // skip common fields, already added + Json::Value elementObj = Json::objectValue; + elementObj[jss::name] = element.sField().getName(); + elementObj[jss::optionality] = element.style(); + templateArray.append(elementObj); + } + defs_[jss::LEDGER_ENTRY_FORMATS][format.getName()] = templateArray; + } + + defs_[jss::TRANSACTION_FLAGS] = Json::objectValue; + for (auto const& [name, value] : getAllTxFlags()) + { + Json::Value txObj = Json::objectValue; + for (auto const& [flagName, flagValue] : value) + { + txObj[flagName] = flagValue; + } + defs_[jss::TRANSACTION_FLAGS][name] = txObj; + } + + defs_[jss::LEDGER_ENTRY_FLAGS] = Json::objectValue; + for (auto const& [name, value] : getAllLedgerFlags()) + { + Json::Value ledgerObj = Json::objectValue; + for (auto const& [flagName, flagValue] : value) + { + ledgerObj[flagName] = flagValue; + } + defs_[jss::LEDGER_ENTRY_FLAGS][name] = ledgerObj; + } + + defs_[jss::ACCOUNT_SET_FLAGS] = Json::objectValue; + for (auto const& [name, value] : getAsfFlagMap()) + { + defs_[jss::ACCOUNT_SET_FLAGS][name] = value; + } + // generate hash { std::string const out = Json::FastWriter().write(defs_);