diff --git a/.clang-tidy b/.clang-tidy index e8e2ca7ac9..35427810a3 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -11,6 +11,7 @@ Checks: "-*, bugprone-copy-constructor-init, bugprone-crtp-constructor-accessibility, bugprone-dangling-handle, + bugprone-derived-method-shadowing-base-method, bugprone-dynamic-static-initializers, bugprone-empty-catch, bugprone-fold-init-type, @@ -21,6 +22,7 @@ Checks: "-*, bugprone-incorrect-roundings, bugprone-infinite-loop, bugprone-integer-division, + bugprone-invalid-enum-default-initialization, bugprone-lambda-function-name, bugprone-macro-parentheses, bugprone-macro-repeated-side-effects, @@ -137,6 +139,7 @@ Checks: "-*, readability-enum-initial-value, readability-identifier-naming, readability-implicit-bool-conversion, + readability-inconsistent-ifelse-braces, readability-make-member-function-const, readability-math-missing-parentheses, readability-misleading-indentation, @@ -145,7 +148,9 @@ Checks: "-*, readability-redundant-declaration, readability-redundant-inline-specifier, readability-redundant-member-init, + readability-redundant-parentheses, readability-redundant-string-init, + readability-redundant-typename, readability-reference-to-constructed-temporary, readability-simplify-boolean-expr, readability-static-definition-in-anonymous-namespace, @@ -153,7 +158,8 @@ Checks: "-*, readability-use-std-min-max " # --- -# bugprone-narrowing-conversions, # this will break a lot of code but we should enable it in the future because it can eliminate a lot of bugs +# bugprone-narrowing-conversions, # This will break a lot of code but we should enable it in the future because it can eliminate a lot of bugs +# misc-override-with-different-visibility, # Will be addressed in a future PR, but for now it generates too many warnings # readability-inconsistent-declaration-parameter-name, # In this codebase this check will break a lot of arg names # readability-static-accessed-through-instance, # this check is probably unnecessary. It makes the code less readable # --- diff --git a/.gersemi/definitions.cmake b/.gersemi/definitions.cmake index 245f827f90..58bc74c70a 100644 --- a/.gersemi/definitions.cmake +++ b/.gersemi/definitions.cmake @@ -96,3 +96,6 @@ function(verbose_find_path variable name) ${ARGN} ) endfunction() + +function(patch_nix_binary target) +endfunction() diff --git a/.github/scripts/levelization/results/ordering.txt b/.github/scripts/levelization/results/ordering.txt index f0048dbf05..e58d06e215 100644 --- a/.github/scripts/levelization/results/ordering.txt +++ b/.github/scripts/levelization/results/ordering.txt @@ -83,7 +83,6 @@ test.conditions > xrpl.basics test.conditions > xrpl.conditions test.consensus > test.csf test.consensus > test.jtx -test.consensus > test.toplevel test.consensus > test.unit_test test.consensus > xrpl.basics test.consensus > xrpld.app diff --git a/.github/scripts/strategy-matrix/linux.json b/.github/scripts/strategy-matrix/linux.json index a9b85b766a..863b910dda 100644 --- a/.github/scripts/strategy-matrix/linux.json +++ b/.github/scripts/strategy-matrix/linux.json @@ -1,5 +1,5 @@ { - "image_tag": "sha-fe4c8ae", + "image_tag": "sha-e29b523", "configs": { "ubuntu": [ { diff --git a/.github/scripts/strategy-matrix/windows.json b/.github/scripts/strategy-matrix/windows.json index e25f9ad131..370e9f5bc7 100644 --- a/.github/scripts/strategy-matrix/windows.json +++ b/.github/scripts/strategy-matrix/windows.json @@ -1,6 +1,6 @@ { "platform": "windows/amd64", - "runner": ["self-hosted", "Windows", "devbox"], + "runner": ["self-hosted", "Windows", "dev-box-windows-2026"], "configs": [ { "build_type": "Release" }, { diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index 0cc9b375a7..0c9eeda712 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -70,6 +70,7 @@ jobs: .github/workflows/reusable-upload-recipe.yml .clang-tidy .codecov.yml + bin/check-tools.sh cfg/** cmake/** conan/** diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index 74bca82019..063cdbff7f 100644 --- a/.github/workflows/on-trigger.yml +++ b/.github/workflows/on-trigger.yml @@ -27,6 +27,7 @@ on: - ".github/workflows/reusable-upload-recipe.yml" - ".clang-tidy" - ".codecov.yml" + - "bin/check-tools.sh" - "cfg/**" - "cmake/**" - "conan/**" diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index cc7b6b6e7e..cb7d4c5382 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -41,7 +41,7 @@ env: jobs: build: runs-on: ubuntu-latest - container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-fe4c8ae + container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-e29b523 steps: - name: Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index 3e6464aaba..a81d9aec67 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -82,7 +82,7 @@ jobs: name: ${{ inputs.config_name }} runs-on: ${{ fromJSON(inputs.runs_on) }} container: ${{ inputs.image != '' && inputs.image || null }} - timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 90 }} + timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 180 }} env: # Use a namespace to keep the objects separate for each configuration. CCACHE_NAMESPACE: ${{ inputs.config_name }} @@ -163,7 +163,7 @@ jobs: CMAKE_ARGS: ${{ inputs.cmake_args }} run: | cmake \ - -G '${{ runner.os == 'Windows' && 'Visual Studio 17 2022' || 'Ninja' }}' \ + -G '${{ runner.os == 'Windows' && 'Visual Studio 18 2026' || 'Ninja' }}' \ -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ ${CMAKE_ARGS} \ @@ -229,21 +229,6 @@ jobs: --parallel "${BUILD_NPROC}" \ --target "${CMAKE_TARGET}" - # This step is needed to allow running in non-Nix environments - - name: Patch binary to use default loader and remove rpath (Linux) - if: ${{ runner.os == 'Linux' && env.SANITIZERS_ENABLED == 'false' }} - run: | - loader="$(/tmp/loader-path.sh)" - patchelf --set-interpreter "${loader}" --remove-rpath "${{ env.BUILD_DIR }}/xrpld" - - # We're only running aarch64 Linux builds in Ubuntu-based images, so this is kept simple - - name: Install libatomic (Linux aarch64) - if: ${{ runner.os == 'Linux' && runner.arch == 'ARM64' }} - run: | - apt update --yes - apt install -y --no-install-recommends \ - libatomic1 - - name: Show ccache statistics if: ${{ inputs.ccache_enabled }} run: | diff --git a/.github/workflows/reusable-clang-tidy.yml b/.github/workflows/reusable-clang-tidy.yml index e99ef574bf..f36463a5d0 100644 --- a/.github/workflows/reusable-clang-tidy.yml +++ b/.github/workflows/reusable-clang-tidy.yml @@ -39,7 +39,7 @@ jobs: needs: [determine-files] if: ${{ always() && !cancelled() && (!inputs.check_only_changed || needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.clang_tidy_config_changed == 'true') }} runs-on: ["self-hosted", "Linux", "X64", "heavy"] - container: "ghcr.io/xrplf/xrpld/nix-debian:sha-fe4c8ae" + container: "ghcr.io/xrplf/xrpld/nix-debian:sha-e29b523" permissions: contents: read issues: write diff --git a/.github/workflows/reusable-package.yml b/.github/workflows/reusable-package.yml index eed4bfc4a3..249e807592 100644 --- a/.github/workflows/reusable-package.yml +++ b/.github/workflows/reusable-package.yml @@ -39,23 +39,8 @@ jobs: working-directory: .github/scripts/strategy-matrix run: ./generate.py --packaging >>"${GITHUB_OUTPUT}" - generate-version: - runs-on: ubuntu-latest - outputs: - version: ${{ steps.version.outputs.version }} - steps: - - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - sparse-checkout: | - .github/actions/generate-version - src/libxrpl/protocol/BuildInfo.cpp - - name: Generate version - id: version - uses: ./.github/actions/generate-version - package: - needs: [generate-matrix, generate-version] + needs: [generate-matrix] if: ${{ github.event.repository.visibility == 'public' }} strategy: fail-fast: false @@ -82,14 +67,13 @@ jobs: - name: Build package env: - PKG_VERSION: ${{ needs.generate-version.outputs.version }} PKG_RELEASE: ${{ inputs.pkg_release }} run: ./package/build_pkg.sh - name: Upload package artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: ${{ matrix.artifact_name }}-pkg-${{ needs.generate-version.outputs.version }} + name: ${{ matrix.artifact_name }}-pkg path: | ${{ env.BUILD_DIR }}/debbuild/*.deb ${{ env.BUILD_DIR }}/debbuild/*.ddeb diff --git a/.github/workflows/reusable-upload-recipe.yml b/.github/workflows/reusable-upload-recipe.yml index a389e98771..a18f76796a 100644 --- a/.github/workflows/reusable-upload-recipe.yml +++ b/.github/workflows/reusable-upload-recipe.yml @@ -40,7 +40,7 @@ defaults: jobs: upload: runs-on: ubuntu-latest - container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-fe4c8ae + container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-e29b523 steps: - name: Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c9dec89435..4cbf4c1dd0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,6 +15,7 @@ repos: hooks: - id: check-added-large-files args: [--maxkb=400, --enforce-all] + - id: check-executables-have-shebangs - id: trailing-whitespace - id: end-of-file-fixer - id: check-merge-conflict @@ -35,13 +36,18 @@ repos: language: python types_or: [c++, c] exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/ + - id: fix-pragma-once + name: fix missing '#pragma once' declarations in header files + language: python + entry: ./bin/pre-commit/fix_pragma_once.py + files: \.(h|hpp)$ - repo: https://github.com/pre-commit/mirrors-clang-format rev: dd18dad857d6133e90bbe478f4f2f22ec0030269 # frozen: v22.1.5 hooks: - id: clang-format args: [--style=file] - "types_or": [c++, c, proto] + types_or: [c++, c, proto] exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/ - repo: https://github.com/BlankSpruce/gersemi-pre-commit diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dbe60a220..bdc62442b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,8 @@ if(target) ) endif() +include(PatchNixBinary) + include(XrplSanity) include(XrplVersion) include(XrplSettings) diff --git a/bin/check-tools.sh b/bin/check-tools.sh index 15b16b6fc8..808f384d5b 100755 --- a/bin/check-tools.sh +++ b/bin/check-tools.sh @@ -90,16 +90,19 @@ if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then check perl check pkg-config check vim + check zip # These tools are present in our Linux CI images and in local development # setups, but not in the macOS CI environment. So check them everywhere # except when running in CI on macOS. if [ "${os}" = "linux" ] || [ -z "${CI:-}" ]; then check clang-format + check dot check doxygen check gcovr check gh check git-cliff + check git-lfs check gpg # pre-commit, or its alternative implementation prek check pre-commit sh -c 'pre-commit --version || prek --version' diff --git a/bin/pre-commit/fix_pragma_once.py b/bin/pre-commit/fix_pragma_once.py new file mode 100755 index 0000000000..08a505b6d0 --- /dev/null +++ b/bin/pre-commit/fix_pragma_once.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +""" +Adds "#pragma once" to the top of header files that don't already have it. + +Usage: ./bin/pre-commit/fix_pragma_once.py ... +""" + +import sys +from pathlib import Path + +PRAGMA_ONCE = "#pragma once\n\n" + + +def fix_pragma_once(path: Path) -> bool: + original = path.read_text(encoding="utf-8") + if PRAGMA_ONCE not in original: + path.write_text(PRAGMA_ONCE + original, encoding="utf-8") + return False + return True + + +def main() -> int: + files = [Path(f) for f in sys.argv[1:]] + success = True + + for path in files: + success &= fix_pragma_once(path) + + return 0 if success else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/cmake/CompilationEnv.cmake b/cmake/CompilationEnv.cmake index 0d44f90974..8e69a4dfdd 100644 --- a/cmake/CompilationEnv.cmake +++ b/cmake/CompilationEnv.cmake @@ -56,3 +56,16 @@ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64") else() message(FATAL_ERROR "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}") endif() + +# -------------------------------------------------------------------- +# Sanitizers +# -------------------------------------------------------------------- +# SANITIZERS is injected by the Conan toolchain when a sanitizer build is +# requested (see conan/profiles/sanitizers). The flags are applied to the +# 'common' target in XrplSanitizers; this flag lets other modules know a +# sanitizer build is active without depending on that module. +if(DEFINED SANITIZERS) + set(SANITIZERS_ENABLED TRUE) +else() + set(SANITIZERS_ENABLED FALSE) +endif() diff --git a/cmake/PatchNixBinary.cmake b/cmake/PatchNixBinary.cmake new file mode 100644 index 0000000000..79ca0b150c --- /dev/null +++ b/cmake/PatchNixBinary.cmake @@ -0,0 +1,53 @@ +#[===================================================================[ + Patch executables to run in non-Nix environments. + + The Nix-based CI image links binaries against an ELF interpreter (loader) + that lives in the Nix store, so the resulting binaries don't run elsewhere + (including once installed from the .deb package). `patch_nix_binary` adds a + POST_BUILD step that resets the interpreter to the system default loader and + drops the rpath. + + This is only active inside the Nix-based image, detected by the presence of + /tmp/loader-path.sh (shipped by that image, resolves the default loader). It + is skipped for sanitizer builds, whose runtime libraries are resolved through + the rpath. Everywhere else `patch_nix_binary` is a no-op. +#]===================================================================] + +include_guard(GLOBAL) + +include(CompilationEnv) + +# Provided by the Nix-based CI image; prints the system default ELF loader path. +set(_loader_path_script "/tmp/loader-path.sh") + +if(is_linux AND NOT SANITIZERS_ENABLED AND EXISTS "${_loader_path_script}") + execute_process( + COMMAND "${_loader_path_script}" + OUTPUT_VARIABLE DEFAULT_LOADER_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY + ) + find_program(PATCHELF_COMMAND patchelf REQUIRED) + set(PATCH_NIX_BINARIES TRUE) + message( + STATUS + "Binaries will be patched to use loader '${DEFAULT_LOADER_PATH}'" + ) +else() + set(PATCH_NIX_BINARIES FALSE) +endif() + +function(patch_nix_binary target) + if(NOT PATCH_NIX_BINARIES) + return() + endif() + add_custom_command( + TARGET ${target} + POST_BUILD + COMMAND + "${PATCHELF_COMMAND}" --set-interpreter "${DEFAULT_LOADER_PATH}" + --remove-rpath "$" + COMMENT "Patching ${target}: set default loader, remove rpath" + VERBATIM + ) +endfunction() diff --git a/cmake/XrplCompiler.cmake b/cmake/XrplCompiler.cmake index 9af8e962d0..cb4e797137 100644 --- a/cmake/XrplCompiler.cmake +++ b/cmake/XrplCompiler.cmake @@ -154,6 +154,15 @@ else() > ) + # On aarch64, libatomic is required for atomic operations. It is not needed on x86_64. + # Linking it statically on Linux + if(is_arm64 AND is_linux) + target_link_options( + common + INTERFACE -Wl,--push-state -Wl,-Bstatic -latomic -Wl,--pop-state + ) + endif() + # Keep -stdlib=libstdc++ off the compile commands, but preserve it for linking. # # Conan turns `compiler.libcxx=libstdc++` into `-stdlib=libstdc++` and puts it in diff --git a/cmake/XrplCore.cmake b/cmake/XrplCore.cmake index 52d7714a99..4d4a800d9a 100644 --- a/cmake/XrplCore.cmake +++ b/cmake/XrplCore.cmake @@ -247,6 +247,7 @@ target_link_modules( if(xrpld) add_executable(xrpld) + patch_nix_binary(xrpld) if(tests) target_compile_definitions(xrpld PUBLIC ENABLE_TESTS) target_compile_definitions( diff --git a/cmake/XrplPackaging.cmake b/cmake/XrplPackaging.cmake index fe885c200c..8e3861925d 100644 --- a/cmake/XrplPackaging.cmake +++ b/cmake/XrplPackaging.cmake @@ -28,7 +28,6 @@ endif() set(package_env SRC_DIR=${CMAKE_SOURCE_DIR} BUILD_DIR=${CMAKE_BINARY_DIR} - PKG_VERSION=${xrpld_version} PKG_RELEASE=${pkg_release} ) diff --git a/cmake/XrplSanitizers.cmake b/cmake/XrplSanitizers.cmake index 64f1841bfb..893c880374 100644 --- a/cmake/XrplSanitizers.cmake +++ b/cmake/XrplSanitizers.cmake @@ -14,11 +14,9 @@ include_guard(GLOBAL) include(CompilationEnv) -if(NOT DEFINED SANITIZERS) - set(SANITIZERS_ENABLED FALSE) +if(NOT SANITIZERS_ENABLED) return() endif() -set(SANITIZERS_ENABLED TRUE) message(STATUS "=== Configuring Sanitizers ===") message(STATUS " SANITIZERS: ${SANITIZERS}") diff --git a/conan.lock b/conan.lock index e2eb8d871a..d80a6d0c57 100644 --- a/conan.lock +++ b/conan.lock @@ -1,9 +1,9 @@ { "version": "0.5", "requires": [ - "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503", + "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056", "xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987", - "sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1776096494.149", + "sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1778091117.311", "soci/4.0.3#fe32b9ad5eb47e79ab9e45a68f363945%1774450067.231", "snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878", "secp256k1/0.7.1#481881709eb0bdd0185a12b912bbe8ad%1770910500.329", @@ -15,19 +15,19 @@ "lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914", "libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492", "libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03", - "libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1776147552.838", + "libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1778091117.848", "jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228", "gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152", - "grpc/1.78.1#b1a9e74b145cc471bed4dc64dc6eb2c1%1774467387.342", + "grpc/1.81.0#2fb144aeb47e7f35c6ebb0e5f35bed31%1781620605.685", "ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772", "date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772", "c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1774439234.681", "bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837", - "boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778050991.9", + "boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778091165.282", "abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196" ], "build_requires": [ - "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503", + "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056", "strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1774447376.964", "protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12", "nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1765850144.707", diff --git a/conanfile.py b/conanfile.py index 2cf5aefbc2..5b78dc22e3 100644 --- a/conanfile.py +++ b/conanfile.py @@ -28,7 +28,7 @@ class Xrpl(ConanFile): requires = [ "ed25519/2015.03", - "grpc/1.78.1", + "grpc/1.81.0", "libarchive/3.8.7", "nudb/2.0.9", "openssl/3.6.2", diff --git a/cspell.config.yaml b/cspell.config.yaml index 0d38c4be7b..8273df6c98 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -301,6 +301,7 @@ words: - txs - ubsan - UBSAN + - ufdio - umant - unacquired - unambiguity diff --git a/include/xrpl/basics/DecayingSample.h b/include/xrpl/basics/DecayingSample.h index d1861ebc4a..910c8f9e14 100644 --- a/include/xrpl/basics/DecayingSample.h +++ b/include/xrpl/basics/DecayingSample.h @@ -12,8 +12,8 @@ template class DecayingSample { public: - using value_type = typename Clock::duration::rep; - using time_point = typename Clock::time_point; + using value_type = Clock::duration::rep; + using time_point = Clock::time_point; DecayingSample() = delete; @@ -93,7 +93,7 @@ template class DecayWindow { public: - using time_point = typename Clock::time_point; + using time_point = Clock::time_point; explicit DecayWindow(time_point now) : when_(now) { diff --git a/include/xrpl/basics/Log.h b/include/xrpl/basics/Log.h index 6bafbc7c54..0699cdd3d9 100644 --- a/include/xrpl/basics/Log.h +++ b/include/xrpl/basics/Log.h @@ -206,8 +206,7 @@ private: #ifndef JLOG #define JLOG(x) \ if (!(x)) \ - { \ - } \ + ; \ else \ x #endif diff --git a/include/xrpl/basics/TaggedCache.h b/include/xrpl/basics/TaggedCache.h index 380b7c687f..973fcd828a 100644 --- a/include/xrpl/basics/TaggedCache.h +++ b/include/xrpl/basics/TaggedCache.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -17,6 +18,22 @@ namespace xrpl { +namespace detail { + +// Replace-policy tags selecting how TaggedCache::canonicalizeImpl resolves a +// collision when the key already exists (defined in TaggedCache.ipp): +// - ReplaceCached: always replace the cached value with `data`. `data` is +// never written back and may be const. +// - ReplaceClient: keep the cached value and write it back into `data` (the +// client's pointer), which must therefore be writable. +// - ReplaceDynamically: call the supplied callback to decide per call; `data` +// is written back when the cached value is kept, so it must be writable. +struct ReplaceCached; +struct ReplaceClient; +struct ReplaceDynamically; + +} // namespace detail + /** Map/cache combination. This class implements a cache and a map. The cache keeps objects alive in the map. The map allows multiple code paths that reference objects @@ -96,6 +113,32 @@ public: bool del(key_type const& key, bool valid); +private: + // Selects the `data` parameter type of canonicalizeImpl from the replace + // policy: const for detail::ReplaceCached (never written back), otherwise + // writable. + template + using CanonicalizeClientPointerType = std::conditional_t< + std::is_same_v, + SharedPointerType const&, + SharedPointerType&>; + + /** Shared implementation of the canonicalize family. + + `policy` selects how a collision is resolved when `key` already exists: + detail::ReplaceCached, detail::ReplaceClient or + detail::ReplaceDynamically. For ReplaceDynamically `replaceCallback` is + invoked with the existing strong pointer and returns whether to replace + the cached value with `data`; for the tag policies it is unused. + */ + template + bool + canonicalizeImpl( + key_type const& key, + CanonicalizeClientPointerType data, + Policy policy, + Callback&& replaceCallback = nullptr); + public: /** Replace aliased objects with originals. @@ -104,19 +147,52 @@ public: This routine eliminates the duplicate and performs a replacement on the callers shared pointer if needed. + `replaceCallback` is a callable taking the existing strong pointer and + returning whether to replace the cached value with `data` (true) or to + keep the cached value and write it back into `data` (false). Because the + write-back case mutates `data`, `data` must be writable. + @param key The key corresponding to the object @param data A shared pointer to the data corresponding to the object. - @param replace Function that decides if cache should be replaced + @param replaceCallback A callable (existing strong pointer -> bool). - @return `true` If the key already existed. - */ - template + @return `true` if an existing live entry was found and used; `false` if a new entry was + inserted or an expired tracked entry was re-cached. + **/ + template bool - canonicalize(key_type const& key, SharedPointerType& data, R&& replaceCallback); + canonicalize(key_type const& key, SharedPointerType& data, Callback&& replaceCallback); + /** Insert/update the canonical entry for `key`, always replacing the + cached value with `data`. + + If an entry already exists for `key`, the cached value is unconditionally + replaced with `data`; otherwise `data` is inserted. `data` is never + written back, so it may be const. + + @param key The key corresponding to the object. + @param data A shared pointer to the data corresponding to the object. + + @return `true` if an existing live entry was found and used; `false` if a new entry was + inserted or an expired tracked entry was re-cached. + **/ bool canonicalizeReplaceCache(key_type const& key, SharedPointerType const& data); + /** Insert the canonical entry for `key`, keeping any existing cached value. + + If an entry already exists for `key`, the cached value is kept and + written back into `data` so the caller ends up with the canonical + object; otherwise `data` is inserted. Because `data` may be overwritten + it must be writable. + + @param key The key corresponding to the object. + @param data A shared pointer to the data corresponding to the object; + updated to the canonical value when one already exists. + + @return `true` if an existing live entry was found and used; `false` if a new entry was + inserted or an expired tracked entry was re-cached. + **/ bool canonicalizeReplaceClient(key_type const& key, SharedPointerType& data); @@ -262,7 +338,7 @@ private: sweepHelper( clock_type::time_point const& whenExpire, [[maybe_unused]] clock_type::time_point const& now, - typename KeyValueCacheType::map_type& partition, + KeyValueCacheType::map_type& partition, SweptPointersVector& stuffToSweep, std::atomic& allRemovals, std::scoped_lock const&); @@ -271,7 +347,7 @@ private: sweepHelper( clock_type::time_point const& whenExpire, clock_type::time_point const& now, - typename KeyOnlyCacheType::map_type& partition, + KeyOnlyCacheType::map_type& partition, SweptPointersVector&, std::atomic& allRemovals, std::scoped_lock const&); diff --git a/include/xrpl/basics/TaggedCache.ipp b/include/xrpl/basics/TaggedCache.ipp index cee02749c6..7e812ce4c7 100644 --- a/include/xrpl/basics/TaggedCache.ipp +++ b/include/xrpl/basics/TaggedCache.ipp @@ -5,6 +5,30 @@ namespace xrpl { +namespace detail { + +// Replace-policy tags selecting how TaggedCache::canonicalizeImpl resolves a +// collision when the key already exists: +// - ReplaceCached: always replace the cached value with `data`. `data` is +// never written back and may be const. +// - ReplaceClient: keep the cached value and write it back into `data` (the +// client's pointer), which must therefore be writable. +// - ReplaceDynamically: call the supplied callback to decide per call; `data` +// is written back when the cached value is kept, so it must be writable. +struct ReplaceCached +{ +}; + +struct ReplaceClient +{ +}; + +struct ReplaceDynamically +{ +}; + +} // namespace detail + template < class Key, class T, @@ -300,13 +324,29 @@ template < class Hash, class KeyEqual, class Mutex> -template +template inline bool TaggedCache:: - canonicalize(key_type const& key, SharedPointerType& data, R&& replaceCallback) + canonicalizeImpl( + key_type const& key, + CanonicalizeClientPointerType data, + [[maybe_unused]] Policy policy, + [[maybe_unused]] Callback&& replaceCallback) { // Return canonical value, store if needed, refresh in cache // Return values: true=we had the data already + + // `Policy` is one of: + // - detail::ReplaceCached: always replace the cached value with `data`; + // `data` is never written back and may be const. + // - detail::ReplaceClient: keep the cached value and write it back into + // `data` (the client's pointer), which must therefore be writable. + // - detail::ReplaceDynamically: call `replaceCallback` to decide at run + // time; `data` must be writable. + // For the latter two the write-back below requires a mutable `data`, so + // passing a const argument is a compile error. + constexpr bool replaceCached = std::is_same_v; + std::scoped_lock const lock(mutex_); auto cit = cache_.find(key); @@ -324,13 +364,14 @@ TaggedCachesecond; entry.touch(clock_.now()); - auto shouldReplace = [&] { - if constexpr (std::is_invocable_r_v) + auto shouldReplaceCached = [&] { + if constexpr (replaceCached) { - // The reason for this extra complexity is for intrusive - // strong/weak combo getting a strong is relatively expensive - // and not needed for many cases. - return replaceCallback(); + return true; + } + else if constexpr (std::is_same_v) + { + return false; } else { @@ -340,11 +381,11 @@ TaggedCache +template +inline bool +TaggedCache:: + canonicalize(key_type const& key, SharedPointerType& data, Callback&& replaceCallback) +{ + return canonicalizeImpl( + key, data, detail::ReplaceDynamically{}, std::forward(replaceCallback)); +} + template < class Key, class T, @@ -389,7 +448,7 @@ inline bool TaggedCache:: canonicalizeReplaceCache(key_type const& key, SharedPointerType const& data) { - return canonicalize(key, const_cast(data), []() { return true; }); + return canonicalizeImpl(key, data, detail::ReplaceCached{}); } template < @@ -405,7 +464,7 @@ inline bool TaggedCache:: canonicalizeReplaceClient(key_type const& key, SharedPointerType& data) { - return canonicalize(key, data, []() { return false; }); + return canonicalizeImpl(key, data, detail::ReplaceClient{}); } template < @@ -676,7 +735,7 @@ TaggedCache& allRemovals, std::scoped_lock const&) @@ -756,7 +815,7 @@ TaggedCache& allRemovals, std::scoped_lock const&) diff --git a/include/xrpl/basics/hardened_hash.h b/include/xrpl/basics/hardened_hash.h index efc77e058b..b8ea1e0f3f 100644 --- a/include/xrpl/basics/hardened_hash.h +++ b/include/xrpl/basics/hardened_hash.h @@ -75,7 +75,7 @@ private: detail::seed_pair seeds_{detail::makeSeedPair<>()}; public: - using result_type = typename HashAlgorithm::result_type; + using result_type = HashAlgorithm::result_type; HardenedHash() = default; diff --git a/include/xrpl/basics/partitioned_unordered_map.h b/include/xrpl/basics/partitioned_unordered_map.h index 3bf64985e5..c51cedf2dd 100644 --- a/include/xrpl/basics/partitioned_unordered_map.h +++ b/include/xrpl/basics/partitioned_unordered_map.h @@ -57,8 +57,8 @@ public: { using iterator_category = std::forward_iterator_tag; partition_map_type* map{nullptr}; - typename partition_map_type::iterator ait{}; - typename map_type::iterator mit; + partition_map_type::iterator ait{}; + map_type::iterator mit; Iterator() = default; @@ -126,8 +126,8 @@ public: using iterator_category = std::forward_iterator_tag; partition_map_type* map{nullptr}; - typename partition_map_type::iterator ait{}; - typename map_type::iterator mit; + partition_map_type::iterator ait{}; + map_type::iterator mit; ConstIterator() = default; diff --git a/include/xrpl/basics/random.h b/include/xrpl/basics/random.h index 17f4a1c213..0b298e12d9 100644 --- a/include/xrpl/basics/random.h +++ b/include/xrpl/basics/random.h @@ -29,6 +29,7 @@ static_assert( namespace detail { // Determines if a type can be called like an Engine +// NOLINTNEXTLINE(readability-redundant-typename): typename required by MSVC template using is_engine = std::is_invocable_r; } // namespace detail diff --git a/include/xrpl/beast/asio/io_latency_probe.h b/include/xrpl/beast/asio/io_latency_probe.h index ce3929a394..5e1b098dcb 100644 --- a/include/xrpl/beast/asio/io_latency_probe.h +++ b/include/xrpl/beast/asio/io_latency_probe.h @@ -18,8 +18,8 @@ template class IOLatencyProbe { private: - using duration = typename Clock::duration; - using time_point = typename Clock::time_point; + using duration = Clock::duration; + using time_point = Clock::time_point; std::recursive_mutex mutex_; std::condition_variable_any cond_; diff --git a/include/xrpl/beast/clock/abstract_clock.h b/include/xrpl/beast/clock/abstract_clock.h index 33d4096d0e..15d785d138 100644 --- a/include/xrpl/beast/clock/abstract_clock.h +++ b/include/xrpl/beast/clock/abstract_clock.h @@ -34,10 +34,10 @@ template class AbstractClock { public: - using rep = typename Clock::rep; - using period = typename Clock::period; - using duration = typename Clock::duration; - using time_point = typename Clock::time_point; + using rep = Clock::rep; + using period = Clock::period; + using duration = Clock::duration; + using time_point = Clock::time_point; using clock_type = Clock; static bool const is_steady = Clock::is_steady; // NOLINT(readability-identifier-naming) diff --git a/include/xrpl/beast/clock/basic_seconds_clock.h b/include/xrpl/beast/clock/basic_seconds_clock.h index 8460dbe26b..5a267e9458 100644 --- a/include/xrpl/beast/clock/basic_seconds_clock.h +++ b/include/xrpl/beast/clock/basic_seconds_clock.h @@ -20,10 +20,10 @@ public: explicit BasicSecondsClock() = default; - using rep = typename Clock::rep; - using period = typename Clock::period; - using duration = typename Clock::duration; - using time_point = typename Clock::time_point; + using rep = Clock::rep; + using period = Clock::period; + using duration = Clock::duration; + using time_point = Clock::time_point; static bool const is_steady = // NOLINT(readability-identifier-naming) Clock::is_steady; diff --git a/include/xrpl/beast/container/detail/aged_container_iterator.h b/include/xrpl/beast/container/detail/aged_container_iterator.h index 8d44646cd4..02fb3927dd 100644 --- a/include/xrpl/beast/container/detail/aged_container_iterator.h +++ b/include/xrpl/beast/container/detail/aged_container_iterator.h @@ -16,15 +16,15 @@ template class AgedContainerIterator { public: - using iterator_category = typename std::iterator_traits::iterator_category; + using iterator_category = std::iterator_traits::iterator_category; using value_type = std::conditional_t< IsConst, typename Iterator::value_type::Stashed::value_type const, typename Iterator::value_type::Stashed::value_type>; - using difference_type = typename std::iterator_traits::difference_type; + using difference_type = std::iterator_traits::difference_type; using pointer = value_type*; using reference = value_type&; - using time_point = typename Iterator::value_type::Stashed::time_point; + using time_point = Iterator::value_type::Stashed::time_point; AgedContainerIterator() = default; diff --git a/include/xrpl/beast/container/detail/aged_ordered_container.h b/include/xrpl/beast/container/detail/aged_ordered_container.h index ad368f2754..4cb2246a22 100644 --- a/include/xrpl/beast/container/detail/aged_ordered_container.h +++ b/include/xrpl/beast/container/detail/aged_ordered_container.h @@ -62,8 +62,8 @@ class AgedOrderedContainer { public: using clock_type = AbstractClock; - using time_point = typename clock_type::time_point; - using duration = typename clock_type::duration; + using time_point = clock_type::time_point; + using duration = clock_type::duration; using key_type = Key; using mapped_type = T; using value_type = std::conditional_t, Key>; @@ -94,8 +94,8 @@ private: { explicit Stashed() = default; - using value_type = typename AgedOrderedContainer::value_type; - using time_point = typename AgedOrderedContainer::time_point; + using value_type = AgedOrderedContainer::value_type; + using time_point = AgedOrderedContainer::time_point; }; Element(time_point const& when, value_type const& value) : value(value), when(when) @@ -192,8 +192,8 @@ private: } }; - using list_type = typename boost::intrusive:: - make_list>::type; + using list_type = + boost::intrusive::make_list>::type; using cont_type = std::conditional_t< IsMulti, @@ -206,8 +206,7 @@ private: boost::intrusive::constant_time_size, boost::intrusive::compare>::type>; - using ElementAllocator = - typename std::allocator_traits::template rebind_alloc; + using ElementAllocator = std::allocator_traits::template rebind_alloc; using ElementAllocatorTraits = std::allocator_traits; @@ -373,8 +372,8 @@ public: using allocator_type = Allocator; using reference = value_type&; using const_reference = value_type const&; - using pointer = typename std::allocator_traits::pointer; - using const_pointer = typename std::allocator_traits::const_pointer; + using pointer = std::allocator_traits::pointer; + using const_pointer = std::allocator_traits::const_pointer; // A set iterator (IsMap==false) is always const // because the elements of a set are immutable. @@ -617,7 +616,7 @@ public: bool MaybeMulti = IsMulti, bool MaybeMap = IsMap, class = std::enable_if_t> - typename std::conditional::type const& + std::conditional::type const& at(K const& k) const; template < @@ -1146,7 +1145,7 @@ private: void touch( beast::detail::AgedContainerIterator pos, - typename clock_type::time_point const& now); + clock_type::time_point const& now); template < bool MaybePropagate = std::allocator_traits::propagate_on_container_swap::value> @@ -1393,7 +1392,7 @@ AgedOrderedContainer::at(K co template template -typename std::conditional::type const& +std::conditional::type const& AgedOrderedContainer::at(K const& k) const { auto const iter(cont_.find(k, std::cref(config_.keyCompare()))); @@ -1732,7 +1731,7 @@ AgedOrderedContainer::operato cend(), other.cbegin(), other.cend(), - [&eq, &other](value_type const& lhs, typename Other::value_type const& rhs) { + [&eq, &other](value_type const& lhs, Other::value_type const& rhs) { return eq(extract(lhs), other.extract(rhs)); }); } @@ -1744,7 +1743,7 @@ template void AgedOrderedContainer::touch( beast::detail::AgedContainerIterator pos, - typename clock_type::time_point const& now) + clock_type::time_point const& now) { auto& e(*pos.iterator()); e.when = now; diff --git a/include/xrpl/beast/container/detail/aged_unordered_container.h b/include/xrpl/beast/container/detail/aged_unordered_container.h index 7162c237d6..3bad12d9e5 100644 --- a/include/xrpl/beast/container/detail/aged_unordered_container.h +++ b/include/xrpl/beast/container/detail/aged_unordered_container.h @@ -67,8 +67,8 @@ class AgedUnorderedContainer { public: using clock_type = AbstractClock; - using time_point = typename clock_type::time_point; - using duration = typename clock_type::duration; + using time_point = clock_type::time_point; + using duration = clock_type::duration; using key_type = Key; using mapped_type = T; using value_type = std::conditional_t, Key>; @@ -99,8 +99,8 @@ private: { explicit Stashed() = default; - using value_type = typename AgedUnorderedContainer::value_type; - using time_point = typename AgedUnorderedContainer::time_point; + using value_type = AgedUnorderedContainer::value_type; + using time_point = AgedUnorderedContainer::time_point; }; Element(time_point const& when, value_type const& value) : value(value), when(when) @@ -201,8 +201,8 @@ private: } }; - using list_type = typename boost::intrusive:: - make_list>::type; + using list_type = + boost::intrusive::make_list>::type; using cont_type = std::conditional_t< IsMulti, @@ -219,16 +219,14 @@ private: boost::intrusive::equal, boost::intrusive::cache_begin>::type>; - using bucket_type = typename cont_type::bucket_type; - using bucket_traits = typename cont_type::bucket_traits; + using bucket_type = cont_type::bucket_type; + using bucket_traits = cont_type::bucket_traits; - using ElementAllocator = - typename std::allocator_traits::template rebind_alloc; + using ElementAllocator = std::allocator_traits::template rebind_alloc; using ElementAllocatorTraits = std::allocator_traits; - using BucketAllocator = - typename std::allocator_traits::template rebind_alloc; + using BucketAllocator = std::allocator_traits::template rebind_alloc; using BucketAllocatorTraits = std::allocator_traits; @@ -542,8 +540,8 @@ public: using allocator_type = Allocator; using reference = value_type&; using const_reference = value_type const&; - using pointer = typename std::allocator_traits::pointer; - using const_pointer = typename std::allocator_traits::const_pointer; + using pointer = std::allocator_traits::pointer; + using const_pointer = std::allocator_traits::const_pointer; // A set iterator (IsMap==false) is always const // because the elements of a set are immutable. @@ -850,7 +848,7 @@ public: bool MaybeMulti = IsMulti, bool MaybeMap = IsMap, class = std::enable_if_t> - typename std::conditional::type const& + std::conditional::type const& at(K const& k) const; template < @@ -1414,7 +1412,7 @@ private: void touch( beast::detail::AgedContainerIterator pos, - typename clock_type::time_point const& now) + clock_type::time_point const& now) { auto& e(*pos.iterator()); e.when = now; @@ -2111,7 +2109,7 @@ template < class KeyEqual, class Allocator> template -typename std::conditional::type const& +std::conditional::type const& AgedUnorderedContainer::at( K const& k) const { diff --git a/include/xrpl/beast/core/List.h b/include/xrpl/beast/core/List.h index 946126a004..1c3827ae1c 100644 --- a/include/xrpl/beast/core/List.h +++ b/include/xrpl/beast/core/List.h @@ -24,7 +24,7 @@ struct CopyConst { explicit CopyConst() = default; - using type = typename std::remove_const::type const; + using type = std::remove_const::type const; }; /** @} */ @@ -56,7 +56,7 @@ class ListIterator { public: using iterator_category = std::bidirectional_iterator_tag; - using value_type = typename beast::detail::CopyConst::type; + using value_type = beast::detail::CopyConst::type; using difference_type = std::ptrdiff_t; using pointer = value_type*; using reference = value_type&; @@ -259,7 +259,7 @@ template class List { public: - using Node = typename detail::ListNode; + using Node = detail::ListNode; using value_type = T; using pointer = value_type*; diff --git a/include/xrpl/beast/core/LockFreeStack.h b/include/xrpl/beast/core/LockFreeStack.h index 6b8f686246..d4ad45cf5c 100644 --- a/include/xrpl/beast/core/LockFreeStack.h +++ b/include/xrpl/beast/core/LockFreeStack.h @@ -12,13 +12,13 @@ template class LockFreeStackIterator { protected: - using Node = typename Container::Node; + using Node = Container::Node; using NodePtr = std::conditional_t; public: using iterator_category = std::forward_iterator_tag; - using value_type = typename Container::value_type; - using difference_type = typename Container::difference_type; + using value_type = Container::value_type; + using difference_type = Container::difference_type; using pointer = std::conditional_t; using reference = std:: diff --git a/include/xrpl/beast/hash/uhash.h b/include/xrpl/beast/hash/uhash.h index 97461f67c7..9ffd5924f7 100644 --- a/include/xrpl/beast/hash/uhash.h +++ b/include/xrpl/beast/hash/uhash.h @@ -11,7 +11,7 @@ struct Uhash { Uhash() = default; - using result_type = typename Hasher::result_type; + using result_type = Hasher::result_type; template result_type diff --git a/include/xrpl/beast/rfc2616.h b/include/xrpl/beast/rfc2616.h index cf80bd26c0..e810733210 100644 --- a/include/xrpl/beast/rfc2616.h +++ b/include/xrpl/beast/rfc2616.h @@ -102,7 +102,7 @@ Result split(FwdIt first, FwdIt last, Char delim) { using namespace detail; - using string = typename Result::value_type; + using string = Result::value_type; Result result; diff --git a/include/xrpl/beast/unit_test/detail/const_container.h b/include/xrpl/beast/unit_test/detail/const_container.h index c171771e45..6826bf4258 100644 --- a/include/xrpl/beast/unit_test/detail/const_container.h +++ b/include/xrpl/beast/unit_test/detail/const_container.h @@ -32,11 +32,11 @@ protected: } public: - using value_type = typename cont_type::value_type; - using size_type = typename cont_type::size_type; - using difference_type = typename cont_type::difference_type; - using iterator = typename cont_type::const_iterator; - using const_iterator = typename cont_type::const_iterator; + using value_type = cont_type::value_type; + using size_type = cont_type::size_type; + using difference_type = cont_type::difference_type; + using iterator = cont_type::const_iterator; + using const_iterator = cont_type::const_iterator; /** Returns `true` if the container is empty. */ [[nodiscard]] bool diff --git a/include/xrpl/beast/unit_test/reporter.h b/include/xrpl/beast/unit_test/reporter.h index 1fdbb451a6..ff990dece5 100644 --- a/include/xrpl/beast/unit_test/reporter.h +++ b/include/xrpl/beast/unit_test/reporter.h @@ -48,7 +48,7 @@ private: std::size_t cases = 0; std::size_t total = 0; std::size_t failed = 0; - typename clock_type::time_point start = clock_type::now(); + clock_type::time_point start = clock_type::now(); explicit SuiteResults(std::string name = "") : name(std::move(name)) { @@ -60,7 +60,7 @@ private: struct Results { - using run_time = std::pair; + using run_time = std::pair; static constexpr auto kMaxTop = 10; @@ -69,7 +69,7 @@ private: std::size_t total = 0; std::size_t failed = 0; std::vector top; - typename clock_type::time_point start = clock_type::now(); + clock_type::time_point start = clock_type::now(); void add(SuiteResults const& r); @@ -91,7 +91,7 @@ public: private: static std::string - fmtdur(typename clock_type::duration const& d); + fmtdur(clock_type::duration const& d); void onSuiteBegin(SuiteInfo const& info) override; @@ -141,9 +141,7 @@ Reporter::Results::add(SuiteResults const& r) top.begin(), top.end(), elapsed, - [](run_time const& t1, typename clock_type::duration const& t2) { - return t1.second > t2; - }); + [](run_time const& t1, clock_type::duration const& t2) { return t1.second > t2; }); if (iter != top.end()) { if (top.size() == kMaxTop) @@ -181,7 +179,7 @@ Reporter::~Reporter() template std::string -Reporter::fmtdur(typename clock_type::duration const& d) +Reporter::fmtdur(clock_type::duration const& d) { using namespace std::chrono; auto const ms = duration_cast(d); diff --git a/include/xrpl/beast/utility/Journal.h b/include/xrpl/beast/utility/Journal.h index 1a0c148d1f..1262a64179 100644 --- a/include/xrpl/beast/utility/Journal.h +++ b/include/xrpl/beast/utility/Journal.h @@ -411,9 +411,9 @@ class BasicLogstream : public std::basic_ostream { using char_type = CharT; using traits_type = Traits; - using int_type = typename traits_type::int_type; - using pos_type = typename traits_type::pos_type; - using off_type = typename traits_type::off_type; + using int_type = traits_type::int_type; + using pos_type = traits_type::pos_type; + using off_type = traits_type::off_type; detail::LogStreamBuf buf_; diff --git a/include/xrpl/beast/utility/maybe_const.h b/include/xrpl/beast/utility/maybe_const.h index 40904471be..10b2eaf7f6 100644 --- a/include/xrpl/beast/utility/maybe_const.h +++ b/include/xrpl/beast/utility/maybe_const.h @@ -15,6 +15,6 @@ struct MaybeConst /** Alias for omitting `typename`. */ template -using maybe_const_t = typename MaybeConst::type; +using maybe_const_t = MaybeConst::type; } // namespace beast diff --git a/include/xrpl/beast/utility/rngfill.h b/include/xrpl/beast/utility/rngfill.h index 1614a594c5..2ea84a7a3d 100644 --- a/include/xrpl/beast/utility/rngfill.h +++ b/include/xrpl/beast/utility/rngfill.h @@ -13,7 +13,7 @@ template void rngfill(void* const buffer, std::size_t const bytes, Generator& g) { - using result_type = typename Generator::result_type; + using result_type = Generator::result_type; constexpr std::size_t kResultSize = sizeof(result_type); std::uint8_t* const bufferStart = static_cast(buffer); @@ -42,7 +42,7 @@ template < void rngfill(std::array& a, Generator& g) { - using result_type = typename Generator::result_type; + using result_type = Generator::result_type; auto i = N / sizeof(result_type); result_type* p = reinterpret_cast(a.data()); while (i--) diff --git a/include/xrpl/json/json_value.h b/include/xrpl/json/json_value.h index e9dcb8bcbe..f786c6a9dc 100644 --- a/include/xrpl/json/json_value.h +++ b/include/xrpl/json/json_value.h @@ -566,6 +566,7 @@ public: using SelfType = ValueConstIterator; ValueConstIterator() = default; + ValueConstIterator(ValueConstIterator const& other) = default; private: /*! \internal Use by Value to create an iterator. @@ -574,12 +575,12 @@ private: public: SelfType& - operator=(ValueIteratorBase const& other); + operator=(SelfType const& other); SelfType operator++(int) { - SelfType temp(*this); + SelfType const temp(*this); ++*this; return temp; } @@ -587,7 +588,7 @@ public: SelfType operator--(int) { - SelfType temp(*this); + SelfType const temp(*this); --*this; return temp; } diff --git a/include/xrpl/ledger/helpers/PermissionedDEXHelpers.h b/include/xrpl/ledger/helpers/PermissionedDEXHelpers.h index 04b12f2fc5..695a4950f0 100644 --- a/include/xrpl/ledger/helpers/PermissionedDEXHelpers.h +++ b/include/xrpl/ledger/helpers/PermissionedDEXHelpers.h @@ -1,4 +1,5 @@ #pragma once + #include namespace xrpl::permissioned_dex { diff --git a/include/xrpl/protocol/Batch.h b/include/xrpl/protocol/Batch.h index fa7641af70..2f2412b3ff 100644 --- a/include/xrpl/protocol/Batch.h +++ b/include/xrpl/protocol/Batch.h @@ -1,3 +1,5 @@ +#pragma once + #include #include #include diff --git a/include/xrpl/protocol/KnownFormats.h b/include/xrpl/protocol/KnownFormats.h index 9aa914ba97..6e21d4bc3a 100644 --- a/include/xrpl/protocol/KnownFormats.h +++ b/include/xrpl/protocol/KnownFormats.h @@ -118,13 +118,13 @@ public: } // begin() and end() are provided for testing purposes. - [[nodiscard]] typename std::forward_list::const_iterator + [[nodiscard]] std::forward_list::const_iterator begin() const { return formats_.begin(); } - [[nodiscard]] typename std::forward_list::const_iterator + [[nodiscard]] std::forward_list::const_iterator end() const { return formats_.end(); diff --git a/include/xrpl/protocol/STBitString.h b/include/xrpl/protocol/STBitString.h index 87c8cd4f45..0267eac22d 100644 --- a/include/xrpl/protocol/STBitString.h +++ b/include/xrpl/protocol/STBitString.h @@ -163,7 +163,7 @@ STBitString::setValue(BaseUInt const& v) } template -typename STBitString::value_type const& +STBitString::value_type const& STBitString::value() const { return value_; diff --git a/include/xrpl/protocol/STInteger.h b/include/xrpl/protocol/STInteger.h index 4e3c9a8923..52e0f7a365 100644 --- a/include/xrpl/protocol/STInteger.h +++ b/include/xrpl/protocol/STInteger.h @@ -120,7 +120,7 @@ STInteger::operator=(value_type const& v) } template -inline typename STInteger::value_type +inline STInteger::value_type STInteger::value() const noexcept { return value_; diff --git a/include/xrpl/protocol/STObject.h b/include/xrpl/protocol/STObject.h index c635e8ce22..e65cc79c78 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -243,7 +243,7 @@ public: @throws STObject::FieldErr if the field is not present. */ template - typename T::value_type + T::value_type operator[](TypedField const& f) const; /** Get the value of a field as a std::optional @@ -290,7 +290,7 @@ public: @throws STObject::FieldErr if the field is not present. */ template - [[nodiscard]] typename T::value_type + [[nodiscard]] T::value_type at(TypedField const& f) const; /** Get the value of a field as std::optional @@ -478,7 +478,7 @@ template class STObject::Proxy { public: - using value_type = typename T::value_type; + using value_type = T::value_type; [[nodiscard]] value_type value() const; @@ -513,13 +513,10 @@ protected: template concept IsArithmeticNumber = std::is_arithmetic_v || std::is_same_v || std::is_same_v; -template < - typename U, - typename Value = typename U::value_type, - typename Unit = typename U::unit_type> +template concept IsArithmeticValueUnit = std::is_same_v> && IsArithmeticNumber && std::is_class_v; -template +template concept IsArithmeticST = !IsArithmeticValueUnit && IsArithmeticNumber; template concept IsArithmetic = IsArithmeticNumber || IsArithmeticST || IsArithmeticValueUnit; @@ -534,7 +531,7 @@ template class STObject::ValueProxy : public Proxy { private: - using value_type = typename T::value_type; + using value_type = T::value_type; public: ValueProxy(ValueProxy const&) = default; @@ -576,7 +573,7 @@ template class STObject::OptionalProxy : public Proxy { private: - using value_type = typename T::value_type; + using value_type = T::value_type; using optional_type = std::optional>; @@ -840,7 +837,7 @@ operator typename STObject::OptionalProxy::optional_type() const } template -typename STObject::OptionalProxy::optional_type +STObject::OptionalProxy::optional_type STObject::OptionalProxy::operator~() const { return optionalValue(); @@ -933,7 +930,7 @@ STObject::OptionalProxy::optionalValue() const -> optional_type } template -typename STObject::OptionalProxy::value_type +STObject::OptionalProxy::value_type STObject::OptionalProxy::valueOr(value_type val) const { return engaged() ? this->value() : val; @@ -1040,7 +1037,7 @@ STObject::getPIndex(int offset) } template -typename T::value_type +T::value_type STObject::operator[](TypedField const& f) const { return at(f); @@ -1068,7 +1065,7 @@ STObject::operator[](OptionaledField const& of) -> OptionalProxy } template -[[nodiscard]] typename T::value_type +[[nodiscard]] T::value_type STObject::at(TypedField const& f) const { auto const b = peekAtPField(f); diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index c89610f354..072bd4778f 100644 --- a/include/xrpl/protocol/TER.h +++ b/include/xrpl/protocol/TER.h @@ -657,13 +657,13 @@ inline bool isTesSuccess(TER x) noexcept { // Makes use of TERSubset::operator bool() - return !(x); + return !x; } inline bool isTecClaim(TER x) noexcept { - return ((x) >= tecCLAIM); + return (x >= tecCLAIM); } std::unordered_map> const& diff --git a/include/xrpl/protocol/Units.h b/include/xrpl/protocol/Units.h index 7fedc05a0d..39f745e84e 100644 --- a/include/xrpl/protocol/Units.h +++ b/include/xrpl/protocol/Units.h @@ -391,7 +391,7 @@ mulDivU(Source1 value, Dest mul, Source2 div) return std::nullopt; } - using desttype = typename Dest::value_type; + using desttype = Dest::value_type; constexpr auto kMax = std::numeric_limits::max(); // Shortcuts, since these happen a lot in the real world diff --git a/include/xrpl/protocol/XChainAttestations.h b/include/xrpl/protocol/XChainAttestations.h index 457af727a2..1aec5fe549 100644 --- a/include/xrpl/protocol/XChainAttestations.h +++ b/include/xrpl/protocol/XChainAttestations.h @@ -379,16 +379,16 @@ public: [[nodiscard]] STArray toSTArray() const; - [[nodiscard]] typename AttCollection::const_iterator + [[nodiscard]] AttCollection::const_iterator begin() const; - [[nodiscard]] typename AttCollection::const_iterator + [[nodiscard]] AttCollection::const_iterator end() const; - typename AttCollection::iterator + AttCollection::iterator begin(); - typename AttCollection::iterator + AttCollection::iterator end(); template @@ -419,7 +419,7 @@ operator==( } template -inline typename XChainAttestationsBase::AttCollection const& +inline XChainAttestationsBase::AttCollection const& XChainAttestationsBase::attestations() const { return attestations_; diff --git a/include/xrpl/protocol/digest.h b/include/xrpl/protocol/digest.h index 50bf2735fb..721ce60767 100644 --- a/include/xrpl/protocol/digest.h +++ b/include/xrpl/protocol/digest.h @@ -206,7 +206,7 @@ sha512Half(Args const&... args) sha512_half_hasher h; using beast::hash_append; hash_append(h, args...); - return static_cast(h); + return static_cast(h); } /** Returns the SHA512-Half of a series of objects. @@ -222,7 +222,7 @@ sha512HalfS(Args const&... args) sha512_half_hasher_s h; using beast::hash_append; hash_append(h, args...); - return static_cast(h); + return static_cast(h); } } // namespace xrpl diff --git a/include/xrpl/shamap/FullBelowCache.h b/include/xrpl/shamap/FullBelowCache.h index 07290dfbd1..e9fd04ac58 100644 --- a/include/xrpl/shamap/FullBelowCache.h +++ b/include/xrpl/shamap/FullBelowCache.h @@ -25,7 +25,7 @@ public: static constexpr auto kDefaultCacheTargetSize = 0; using key_type = uint256; - using clock_type = typename CacheType::clock_type; + using clock_type = CacheType::clock_type; /** Construct the cache. diff --git a/include/xrpl/tx/paths/detail/AmountSpec.h b/include/xrpl/tx/paths/detail/AmountSpec.h deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/include/xrpl/tx/paths/detail/FlowDebugInfo.h b/include/xrpl/tx/paths/detail/FlowDebugInfo.h index ec7df86e53..1ccfba34ce 100644 --- a/include/xrpl/tx/paths/detail/FlowDebugInfo.h +++ b/include/xrpl/tx/paths/detail/FlowDebugInfo.h @@ -3,7 +3,6 @@ #include #include #include -#include #include diff --git a/include/xrpl/tx/paths/detail/StrandFlow.h b/include/xrpl/tx/paths/detail/StrandFlow.h index 31f0182258..4d988a4c7e 100644 --- a/include/xrpl/tx/paths/detail/StrandFlow.h +++ b/include/xrpl/tx/paths/detail/StrandFlow.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/package/README.md b/package/README.md index 63c2ab88fc..4b78106c4c 100644 --- a/package/README.md +++ b/package/README.md @@ -6,10 +6,10 @@ This directory contains all files needed to build RPM and Debian packages for `x ``` package/ - build_pkg.sh Staging and build script (called by CMake targets and CI) + build_pkg.sh Staging and build script (called by the CMake `package` target and CI) rpm/ - xrpld.spec RPM spec (xrpld_version/pkg_release passed via rpmbuild --define) - debian/ Debian control files (control, rules, install, links, conffiles, ...) + xrpld.spec RPM spec + debian/ Debian control files (control, rules, copyright, xrpld.docs, xrpld.links, source/format) shared/ xrpld.service systemd unit file (used by both RPM and DEB) xrpld.sysusers sysusers.d config (used by both RPM and DEB) @@ -21,21 +21,19 @@ package/ Packaging targets and their container images are declared in [`.github/scripts/strategy-matrix/linux.json`](../.github/scripts/strategy-matrix/linux.json) -inside `package_configs` configurations. Today only -`linux/amd64` is emitted. The package format -(deb or rpm) is inferred at build time from the container's package manager -(`apt-get` -> deb, `dnf`/`yum` -> rpm). The image tag is composed as -`ghcr.io/xrplf/xrpld/packaging-:sha-` — -the same scheme used by `reusable-build-test.yml`. Bump `image_sha` in -`linux.json` and both CI and local builds pick up the new image with no -workflow edits. +under `package_configs`, one entry per distro. Today only `linux/amd64` is +emitted. Each entry pins its full container image in an `image` field; to move +to a new image, edit that field and both CI and local builds pick it up. The +package format (deb or rpm) is inferred at build time from the container's +package manager (`apt-get` -> deb, `dnf`/`yum` -> rpm). -| Package type | Image (derived from `linux.json`) | Tool required | -| ------------ | ---------------------------------------------------- | --------------------------------------------------------------- | -| RPM | `ghcr.io/xrplf/xrpld/packaging-rhel:sha-` | `rpmbuild` | -| DEB | `ghcr.io/xrplf/xrpld/packaging-debian:sha-` | `dpkg-buildpackage`, `debhelper (>= 13)`, `dh-sequence-systemd` | +| Package type | Image (`package_configs.[].image` in `linux.json`) | Tools required | +| ------------ | ---------------------------------------------------------- | --------------------------------------------------- | +| RPM | `ghcr.io/xrplf/xrpld/packaging-rhel:sha-` | `rpmbuild` | +| DEB | `ghcr.io/xrplf/xrpld/packaging-debian:sha-` | `dpkg-buildpackage`, debhelper with compat level 13 | -To print the exact image tags for the current `linux.json`: +To print the full packaging matrix (artifact names and images) for the current +`linux.json`: ```bash ./.github/scripts/strategy-matrix/generate.py --packaging @@ -46,12 +44,13 @@ To print the exact image tags for the current `linux.json`: ### Via CI Caller workflows (`on-pr.yml`, `on-tag.yml`, `on-trigger.yml`) call -`reusable-strategy-matrix.yml` with `mode: packaging` to generate the matrix of -`{artifact_name, os}` entries, then fan out to -`reusable-package.yml` per entry. That workflow downloads the pre-built `xrpld` -binary artifact, detects the package format from the container, and calls -`build_pkg.sh` directly — no CMake configure or build step is needed inside -the packaging job. +`reusable-package.yml`. That workflow generates its own packaging matrix from +`package_configs` in `linux.json` (via `generate.py --packaging`) and fans out +one job per distro. Each job downloads the pre-built `xrpld` binary artifact and +runs in that distro's container, so the package format follows from the +container's package manager. The packaging script derives the package version +from the downloaded binary's `xrpld --version` output; no CMake configure or +build step is needed inside the packaging job. ### Locally (mirrors CI) @@ -60,22 +59,19 @@ inside the same container CI uses. The image tag is derived from `linux.json` so you don't need to hardcode a SHA. ```bash -# From the repo root. Pick any image flagged with `"package": true` in -# linux.json; the package format is inferred from the container's package -# manager. Example for the rpm-producing image: -IMAGE=$(jq -r ' - .os | map(select(.package == true))[0] | - "ghcr.io/xrplf/ci/\(.distro_name)-\(.distro_version):\(.compiler_name)-\(.compiler_version)-sha-\(.image_sha)" -' .github/scripts/strategy-matrix/linux.json) +# From the repo root. Each distro's container image is the `image` field of its +# package_configs entry in linux.json; the package format is inferred from the +# container's package manager. Example for the rpm-producing image (use +# .package_configs.debian[0].image for the deb image): +IMAGE=$(jq -r '.package_configs.rhel[0].image' .github/scripts/strategy-matrix/linux.json) -VERSION=2.4.0-local PKG_RELEASE=1 docker run --rm \ -v "$(pwd):/src" \ -w /src \ - "$IMAGE" \ - ./package/build_pkg.sh --pkg-version "$VERSION" --pkg-release "$PKG_RELEASE" + "${IMAGE}" \ + ./package/build_pkg.sh --pkg-release "${PKG_RELEASE}" # Output: # build/debbuild/*.deb (DEB + dbgsym .ddeb) @@ -91,41 +87,73 @@ needed, but the host toolchain replaces the pinned CI image: ```bash cmake \ -Dxrpld=ON \ - -Dxrpld_version=2.4.0-local \ + -Dpkg_release=1 \ -Dtests=OFF \ .. cmake --build . --target package # deb on Debian/Ubuntu, rpm on RHEL ``` -The `cmake/XrplPackaging.cmake` module defines the target only if at least one -of `rpmbuild` / `dpkg-buildpackage` is present; `build_pkg.sh` then infers the -package format from the host's package manager. The packaging script installs -to FHS-standard paths (`/usr/bin`, `/etc/xrpld`, etc.) regardless of +The `cmake/XrplPackaging.cmake` module defines the `package` target only if at +least one of `rpmbuild` / `dpkg-buildpackage` is present; `build_pkg.sh` then +infers the package format from the host's package manager. The packaging script +installs to FHS-standard paths (`/usr/bin`, `/etc/xrpld`, etc.) regardless of `CMAKE_INSTALL_PREFIX`. +The package version is not a CMake input on this path: `build_pkg.sh` derives it +from the just-built `xrpld` binary's `xrpld --version` output. The package +release defaults to 1 and is overridable with `-Dpkg_release=N`. + ## How `build_pkg.sh` works -`build_pkg.sh` accepts long-form flags, each of which can also be set via an -environment variable. Flags override env vars; env vars override the built-in -defaults. Run `./package/build_pkg.sh --help` for the same table: +`build_pkg.sh` derives the `xrpld` software version from +`${BUILD_DIR}/xrpld --version` in both package formats. -| Flag | Env var | Default | Purpose | -| -------------------------- | ------------------- | ----------------------------- | ----------------------------------- | -| `--src-dir DIR` | `SRC_DIR` | `$PWD` | repo root | -| `--build-dir DIR` | `BUILD_DIR` | `$PWD/build` | directory holding pre-built `xrpld` | -| `--pkg-version STR` | `PKG_VERSION` | parsed from `xrpld --version` | version string, e.g. `3.2.0-b1` | -| `--pkg-release N` | `PKG_RELEASE` | `1` | package release number | -| `--source-date-epoch SECS` | `SOURCE_DATE_EPOCH` | latest git commit ctime | reproducibility timestamp | +The binary's version is already SemVer-validated by `BuildInfo`. +`build_pkg.sh` converts pre-release versions such as `3.2.0-b1` or +`3.2.0-rc1` from `-` to `~` for package metadata so pre-releases sort before +the final release. If that normalized package version still contains `-`, +packaging fails because RPM forbids `-` in `Version`, and Debian uses `-` as +the upstream/revision separator. + +`pkg_version` is the normalized package metadata version derived inside +`build_pkg.sh` from the binary-reported `xrpld` version (`-` pre-release +separator converted to `~`). It is not a separate user input. + +`PKG_RELEASE` is a different value: the package release iteration for that +`xrpld` version. RPM receives the normalized `pkg_version` and `PKG_RELEASE` as +the `pkg_version` and `pkg_release` macros for its `Version` and `Release` +values; DEB writes them as `${pkg_version}-${PKG_RELEASE}` in +`debian/changelog`. + +With `PKG_RELEASE=1`, the package metadata becomes: + +| Input version | RPM version/release | Debian version | +| ------------------ | ---------------------------- | -------------------- | +| `3.2.0` | `3.2.0-1%{?dist}` | `3.2.0-1` | +| `3.2.0-b0+abc1234` | `3.2.0~b0+abc1234-1%{?dist}` | `3.2.0~b0+abc1234-1` | +| `3.2.0-b1` | `3.2.0~b1-1%{?dist}` | `3.2.0~b1-1` | +| `3.2.0-rc1` | `3.2.0~rc1-1%{?dist}` | `3.2.0~rc1-1` | + +The Debian changelog entry carries the repository component: final releases use +`stable`, `b0` builds, including `b0+metadata`, use `develop`, and `bN`/`rcN` +pre-releases use `unstable`. +Build metadata on a final release, such as `3.2.0+abc123`, is rejected. + +The RPM path intentionally uses `~` in `Version`, matching the Debian +pre-release ordering convention, so RPM filenames/NVRs begin with forms like +`xrpld-3.2.0~b1-...` and `xrpld-3.2.0~rc1-...` instead of encoding +pre-releases with an older `0..` RPM `Release` value. The package format (`deb` or `rpm`) is inferred from the host's package manager (`apt-get` -> deb, `dnf`/`yum` -> rpm). Hosts without one of those fail early. Flags are for explicit invocation; environment variables are intended for -CMake/systemd/CI integration. The CI workflow and the CMake `package` target -both invoke `build_pkg.sh` with no flags, configuring it entirely via env -(see `cmake/XrplPackaging.cmake`). +CMake/CI integration. The CI workflow and the CMake `package` target both invoke +`build_pkg.sh` with no flags; CMake supplies `SRC_DIR`, `BUILD_DIR`, and +`PKG_RELEASE` via env, while CI supplies `BUILD_DIR` and `PKG_RELEASE` via env +and lets the script use defaults for the rest. It resolves `SRC_DIR` and `BUILD_DIR` to absolute paths, then calls `stage_common()` to copy the binary, config files, and shared support files @@ -134,18 +162,32 @@ into the staging area, and invokes the platform build tool. ### RPM 1. Creates the standard `rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}` tree inside the build directory. -2. Copies `xrpld.spec` and all source files (binary, configs, service files) into `SOURCES/`. -3. Runs `rpmbuild -bb --define "xrpld_version ..." --define "pkg_release ..."`. The spec uses manual `install` commands to place files. +2. Copies `xrpld.spec` and all shared source files (binary, configs, service files) into `SOURCES/`. +3. Runs `rpmbuild -bb`, passing the normalized package metadata version as the + `pkg_version` RPM macro and `PKG_RELEASE` as the `pkg_release` RPM macro. + The spec uses manual `install` commands to place files, disables `dwz`, and + writes uncompressed RPM payloads while generating debuginfo packages. 4. Output: `rpmbuild/RPMS/x86_64/xrpld-*.rpm` +The uncompressed RPM payload setting is intentionally unconditional for +generated RPMs. It trades larger RPM artifacts for much shorter package +build/validation time, which keeps RPM package validation in the same rough time +class as Debian package validation. + +RPM upgrades intentionally do not restart a running `xrpld` service. The spec +uses `%systemd_postun`, matching Debian's `dh_installsystemd +--no-stop-on-upgrade` behavior; operators pick up the new binary on the next +service restart. + ### DEB 1. Creates a staging source tree at `debbuild/source/` inside the build directory. 2. Stages the binary, configs, `README.md`, and `LICENSE.md`. 3. Copies `package/debian/` control files into `debbuild/source/debian/`. 4. Copies shared service/sysusers/tmpfiles into `debian/` where `dh_installsystemd`, `dh_installsysusers`, and `dh_installtmpfiles` pick them up automatically. -5. Generates a minimal `debian/changelog` (pre-release versions use `~` instead of `-`). -6. Runs `dpkg-buildpackage -b --no-sign`. `debian/rules` uses manual `install` commands. +5. Generates a minimal `debian/changelog` using `${pkg_version}-${PKG_RELEASE}`, + where `pkg_version` is derived from the binary-reported `xrpld` version. +6. Runs `dpkg-buildpackage -b --no-sign -d` (`-d` skips the build-dependency check, since the binary is already built). `debian/rules` uses manual `install` commands. 7. Output: `debbuild/*.deb` and `debbuild/*.ddeb` (dbgsym package) ## Post-build verification @@ -161,11 +203,14 @@ rpm -qlp rpmbuild/RPMS/x86_64/*.rpm ## Reproducibility -The following environment variables improve build reproducibility. They are not -set automatically by `build_pkg.sh`; set them manually if needed: +`build_pkg.sh` already defaults `SOURCE_DATE_EPOCH` to the latest git commit +time, or the current time outside a git tree, and exports it (override with +`--source-date-epoch` / `SOURCE_DATE_EPOCH`); the RPM spec clamps file +modification times to it via `%build_mtime_policy`. The remaining variables +below further improve reproducibility but are _not_ set by the script — export +them yourself if needed: ```bash -export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) export TZ=UTC export LC_ALL=C.UTF-8 export GZIP=-n diff --git a/package/build_pkg.sh b/package/build_pkg.sh index e2ec8fee3d..3684fc096a 100755 --- a/package/build_pkg.sh +++ b/package/build_pkg.sh @@ -3,20 +3,18 @@ set -euo pipefail # Build an RPM or Debian package from a pre-built xrpld binary. # -# Flags override env vars; env vars override defaults. Env vars are intended -# for CMake/systemd/CI integration; flags are for explicit invocation. +# Flags override env vars; env vars override defaults. usage() { cat <<'EOF' Usage: build_pkg.sh [options] Options (each can also be set via the env var shown): - --src-dir DIR repo root [SRC_DIR; default: $PWD] - --build-dir DIR directory holding xrpld [BUILD_DIR; default: $PWD/build] - --pkg-version STR version, e.g. 3.2.0-b1 [PKG_VERSION; default: parsed from xrpld --version] - --pkg-release N package release number [PKG_RELEASE; default: 1] - --source-date-epoch SECS reproducibility timestamp [SOURCE_DATE_EPOCH; default: latest git commit ctime] - -h, --help show this help and exit + --src-dir DIR repo root [SRC_DIR; default: ${PWD}] + --build-dir DIR directory holding xrpld [BUILD_DIR; default: ${PWD}/build] + --pkg-release N package release iteration [PKG_RELEASE; default: 1] + --source-date-epoch SECS reproducibility timestamp [SOURCE_DATE_EPOCH; latest git ctime; fallback: current time] + -h, --help show this help and exit EOF } @@ -30,8 +28,7 @@ need_arg() { # Seed from env. CLI parsing below overrides these directly. SRC_DIR="${SRC_DIR:-}" BUILD_DIR="${BUILD_DIR:-}" -PKG_VERSION="${PKG_VERSION:-}" -PKG_RELEASE="${PKG_RELEASE:-}" +PKG_RELEASE="${PKG_RELEASE:-1}" SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-}" while [[ $# -gt 0 ]]; do @@ -46,11 +43,6 @@ while [[ $# -gt 0 ]]; do BUILD_DIR="$2" shift 2 ;; - --pkg-version) - need_arg "$@" - PKG_VERSION="$2" - shift 2 - ;; --pkg-release) need_arg "$@" PKG_RELEASE="$2" @@ -74,19 +66,61 @@ while [[ $# -gt 0 ]]; do done SRC_DIR="$(cd "${SRC_DIR:-${PWD}}" && pwd)" -BUILD_DIR="$(cd "${BUILD_DIR:-${PWD}/build}" && pwd)" -PKG_RELEASE="${PKG_RELEASE:-1}" - -if [[ -z "${PKG_VERSION}" ]]; then - PKG_VERSION="$("${BUILD_DIR}/xrpld" --version | awk 'NR==1 {print $3; exit}')" +BUILD_DIR="${BUILD_DIR:-${PWD}/build}" +if [[ ! -d "${BUILD_DIR}" ]]; then + echo "build_pkg.sh: build directory not found: ${BUILD_DIR}" >&2 + echo "Build xrpld before packaging, or set BUILD_DIR to the directory containing xrpld." >&2 + exit 1 fi +BUILD_DIR="$(cd "${BUILD_DIR}" && pwd)" -if [[ -z "${PKG_VERSION}" ]]; then - echo "PKG_VERSION is empty (not provided and could not be derived)." >&2 +xrpld_binary="${BUILD_DIR}/xrpld" +if [[ ! -x "${xrpld_binary}" ]]; then + echo "build_pkg.sh: expected executable xrpld binary at ${xrpld_binary}." >&2 + echo "Build xrpld before packaging, or set BUILD_DIR to the directory containing xrpld." >&2 exit 1 fi -VERSION="${PKG_VERSION}" +xrpld_version="$("${xrpld_binary}" --version | awk 'NR == 1 { print $3 }')" + +if [[ -z "${xrpld_version}" ]]; then + echo "build_pkg.sh: unable to derive xrpld version from ${xrpld_binary} --version." >&2 + exit 1 +fi + +# The version as the package formats consume it: identical to xrpld_version +# except a pre-release uses '~' (3.2.0-b1 -> 3.2.0~b1), which also sorts before +# the final 3.2.0; a no-op for a final release. Lowercase = derived internally, +# not an input (cf. pkg_type). +pkg_version="${xrpld_version}" +pre_release="" +if [[ "${xrpld_version}" == *-* ]]; then + pre_release="${xrpld_version#*-}" + pkg_version="${xrpld_version%%-*}~${pre_release}" +fi + +# BuildInfo already SemVer-validates the binary's version. Packaging adds one +# narrower constraint: after pre-release normalization, the package version must +# not contain '-' because RPM forbids it in Version and Debian uses it as the +# upstream/revision separator. +if [[ "${pkg_version}" == *-* ]]; then + echo "build_pkg.sh: unsupported xrpld version '${xrpld_version}'." >&2 + echo "Package version '${pkg_version}' cannot contain '-'." >&2 + echo "Use a single-token pre-release like 3.2.0-b1 or 3.2.0-rc2." >&2 + exit 1 +fi + +if [[ -z "${pre_release}" && "${xrpld_version}" == *+* ]]; then + echo "build_pkg.sh: unsupported xrpld version '${xrpld_version}'." >&2 + echo "Build metadata is only supported on bN/rcN pre-releases." >&2 + exit 1 +fi + +if [[ -n "${pre_release}" && ! "${pre_release}" =~ ^(b0|b[1-9][0-9]*|rc[0-9]+)(\+.*)?$ ]]; then + echo "build_pkg.sh: unsupported xrpld pre-release '${pre_release}'." >&2 + echo "Use bN or rcN, e.g. 3.2.0-b1 or 3.2.0-rc2." >&2 + exit 1 +fi if command -v apt-get >/dev/null 2>&1; then pkg_type=deb @@ -98,32 +132,15 @@ else fi if [[ -z "${SOURCE_DATE_EPOCH}" ]]; then - if git -C "$SRC_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then - SOURCE_DATE_EPOCH="$(git -C "$SRC_DIR" log -1 --format=%ct)" + if git -C "${SRC_DIR}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + SOURCE_DATE_EPOCH="$(git -C "${SRC_DIR}" log -1 --format=%ct)" else SOURCE_DATE_EPOCH="$(date +%s)" fi fi export SOURCE_DATE_EPOCH -CHANGELOG_DATE="$(date -u -R -d "@$SOURCE_DATE_EPOCH")" - -# Split VERSION at the first '-' into base and optional pre-release suffix. -# Examples: "3.2.0" -> ("3.2.0", ""); "3.2.0-b1" -> ("3.2.0", "b1"). -VER_BASE="${VERSION%%-*}" -VER_SUFFIX="${VERSION#*-}" -[[ "${VER_SUFFIX}" == "${VERSION}" ]] && VER_SUFFIX="" - -# Reject multi-segment suffixes (e.g. "beta-1", "rc1-15-gabc123"). Neither an -# RPM Version nor a Debian upstream version may contain '-' (it's the NVR / -# version-revision separator), and the convention here is single-token -# suffixes like b1 or rc2. Fail early with a clear message rather than letting -# the package tooling blow up or silently mangle dashes. -if [[ "${VER_SUFFIX}" == *-* ]]; then - echo "build_pkg.sh: multi-segment pre-release in VERSION='${VERSION}' (suffix '${VER_SUFFIX}')." >&2 - echo "Use single-token suffixes like 3.2.0-b1 or 3.2.0-rc2." >&2 - exit 1 -fi +CHANGELOG_DATE="$(date -u -R -d "@${SOURCE_DATE_EPOCH}")" SHARED="${SRC_DIR}/package/shared" DEBIAN_DIR="${SRC_DIR}/package/debian" @@ -143,7 +160,6 @@ stage_common() { cp "${SHARED}/xrpld.sysusers" "${dest}/xrpld.sysusers" cp "${SHARED}/xrpld.tmpfiles" "${dest}/xrpld.tmpfiles" cp "${SHARED}/xrpld.logrotate" "${dest}/xrpld.logrotate" - cp "${SHARED}/50-xrpld.preset" "${dest}/50-xrpld.preset" } build_rpm() { @@ -154,18 +170,11 @@ build_rpm() { cp "${SRC_DIR}/package/rpm/xrpld.spec" "${topdir}/SPECS/xrpld.spec" stage_common "${topdir}/SOURCES" - # Pre-releases use the modern rpm '~' convention (rpm >= 4.10): the suffix - # goes in Version (e.g. 3.2.0~b1), which rpmvercmp sorts *before* the final - # 3.2.0 — identical semantics to Debian's '~'. Release is just the package - # release number. This replaces the older "0.." Release - # hack and keeps the RPM and DEB version strings symmetric. - local rpm_version="${VER_BASE}${VER_SUFFIX:+~${VER_SUFFIX}}" - set -x rpmbuild -bb \ --define "_topdir ${topdir}" \ - --define "xrpld_version ${rpm_version}" \ - --define "xrpld_release ${PKG_RELEASE}" \ + --define "pkg_version ${pkg_version}" \ + --define "pkg_release ${PKG_RELEASE}" \ "${topdir}/SPECS/xrpld.spec" } @@ -182,23 +191,26 @@ build_deb() { cp "${staging}/xrpld.tmpfiles" "${staging}/debian/xrpld.tmpfiles" cp "${staging}/xrpld.logrotate" "${staging}/debian/xrpld.logrotate" - # Debian '~' marks a pre-release; 3.2.0~b1 sorts before 3.2.0. - local deb_full_version="${VER_BASE}${VER_SUFFIX:+~${VER_SUFFIX}}-${PKG_RELEASE}" - - # Derive release channel from the version suffix: - # (none) -> stable (tagged release) - # b0 -> develop (develop-branch build) - # b, rc -> unstable (pre-release) - local deb_distribution - case "${VER_SUFFIX}" in - "") deb_distribution="stable" ;; - b0) deb_distribution="develop" ;; - *) deb_distribution="unstable" ;; - esac + # Choose the Debian repository component for this package. + # 3.2.0 -> stable, *-b0[+metadata] -> develop, + # bN/rcN pre-releases -> unstable. + local deb_component + if [[ -z "${pre_release}" ]]; then + deb_component="stable" + elif [[ "${pre_release}" =~ ^b0(\+.*)?$ ]]; then + deb_component="develop" + elif [[ "${pre_release}" =~ ^(b[1-9][0-9]*|rc[0-9]+)(\+.*)?$ ]]; then + deb_component="unstable" + else + echo "build_pkg.sh: unsupported xrpld pre-release '${pre_release}'." >&2 + echo "Use bN or rcN, e.g. 3.2.0-b1 or 3.2.0-rc2." >&2 + exit 1 + fi + # Debian version is [~
]-.
     cat >"${staging}/debian/changelog" <  ${CHANGELOG_DATE}
 EOF
