Compare commits

..

5 Commits

Author SHA1 Message Date
Ed Hennis
d62ad9a8e7 Merge branch 'develop' into ximinez/fix-getkeys 2026-06-23 11:10:09 -04:00
Ed Hennis
d7e7baa675 Merge branch 'develop' into ximinez/fix-getkeys 2026-06-22 16:42:07 -04:00
Ed Hennis
054284701e Apply suggestion from @xrplf-ai-reviewer[bot]
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
2026-06-17 15:20:48 -04:00
Ed Hennis
eb4681da51 Merge branch 'develop' into ximinez/fix-getkeys 2026-06-17 15:19:55 -04:00
Ed Hennis
9b3dd7002d fix: Allocate TaggedCache::getKeys() memory outside of lock
- Uses a loop in case the size grows while the lock is free. Guarantees
  the result vector will not need to allocate under lock.
2026-06-17 13:06:40 -04:00
51 changed files with 77 additions and 635 deletions

View File

@@ -96,6 +96,3 @@ function(verbose_find_path variable name)
${ARGN} ${ARGN}
) )
endfunction() endfunction()
function(patch_nix_binary target)
endfunction()

View File

@@ -1,5 +1,5 @@
{ {
"image_tag": "sha-e29b523", "image_tag": "sha-fe4c8ae",
"configs": { "configs": {
"ubuntu": [ "ubuntu": [
{ {

View File

@@ -1,6 +1,6 @@
{ {
"platform": "windows/amd64", "platform": "windows/amd64",
"runner": ["self-hosted", "Windows", "dev-box-windows-2026"], "runner": ["self-hosted", "Windows", "devbox"],
"configs": [ "configs": [
{ "build_type": "Release" }, { "build_type": "Release" },
{ {

View File

@@ -70,7 +70,6 @@ jobs:
.github/workflows/reusable-upload-recipe.yml .github/workflows/reusable-upload-recipe.yml
.clang-tidy .clang-tidy
.codecov.yml .codecov.yml
bin/check-tools.sh
cfg/** cfg/**
cmake/** cmake/**
conan/** conan/**

View File

@@ -27,7 +27,6 @@ on:
- ".github/workflows/reusable-upload-recipe.yml" - ".github/workflows/reusable-upload-recipe.yml"
- ".clang-tidy" - ".clang-tidy"
- ".codecov.yml" - ".codecov.yml"
- "bin/check-tools.sh"
- "cfg/**" - "cfg/**"
- "cmake/**" - "cmake/**"
- "conan/**" - "conan/**"

View File

@@ -41,7 +41,7 @@ env:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-e29b523 container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-fe4c8ae
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

View File

@@ -82,7 +82,7 @@ jobs:
name: ${{ inputs.config_name }} name: ${{ inputs.config_name }}
runs-on: ${{ fromJSON(inputs.runs_on) }} runs-on: ${{ fromJSON(inputs.runs_on) }}
container: ${{ inputs.image != '' && inputs.image || null }} container: ${{ inputs.image != '' && inputs.image || null }}
timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 180 }} timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 90 }}
env: env:
# Use a namespace to keep the objects separate for each configuration. # Use a namespace to keep the objects separate for each configuration.
CCACHE_NAMESPACE: ${{ inputs.config_name }} CCACHE_NAMESPACE: ${{ inputs.config_name }}
@@ -163,7 +163,7 @@ jobs:
CMAKE_ARGS: ${{ inputs.cmake_args }} CMAKE_ARGS: ${{ inputs.cmake_args }}
run: | run: |
cmake \ cmake \
-G '${{ runner.os == 'Windows' && 'Visual Studio 18 2026' || 'Ninja' }}' \ -G '${{ runner.os == 'Windows' && 'Visual Studio 17 2022' || 'Ninja' }}' \
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \
-DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \
${CMAKE_ARGS} \ ${CMAKE_ARGS} \
@@ -229,6 +229,21 @@ jobs:
--parallel "${BUILD_NPROC}" \ --parallel "${BUILD_NPROC}" \
--target "${CMAKE_TARGET}" --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 - name: Show ccache statistics
if: ${{ inputs.ccache_enabled }} if: ${{ inputs.ccache_enabled }}
run: | run: |

View File

@@ -39,7 +39,7 @@ jobs:
needs: [determine-files] 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') }} 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"] runs-on: ["self-hosted", "Linux", "X64", "heavy"]
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-e29b523" container: "ghcr.io/xrplf/xrpld/nix-debian:sha-fe4c8ae"
permissions: permissions:
contents: read contents: read
issues: write issues: write

View File

@@ -40,7 +40,7 @@ defaults:
jobs: jobs:
upload: upload:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-e29b523 container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-fe4c8ae
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

View File

@@ -15,7 +15,6 @@ repos:
hooks: hooks:
- id: check-added-large-files - id: check-added-large-files
args: [--maxkb=400, --enforce-all] args: [--maxkb=400, --enforce-all]
- id: check-executables-have-shebangs
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
- id: check-merge-conflict - id: check-merge-conflict
@@ -36,18 +35,13 @@ repos:
language: python language: python
types_or: [c++, c] types_or: [c++, c]
exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/ 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 - repo: https://github.com/pre-commit/mirrors-clang-format
rev: dd18dad857d6133e90bbe478f4f2f22ec0030269 # frozen: v22.1.5 rev: dd18dad857d6133e90bbe478f4f2f22ec0030269 # frozen: v22.1.5
hooks: hooks:
- id: clang-format - id: clang-format
args: [--style=file] args: [--style=file]
types_or: [c++, c, proto] "types_or": [c++, c, proto]
exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/ exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/
- repo: https://github.com/BlankSpruce/gersemi-pre-commit - repo: https://github.com/BlankSpruce/gersemi-pre-commit

View File

@@ -57,8 +57,6 @@ if(target)
) )
endif() endif()
include(PatchNixBinary)
include(XrplSanity) include(XrplSanity)
include(XrplVersion) include(XrplVersion)
include(XrplSettings) include(XrplSettings)

View File

@@ -90,19 +90,16 @@ if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
check perl check perl
check pkg-config check pkg-config
check vim check vim
check zip
# These tools are present in our Linux CI images and in local development # 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 # setups, but not in the macOS CI environment. So check them everywhere
# except when running in CI on macOS. # except when running in CI on macOS.
if [ "${os}" = "linux" ] || [ -z "${CI:-}" ]; then if [ "${os}" = "linux" ] || [ -z "${CI:-}" ]; then
check clang-format check clang-format
check dot
check doxygen check doxygen
check gcovr check gcovr
check gh check gh
check git-cliff check git-cliff
check git-lfs
check gpg check gpg
# pre-commit, or its alternative implementation prek # pre-commit, or its alternative implementation prek
check pre-commit sh -c 'pre-commit --version || prek --version' check pre-commit sh -c 'pre-commit --version || prek --version'

View File

@@ -1,34 +0,0 @@
#!/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 <file1> <file2> ...
"""
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())

View File

@@ -56,16 +56,3 @@ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64")
else() else()
message(FATAL_ERROR "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}") message(FATAL_ERROR "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}")
endif() 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()

View File

@@ -1,53 +0,0 @@
#[===================================================================[
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 "$<TARGET_FILE:${target}>"
COMMENT "Patching ${target}: set default loader, remove rpath"
VERBATIM
)
endfunction()

View File

@@ -154,15 +154,6 @@ 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. # Keep -stdlib=libstdc++ off the compile commands, but preserve it for linking.
# #
# Conan turns `compiler.libcxx=libstdc++` into `-stdlib=libstdc++` and puts it in # Conan turns `compiler.libcxx=libstdc++` into `-stdlib=libstdc++` and puts it in

View File

@@ -247,7 +247,6 @@ target_link_modules(
if(xrpld) if(xrpld)
add_executable(xrpld) add_executable(xrpld)
patch_nix_binary(xrpld)
if(tests) if(tests)
target_compile_definitions(xrpld PUBLIC ENABLE_TESTS) target_compile_definitions(xrpld PUBLIC ENABLE_TESTS)
target_compile_definitions( target_compile_definitions(

View File

@@ -14,9 +14,11 @@
include_guard(GLOBAL) include_guard(GLOBAL)
include(CompilationEnv) include(CompilationEnv)
if(NOT SANITIZERS_ENABLED) if(NOT DEFINED SANITIZERS)
set(SANITIZERS_ENABLED FALSE)
return() return()
endif() endif()
set(SANITIZERS_ENABLED TRUE)
message(STATUS "=== Configuring Sanitizers ===") message(STATUS "=== Configuring Sanitizers ===")
message(STATUS " SANITIZERS: ${SANITIZERS}") message(STATUS " SANITIZERS: ${SANITIZERS}")

View File

@@ -1,9 +1,9 @@
{ {
"version": "0.5", "version": "0.5",
"requires": [ "requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056", "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987", "xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1778091117.311", "sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1776096494.149",
"soci/4.0.3#fe32b9ad5eb47e79ab9e45a68f363945%1774450067.231", "soci/4.0.3#fe32b9ad5eb47e79ab9e45a68f363945%1774450067.231",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878", "snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
"secp256k1/0.7.1#481881709eb0bdd0185a12b912bbe8ad%1770910500.329", "secp256k1/0.7.1#481881709eb0bdd0185a12b912bbe8ad%1770910500.329",
@@ -15,19 +15,19 @@
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914", "lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492", "libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03", "libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1778091117.848", "libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1776147552.838",
"jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228", "jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152", "gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152",
"grpc/1.81.0#2fb144aeb47e7f35c6ebb0e5f35bed31%1781620605.685", "grpc/1.78.1#b1a9e74b145cc471bed4dc64dc6eb2c1%1774467387.342",
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772", "ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772",
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772", "date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1774439234.681", "c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1774439234.681",
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837", "bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778091165.282", "boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778050991.9",
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196" "abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
], ],
"build_requires": [ "build_requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056", "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503",
"strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1774447376.964", "strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1774447376.964",
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12", "protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1765850144.707", "nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1765850144.707",

View File

@@ -28,7 +28,7 @@ class Xrpl(ConanFile):
requires = [ requires = [
"ed25519/2015.03", "ed25519/2015.03",
"grpc/1.81.0", "grpc/1.78.1",
"libarchive/3.8.7", "libarchive/3.8.7",
"nudb/2.0.9", "nudb/2.0.9",
"openssl/3.6.2", "openssl/3.6.2",

View File

@@ -9,7 +9,6 @@
#include <xrpl/beast/insight/Insight.h> #include <xrpl/beast/insight/Insight.h>
#include <atomic> #include <atomic>
#include <cstddef>
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <thread> #include <thread>
@@ -18,22 +17,6 @@
namespace xrpl { 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. /** Map/cache combination.
This class implements a cache and a map. The cache keeps objects alive 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 in the map. The map allows multiple code paths that reference objects
@@ -113,32 +96,6 @@ public:
bool bool
del(key_type const& key, bool valid); 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 <typename Policy>
using CanonicalizeClientPointerType = std::conditional_t<
std::is_same_v<detail::ReplaceCached, Policy>,
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 <class Policy, class Callback = std::nullptr_t>
bool
canonicalizeImpl(
key_type const& key,
CanonicalizeClientPointerType<Policy> data,
Policy policy,
Callback&& replaceCallback = nullptr);
public: public:
/** Replace aliased objects with originals. /** Replace aliased objects with originals.
@@ -147,52 +104,19 @@ public:
This routine eliminates the duplicate and performs a replacement This routine eliminates the duplicate and performs a replacement
on the callers shared pointer if needed. 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 key The key corresponding to the object
@param data A shared pointer to the data corresponding to the object. @param data A shared pointer to the data corresponding to the object.
@param replaceCallback A callable (existing strong pointer -> bool). @param replace Function that decides if cache should be replaced
@return `true` if an existing live entry was found and used; `false` if a new entry was @return `true` If the key already existed.
inserted or an expired tracked entry was re-cached. */
**/ template <class R>
template <class Callback>
bool bool
canonicalize(key_type const& key, SharedPointerType& data, Callback&& replaceCallback); canonicalize(key_type const& key, SharedPointerType& data, R&& 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 bool
canonicalizeReplaceCache(key_type const& key, SharedPointerType const& data); 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 bool
canonicalizeReplaceClient(key_type const& key, SharedPointerType& data); canonicalizeReplaceClient(key_type const& key, SharedPointerType& data);

View File

@@ -2,33 +2,10 @@
#include <xrpl/basics/IntrusivePointer.ipp> #include <xrpl/basics/IntrusivePointer.ipp>
#include <xrpl/basics/TaggedCache.h> #include <xrpl/basics/TaggedCache.h>
#include <xrpl/basics/scope.h>
namespace xrpl { 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 < template <
class Key, class Key,
class T, class T,
@@ -324,29 +301,13 @@ template <
class Hash, class Hash,
class KeyEqual, class KeyEqual,
class Mutex> class Mutex>
template <class Policy, class Callback> template <class R>
inline bool inline bool
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>:: TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
canonicalizeImpl( canonicalize(key_type const& key, SharedPointerType& data, R&& replaceCallback)
key_type const& key,
CanonicalizeClientPointerType<Policy> data,
[[maybe_unused]] Policy policy,
[[maybe_unused]] Callback&& replaceCallback)
{ {
// Return canonical value, store if needed, refresh in cache // Return canonical value, store if needed, refresh in cache
// Return values: true=we had the data already // 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<Policy, detail::ReplaceCached>;
std::scoped_lock const lock(mutex_); std::scoped_lock const lock(mutex_);
auto cit = cache_.find(key); auto cit = cache_.find(key);
@@ -364,14 +325,13 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
Entry& entry = cit->second; Entry& entry = cit->second;
entry.touch(clock_.now()); entry.touch(clock_.now());
auto shouldReplaceCached = [&] { auto shouldReplace = [&] {
if constexpr (replaceCached) if constexpr (std::is_invocable_r_v<bool, R>)
{ {
return true; // The reason for this extra complexity is for intrusive
} // strong/weak combo getting a strong is relatively expensive
else if constexpr (std::is_same_v<Policy, detail::ReplaceClient>) // and not needed for many cases.
{ return replaceCallback();
return false;
} }
else else
{ {
@@ -381,11 +341,11 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
if (entry.isCached()) if (entry.isCached())
{ {
if (shouldReplaceCached()) if (shouldReplace())
{ {
entry.ptr = data; entry.ptr = data;
} }
else if constexpr (!replaceCached) else
{ {
data = entry.ptr.getStrong(); data = entry.ptr.getStrong();
} }
@@ -397,11 +357,11 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
if (cachedData) if (cachedData)
{ {
if (shouldReplaceCached()) if (shouldReplace())
{ {
entry.ptr = data; entry.ptr = data;
} }
else if constexpr (!replaceCached) else
{ {
entry.ptr.convertToStrong(); entry.ptr.convertToStrong();
data = cachedData; data = cachedData;
@@ -417,24 +377,6 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
return false; return false;
} }
template <
class Key,
class T,
bool IsKeyCache,
class SharedWeakUnionPointer,
class SharedPointerType,
class Hash,
class KeyEqual,
class Mutex>
template <class Callback>
inline bool
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
canonicalize(key_type const& key, SharedPointerType& data, Callback&& replaceCallback)
{
return canonicalizeImpl(
key, data, detail::ReplaceDynamically{}, std::forward<Callback>(replaceCallback));
}
template < template <
class Key, class Key,
class T, class T,
@@ -448,7 +390,7 @@ inline bool
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>:: TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
canonicalizeReplaceCache(key_type const& key, SharedPointerType const& data) canonicalizeReplaceCache(key_type const& key, SharedPointerType const& data)
{ {
return canonicalizeImpl(key, data, detail::ReplaceCached{}); return canonicalize(key, const_cast<SharedPointerType&>(data), []() { return true; });
} }
template < template <
@@ -464,7 +406,7 @@ inline bool
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>:: TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
canonicalizeReplaceClient(key_type const& key, SharedPointerType& data) canonicalizeReplaceClient(key_type const& key, SharedPointerType& data)
{ {
return canonicalizeImpl(key, data, detail::ReplaceClient{}); return canonicalize(key, data, []() { return false; });
} }
template < template <
@@ -595,8 +537,15 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
std::vector<key_type> v; std::vector<key_type> v;
{ {
std::scoped_lock const lock(mutex_); std::unique_lock lock(mutex_);
v.reserve(cache_.size()); for (auto size = cache_.size(); v.capacity() < size; size = cache_.size())
{
ScopeUnlock const unlock(lock);
v.reserve(size);
}
XRPL_ASSERT(lock.owns_lock(), "xrpl::TaggedCache::getKeys(): owns lock");
XRPL_ASSERT(
v.capacity() >= cache_.size(), "xrpl::TaggedCache::getKeys(): sufficient capacity");
for (auto const& _ : cache_) for (auto const& _ : cache_)
v.push_back(_.first); v.push_back(_.first);
} }

View File

@@ -1,5 +1,4 @@
#pragma once #pragma once
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
namespace xrpl::permissioned_dex { namespace xrpl::permissioned_dex {

View File

@@ -1,5 +1,3 @@
#pragma once
#include <xrpl/protocol/HashPrefix.h> #include <xrpl/protocol/HashPrefix.h>
#include <xrpl/protocol/STVector256.h> #include <xrpl/protocol/STVector256.h>
#include <xrpl/protocol/Serializer.h> #include <xrpl/protocol/Serializer.h>

View File

@@ -3,6 +3,7 @@
#include <xrpl/ledger/PaymentSandbox.h> #include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/protocol/IOUAmount.h> #include <xrpl/protocol/IOUAmount.h>
#include <xrpl/protocol/XRPAmount.h> #include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/paths/detail/AmountSpec.h>
#include <boost/container/flat_map.hpp> #include <boost/container/flat_map.hpp>

View File

@@ -9,6 +9,7 @@
#include <xrpl/protocol/IOUAmount.h> #include <xrpl/protocol/IOUAmount.h>
#include <xrpl/protocol/XRPAmount.h> #include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/paths/Flow.h> #include <xrpl/tx/paths/Flow.h>
#include <xrpl/tx/paths/detail/AmountSpec.h>
#include <xrpl/tx/paths/detail/FlatSets.h> #include <xrpl/tx/paths/detail/FlatSets.h>
#include <xrpl/tx/paths/detail/FlowDebugInfo.h> #include <xrpl/tx/paths/detail/FlowDebugInfo.h>
#include <xrpl/tx/paths/detail/Steps.h> #include <xrpl/tx/paths/detail/Steps.h>

View File

@@ -905,11 +905,6 @@ BookStep<TIn, TOut, TDerived>::getAMMOffer(
ReadView const& view, ReadView const& view,
std::optional<Quality> const& clobQuality) const std::optional<Quality> 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_) if (ammLiquidity_)
return ammLiquidity_->getOffer(view, clobQuality); return ammLiquidity_->getOffer(view, clobQuality);
return std::nullopt; return std::nullopt;

View File

@@ -10,6 +10,7 @@
#include <xrpl/protocol/STAmount.h> #include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STPathSet.h> #include <xrpl/protocol/STPathSet.h>
#include <xrpl/tx/paths/RippleCalc.h> #include <xrpl/tx/paths/RippleCalc.h>
#include <xrpl/tx/paths/detail/AmountSpec.h>
#include <xrpl/tx/paths/detail/Steps.h> #include <xrpl/tx/paths/detail/Steps.h>
#include <xrpl/tx/paths/detail/StrandFlow.h> #include <xrpl/tx/paths/detail/StrandFlow.h>
#include <xrpl/tx/transactors/dex/AMMContext.h> #include <xrpl/tx/transactors/dex/AMMContext.h>

View File

@@ -16,6 +16,7 @@
#include <xrpl/protocol/TER.h> #include <xrpl/protocol/TER.h>
#include <xrpl/protocol/UintTypes.h> #include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/XRPAmount.h> #include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/paths/detail/AmountSpec.h>
#include <xrpl/tx/paths/detail/EitherAmount.h> #include <xrpl/tx/paths/detail/EitherAmount.h>
#include <xrpl/tx/paths/detail/StepChecks.h> #include <xrpl/tx/paths/detail/StepChecks.h>
#include <xrpl/tx/paths/detail/Steps.h> #include <xrpl/tx/paths/detail/Steps.h>

View File

@@ -52,13 +52,9 @@ DelegateSet::preclaim(PreclaimContext const& ctx)
if (!ctx.view.exists(keylet::account(ctx.tx[sfAccount]))) if (!ctx.view.exists(keylet::account(ctx.tx[sfAccount])))
return terNO_ACCOUNT; // LCOV_EXCL_LINE return terNO_ACCOUNT; // LCOV_EXCL_LINE
auto const sleAuthorize = ctx.view.read(keylet::account(ctx.tx[sfAuthorize])); if (!ctx.view.exists(keylet::account(ctx.tx[sfAuthorize])))
if (!sleAuthorize)
return tecNO_TARGET; return tecNO_TARGET;
if (isPseudoAccount(sleAuthorize))
return tecNO_PERMISSION;
// Deleting the delegate object is invalid if it doesnt exist. // Deleting the delegate object is invalid if it doesnt exist.
if (ctx.tx.getFieldArray(sfPermissions).empty() && if (ctx.tx.getFieldArray(sfPermissions).empty() &&
!ctx.view.exists(keylet::delegate(ctx.tx[sfAccount], ctx.tx[sfAuthorize]))) !ctx.view.exists(keylet::delegate(ctx.tx[sfAccount], ctx.tx[sfAuthorize])))

View File

@@ -235,19 +235,6 @@ class Delegate_test : public beast::unit_test::Suite
env(delegate::set(gw, Account("unknown"), {"Payment"}), Ter(tecNO_TARGET)); 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 // non-delegable transaction
{ {
env(delegate::set(gw, alice, {"SetRegularKey"}), Ter(temMALFORMED)); env(delegate::set(gw, alice, {"SetRegularKey"}), Ter(temMALFORMED));

View File

@@ -993,83 +993,6 @@ class PermissionedDEX_test : public beast::unit_test::Suite
BEAST_EXPECT(usd == USD(45)); 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 void
testHybridOfferCreate(FeatureBitset features) testHybridOfferCreate(FeatureBitset features)
{ {
@@ -2020,8 +1943,6 @@ public:
testOfferTokenIssuerInDomain(all); testOfferTokenIssuerInDomain(all);
testRemoveUnfundedOffer(all); testRemoveUnfundedOffer(all);
testAmmNotUsed(all); testAmmNotUsed(all);
testAmmQualityNotLeaked(all);
testAmmQualityNotLeaked(all - fixCleanup3_3_0);
testAutoBridge(all); testAutoBridge(all);
// Test hybrid offers // Test hybrid offers

View File

@@ -8,21 +8,18 @@
#include <test/jtx/pay.h> #include <test/jtx/pay.h>
#include <test/jtx/permissioned_domains.h> #include <test/jtx/permissioned_domains.h>
#include <test/jtx/ter.h> #include <test/jtx/ter.h>
#include <test/jtx/ticket.h>
#include <test/jtx/txflags.h> #include <test/jtx/txflags.h>
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/beast/unit_test/suite.h> #include <xrpl/beast/unit_test/suite.h>
#include <xrpl/json/json_value.h> #include <xrpl/json/json_value.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Protocol.h> #include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/TER.h> #include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h> #include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/jss.h> #include <xrpl/protocol/jss.h>
#include <cstddef> #include <cstddef>
#include <cstdint>
#include <exception> #include <exception>
#include <map> #include <map>
#include <optional> #include <optional>
@@ -529,47 +526,6 @@ class PermissionedDomains_test : public beast::unit_test::Suite
BEAST_EXPECT(env.ownerCount(alice) == 1); 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: public:
void void
run() override run() override
@@ -584,8 +540,6 @@ public:
testDelete(withFix_); testDelete(withFix_);
testAccountReserve(withFeature_); testAccountReserve(withFeature_);
testAccountReserve(withFix_); testAccountReserve(withFix_);
testTicket(withFeature_);
testTicket(withFix_);
} }
}; };

View File

@@ -1,7 +1,5 @@
#include <test/unit_test/SuiteJournal.h> #include <test/unit_test/SuiteJournal.h>
#include <xrpl/basics/IntrusivePointer.h>
#include <xrpl/basics/IntrusiveRefCounts.h>
#include <xrpl/basics/TaggedCache.h> #include <xrpl/basics/TaggedCache.h>
#include <xrpl/basics/TaggedCache.ipp> // IWYU pragma: keep #include <xrpl/basics/TaggedCache.ipp> // IWYU pragma: keep
#include <xrpl/basics/chrono.h> #include <xrpl/basics/chrono.h>
@@ -10,7 +8,6 @@
#include <xrpl/protocol/Protocol.h> #include <xrpl/protocol/Protocol.h>
#include <memory> #include <memory>
#include <utility>
namespace xrpl { namespace xrpl {
@@ -136,113 +133,6 @@ public:
BEAST_EXPECT(c.getCacheSize() == 0); BEAST_EXPECT(c.getCacheSize() == 0);
BEAST_EXPECT(c.getTrackSize() == 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<std::string>("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<MyRefCountObject>,
intr_ptr::SharedPtr<MyRefCountObject>>;
IntrPtrCache intrPtrCache("IntrPtrTest", 1, 1s, clock, journal);
intrPtrCache.canonicalizeReplaceCache(1, intr_ptr::makeShared<MyRefCountObject>("one"));
BEAST_EXPECT(intrPtrCache.getCacheSize() == 1);
BEAST_EXPECT(intrPtrCache.size() == 1);
{
{
intrPtrCache.canonicalizeReplaceCache(
1, intr_ptr::makeShared<MyRefCountObject>("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<MyRefCountObject>("one_replaced_2"));
auto p2 = intrPtrCache.fetch(1);
BEAST_EXPECT(*p2 == "one_replaced_2");
intrPtrCache.del(1, true);
}
intrPtrCache.canonicalizeReplaceCache(
1, intr_ptr::makeShared<MyRefCountObject>("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);
}
} }
}; };

View File

@@ -1,5 +1,3 @@
#pragma once
#include <xrpl/basics/random.h> #include <xrpl/basics/random.h>
#include <xrpl/beast/net/IPEndpoint.h> #include <xrpl/beast/net/IPEndpoint.h>

View File

@@ -1,5 +1,3 @@
#pragma once
#include <test/csf/BasicNetwork.h> #include <test/csf/BasicNetwork.h>
#include <test/csf/Digraph.h> #include <test/csf/Digraph.h>
#include <test/csf/Histogram.h> #include <test/csf/Histogram.h>

View File

@@ -174,22 +174,12 @@ public:
ammRpcInfo( ammRpcInfo(
std::optional<AccountID> const& account = std::nullopt, std::optional<AccountID> const& account = std::nullopt,
std::optional<std::string> const& ledgerIndex = std::nullopt, std::optional<std::string> const& ledgerIndex = std::nullopt,
std::optional<Asset> const& asset1 = std::nullopt, std::optional<Asset> asset1 = std::nullopt,
std::optional<Asset> const& asset2 = std::nullopt, std::optional<Asset> asset2 = std::nullopt,
std::optional<AccountID> const& ammAccount = std::nullopt, std::optional<AccountID> const& ammAccount = std::nullopt,
bool ignoreParams = false, bool ignoreParams = false,
unsigned apiVersion = RPC::kApiInvalidVersion) const; unsigned apiVersion = RPC::kApiInvalidVersion) const;
[[nodiscard]] json::Value
ammRpcInfo(
std::optional<json::Value> const& account,
std::optional<std::string> const& ledgerIndex,
std::optional<Asset> const& asset1,
std::optional<Asset> const& asset2,
std::optional<json::Value> const& ammAccount,
bool ignoreParams,
unsigned apiVersion) const;
/** Verify the AMM balances. /** Verify the AMM balances.
*/ */
[[nodiscard]] bool [[nodiscard]] bool

View File

@@ -183,37 +183,15 @@ json::Value
AMM::ammRpcInfo( AMM::ammRpcInfo(
std::optional<AccountID> const& account, std::optional<AccountID> const& account,
std::optional<std::string> const& ledgerIndex, std::optional<std::string> const& ledgerIndex,
std::optional<Asset> const& asset1, std::optional<Asset> asset1,
std::optional<Asset> const& asset2, std::optional<Asset> asset2,
std::optional<AccountID> const& ammAccount, std::optional<AccountID> const& ammAccount,
bool ignoreParams, bool ignoreParams,
unsigned apiVersion) const 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<json::Value> const& account,
std::optional<std::string> const& ledgerIndex,
std::optional<Asset> const& asset1,
std::optional<Asset> const& asset2,
std::optional<json::Value> const& ammAccount,
bool ignoreParams,
unsigned apiVersion) const
{ {
json::Value jv; json::Value jv;
if (account) if (account)
jv[jss::account] = *account; jv[jss::account] = to_string(*account);
if (ledgerIndex) if (ledgerIndex)
jv[jss::ledger_index] = *ledgerIndex; jv[jss::ledger_index] = *ledgerIndex;
if (!ignoreParams) if (!ignoreParams)
@@ -231,7 +209,7 @@ AMM::ammRpcInfo(
jv[jss::asset2] = STIssue(sfAsset2, asset2_.asset()).getJson(JsonOptions::Values::None); jv[jss::asset2] = STIssue(sfAsset2, asset2_.asset()).getJson(JsonOptions::Values::None);
} }
if (ammAccount) if (ammAccount)
jv[jss::amm_account] = *ammAccount; jv[jss::amm_account] = to_string(*ammAccount);
} }
auto jr = auto jr =
(apiVersion == RPC::kApiInvalidVersion (apiVersion == RPC::kApiInvalidVersion

View File

@@ -65,26 +65,6 @@ public:
BEAST_EXPECT(jv[jss::error_message] == "Account malformed."); 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::tuple<std::optional<Issue>, std::optional<Issue>, TestAccount, bool>> const std::vector<std::tuple<std::optional<Issue>, std::optional<Issue>, TestAccount, bool>> const
invalidParams = { invalidParams = {
{xrpIssue(), std::nullopt, TestAccount::None, false}, {xrpIssue(), std::nullopt, TestAccount::None, false},

View File

@@ -1,5 +1,3 @@
#pragma once
#include <xrpl/protocol/SecretKey.h> #include <xrpl/protocol/SecretKey.h>
#include <cstring> #include <cstring>

View File

@@ -13,7 +13,6 @@ add_executable(
helpers/TestSink.cpp helpers/TestSink.cpp
helpers/TxTest.cpp helpers/TxTest.cpp
) )
patch_nix_binary(xrpl_tests)
set_target_properties( set_target_properties(
xrpl_tests xrpl_tests
PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"

View File

View File

@@ -1,5 +1,4 @@
#pragma once #pragma once
#include <xrpl/shamap/SHAMapInnerNode.h> #include <xrpl/shamap/SHAMapInnerNode.h>
#include <cstddef> #include <cstddef>

View File

@@ -3,6 +3,7 @@
#include <xrpld/app/main/Application.h> #include <xrpld/app/main/Application.h>
#include <xrpld/rpc/detail/AssetCache.h> #include <xrpld/rpc/detail/AssetCache.h>
#include <xrpld/rpc/detail/PathRequest.h> #include <xrpld/rpc/detail/PathRequest.h>
#include <xrpld/rpc/detail/RippleLineCache.h>
#include <atomic> #include <atomic>
#include <mutex> #include <mutex>

View File

@@ -3,6 +3,7 @@
#include <xrpld/app/main/Application.h> #include <xrpld/app/main/Application.h>
#include <xrpld/rpc/detail/AssetCache.h> #include <xrpld/rpc/detail/AssetCache.h>
#include <xrpld/rpc/detail/PathfinderUtils.h> #include <xrpld/rpc/detail/PathfinderUtils.h>
#include <xrpld/rpc/detail/RippleLineCache.h>
#include <xrpld/rpc/detail/TrustLine.h> #include <xrpld/rpc/detail/TrustLine.h>
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>

View File

View File

View File

@@ -1,5 +1,3 @@
#pragma once
#include <xrpld/rpc/detail/RPCHelpers.h> #include <xrpld/rpc/detail/RPCHelpers.h>
#include <xrpl/basics/StringUtilities.h> #include <xrpl/basics/StringUtilities.h>

View File

@@ -120,10 +120,7 @@ doAMMInfo(RPC::JsonContext& context)
if (params.isMember(jss::amm_account)) if (params.isMember(jss::amm_account))
{ {
auto const& ammAccount = params[jss::amm_account]; auto const id = parseBase58<AccountID>((params[jss::amm_account].asString()));
if (!ammAccount.isString())
return std::unexpected(RpcActMalformed);
auto const id = parseBase58<AccountID>(ammAccount.asString());
if (!id) if (!id)
return std::unexpected(RpcActMalformed); return std::unexpected(RpcActMalformed);
auto const sle = ledger->read(keylet::account(*id)); auto const sle = ledger->read(keylet::account(*id));
@@ -136,10 +133,7 @@ doAMMInfo(RPC::JsonContext& context)
if (params.isMember(jss::account)) if (params.isMember(jss::account))
{ {
auto const& localAccount = params[jss::account]; accountID = parseBase58<AccountID>(params[jss::account].asString());
if (!localAccount.isString())
return std::unexpected(RpcActMalformed);
accountID = parseBase58<AccountID>(localAccount.asString());
if (!accountID || !ledger->read(keylet::account(*accountID))) if (!accountID || !ledger->read(keylet::account(*accountID)))
return std::unexpected(RpcActMalformed); return std::unexpected(RpcActMalformed);
} }