From c38aabdaee434f143c457a7bb369f843c6847d84 Mon Sep 17 00:00:00 2001 From: Alex Kremer Date: Tue, 10 Mar 2026 17:42:49 +0000 Subject: [PATCH 01/32] chore: Enable clang-tidy `bugprone-unhandled-self-assignment` check (#6504) --- .clang-tidy | 2 +- src/libxrpl/protocol/STBase.cpp | 3 +++ src/libxrpl/resource/Consumer.cpp | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index 04bb857609..f374af30a4 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -64,6 +64,7 @@ Checks: "-*, bugprone-undefined-memory-manipulation, bugprone-undelegated-constructor, bugprone-unhandled-exception-at-new, + bugprone-unhandled-self-assignment, bugprone-unique-ptr-array-mismatch, bugprone-unsafe-functions, bugprone-unused-local-non-trivial-variable, @@ -100,7 +101,6 @@ Checks: "-*, # bugprone-switch-missing-default-case, # bugprone-unused-return-value, # bugprone-use-after-move, -# bugprone-unhandled-self-assignment, # bugprone-unused-raii, # # cppcoreguidelines-misleading-capture-default-by-value, diff --git a/src/libxrpl/protocol/STBase.cpp b/src/libxrpl/protocol/STBase.cpp index f6b1dcec58..3c5c34ae4e 100644 --- a/src/libxrpl/protocol/STBase.cpp +++ b/src/libxrpl/protocol/STBase.cpp @@ -23,6 +23,9 @@ STBase::STBase(SField const& n) : fName(&n) STBase& STBase::operator=(STBase const& t) { + if (this == &t) + return *this; + if (!fName->isUseful()) fName = t.fName; return *this; diff --git a/src/libxrpl/resource/Consumer.cpp b/src/libxrpl/resource/Consumer.cpp index 11c23b1044..e9435bd340 100644 --- a/src/libxrpl/resource/Consumer.cpp +++ b/src/libxrpl/resource/Consumer.cpp @@ -39,6 +39,9 @@ Consumer::~Consumer() Consumer& Consumer::operator=(Consumer const& other) { + if (this == &other) + return *this; + // remove old ref if (m_logic && m_entry) m_logic->release(*m_entry); From 8345cd77df4c9f783959cfe2a16004511efdfaa9 Mon Sep 17 00:00:00 2001 From: Alex Kremer Date: Tue, 10 Mar 2026 19:48:56 +0000 Subject: [PATCH 02/32] chore: Enable clang-tidy `bugprone-unused-raii` check (#6505) --- .clang-tidy | 1 + src/test/jtx/Env_test.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index f374af30a4..87b6a42daa 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -67,6 +67,7 @@ Checks: "-*, bugprone-unhandled-self-assignment, bugprone-unique-ptr-array-mismatch, bugprone-unsafe-functions, + bugprone-unused-raii, bugprone-unused-local-non-trivial-variable, bugprone-virtual-near-miss, cppcoreguidelines-no-suspend-with-lock, diff --git a/src/test/jtx/Env_test.cpp b/src/test/jtx/Env_test.cpp index 6d151c27ce..1fdf4e6db3 100644 --- a/src/test/jtx/Env_test.cpp +++ b/src/test/jtx/Env_test.cpp @@ -39,9 +39,9 @@ public: a = std::move(b); Account c(std::move(a)); } - Account("alice"); - Account("alice", KeyType::secp256k1); - Account("alice", KeyType::ed25519); + Account("alice"); // NOLINT(bugprone-unused-raii) + Account("alice", KeyType::secp256k1); // NOLINT(bugprone-unused-raii) + Account("alice", KeyType::ed25519); // NOLINT(bugprone-unused-raii) auto const gw = Account("gw"); [](AccountID) {}(gw); auto const USD = gw["USD"]; @@ -56,11 +56,11 @@ public: { using namespace jtx; - PrettyAmount(0); - PrettyAmount(1); - PrettyAmount(0u); - PrettyAmount(1u); - PrettyAmount(-1); + PrettyAmount(0); // NOLINT(bugprone-unused-raii) + PrettyAmount(1); // NOLINT(bugprone-unused-raii) + PrettyAmount(0u); // NOLINT(bugprone-unused-raii) + PrettyAmount(1u); // NOLINT(bugprone-unused-raii) + PrettyAmount(-1); // NOLINT(bugprone-unused-raii) static_assert(!std::is_trivially_constructible::value, ""); static_assert(!std::is_trivially_constructible::value, ""); static_assert(!std::is_trivially_constructible::value, ""); From f27d8f389063b02f50b44019a4b47a7b9d775d8c Mon Sep 17 00:00:00 2001 From: Alex Kremer Date: Tue, 10 Mar 2026 20:12:15 +0000 Subject: [PATCH 03/32] chore: Enable clang-tidy `bugprone-inc-dec-in-conditions` check (#6455) --- .clang-tidy | 2 +- src/libxrpl/basics/base64.cpp | 2 +- src/libxrpl/json/json_reader.cpp | 21 +++++++++------------ src/libxrpl/json/json_writer.cpp | 2 +- src/libxrpl/ledger/BookDirs.cpp | 10 +++++----- 5 files changed, 17 insertions(+), 20 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 87b6a42daa..dd95153ff5 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -14,6 +14,7 @@ Checks: "-*, bugprone-fold-init-type, bugprone-forward-declaration-namespace, bugprone-inaccurate-erase, + bugprone-inc-dec-in-conditions, bugprone-incorrect-enable-if, bugprone-incorrect-roundings, bugprone-infinite-loop, @@ -97,7 +98,6 @@ Checks: "-*, # checks that have some issues that need to be resolved: # # bugprone-crtp-constructor-accessibility, -# bugprone-inc-dec-in-conditions, # bugprone-move-forwarding-reference, # bugprone-switch-missing-default-case, # bugprone-unused-return-value, diff --git a/src/libxrpl/basics/base64.cpp b/src/libxrpl/basics/base64.cpp index fa06ac2cdc..76e924f42e 100644 --- a/src/libxrpl/basics/base64.cpp +++ b/src/libxrpl/basics/base64.cpp @@ -168,7 +168,7 @@ decode(void* dest, char const* src, std::size_t len) break; ++in; c4[i] = v; - if (++i == 4) + if (++i; i == 4) { c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4); c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); diff --git a/src/libxrpl/json/json_reader.cpp b/src/libxrpl/json/json_reader.cpp index d28268454f..3e75757f12 100644 --- a/src/libxrpl/json/json_reader.cpp +++ b/src/libxrpl/json/json_reader.cpp @@ -729,21 +729,18 @@ Reader::decodeUnicodeCodePoint(Token& token, Location& current, Location end, un unsigned int surrogatePair; - if (*(current++) == '\\' && *(current++) == 'u') - { - if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) - { - unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); - } - else - return false; - } - else + if (*current != '\\' || *(current + 1) != 'u') return addError( - "expecting another \\u token to begin the second half of a " - "unicode surrogate pair", + "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current); + + current += 2; // skip two characters checked above + + if (!decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) + return false; + + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); } return true; diff --git a/src/libxrpl/json/json_writer.cpp b/src/libxrpl/json/json_writer.cpp index 13baf95845..8ba2576552 100644 --- a/src/libxrpl/json/json_writer.cpp +++ b/src/libxrpl/json/json_writer.cpp @@ -319,7 +319,7 @@ StyledWriter::writeValue(Value const& value) document_ += " : "; writeValue(childValue); - if (++it == members.end()) + if (++it; it == members.end()) break; document_ += ","; diff --git a/src/libxrpl/ledger/BookDirs.cpp b/src/libxrpl/ledger/BookDirs.cpp index 4ba5945e8e..699d6c2879 100644 --- a/src/libxrpl/ledger/BookDirs.cpp +++ b/src/libxrpl/ledger/BookDirs.cpp @@ -74,8 +74,10 @@ BookDirs::const_iterator::operator++() XRPL_ASSERT(index_ != zero, "xrpl::BookDirs::const_iterator::operator++ : nonzero index"); if (!cdirNext(*view_, cur_key_, sle_, entry_, index_)) { - if (index_ != 0 || - (cur_key_ = view_->succ(++cur_key_, next_quality_).value_or(zero)) == zero) + if (index_ == 0) + cur_key_ = view_->succ(++cur_key_, next_quality_).value_or(zero); + + if (index_ != 0 || cur_key_ == zero) { cur_key_ = key_; entry_ = 0; @@ -84,9 +86,7 @@ BookDirs::const_iterator::operator++() else if (!cdirFirst(*view_, cur_key_, sle_, entry_, index_)) { // LCOV_EXCL_START - UNREACHABLE( - "xrpl::BookDirs::const_iterator::operator++ : directory is " - "empty"); + UNREACHABLE("xrpl::BookDirs::const_iterator::operator++ : directory is empty"); // LCOV_EXCL_STOP } } From eb7c8c6c7a6dc5b11cc06c1beac06170a714f77f Mon Sep 17 00:00:00 2001 From: Michael Legleux Date: Tue, 10 Mar 2026 16:38:43 -0700 Subject: [PATCH 04/32] chore: Use CMake components for install (#6485) * chore: Use components for install * rm CMake export targets * reformat --- CMakeLists.txt | 1 - cmake/XrplConfig.cmake | 60 --------------------- cmake/XrplInstall.cmake | 112 +++++++++------------------------------- 3 files changed, 25 insertions(+), 148 deletions(-) delete mode 100644 cmake/XrplConfig.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ca0798ae4..33f68451c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,7 +131,6 @@ if(coverage) include(XrplCov) endif() -set(PROJECT_EXPORT_SET XrplExports) include(XrplCore) include(XrplInstall) include(XrplValidatorKeys) diff --git a/cmake/XrplConfig.cmake b/cmake/XrplConfig.cmake deleted file mode 100644 index 76f9af14b1..0000000000 --- a/cmake/XrplConfig.cmake +++ /dev/null @@ -1,60 +0,0 @@ -include(CMakeFindDependencyMacro) -# need to represent system dependencies of the lib here -#[=========================================================[ - Boost -#]=========================================================] -if(static OR APPLE OR MSVC) - set(Boost_USE_STATIC_LIBS ON) -endif() -set(Boost_USE_MULTITHREADED ON) -if(static OR MSVC) - set(Boost_USE_STATIC_RUNTIME ON) -else() - set(Boost_USE_STATIC_RUNTIME OFF) -endif() -find_dependency( - Boost - COMPONENTS - chrono - container - context - coroutine - date_time - filesystem - program_options - regex - system - thread -) -#[=========================================================[ - OpenSSL -#]=========================================================] -if(NOT DEFINED OPENSSL_ROOT_DIR) - if(DEFINED ENV{OPENSSL_ROOT}) - set(OPENSSL_ROOT_DIR $ENV{OPENSSL_ROOT}) - elseif(APPLE) - find_program(homebrew brew) - if(homebrew) - execute_process( - COMMAND ${homebrew} --prefix openssl - OUTPUT_VARIABLE OPENSSL_ROOT_DIR - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - endif() - endif() - file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}" OPENSSL_ROOT_DIR) -endif() - -if(static OR APPLE OR MSVC) - set(OPENSSL_USE_STATIC_LIBS ON) -endif() -set(OPENSSL_MSVC_STATIC_RT ON) -find_dependency(OpenSSL REQUIRED) -find_dependency(ZLIB) -find_dependency(date) -if(TARGET ZLIB::ZLIB) - set_target_properties( - OpenSSL::Crypto - PROPERTIES INTERFACE_LINK_LIBRARIES ZLIB::ZLIB - ) -endif() diff --git a/cmake/XrplInstall.cmake b/cmake/XrplInstall.cmake index 6ea41b5ffd..339cdb51ec 100644 --- a/cmake/XrplInstall.cmake +++ b/cmake/XrplInstall.cmake @@ -2,100 +2,38 @@ install stuff #]===================================================================] -include(create_symbolic_link) +include(GNUInstallDirs) -# If no suffix is defined for executables (e.g. Windows uses .exe but Linux -# and macOS use none), then explicitly set it to the empty string. -if(NOT DEFINED suffix) - set(suffix "") +if(is_root_project AND TARGET xrpld) + install( + TARGETS xrpld + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT runtime + ) + + install( + FILES "${CMAKE_CURRENT_SOURCE_DIR}/cfg/xrpld-example.cfg" + DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/xrpld" + RENAME xrpld.cfg + COMPONENT runtime + ) + + install( + FILES "${CMAKE_CURRENT_SOURCE_DIR}/cfg/validators-example.txt" + DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/xrpld" + RENAME validators.txt + COMPONENT runtime + ) endif() install( - TARGETS - common - opts - xrpl_boost - xrpl_libs - xrpl_syslibs - xrpl.imports.main - xrpl.libpb - xrpl.libxrpl - xrpl.libxrpl.basics - xrpl.libxrpl.beast - xrpl.libxrpl.conditions - xrpl.libxrpl.core - xrpl.libxrpl.crypto - xrpl.libxrpl.git - xrpl.libxrpl.json - xrpl.libxrpl.rdb - xrpl.libxrpl.ledger - xrpl.libxrpl.net - xrpl.libxrpl.nodestore - xrpl.libxrpl.protocol - xrpl.libxrpl.resource - xrpl.libxrpl.server - xrpl.libxrpl.shamap - xrpl.libxrpl.tx - antithesis-sdk-cpp - EXPORT XrplExports - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib - RUNTIME DESTINATION bin - INCLUDES DESTINATION include + TARGETS xrpl.libpb xrpl.libxrpl + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT development + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT development + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT development ) install( DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/xrpl" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" -) - -install( - EXPORT XrplExports - FILE XrplTargets.cmake - NAMESPACE Xrpl:: - DESTINATION lib/cmake/xrpl -) -include(CMakePackageConfigHelpers) -write_basic_package_version_file( - XrplConfigVersion.cmake - VERSION ${xrpld_version} - COMPATIBILITY SameMajorVersion -) - -if(is_root_project AND TARGET xrpld) - install(TARGETS xrpld RUNTIME DESTINATION bin) - set_target_properties(xrpld PROPERTIES INSTALL_RPATH_USE_LINK_PATH ON) - # sample configs should not overwrite existing files - # install if-not-exists workaround as suggested by - # https://cmake.org/Bug/view.php?id=12646 - install( - CODE - " - macro (copy_if_not_exists SRC DEST NEWNAME) - if (NOT EXISTS \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\${DEST}/\${NEWNAME}\") - file (INSTALL FILE_PERMISSIONS OWNER_READ OWNER_WRITE DESTINATION \"\${CMAKE_INSTALL_PREFIX}/\${DEST}\" FILES \"\${SRC}\" RENAME \"\${NEWNAME}\") - else () - message (\"-- Skipping : \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\${DEST}/\${NEWNAME}\") - endif () - endmacro() - copy_if_not_exists(\"${CMAKE_CURRENT_SOURCE_DIR}/cfg/xrpld-example.cfg\" etc xrpld.cfg) - copy_if_not_exists(\"${CMAKE_CURRENT_SOURCE_DIR}/cfg/validators-example.txt\" etc validators.txt) - " - ) - install( - CODE - " - set(CMAKE_MODULE_PATH \"${CMAKE_MODULE_PATH}\") - include(create_symbolic_link) - create_symbolic_link(xrpld${suffix} \ - \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/rippled${suffix}) - " - ) -endif() - -install( - FILES - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/XrplConfig.cmake - ${CMAKE_CURRENT_BINARY_DIR}/XrplConfigVersion.cmake - DESTINATION lib/cmake/xrpl + COMPONENT development ) From 24a5cbaa93baef97d7aa906f30ea95a983b391e7 Mon Sep 17 00:00:00 2001 From: Michael Legleux Date: Tue, 10 Mar 2026 16:59:43 -0700 Subject: [PATCH 05/32] chore: Build voidstar on amd64 only (#6481) * chore: Build voidstar on amd64 only * fatal error if configuring voidstar on non x86 --- .github/scripts/strategy-matrix/generate.py | 4 ++-- cmake/XrplSanity.cmake | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/scripts/strategy-matrix/generate.py b/.github/scripts/strategy-matrix/generate.py index 27eb60c005..532bf2ed57 100755 --- a/.github/scripts/strategy-matrix/generate.py +++ b/.github/scripts/strategy-matrix/generate.py @@ -55,7 +55,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list: # fee to 500. # - Bookworm using GCC 15: Debug on linux/amd64, enable code # coverage (which will be done below). - # - Bookworm using Clang 16: Debug on linux/arm64, enable voidstar. + # - Bookworm using Clang 16: Debug on linux/amd64, enable voidstar. # - Bookworm using Clang 17: Release on linux/amd64, set the # reference fee to 1000. # - Bookworm using Clang 20: Debug on linux/amd64. @@ -78,7 +78,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list: if ( f"{os['compiler_name']}-{os['compiler_version']}" == "clang-16" and build_type == "Debug" - and architecture["platform"] == "linux/arm64" + and architecture["platform"] == "linux/amd64" ): cmake_args = f"-Dvoidstar=ON {cmake_args}" skip = False diff --git a/cmake/XrplSanity.cmake b/cmake/XrplSanity.cmake index 24b4d4d408..a35645ad5c 100644 --- a/cmake/XrplSanity.cmake +++ b/cmake/XrplSanity.cmake @@ -50,6 +50,13 @@ if(MSVC AND CMAKE_GENERATOR_PLATFORM STREQUAL "Win32") message(FATAL_ERROR "Visual Studio 32-bit build is not supported.") endif() +if(voidstar AND NOT is_amd64) + message( + FATAL_ERROR + "The voidstar library only supported on amd64/x86_64. Detected archictecture was: ${CMAKE_SYSTEM_PROCESSOR}" + ) +endif() + if(APPLE AND NOT HOMEBREW) find_program(HOMEBREW brew) endif() From 3baf5454f2d7ec30f360a684ec7e05cdad38667b Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 11 Mar 2026 11:48:40 +0100 Subject: [PATCH 06/32] ci: Only upload artifacts in the XRPLF/rippled repository (#6523) This change will only attempt to upload artifacts for CI runs performed in the XRPLF/rippled repository. --- .github/workflows/on-pr.yml | 5 ++--- .github/workflows/on-tag.yml | 3 +-- .github/workflows/on-trigger.yml | 4 ++-- .github/workflows/reusable-build-test-config.yml | 4 ++-- .github/workflows/upload-conan-deps.yml | 4 ++-- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index b0010fe0ba..f54a95e223 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -141,9 +141,8 @@ jobs: needs: - should-run - build-test - # Only run when committing to a PR that targets a release branch in the - # XRPLF repository. - if: ${{ github.repository_owner == 'XRPLF' && needs.should-run.outputs.go == 'true' && startsWith(github.ref, 'refs/heads/release') }} + # Only run when committing to a PR that targets a release branch. + if: ${{ github.repository == 'XRPLF/rippled' && needs.should-run.outputs.go == 'true' && startsWith(github.ref, 'refs/heads/release') }} uses: ./.github/workflows/reusable-upload-recipe.yml secrets: remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} diff --git a/.github/workflows/on-tag.yml b/.github/workflows/on-tag.yml index c6361b4016..af3ea4309e 100644 --- a/.github/workflows/on-tag.yml +++ b/.github/workflows/on-tag.yml @@ -17,8 +17,7 @@ defaults: jobs: upload-recipe: - # Only run when a tag is pushed to the XRPLF repository. - if: ${{ github.repository_owner == 'XRPLF' }} + if: ${{ github.repository == 'XRPLF/rippled' }} uses: ./.github/workflows/reusable-upload-recipe.yml secrets: remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index c7e0e8c3aa..5856c67bd3 100644 --- a/.github/workflows/on-trigger.yml +++ b/.github/workflows/on-trigger.yml @@ -92,8 +92,8 @@ jobs: upload-recipe: needs: build-test - # Only run when pushing to the develop branch in the XRPLF repository. - if: ${{ github.repository_owner == 'XRPLF' && github.event_name == 'push' && github.ref == 'refs/heads/develop' }} + # Only run when pushing to the develop branch. + if: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' && github.ref == 'refs/heads/develop' }} uses: ./.github/workflows/reusable-upload-recipe.yml secrets: remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index 75fe546b18..66f46709cd 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -176,7 +176,7 @@ jobs: fi - name: Upload the binary (Linux) - if: ${{ github.repository_owner == 'XRPLF' && runner.os == 'Linux' }} + if: ${{ github.repository == 'XRPLF/rippled' && runner.os == 'Linux' }} uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: xrpld-${{ inputs.config_name }} @@ -266,7 +266,7 @@ jobs: --target coverage - name: Upload coverage report - if: ${{ github.repository_owner == 'XRPLF' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }} + if: ${{ github.repository == 'XRPLF/rippled' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }} uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 with: disable_search: true diff --git a/.github/workflows/upload-conan-deps.yml b/.github/workflows/upload-conan-deps.yml index df8aa43a18..d0fea4b8ae 100644 --- a/.github/workflows/upload-conan-deps.yml +++ b/.github/workflows/upload-conan-deps.yml @@ -103,11 +103,11 @@ jobs: sanitizers: ${{ matrix.sanitizers }} - name: Log into Conan remote - if: ${{ github.repository_owner == 'XRPLF' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }} + if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }} run: conan remote login "${CONAN_REMOTE_NAME}" "${{ secrets.CONAN_REMOTE_USERNAME }}" --password "${{ secrets.CONAN_REMOTE_PASSWORD }}" - name: Upload Conan packages - if: ${{ github.repository_owner == 'XRPLF' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }} + if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }} env: FORCE_OPTION: ${{ github.event.inputs.force_upload == 'true' && '--force' || '' }} run: conan upload "*" --remote="${CONAN_REMOTE_NAME}" --confirm ${FORCE_OPTION} From 01c977bbfe4a36eb8ab65084fa5781d44d8bc6e5 Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 11 Mar 2026 13:43:58 +0100 Subject: [PATCH 07/32] ci: Fix rules used to determine when to upload Conan recipes (#6524) The refs as previously used pointed to the source branch, not the target branch. However, determining the target branch is different depending on the GitHub event. The pull request logic was incorrect and needed to be fixed, and the logic inside the workflow could be simplified. Both modifications have been made in this commit. --- .github/workflows/on-pr.yml | 2 +- .github/workflows/reusable-upload-recipe.yml | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index f54a95e223..66893d19d3 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -142,7 +142,7 @@ jobs: - should-run - build-test # Only run when committing to a PR that targets a release branch. - if: ${{ github.repository == 'XRPLF/rippled' && needs.should-run.outputs.go == 'true' && startsWith(github.ref, 'refs/heads/release') }} + if: ${{ github.repository == 'XRPLF/rippled' && needs.should-run.outputs.go == 'true' && github.event_name == 'pull_request' && startsWith(github.event.pull_request.base.ref, 'release') }} uses: ./.github/workflows/reusable-upload-recipe.yml secrets: remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} diff --git a/.github/workflows/reusable-upload-recipe.yml b/.github/workflows/reusable-upload-recipe.yml index 6245fd06e1..178dd65b8e 100644 --- a/.github/workflows/reusable-upload-recipe.yml +++ b/.github/workflows/reusable-upload-recipe.yml @@ -69,22 +69,28 @@ jobs: conan export . --version=${{ steps.version.outputs.version }} conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/${{ steps.version.outputs.version }} + # When this workflow is triggered by a push event, it will always be when merging into the + # 'develop' branch, see on-trigger.yml. - name: Upload Conan recipe (develop) - if: ${{ github.ref == 'refs/heads/develop' }} + if: ${{ github.event_name == 'push' }} env: REMOTE_NAME: ${{ inputs.remote_name }} run: | conan export . --version=develop conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/develop + # When this workflow is triggered by a pull request event, it will always be when merging into + # one of the 'release' branches, see on-pr.yml. - name: Upload Conan recipe (rc) - if: ${{ startsWith(github.ref, 'refs/heads/release') }} + if: ${{ github.event_name == 'pull_request' }} env: REMOTE_NAME: ${{ inputs.remote_name }} run: | conan export . --version=rc conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/rc + # When this workflow is triggered by a tag event, it will always be when tagging a final + # release, see on-tag.yml. - name: Upload Conan recipe (release) if: ${{ github.event_name == 'tag' }} env: From bee2d112c6cc067e478d5f9dbc9748ad927382c4 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 11 Mar 2026 13:18:18 +0000 Subject: [PATCH 08/32] ci: Fix how clang-tidy is run when .clang-tidy is changed (#6521) --- .clang-tidy | 2 +- .github/workflows/reusable-clang-tidy.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index dd95153ff5..fc067fde6f 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -100,9 +100,9 @@ Checks: "-*, # bugprone-crtp-constructor-accessibility, # bugprone-move-forwarding-reference, # bugprone-switch-missing-default-case, +# bugprone-unused-raii, # bugprone-unused-return-value, # bugprone-use-after-move, -# bugprone-unused-raii, # # cppcoreguidelines-misleading-capture-default-by-value, # cppcoreguidelines-init-variables, diff --git a/.github/workflows/reusable-clang-tidy.yml b/.github/workflows/reusable-clang-tidy.yml index 5319c1627a..3d4bc3b2e3 100644 --- a/.github/workflows/reusable-clang-tidy.yml +++ b/.github/workflows/reusable-clang-tidy.yml @@ -51,5 +51,5 @@ jobs: if: ${{ always() && !cancelled() && (!inputs.check_only_changed || needs.determine-files.outputs.any_cpp_changed == 'true' || needs.determine-files.outputs.clang_tidy_config_changed == 'true') }} uses: ./.github/workflows/reusable-clang-tidy-files.yml with: - files: ${{ (needs.determine-files.outputs.clang_tidy_config_changed == 'true' && '') || (inputs.check_only_changed && needs.determine-files.outputs.all_changed_files || '') }} + files: ${{ needs.determine-files.outputs.clang_tidy_config_changed == 'true' && '' || (inputs.check_only_changed && needs.determine-files.outputs.all_changed_files || '') }} create_issue_on_failure: ${{ inputs.create_issue_on_failure }} From 7b3724b7a35596a39e6ee740f5a16bf08d5ed54f Mon Sep 17 00:00:00 2001 From: Alex Kremer Date: Wed, 11 Mar 2026 14:04:26 +0000 Subject: [PATCH 09/32] fix: Add missed clang-tidy `bugprone-inc-dec-conditions` check (#6526) --- src/xrpld/app/ledger/detail/LedgerReplayer.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/xrpld/app/ledger/detail/LedgerReplayer.cpp b/src/xrpld/app/ledger/detail/LedgerReplayer.cpp index f649eb4d95..5aacf5b5a6 100644 --- a/src/xrpld/app/ledger/detail/LedgerReplayer.cpp +++ b/src/xrpld/app/ledger/detail/LedgerReplayer.cpp @@ -98,8 +98,12 @@ LedgerReplayer::createDeltas(std::shared_ptr task) { auto skipListItem = std::find(parameter.skipList_.begin(), parameter.skipList_.end(), parameter.startHash_); - if (skipListItem == parameter.skipList_.end() || - ++skipListItem == parameter.skipList_.end()) + auto const wasLast = skipListItem == parameter.skipList_.end(); + if (not wasLast) + ++skipListItem; + auto const isLast = skipListItem == parameter.skipList_.end(); + + if (wasLast || isLast) { JLOG(j_.error()) << "Task parameter error when creating deltas " << parameter.finishHash_; From c791cae1ec627bd34b29894eb9ea5ef78b36ce1e Mon Sep 17 00:00:00 2001 From: Sergey Kuznetsov Date: Wed, 11 Mar 2026 18:06:12 +0000 Subject: [PATCH 10/32] test: Fix flaky subscribe tests (#6510) Subscribe tests have a problem that there is no way to synchronize application running in background threads and test threads. Threads are communicating via websocket messages. When the code is compiled in debug mode with code coverage enabled it executes quite slow, so receiving websocket messages by the client in subscribe tests may time out. This change does 2 things to fix the problem: * Increases timeout for receiving a websocket message. * Decreases the number of tests running in parallel. While testing the fix for subscribe test another flaky test in ledger replay was found, which has also been addressed. --- .../workflows/reusable-build-test-config.yml | 2 ++ src/test/app/LedgerReplay_test.cpp | 24 ++++++++++++++++--- src/test/rpc/Subscribe_test.cpp | 16 ++++++++----- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index 66f46709cd..83ece81919 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -230,6 +230,8 @@ jobs: BUILD_NPROC: ${{ steps.nproc.outputs.nproc }} run: | set -o pipefail + # Coverage builds are slower due to instrumentation; use fewer parallel jobs to avoid flakiness + [ "$COVERAGE_ENABLED" = "true" ] && BUILD_NPROC=$(( BUILD_NPROC - 2 )) ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log - name: Show test failure summary diff --git a/src/test/app/LedgerReplay_test.cpp b/src/test/app/LedgerReplay_test.cpp index 5568a90d03..4428e82ea9 100644 --- a/src/test/app/LedgerReplay_test.cpp +++ b/src/test/app/LedgerReplay_test.cpp @@ -1128,9 +1128,27 @@ struct LedgerReplayer_test : public beast::unit_test::suite BEAST_EXPECT(net.client.waitAndCheckStatus( finalHash, totalReplay, TaskStatus::Completed, TaskStatus::Completed, deltaStatuses)); - // sweep - net.client.replayer.sweep(); - BEAST_EXPECT(net.client.countsAsExpected(0, 0, 0)); + // sweep() cleans up skipLists_ and deltas_ by removing entries whose + // weak_ptr can no longer be locked. Those weak_ptrs expire only when the + // last shared_ptr holder releases the sub-task. The sole owner is the + // LedgerReplayTask, but a JobQueue worker thread may still hold a + // temporary shared_ptr to a sub-task (from wptr.lock()) while executing + // the timer job that drove the task to completion. If sweep() runs before + // that thread unwinds, the weak_ptr is still lockable and the map entry + // is not removed. We retry until the worker thread finishes. + auto waitForSweep = [&net]() { + for (auto numAttempts = 0; numAttempts < 20; ++numAttempts) + { + net.client.replayer.sweep(); + if (net.client.countsAsExpected(0, 0, 0)) + { + return true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return false; + }; + BEAST_EXPECT(waitForSweep()); } void diff --git a/src/test/rpc/Subscribe_test.cpp b/src/test/rpc/Subscribe_test.cpp index 414bceefd7..9a24980d49 100644 --- a/src/test/rpc/Subscribe_test.cpp +++ b/src/test/rpc/Subscribe_test.cpp @@ -802,13 +802,17 @@ public: * return {true, true} if received numReplies replies and also * received a tx with the account_history_tx_first == true */ - auto getTxHash = [](WSClient& wsc, IdxHashVec& v, int numReplies) -> std::pair { + auto getTxHash = [](WSClient& wsc, + IdxHashVec& v, + int numReplies, + std::chrono::milliseconds timeout = + std::chrono::milliseconds{5000}) -> std::pair { bool first_flag = false; for (int i = 0; i < numReplies; ++i) { std::uint32_t idx{0}; - auto reply = wsc.getMsg(100ms); + auto reply = wsc.getMsg(timeout); if (reply) { auto r = *reply; @@ -982,7 +986,7 @@ public: BEAST_EXPECT(goodSubRPC(jv)); sendPayments(env, env.master, alice, 1, 1); - r = getTxHash(*wscTxHistory, vec, 1); + r = getTxHash(*wscTxHistory, vec, 1, 10ms); BEAST_EXPECT(!r.first); } { @@ -1001,7 +1005,7 @@ public: return; IdxHashVec genesisFullHistoryVec; BEAST_EXPECT(env.syncClose()); - if (!BEAST_EXPECT(!getTxHash(*wscTxHistory, genesisFullHistoryVec, 1).first)) + if (!BEAST_EXPECT(!getTxHash(*wscTxHistory, genesisFullHistoryVec, 1, 10ms).first)) return; /* @@ -1161,7 +1165,7 @@ public: { // take out existing txns from the stream IdxHashVec tempVec; - getTxHash(*ws, tempVec, 100); + getTxHash(*ws, tempVec, 100, 1000ms); } auto count = mixedPayments(); @@ -1195,7 +1199,7 @@ public: { // take out existing txns from the stream IdxHashVec tempVec; - getTxHash(*wscLong, tempVec, 100); + getTxHash(*wscLong, tempVec, 100, 1000ms); } // repeat the payments many rounds From ce9ccf844a23963d08fc55ccc8a1d4b32d9cf41b Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 11 Mar 2026 15:36:03 -0400 Subject: [PATCH 11/32] fix: Remove unneeded import, fix log (#6532) This change: * Removes an unneeded import in `DeleteAccount.cpp`. * Fixes a typo in a log statement in `SetAccount.cpp`. --- src/libxrpl/tx/transactors/account/DeleteAccount.cpp | 1 - src/libxrpl/tx/transactors/account/SetAccount.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libxrpl/tx/transactors/account/DeleteAccount.cpp b/src/libxrpl/tx/transactors/account/DeleteAccount.cpp index a9f7dbe674..68c0d7f304 100644 --- a/src/libxrpl/tx/transactors/account/DeleteAccount.cpp +++ b/src/libxrpl/tx/transactors/account/DeleteAccount.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/account/SetAccount.cpp b/src/libxrpl/tx/transactors/account/SetAccount.cpp index 32eb2afbb8..032b4727ad 100644 --- a/src/libxrpl/tx/transactors/account/SetAccount.cpp +++ b/src/libxrpl/tx/transactors/account/SetAccount.cpp @@ -491,7 +491,7 @@ SetAccount::doApply() if (messageKey.empty()) { - JLOG(j_.debug()) << "set message key"; + JLOG(j_.debug()) << "clear message key"; sle->makeFieldAbsent(sfMessageKey); } else From 46d5c67a8d2315e44e93dcbaf5dd3402d22cba28 Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Wed, 11 Mar 2026 17:27:35 -0400 Subject: [PATCH 12/32] fix: Mark SAV and Lending transactions as `NotDelegable` (#6489) New transactions should be marked as `NotDelegable`, until the interactions with other transactions have been fully tested and validated. --- .../xrpl/protocol/detail/transactions.macro | 30 ++--- src/test/app/Delegate_test.cpp | 59 +++++++- src/test/app/Vault_test.cpp | 126 ------------------ 3 files changed, 67 insertions(+), 148 deletions(-) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index 08832acde7..01f5b50fb6 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -830,7 +830,7 @@ TRANSACTION(ttDELEGATE_SET, 64, DelegateSet, # include #endif TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, createPseudoAcct | createMPTIssuance | mustModifyVault, ({ @@ -848,7 +848,7 @@ TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, # include #endif TRANSACTION(ttVAULT_SET, 66, VaultSet, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, mustModifyVault, ({ @@ -863,7 +863,7 @@ TRANSACTION(ttVAULT_SET, 66, VaultSet, # include #endif TRANSACTION(ttVAULT_DELETE, 67, VaultDelete, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, mustDeleteAcct | destroyMPTIssuance | mustModifyVault, ({ @@ -875,7 +875,7 @@ TRANSACTION(ttVAULT_DELETE, 67, VaultDelete, # include #endif TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, mayAuthorizeMPT | mustModifyVault, ({ @@ -888,7 +888,7 @@ TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit, # include #endif TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, mayDeleteMPT | mayAuthorizeMPT | mustModifyVault, ({ @@ -903,7 +903,7 @@ TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, # include #endif TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, mayDeleteMPT | mustModifyVault, ({ @@ -932,7 +932,7 @@ TRANSACTION(ttBATCH, 71, Batch, # include #endif TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, createPseudoAcct | mayAuthorizeMPT, ({ {sfVaultID, soeREQUIRED}, @@ -949,7 +949,7 @@ TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet, # include #endif TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, mustDeleteAcct | mayAuthorizeMPT, ({ {sfLoanBrokerID, soeREQUIRED}, @@ -960,7 +960,7 @@ TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete, # include #endif TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, noPriv, ({ {sfLoanBrokerID, soeREQUIRED}, @@ -972,7 +972,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit, # include #endif TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, mayAuthorizeMPT, ({ {sfLoanBrokerID, soeREQUIRED}, @@ -987,7 +987,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw, # include #endif TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, noPriv, ({ {sfLoanBrokerID, soeOPTIONAL}, @@ -999,7 +999,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback, # include #endif TRANSACTION(ttLOAN_SET, 80, LoanSet, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, mayAuthorizeMPT | mustModifyVault, ({ {sfLoanBrokerID, soeREQUIRED}, @@ -1026,7 +1026,7 @@ TRANSACTION(ttLOAN_SET, 80, LoanSet, # include #endif TRANSACTION(ttLOAN_DELETE, 81, LoanDelete, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, noPriv, ({ {sfLoanID, soeREQUIRED}, @@ -1037,7 +1037,7 @@ TRANSACTION(ttLOAN_DELETE, 81, LoanDelete, # include #endif TRANSACTION(ttLOAN_MANAGE, 82, LoanManage, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, // All of the LoanManage options will modify the vault, but the // transaction can succeed without options, essentially making it @@ -1051,7 +1051,7 @@ TRANSACTION(ttLOAN_MANAGE, 82, LoanManage, # include #endif TRANSACTION(ttLOAN_PAY, 84, LoanPay, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, mayAuthorizeMPT | mustModifyVault, ({ {sfLoanID, soeREQUIRED}, diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp index 31a394eeeb..cbfa29c215 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -1614,13 +1614,7 @@ class Delegate_test : public beast::unit_test::suite {"CredentialDelete", featureCredentials}, {"NFTokenModify", featureDynamicNFT}, {"PermissionedDomainSet", featurePermissionedDomains}, - {"PermissionedDomainDelete", featurePermissionedDomains}, - {"VaultCreate", featureSingleAssetVault}, - {"VaultSet", featureSingleAssetVault}, - {"VaultDelete", featureSingleAssetVault}, - {"VaultDeposit", featureSingleAssetVault}, - {"VaultWithdraw", featureSingleAssetVault}, - {"VaultClawback", featureSingleAssetVault}}; + {"PermissionedDomainDelete", featurePermissionedDomains}}; // Can not delegate tx if any required feature disabled. { @@ -1660,6 +1654,56 @@ class Delegate_test : public beast::unit_test::suite } } + void + testTxDelegableCount() + { + testcase("Delegable Transactions Completeness"); + + std::size_t delegableCount = 0; + +#pragma push_macro("TRANSACTION") +#undef TRANSACTION + +#define TRANSACTION(tag, value, name, delegable, ...) \ + if (delegable == xrpl::delegable) \ + { \ + delegableCount++; \ + } + +#include + +#undef TRANSACTION +#pragma pop_macro("TRANSACTION") + + // ==================================================================== + // IMPORTANT NOTICE: + // + // If this test fails, it indicates that the 'Delegation::delegable' status + // in transactions.macro has been changed. Delegation allows accounts to act + // on behalf of others, significantly increasing the security surface. + // + // + // To ENSURE any added transaction is safe and compatible with delegation: + // + // 1. Verify that the transaction is intended to be delegable. + // 2. Every standard test case for that transaction MUST be + // duplicated and verified for a Delegated context. + // 3. Ensure that Fee, Reserve, and Signing are correctly handled. + // + // DO NOT modify expectedDelegableCount unless all scenarios, including + // edge cases, have been fully tested and verified. + // ==================================================================== + std::size_t const expectedDelegableCount = 75; + + BEAST_EXPECTS( + delegableCount == expectedDelegableCount, + "\n[SECURITY] New delegable transaction detected!" + "\n Expected: " + + std::to_string(expectedDelegableCount) + + "\n Actual: " + std::to_string(delegableCount) + + "\n Action: Verify security requirements to interact with Delegation feature"); + } + void run() override { @@ -1684,6 +1728,7 @@ class Delegate_test : public beast::unit_test::suite testMultiSignQuorumNotMet(); testPermissionValue(all); testTxRequireFeatures(all); + testTxDelegableCount(); } }; BEAST_DEFINE_TESTSUITE(Delegate, app, xrpl); diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index 7ae9faf18f..26ec59994d 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -4500,131 +4500,6 @@ class Vault_test : public beast::unit_test::suite } } - void - testDelegate() - { - using namespace test::jtx; - - Env env(*this, testable_amendments()); - Account alice{"alice"}; - Account bob{"bob"}; - Account carol{"carol"}; - - struct CaseArgs - { - PrettyAsset asset = xrpIssue(); - }; - - auto const xrpBalance = [this]( - Env const& env, Account const& account) -> std::optional { - auto sle = env.le(keylet::account(account.id())); - if (BEAST_EXPECT(sle != nullptr)) - return sle->getFieldAmount(sfBalance).xrp().drops(); - return std::nullopt; - }; - - auto testCase = [&, this](auto test, CaseArgs args = {}) { - Env env{*this, testable_amendments() | featureSingleAssetVault}; - - Vault vault{env}; - - // use different initial amount to distinguish the source balance - env.fund(XRP(10000), alice); - env.fund(XRP(20000), bob); - env.fund(XRP(30000), carol); - env.close(); - - env(delegate::set( - carol, - alice, - {"Payment", - "VaultCreate", - "VaultSet", - "VaultDelete", - "VaultDeposit", - "VaultWithdraw", - "VaultClawback"})); - - test(env, vault, args.asset); - }; - - testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) { - testcase("delegated vault creation"); - auto startBalance = xrpBalance(env, carol); - if (!BEAST_EXPECT(startBalance.has_value())) - return; - - auto [tx, keylet] = vault.create({.owner = carol, .asset = asset}); - env(tx, delegate::as(alice)); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance); - }); - - testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) { - testcase("delegated deposit and withdrawal"); - auto [tx, keylet] = vault.create({.owner = carol, .asset = asset}); - env(tx); - env.close(); - - auto const amount = 1513; - auto const baseFee = env.current()->fees().base; - - auto startBalance = xrpBalance(env, carol); - if (!BEAST_EXPECT(startBalance.has_value())) - return; - - tx = vault.deposit({.depositor = carol, .id = keylet.key, .amount = asset(amount)}); - env(tx, delegate::as(alice)); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount); - - tx = - vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(amount - 1)}); - env(tx, delegate::as(alice)); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - 1); - - tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(1)}); - env(tx); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee); - }); - - testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) { - testcase("delegated withdrawal same as base fee and deletion"); - auto [tx, keylet] = vault.create({.owner = carol, .asset = asset}); - env(tx); - env.close(); - - auto const amount = 25537; - auto const baseFee = env.current()->fees().base; - - auto startBalance = xrpBalance(env, carol); - if (!BEAST_EXPECT(startBalance.has_value())) - return; - - tx = vault.deposit({.depositor = carol, .id = keylet.key, .amount = asset(amount)}); - env(tx); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount - baseFee); - - tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(baseFee)}); - env(tx, delegate::as(alice)); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount); - - tx = vault.withdraw( - {.depositor = carol, .id = keylet.key, .amount = asset(amount - baseFee)}); - env(tx, delegate::as(alice)); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee); - - tx = vault.del({.owner = carol, .id = keylet.key}); - env(tx, delegate::as(alice)); - env.close(); - }); - } - void testVaultClawbackBurnShares() { @@ -5374,7 +5249,6 @@ public: testFailedPseudoAccount(); testScaleIOU(); testRPC(); - testDelegate(); testVaultClawbackBurnShares(); testVaultClawbackAssets(); testAssetsMaximum(); From e460ea084029063a92183728c9fa047bd16677d9 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Thu, 12 Mar 2026 05:39:40 +0000 Subject: [PATCH 13/32] ci: Move Type of Change from PR template to CONTRIBUTING (#6522) Now that prefixes in PR titles are being validated as part of CI, the "Type of Change" section in the PR template is no longer needed. The prefixes and descriptions in the `CONTRIBUTING.md` file have been updated to reflect the currently supported list. --- .github/pull_request_template.md | 16 -------------- CONTRIBUTING.md | 37 +++++++++++++++----------------- 2 files changed, 17 insertions(+), 36 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3ab3a38807..f1f7aa18f7 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -29,22 +29,6 @@ If a refactor, how is this better than the previous implementation? If there is a spec or design document for this feature, please link it here. --> -### Type of Change - - - -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] Refactor (non-breaking change that only restructures code) -- [ ] Performance (increase or change in throughput and/or latency) -- [ ] Tests (you added tests for code that already exists, or your new feature included in this PR) -- [ ] Documentation update -- [ ] Chore (no impact to binary, e.g. `.gitignore`, formatting, dropping support for older tooling) -- [ ] Release - ### API Impact