diff --git a/package/rpm/xrpld.spec b/package/rpm/xrpld.spec
index 5595fd0d8d..61c2d61ec6 100644
--- a/package/rpm/xrpld.spec
+++ b/package/rpm/xrpld.spec
@@ -1,6 +1,14 @@
+%if "%{?pkg_version}" == ""
+%{error:pkg_version must be defined}
+%endif
+
+%if "%{?pkg_release}" == ""
+%{error:pkg_release must be defined}
+%endif
+
 Name:     xrpld
-Version:  %{xrpld_version}
-Release:  %{xrpld_release}%{?dist}
+Version:  %{pkg_version}
+Release:  %{pkg_release}%{?dist}
 Summary:  XRP Ledger daemon
 
 License:  ISC
@@ -11,6 +19,9 @@ BuildRequires: systemd-rpm-macros
 
 %undefine _debugsource_packages
 %debug_package
+# Intentionally trade larger RPM artifacts for faster package validation.
+%global _binary_payload w.ufdio
+%global _find_debuginfo_dwz_opts %{nil}
 
 %build_mtime_policy clamp_to_source_date_epoch
 
@@ -37,7 +48,10 @@ install -Dm0644 %{_sourcedir}/validators.txt       %{buildroot}%{_sysconfdir}/%{
 install -Dm0644 %{_sourcedir}/xrpld.service        %{buildroot}%{_unitdir}/xrpld.service
 install -Dm0644 %{_sourcedir}/xrpld.sysusers       %{buildroot}%{_sysusersdir}/xrpld.conf
 install -Dm0644 %{_sourcedir}/xrpld.tmpfiles       %{buildroot}%{_tmpfilesdir}/xrpld.conf
-install -Dm0644 %{_sourcedir}/50-xrpld.preset      %{buildroot}%{_presetdir}/50-xrpld.preset
+install -Dm0644 /dev/null %{buildroot}%{_presetdir}/50-xrpld.preset
+cat >%{buildroot}%{_presetdir}/50-xrpld.preset <<'EOF'
+enable xrpld.service
+EOF
 
 # Logrotate config
 install -Dm0644 %{_sourcedir}/xrpld.logrotate      %{buildroot}%{_sysconfdir}/logrotate.d/%{name}
@@ -62,7 +76,7 @@ systemd-tmpfiles --create %{_tmpfilesdir}/xrpld.conf || :
 %systemd_preun xrpld.service
 
 %postun
-%systemd_postun_with_restart xrpld.service
+%systemd_postun xrpld.service
 
 %files
 %license %{_docdir}/%{name}/LICENSE.md
diff --git a/package/shared/50-xrpld.preset b/package/shared/50-xrpld.preset
deleted file mode 100644
index bfbcd56577..0000000000
--- a/package/shared/50-xrpld.preset
+++ /dev/null
@@ -1,2 +0,0 @@
-# /usr/lib/systemd/system-preset/50-xrpld.preset
-enable xrpld.service
diff --git a/src/libxrpl/json/json_valueiterator.cpp b/src/libxrpl/json/json_valueiterator.cpp
index 5a3a5ffcdb..bb49902f70 100644
--- a/src/libxrpl/json/json_valueiterator.cpp
+++ b/src/libxrpl/json/json_valueiterator.cpp
@@ -132,7 +132,7 @@ ValueConstIterator::ValueConstIterator(Value::ObjectValues::iterator const& curr
 }
 
 ValueConstIterator&
-ValueConstIterator::operator=(ValueIteratorBase const& other)
+ValueConstIterator::operator=(SelfType const& other)
 {
     copy(other);
     return *this;
diff --git a/src/libxrpl/protocol/XChainAttestations.cpp b/src/libxrpl/protocol/XChainAttestations.cpp
index 805d08c097..792fe5da9d 100644
--- a/src/libxrpl/protocol/XChainAttestations.cpp
+++ b/src/libxrpl/protocol/XChainAttestations.cpp
@@ -628,28 +628,28 @@ XChainAttestationsBase::XChainAttestationsBase(
 }
 
 template 
-typename XChainAttestationsBase::AttCollection::const_iterator
+XChainAttestationsBase::AttCollection::const_iterator
 XChainAttestationsBase::begin() const
 {
     return attestations_.begin();
 }
 
 template 
-typename XChainAttestationsBase::AttCollection::const_iterator
+XChainAttestationsBase::AttCollection::const_iterator
 XChainAttestationsBase::end() const
 {
     return attestations_.end();
 }
 
 template 
-typename XChainAttestationsBase::AttCollection::iterator
+XChainAttestationsBase::AttCollection::iterator
 XChainAttestationsBase::begin()
 {
     return attestations_.begin();
 }
 
 template 
-typename XChainAttestationsBase::AttCollection::iterator
+XChainAttestationsBase::AttCollection::iterator
 XChainAttestationsBase::end()
 {
     return attestations_.end();
diff --git a/src/libxrpl/protocol/tokens.cpp b/src/libxrpl/protocol/tokens.cpp
index fcd822a747..d04ceaa3a6 100644
--- a/src/libxrpl/protocol/tokens.cpp
+++ b/src/libxrpl/protocol/tokens.cpp
@@ -135,16 +135,16 @@ static constexpr std::array const kAlphabetReverse = []() {
 }();
 
 template 
-static typename Hasher::result_type
+static Hasher::result_type
 digest(void const* data, std::size_t size) noexcept
 {
     Hasher h;
     h(data, size);
-    return static_cast(h);
+    return static_cast(h);
 }
 
 template >
-static typename Hasher::result_type
+static Hasher::result_type
 digest(std::array const& v)
 {
     return digest(v.data(), v.size());
@@ -152,7 +152,7 @@ digest(std::array const& v)
 
 // Computes a double digest (e.g. digest of the digest)
 template 
-static typename Hasher::result_type
+static Hasher::result_type
 digest2(Args const&... args)
 {
     return digest(digest(args...));
diff --git a/src/libxrpl/shamap/SHAMapInnerNode.cpp b/src/libxrpl/shamap/SHAMapInnerNode.cpp
index ee6ebf7f3f..74a0e4515f 100644
--- a/src/libxrpl/shamap/SHAMapInnerNode.cpp
+++ b/src/libxrpl/shamap/SHAMapInnerNode.cpp
@@ -200,7 +200,7 @@ SHAMapInnerNode::updateHash()
         using beast::hash_append;
         hash_append(h, HashPrefix::InnerNode);
         iterChildren([&](SHAMapHash const& hh) { hash_append(h, hh); });
-        nh = static_cast(h);
+        nh = static_cast(h);
     }
     hash_ = SHAMapHash{nh};
 }
diff --git a/src/libxrpl/tx/invariants/InvariantCheck.cpp b/src/libxrpl/tx/invariants/InvariantCheck.cpp
index b4a533905c..e29a9fe661 100644
--- a/src/libxrpl/tx/invariants/InvariantCheck.cpp
+++ b/src/libxrpl/tx/invariants/InvariantCheck.cpp
@@ -407,8 +407,10 @@ AccountRootsNotDeleted::finalize(
                                "succeeded without deleting an account";
         }
         else
+        {
             JLOG(j.fatal()) << "Invariant failed: account deletion "
                                "succeeded but deleted multiple accounts!";
+        }
         return false;
     }
 
diff --git a/src/libxrpl/tx/paths/BookStep.cpp b/src/libxrpl/tx/paths/BookStep.cpp
index 5cc2a987b8..448172872a 100644
--- a/src/libxrpl/tx/paths/BookStep.cpp
+++ b/src/libxrpl/tx/paths/BookStep.cpp
@@ -905,6 +905,11 @@ BookStep::getAMMOffer(
     ReadView const& view,
     std::optional const& clobQuality) const
 {
+    // AMM doesn't support domain books. When fixCleanup3_3_0 is enabled, exclude
+    // AMM liquidity so quality estimation matches actual crossing (tryAMM skips
+    // AMM for domain books).
+    if (book_.domain && view.rules().enabled(fixCleanup3_3_0))
+        return std::nullopt;
     if (ammLiquidity_)
         return ammLiquidity_->getOffer(view, clobQuality);
     return std::nullopt;
@@ -1472,8 +1477,8 @@ bookStepEqual(Step const& step, xrpl::Book const& book)
 {
     return std::visit(
         [&](TIn const&, TOut const&) {
-            using TIn_ = typename TIn::amount_type;
-            using TOut_ = typename TOut::amount_type;
+            using TIn_ = TIn::amount_type;
+            using TOut_ = TOut::amount_type;
 
             if constexpr (ValidTaker)
             {
diff --git a/src/libxrpl/tx/paths/Flow.cpp b/src/libxrpl/tx/paths/Flow.cpp
index 39a9e83e69..80e05f058c 100644
--- a/src/libxrpl/tx/paths/Flow.cpp
+++ b/src/libxrpl/tx/paths/Flow.cpp
@@ -10,7 +10,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 #include 
 #include 
@@ -126,8 +125,8 @@ flow(
     // amount types.
     return std::visit(
         [&, &strands = strands](TIn const&, TOut const&) {
-            using TIn_ = typename TIn::amount_type;
-            using TOut_ = typename TOut::amount_type;
+            using TIn_ = TIn::amount_type;
+            using TOut_ = TOut::amount_type;
             return finishFlow(
                 sb,
                 srcAsset,
diff --git a/src/libxrpl/tx/paths/XRPEndpointStep.cpp b/src/libxrpl/tx/paths/XRPEndpointStep.cpp
index 314780c3a7..efdad92791 100644
--- a/src/libxrpl/tx/paths/XRPEndpointStep.cpp
+++ b/src/libxrpl/tx/paths/XRPEndpointStep.cpp
@@ -16,7 +16,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 #include 
 #include 
diff --git a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp
index 82fe88aa9f..32a51555b1 100644
--- a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp
+++ b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp
@@ -52,9 +52,13 @@ DelegateSet::preclaim(PreclaimContext const& ctx)
     if (!ctx.view.exists(keylet::account(ctx.tx[sfAccount])))
         return terNO_ACCOUNT;  // LCOV_EXCL_LINE
 
-    if (!ctx.view.exists(keylet::account(ctx.tx[sfAuthorize])))
+    auto const sleAuthorize = ctx.view.read(keylet::account(ctx.tx[sfAuthorize]));
+    if (!sleAuthorize)
         return tecNO_TARGET;
 
+    if (isPseudoAccount(sleAuthorize))
+        return tecNO_PERMISSION;
+
     // Deleting the delegate object is invalid if it doesn’t exist.
     if (ctx.tx.getFieldArray(sfPermissions).empty() &&
         !ctx.view.exists(keylet::delegate(ctx.tx[sfAccount], ctx.tx[sfAuthorize])))
diff --git a/src/libxrpl/tx/transactors/escrow/Escrow.cpp b/src/libxrpl/tx/transactors/escrow/Escrow.cpp
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/src/libxrpl/tx/transactors/system/TicketCreate.cpp b/src/libxrpl/tx/transactors/system/TicketCreate.cpp
index 5be00fe76c..be24b6326a 100644
--- a/src/libxrpl/tx/transactors/system/TicketCreate.cpp
+++ b/src/libxrpl/tx/transactors/system/TicketCreate.cpp
@@ -120,7 +120,7 @@ TicketCreate::doApply()
     }
 
     // Update the record of the number of Tickets this account owns.
-    std::uint32_t const oldTicketCount = (*(sleAccountRoot))[~sfTicketCount].valueOr(0u);
+    std::uint32_t const oldTicketCount = (*sleAccountRoot)[~sfTicketCount].valueOr(0u);
 
     sleAccountRoot->setFieldU32(sfTicketCount, oldTicketCount + ticketCount);
 
diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp
index 20668a42bf..1516219e46 100644
--- a/src/test/app/Delegate_test.cpp
+++ b/src/test/app/Delegate_test.cpp
@@ -235,6 +235,19 @@ class Delegate_test : public beast::unit_test::Suite
             env(delegate::set(gw, Account("unknown"), {"Payment"}), Ter(tecNO_TARGET));
         }
 
+        // Delegating to a pseudo-account is not allowed, should return tecNO_PERMISSION
+        {
+            Vault const vault{env};
+            auto [tx, keylet] = vault.create({.owner = gw, .asset = xrpIssue()});
+            env(tx);
+            env.close();
+
+            auto const sleVault = env.le(keylet);
+            BEAST_EXPECT(sleVault);
+            Account const vaultPseudo{"vault", sleVault->at(sfAccount)};
+            env(delegate::set(gw, vaultPseudo, {"Payment"}), Ter(tecNO_PERMISSION));
+        }
+
         // non-delegable transaction
         {
             env(delegate::set(gw, alice, {"SetRegularKey"}), Ter(temMALFORMED));
diff --git a/src/test/app/LedgerReplay_test.cpp b/src/test/app/LedgerReplay_test.cpp
index 49857e02ca..703ace3553 100644
--- a/src/test/app/LedgerReplay_test.cpp
+++ b/src/test/app/LedgerReplay_test.cpp
@@ -1139,10 +1139,10 @@ struct LedgerReplayer_test : public beast::unit_test::Suite
 
         {
             Config c;
-            std::string const toLoad = (R"xrpldConfig(
+            std::string const toLoad = R"xrpldConfig(
 [ledger_replay]
 0
-)xrpldConfig");
+)xrpldConfig";
             c.loadFromString(toLoad);
             BEAST_EXPECT(c.ledgerReplay == false);
         }
diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp
index dbb0033368..2d70efd9b2 100644
--- a/src/test/app/Loan_test.cpp
+++ b/src/test/app/Loan_test.cpp
@@ -3006,9 +3006,9 @@ protected:
             }
 
             if (mptTest)
-                (mptTest)(env, brokers[0], mptt);
+                mptTest(env, brokers[0], mptt);
             if (iouTest)
-                (iouTest)(env, brokers[1]);
+                iouTest(env, brokers[1]);
         };
 
         testCase(
diff --git a/src/test/app/NFTokenDir_test.cpp b/src/test/app/NFTokenDir_test.cpp
index 117f2bc816..e24a524b81 100644
--- a/src/test/app/NFTokenDir_test.cpp
+++ b/src/test/app/NFTokenDir_test.cpp
@@ -143,7 +143,7 @@ class NFTokenDir_test : public beast::unit_test::Suite
         for (uint256 const& nftID : nftIDs)
         {
             offers.emplace_back(keylet::nftoffer(issuer, env.seq(issuer)).key);
-            env(token::createOffer(issuer, nftID, XRP(0)), Txflags((tfSellNFToken)));
+            env(token::createOffer(issuer, nftID, XRP(0)), Txflags(tfSellNFToken));
             env.close();
         }
 
@@ -217,7 +217,7 @@ class NFTokenDir_test : public beast::unit_test::Suite
                 offers.emplace_back(keylet::nftoffer(account, env.seq(account)).key);
                 env(token::createOffer(account, nftID, XRP(0)),
                     token::Destination(buyer),
-                    Txflags((tfSellNFToken)));
+                    Txflags(tfSellNFToken));
             }
             env.close();
 
@@ -421,7 +421,7 @@ class NFTokenDir_test : public beast::unit_test::Suite
                 offers.emplace_back(keylet::nftoffer(account, env.seq(account)).key);
                 env(token::createOffer(account, nftID, XRP(0)),
                     token::Destination(buyer),
-                    Txflags((tfSellNFToken)));
+                    Txflags(tfSellNFToken));
             }
             env.close();
 
@@ -651,7 +651,7 @@ class NFTokenDir_test : public beast::unit_test::Suite
             offers.emplace_back(keylet::nftoffer(account, env.seq(account)).key);
             env(token::createOffer(account, nftID, XRP(0)),
                 token::Destination(buyer),
-                Txflags((tfSellNFToken)));
+                Txflags(tfSellNFToken));
         }
         env.close();
 
@@ -823,7 +823,7 @@ class NFTokenDir_test : public beast::unit_test::Suite
                 offers[i].emplace_back(keylet::nftoffer(account, env.seq(account)).key);
                 env(token::createOffer(account, nftID, XRP(0)),
                     token::Destination(buyer),
-                    Txflags((tfSellNFToken)));
+                    Txflags(tfSellNFToken));
             }
         }
         env.close();
diff --git a/src/test/app/OfferMPT_test.cpp b/src/test/app/OfferMPT_test.cpp
index ed0b2ffbe3..ac50924ac2 100644
--- a/src/test/app/OfferMPT_test.cpp
+++ b/src/test/app/OfferMPT_test.cpp
@@ -3431,7 +3431,7 @@ public:
         auto const gw = Account("gateway");
 
         auto const fee = env.current()->fees().base;
-        env.fund(reserve(env, 2) + drops(9999640) + (fee), ann);
+        env.fund(reserve(env, 2) + drops(9999640) + fee, ann);
         env.fund(reserve(env, 2) + (fee * 4), gw);
         env.close();
 
@@ -3467,7 +3467,7 @@ public:
         auto const bob = Account("bob");
 
         auto const fee = env.current()->fees().base;
-        env.fund(reserve(env, 2) + drops(400'000'000'000) + (fee), alice, bob);
+        env.fund(reserve(env, 2) + drops(400'000'000'000) + fee, alice, bob);
         env.fund(reserve(env, 2) + (fee * 4), gw);
         env.close();
 
diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp
index ea8b0a7c0e..1a2f0b2b12 100644
--- a/src/test/app/Offer_test.cpp
+++ b/src/test/app/Offer_test.cpp
@@ -3621,7 +3621,7 @@ public:
         auto const btc = gw["BTC"];
 
         auto const fee = env.current()->fees().base;
-        env.fund(reserve(env, 2) + drops(9999640) + (fee), ann);
+        env.fund(reserve(env, 2) + drops(9999640) + fee, ann);
         env.fund(reserve(env, 2) + (fee * 4), gw);
         env.close();
 
@@ -3659,7 +3659,7 @@ public:
         auto const cny = gw["CNY"];
 
         auto const fee = env.current()->fees().base;
-        env.fund(reserve(env, 2) + drops(400000000000) + (fee), alice, bob);
+        env.fund(reserve(env, 2) + drops(400000000000) + fee, alice, bob);
         env.fund(reserve(env, 2) + (fee * 4), gw);
         env.close();
 
@@ -3706,7 +3706,7 @@ public:
         auto const jpy = gw["JPY"];
 
         auto const fee = env.current()->fees().base;
-        env.fund(reserve(env, 2) + drops(400000000000) + (fee), alice, bob);
+        env.fund(reserve(env, 2) + drops(400000000000) + fee, alice, bob);
         env.fund(reserve(env, 2) + (fee * 4), gw);
         env.close();
 
@@ -3759,7 +3759,7 @@ public:
         auto const jpy = gw2["JPY"];
 
         auto const fee = env.current()->fees().base;
-        env.fund(reserve(env, 2) + drops(400000000000) + (fee), alice, bob);
+        env.fund(reserve(env, 2) + drops(400000000000) + fee, alice, bob);
         env.fund(reserve(env, 2) + (fee * 4), gw1, gw2);
         env.close();
 
diff --git a/src/test/app/PermissionedDEX_test.cpp b/src/test/app/PermissionedDEX_test.cpp
index 99e69ce482..51ca321f7e 100644
--- a/src/test/app/PermissionedDEX_test.cpp
+++ b/src/test/app/PermissionedDEX_test.cpp
@@ -993,6 +993,83 @@ class PermissionedDEX_test : public beast::unit_test::Suite
         BEAST_EXPECT(usd == USD(45));
     }
 
+    void
+    testAmmQualityNotLeaked(FeatureBitset features)
+    {
+        bool const excludesAmmFromDomainQuality = features[fixCleanup3_3_0];
+
+        testcase << "AMM quality not leaked into domain BookStep"
+                 << (excludesAmmFromDomainQuality ? " (Cleanup3_3_0 enabled)"
+                                                  : " (Cleanup3_3_0 disabled)");
+
+        Env env(*this, features);
+        auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+            PermissionedDEX(env);
+        auto const eur = gw["EUR"];
+
+        env.trust(eur(1000), bob, domainOwner);
+        env.close();
+        env(pay(gw, bob, eur(100)));
+        env.close();
+
+        env(pay(gw, alice, USD(500)));
+        env.close();
+
+        // The AMM makes the direct XRP->USD book look much better than it
+        // really is for domain payments. The domain LOB direct path is 1:1,
+        // while the competing XRP->EUR->USD path is 2:1.
+        AMM const amm(env, alice, XRP(10), USD(500));
+
+        auto const directOfferSeq{env.seq(bob)};
+        env(offer(bob, XRP(10), USD(10)), Domain(domainID));
+        env.close();
+
+        auto const xrpEurOfferSeq{env.seq(bob)};
+        env(offer(bob, XRP(10), eur(20)), Domain(domainID));
+        env.close();
+
+        auto const eurUsdOfferSeq{env.seq(domainOwner)};
+        env(offer(domainOwner, eur(20), USD(20)), Domain(domainID));
+        env.close();
+
+        auto const carolBalBefore = env.balance(carol, USD);
+
+        // Both paths compete for the same XRP(10) sendmax. If AMM quality leaks
+        // into the direct domain book, the engine ranks direct XRP->USD first
+        // but crossing can only consume the 1:1 LOB offer. With the fix, the
+        // direct book is ranked by its domain LOB quality, so the 2:1
+        // XRP->EUR->USD path executes first.
+        env(pay(alice, carol, USD(100)),
+            Path(~USD),
+            Path(~eur, ~USD),
+            Sendmax(XRP(10)),
+            Txflags(tfPartialPayment | tfNoRippleDirect),
+            Domain(domainID));
+        env.close();
+
+        auto const delivered = env.balance(carol, USD) - carolBalBefore;
+        if (excludesAmmFromDomainQuality)
+        {
+            BEAST_EXPECT(delivered == USD(20));
+
+            BEAST_EXPECT(checkOffer(env, bob, directOfferSeq, XRP(10), USD(10), 0, true));
+            BEAST_EXPECT(!offerExists(env, bob, xrpEurOfferSeq));
+            BEAST_EXPECT(!offerExists(env, domainOwner, eurUsdOfferSeq));
+        }
+        else
+        {
+            BEAST_EXPECT(delivered == USD(10));
+
+            BEAST_EXPECT(!offerExists(env, bob, directOfferSeq));
+            BEAST_EXPECT(checkOffer(env, bob, xrpEurOfferSeq, XRP(10), eur(20), 0, true));
+            BEAST_EXPECT(checkOffer(env, domainOwner, eurUsdOfferSeq, eur(20), USD(20), 0, true));
+        }
+
+        auto [xrp, usd, lpt] = amm.balances(XRP, USD);
+        BEAST_EXPECT(xrp == XRP(10));
+        BEAST_EXPECT(usd == USD(500));
+    }
+
     void
     testHybridOfferCreate(FeatureBitset features)
     {
@@ -1943,6 +2020,8 @@ public:
         testOfferTokenIssuerInDomain(all);
         testRemoveUnfundedOffer(all);
         testAmmNotUsed(all);
+        testAmmQualityNotLeaked(all);
+        testAmmQualityNotLeaked(all - fixCleanup3_3_0);
         testAutoBridge(all);
 
         // Test hybrid offers
diff --git a/src/test/app/PermissionedDomains_test.cpp b/src/test/app/PermissionedDomains_test.cpp
index f2d7bce152..2cffc18682 100644
--- a/src/test/app/PermissionedDomains_test.cpp
+++ b/src/test/app/PermissionedDomains_test.cpp
@@ -8,18 +8,21 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -526,6 +529,47 @@ class PermissionedDomains_test : public beast::unit_test::Suite
         BEAST_EXPECT(env.ownerCount(alice) == 1);
     }
 
+    void
+    testTicket(FeatureBitset features)
+    {
+        testcase("Tickets");
+
+        using namespace test::jtx;
+
+        Env env(*this, features);
+        Account const alice("alice");
+        env.fund(XRP(1000), alice);
+
+        pdomain::Credentials const credentials{
+            {.issuer = alice, .credType = "credential1"},
+        };
+
+        std::uint32_t seq{env.seq(alice)};
+        env(ticket::create(alice, 2));
+
+        {
+            env(pdomain::setTx(alice, credentials), ticket::Use(++seq));
+            auto domain = pdomain::getNewDomain(env.meta());
+            if (features[fixCleanup3_1_3])
+            {
+                BEAST_EXPECT(domain == keylet::permissionedDomain(alice.id(), seq).key);
+            }
+            else
+            {
+                BEAST_EXPECT(domain == keylet::permissionedDomain(alice.id(), 0).key);
+            }
+        }
+
+        if (features[fixCleanup3_1_3])
+        {
+            env(pdomain::setTx(alice, credentials), ticket::Use(++seq));
+        }
+        else
+        {
+            env(pdomain::setTx(alice, credentials), ticket::Use(++seq), Ter(tefEXCEPTION));
+        }
+    }
+
 public:
     void
     run() override
@@ -540,6 +584,8 @@ public:
         testDelete(withFix_);
         testAccountReserve(withFeature_);
         testAccountReserve(withFix_);
+        testTicket(withFeature_);
+        testTicket(withFix_);
     }
 };
 
diff --git a/src/test/app/TxQ_test.cpp b/src/test/app/TxQ_test.cpp
index 0ae6b4d80a..97cb035578 100644
--- a/src/test/app/TxQ_test.cpp
+++ b/src/test/app/TxQ_test.cpp
@@ -1176,7 +1176,7 @@ public:
         // bankrupt Alice. Fails, because an account can't have
         // more than the minimum reserve in flight before the
         // last queued transaction
-        aliceFee = env.le(alice)->getFieldAmount(sfBalance).xrp().drops() - (62);
+        aliceFee = env.le(alice)->getFieldAmount(sfBalance).xrp().drops() - 62;
         env(noop(alice), Seq(aliceSeq), Fee(aliceFee), Ter(telCAN_NOT_QUEUE_BALANCE));
         checkMetrics(*this, env, 4, 10, 6, 5);
 
diff --git a/src/test/basics/TaggedCache_test.cpp b/src/test/basics/TaggedCache_test.cpp
index 77cd25e543..26564a4de8 100644
--- a/src/test/basics/TaggedCache_test.cpp
+++ b/src/test/basics/TaggedCache_test.cpp
@@ -1,5 +1,7 @@
 #include 
 
+#include 
+#include 
 #include 
 #include   // IWYU pragma: keep
 #include 
@@ -8,6 +10,7 @@
 #include 
 
 #include 
+#include 
 
 namespace xrpl {
 
@@ -133,6 +136,113 @@ public:
             BEAST_EXPECT(c.getCacheSize() == 0);
             BEAST_EXPECT(c.getTrackSize() == 0);
         }
+        {
+            BEAST_EXPECT(!c.insert(5, "five"));
+            BEAST_EXPECT(c.getCacheSize() == 1);
+            BEAST_EXPECT(c.size() == 1);
+
+            {
+                auto const p1 = c.fetch(5);
+                BEAST_EXPECT(p1 != nullptr);
+                BEAST_EXPECT(c.getCacheSize() == 1);
+                BEAST_EXPECT(c.size() == 1);
+
+                // Advance the clock a lot
+                ++clock;
+                c.sweep();
+                BEAST_EXPECT(c.getCacheSize() == 0);
+                BEAST_EXPECT(c.size() == 1);
+
+                auto p2 = std::make_shared("five_2");
+                BEAST_EXPECT(c.canonicalizeReplaceCache(5, p2));
+                BEAST_EXPECT(c.getCacheSize() == 1);
+                BEAST_EXPECT(c.size() == 1);
+                // Make sure the caller's original pointer is unchanged
+                BEAST_EXPECT(p1.get() != p2.get());
+                BEAST_EXPECT(*p2 == "five_2");
+
+                auto const p3 = c.fetch(5);
+                BEAST_EXPECT(p3 != nullptr);
+                BEAST_EXPECT(p3.get() == p2.get());
+                BEAST_EXPECT(p3.get() != p1.get());
+            }
+
+            ++clock;
+            c.sweep();
+            BEAST_EXPECT(c.getCacheSize() == 0);
+            BEAST_EXPECT(c.size() == 0);
+        }
+
+        {
+            testcase("intrptr");
+
+            struct MyRefCountObject : IntrusiveRefCounts
+            {
+                std::string data;
+
+                // Needed to support weak intrusive pointers
+                virtual void
+                partialDestructor() {};
+
+                MyRefCountObject() = default;
+                explicit MyRefCountObject(std::string data) : data(std::move(data))
+                {
+                }
+
+                bool
+                operator==(std::string const& other) const
+                {
+                    return data == other;
+                }
+            };
+
+            using IntrPtrCache = TaggedCache<
+                Key,
+                MyRefCountObject,
+                /*IsKeyCache*/ false,
+                intr_ptr::SharedWeakUnionPtr,
+                intr_ptr::SharedPtr>;
+
+            IntrPtrCache intrPtrCache("IntrPtrTest", 1, 1s, clock, journal);
+
+            intrPtrCache.canonicalizeReplaceCache(1, intr_ptr::makeShared("one"));
+            BEAST_EXPECT(intrPtrCache.getCacheSize() == 1);
+            BEAST_EXPECT(intrPtrCache.size() == 1);
+
+            {
+                {
+                    intrPtrCache.canonicalizeReplaceCache(
+                        1, intr_ptr::makeShared("one_replaced"));
+
+                    auto p = intrPtrCache.fetch(1);
+                    BEAST_EXPECT(*p == "one_replaced");
+
+                    // Advance the clock a lot
+                    ++clock;
+                    intrPtrCache.sweep();
+                    BEAST_EXPECT(intrPtrCache.getCacheSize() == 0);
+                    BEAST_EXPECT(intrPtrCache.size() == 1);
+
+                    intrPtrCache.canonicalizeReplaceCache(
+                        1, intr_ptr::makeShared("one_replaced_2"));
+
+                    auto p2 = intrPtrCache.fetch(1);
+                    BEAST_EXPECT(*p2 == "one_replaced_2");
+
+                    intrPtrCache.del(1, true);
+                }
+
+                intrPtrCache.canonicalizeReplaceCache(
+                    1, intr_ptr::makeShared("one_replaced_3"));
+                auto p3 = intrPtrCache.fetch(1);
+                BEAST_EXPECT(*p3 == "one_replaced_3");
+            }
+
+            ++clock;
+            intrPtrCache.sweep();
+            BEAST_EXPECT(intrPtrCache.getCacheSize() == 0);
+            BEAST_EXPECT(intrPtrCache.size() == 0);
+        }
     }
 };
 
diff --git a/src/test/beast/IPEndpointCommon.h b/src/test/beast/IPEndpointCommon.h
index 15566cb830..0ff0da35be 100644
--- a/src/test/beast/IPEndpointCommon.h
+++ b/src/test/beast/IPEndpointCommon.h
@@ -1,3 +1,5 @@
+#pragma once
+
 #include 
 #include 
 
diff --git a/src/test/beast/aged_associative_container_test.cpp b/src/test/beast/aged_associative_container_test.cpp
index d7f74aaa7d..2ac5fc5a33 100644
--- a/src/test/beast/aged_associative_container_test.cpp
+++ b/src/test/beast/aged_associative_container_test.cpp
@@ -11,6 +11,7 @@
 #include 
 #include 
 
+#include 
 #include 
 #include 
 #include 
@@ -232,10 +233,10 @@ public:
     {
     public:
         using T = void;
-        using Value = typename Base::Key;
+        using Value = Base::Key;
         using Values = std::vector;
 
-        static typename Base::Key const&
+        static Base::Key const&
         extract(Value const& value)
         {
             return value;  // NOLINT(bugprone-return-const-ref-from-parameter)
@@ -271,7 +272,7 @@ public:
         using Value = std::pair;
         using Values = std::vector;
 
-        static typename Base::Key const&
+        static Base::Key const&
         extract(Value const& value)
         {
             return value.first;
@@ -387,7 +388,7 @@ public:
     struct EqualValue
     {
         bool
-        operator()(typename Traits::Value const& lhs, typename Traits::Value const& rhs)
+        operator()(Traits::Value const& lhs, Traits::Value const& rhs)
         {
             return Traits::extract(lhs) == Traits::extract(rhs);
         }
@@ -647,7 +648,7 @@ AgedAssociativeContainerTestBase::checkUnorderedContentsRefRef(C&& c, Values con
     using Cont = std::remove_reference_t;
     using Traits =
         TestTraits;
-    using size_type = typename Cont::size_type;
+    using size_type = Cont::size_type;
     auto const hash(c.hashFunction());
     auto const keyEq(c.keyEq());
     for (size_type i(0); i < c.bucketCount(); ++i)
@@ -655,10 +656,9 @@ AgedAssociativeContainerTestBase::checkUnorderedContentsRefRef(C&& c, Values con
         auto const last(c.end(i));
         for (auto iter(c.begin(i)); iter != last; ++iter)
         {
-            auto const match(
-                std::find_if(v.begin(), v.end(), [iter](typename Values::value_type const& e) {
-                    return Traits::extract(*iter) == Traits::extract(e);
-                }));
+            auto const match(std::ranges::find_if(v, [iter](Values::value_type const& e) {
+                return Traits::extract(*iter) == Traits::extract(e);
+            }));
             BEAST_EXPECT(match != v.end());
             BEAST_EXPECT(keyEq(Traits::extract(*iter), Traits::extract(*match)));
             BEAST_EXPECT(hash(Traits::extract(*iter)) == hash(Traits::extract(*match)));
@@ -671,7 +671,7 @@ void
 AgedAssociativeContainerTestBase::checkContentsRefRef(C&& c, Values const& v)
 {
     using Cont = std::remove_reference_t;
-    using size_type = typename Cont::size_type;
+    using size_type = Cont::size_type;
 
     BEAST_EXPECT(c.size() == v.size());
     BEAST_EXPECT(size_type(std::distance(c.begin(), c.end())) == v.size());
@@ -703,7 +703,7 @@ AgedAssociativeContainerTestBase::checkContents(Cont& c)
 {
     using Traits =
         TestTraits;
-    using Values = typename Traits::Values;
+    using Values = Traits::Values;
     checkContents(c, Values());
 }
 
@@ -719,10 +719,10 @@ std::enable_if_t
 AgedAssociativeContainerTestBase::testConstructEmpty()
 {
     using Traits = TestTraits;
-    using Comp = typename Traits::Comp;
-    using Alloc = typename Traits::Alloc;
-    using MyComp = typename Traits::MyComp;
-    using MyAlloc = typename Traits::MyAlloc;
+    using Comp = Traits::Comp;
+    using Alloc = Traits::Alloc;
+    using MyComp = Traits::MyComp;
+    using MyAlloc = Traits::MyAlloc;
     typename Traits::ManualClock clock;
 
     // testcase (Traits::name() + " empty");
@@ -755,12 +755,12 @@ std::enable_if_t
 AgedAssociativeContainerTestBase::testConstructEmpty()
 {
     using Traits = TestTraits;
-    using Hash = typename Traits::Hash;
-    using Equal = typename Traits::Equal;
-    using Alloc = typename Traits::Alloc;
-    using MyHash = typename Traits::MyHash;
-    using MyEqual = typename Traits::MyEqual;
-    using MyAlloc = typename Traits::MyAlloc;
+    using Hash = Traits::Hash;
+    using Equal = Traits::Equal;
+    using Alloc = Traits::Alloc;
+    using MyHash = Traits::MyHash;
+    using MyEqual = Traits::MyEqual;
+    using MyAlloc = Traits::MyAlloc;
     typename Traits::ManualClock clock;
 
     // testcase (Traits::name() + " empty");
@@ -813,10 +813,10 @@ std::enable_if_t
 AgedAssociativeContainerTestBase::testConstructRange()
 {
     using Traits = TestTraits;
-    using Comp = typename Traits::Comp;
-    using Alloc = typename Traits::Alloc;
-    using MyComp = typename Traits::MyComp;
-    using MyAlloc = typename Traits::MyAlloc;
+    using Comp = Traits::Comp;
+    using Alloc = Traits::Alloc;
+    using MyComp = Traits::MyComp;
+    using MyAlloc = Traits::MyAlloc;
     typename Traits::ManualClock clock;
     auto const v(Traits::values());
 
@@ -860,12 +860,12 @@ std::enable_if_t
 AgedAssociativeContainerTestBase::testConstructRange()
 {
     using Traits = TestTraits;
-    using Hash = typename Traits::Hash;
-    using Equal = typename Traits::Equal;
-    using Alloc = typename Traits::Alloc;
-    using MyHash = typename Traits::MyHash;
-    using MyEqual = typename Traits::MyEqual;
-    using MyAlloc = typename Traits::MyAlloc;
+    using Hash = Traits::Hash;
+    using Equal = Traits::Equal;
+    using Alloc = Traits::Alloc;
+    using MyHash = Traits::MyHash;
+    using MyEqual = Traits::MyEqual;
+    using MyAlloc = Traits::MyAlloc;
     typename Traits::ManualClock clock;
     auto const v(Traits::values());
 
@@ -962,7 +962,7 @@ void
 AgedAssociativeContainerTestBase::testCopyMove()
 {
     using Traits = TestTraits;
-    using Alloc = typename Traits::Alloc;
+    using Alloc = Traits::Alloc;
     typename Traits::ManualClock clock;
     auto const v(Traits::values());
 
@@ -1307,7 +1307,7 @@ AgedAssociativeContainerTestBase::testChronological()
     // Test touch() with a non-const iterator.
     for (auto iter(v.crbegin()); iter != v.crend(); ++iter)
     {
-        using iterator = typename decltype(c)::iterator;
+        using iterator = decltype(c)::iterator;
         iterator const found(c.find(Traits::extract(*iter)));
 
         BEAST_EXPECT(found != c.cend());
@@ -1327,7 +1327,7 @@ AgedAssociativeContainerTestBase::testChronological()
     // Test touch() with a const_iterator
     for (auto iter(v.cbegin()); iter != v.cend(); ++iter)
     {
-        using const_iterator = typename decltype(c)::const_iterator;
+        using const_iterator = decltype(c)::const_iterator;
         const_iterator const found(c.find(Traits::extract(*iter)));
 
         BEAST_EXPECT(found != c.cend());
diff --git a/src/test/beast/beast_io_latency_probe_test.cpp b/src/test/beast/beast_io_latency_probe_test.cpp
index 5f183ef091..b7e4980f05 100644
--- a/src/test/beast/beast_io_latency_probe_test.cpp
+++ b/src/test/beast/beast_io_latency_probe_test.cpp
@@ -36,8 +36,8 @@ class io_latency_probe_test : public beast::unit_test::Suite, public beast::test
     template 
     struct MeasureAsioTimers
     {
-        using duration = typename Clock::duration;
-        using rep = typename MeasureClock::duration::rep;
+        using duration = Clock::duration;
+        using rep = MeasureClock::duration::rep;
 
         std::vector elapsedTimes;
 
diff --git a/src/test/consensus/ByzantineFailureSim_test.cpp b/src/test/consensus/ByzantineFailureSim_test.cpp
index ad75a78086..c3c51125b5 100644
--- a/src/test/consensus/ByzantineFailureSim_test.cpp
+++ b/src/test/consensus/ByzantineFailureSim_test.cpp
@@ -1,4 +1,3 @@
-#include 
 #include 
 #include 
 #include 
diff --git a/src/test/consensus/Consensus_test.cpp b/src/test/consensus/Consensus_test.cpp
index 629a97f0ea..92a4c67e32 100644
--- a/src/test/consensus/Consensus_test.cpp
+++ b/src/test/consensus/Consensus_test.cpp
@@ -1,4 +1,3 @@
-#include 
 #include 
 #include 
 #include 
diff --git a/src/test/consensus/DistributedValidatorsSim_test.cpp b/src/test/consensus/DistributedValidatorsSim_test.cpp
index 6d9ac6bede..1def09db13 100644
--- a/src/test/consensus/DistributedValidatorsSim_test.cpp
+++ b/src/test/consensus/DistributedValidatorsSim_test.cpp
@@ -1,4 +1,3 @@
-#include 
 #include 
 #include 
 #include 
diff --git a/src/test/consensus/ScaleFreeSim_test.cpp b/src/test/consensus/ScaleFreeSim_test.cpp
index 7e75aea72a..e533e09eb0 100644
--- a/src/test/consensus/ScaleFreeSim_test.cpp
+++ b/src/test/consensus/ScaleFreeSim_test.cpp
@@ -1,4 +1,3 @@
-#include 
 #include 
 #include 
 #include 
diff --git a/src/test/csf.h b/src/test/csf.h
index 81af3491c4..d2ddbb460d 100644
--- a/src/test/csf.h
+++ b/src/test/csf.h
@@ -1,3 +1,5 @@
+#pragma once
+
 #include 
 #include 
 #include 
diff --git a/src/test/csf/BasicNetwork.h b/src/test/csf/BasicNetwork.h
index e1d519b30b..418cdcf289 100644
--- a/src/test/csf/BasicNetwork.h
+++ b/src/test/csf/BasicNetwork.h
@@ -64,9 +64,9 @@ class BasicNetwork
 
     using clock_type = Scheduler::clock_type;
 
-    using duration = typename clock_type::duration;
+    using duration = clock_type::duration;
 
-    using time_point = typename clock_type::time_point;
+    using time_point = clock_type::time_point;
 
     struct LinkType
     {
diff --git a/src/test/csf/Digraph.h b/src/test/csf/Digraph.h
index 6fbb8c3514..20e45faa5b 100644
--- a/src/test/csf/Digraph.h
+++ b/src/test/csf/Digraph.h
@@ -128,7 +128,7 @@ public:
     outVertices() const
     {
         return boost::adaptors::transform(
-            graph_, [](typename Graph::value_type const& v) { return v.first; });
+            graph_, [](Graph::value_type const& v) { return v.first; });
     }
 
     /** Range over target vertices
@@ -139,7 +139,7 @@ public:
     [[nodiscard]] auto
     outVertices(Vertex source) const
     {
-        auto transform = [](typename Links::value_type const& link) { return link.first; };
+        auto transform = [](Links::value_type const& link) { return link.first; };
         auto it = graph_.find(source);
         if (it != graph_.end())
             return boost::adaptors::transform(it->second, transform);
@@ -165,7 +165,7 @@ public:
     [[nodiscard]] auto
     outEdges(Vertex source) const
     {
-        auto transform = [source](typename Links::value_type const& link) {
+        auto transform = [source](Links::value_type const& link) {
             return Edge{source, link.first, link.second};
         };
 
diff --git a/src/test/csf/Scheduler.h b/src/test/csf/Scheduler.h
index ede43be854..e7cbe27036 100644
--- a/src/test/csf/Scheduler.h
+++ b/src/test/csf/Scheduler.h
@@ -27,9 +27,9 @@ class Scheduler
 public:
     using clock_type = beast::ManualClock;
 
-    using duration = typename clock_type::duration;
+    using duration = clock_type::duration;
 
-    using time_point = typename clock_type::time_point;
+    using time_point = clock_type::time_point;
 
 private:
     using by_when_hook =
@@ -87,14 +87,14 @@ private:
     class QueueType
     {
     private:
-        using by_when_set = typename boost::intrusive::
+        using by_when_set = boost::intrusive::
             make_multiset>::type;
         // alloc_ is owned by the scheduler
         boost::container::pmr::monotonic_buffer_resource* alloc_;
         by_when_set byWhen_;
 
     public:
-        using iterator = typename by_when_set::iterator;
+        using iterator = by_when_set::iterator;
 
         QueueType(QueueType const&) = delete;
         QueueType&
@@ -114,7 +114,7 @@ private:
         end();
 
         template 
-        typename by_when_set::iterator
+        by_when_set::iterator
         emplace(time_point when, Handler&& h);
 
         iterator
@@ -287,7 +287,7 @@ Scheduler::QueueType::end() -> iterator
 
 template 
 inline auto
-Scheduler::QueueType::emplace(time_point when, Handler&& h) -> typename by_when_set::iterator
+Scheduler::QueueType::emplace(time_point when, Handler&& h) -> by_when_set::iterator
 {
     using event_type = EventImpl>;
     auto const p = alloc_->allocate(sizeof(event_type));
@@ -296,7 +296,7 @@ Scheduler::QueueType::emplace(time_point when, Handler&& h) -> typename by_when_
 }
 
 inline auto
-Scheduler::QueueType::erase(iterator iter) -> typename by_when_set::iterator
+Scheduler::QueueType::erase(iterator iter) -> by_when_set::iterator
 {
     auto& e = *iter;
     auto next = byWhen_.erase(iter);
@@ -309,7 +309,7 @@ Scheduler::QueueType::erase(iterator iter) -> typename by_when_set::iterator
 struct Scheduler::CancelToken
 {
 private:
-    typename QueueType::iterator iter_;
+    QueueType::iterator iter_;
 
 public:
     CancelToken() = delete;
@@ -319,7 +319,7 @@ public:
 
 private:
     friend class Scheduler;
-    CancelToken(typename QueueType::iterator iter) : iter_(iter)
+    CancelToken(QueueType::iterator iter) : iter_(iter)
     {
     }
 };
diff --git a/src/test/csf/SimTime.h b/src/test/csf/SimTime.h
index e38a375e02..125674f15d 100644
--- a/src/test/csf/SimTime.h
+++ b/src/test/csf/SimTime.h
@@ -11,7 +11,7 @@ using RealDuration = RealClock::duration;
 using RealTime = RealClock::time_point;
 
 using SimClock = beast::ManualClock;
-using SimDuration = typename SimClock::duration;
-using SimTime = typename SimClock::time_point;
+using SimDuration = SimClock::duration;
+using SimTime = SimClock::time_point;
 
 }  // namespace xrpl::test::csf
diff --git a/src/test/csf/random.h b/src/test/csf/random.h
index 08002aed7f..30f98e37fe 100644
--- a/src/test/csf/random.h
+++ b/src/test/csf/random.h
@@ -72,14 +72,14 @@ public:
     Selector(RAIter first, RAIter last, std::vector const& w, Generator& g)
         : first_{first}, last_{last}, dd_{w.begin(), w.end()}, g_{g}
     {
-        using tag = typename std::iterator_traits::iterator_category;
+        using tag = std::iterator_traits::iterator_category;
         static_assert(
             std::is_same_v,
             "Selector only supports random access iterators.");
         // TODO: Allow for forward iterators
     }
 
-    typename std::iterator_traits::value_type
+    std::iterator_traits::value_type
     operator()()
     {
         auto idx = dd_(g_);
diff --git a/src/test/jtx/AMM.h b/src/test/jtx/AMM.h
index deadd80290..99131637bb 100644
--- a/src/test/jtx/AMM.h
+++ b/src/test/jtx/AMM.h
@@ -174,12 +174,22 @@ public:
     ammRpcInfo(
         std::optional const& account = std::nullopt,
         std::optional const& ledgerIndex = std::nullopt,
-        std::optional asset1 = std::nullopt,
-        std::optional asset2 = std::nullopt,
+        std::optional const& asset1 = std::nullopt,
+        std::optional const& asset2 = std::nullopt,
         std::optional const& ammAccount = std::nullopt,
         bool ignoreParams = false,
         unsigned apiVersion = RPC::kApiInvalidVersion) const;
 
+    [[nodiscard]] json::Value
+    ammRpcInfo(
+        std::optional const& account,
+        std::optional const& ledgerIndex,
+        std::optional const& asset1,
+        std::optional const& asset2,
+        std::optional const& ammAccount,
+        bool ignoreParams,
+        unsigned apiVersion) const;
+
     /** Verify the AMM balances.
      */
     [[nodiscard]] bool
diff --git a/src/test/jtx/TestHelpers.h b/src/test/jtx/TestHelpers.h
index 27c54d830b..0e35e6b9ec 100644
--- a/src/test/jtx/TestHelpers.h
+++ b/src/test/jtx/TestHelpers.h
@@ -27,6 +27,7 @@ namespace xrpl::test::jtx {
 */
 template <
     class SField,
+    // NOLINTNEXTLINE(readability-redundant-typename): typename required by MSVC
     class StoredValue = typename SField::type::value_type,
     class OutputValue = StoredValue>
 struct JTxField
@@ -213,8 +214,8 @@ template 
 struct JTxFieldWrapper
 {
     using JF = JTxField;
-    using SF = typename JF::SF;
-    using SV = typename JF::SV;
+    using SF = JF::SF;
+    using SV = JF::SV;
 
 protected:
     SF const& sfield_;
@@ -266,9 +267,11 @@ public:
     }
 };
 
+// NOLINTNEXTLINE(readability-redundant-typename): typename required by MSVC
 template 
 using valueUnitWrapper = JTxFieldWrapper>;
 
+// NOLINTNEXTLINE(readability-redundant-typename): typename required by MSVC
 template 
 using simpleField = JTxFieldWrapper>;
 
diff --git a/src/test/jtx/impl/AMM.cpp b/src/test/jtx/impl/AMM.cpp
index c6dc14081a..2184f7e1b0 100644
--- a/src/test/jtx/impl/AMM.cpp
+++ b/src/test/jtx/impl/AMM.cpp
@@ -183,15 +183,37 @@ json::Value
 AMM::ammRpcInfo(
     std::optional const& account,
     std::optional const& ledgerIndex,
-    std::optional asset1,
-    std::optional asset2,
+    std::optional const& asset1,
+    std::optional const& asset2,
     std::optional const& ammAccount,
     bool ignoreParams,
     unsigned apiVersion) const
+{
+    auto const toJson = [](AccountID const& a) { return json::Value{to_string(a)}; };
+
+    return ammRpcInfo(
+        account.transform(toJson),
+        ledgerIndex,
+        asset1,
+        asset2,
+        ammAccount.transform(toJson),
+        ignoreParams,
+        apiVersion);
+}
+
+json::Value
+AMM::ammRpcInfo(
+    std::optional const& account,
+    std::optional const& ledgerIndex,
+    std::optional const& asset1,
+    std::optional const& asset2,
+    std::optional const& ammAccount,
+    bool ignoreParams,
+    unsigned apiVersion) const
 {
     json::Value jv;
     if (account)
-        jv[jss::account] = to_string(*account);
+        jv[jss::account] = *account;
     if (ledgerIndex)
         jv[jss::ledger_index] = *ledgerIndex;
     if (!ignoreParams)
@@ -209,7 +231,7 @@ AMM::ammRpcInfo(
             jv[jss::asset2] = STIssue(sfAsset2, asset2_.asset()).getJson(JsonOptions::Values::None);
         }
         if (ammAccount)
-            jv[jss::amm_account] = to_string(*ammAccount);
+            jv[jss::amm_account] = *ammAccount;
     }
     auto jr =
         (apiVersion == RPC::kApiInvalidVersion
diff --git a/src/test/nodestore/Timing_test.cpp b/src/test/nodestore/Timing_test.cpp
index f5e6bf8aa4..e308d0d5ff 100644
--- a/src/test/nodestore/Timing_test.cpp
+++ b/src/test/nodestore/Timing_test.cpp
@@ -57,7 +57,7 @@ template 
 static void
 rngcpy(void* buffer, std::size_t bytes, Generator& g)
 {
-    using result_type = typename Generator::result_type;
+    using result_type = Generator::result_type;
     while (bytes >= sizeof(result_type))
     {
         auto const v = g();
diff --git a/src/test/rpc/AMMInfo_test.cpp b/src/test/rpc/AMMInfo_test.cpp
index 28c536aab9..987df6c724 100644
--- a/src/test/rpc/AMMInfo_test.cpp
+++ b/src/test/rpc/AMMInfo_test.cpp
@@ -65,6 +65,26 @@ public:
             BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
         });
 
+        // Account is not a string
+        testAMM([&](AMM& ammAlice, Env&) {
+            auto const jv =
+                ammAlice.ammRpcInfo(json::Value{42}, std::nullopt, XRP, USD, std::nullopt, true, 3);
+            BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
+        });
+
+        // AMM Account is not a string
+        testAMM([&](AMM& ammAlice, Env&) {
+            auto const jv = ammAlice.ammRpcInfo(
+                json::Value{to_string(ammAlice.ammAccount())},
+                std::nullopt,
+                XRP,
+                USD,
+                json::Value{42},
+                false,
+                3);
+            BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
+        });
+
         std::vector, std::optional, TestAccount, bool>> const
             invalidParams = {
                 {xrpIssue(), std::nullopt, TestAccount::None, false},
diff --git a/src/test/server/Server_test.cpp b/src/test/server/Server_test.cpp
index 54091ef767..9ff0015b5d 100644
--- a/src/test/server/Server_test.cpp
+++ b/src/test/server/Server_test.cpp
@@ -169,7 +169,7 @@ public:
     // Connect to an address
     template 
     bool
-    connect(Socket& s, typename Socket::endpoint_type const& ep)
+    connect(Socket& s, Socket::endpoint_type const& ep)
     {
         try
         {
diff --git a/src/test/unit_test/multi_runner.cpp b/src/test/unit_test/multi_runner.cpp
index 3a56d22654..71208313a4 100644
--- a/src/test/unit_test/multi_runner.cpp
+++ b/src/test/unit_test/multi_runner.cpp
@@ -69,9 +69,7 @@ Results::add(SuiteResults const& r)
             top.begin(),
             top.end(),
             elapsed,
-            [](run_time const& t1, typename clock_type::duration const& t2) {
-                return t1.second > t2;
-            });
+            [](run_time const& t1, clock_type::duration const& t2) { return t1.second > t2; });
 
         if (iter != top.end())
         {
diff --git a/src/test/unit_test/multi_runner.h b/src/test/unit_test/multi_runner.h
index 8b07559e8c..55cf6c25fa 100644
--- a/src/test/unit_test/multi_runner.h
+++ b/src/test/unit_test/multi_runner.h
@@ -42,7 +42,7 @@ struct SuiteResults
     std::size_t cases = 0;
     std::size_t total = 0;
     std::size_t failed = 0;
-    typename clock_type::time_point start = clock_type::now();
+    clock_type::time_point start = clock_type::now();
 
     explicit SuiteResults(std::string name = "") : name(std::move(name))
     {
@@ -57,7 +57,7 @@ struct Results
     using static_string = boost::beast::static_string<256>;
     // results may be stored in shared memory. Use `static_string` to ensure
     // pointers from different memory spaces do not co-mingle
-    using run_time = std::pair;
+    using run_time = std::pair;
 
     static constexpr auto kMaxTop = 10;
 
@@ -66,7 +66,7 @@ struct Results
     std::size_t total = 0;
     std::size_t failed = 0;
     boost::container::static_vector top;
-    typename clock_type::time_point start = clock_type::now();
+    clock_type::time_point start = clock_type::now();
 
     void
     add(SuiteResults const& r);
diff --git a/src/test/unit_test/utils.h b/src/test/unit_test/utils.h
index 028823c763..677bbff31b 100644
--- a/src/test/unit_test/utils.h
+++ b/src/test/unit_test/utils.h
@@ -1,3 +1,5 @@
+#pragma once
+
 #include 
 
 #include 
diff --git a/src/tests/libxrpl/CMakeLists.txt b/src/tests/libxrpl/CMakeLists.txt
index 2dae6fccb9..bd56028728 100644
--- a/src/tests/libxrpl/CMakeLists.txt
+++ b/src/tests/libxrpl/CMakeLists.txt
@@ -13,6 +13,7 @@ add_executable(
     helpers/TestSink.cpp
     helpers/TxTest.cpp
 )
+patch_nix_binary(xrpl_tests)
 set_target_properties(
     xrpl_tests
     PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
diff --git a/src/xrpld/app/consensus/RCLConsensus.cpp b/src/xrpld/app/consensus/RCLConsensus.cpp
index a474c9c339..a7c2b26c04 100644
--- a/src/xrpld/app/consensus/RCLConsensus.cpp
+++ b/src/xrpld/app/consensus/RCLConsensus.cpp
@@ -580,7 +580,9 @@ RCLConsensus::Adaptor::doAccept(
         JLOG(j_.info()) << "CNF Val " << newLCLHash;
     }
     else
+    {
         JLOG(j_.info()) << "CNF buildLCL " << newLCLHash;
+    }
 
     // See if we can accept a ledger as fully-validated
     ledgerMaster_.consensusBuilt(built.ledger, result.txns.id(), std::move(consensusJson));
@@ -796,7 +798,9 @@ RCLConsensus::Adaptor::buildLCL(
         JLOG(j_.debug()) << "Consensus built ledger we were acquiring";
     }
     else
+    {
         JLOG(j_.debug()) << "Consensus built new ledger";
+    }
     return RCLCxLedger{std::move(built)};
 }
 
diff --git a/src/xrpld/app/consensus/RCLValidations.cpp b/src/xrpld/app/consensus/RCLValidations.cpp
index 02ff86b23d..9d40e60b00 100644
--- a/src/xrpld/app/consensus/RCLValidations.cpp
+++ b/src/xrpld/app/consensus/RCLValidations.cpp
@@ -46,8 +46,10 @@ RCLValidatedLedger::RCLValidatedLedger(
         ancestors_ = hashIndex->getFieldV256(sfHashes).value();
     }
     else
+    {
         JLOG(j_.warn()) << "Ledger " << ledgerSeq_ << ":" << ledgerID_
                         << " missing recent ancestor hashes";
+    }
 }
 
 auto
diff --git a/src/xrpld/app/ledger/LedgerHistory.cpp b/src/xrpld/app/ledger/LedgerHistory.cpp
index 8520fc941f..b7e1772942 100644
--- a/src/xrpld/app/ledger/LedgerHistory.cpp
+++ b/src/xrpld/app/ledger/LedgerHistory.cpp
@@ -368,8 +368,10 @@ LedgerHistory::handleMismatch(
                              << " validated: " << to_string(*validatedConsensusHash);
         }
         else
+        {
             JLOG(j_.error()) << "MISMATCH with same consensus transaction set: "
                              << to_string(*builtConsensusHash);
+        }
     }
 
     // Find differences between built and valid ledgers
@@ -381,8 +383,10 @@ LedgerHistory::handleMismatch(
         JLOG(j_.error()) << "MISMATCH with same " << builtTx.size() << " transactions";
     }
     else
+    {
         JLOG(j_.error()) << "MISMATCH with " << builtTx.size() << " built and " << validTx.size()
                          << " valid transactions.";
+    }
 
     JLOG(j_.error()) << "built\n" << getJson({*builtLedger, {}});
     JLOG(j_.error()) << "valid\n" << getJson({*validLedger, {}});
diff --git a/src/xrpld/app/ledger/OrderBookDB.h b/src/xrpld/app/ledger/OrderBookDB.h
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/src/xrpld/app/ledger/detail/BuildLedger.cpp b/src/xrpld/app/ledger/detail/BuildLedger.cpp
index a77c1c9c50..2a4121ede9 100644
--- a/src/xrpld/app/ledger/detail/BuildLedger.cpp
+++ b/src/xrpld/app/ledger/detail/BuildLedger.cpp
@@ -204,9 +204,11 @@ buildLedger(
                                 << accum.txCount();
             }
             else
+            {
                 JLOG(j.debug()) << "Applied " << applied << " transactions. "
                                 << "Total transactions in ledger (including Inner Batch): "
                                 << accum.txCount();
+            }
         });
 }
 
diff --git a/src/xrpld/app/ledger/detail/LedgerCleaner.cpp b/src/xrpld/app/ledger/detail/LedgerCleaner.cpp
index 9f2db9d2f2..b96f01e577 100644
--- a/src/xrpld/app/ledger/detail/LedgerCleaner.cpp
+++ b/src/xrpld/app/ledger/detail/LedgerCleaner.cpp
@@ -366,7 +366,9 @@ private:
             }
         }
         else
+        {
             JLOG(j_.warn()) << "Validated ledger is prior to target ledger";
+        }
 
         return ledgerHash;
     }
diff --git a/src/xrpld/app/ledger/detail/LedgerMaster.cpp b/src/xrpld/app/ledger/detail/LedgerMaster.cpp
index 9baad0ec90..31510b44d2 100644
--- a/src/xrpld/app/ledger/detail/LedgerMaster.cpp
+++ b/src/xrpld/app/ledger/detail/LedgerMaster.cpp
@@ -755,7 +755,9 @@ LedgerMaster::getFetchPack(LedgerIndex missing, InboundLedger::Reason reason)
         JLOG(journal_.trace()) << "Requested fetch pack for " << missing;
     }
     else
+    {
         JLOG(journal_.debug()) << "No peer for fetch pack";
+    }
 }
 
 void
@@ -1797,10 +1799,14 @@ LedgerMaster::fetchForHistory(
                     getFetchPack(missing, reason);
                 }
                 else
+                {
                     JLOG(journal_.trace()) << "fetchForHistory no fetch pack for " << missing;
+                }
             }
             else
+            {
                 JLOG(journal_.debug()) << "fetchForHistory found failed acquire";
+            }
         }
         if (ledger)
         {
diff --git a/src/xrpld/app/main/Application.cpp b/src/xrpld/app/main/Application.cpp
index 67b5e30eb7..c2fb78ce79 100644
--- a/src/xrpld/app/main/Application.cpp
+++ b/src/xrpld/app/main/Application.cpp
@@ -1612,7 +1612,9 @@ ApplicationImp::signalStop(std::string const& msg)
             JLOG(journal_.warn()) << "Server stopping";
         }
         else
+        {
             JLOG(journal_.warn()) << "Server stopping: " << msg;
+        }
 
         isTimeToStop.notify_all();
     }
diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp
index d807dea10a..1802441a66 100644
--- a/src/xrpld/app/misc/NetworkOPs.cpp
+++ b/src/xrpld/app/misc/NetworkOPs.cpp
@@ -1687,11 +1687,13 @@ NetworkOPsImp::apply(std::unique_lock& batchLock)
                         e.transaction->setKept();
                     }
                     else
+                    {
                         JLOG(journal_.debug())
                             << "Not holding transaction " << e.transaction->getID() << ": "
                             << (e.local ? "local" : "network") << ", "
                             << "result: " << e.result << " ledgers left: "
                             << (ledgersLeft ? to_string(*ledgersLeft) : "unspecified");
+                    }
                 }
             }
             else
diff --git a/src/xrpld/app/misc/detail/Transaction.cpp b/src/xrpld/app/misc/detail/Transaction.cpp
index 425a5723fb..2c55c474eb 100644
--- a/src/xrpld/app/misc/detail/Transaction.cpp
+++ b/src/xrpld/app/misc/detail/Transaction.cpp
@@ -73,7 +73,7 @@ Transaction::setStatus(
 TransStatus
 Transaction::sqlTransactionStatus(boost::optional const& status)
 {
-    auto const c = (status) ? safeCast((*status)[0]) : TxnSql::Unknown;
+    auto const c = status ? safeCast((*status)[0]) : TxnSql::Unknown;
 
     switch (static_cast(c))
     {
diff --git a/src/xrpld/app/misc/detail/ValidatorSite.cpp b/src/xrpld/app/misc/detail/ValidatorSite.cpp
index 76fd078174..6ce3711652 100644
--- a/src/xrpld/app/misc/detail/ValidatorSite.cpp
+++ b/src/xrpld/app/misc/detail/ValidatorSite.cpp
@@ -339,8 +339,10 @@ ValidatorSite::onRequestTimeout(std::size_t siteIdx, error_code const& ec)
             JLOG(j_.warn()) << "Request for " << site.activeResource->uri << " took too long";
         }
         else
+        {
             JLOG(j_.error()) << "Request took too long, but a response has "
                                 "already been processed";
+        }
     }
 
     std::scoped_lock const lockState{stateMutex_};
diff --git a/src/xrpld/app/rdb/backend/detail/Node.cpp b/src/xrpld/app/rdb/backend/detail/Node.cpp
index 9b7db6f0f5..dcc146397b 100644
--- a/src/xrpld/app/rdb/backend/detail/Node.cpp
+++ b/src/xrpld/app/rdb/backend/detail/Node.cpp
@@ -125,7 +125,7 @@ makeLedgerDBs(
             std::size_t notnull = 0, dfltValue = 0, pk = 0;
             soci::indicator ind = soci::i_null;
             soci::statement st =
-                (tx->getSession().prepare << ("PRAGMA table_info(AccountTransactions);"),
+                (tx->getSession().prepare << "PRAGMA table_info(AccountTransactions);",
                  soci::into(cid),
                  soci::into(name),
                  soci::into(type),
@@ -1065,10 +1065,10 @@ accountTxPage(
     if (findLedger == 0)
     {
         sql = boost::str(
-            boost::format(kPrefix + (R"(AccountTransactions.LedgerSeq BETWEEN %u AND %u
+            boost::format(kPrefix + R"(AccountTransactions.LedgerSeq BETWEEN %u AND %u
              ORDER BY AccountTransactions.LedgerSeq %s,
              AccountTransactions.TxnSeq %s
-             LIMIT %u;)")) %
+             LIMIT %u;)") %
             toBase58(options.account) % options.ledgerRange.min % options.ledgerRange.max % order %
             order % queryLimit);
     }
@@ -1080,7 +1080,7 @@ accountTxPage(
 
         auto b58acct = toBase58(options.account);
         sql = boost::str(
-            boost::format((
+            boost::format(
                 R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
             Status,RawTxn,TxnMeta
             FROM AccountTransactions, Transactions WHERE
@@ -1097,7 +1097,7 @@ accountTxPage(
             ORDER BY AccountTransactions.LedgerSeq %s,
             AccountTransactions.TxnSeq %s
             LIMIT %u;
-            )")) %
+            )") %
             b58acct % minLedger % maxLedger % b58acct % findLedger % compare % findSeq % order %
             order % queryLimit);
     }
diff --git a/src/xrpld/consensus/Consensus.h b/src/xrpld/consensus/Consensus.h
index 131db30ce0..b8d04e18b5 100644
--- a/src/xrpld/consensus/Consensus.h
+++ b/src/xrpld/consensus/Consensus.h
@@ -276,11 +276,11 @@ checkConsensus(
 template 
 class Consensus
 {
-    using Ledger_t = typename Adaptor::Ledger_t;
-    using TxSet_t = typename Adaptor::TxSet_t;
-    using NodeID_t = typename Adaptor::NodeID_t;
-    using Tx_t = typename TxSet_t::Tx;
-    using PeerPosition_t = typename Adaptor::PeerPosition_t;
+    using Ledger_t = Adaptor::Ledger_t;
+    using TxSet_t = Adaptor::TxSet_t;
+    using NodeID_t = Adaptor::NodeID_t;
+    using Tx_t = TxSet_t::Tx;
+    using PeerPosition_t = Adaptor::PeerPosition_t;
     using Proposal_t = ConsensusProposal;
 
     using Result = ConsensusResult;
@@ -341,7 +341,7 @@ public:
     void
     startRound(
         NetClock::time_point const& now,
-        typename Ledger_t::ID const& prevLedgerID,
+        Ledger_t::ID const& prevLedgerID,
         Ledger_t prevLedger,
         hash_set const& nowUntrusted,
         bool proposing,
@@ -402,7 +402,7 @@ public:
 
         @return ID of previous ledger
     */
-    typename Ledger_t::ID
+    Ledger_t::ID
     prevLedgerID() const
     {
         return prevLedgerID_;
@@ -428,16 +428,14 @@ private:
     void
     startRoundInternal(
         NetClock::time_point const& now,
-        typename Ledger_t::ID const& prevLedgerID,
+        Ledger_t::ID const& prevLedgerID,
         Ledger_t const& prevLedger,
         ConsensusMode mode,
         std::unique_ptr const& clog);
 
     // Change our view of the previous ledger
     void
-    handleWrongLedger(
-        typename Ledger_t::ID const& lgrId,
-        std::unique_ptr const& clog);
+    handleWrongLedger(Ledger_t::ID const& lgrId, std::unique_ptr const& clog);
 
     /** Check if our previous ledger matches the network's.
 
@@ -568,7 +566,7 @@ private:
     // Non-peer (self) consensus data
 
     // Last validated ledger ID provided to consensus
-    typename Ledger_t::ID prevLedgerID_;
+    Ledger_t::ID prevLedgerID_;
     // Last validated ledger seen by consensus
     Ledger_t previousLedger_;
 
@@ -616,7 +614,7 @@ template 
 void
 Consensus::startRound(
     NetClock::time_point const& now,
-    typename Ledger_t::ID const& prevLedgerID,
+    Ledger_t::ID const& prevLedgerID,
     Ledger_t prevLedger,
     hash_set const& nowUntrusted,
     bool proposing,
@@ -661,7 +659,7 @@ template 
 void
 Consensus::startRoundInternal(
     NetClock::time_point const& now,
-    typename Ledger_t::ID const& prevLedgerID,
+    Ledger_t::ID const& prevLedgerID,
     Ledger_t const& prevLedger,
     ConsensusMode mode,
     std::unique_ptr const& clog)
@@ -811,7 +809,9 @@ Consensus::peerProposalInternal(
                 gotTxSet(now_, *set);
             }
             else
+            {
                 JLOG(j_.debug()) << "Don't have tx set for peer";
+            }
         }
         else if (result_)
         {
@@ -1025,7 +1025,7 @@ Consensus::getJson(bool full) const
 template 
 void
 Consensus::handleWrongLedger(
-    typename Ledger_t::ID const& lgrId,
+    Ledger_t::ID const& lgrId,
     std::unique_ptr const& clog)
 {
     CLOG(clog) << "handleWrongLedger. ";
diff --git a/src/xrpld/consensus/ConsensusTypes.h b/src/xrpld/consensus/ConsensusTypes.h
index 64a7f5fdea..f043fc0663 100644
--- a/src/xrpld/consensus/ConsensusTypes.h
+++ b/src/xrpld/consensus/ConsensusTypes.h
@@ -183,11 +183,11 @@ enum class ConsensusState {
 template 
 struct ConsensusResult
 {
-    using Ledger_t = typename Traits::Ledger_t;
-    using TxSet_t = typename Traits::TxSet_t;
-    using NodeID_t = typename Traits::NodeID_t;
+    using Ledger_t = Traits::Ledger_t;
+    using TxSet_t = Traits::TxSet_t;
+    using NodeID_t = Traits::NodeID_t;
 
-    using Tx_t = typename TxSet_t::Tx;
+    using Tx_t = TxSet_t::Tx;
     using Proposal_t = ConsensusProposal;
     using Dispute_t = DisputedTx;
 
diff --git a/src/xrpld/consensus/DisputedTx.h b/src/xrpld/consensus/DisputedTx.h
index 1c0c069f54..ba8329714b 100644
--- a/src/xrpld/consensus/DisputedTx.h
+++ b/src/xrpld/consensus/DisputedTx.h
@@ -29,7 +29,7 @@ namespace xrpl {
 template 
 class DisputedTx
 {
-    using TxID_t = typename Tx::ID;
+    using TxID_t = Tx::ID;
     using Map_t = boost::container::flat_map;
 
 public:
diff --git a/src/xrpld/consensus/LedgerTrie.h b/src/xrpld/consensus/LedgerTrie.h
index 9d76c7f283..cd9662ff02 100644
--- a/src/xrpld/consensus/LedgerTrie.h
+++ b/src/xrpld/consensus/LedgerTrie.h
@@ -21,8 +21,8 @@ template 
 class SpanTip
 {
 public:
-    using Seq = typename Ledger::Seq;
-    using ID = typename Ledger::ID;
+    using Seq = Ledger::Seq;
+    using ID = Ledger::ID;
 
     SpanTip(Seq s, ID i, Ledger const lgr) : seq{s}, id{i}, ledger_{std::move(lgr)}
     {
@@ -58,8 +58,8 @@ namespace ledger_trie_detail {
 template 
 class Span
 {
-    using Seq = typename Ledger::Seq;
-    using ID = typename Ledger::ID;
+    using Seq = Ledger::Seq;
+    using ID = Ledger::ID;
 
     // The span is the half-open interval [start,end) of ledger_
     Seq start_{0};
@@ -323,8 +323,8 @@ struct Node
 template 
 class LedgerTrie
 {
-    using Seq = typename Ledger::Seq;
-    using ID = typename Ledger::ID;
+    using Seq = Ledger::Seq;
+    using ID = Ledger::ID;
 
     using Node = ledger_trie_detail::Node;
     using Span = ledger_trie_detail::Span;
diff --git a/src/xrpld/consensus/Validations.h b/src/xrpld/consensus/Validations.h
index 2f5762ce83..f109ae620b 100644
--- a/src/xrpld/consensus/Validations.h
+++ b/src/xrpld/consensus/Validations.h
@@ -267,13 +267,13 @@ to_string(ValStatus m)
 template 
 class Validations
 {
-    using Mutex = typename Adaptor::Mutex;
-    using Validation = typename Adaptor::Validation;
-    using Ledger = typename Adaptor::Ledger;
-    using ID = typename Ledger::ID;
-    using Seq = typename Ledger::Seq;
-    using NodeID = typename Validation::NodeID;
-    using NodeKey = typename Validation::NodeKey;
+    using Mutex = Adaptor::Mutex;
+    using Validation = Adaptor::Validation;
+    using Ledger = Adaptor::Ledger;
+    using ID = Ledger::ID;
+    using Seq = Ledger::Seq;
+    using NodeID = Validation::NodeID;
+    using NodeKey = Validation::NodeKey;
 
     using WrappedValidationType =
         std::decay_t>;
diff --git a/src/xrpld/overlay/Slot.h b/src/xrpld/overlay/Slot.h
index 0600265500..7490798787 100644
--- a/src/xrpld/overlay/Slot.h
+++ b/src/xrpld/overlay/Slot.h
@@ -82,7 +82,7 @@ class Slot final
 private:
     friend class Slots;
     using id_t = Peer::id_t;
-    using time_point = typename ClockType::time_point;
+    using time_point = ClockType::time_point;
 
     // a callback to report ignored squelches
     using ignored_squelch_callback = std::function;
@@ -217,7 +217,7 @@ private:
     std::uint16_t reachedThreshold_{0};
 
     // last time peers were selected, used to age the slot
-    typename ClockType::time_point lastSelected_;
+    ClockType::time_point lastSelected_;
 
     SlotState state_{SlotState::Counting};  // slot's state
     SquelchHandler const& handler_;         // squelch/unsquelch handler
@@ -483,7 +483,7 @@ Slot::notInState(PeerState state) const
 }
 
 template 
-std::set
+std::set
 Slot::getSelected() const
 {
     std::set r;
@@ -496,7 +496,7 @@ Slot::getSelected() const
 }
 
 template 
-std::unordered_map>
+std::unordered_map>
 Slot::getPeers() const
 {
     using namespace std::chrono;
@@ -526,8 +526,8 @@ Slot::getPeers() const
 template 
 class Slots final
 {
-    using time_point = typename ClockType::time_point;
-    using id_t = typename Peer::id_t;
+    using time_point = ClockType::time_point;
+    using id_t = Peer::id_t;
     using messages = beast::aged_unordered_map<
         uint256,
         std::unordered_set,
@@ -600,7 +600,7 @@ public:
         PublicKey const& validator,
         id_t id,
         protocol::MessageType type,
-        typename Slot::ignored_squelch_callback callback);
+        Slot::ignored_squelch_callback callback);
 
     /** Check if peers stopped relaying messages
      * and if slots stopped receiving messages from the validator.
@@ -651,9 +651,8 @@ public:
     /** Get peers info. Return map of peer's state, count, and squelch
      * expiration milliseconds.
      */
-    std::
-        unordered_map>
-        getPeers(PublicKey const& validator)
+    std::unordered_map>
+    getPeers(PublicKey const& validator)
     {
         auto const& it = slots_.find(validator);
         if (it != slots_.end())
@@ -742,7 +741,7 @@ Slots::updateSlotAndSquelch(
     PublicKey const& validator,
     id_t id,
     protocol::MessageType type,
-    typename Slot::ignored_squelch_callback callback)
+    Slot::ignored_squelch_callback callback)
 {
     if (!addPeerMessage(key, id))
         return;
diff --git a/src/xrpld/overlay/Squelch.h b/src/xrpld/overlay/Squelch.h
index b509f293c2..96d8c26f1d 100644
--- a/src/xrpld/overlay/Squelch.h
+++ b/src/xrpld/overlay/Squelch.h
@@ -14,7 +14,7 @@ namespace xrpl::reduce_relay {
 template 
 class Squelch
 {
-    using time_point = typename ClockType::time_point;
+    using time_point = ClockType::time_point;
 
 public:
     explicit Squelch(beast::Journal journal) : journal_(journal)
diff --git a/src/xrpld/overlay/detail/PeerImp.cpp b/src/xrpld/overlay/detail/PeerImp.cpp
index 21e84d6fc7..e1d7d23215 100644
--- a/src/xrpld/overlay/detail/PeerImp.cpp
+++ b/src/xrpld/overlay/detail/PeerImp.cpp
@@ -70,7 +70,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 #include 
 #include 
@@ -198,78 +197,70 @@ stringIsUInt256Sized(std::string const& pBuffStr)
 void
 PeerImp::run()
 {
-    if (!strand_.running_in_this_thread())
-    {
-        post(strand_, std::bind(&PeerImp::run, shared_from_this()));
-        return;
-    }
+    dispatch(strand_, [self = shared_from_this()]() {
+        auto parseLedgerHash = [](std::string_view value) -> std::optional {
+            if (uint256 ret; ret.parseHex(value))
+                return ret;
 
-    auto parseLedgerHash = [](std::string_view value) -> std::optional {
-        if (uint256 ret; ret.parseHex(value))
-            return ret;
+            if (auto const s = base64Decode(value); s.size() == uint256::size())
+                return uint256::fromRaw(s);
 
-        if (auto const s = base64Decode(value); s.size() == uint256::size())
-            return uint256::fromRaw(s);
+            return std::nullopt;
+        };
 
-        return std::nullopt;
-    };
+        std::optional closed;
+        std::optional previous;
 
-    std::optional closed;
-    std::optional previous;
+        if (auto const iter = self->headers_.find("Closed-Ledger"); iter != self->headers_.end())
+        {
+            closed = parseLedgerHash(iter->value());
 
-    if (auto const iter = headers_.find("Closed-Ledger"); iter != headers_.end())
-    {
-        closed = parseLedgerHash(iter->value());
+            if (!closed)
+                self->fail("Malformed handshake data (1)");
+        }
 
-        if (!closed)
-            fail("Malformed handshake data (1)");
-    }
+        if (auto const iter = self->headers_.find("Previous-Ledger"); iter != self->headers_.end())
+        {
+            previous = parseLedgerHash(iter->value());
 
-    if (auto const iter = headers_.find("Previous-Ledger"); iter != headers_.end())
-    {
-        previous = parseLedgerHash(iter->value());
+            if (!previous)
+                self->fail("Malformed handshake data (2)");
+        }
 
-        if (!previous)
-            fail("Malformed handshake data (2)");
-    }
+        if (previous && !closed)
+            self->fail("Malformed handshake data (3)");
 
-    if (previous && !closed)
-        fail("Malformed handshake data (3)");
+        {
+            std::scoped_lock const sl(self->recentLock_);
+            if (closed)
+                self->closedLedgerHash_ = *closed;
+            if (previous)
+                self->previousLedgerHash_ = *previous;
+        }
 
-    {
-        std::scoped_lock const sl(recentLock_);
-        if (closed)
-            closedLedgerHash_ = *closed;
-        if (previous)
-            previousLedgerHash_ = *previous;
-    }
+        if (self->inbound_)
+        {
+            self->doAccept();
+        }
+        else
+        {
+            self->doProtocolStart();
+        }
 
-    if (inbound_)
-    {
-        doAccept();
-    }
-    else
-    {
-        doProtocolStart();
-    }
-
-    // Anything else that needs to be done with the connection should be
-    // done in doProtocolStart
+        // Anything else that needs to be done with the connection should be
+        // done in doProtocolStart
+    });
 }
 
 void
 PeerImp::stop()
 {
-    if (!strand_.running_in_this_thread())
-    {
-        post(strand_, std::bind(&PeerImp::stop, shared_from_this()));
-        return;
-    }
+    dispatch(strand_, [self = shared_from_this()]() {
+        if (!self->socket_.is_open())
+            return;
 
-    if (!socket_.is_open())
-        return;
-
-    close();
+        self->close();
+    });
 }
 
 //------------------------------------------------------------------------------
@@ -277,126 +268,111 @@ PeerImp::stop()
 void
 PeerImp::send(std::shared_ptr const& m)
 {
-    if (!strand_.running_in_this_thread())
-    {
-        post(strand_, std::bind(&PeerImp::send, shared_from_this(), m));
-        return;
-    }
-    if (gracefulClose_)
-        return;
-    if (detaching_)
-        return;
-    if (!socket_.is_open())
-        return;
+    dispatch(strand_, [self = shared_from_this(), m]() {
+        if (self->gracefulClose_)
+            return;
+        if (self->detaching_)
+            return;
+        if (!self->socket_.is_open())
+            return;
 
-    auto validator = m->getValidatorKey();
-    if (validator && !squelch_.expireSquelch(*validator))
-    {
-        overlay_.reportOutboundTraffic(
-            TrafficCount::Category::SquelchSuppressed,
-            static_cast(m->getBuffer(compressionEnabled_).size()));
-        return;
-    }
+        auto validator = m->getValidatorKey();
+        if (validator && !self->squelch_.expireSquelch(*validator))
+        {
+            self->overlay_.reportOutboundTraffic(
+                TrafficCount::Category::SquelchSuppressed,
+                static_cast(m->getBuffer(self->compressionEnabled_).size()));
+            return;
+        }
 
-    // report categorized outgoing traffic
-    overlay_.reportOutboundTraffic(
-        safeCast(m->getCategory()),
-        static_cast(m->getBuffer(compressionEnabled_).size()));
+        // report categorized outgoing traffic
+        self->overlay_.reportOutboundTraffic(
+            safeCast(m->getCategory()),
+            static_cast(m->getBuffer(self->compressionEnabled_).size()));
 
-    // report total outgoing traffic
-    overlay_.reportOutboundTraffic(
-        TrafficCount::Category::Total, static_cast(m->getBuffer(compressionEnabled_).size()));
+        // report total outgoing traffic
+        self->overlay_.reportOutboundTraffic(
+            TrafficCount::Category::Total,
+            static_cast(m->getBuffer(self->compressionEnabled_).size()));
 
-    auto sendqSize = sendQueue_.size();
+        auto sendqSize = self->sendQueue_.size();
 
-    if (sendqSize < Tuning::kTargetSendQueue)
-    {
-        // To detect a peer that does not read from their
-        // side of the connection, we expect a peer to have
-        // a small senq periodically
-        largeSendq_ = 0;
-    }
-    else if (auto sink = journal_.debug(); sink && (sendqSize % Tuning::kSendQueueLogFreq) == 0)
-    {
-        std::string const n = name();
-        sink << n << " sendq: " << sendqSize;
-    }
+        if (sendqSize < Tuning::kTargetSendQueue)
+        {
+            // To detect a peer that does not read from their
+            // side of the connection, we expect a peer to have
+            // a small sendq periodically
+            self->largeSendq_ = 0;
+        }
+        else if (
+            auto sink = self->journal_.debug();
+            sink && (sendqSize % Tuning::kSendQueueLogFreq) == 0)
+        {
+            std::string const n = self->name();
+            sink << n << " sendq: " << sendqSize;
+        }
 
-    sendQueue_.push(m);
+        self->sendQueue_.push(m);
 
-    if (sendqSize != 0)
-        return;
+        if (sendqSize != 0)
+            return;
 
-    boost::asio::async_write(
-        stream_,
-        boost::asio::buffer(sendQueue_.front()->getBuffer(compressionEnabled_)),
-        bind_executor(
-            strand_,
-            std::bind(
-                &PeerImp::onWriteMessage,
-                shared_from_this(),
-                std::placeholders::_1,
-                std::placeholders::_2)));
+        boost::asio::async_write(
+            self->stream_,
+            boost::asio::buffer(self->sendQueue_.front()->getBuffer(self->compressionEnabled_)),
+            bind_executor(
+                self->strand_,
+                std::bind(
+                    &PeerImp::onWriteMessage, self, std::placeholders::_1, std::placeholders::_2)));
+    });
 }
 
 void
 PeerImp::sendTxQueue()
 {
-    if (!strand_.running_in_this_thread())
-    {
-        post(strand_, std::bind(&PeerImp::sendTxQueue, shared_from_this()));
-        return;
-    }
-
-    if (!txQueue_.empty())
-    {
-        protocol::TMHaveTransactions ht;
-        std::ranges::for_each(
-            txQueue_, [&](auto const& hash) { ht.add_hashes(hash.data(), hash.size()); });
-        JLOG(pJournal_.trace()) << "sendTxQueue " << txQueue_.size();
-        txQueue_.clear();
-        send(std::make_shared(ht, protocol::mtHAVE_TRANSACTIONS));
-    }
+    dispatch(strand_, [self = shared_from_this()]() {
+        if (!self->txQueue_.empty())
+        {
+            protocol::TMHaveTransactions ht;
+            std::ranges::for_each(
+                self->txQueue_, [&](auto const& hash) { ht.add_hashes(hash.data(), hash.size()); });
+            JLOG(self->pJournal_.trace()) << "sendTxQueue " << self->txQueue_.size();
+            self->txQueue_.clear();
+            self->send(std::make_shared(ht, protocol::mtHAVE_TRANSACTIONS));
+        }
+    });
 }
 
 void
 PeerImp::addTxQueue(uint256 const& hash)
 {
-    if (!strand_.running_in_this_thread())
-    {
-        post(strand_, std::bind(&PeerImp::addTxQueue, shared_from_this(), hash));
-        return;
-    }
+    dispatch(strand_, [self = shared_from_this(), hash]() {
+        if (self->txQueue_.size() == reduce_relay::kMaxTxQueueSize)
+        {
+            JLOG(self->pJournal_.warn()) << "addTxQueue exceeds the cap";
+            self->sendTxQueue();
+        }
 
-    if (txQueue_.size() == reduce_relay::kMaxTxQueueSize)
-    {
-        JLOG(pJournal_.warn()) << "addTxQueue exceeds the cap";
-        sendTxQueue();
-    }
-
-    txQueue_.insert(hash);
-    JLOG(pJournal_.trace()) << "addTxQueue " << txQueue_.size();
+        self->txQueue_.insert(hash);
+        JLOG(self->pJournal_.trace()) << "addTxQueue " << self->txQueue_.size();
+    });
 }
 
 void
 PeerImp::removeTxQueue(uint256 const& hash)
 {
-    if (!strand_.running_in_this_thread())
-    {
-        post(strand_, std::bind(&PeerImp::removeTxQueue, shared_from_this(), hash));
-        return;
-    }
-
-    auto removed = txQueue_.erase(hash);
-    JLOG(pJournal_.trace()) << "removeTxQueue " << removed;
+    dispatch(strand_, [self = shared_from_this(), hash]() {
+        auto removed = self->txQueue_.erase(hash);
+        JLOG(self->pJournal_.trace()) << "removeTxQueue " << removed;
+    });
 }
 
 void
 PeerImp::charge(Resource::Charge const& fee, std::string const& context)
 {
-    dispatch(strand_, [this, self = shared_from_this(), fee, context]() {
-        if ((usage_.charge(fee, context) == Resource::Disposition::Drop) &&
-            usage_.disconnect(pJournal_))
+    dispatch(strand_, [self = shared_from_this(), fee, context]() {
+        if ((self->usage_.charge(fee, context) == Resource::Disposition::Drop) &&
+            self->usage_.disconnect(self->pJournal_))
         {
             // Idempotent: only the first worker to observe Drop counts the
             // metric and posts fail().  Without the guard, several queued
@@ -405,11 +381,11 @@ PeerImp::charge(Resource::Charge const& fee, std::string const& context)
             // shutdowns.  fail(std::string const&) self-posts to strand_
             // when invoked off-strand.
             bool expected = false;
-            if (chargeDisconnectFired_.compare_exchange_strong(
+            if (self->chargeDisconnectFired_.compare_exchange_strong(
                     expected, true, std::memory_order_acq_rel))
             {
-                overlay_.incPeerDisconnectCharges();
-                fail("charge: Resources");
+                self->overlay_.incPeerDisconnectCharges();
+                self->fail("charge: Resources");
             }
         }
     });
@@ -640,20 +616,14 @@ PeerImp::close()
 void
 PeerImp::fail(std::string const& reason)
 {
-    if (!strand_.running_in_this_thread())
-    {
-        post(
-            strand_,
-            std::bind(
-                (void (Peer::*)(std::string const&))&PeerImp::fail, shared_from_this(), reason));
-        return;
-    }
-    if (journal_.active(beast::Severity::Warning) && socket_.is_open())
-    {
-        std::string const n = name();
-        JLOG(journal_.warn()) << n << " failed: " << reason;
-    }
-    close();
+    dispatch(strand_, [self = shared_from_this(), reason]() {
+        if (self->journal_.active(beast::Severity::Warning) && self->socket_.is_open())
+        {
+            std::string const n = self->name();
+            JLOG(self->journal_.warn()) << n << " failed: " << reason;
+        }
+        self->close();
+    });
 }
 
 void
@@ -2752,45 +2722,42 @@ PeerImp::onMessage(std::shared_ptr const& m)
 void
 PeerImp::onMessage(std::shared_ptr const& m)
 {
-    using on_message_fn = void (PeerImp::*)(std::shared_ptr const&);
-    if (!strand_.running_in_this_thread())
-    {
-        post(strand_, std::bind((on_message_fn)&PeerImp::onMessage, shared_from_this(), m));
-        return;
-    }
+    dispatch(strand_, [self = shared_from_this(), m]() {
+        if (!m->has_validatorpubkey())
+        {
+            self->fee_.update(Resource::kFeeInvalidData, "squelch no pubkey");
+            return;
+        }
+        auto validator = m->validatorpubkey();
+        auto const slice{makeSlice(validator)};
+        if (!publicKeyType(slice))
+        {
+            self->fee_.update(Resource::kFeeInvalidData, "squelch bad pubkey");
+            return;
+        }
+        PublicKey const key(slice);
 
-    if (!m->has_validatorpubkey())
-    {
-        fee_.update(Resource::kFeeInvalidData, "squelch no pubkey");
-        return;
-    }
-    auto validator = m->validatorpubkey();
-    auto const slice{makeSlice(validator)};
-    if (!publicKeyType(slice))
-    {
-        fee_.update(Resource::kFeeInvalidData, "squelch bad pubkey");
-        return;
-    }
-    PublicKey const key(slice);
+        // Ignore the squelch for validator's own messages.
+        if (key == self->app_.getValidationPublicKey())
+        {
+            JLOG(self->pJournal_.debug())
+                << "onMessage: TMSquelch discarding validator's squelch " << slice;
+            return;
+        }
 
-    // Ignore the squelch for validator's own messages.
-    if (key == app_.getValidationPublicKey())
-    {
-        JLOG(pJournal_.debug()) << "onMessage: TMSquelch discarding validator's squelch " << slice;
-        return;
-    }
+        std::uint32_t const duration = m->has_squelchduration() ? m->squelchduration() : 0;
+        if (!m->squelch())
+        {
+            self->squelch_.removeSquelch(key);
+        }
+        else if (!self->squelch_.addSquelch(key, std::chrono::seconds{duration}))
+        {
+            self->fee_.update(Resource::kFeeInvalidData, "squelch duration");
+        }
 
-    std::uint32_t const duration = m->has_squelchduration() ? m->squelchduration() : 0;
-    if (!m->squelch())
-    {
-        squelch_.removeSquelch(key);
-    }
-    else if (!squelch_.addSquelch(key, std::chrono::seconds{duration}))
-    {
-        fee_.update(Resource::kFeeInvalidData, "squelch duration");
-    }
-
-    JLOG(pJournal_.debug()) << "onMessage: TMSquelch " << slice << " " << id() << " " << duration;
+        JLOG(self->pJournal_.debug())
+            << "onMessage: TMSquelch " << slice << " " << self->id() << " " << duration;
+    });
 }
 
 //--------------------------------------------------------------------------
diff --git a/src/xrpld/overlay/detail/PeerImp.h b/src/xrpld/overlay/detail/PeerImp.h
index 26d7e0a832..28fb6b33a4 100644
--- a/src/xrpld/overlay/detail/PeerImp.h
+++ b/src/xrpld/overlay/detail/PeerImp.h
@@ -293,7 +293,7 @@ public:
     /** Send a set of PeerFinder endpoints as a protocol message. */
     template <
         class FwdIt,
-        class = typename std::enable_if_t<
+        class = std::enable_if_t<
             std::is_same_v::value_type, PeerFinder::Endpoint>>>
     void
     sendEndpoints(FwdIt first, FwdIt last);
diff --git a/src/xrpld/overlay/detail/Tuning.h b/src/xrpld/overlay/detail/Tuning.h
index 20a60d470e..8357fcd130 100644
--- a/src/xrpld/overlay/detail/Tuning.h
+++ b/src/xrpld/overlay/detail/Tuning.h
@@ -1,4 +1,5 @@
 #pragma once
+
 #include 
 
 #include 
diff --git a/src/xrpld/overlay/detail/ZeroCopyStream.h b/src/xrpld/overlay/detail/ZeroCopyStream.h
index d8d311105d..f77f266321 100644
--- a/src/xrpld/overlay/detail/ZeroCopyStream.h
+++ b/src/xrpld/overlay/detail/ZeroCopyStream.h
@@ -17,7 +17,7 @@ template 
 class ZeroCopyInputStream : public ::google::protobuf::io::ZeroCopyInputStream
 {
 private:
-    using iterator = typename Buffers::const_iterator;
+    using iterator = Buffers::const_iterator;
     using const_buffer = boost::asio::const_buffer;
 
     google::protobuf::int64 count_ = 0;
@@ -110,8 +110,8 @@ template 
 class ZeroCopyOutputStream : public ::google::protobuf::io::ZeroCopyOutputStream
 {
 private:
-    using buffers_type = typename Streambuf::mutable_buffers_type;
-    using iterator = typename buffers_type::const_iterator;
+    using buffers_type = Streambuf::mutable_buffers_type;
+    using iterator = buffers_type::const_iterator;
     using mutable_buffer = boost::asio::mutable_buffer;
 
     Streambuf& streambuf_;
diff --git a/src/xrpld/peerfinder/detail/Checker.h b/src/xrpld/peerfinder/detail/Checker.h
index 8ab084830c..2f324bf8b6 100644
--- a/src/xrpld/peerfinder/detail/Checker.h
+++ b/src/xrpld/peerfinder/detail/Checker.h
@@ -34,8 +34,8 @@ private:
     template 
     struct AsyncOp : BasicAsyncOp
     {
-        using socket_type = typename Protocol::socket;
-        using endpoint_type = typename Protocol::endpoint;
+        using socket_type = Protocol::socket;
+        using endpoint_type = Protocol::endpoint;
 
         Checker& checker;
         socket_type socket;
@@ -57,8 +57,8 @@ private:
 
     //--------------------------------------------------------------------------
 
-    using list_type = typename boost::intrusive::
-        make_list>::type;
+    using list_type =
+        boost::intrusive::make_list>::type;
 
     std::mutex mutex_;
     std::condition_variable cond_;
diff --git a/src/xrpld/peerfinder/detail/Livecache.h b/src/xrpld/peerfinder/detail/Livecache.h
index 84efcb7bd1..c5f04be90a 100644
--- a/src/xrpld/peerfinder/detail/Livecache.h
+++ b/src/xrpld/peerfinder/detail/Livecache.h
@@ -65,12 +65,12 @@ public:
         };
 
     public:
-        using iterator = boost::transform_iterator;
+        using iterator = boost::transform_iterator;
 
         using const_iterator = iterator;
 
         using reverse_iterator =
-            boost::transform_iterator;
+            boost::transform_iterator;
 
         using const_reverse_iterator = reverse_iterator;
 
@@ -132,7 +132,7 @@ public:
         }
 
     private:
-        explicit Hop(typename beast::MaybeConst::type& list) : list_(list)
+        explicit Hop(beast::MaybeConst::type& list) : list_(list)
         {
         }
 
@@ -145,7 +145,7 @@ protected:
     // Work-around to call Hop's private constructor from Livecache
     template 
     static Hop
-    makeHop(typename beast::MaybeConst::type& list)
+    makeHop(beast::MaybeConst::type& list)
     {
         return Hop(list);
     }
@@ -208,30 +208,29 @@ public:
         template 
         struct Transform
         {
-            using first_argument = typename lists_type::value_type;
+            using first_argument = lists_type::value_type;
             using result_type = Hop;
 
             explicit Transform() = default;
 
             Hop
-            operator()(typename beast::MaybeConst::type&
-                           list) const
+            operator()(beast::MaybeConst::type& list) const
             {
                 return makeHop(list);
             }
         };
 
     public:
-        using iterator = boost::transform_iterator, typename lists_type::iterator>;
+        using iterator = boost::transform_iterator, lists_type::iterator>;
 
         using const_iterator =
-            boost::transform_iterator, typename lists_type::const_iterator>;
+            boost::transform_iterator, lists_type::const_iterator>;
 
         using reverse_iterator =
-            boost::transform_iterator, typename lists_type::reverse_iterator>;
+            boost::transform_iterator, lists_type::reverse_iterator>;
 
         using const_reverse_iterator =
-            boost::transform_iterator, typename lists_type::const_reverse_iterator>;
+            boost::transform_iterator, lists_type::const_reverse_iterator>;
 
         iterator
         begin()
@@ -338,7 +337,7 @@ public:
     }
 
     /** Returns the number of entries in the cache. */
-    typename cache_type::size_type
+    cache_type::size_type
     size() const
     {
         return cache_.size();
diff --git a/src/xrpld/peerfinder/detail/Logic.h b/src/xrpld/peerfinder/detail/Logic.h
index 815858cf00..55cce506ba 100644
--- a/src/xrpld/peerfinder/detail/Logic.h
+++ b/src/xrpld/peerfinder/detail/Logic.h
@@ -980,7 +980,7 @@ public:
     /** Adds eligible Fixed addresses for outbound attempts. */
     template 
     void
-    getFixed(std::size_t needed, Container& c, typename ConnectHandouts::Squelches& squelches)
+    getFixed(std::size_t needed, Container& c, ConnectHandouts::Squelches& squelches)
     {
         auto const now(clock.now());
         for (auto iter = fixed_.begin(); needed && iter != fixed_.end(); ++iter)
diff --git a/src/xrpld/rpc/detail/PathRequestManager.h b/src/xrpld/rpc/detail/PathRequestManager.h
index 5a5cfde402..c8e272a97d 100644
--- a/src/xrpld/rpc/detail/PathRequestManager.h
+++ b/src/xrpld/rpc/detail/PathRequestManager.h
@@ -3,7 +3,6 @@
 #include 
 #include 
 #include 
-#include 
 
 #include 
 #include 
diff --git a/src/xrpld/rpc/detail/Pathfinder.cpp b/src/xrpld/rpc/detail/Pathfinder.cpp
index 25da86ef8f..e1a2a4acf6 100644
--- a/src/xrpld/rpc/detail/Pathfinder.cpp
+++ b/src/xrpld/rpc/detail/Pathfinder.cpp
@@ -3,7 +3,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 
 #include 
diff --git a/src/xrpld/rpc/detail/RPCCall.cpp b/src/xrpld/rpc/detail/RPCCall.cpp
index f405ffe4de..123b9fc7a5 100644
--- a/src/xrpld/rpc/detail/RPCCall.cpp
+++ b/src/xrpld/rpc/detail/RPCCall.cpp
@@ -1589,7 +1589,7 @@ struct RPCCallImp
 
             jvResult["result"] = jvReply;
 
-            (callbackFuncP)(jvResult);
+            callbackFuncP(jvResult);
         }
 
         return false;
diff --git a/src/xrpld/rpc/detail/RippleLineCache.cpp b/src/xrpld/rpc/detail/RippleLineCache.cpp
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/src/xrpld/rpc/detail/RippleLineCache.h b/src/xrpld/rpc/detail/RippleLineCache.h
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/src/xrpld/rpc/handlers/admin/keygen/WalletPropose.cpp b/src/xrpld/rpc/handlers/admin/keygen/WalletPropose.cpp
index c783319049..4b5f1821e3 100644
--- a/src/xrpld/rpc/handlers/admin/keygen/WalletPropose.cpp
+++ b/src/xrpld/rpc/handlers/admin/keygen/WalletPropose.cpp
@@ -39,7 +39,7 @@ estimateEntropy(std::string const& input)
     {
         (void)_;
         auto x = f / input.length();
-        se += (x)*log2(x);
+        se += x * log2(x);
     }
 
     // We multiply it by the length, to get an estimate of
diff --git a/src/xrpld/rpc/handlers/ledger/LedgerEntryHelpers.h b/src/xrpld/rpc/handlers/ledger/LedgerEntryHelpers.h
index 463547a90d..11f6553dfa 100644
--- a/src/xrpld/rpc/handlers/ledger/LedgerEntryHelpers.h
+++ b/src/xrpld/rpc/handlers/ledger/LedgerEntryHelpers.h
@@ -1,3 +1,5 @@
+#pragma once
+
 #include 
 
 #include 
diff --git a/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp b/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp
index 2c2f96b0e8..043fdd2d7b 100644
--- a/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp
+++ b/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp
@@ -120,7 +120,10 @@ doAMMInfo(RPC::JsonContext& context)
 
         if (params.isMember(jss::amm_account))
         {
-            auto const id = parseBase58((params[jss::amm_account].asString()));
+            auto const& ammAccount = params[jss::amm_account];
+            if (!ammAccount.isString())
+                return std::unexpected(RpcActMalformed);
+            auto const id = parseBase58(ammAccount.asString());
             if (!id)
                 return std::unexpected(RpcActMalformed);
             auto const sle = ledger->read(keylet::account(*id));
@@ -133,7 +136,10 @@ doAMMInfo(RPC::JsonContext& context)
 
         if (params.isMember(jss::account))
         {
-            accountID = parseBase58(params[jss::account].asString());
+            auto const& localAccount = params[jss::account];
+            if (!localAccount.isString())
+                return std::unexpected(RpcActMalformed);
+            accountID = parseBase58(localAccount.asString());
             if (!accountID || !ledger->read(keylet::account(*accountID)))
                 return std::unexpected(RpcActMalformed);
         }
diff --git a/src/xrpld/rpc/json_body.h b/src/xrpld/rpc/json_body.h
index 4520a8bf39..5c4c56cef8 100644
--- a/src/xrpld/rpc/json_body.h
+++ b/src/xrpld/rpc/json_body.h
@@ -22,7 +22,7 @@ struct JsonBody
         dynamic_buffer_type buffer_;
 
     public:
-        using const_buffers_type = typename dynamic_buffer_type::const_buffers_type;
+        using const_buffers_type = dynamic_buffer_type::const_buffers_type;
 
         using is_deferred = std::false_type;