Merge branch 'develop' into gregtatcam/mpt/mpt-invariants-gate-update

This commit is contained in:
Gregory Tsipenyuk
2026-08-06 13:19:39 -04:00
381 changed files with 16067 additions and 11896 deletions

View File

@@ -75,6 +75,8 @@ Checks: "-*,
# readability-static-accessed-through-instance, # this check is probably unnecessary. It makes the code less readable
# ---
FormatStyle: file
CheckOptions:
bugprone-unsafe-functions.ReportMoreUnsafeFunctions: true
bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc
@@ -83,6 +85,8 @@ CheckOptions:
readability-braces-around-statements.ShortStatementLines: 2
readability-identifier-naming.MacroDefinitionCase: UPPER_CASE
readability-identifier-naming.NamespaceCase: lower_case
readability-identifier-naming.InlineNamespaceCase: lower_case
readability-identifier-naming.ClassCase: CamelCase
readability-identifier-naming.StructCase: CamelCase
readability-identifier-naming.UnionCase: CamelCase

2
.envrc
View File

@@ -1 +1,3 @@
watch_file nix/*.nix
use flake

View File

@@ -33,6 +33,10 @@ def get_cmake_args(build_type: str, extra_args: str) -> str:
# Every config must declare 'minimal'. Minimal configs form the reduced matrix
# built for pull requests by default; the full matrix adds the rest. Packaging
# configs declare it too, but packaging is gated in the workflow, not by it.
#
# Configs may also opt into 'benchmark' to smoke-run the benchmarks. Note that
# the flag applies to every entry a config expands into, so only set it on
# configs that expand to a single combination.
@dataclasses.dataclass
@@ -43,6 +47,7 @@ class LinuxConfig:
build_type: list[str]
arch: list[str]
minimal: bool
benchmark: bool = False # if true, smoke-run the benchmarks after testing
sanitizers: list[str] = dataclasses.field(default_factory=list)
suffix: str = ""
extra_cmake_args: str = ""
@@ -81,6 +86,7 @@ class PlatformConfig:
build_type: list[str]
minimal: bool
build_only: bool = False # if true, skip tests (e.g. macos/Windows Debug)
benchmark: bool = False # if true, smoke-run the benchmarks after testing
extra_cmake_args: str = ""
def __post_init__(self) -> None:
@@ -125,6 +131,7 @@ class MatrixEntry:
cmake_args: str
cmake_target: str
build_only: bool
benchmark: bool
build_type: str
architecture: Architecture
sanitizers: str
@@ -193,6 +200,7 @@ def expand_linux_matrix(linux: LinuxFile, minimal: bool) -> list[MatrixEntry]:
cmake_args=get_cmake_args(build_type, cfg.extra_cmake_args),
cmake_target="all",
build_only=False,
benchmark=cfg.benchmark,
build_type=build_type,
architecture=arch_info,
sanitizers=sanitizer,
@@ -245,6 +253,7 @@ def expand_platform_matrix(pf: PlatformFile, minimal: bool) -> list[MatrixEntry]
cmake_args=get_cmake_args(build_type, cfg.extra_cmake_args),
cmake_target="install" if is_windows else "all",
build_only=cfg.build_only,
benchmark=cfg.benchmark,
build_type=build_type,
architecture=Architecture(platform=pf.platform, runner=pf.runner),
sanitizers="",

View File

@@ -2,12 +2,22 @@
"image_tag": "sha-fecfc0c",
"configs": {
"ubuntu": [
{
"compiler": ["gcc"],
"build_type": ["Debug"],
"arch": ["amd64"],
"minimal": true,
"suffix": "coverage",
"extra_cmake_args": "-DUNIT_TEST_REFERENCE_FEE=500 -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0"
},
{
"compiler": ["clang"],
"build_type": ["Release"],
"arch": ["amd64"],
"minimal": true
"minimal": true,
"benchmark": true
},
{
"compiler": ["gcc"],
"build_type": ["Release"],
@@ -29,14 +39,6 @@
"sanitizers": ["address", "undefinedbehavior"]
},
{
"compiler": ["gcc"],
"build_type": ["Debug"],
"arch": ["amd64"],
"minimal": true,
"suffix": "coverage",
"extra_cmake_args": "-DUNIT_TEST_REFERENCE_FEE=500 -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0"
},
{
"compiler": ["clang"],
"build_type": ["Debug"],

View File

@@ -82,6 +82,7 @@ jobs:
.github/scripts/strategy-matrix/**
.github/workflows/reusable-build-test-config.yml
.github/workflows/reusable-build-test.yml
.github/workflows/reusable-check-autogen.yml
.github/workflows/reusable-clang-tidy.yml
.github/workflows/reusable-package.yml
.github/workflows/reusable-strategy-matrix.yml
@@ -126,6 +127,11 @@ jobs:
outputs:
go: ${{ steps.go.outputs.go == 'true' }}
check-autogen:
needs: should-run
if: ${{ needs.should-run.outputs.go == 'true' }}
uses: ./.github/workflows/reusable-check-autogen.yml
check-levelization:
needs: should-run
if: ${{ needs.should-run.outputs.go == 'true' }}
@@ -200,6 +206,7 @@ jobs:
passed:
if: failure() || cancelled()
needs:
- check-autogen
- check-levelization
- check-rename
- clang-tidy

View File

@@ -20,6 +20,7 @@ on:
- ".github/scripts/strategy-matrix/**"
- ".github/workflows/reusable-build-test-config.yml"
- ".github/workflows/reusable-build-test.yml"
- ".github/workflows/reusable-check-autogen.yml"
- ".github/workflows/reusable-clang-tidy.yml"
- ".github/workflows/reusable-package.yml"
- ".github/workflows/reusable-strategy-matrix.yml"
@@ -67,6 +68,9 @@ defaults:
shell: bash
jobs:
check-autogen:
uses: ./.github/workflows/reusable-check-autogen.yml
clang-tidy:
uses: ./.github/workflows/reusable-clang-tidy.yml
permissions:

View File

@@ -3,6 +3,12 @@ name: Build and test configuration
on:
workflow_call:
inputs:
benchmark:
description: "Whether to smoke-run the benchmarks after testing."
required: false
type: boolean
default: false
build_only:
description: 'Whether to only build or to build and test the code ("true", "false").'
required: true
@@ -170,9 +176,9 @@ jobs:
..
# Export the sanitizer options before any instrumented binary runs. The
# protocol code-gen and build steps below invoke instrumented dependency
# tools (protoc, grpc), so setting UBSAN_OPTIONS here lets the UBSan
# suppression list silence their diagnostics too, not just at test time.
# build step below invokes instrumented dependency tools (protoc, grpc),
# so setting UBSAN_OPTIONS here lets the UBSan suppression list silence
# their diagnostics too, not just at test time.
# GITHUB_WORKSPACE (not the github.workspace context) is used so the path
# resolves correctly inside the container job.
- name: Set sanitizer options
@@ -190,32 +196,6 @@ jobs:
echo "UBSAN_OPTIONS=include=${SUPP}/runtime-ubsan-options.txt:suppressions=${SUPP}/ubsan.supp" >>${GITHUB_ENV}
echo "LSAN_OPTIONS=include=${SUPP}/runtime-lsan-options.txt:suppressions=${SUPP}/lsan.supp" >>${GITHUB_ENV}
- name: Check protocol autogen files are up-to-date
working-directory: ${{ env.BUILD_DIR }}
env:
MESSAGE: |
The generated protocol wrapper classes are out of date.
This typically happens when the macro files or generator scripts
have changed but the generated files were not regenerated.
To fix this:
1. Run: cmake --build . --target setup_code_gen
2. Run: cmake --build . --target code_gen
3. Commit and push the regenerated files
run: |
set -e
cmake --build . --target setup_code_gen
cmake --build . --target code_gen
DIFF=$(git -C .. status --porcelain -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen)
if [ -n "${DIFF}" ]; then
echo "::error::Generated protocol files are out of date"
git -C .. diff -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen
echo "${MESSAGE}"
exit 1
fi
- name: Build the binary
working-directory: ${{ env.BUILD_DIR }}
env:
@@ -328,11 +308,14 @@ jobs:
# Smoke-run every benchmark module with a single repetition to confirm the
# benchmarks still build and execute. This is a correctness check, not a
# performance measurement, so it is skipped for instrumented builds
# (sanitizers/coverage/voidstar), where it would be slow and meaningless,
# and on Windows, where the `install` target does not build them.
# performance measurement, so there is nothing to gain from repeating it
# across configurations: it is opted into by a single config in the
# strategy matrix (see the 'benchmark' flag in the JSON files), which
# keeps it off instrumented builds (sanitizers/coverage/voidstar), where
# it would be slow and meaningless, off Debug builds, where it is much
# slower, and off Windows, where the `install` target does not build them.
- name: Run the benchmarks
if: ${{ !inputs.build_only && runner.os != 'Windows' && env.SANITIZERS_ENABLED == 'false' && env.COVERAGE_ENABLED != 'true' && env.VOIDSTAR_ENABLED != 'true' }}
if: ${{ inputs.benchmark }}
working-directory: ${{ env.BUILD_DIR }}
run: |
rc=0

View File

@@ -40,6 +40,7 @@ jobs:
fail-fast: ${{ github.event_name == 'merge_group' }}
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
with:
benchmark: ${{ matrix.benchmark }}
build_only: ${{ matrix.build_only }}
build_type: ${{ matrix.build_type }}
ccache_enabled: ${{ inputs.ccache_enabled }}

View File

@@ -0,0 +1,76 @@
# This workflow checks that the generated protocol wrapper classes are
# up-to-date with the macro files and generator scripts they are produced from,
# see more info in include/xrpl/protocol_autogen/README.md.
name: Check autogen
# This workflow can only be triggered by other workflows.
on: workflow_call
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-autogen
cancel-in-progress: true
defaults:
run:
shell: bash
env:
BUILD_DIR: build/codegen
jobs:
autogen:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.13"
# Code generation is pure Python, so the standalone project below offers
# the same targets as the main build without needing its dependencies or
# a compiler, which keeps this job down to a few seconds.
- name: Configure CMake
run: cmake -S cmake/codegen -B "${BUILD_DIR}"
- name: Install code generation dependencies
run: cmake --build "${BUILD_DIR}" --target setup_code_gen
- name: Generate code
run: cmake --build "${BUILD_DIR}" --target code_gen
- name: Check for differences
env:
MESSAGE: |
The generated protocol wrapper classes are out of date.
This typically happens when the macro files or generator scripts
have changed but the generated files were not regenerated.
Run the following from the repository root, then commit and push
the regenerated files. This needs neither the dependencies nor a
compiler. See include/xrpl/protocol_autogen/README.md for more info.
cmake -S cmake/codegen -B build/codegen
cmake --build build/codegen --target setup_code_gen
cmake --build build/codegen --target code_gen
In an already configured build directory, the 'setup_code_gen' and
'code_gen' targets do the same thing.
run: |
# Record untracked files in the index without staging their contents,
# so that classes generated for a newly added transaction or ledger
# entry type show up in the diff below rather than silently as an
# empty one.
git add --intent-to-add .
DIFF=$(git status --porcelain)
if [ -n "${DIFF}" ]; then
# Print the differences to give the contributor a hint about what to
# expect when running code generation on their own machine.
git diff
echo "${MESSAGE}"
exit 1
fi

View File

@@ -95,7 +95,7 @@ jobs:
TARGETS: ${{ needs.determine-files.outputs.need_full_run != 'true' && needs.determine-files.outputs.cpp_changed_files || 'include src tests' }}
run: |
set -o pipefail
run-clang-tidy -j ${{ steps.nproc.outputs.nproc }} -p "${BUILD_DIR}" -quiet -fix -allow-no-checks ${TARGETS} 2>&1 | tee "${OUTPUT_FILE}"
run-clang-tidy -j ${{ steps.nproc.outputs.nproc }} -p "${BUILD_DIR}" -quiet -fix -format -allow-no-checks ${TARGETS} 2>&1 | tee "${OUTPUT_FILE}"
- name: Print filtered clang-tidy errors
if: ${{ steps.run_clang_tidy.outcome != 'success' }}

View File

@@ -42,6 +42,8 @@ Our Linux CI tooling is distro-independent and uses a Nix-based environment, so
### macOS
Many `xrpld` engineers use macOS for development.
The minimum supported version is macOS 15 (Sequoia).
CI testing is done in macOS 26 (Tahoe), but the build defaults `CMAKE_OSX_DEPLOYMENT_TARGET` to 15.
### Windows
@@ -245,7 +247,17 @@ cmake --build . --target setup_code_gen # create venv and install dependencies
cmake --build . --target code_gen # regenerate code
```
The regenerated files should be committed alongside your changes.
The same targets are also available as a standalone project, which does not
need the dependencies to be configured first:
```
cmake -S cmake/codegen -B build/codegen
cmake --build build/codegen --target setup_code_gen
cmake --build build/codegen --target code_gen
```
The regenerated files should be committed alongside your changes. CI verifies
that they are up-to-date.
## Coverage report

View File

@@ -13,6 +13,23 @@ if(DEFINED CMAKE_MODULE_PATH)
endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# Must be set before project() because project() consumes it when configuring the compiler and SDK.
# A user-provided -DCMAKE_OSX_DEPLOYMENT_TARGET still takes precedence.
#
# CMAKE_SYSTEM_NAME can't be used before project(), so CMAKE_HOST_SYSTEM_NAME is used instead.
#
# When CMAKE_OSX_DEPLOYMENT_TARGET is bumped to >=26.0, FastFloat dependency won't be needed anymore
if(
CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin"
AND NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET
)
set(CMAKE_OSX_DEPLOYMENT_TARGET
"15.0"
CACHE STRING
"Minimum macOS deployment version"
)
endif()
project(xrpl)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 23)
@@ -87,6 +104,7 @@ include(deps/Boost)
add_subdirectory(external/antithesis-sdk)
find_package(date REQUIRED)
find_package(ed25519 REQUIRED)
find_package(FastFloat REQUIRED)
find_package(gRPC REQUIRED)
find_package(LibArchive REQUIRED)
find_package(lz4 REQUIRED)
@@ -102,6 +120,7 @@ target_link_libraries(
xrpl_libs
INTERFACE
ed25519::ed25519
FastFloat::fast_float
lz4::lz4
mpt-crypto::mpt-crypto
OpenSSL::Crypto

View File

@@ -348,12 +348,14 @@ run-clang-tidy -p build -allow-no-checks src tests
```
This will check all source files in the `src`, `include` and `tests` directories using the compile commands from your `build` directory.
If you wish to automatically fix whatever clang-tidy finds _and_ is capable of fixing, add `-fix` to the above command:
If you wish to automatically fix whatever clang-tidy finds _and_ is capable of fixing, add `-fix -format` to the above command:
```
run-clang-tidy -p build -quiet -fix -allow-no-checks src tests
run-clang-tidy -p build -quiet -fix -format -allow-no-checks src tests
```
`-format` reformats the fixed code with [`.clang-format`](./.clang-format); without it the fixes are inserted in LLVM style and the `clang-format` hook rewrites them afterwards.
## Contracts and instrumentation
We are using [Antithesis](https://antithesis.com/) for continuous fuzzing,

View File

@@ -144,7 +144,11 @@ def main():
+ files
)
canonicalize_fix_paths(Path(fixes_dir))
applied = subprocess.run([clang_apply_replacements, fixes_dir])
# `FormatStyle` in .clang-tidy does not reach this path,
# so ask for the repository style here.
applied = subprocess.run(
[clang_apply_replacements, "--format", "--style=file", fixes_dir]
)
return result.returncode or applied.returncode

View File

@@ -1,3 +1,5 @@
include_guard()
include(isolate_headers)
# Define a benchmark executable for the module `name`.

View File

@@ -2,21 +2,22 @@
Protocol Autogen - Code generation for protocol wrapper classes
#]===================================================================]
# The repository root, derived from the location of this file rather than from
# the including project, so that the targets below can also be offered on their
# own by cmake/codegen/CMakeLists.txt.
get_filename_component(XRPL_ROOT "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
set(CODEGEN_VENV_DIR
"${CMAKE_CURRENT_SOURCE_DIR}/.venv"
"${XRPL_ROOT}/.venv"
CACHE PATH
"Path to a Python virtual environment for code generation. A venv will be created here by setup_code_gen and used to run generation scripts."
)
# Directory paths
set(MACRO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/xrpl/protocol/detail")
set(AUTOGEN_HEADER_DIR
"${CMAKE_CURRENT_SOURCE_DIR}/include/xrpl/protocol_autogen"
)
set(AUTOGEN_TEST_DIR
"${CMAKE_CURRENT_SOURCE_DIR}/src/tests/libxrpl/protocol_autogen"
)
set(SCRIPTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake/scripts/codegen")
set(MACRO_DIR "${XRPL_ROOT}/include/xrpl/protocol/detail")
set(AUTOGEN_HEADER_DIR "${XRPL_ROOT}/include/xrpl/protocol_autogen")
set(AUTOGEN_TEST_DIR "${XRPL_ROOT}/src/tests/libxrpl/protocol_autogen")
set(SCRIPTS_DIR "${XRPL_ROOT}/cmake/scripts/codegen")
# Input macro files
set(TRANSACTIONS_MACRO "${MACRO_DIR}/transactions.macro")
@@ -114,14 +115,14 @@ if(CODEGEN_VENV_DIR)
setup_code_gen
COMMAND ${Python3_EXECUTABLE} -m venv "${CODEGEN_VENV_DIR}"
COMMAND ${CODEGEN_PYTHON} -m pip install -r "${REQUIREMENTS_FILE}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
WORKING_DIRECTORY "${XRPL_ROOT}"
COMMENT "Creating venv and installing code generation dependencies..."
)
else()
add_custom_target(
setup_code_gen
COMMAND ${Python3_EXECUTABLE} -m pip install -r "${REQUIREMENTS_FILE}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
WORKING_DIRECTORY "${XRPL_ROOT}"
COMMENT "Installing code generation dependencies..."
)
endif()
@@ -139,8 +140,8 @@ add_custom_target(
-DSFIELDS_MACRO=${SFIELDS_MACRO}
-DAUTOGEN_HEADER_DIR=${AUTOGEN_HEADER_DIR}
-DAUTOGEN_TEST_DIR=${AUTOGEN_TEST_DIR} -P
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/XrplProtocolAutogenRun.cmake"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_LIST_DIR}/XrplProtocolAutogenRun.cmake"
WORKING_DIRECTORY "${XRPL_ROOT}"
COMMENT "Running protocol code generation..."
SOURCES ${ALL_INPUT_FILES}
)

View File

@@ -0,0 +1,21 @@
#[===================================================================[
Protocol Autogen - Standalone project
Exposes the 'setup_code_gen' and 'code_gen' targets on their own, without
configuring the rest of xrpl. Code generation is pure Python, so this needs
neither the dependencies nor a compiler, which makes it usable in CI and by
contributors who only want to regenerate the protocol wrapper classes:
cmake -S cmake/codegen -B build/codegen
cmake --build build/codegen --target setup_code_gen
cmake --build build/codegen --target code_gen
The targets are identical to the ones offered by the top-level build, since
both come from cmake/XrplProtocolAutogen.cmake.
#]===================================================================]
cmake_minimum_required(VERSION 3.16)
project(xrpl_codegen LANGUAGES NONE)
include("${CMAKE_CURRENT_LIST_DIR}/../XrplProtocolAutogen.cmake")

View File

@@ -20,6 +20,7 @@
"jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1782392402.791979",
"grpc/1.81.1#f729f6d75992d20f9c72828e9142d62f%1783945160.094135",
"fast_float/8.2.10#f6f28d6bb22112078e7dbda611caf681%1782494504.298",
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1782307148.15562",
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1782392402.538492",
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1782392402.681654",
@@ -34,7 +35,7 @@
"protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1782392410.233933",
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1782395690.33162",
"msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649",
"m4/1.4.19#34c4bbc3eeebe98ca6edf2f52d602e7d%1777282960.259",
"m4/1.4.19#1727f439cf74e83826ec96d0b4904eee%1784541921.659",
"cmake/4.3.3#840cf00ea09777e05c2050a50a82c722%1782392418.696091",
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1782392402.624226",
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",

View File

@@ -5,6 +5,13 @@
{% if os == "Linux" %}
{% set compiler_version = detect_api.default_compiler_version(compiler, version) %}
{% endif %}
{% if os == "Macos" %}
{# Minimum macOS the dependencies target. #}
{# Without this, Conan builds each dependency against the (possibly newer) host SDK, so the #}
{# dependency objects target a newer macOS than the binary and the linker warns. #}
{# Keep at or below CMAKE_OSX_DEPLOYMENT_TARGET in CMakeLists.txt. #}
{% set min_macos_version = "15.0" %}
{% endif %}
[settings]
os={{ os }}
@@ -18,6 +25,9 @@ compiler.runtime=static
{% else %}
compiler.libcxx={{ detect_api.detect_libcxx(compiler, version, compiler_exe) }}
{% endif %}
{% if os == "Macos" %}
os.version={{ min_macos_version }}
{% endif %}
[conf]
{# The Boost recipe builds with b2, which doesn't use Conan's toolchain files. #}
@@ -41,3 +51,13 @@ tools.build:compiler_executables={'c':'{{ cc_exe }}','cpp':'{{ cxx_exe }}'}
{# More info: https://docs.conan.io/2/reference/extensions/binary_compatibility.html #}
user.package:cppstd_version=23
tools.info.package_id:confs+=["user.package:cppstd_version"]
{% if os == "Macos" %}
[buildenv]
{# os.version adds -mmacosx-version-min to compiler command lines, #}
{# but Boost.Context's b2 assembly (.S) rule ignores it, #}
{# so those objects keep the host SDK version and still warn at link time. #}
{# clang's assembler honors this env var regardless, pinning them. #}
{# Scoped to boost/* since it is the only gap. #}
boost/*:MACOSX_DEPLOYMENT_TARGET={{ min_macos_version }}
{% endif %}

View File

@@ -29,6 +29,7 @@ class Xrpl(ConanFile):
requires = [
"ed25519/2015.03",
"fast_float/8.2.10",
"grpc/1.81.1",
"libarchive/3.8.7",
"nudb/2.0.9",
@@ -211,6 +212,7 @@ class Xrpl(ConanFile):
"boost::thread",
"date::date",
"ed25519::ed25519",
"fast_float::fast_float",
"grpc::grpc++",
"libarchive::libarchive",
"lz4::lz4",

View File

@@ -3,6 +3,78 @@
Common issues encountered when using the [Nix development shell](./nix.md), and
how to resolve them.
## `command not found: nix` after a macOS update
If a shell suddenly can't find `nix` at all:
```
$ nix develop
zsh: command not found: nix
```
then Nix is almost certainly still installed — only the shell hook that puts it
on your `PATH` is gone. Confirm that first:
```bash
ls -l /nix/var/nix/profiles/default/bin/nix
```
If that exists, the installation is fine and this is purely a `PATH` problem.
### Why it happens
The installer does not touch your dotfiles. Instead it sources a setup script
from the Nix store by editing **system-wide** rc files:
| Shell | File the installer edits |
| ----- | ------------------------------------- |
| bash | `/etc/bashrc`, `/etc/bash.bashrc` |
| zsh | `/etc/zshrc` |
| fish | `$__fish_sysconf_dir/conf.d/nix.fish` |
macOS manages `/etc/zshrc`, so an OS update can replace it with the vendor copy
and silently drop the Nix block. `/etc/bashrc` and the fish file usually survive,
which is why the breakage often shows up in zsh only. You can verify this by
diffing against the backup the installer left behind:
```bash
diff /etc/zshrc /etc/zshrc.backup-before-nix
```
If they are identical, the Nix snippet was wiped. This is upstream issue
[NixOS/nix#3616](https://github.com/NixOS/nix/issues/3616).
### Fix
To unblock the current shell:
```bash
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
```
For a permanent fix, add the snippet to your **user** rc file rather than
restoring `/etc/zshrc` — user dotfiles are not clobbered by OS updates:
```bash
cat >>~/.zshrc <<'EOF'
# Nix
if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
. '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
fi
# End Nix
EOF
```
The scripts guard against double-sourcing via `__ETC_PROFILE_NIX_SOURCED`, so
this is safe even if a system-wide hook is later restored.
> [!NOTE]
> `/etc/zshrc` and `~/.zshrc` are only read by **interactive** zsh. If the
> snippet is present but `zsh -c '…'`, a script, or an IDE terminal still can't
> find `nix`, that shell is non-interactive — put the snippet in `~/.zshenv`
> instead.
## Git worktrees
If `nix develop` fails with an error like:

View File

@@ -11,7 +11,7 @@ namespace xrpl {
class Resolver
{
public:
using HandlerType = std::function<void(std::string, std::vector<beast::IP::Endpoint>)>;
using HandlerType = std::function<void(std::string, std::vector<beast::ip::Endpoint>)>;
virtual ~Resolver() = 0;

View File

@@ -11,6 +11,7 @@
#include <limits>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
@@ -251,4 +252,11 @@ makeSlice(std::basic_string<char, Traits, Alloc> const& s)
return Slice(s.data(), s.size());
}
template <class Traits>
Slice
makeSlice(std::basic_string_view<char, Traits> s)
{
return Slice(s.data(), s.size());
}
} // namespace xrpl

View File

@@ -58,7 +58,7 @@ struct LexicalCast<Out, std::string_view>
"beast::LexicalCast can only be used with integral types");
template <class Integral = Out>
bool
constexpr bool
operator()(Integral& out, std::string_view in) const
requires(std::is_integral_v<Integral> && !std::is_same_v<Integral, bool>)
{
@@ -110,7 +110,7 @@ struct LexicalCast<Out, boost::core::basic_string_view<char>>
{
explicit LexicalCast() = default;
bool
constexpr bool
operator()(Out& out, boost::core::basic_string_view<char> in) const
{
return LexicalCast<Out, std::string_view>()(out, in);
@@ -123,7 +123,7 @@ struct LexicalCast<Out, std::string>
{
explicit LexicalCast() = default;
bool
constexpr bool
operator()(Out& out, std::string in) const
{
return LexicalCast<Out, std::string_view>()(out, in);
@@ -136,7 +136,7 @@ struct LexicalCast<Out, char const*>
{
explicit LexicalCast() = default;
bool
constexpr bool
operator()(Out& out, char const* in) const
{
XRPL_ASSERT(in, "beast::detail::LexicalCast(char const*) : non-null input");
@@ -151,7 +151,7 @@ struct LexicalCast<Out, char*>
{
explicit LexicalCast() = default;
bool
constexpr bool
operator()(Out& out, char* in) const
{
XRPL_ASSERT(in, "beast::detail::LexicalCast(char*) : non-null input");
@@ -177,7 +177,7 @@ struct BadLexicalCast : public std::bad_cast
* @return `false` if there was a parsing or range error
*/
template <class Out, class In>
bool
constexpr bool
lexicalCastChecked(Out& out, In in)
{
return detail::LexicalCast<Out, In>()(out, in);
@@ -191,7 +191,7 @@ lexicalCastChecked(Out& out, In in)
* @return The new type.
*/
template <class Out, class In>
Out
constexpr Out
lexicalCastThrow(In in)
{
if (Out out; lexicalCastChecked(out, in))
@@ -207,7 +207,7 @@ lexicalCastThrow(In in)
* @return The new type.
*/
template <class Out, class In>
Out
constexpr Out
lexicalCast(In in, Out defaultValue = Out())
{
if (Out out; lexicalCastChecked(out, in))

View File

@@ -17,14 +17,14 @@ namespace beast {
class SemanticVersion
{
public:
using identifier_list = std::vector<std::string>;
using IdentifierList = std::vector<std::string>;
int majorVersion;
int minorVersion;
int patchVersion;
identifier_list preReleaseIdentifiers;
identifier_list metaData;
IdentifierList preReleaseIdentifiers;
IdentifierList metaData;
SemanticVersion();

View File

@@ -26,7 +26,7 @@ public:
* @param journal Destination for logging output.
*/
static std::shared_ptr<StatsDCollector>
make(IP::Endpoint const& address, std::string const& prefix, Journal journal);
make(ip::Endpoint const& address, std::string const& prefix, Journal journal);
};
} // namespace beast::insight

View File

@@ -15,7 +15,7 @@
//------------------------------------------------------------------------------
namespace beast {
namespace IP {
namespace ip {
using Address = boost::asio::ip::address;
@@ -73,13 +73,13 @@ isPublic(Address const& addr)
return (addr.is_v4()) ? isPublic(addr.to_v4()) : isPublic(addr.to_v6());
}
} // namespace IP
} // namespace ip
//------------------------------------------------------------------------------
template <class Hasher>
void
hash_append(Hasher& h, beast::IP::Address const& addr) noexcept
hash_append(Hasher& h, beast::ip::Address const& addr) noexcept
{
using beast::hash_append;
if (addr.is_v4())
@@ -101,12 +101,12 @@ hash_append(Hasher& h, beast::IP::Address const& addr) noexcept
namespace boost {
template <>
struct hash<::beast::IP::Address>
struct hash<::beast::ip::Address>
{
explicit hash() = default;
std::size_t
operator()(::beast::IP::Address const& addr) const
operator()(::beast::ip::Address const& addr) const
{
return ::beast::Uhash<>{}(addr);
}

View File

@@ -4,7 +4,7 @@
#include <boost/asio.hpp>
namespace beast::IP {
namespace beast::ip {
/**
* Convert to Endpoint.
@@ -32,7 +32,7 @@ toAsioAddress(Endpoint const& endpoint);
boost::asio::ip::tcp::endpoint
toAsioEndpoint(Endpoint const& endpoint);
} // namespace beast::IP
} // namespace beast::ip
namespace beast {
@@ -41,25 +41,25 @@ struct IPAddressConversion
{
explicit IPAddressConversion() = default;
static IP::Endpoint
static ip::Endpoint
fromAsio(boost::asio::ip::address const& address)
{
return IP::fromAsio(address);
return ip::fromAsio(address);
}
static IP::Endpoint
static ip::Endpoint
fromAsio(boost::asio::ip::tcp::endpoint const& endpoint)
{
return IP::fromAsio(endpoint);
return ip::fromAsio(endpoint);
}
static boost::asio::ip::address
toAsioAddress(IP::Endpoint const& address)
toAsioAddress(ip::Endpoint const& address)
{
return IP::toAsioAddress(address);
return ip::toAsioAddress(address);
}
static boost::asio::ip::tcp::endpoint
toAsioEndpoint(IP::Endpoint const& address)
toAsioEndpoint(ip::Endpoint const& address)
{
return IP::toAsioEndpoint(address);
return ip::toAsioEndpoint(address);
}
};

View File

@@ -2,7 +2,7 @@
#include <boost/asio/ip/address_v4.hpp>
namespace beast::IP {
namespace beast::ip {
using AddressV4 = boost::asio::ip::address_v4;
@@ -25,4 +25,4 @@ isPublic(AddressV4 const& addr);
char
getClass(AddressV4 const& address);
} // namespace beast::IP
} // namespace beast::ip

View File

@@ -2,7 +2,7 @@
#include <boost/asio/ip/address_v6.hpp>
namespace beast::IP {
namespace beast::ip {
using AddressV6 = boost::asio::ip::address_v6;
@@ -18,4 +18,4 @@ isPrivate(AddressV6 const& addr);
bool
isPublic(AddressV6 const& addr);
} // namespace beast::IP
} // namespace beast::ip

View File

@@ -13,7 +13,7 @@
#include <optional>
#include <string>
namespace beast::IP {
namespace beast::ip {
using Port = std::uint16_t;
@@ -223,7 +223,7 @@ operator<<(OutputStream& os, Endpoint const& endpoint)
std::istream&
operator>>(std::istream& is, Endpoint& endpoint);
} // namespace beast::IP
} // namespace beast::ip
//------------------------------------------------------------------------------
@@ -232,12 +232,12 @@ namespace std {
* std::hash support.
*/
template <>
struct hash<::beast::IP::Endpoint>
struct hash<::beast::ip::Endpoint>
{
hash() = default;
std::size_t
operator()(::beast::IP::Endpoint const& endpoint) const
operator()(::beast::ip::Endpoint const& endpoint) const
{
return ::beast::Uhash<>{}(endpoint);
}
@@ -249,12 +249,12 @@ namespace boost {
* boost::hash support.
*/
template <>
struct hash<::beast::IP::Endpoint>
struct hash<::beast::ip::Endpoint>
{
hash() = default;
std::size_t
operator()(::beast::IP::Endpoint const& endpoint) const
operator()(::beast::ip::Endpoint const& endpoint) const
{
return ::beast::Uhash<>{}(endpoint);
}

View File

@@ -295,6 +295,20 @@ public:
return runner_->arg();
}
protected:
/**
* Lets a suite compose other suites (e.g. an aggregator that reruns a
* group of related suites under its own name) via `SuiteInfo::run`.
*
* @return The runner this suite is executing under.
*/
Runner&
runner() const
{
return *runner_;
}
public:
/**
* DEPRECATED
* @return `true` if the test condition indicates success(a false value)

View File

@@ -18,9 +18,9 @@ namespace xrpl {
namespace node_store {
class Database;
} // namespace node_store
namespace Resource {
namespace resource {
class Manager;
} // namespace Resource
} // namespace resource
namespace perf {
class PerfLog;
} // namespace perf
@@ -160,7 +160,7 @@ public:
virtual PeerReservationTable&
getPeerReservations() = 0;
virtual Resource::Manager&
virtual resource::Manager&
getResourceManager() = 0;
// Storage services

View File

@@ -623,6 +623,7 @@ class ValueConstIterator : public ValueIteratorBase
public:
using size_t = unsigned int;
using difference_type = int;
using value_type = Value const;
using reference = Value const&;
using pointer = Value const*;
using SelfType = ValueConstIterator;
@@ -687,6 +688,7 @@ class ValueIterator : public ValueIteratorBase
public:
using size_t = unsigned int;
using difference_type = int;
using value_type = Value;
using reference = Value&;
using pointer = Value*;
using SelfType = ValueIterator;

View File

@@ -286,6 +286,77 @@ computeFullPaymentInterest(
std::uint32_t startDate,
TenthBips32 closeInterestRate);
// Deltas applied to Vault.AssetsTotal and LoanBroker.DebtTotal at a single
// accounting touch point (origination, payment, impair/unimpair/default).
struct AccountingDeltas
{
Number assetsTotalDelta;
Number debtTotalDelta;
};
// Whole-life (pre-LendingProtocolV1_1) recognition model: interest is
// recognized into AssetsTotal/DebtTotal up front, at origination.
namespace accrual {
// LoanSet origination: what's added to Vault.AssetsTotal and LoanBroker.DebtTotal
AccountingDeltas
loanOriginationDeltas(Number const& principalRequested, Number const& interestDue);
// LoanSet origination: would recognizing this loan's interest push
// Vault.AssetsTotal past Vault.AssetsMaximum?
bool
loanOriginationExceedsVaultMaximum(
Number const& vaultMaximum,
Number const& vaultTotal,
Number const& interestDue);
// LoanManage impair/unimpair/default: the vault's exposure to this loan
Number
loanVaultExposure(SLE::const_ref loanSle);
// LoanPay: what's added to Vault.AssetsTotal and subtracted from LoanBroker.DebtTotal for a payment
AccountingDeltas
loanPaymentDeltas(LoanPaymentParts const& parts);
} // namespace accrual
// Cash-basis (LendingProtocolV1_1) recognition model: AssetsTotal/DebtTotal
// are principal-only, interest is recognized only as it's actually paid.
namespace cash_basis {
AccountingDeltas
loanOriginationDeltas(Number const& principalRequested);
Number
loanVaultExposure(SLE::const_ref loanSle);
AccountingDeltas
loanPaymentDeltas(LoanPaymentParts const& parts);
} // namespace cash_basis
// Public dispatchers: pick cash_basis:: if featureLendingProtocolV1_1 is
// enabled AND the Vault's LEVersion (VaultHelpers::getVaultVersion) is
// VaultVersion::CashBasis, else accrual::. These are the only entry points
// transactors call.
AccountingDeltas
loanOriginationDeltas(
SLE::const_ref vaultSle,
Number const& principalRequested,
Number const& interestDue);
bool
loanOriginationExceedsVaultMaximum(
SLE::const_ref vaultSle,
Number const& vaultTotal,
Number const& interestDue);
Number
loanVaultExposure(SLE::const_ref vaultSle, SLE::const_ref loanSle);
AccountingDeltas
loanPaymentDeltas(SLE::const_ref vaultSle, LoanPaymentParts const& parts);
namespace detail {
// These classes and functions should only be accessed by LendingHelper
// functions and unit tests

View File

@@ -2,6 +2,7 @@
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STLedgerEntry.h>
@@ -107,4 +108,19 @@ sharesToAssetsWithdraw(
[[nodiscard]] bool
isSoleShareholder(ReadView const& view, AccountID const& account, SLE::const_ref issuance);
/**
* Resolves a Vault's LEVersion, the single point every accounting touch
* point should call to determine which recognition model (accrual vs.
* cash-basis) a Vault uses. Vaults created before featureLendingProtocolV1_1
* activated never have sfLEVersion set, which resolves here to
* VaultVersion::Legacy.
*
* @param vault The vault SLE.
*
* @return The Vault's LEVersion, or VaultVersion::Legacy if the field is
* absent.
*/
[[nodiscard]] VaultVersion
getVaultVersion(SLE::const_ref vault);
} // namespace xrpl

View File

@@ -67,16 +67,16 @@ public:
return socket_->next_layer();
}
beast::IP::Endpoint
beast::ip::Endpoint
localEndpoint()
{
return beast::IP::fromAsio(lowestLayer().local_endpoint());
return beast::ip::fromAsio(lowestLayer().local_endpoint());
}
beast::IP::Endpoint
beast::ip::Endpoint
remoteEndpoint()
{
return beast::IP::fromAsio(lowestLayer().remote_endpoint());
return beast::ip::fromAsio(lowestLayer().remote_endpoint());
}
lowest_layer_type&

View File

@@ -9,7 +9,7 @@
#include <string>
#include <string_view>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
struct PeerLimitConfig
{
@@ -28,7 +28,7 @@ struct Config
* This includes both inbound and outbound, but does not include
* fixed peers.
*/
std::size_t maxPeers{Tuning::kDefaultMaxPeers};
std::size_t maxPeers{tuning::kDefaultMaxPeers};
/**
* The number of automatic outbound connections to maintain.
@@ -100,7 +100,7 @@ struct Config
onWrite(beast::PropertyStream::Map& map) const;
/**
* Make PeerFinder::Config from peer limit and server mode parameters.
* Make peer_finder::Config from peer limit and server mode parameters.
*/
static Config
makeConfig(
@@ -160,4 +160,4 @@ to_string(Result result) noexcept
return "unknown";
}
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -15,7 +15,7 @@
#include <utility>
#include <vector>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
/**
* Maintains a set of IP addresses used for getting into the network.
@@ -68,17 +68,17 @@ public:
* file, along with the set of corresponding IP addresses.
*/
virtual void
addFixedPeer(std::string_view name, std::vector<beast::IP::Endpoint> const& addresses) = 0;
addFixedPeer(std::string_view name, std::vector<beast::ip::Endpoint> const& addresses) = 0;
/**
* Add a set of strings as fallback IP::Endpoint sources.
* Add a set of strings as fallback ip::Endpoint sources.
* @param name A label used for diagnostics.
*/
virtual void
addFallbackStrings(std::string const& name, std::vector<std::string> const& strings) = 0;
/**
* Add a URL as a fallback location to obtain IP::Endpoint sources.
* Add a URL as a fallback location to obtain ip::Endpoint sources.
* @param name A label used for diagnostics.
*/
/* VFALCO NOTE Unimplemented
@@ -95,8 +95,8 @@ public:
*/
virtual std::pair<std::shared_ptr<Slot>, Result>
newInboundSlot(
beast::IP::Endpoint const& localEndpoint,
beast::IP::Endpoint const& remoteEndpoint) = 0;
beast::ip::Endpoint const& localEndpoint,
beast::ip::Endpoint const& remoteEndpoint) = 0;
/**
* Create a new outbound slot with the specified remote endpoint.
@@ -104,7 +104,7 @@ public:
* Usually this is because of a duplicate connection.
*/
virtual std::pair<std::shared_ptr<Slot>, Result>
newOutboundSlot(beast::IP::Endpoint const& remoteEndpoint) = 0;
newOutboundSlot(beast::ip::Endpoint const& remoteEndpoint) = 0;
/**
* Called when mtENDPOINTS is received.
@@ -145,7 +145,7 @@ public:
* @return `true` if the connection should be kept
*/
virtual bool
onConnected(std::shared_ptr<Slot> const& slot, beast::IP::Endpoint const& localEndpoint) = 0;
onConnected(std::shared_ptr<Slot> const& slot, beast::ip::Endpoint const& localEndpoint) = 0;
/**
* Request an active slot type.
@@ -162,7 +162,7 @@ public:
/**
* Return a set of addresses we should connect to.
*/
virtual std::vector<beast::IP::Endpoint>
virtual std::vector<beast::ip::Endpoint>
autoconnect() = 0;
virtual std::vector<std::pair<std::shared_ptr<Slot>, std::vector<Endpoint>>>
@@ -176,4 +176,4 @@ public:
oncePerSecond() = 0;
};
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -7,7 +7,7 @@
#include <memory>
#include <optional>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
/**
* Properties and state associated with a peer to peer overlay connection.
@@ -52,13 +52,13 @@ public:
/**
* The remote endpoint of socket.
*/
[[nodiscard]] virtual beast::IP::Endpoint const&
[[nodiscard]] virtual beast::ip::Endpoint const&
remoteEndpoint() const = 0;
/**
* The local endpoint of the socket, when known.
*/
[[nodiscard]] virtual std::optional<beast::IP::Endpoint> const&
[[nodiscard]] virtual std::optional<beast::ip::Endpoint> const&
localEndpoint() const = 0;
[[nodiscard]] virtual std::optional<std::uint16_t>
@@ -72,4 +72,4 @@ public:
publicKey() const = 0;
};
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -8,14 +8,14 @@
#include <cstdint>
#include <vector>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
using clock_type = beast::AbstractClock<std::chrono::steady_clock>;
/**
* Represents a set of addresses.
*/
using IPAddresses = std::vector<beast::IP::Endpoint>;
using IPAddresses = std::vector<beast::ip::Endpoint>;
//------------------------------------------------------------------------------
@@ -26,10 +26,10 @@ struct Endpoint
{
Endpoint() = default;
Endpoint(beast::IP::Endpoint ep, std::uint32_t hops);
Endpoint(beast::ip::Endpoint ep, std::uint32_t hops);
std::uint32_t hops = 0;
beast::IP::Endpoint address;
beast::ip::Endpoint address;
};
inline bool
@@ -43,4 +43,4 @@ operator<(Endpoint const& lhs, Endpoint const& rhs)
*/
using Endpoints = std::vector<Endpoint>;
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -14,7 +14,7 @@
#include <functional>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
/**
* Stores IP addresses useful for gaining initial connections.
@@ -65,7 +65,7 @@ private:
};
using left_t = boost::bimaps::
unordered_set_of<beast::IP::Endpoint, boost::hash<beast::IP::Endpoint>, std::equal_to<>>;
unordered_set_of<beast::ip::Endpoint, boost::hash<beast::ip::Endpoint>, std::equal_to<>>;
using right_t = boost::bimaps::multiset_of<Entry, std::less<>>;
using map_type = boost::bimap<left_t, right_t>;
using value_type = map_type::value_type;
@@ -73,11 +73,11 @@ private:
struct Transform
{
using first_argument_type = map_type::right_map::const_iterator::value_type const&;
using result_type = beast::IP::Endpoint const&;
using result_type = beast::ip::Endpoint const&;
explicit Transform() = default;
beast::IP::Endpoint const&
beast::ip::Endpoint const&
operator()(map_type::right_map::const_iterator::value_type const& v) const
{
return v.get_left();
@@ -121,7 +121,7 @@ public:
size() const;
/**
* IP::Endpoint iterators that traverse in decreasing valence.
* ip::Endpoint iterators that traverse in decreasing valence.
*/
/** @{ */
[[nodiscard]] const_iterator
@@ -146,25 +146,25 @@ public:
* Add a newly-learned address to the cache.
*/
bool
insert(beast::IP::Endpoint const& endpoint);
insert(beast::ip::Endpoint const& endpoint);
/**
* Add a staticallyconfigured address to the cache.
*/
bool
insertStatic(beast::IP::Endpoint const& endpoint);
insertStatic(beast::ip::Endpoint const& endpoint);
/**
* Called when an outbound connection handshake completes.
*/
void
onSuccess(beast::IP::Endpoint const& endpoint);
onSuccess(beast::ip::Endpoint const& endpoint);
/**
* Called when an outbound connection attempt fails to handshake.
*/
void
onFailure(beast::IP::Endpoint const& endpoint);
onFailure(beast::ip::Endpoint const& endpoint);
/**
* Stores the cache in the persistent database on a timer.
@@ -189,4 +189,4 @@ private:
flagForUpdate();
};
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -11,7 +11,7 @@
#include <memory>
#include <mutex>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
/**
* Tests remote listening sockets to make sure they are connectable.
@@ -104,7 +104,7 @@ public:
*/
template <class Handler>
void
asyncConnect(beast::IP::Endpoint const& endpoint, Handler&& handler);
asyncConnect(beast::ip::Endpoint const& endpoint, Handler&& handler);
private:
void
@@ -179,7 +179,7 @@ Checker<Protocol>::wait()
template <class Protocol>
template <class Handler>
void
Checker<Protocol>::asyncConnect(beast::IP::Endpoint const& endpoint, Handler&& handler)
Checker<Protocol>::asyncConnect(beast::ip::Endpoint const& endpoint, Handler&& handler)
{
auto const op =
std::make_shared<AsyncOp<Handler>>(*this, ioContext_, std::forward<Handler>(handler));
@@ -202,4 +202,4 @@ Checker<Protocol>::remove(BasicAsyncOp& op)
cond_.notify_all();
}
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -10,7 +10,7 @@
#include <sstream>
#include <string>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
/**
* Direction of a slot count adjustment.
@@ -50,7 +50,7 @@ public:
// Must be handshaked and in the right state
XRPL_ASSERT(
s.state() == Slot::State::Connected || s.state() == Slot::State::Accept,
"xrpl::PeerFinder::Counts::can_activate : valid input state");
"xrpl::peer_finder::Counts::can_activate : valid input state");
if (s.fixed() || s.reserved())
return true;
@@ -67,9 +67,9 @@ public:
[[nodiscard]] std::size_t
attemptsNeeded() const
{
if (attempts_ >= Tuning::kMaxConnectAttempts)
if (attempts_ >= tuning::kMaxConnectAttempts)
return 0;
return Tuning::kMaxConnectAttempts - attempts_;
return tuning::kMaxConnectAttempts - attempts_;
}
/**
@@ -295,7 +295,7 @@ private:
switch (s.state())
{
case Slot::State::Accept:
XRPL_ASSERT(s.inbound(), "xrpl::PeerFinder::Counts::adjust : input is inbound");
XRPL_ASSERT(s.inbound(), "xrpl::peer_finder::Counts::adjust : input is inbound");
acceptCount_ += n;
break;
@@ -303,7 +303,7 @@ private:
case Slot::State::Connected:
XRPL_ASSERT(
!s.inbound(),
"xrpl::PeerFinder::Counts::adjust : input is not "
"xrpl::peer_finder::Counts::adjust : input is not "
"inbound");
attempts_ += n;
break;
@@ -331,7 +331,7 @@ private:
// LCOV_EXCL_START
default:
UNREACHABLE("xrpl::PeerFinder::Counts::adjust : invalid input state");
UNREACHABLE("xrpl::peer_finder::Counts::adjust : invalid input state");
break;
// LCOV_EXCL_STOP
};
@@ -391,4 +391,4 @@ private:
int closingCount_{0};
};
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -7,7 +7,7 @@
#include <chrono>
#include <cstddef>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
/**
* Metadata for a Fixed slot.
@@ -36,8 +36,8 @@ public:
void
failure(clock_type::time_point const& now)
{
failures_ = std::min(failures_ + 1, Tuning::kConnectionBackoff.size() - 1);
when_ = now + std::chrono::minutes(Tuning::kConnectionBackoff[failures_]);
failures_ = std::min(failures_ + 1, tuning::kConnectionBackoff.size() - 1);
when_ = now + std::chrono::minutes(tuning::kConnectionBackoff[failures_]);
}
/**
@@ -55,4 +55,4 @@ private:
std::size_t failures_{0};
};
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -12,7 +12,7 @@
#include <utility>
#include <vector>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
namespace detail {
@@ -28,7 +28,7 @@ template <class Target, class HopContainer>
std::size_t
handoutOne(Target& t, HopContainer& h)
{
XRPL_ASSERT(!t.full(), "xrpl::PeerFinder::detail::handoutOne : target is not full");
XRPL_ASSERT(!t.full(), "xrpl::peer_finder::detail::handoutOne : target is not full");
for (auto it = h.begin(); it != h.end(); ++it)
{
auto const& e = *it;
@@ -95,7 +95,7 @@ public:
[[nodiscard]] bool
full() const
{
return list_.size() >= Tuning::kRedirectEndpointCount;
return list_.size() >= tuning::kRedirectEndpointCount;
}
[[nodiscard]] SlotImp::ptr const&
@@ -124,7 +124,7 @@ private:
template <class>
RedirectHandouts::RedirectHandouts(SlotImp::ptr slot) : slot_(std::move(slot))
{
list_.reserve(Tuning::kRedirectEndpointCount);
list_.reserve(tuning::kRedirectEndpointCount);
}
template <class>
@@ -138,7 +138,7 @@ RedirectHandouts::tryInsert(Endpoint const& ep)
// addresses in a peer HTTP handshake instead of
// the tmENDPOINTS message.
//
if (ep.hops > Tuning::kMaxHops)
if (ep.hops > tuning::kMaxHops)
return false;
// Don't send them our address
@@ -181,7 +181,7 @@ public:
[[nodiscard]] bool
full() const
{
return list_.size() >= Tuning::kNumberOfEndpoints;
return list_.size() >= tuning::kNumberOfEndpoints;
}
void
@@ -210,7 +210,7 @@ private:
template <class>
SlotHandouts::SlotHandouts(SlotImp::ptr slot) : slot_(std::move(slot))
{
list_.reserve(Tuning::kNumberOfEndpoints);
list_.reserve(tuning::kNumberOfEndpoints);
}
template <class>
@@ -220,7 +220,7 @@ SlotHandouts::tryInsert(Endpoint const& ep)
if (full())
return false;
if (ep.hops > Tuning::kMaxHops)
if (ep.hops > tuning::kMaxHops)
return false;
if (slot_->recent.filter(ep.address, ep.hops))
@@ -259,9 +259,9 @@ class ConnectHandouts
public:
// Keeps track of addresses we have made outgoing connections
// to, for the purposes of not connecting to them too frequently.
using Squelches = beast::aged_set<beast::IP::Address>;
using Squelches = beast::aged_set<beast::ip::Address>;
using list_type = std::vector<beast::IP::Endpoint>;
using list_type = std::vector<beast::ip::Endpoint>;
private:
std::size_t needed_;
@@ -274,7 +274,7 @@ public:
template <class = void>
bool
tryInsert(beast::IP::Endpoint const& endpoint);
tryInsert(beast::ip::Endpoint const& endpoint);
[[nodiscard]] bool
empty() const
@@ -316,13 +316,13 @@ ConnectHandouts::ConnectHandouts(std::size_t needed, Squelches& squelches)
template <class>
bool
ConnectHandouts::tryInsert(beast::IP::Endpoint const& endpoint)
ConnectHandouts::tryInsert(beast::ip::Endpoint const& endpoint)
{
if (full())
return false;
// Make sure the address isn't already in our list
if (std::ranges::any_of(list_, [&endpoint](beast::IP::Endpoint const& other) {
if (std::ranges::any_of(list_, [&endpoint](beast::ip::Endpoint const& other) {
// Ignore port for security reasons
return other.address() == endpoint.address();
}))
@@ -341,4 +341,4 @@ ConnectHandouts::tryInsert(beast::IP::Endpoint const& endpoint)
return true;
}
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -29,7 +29,7 @@
#include <utility>
#include <vector>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
template <class>
class Livecache;
@@ -188,10 +188,10 @@ class Livecache : protected detail::LivecacheBase
{
private:
using cache_type = beast::aged_map<
beast::IP::Endpoint,
beast::ip::Endpoint,
Element,
std::chrono::steady_clock,
std::less<beast::IP::Endpoint>,
std::less<beast::ip::Endpoint>,
Allocator>;
beast::Journal journal_;
@@ -220,8 +220,8 @@ public:
// but not given out (since they would exceed maxHops). They
// are used for automatic connection attempts.
//
using Histogram = std::array<int, 1 + Tuning::kMaxHops + 1>;
using lists_type = std::array<list_type, 1 + Tuning::kMaxHops + 1>;
using Histogram = std::array<int, 1 + tuning::kMaxHops + 1>;
using lists_type = std::array<list_type, 1 + tuning::kMaxHops + 1>;
template <bool IsConst>
struct Transform
@@ -400,7 +400,7 @@ Livecache<Allocator>::expire()
{
std::size_t n(0);
typename cache_type::time_point const expired(
cache_.clock().now() - Tuning::kLiveCacheSecondsToLive);
cache_.clock().now() - tuning::kLiveCacheSecondsToLive);
for (auto iter(cache_.chronological.begin());
iter != cache_.chronological.end() && iter.when() <= expired;)
{
@@ -427,8 +427,8 @@ Livecache<Allocator>::insert(Endpoint const& ep)
// when redirecting.
//
XRPL_ASSERT(
ep.hops <= (Tuning::kMaxHops + 1),
"xrpl::PeerFinder::Livecache::insert : maximum input hops");
ep.hops <= (tuning::kMaxHops + 1),
"xrpl::peer_finder::Livecache::insert : maximum input hops");
auto result = cache_.emplace(ep.address, ep);
Element& e(result.first->second);
if (result.second)
@@ -468,7 +468,7 @@ void
Livecache<Allocator>::onWrite(beast::PropertyStream::Map& map)
{
typename cache_type::time_point const expired(
cache_.clock().now() - Tuning::kLiveCacheSecondsToLive);
cache_.clock().now() - tuning::kLiveCacheSecondsToLive);
map["size"] = size();
map["hist"] = hops.histogram();
beast::PropertyStream::Set set("entries", map);
@@ -527,8 +527,8 @@ void
Livecache<Allocator>::HopsT::insert(Element& e)
{
XRPL_ASSERT(
e.endpoint.hops <= Tuning::kMaxHops + 1,
"xrpl::PeerFinder::Livecache::HopsT::insert : maximum input hops");
e.endpoint.hops <= tuning::kMaxHops + 1,
"xrpl::peer_finder::Livecache::HopsT::insert : maximum input hops");
// This has security implications without a shuffle
lists_[e.endpoint.hops].push_front(e);
++hist_[e.endpoint.hops];
@@ -539,8 +539,8 @@ void
Livecache<Allocator>::HopsT::reinsert(Element& e, std::uint32_t numHops)
{
XRPL_ASSERT(
numHops <= Tuning::kMaxHops + 1,
"xrpl::PeerFinder::Livecache::HopsT::reinsert : maximum hops input");
numHops <= tuning::kMaxHops + 1,
"xrpl::peer_finder::Livecache::HopsT::reinsert : maximum hops input");
auto& list = lists_[e.endpoint.hops];
list.erase(list.iterator_to(e));
@@ -561,4 +561,4 @@ Livecache<Allocator>::HopsT::remove(Element& e)
list.erase(list.iterator_to(e));
}
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -43,7 +43,7 @@
#include <utility>
#include <vector>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
/**
* The Logic for maintaining the list of Slot addresses.
@@ -57,7 +57,7 @@ public:
// Maps remote endpoints to slots. Since a slot has a
// remote endpoint upon construction, this holds all counts_.
//
using Slots = std::map<beast::IP::Endpoint, std::shared_ptr<SlotImp>>;
using Slots = std::map<beast::ip::Endpoint, std::shared_ptr<SlotImp>>;
beast::Journal journal;
clock_type& clock;
@@ -81,7 +81,7 @@ private:
Counts counts_;
// A list of slots that should always be connected
std::map<beast::IP::Endpoint, Fixed> fixed_;
std::map<beast::ip::Endpoint, Fixed> fixed_;
public:
// Live livecache from mtENDPOINTS messages
@@ -96,7 +96,7 @@ public:
// The addresses (but not port) we are connected to. This includes
// outgoing connection attempts. Note that this set can contain
// duplicates (since the port is not set)
std::multiset<beast::IP::Address> connectedAddresses;
std::multiset<beast::ip::Address> connectedAddresses;
// Set of public keys belonging to active peers
std::set<PublicKey> keys;
@@ -170,13 +170,13 @@ public:
}
void
addFixedPeer(std::string_view name, beast::IP::Endpoint const& ep)
addFixedPeer(std::string_view name, beast::ip::Endpoint const& ep)
{
addFixedPeer(name, std::vector<beast::IP::Endpoint>{ep});
addFixedPeer(name, std::vector<beast::ip::Endpoint>{ep});
}
void
addFixedPeer(std::string_view name, std::vector<beast::IP::Endpoint> const& addresses)
addFixedPeer(std::string_view name, std::vector<beast::ip::Endpoint> const& addresses)
{
std::scoped_lock const _(lock);
@@ -213,8 +213,8 @@ public:
// Called when the Checker completes a connectivity test
void
checkComplete(
beast::IP::Endpoint const& remoteAddress,
beast::IP::Endpoint const& checkedAddress,
beast::ip::Endpoint const& remoteAddress,
beast::ip::Endpoint const& checkedAddress,
boost::system::error_code ec)
{
if (ec == boost::asio::error::operation_aborted)
@@ -256,8 +256,8 @@ public:
std::pair<SlotImp::ptr, Result>
newInboundSlot(
beast::IP::Endpoint const& localEndpoint,
beast::IP::Endpoint const& remoteEndpoint)
beast::ip::Endpoint const& localEndpoint,
beast::ip::Endpoint const& remoteEndpoint)
{
JLOG(journal.debug()) << std::left << std::setw(18) << "Logic accept" << remoteEndpoint
<< " on local " << localEndpoint;
@@ -293,7 +293,7 @@ public:
// Remote address must not already exist
XRPL_ASSERT(
result.second,
"xrpl::PeerFinder::Logic::new_inbound_slot : remote endpoint "
"xrpl::peer_finder::Logic::new_inbound_slot : remote endpoint "
"inserted");
// Add to the connected address list
connectedAddresses.emplace(remoteEndpoint.address());
@@ -306,7 +306,7 @@ public:
// Can't check for self-connect because we don't know the local endpoint
std::pair<SlotImp::ptr, Result>
newOutboundSlot(beast::IP::Endpoint const& remoteEndpoint)
newOutboundSlot(beast::ip::Endpoint const& remoteEndpoint)
{
JLOG(journal.debug()) << std::left << std::setw(18) << "Logic connect " << remoteEndpoint;
@@ -329,7 +329,7 @@ public:
// Remote address must not already exist
XRPL_ASSERT(
result.second,
"xrpl::PeerFinder::Logic::new_outbound_slot : remote endpoint "
"xrpl::peer_finder::Logic::new_outbound_slot : remote endpoint "
"inserted");
// Add to the connected address list
@@ -342,7 +342,7 @@ public:
}
bool
onConnected(SlotImp::ptr const& slot, beast::IP::Endpoint const& localEndpoint)
onConnected(SlotImp::ptr const& slot, beast::ip::Endpoint const& localEndpoint)
{
beast::WrappedSink sink{journal.sink(), slot->prefix()};
beast::Journal const journal{sink};
@@ -354,7 +354,7 @@ public:
// The object must exist in our table
XRPL_ASSERT(
slots.contains(slot->remoteEndpoint()),
"xrpl::PeerFinder::Logic::onConnected : valid slot input");
"xrpl::peer_finder::Logic::onConnected : valid slot input");
// Assign the local endpoint now that it's known
slot->localEndpoint(localEndpoint);
@@ -365,7 +365,7 @@ public:
{
XRPL_ASSERT(
iter->second->localEndpoint() == slot->remoteEndpoint(),
"xrpl::PeerFinder::Logic::onConnected : local and remote "
"xrpl::peer_finder::Logic::onConnected : local and remote "
"endpoints do match");
JLOG(journal.warn()) << "Logic dropping as self connect";
return false;
@@ -393,11 +393,11 @@ public:
// The object must exist in our table
XRPL_ASSERT(
slots.contains(slot->remoteEndpoint()),
"xrpl::PeerFinder::Logic::activate : valid slot input");
"xrpl::peer_finder::Logic::activate : valid slot input");
// Must be accepted or connected
XRPL_ASSERT(
slot->state() == Slot::State::Accept || slot->state() == Slot::State::Connected,
"xrpl::PeerFinder::Logic::activate : valid slot state");
"xrpl::peer_finder::Logic::activate : valid slot state");
// Check for duplicate connection by key
if (keys.contains(key))
@@ -425,7 +425,7 @@ public:
{
[[maybe_unused]] bool const inserted = keys.insert(key).second;
// Public key must not already exist
XRPL_ASSERT(inserted, "xrpl::PeerFinder::Logic::activate : public key inserted");
XRPL_ASSERT(inserted, "xrpl::peer_finder::Logic::activate : public key inserted");
}
// Change state and update counts
@@ -443,7 +443,7 @@ public:
if (iter == fixed_.end())
{
logicError(
"PeerFinder::Logic::activate(): remote_endpoint "
"peer_finder::Logic::activate(): remote_endpoint "
"missing from fixed_");
}
@@ -476,10 +476,10 @@ public:
// VFALCO TODO This should add the returned addresses to the
// squelch list in one go once the list is built,
// rather than having each module add to the squelch list.
std::vector<beast::IP::Endpoint>
std::vector<beast::ip::Endpoint>
autoconnect()
{
std::vector<beast::IP::Endpoint> none;
std::vector<beast::ip::Endpoint> none;
std::scoped_lock const _(lock);
@@ -635,7 +635,7 @@ public:
// either. ipv6 has a slightly more compact string
// representation of 0, so use that for self entries.
ep.address =
beast::IP::Endpoint(beast::IP::AddressV6()).atPort(config_.listeningPort);
beast::ip::Endpoint(beast::ip::AddressV6()).atPort(config_.listeningPort);
for (auto& t : targets)
t.insert(ep);
}
@@ -656,7 +656,7 @@ public:
result.emplace_back(slot, list);
}
whenBroadcast = now + Tuning::kSecondsPerMessage;
whenBroadcast = now + tuning::kSecondsPerMessage;
}
return result;
@@ -675,7 +675,7 @@ public:
entry.second->expire();
// Expire the recent attempts table
beast::expire(squelches, Tuning::kRecentAttemptDuration);
beast::expire(squelches, tuning::kRecentAttemptDuration);
bootcache.periodicActivity();
}
@@ -692,7 +692,7 @@ public:
Endpoint& ep(*iter);
// Enforce hop limit
if (ep.hops > Tuning::kMaxHops)
if (ep.hops > tuning::kMaxHops)
{
JLOG(journal.debug()) << std::left << std::setw(18) << "Endpoints drop "
<< ep.address << " for excess hops " << ep.hops;
@@ -754,10 +754,10 @@ public:
beast::Journal const journal{sink};
// If we're sent too many endpoints, sample them at random:
if (list.size() > Tuning::kNumberOfEndpointsMax)
if (list.size() > tuning::kNumberOfEndpointsMax)
{
std::shuffle(list.begin(), list.end(), defaultPrng());
list.resize(Tuning::kNumberOfEndpointsMax);
list.resize(tuning::kNumberOfEndpointsMax);
}
JLOG(journal.trace()) << "Endpoints contained " << list.size()
@@ -768,12 +768,12 @@ public:
// The object must exist in our table
XRPL_ASSERT(
slots.contains(slot->remoteEndpoint()),
"xrpl::PeerFinder::Logic::onEndpoints : valid slot input");
"xrpl::peer_finder::Logic::onEndpoints : valid slot input");
// Must be handshaked!
XRPL_ASSERT(
slot->state() == Slot::State::Active,
"xrpl::PeerFinder::Logic::onEndpoints : valid slot state");
"xrpl::peer_finder::Logic::onEndpoints : valid slot state");
clock_type::time_point const now(clock.now());
@@ -785,7 +785,7 @@ public:
for (auto const& ep : list)
{
XRPL_ASSERT(ep.hops, "xrpl::PeerFinder::Logic::onEndpoints : nonzero hops");
XRPL_ASSERT(ep.hops, "xrpl::peer_finder::Logic::onEndpoints : nonzero hops");
slot->recent.insert(ep.address, ep.hops);
@@ -837,7 +837,7 @@ public:
bootcache.insert(ep.address);
}
slot->whenAcceptEndpoints = now + Tuning::kSecondsPerMessage;
slot->whenAcceptEndpoints = now + tuning::kSecondsPerMessage;
}
//--------------------------------------------------------------------------
@@ -851,7 +851,7 @@ public:
if (iter == slots.end())
{
logicError(
"PeerFinder::Logic::remove(): remote_endpoint "
"peer_finder::Logic::remove(): remote_endpoint "
"missing from slots_");
}
@@ -866,7 +866,7 @@ public:
if (iter == keys.end())
{
logicError(
"PeerFinder::Logic::remove(): public_key missing "
"peer_finder::Logic::remove(): public_key missing "
"from keys_");
}
@@ -879,7 +879,7 @@ public:
if (iter == connectedAddresses.end())
{
logicError(
"PeerFinder::Logic::remove(): remote_endpoint "
"peer_finder::Logic::remove(): remote_endpoint "
"address missing from connectedAddresses_");
}
@@ -907,7 +907,7 @@ public:
if (iter == fixed_.end())
{
logicError(
"PeerFinder::Logic::on_closed(): remote_endpoint "
"peer_finder::Logic::on_closed(): remote_endpoint "
"missing from fixed_");
}
@@ -943,7 +943,7 @@ public:
// LCOV_EXCL_START
default:
UNREACHABLE(
"xrpl::PeerFinder::Logic::on_closed : invalid slot "
"xrpl::peer_finder::Logic::on_closed : invalid slot "
"state");
break;
// LCOV_EXCL_STOP
@@ -968,17 +968,17 @@ public:
// Returns `true` if the address matches a fixed slot address
// Must have the lock held
bool
fixed(beast::IP::Endpoint const& endpoint) const
fixed(beast::ip::Endpoint const& endpoint) const
{
return std::ranges::any_of(
fixed_, [&endpoint](auto const& entry) { return entry.first == endpoint; });
}
// Returns `true` if the address matches a fixed slot address
// Note that this does not use the port information in the IP::Endpoint
// Note that this does not use the port information in the ip::Endpoint
// Must have the lock held
bool
fixed(beast::IP::Address const& address) const
fixed(beast::ip::Address const& address) const
{
return std::ranges::any_of(
fixed_, [&address](auto const& entry) { return entry.first.address() == address; });
@@ -1097,9 +1097,9 @@ public:
//
//--------------------------------------------------------------------------
// Returns true if the IP::Endpoint contains no invalid data.
// Returns true if the ip::Endpoint contains no invalid data.
bool
isValidAddress(beast::IP::Endpoint const& address)
isValidAddress(beast::ip::Endpoint const& address)
{
if (isUnspecified(address))
return false;
@@ -1220,7 +1220,7 @@ Logic<Checker>::onRedirects(
{
std::scoped_lock const _(lock);
std::size_t n = 0;
for (; first != last && n < Tuning::kMaxRedirects; ++first, ++n)
for (; first != last && n < tuning::kMaxRedirects; ++first, ++n)
bootcache.insert(beast::IPAddressConversion::fromAsio(*first));
if (n > 0)
{
@@ -1229,4 +1229,4 @@ Logic<Checker>::onRedirects(
}
}
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -12,7 +12,7 @@
#include <optional>
#include <string>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
class SlotImp : public Slot
{
@@ -21,13 +21,13 @@ public:
// inbound
SlotImp(
beast::IP::Endpoint const& localEndpoint,
beast::IP::Endpoint remoteEndpoint,
beast::ip::Endpoint const& localEndpoint,
beast::ip::Endpoint remoteEndpoint,
bool fixed,
clock_type& clock);
// outbound
SlotImp(beast::IP::Endpoint remoteEndpoint, bool fixed, clock_type& clock);
SlotImp(beast::ip::Endpoint remoteEndpoint, bool fixed, clock_type& clock);
bool
inbound() const override
@@ -53,13 +53,13 @@ public:
return state_;
}
beast::IP::Endpoint const&
beast::ip::Endpoint const&
remoteEndpoint() const override
{
return remoteEndpoint_;
}
std::optional<beast::IP::Endpoint> const&
std::optional<beast::ip::Endpoint> const&
localEndpoint() const override
{
return localEndpoint_;
@@ -93,13 +93,13 @@ public:
}
void
localEndpoint(beast::IP::Endpoint const& endpoint)
localEndpoint(beast::ip::Endpoint const& endpoint)
{
localEndpoint_ = endpoint;
}
void
remoteEndpoint(beast::IP::Endpoint const& endpoint)
remoteEndpoint(beast::ip::Endpoint const& endpoint)
{
remoteEndpoint_ = endpoint;
}
@@ -140,20 +140,20 @@ public:
* sending a slot the same address too frequently.
*/
void
insert(beast::IP::Endpoint const& ep, std::uint32_t hops);
insert(beast::ip::Endpoint const& ep, std::uint32_t hops);
/**
* Returns `true` if we should not send endpoint to the slot.
*/
bool
filter(beast::IP::Endpoint const& ep, std::uint32_t hops);
filter(beast::ip::Endpoint const& ep, std::uint32_t hops);
private:
void
expire();
friend class SlotImp;
beast::aged_unordered_map<beast::IP::Endpoint, std::uint32_t> cache_;
beast::aged_unordered_map<beast::ip::Endpoint, std::uint32_t> cache_;
} recent;
void
@@ -167,8 +167,8 @@ private:
bool const fixed_;
bool reserved_;
State state_;
beast::IP::Endpoint remoteEndpoint_;
std::optional<beast::IP::Endpoint> localEndpoint_;
beast::ip::Endpoint remoteEndpoint_;
std::optional<beast::ip::Endpoint> localEndpoint_;
std::optional<PublicKey> publicKey_;
static std::int32_t constexpr kUnknownPort = -1;
@@ -196,4 +196,4 @@ public:
clock_type::time_point whenAcceptEndpoints;
};
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -7,7 +7,7 @@
#include <string>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
/**
* A static or dynamic source of peer addresses.
@@ -46,4 +46,4 @@ public:
fetch(Results& results, beast::Journal journal) = 0;
};
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -6,7 +6,7 @@
#include <string>
#include <vector>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
/**
* Provides addresses from a static set of strings.
@@ -22,4 +22,4 @@ public:
make(std::string const& name, Strings const& strings);
};
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -6,7 +6,7 @@
#include <functional>
#include <vector>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
/**
* Abstract persistence for PeerFinder data.
@@ -17,7 +17,7 @@ public:
virtual ~Store() = default;
// load the bootstrap cache
using load_callback = std::function<void(beast::IP::Endpoint, int)>;
using load_callback = std::function<void(beast::ip::Endpoint, int)>;
virtual std::size_t
load(load_callback const& cb) = 0;
@@ -26,11 +26,11 @@ public:
{
explicit Entry() = default;
beast::IP::Endpoint endpoint;
beast::ip::Endpoint endpoint;
int valence{};
};
virtual void
save(std::vector<Entry> const& v) = 0;
};
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -9,7 +9,7 @@
* Heuristically tuned constants.
*/
/** @{ */
namespace xrpl::PeerFinder::Tuning {
namespace xrpl::peer_finder::tuning {
//---------------------------------------------------------
//
@@ -111,5 +111,5 @@ constexpr std::chrono::seconds kLiveCacheSecondsToLive(30);
// Note that we ignore the port for purposes of comparison.
constexpr std::chrono::seconds kRecentAttemptDuration(60);
} // namespace xrpl::PeerFinder::Tuning
} // namespace xrpl::peer_finder::tuning
/** @} */

View File

@@ -10,7 +10,7 @@
#include <memory>
namespace xrpl::PeerFinder {
namespace xrpl::peer_finder {
/**
* @brief Create a new Manager.
@@ -33,4 +33,4 @@ makeManager(
Store& store,
beast::insight::Collector::ptr const& collector);
} // namespace xrpl::PeerFinder
} // namespace xrpl::peer_finder

View File

@@ -246,7 +246,15 @@ message TMGetObjectByHash {
message TMLedgerNode {
required bytes nodedata = 1;
optional bytes nodeid = 2; // missing for ledger base data
// Used when protocol version <2.3. Not set for ledger base data.
optional bytes nodeid = 2;
// Used when protocol version >=2.3. Neither value is set for ledger base data.
oneof reference {
bytes id = 3; // Set for inner nodes.
uint32 depth = 4; // Set for leaf nodes.
}
}
enum TMLedgerInfoType {

View File

@@ -33,7 +33,7 @@ namespace xrpl {
* Command line Requests use apiCommandLineVersion.
*/
namespace RPC {
namespace rpc {
template <unsigned int Version>
static constexpr std::integral_constant<unsigned, Version> kApiVersion = {};
@@ -60,7 +60,7 @@ static_assert(kApiMaximumValidVersion >= kApiMaximumSupportedVersion);
inline void
setVersion(json::Value& parent, unsigned int apiVersion, bool betaEnabled)
{
XRPL_ASSERT(apiVersion != kApiInvalidVersion, "xrpl::RPC::setVersion : input is valid");
XRPL_ASSERT(apiVersion != kApiInvalidVersion, "xrpl::rpc::setVersion : input is valid");
auto& retObj = parent[jss::version] = json::ValueType::Object;
@@ -99,12 +99,12 @@ setVersion(json::Value& parent, unsigned int apiVersion, bool betaEnabled)
inline unsigned int
getAPIVersionNumber(json::Value const& jv, bool betaEnabled)
{
static json::Value const kMinVersion(RPC::kApiMinimumSupportedVersion);
static json::Value const kMinVersion(rpc::kApiMinimumSupportedVersion);
json::Value const maxVersion(
betaEnabled ? RPC::kApiBetaVersion : RPC::kApiMaximumSupportedVersion);
betaEnabled ? rpc::kApiBetaVersion : rpc::kApiMaximumSupportedVersion);
if (!jv.isObject() || !jv.isMember(jss::api_version))
return RPC::kApiVersionIfUnspecified;
return rpc::kApiVersionIfUnspecified;
try
{
@@ -113,33 +113,33 @@ getAPIVersionNumber(json::Value const& jv, bool betaEnabled)
{
case json::ValueType::Int:
if (rawVersion.asInt() < 0)
return RPC::kApiInvalidVersion;
return rpc::kApiInvalidVersion;
[[fallthrough]];
case json::ValueType::UInt: {
auto const apiVersion = rawVersion.asUInt();
if (apiVersion < kMinVersion || apiVersion > maxVersion)
return RPC::kApiInvalidVersion;
return rpc::kApiInvalidVersion;
return apiVersion;
}
default:
return RPC::kApiInvalidVersion;
return rpc::kApiInvalidVersion;
}
}
catch (...)
{
return RPC::kApiInvalidVersion;
return rpc::kApiInvalidVersion;
}
}
} // namespace RPC
} // namespace rpc
template <unsigned MinVer, unsigned MaxVer, typename Fn, typename... Args>
void
forApiVersions(Fn const& fn, Args&&... args)
requires //
(MaxVer >= MinVer) && //
(MinVer >= RPC::kApiMinimumSupportedVersion) && //
(RPC::kApiMaximumValidVersion >= MaxVer) && requires {
(MinVer >= rpc::kApiMinimumSupportedVersion) && //
(rpc::kApiMaximumValidVersion >= MaxVer) && requires {
fn(std::integral_constant<unsigned int, MinVer>{}, std::forward<Args>(args)...);
fn(std::integral_constant<unsigned int, MaxVer>{}, std::forward<Args>(args)...);
}
@@ -158,11 +158,11 @@ template <typename Fn, typename... Args>
void
forAllApiVersions(Fn const& fn, Args&&... args)
requires requires {
forApiVersions<RPC::kApiMinimumSupportedVersion, RPC::kApiMaximumValidVersion>(
forApiVersions<rpc::kApiMinimumSupportedVersion, rpc::kApiMaximumValidVersion>(
fn, std::forward<Args>(args)...);
}
{
forApiVersions<RPC::kApiMinimumSupportedVersion, RPC::kApiMaximumValidVersion>(
forApiVersions<rpc::kApiMinimumSupportedVersion, rpc::kApiMaximumValidVersion>(
fn, std::forward<Args>(args)...);
}

View File

@@ -8,7 +8,7 @@
* Versioning information for this build.
*/
// VFALCO The namespace is deprecated
namespace xrpl::BuildInfo {
namespace xrpl::build_info {
/**
* Server version.
@@ -84,4 +84,4 @@ isXrpldVersion(std::uint64_t version);
bool
isNewerVersion(std::uint64_t version);
} // namespace xrpl::BuildInfo
} // namespace xrpl::build_info

View File

@@ -167,7 +167,7 @@ enum WarningCodeI {
// VFALCO NOTE these should probably not be in the RPC namespace.
namespace RPC {
namespace rpc {
/**
* Maps an rpc error code to its token, default message, and HTTP status.
@@ -337,7 +337,7 @@ containsError(json::Value const& json);
int
errorCodeHttpStatus(ErrorCodeI code);
} // namespace RPC
} // namespace rpc
/**
* Returns a single string with the contents of an RPC error.

View File

@@ -188,6 +188,6 @@ struct MultiApiJson
// Wrapper for Json for all supported API versions.
using MultiApiJson =
detail::MultiApiJson<RPC::kApiMinimumSupportedVersion, RPC::kApiMaximumValidVersion>;
detail::MultiApiJson<rpc::kApiMinimumSupportedVersion, rpc::kApiMaximumValidVersion>;
} // namespace xrpl

View File

@@ -6,7 +6,7 @@
#include <memory>
namespace xrpl::RPC {
namespace xrpl::rpc {
/**
* Adds common synthetic fields to transaction-related JSON responses
@@ -16,4 +16,4 @@ void
insertNFTSyntheticInJson(json::Value&, std::shared_ptr<STTx const> const&, TxMeta const&);
/** @} */
} // namespace xrpl::RPC
} // namespace xrpl::rpc

View File

@@ -139,7 +139,7 @@ tenthBipsOfValue(T value, TenthBips<TBips> bips)
return value * bips.value() / kTenthBipsPerUnity.value();
}
namespace Lending {
namespace lending {
/**
* The maximum management fee rate allowed by a loan broker in 1/10 bips.
*
@@ -236,7 +236,7 @@ static constexpr int kLoanPaymentsPerFeeIncrement = 5;
* without an amendment
*/
static constexpr int kLoanMaximumPaymentsPerTransaction = 100;
} // namespace Lending
} // namespace lending
/**
* The maximum length of a URI inside an NFT
@@ -316,6 +316,17 @@ constexpr std::uint8_t kVaultDefaultIouScale = 6;
*/
constexpr std::uint8_t kVaultMaximumIouScale = 18;
/**
* Vault ledger-entry schema versions. Assigned to newly created
* Vaults once featureLendingProtocolV1_1 is enabled. Vaults created before
* activation are left without LEVersion (implicit legacy version 0,
* accrual-basis accounting).
*/
enum class VaultVersion : uint8_t {
Legacy = 0,
CashBasis,
};
/**
* Maximum recursion depth for vault shares being put as an asset inside
* another vault; counted from 0

View File

@@ -260,7 +260,7 @@ calcAccountID(PublicKey const& pk);
inline std::string
getFingerprint(
beast::IP::Endpoint const& address,
beast::ip::Endpoint const& address,
std::optional<PublicKey> const& publicKey = std::nullopt,
std::optional<std::string> const& id = std::nullopt)
{

View File

@@ -20,7 +20,7 @@
namespace xrpl {
namespace Attestations {
namespace attestations {
struct AttestationBase
{
@@ -227,7 +227,7 @@ struct CmpByCreateCount
}
};
}; // namespace Attestations
}; // namespace attestations
// Result when checking when two attestation match.
enum class AttestationMatch {
@@ -241,7 +241,7 @@ enum class AttestationMatch {
struct XChainClaimAttestation
{
using TSignedAttestation = Attestations::AttestationClaim;
using TSignedAttestation = attestations::AttestationClaim;
static SField const& arrayFieldName;
AccountID keyAccount;
@@ -297,7 +297,7 @@ struct XChainClaimAttestation
struct XChainCreateAccountAttestation
{
using TSignedAttestation = Attestations::AttestationCreateAccount;
using TSignedAttestation = attestations::AttestationCreateAccount;
static SField const& arrayFieldName;
AccountID keyAccount;

View File

@@ -505,6 +505,7 @@ LEDGER_ENTRY(ltVAULT, 0x0084, Vault, vault, ({
{sfShareMPTID, SoeRequired},
{sfWithdrawalPolicy, SoeRequired},
{sfScale, SoeDefault},
{sfLEVersion, SoeDefault},
// no SharesTotal ever (use MPTIssuance.sfOutstandingAmount)
// no PermissionedDomainID ever (use MPTIssuance.sfDomainID)
}))

View File

@@ -18,6 +18,7 @@ TYPED_SFIELD(sfMethod, UINT8, 2)
TYPED_SFIELD(sfTransactionResult, UINT8, 3)
TYPED_SFIELD(sfScale, UINT8, 4)
TYPED_SFIELD(sfAssetScale, UINT8, 5)
TYPED_SFIELD(sfLEVersion, UINT8, 6)
// 8-bit integers (uncommon)
TYPED_SFIELD(sfTickSize, UINT8, 16)

View File

@@ -23,6 +23,16 @@ By default, `CODEGEN_VENV_DIR` points to `.venv` in the project root. The
`setup_code_gen` target creates a venv there and installs the required packages.
The `code_gen` target then uses the venv's Python interpreter to run generation.
Generation is pure Python, so the same targets are also available as a
standalone project that needs neither the dependencies nor a compiler. This is
what CI uses, and it is handy if you only want to regenerate these files:
```bash
cmake -S cmake/codegen -B build/codegen
cmake --build build/codegen --target setup_code_gen
cmake --build build/codegen --target code_gen
```
### Python Dependencies
The code generation requires the following Python packages (installed by `setup_code_gen`):

View File

@@ -287,6 +287,30 @@ public:
{
return this->sle_->isFieldPresent(sfScale);
}
/**
* @brief Get sfLEVersion (SoeDefault)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT8::type::value_type>
getLEVersion() const
{
if (hasLEVersion())
return this->sle_->at(sfLEVersion);
return std::nullopt;
}
/**
* @brief Check if sfLEVersion is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasLEVersion() const
{
return this->sle_->isFieldPresent(sfLEVersion);
}
};
/**
@@ -508,6 +532,17 @@ public:
return *this;
}
/**
* @brief Set sfLEVersion (SoeDefault)
* @return Reference to this builder for method chaining.
*/
VaultBuilder&
setLEVersion(std::decay_t<typename SF_UINT8::type::value_type> const& value)
{
object_[sfLEVersion] = value;
return *this;
}
/**
* @brief Build and return the completed Vault wrapper.
* @param index The ledger entry index.

View File

@@ -4,7 +4,7 @@
#include <ostream>
#include <string>
namespace xrpl::Resource {
namespace xrpl::resource {
/**
* A consumption charge.
@@ -32,7 +32,7 @@ public:
label() const;
/**
* Return the cost of the charge in Resource::Manager units.
* Return the cost of the charge in resource::Manager units.
*/
[[nodiscard]] value_type
cost() const;
@@ -60,4 +60,4 @@ private:
std::ostream&
operator<<(std::ostream& os, Charge const& v);
} // namespace xrpl::Resource
} // namespace xrpl::resource

View File

@@ -8,7 +8,7 @@
#include <ostream>
#include <string>
namespace xrpl::Resource {
namespace xrpl::resource {
struct Entry;
class Logic;
@@ -96,4 +96,4 @@ private:
std::ostream&
operator<<(std::ostream& os, Consumer const& v);
} // namespace xrpl::Resource
} // namespace xrpl::resource

View File

@@ -1,6 +1,6 @@
#pragma once
namespace xrpl::Resource {
namespace xrpl::resource {
/**
* The disposition of a consumer after applying a load charge.
@@ -24,4 +24,4 @@ enum class Disposition {
Drop
};
} // namespace xrpl::Resource
} // namespace xrpl::resource

View File

@@ -2,7 +2,7 @@
#include <xrpl/resource/Charge.h>
namespace xrpl::Resource {
namespace xrpl::resource {
/**
* Schedule of fees charged for imposing load on the server.
@@ -31,4 +31,4 @@ extern Charge const kFeeWarning; // The cost of receiving a warning.
extern Charge const kFeeDrop; // The cost of being dropped for excess load.
/** @} */
} // namespace xrpl::Resource
} // namespace xrpl::resource

View File

@@ -4,7 +4,7 @@
#include <vector>
namespace xrpl::Resource {
namespace xrpl::resource {
/**
* Data format for exchanging consumption information across peers.
@@ -21,10 +21,10 @@ struct Gossip
explicit Item() = default;
int balance{};
beast::IP::Endpoint address;
beast::ip::Endpoint address;
};
std::vector<Item> items;
};
} // namespace xrpl::Resource
} // namespace xrpl::resource

View File

@@ -1,4 +1,4 @@
# Resource::Manager
# resource::Manager
The ResourceManager module has these responsibilities:
@@ -36,7 +36,7 @@ to the general public.
## Consumer Types
Consumers are placed into three classifications (as identified by the
Resource::Kind enumeration):
resource::Kind enumeration):
- InBound,
- OutBound, and
@@ -72,6 +72,6 @@ drop connections to those IP addresses that occur commonly in the gossip.
## Access
In xrpld, the Application holds a unique instance of Resource::Manager,
In xrpld, the Application holds a unique instance of resource::Manager,
which may be retrieved by calling the method
`Application::getResourceManager()`.

View File

@@ -14,7 +14,7 @@
#include <string>
#include <string_view>
namespace xrpl::Resource {
namespace xrpl::resource {
/**
* Tracks load and resource consumption.
@@ -32,10 +32,10 @@ public:
* IP if proxied.
*/
virtual Consumer
newInboundEndpoint(beast::IP::Endpoint const& address) = 0;
newInboundEndpoint(beast::ip::Endpoint const& address) = 0;
virtual Consumer
newInboundEndpoint(
beast::IP::Endpoint const& address,
beast::ip::Endpoint const& address,
bool const proxy,
std::string_view forwardedFor) = 0;
@@ -43,13 +43,13 @@ public:
* Create a new endpoint keyed by outbound IP address and port.
*/
virtual Consumer
newOutboundEndpoint(beast::IP::Endpoint const& address) = 0;
newOutboundEndpoint(beast::ip::Endpoint const& address) = 0;
/**
* Create a new unlimited endpoint keyed by forwarded IP.
*/
virtual Consumer
newUnlimitedEndpoint(beast::IP::Endpoint const& address) = 0;
newUnlimitedEndpoint(beast::ip::Endpoint const& address) = 0;
/**
* Extract packaged consumer information for export.
@@ -78,4 +78,4 @@ public:
std::unique_ptr<Manager>
makeManager(beast::insight::Collector::ptr const& collector, beast::Journal journal);
} // namespace xrpl::Resource
} // namespace xrpl::resource

View File

@@ -12,7 +12,7 @@
#include <ostream>
#include <string>
namespace xrpl::Resource {
namespace xrpl::resource {
using clock_type = beast::AbstractClock<std::chrono::steady_clock>;
@@ -91,4 +91,4 @@ operator<<(std::ostream& os, Entry const& v)
return os;
}
} // namespace xrpl::Resource
} // namespace xrpl::resource

View File

@@ -5,7 +5,7 @@
#include <vector>
namespace xrpl::Resource {
namespace xrpl::resource {
/**
* A set of imported consumer data from a gossip origin.
@@ -32,4 +32,4 @@ struct Import
std::vector<Item> items;
};
} // namespace xrpl::Resource
} // namespace xrpl::resource

View File

@@ -7,17 +7,17 @@
#include <cstddef>
#include <utility>
namespace xrpl::Resource {
namespace xrpl::resource {
// The consumer key
struct Key
{
Kind kind;
beast::IP::Endpoint address;
beast::ip::Endpoint address;
Key() = delete;
Key(Kind k, beast::IP::Endpoint addr) : kind(k), address(std::move(addr))
Key(Kind k, beast::ip::Endpoint addr) : kind(k), address(std::move(addr))
{
}
@@ -47,4 +47,4 @@ struct Key
};
};
} // namespace xrpl::Resource
} // namespace xrpl::resource

View File

@@ -1,6 +1,6 @@
#pragma once
namespace xrpl::Resource {
namespace xrpl::resource {
/**
* Kind of consumer.
@@ -12,4 +12,4 @@ namespace xrpl::Resource {
*/
enum class Kind { Inbound, Outbound, Unlimited };
} // namespace xrpl::Resource
} // namespace xrpl::resource

View File

@@ -24,7 +24,7 @@
#include <tuple>
#include <utility>
namespace xrpl::Resource {
namespace xrpl::resource {
class Logic
{
@@ -96,7 +96,7 @@ public:
}
Consumer
newInboundEndpoint(beast::IP::Endpoint const& address)
newInboundEndpoint(beast::ip::Endpoint const& address)
{
Entry* entry(nullptr);
@@ -126,7 +126,7 @@ public:
}
Consumer
newOutboundEndpoint(beast::IP::Endpoint const& address)
newOutboundEndpoint(beast::ip::Endpoint const& address)
{
Entry* entry(nullptr);
@@ -159,7 +159,7 @@ public:
* enabled.
*/
Consumer
newUnlimitedEndpoint(beast::IP::Endpoint const& address)
newUnlimitedEndpoint(beast::ip::Endpoint const& address)
{
Entry* entry(nullptr);
@@ -387,7 +387,7 @@ public:
{
std::scoped_lock const _(lock_);
Entry& entry(iter->second);
XRPL_ASSERT(entry.refcount == 0, "xrpl::Resource::Logic::erase : entry not used");
XRPL_ASSERT(entry.refcount == 0, "xrpl::resource::Logic::erase : entry not used");
inactive_.erase(inactive_.iteratorTo(entry));
table_.erase(iter);
}
@@ -421,7 +421,7 @@ public:
default:
// LCOV_EXCL_START
UNREACHABLE(
"xrpl::Resource::Logic::release : invalid entry "
"xrpl::resource::Logic::release : invalid entry "
"kind");
break;
// LCOV_EXCL_STOP
@@ -440,7 +440,7 @@ public:
static_assert(
kFeeLogAsWarn > kFeeLogAsInfo && kFeeLogAsInfo > kFeeLogAsDebug && kFeeLogAsDebug > 10);
static auto kGetStream = [](Resource::Charge::value_type cost, beast::Journal& journal) {
static auto kGetStream = [](resource::Charge::value_type cost, beast::Journal& journal) {
if (cost >= kFeeLogAsWarn)
return journal.warn();
if (cost >= kFeeLogAsInfo)
@@ -564,4 +564,4 @@ public:
}
};
} // namespace xrpl::Resource
} // namespace xrpl::resource

View File

@@ -2,7 +2,7 @@
#include <chrono>
namespace xrpl::Resource {
namespace xrpl::resource {
/**
* Tunable constants.
@@ -26,4 +26,4 @@ static constexpr std::chrono::seconds kSecondsUntilExpiration{300};
// Number of seconds until imported gossip expires
static constexpr std::chrono::seconds kGossipExpirationSeconds{30};
} // namespace xrpl::Resource
} // namespace xrpl::resource

View File

@@ -62,7 +62,7 @@ public:
using ref = std::shared_ptr<InfoSub> const&;
using Consumer = Resource::Consumer;
using Consumer = resource::Consumer;
public:
/**

View File

@@ -52,7 +52,7 @@ public:
/**
* Returns the remote address of the connection.
*/
virtual beast::IP::Endpoint
virtual beast::ip::Endpoint
remoteAddress() = 0;
/**

View File

@@ -157,7 +157,7 @@ protected:
return port_;
}
beast::IP::Endpoint
beast::ip::Endpoint
remoteAddress() override
{
return beast::IPAddressConversion::fromAsio(remoteAddress_);

View File

@@ -196,7 +196,7 @@ BaseWSPeer<Handler, Impl>::run()
startTimer();
closeOnTimer_ = true;
impl().ws_.set_option(boost::beast::websocket::stream_base::decorator([](auto& res) {
res.set(boost::beast::http::field::server, BuildInfo::getFullVersionString());
res.set(boost::beast::http::field::server, build_info::getFullVersionString());
}));
impl().ws_.async_accept(
request_, bind_executor(strand_, [self = impl().shared_from_this()](error_code const& ec) {

View File

@@ -3,7 +3,6 @@
#include <xrpl/basics/Blob.h>
#include <xrpl/basics/IntrusivePointer.h>
#include <xrpl/basics/SHAMapHash.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h>
@@ -95,6 +94,21 @@ enum class SHAMapState {
*
* See https://en.wikipedia.org/wiki/Merkle_tree
*/
/**
* Holds a SHAMap node's identity, leaf status, and serialized data. Used by
* getNodeFat to return node data for peer synchronization.
*/
struct SHAMapNodeData
{
SHAMapNodeID nodeID;
// The `data` field (a Blob, 8-byte aligned) needs 4 bytes of padding after the `nodeID` field
// (36 bytes, 4-byte aligned) regardless of what comes between them, so `isLeaf` costs nothing
// extra here. Moving it after `data` would add 8 bytes to the size of this struct instead.
bool isLeaf;
Blob data;
};
class SHAMap
{
private:
@@ -289,10 +303,10 @@ public:
std::vector<std::pair<SHAMapNodeID, uint256>>
getMissingNodes(int maxNodes, SHAMapSyncFilter const* filter);
bool
[[nodiscard]] bool
getNodeFat(
SHAMapNodeID const& wanted,
std::vector<std::pair<SHAMapNodeID, Blob>>& data,
std::vector<SHAMapNodeData>& data,
bool fatLeaves,
std::uint32_t depth) const;
@@ -321,10 +335,45 @@ public:
void
serializeRoot(Serializer& s) const;
/**
* Add a root node to the SHAMap during synchronization.
*
* This function is used when receiving the root node of a SHAMap from a peer during ledger
* synchronization. The node must already have been deserialized.
*
* @param hash The expected hash of the root node.
* @param rootNode A deserialized root node to add.
* @param filter Optional sync filter to track received nodes.
* @return Status indicating whether the node was useful, duplicate, or invalid.
*
* @note This function expects the rootNode to be a valid, deserialized SHAMapTreeNode. The
* caller is responsible for deserialization and basic validation before calling this
* function.
*/
SHAMapAddNode
addRootNode(SHAMapHash const& hash, Slice const& rootNode, SHAMapSyncFilter const* filter);
addRootNode(SHAMapHash const& hash, SHAMapTreeNodePtr rootNode, SHAMapSyncFilter const* filter);
/**
* Add a known node at a specific position in the SHAMap during synchronization.
*
* This function is used when receiving nodes from peers during ledger synchronization. The node
* is inserted at the position specified by nodeID. The node must already have been
* deserialized.
*
* @param nodeID The position in the tree where this node belongs.
* @param treeNode A deserialized tree node to add.
* @param filter Optional sync filter to track received nodes.
* @return Status indicating whether the node was useful, duplicate, or invalid.
*
* @note This function expects the treeNode to be a valid, deserialized SHAMapTreeNode. The
* caller is responsible for deserialization and basic validation before calling this
* function. This also means that the nodeID must be consistent with the node's content.
*/
SHAMapAddNode
addKnownNode(SHAMapNodeID const& nodeID, Slice const& rawNode, SHAMapSyncFilter const* filter);
addKnownNode(
SHAMapNodeID const& nodeID,
SHAMapTreeNodePtr treeNode,
SHAMapSyncFilter const* filter);
// status functions
void

View File

@@ -1,6 +1,9 @@
#pragma once
#include <xrpl/basics/SHAMapHash.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/safe_cast.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/shamap/SHAMapItem.h>
#include <xrpl/shamap/SHAMapNodeID.h>
#include <xrpl/shamap/SHAMapTreeNode.h>
@@ -60,4 +63,16 @@ public:
getString(SHAMapNodeID const&) const final;
};
/**
* Return the key of the item held by a SHAMap leaf node.
*
* @param node a node known to be a leaf (see SHAMapTreeNode::isLeaf).
*/
inline uint256 const&
leafKey(SHAMapTreeNode const& node)
{
XRPL_ASSERT(node.isLeaf(), "xrpl::leafKey : node is a leaf");
return safeDowncast<SHAMapLeafNode const&>(node).peekItem()->key();
}
} // namespace xrpl

View File

@@ -102,6 +102,10 @@ undefined:nudb
# Snappy compression library intentional overflows
unsigned-integer-overflow:snappy.cc
# fast_float parses floats with a SWAR trick (parse_eight_digits_unrolled) that
# multiplies eight packed digits modulo 2^64; the wraparound is by design.
unsigned-integer-overflow:fast_float
# Abseil intentional overflows in hashing, RNG and time arithmetic.
# Matched at library scope (like boost above): the wraparound is by design
# across many absl files (hash mixing, raw_hash_set probing, duration math,
@@ -192,8 +196,9 @@ unsigned-integer-overflow:rpc/handlers/orderbook/GetAggregatePrice.cpp
# Test-only intentional overflow/underflow in fixture and unit-test arithmetic.
unsigned-integer-overflow:tests/libxrpl/basics/RangeSet.cpp
unsigned-integer-overflow:test/app/Batch_test.cpp
unsigned-integer-overflow:test/app/ConfidentialTransfer_test.cpp
unsigned-integer-overflow:test/app/Invariants_test.cpp
unsigned-integer-overflow:test/app/Loan_test.cpp
unsigned-integer-overflow:test/app/lending/LoanSecurity_test.cpp
unsigned-integer-overflow:test/app/NFToken_test.cpp
unsigned-integer-overflow:test/app/OfferMPT_test.cpp
unsigned-integer-overflow:test/app/Offer_test.cpp

View File

@@ -20,21 +20,22 @@
namespace xrpl::node_store {
namespace {
constexpr std::size_t kPoolSizes[] = {1000, 10000, 100000};
constexpr int kThreadCounts[] = {1, 4, 8};
constexpr auto kPoolSizes = std::to_array<std::size_t>({1000, 10000, 100000});
constexpr auto kThreadCounts = std::to_array<std::size_t>({1, 4, 8});
constexpr std::size_t kBatchSize = 256;
constexpr std::size_t kMissRatio = 5;
constexpr std::string_view kNamePrefix = "BM_Backend_";
constexpr std::string_view kNameSeparator = "/";
struct RunState
{
std::unique_ptr<BackendHarness> harness;
Batch present; // prefix-1 objects, eligible to be stored
Batch recent; // prefix-1 objects in the "future" key space
std::vector<uint256> missing; // prefix-2 keys that are never stored
std::vector<std::size_t> shuffle; // [0, poolSize) permutation for random-like access
std::size_t avgPayload = 0; // mean getData().size() over `present`
std::unique_ptr<BackendHarness> harness; ///< backend under test, rebuilt per run
Batch present; ///< prefix-1 objects, eligible to be stored
Batch recent; ///< prefix-1 objects in the "future" key space
std::vector<uint256> missing; ///< prefix-2 keys that are never stored
std::vector<std::size_t> shuffle; ///< [0, poolSize) permutation for random-like access
std::size_t avgPayload = 0; ///< mean getData().size() over `present`
void
release()
@@ -85,7 +86,7 @@ Workload const kInsert{
},
.iterate =
[](IterateContext const& ctx) {
auto& [rs, backend, index, poolSize] = ctx;
auto const& [rs, backend, index, poolSize] = ctx;
backend.store(rs.present[index % poolSize]);
},
.reportBytes = true,
@@ -104,7 +105,7 @@ Workload const kFetch{
},
.iterate =
[](IterateContext const& ctx) {
auto& [rs, backend, index, poolSize] = ctx;
auto const& [rs, backend, index, poolSize] = ctx;
std::shared_ptr<NodeObject> result;
backend.fetch(rs.present[index % poolSize]->getHash(), &result);
benchmark::DoNotOptimize(result);
@@ -118,7 +119,7 @@ Workload const kMissing{
.setup = [](SetupContext const& ctx) { ctx.rs.missing = makeMissingKeys(ctx.poolSize); },
.iterate =
[](IterateContext const& ctx) {
auto& [rs, backend, index, poolSize] = ctx;
auto const& [rs, backend, index, poolSize] = ctx;
std::shared_ptr<NodeObject> result;
backend.fetch(rs.missing[index % poolSize], &result);
benchmark::DoNotOptimize(result);
@@ -139,10 +140,10 @@ Workload const kMixed{
},
.iterate =
[](IterateContext const& ctx) {
auto& [rs, backend, index, poolSize] = ctx;
auto const& [rs, backend, index, poolSize] = ctx;
std::shared_ptr<NodeObject> result;
auto const pick = rs.shuffle[index % poolSize];
if (index % 5 == 0)
if (index % kMissRatio == 0)
{
backend.fetch(rs.missing[pick], &result);
}
@@ -170,7 +171,7 @@ Workload const kWork{
},
.iterate =
[](IterateContext const& ctx) {
auto& [rs, backend, index, poolSize] = ctx;
auto const& [rs, backend, index, poolSize] = ctx;
auto const slot = index % poolSize;
auto const pick = rs.shuffle[slot];
@@ -239,7 +240,7 @@ registerWorkload(BackendConfig const& bc, Workload const& w)
{
auto rs = std::make_shared<RunState>();
auto* b = benchmark::RegisterBenchmark(name, makeRunner(w, cfg, rs));
b->RangeMultiplier(10)->Range(kPoolSizes[0], kPoolSizes[std::size(kPoolSizes) - 1]);
b->RangeMultiplier(10)->Range(kPoolSizes.front(), kPoolSizes.back());
b->Threads(1)->Threads(4)->Threads(8)->UseRealTime();
return;
@@ -249,14 +250,14 @@ registerWorkload(BackendConfig const& bc, Workload const& w)
{
for (auto const threads : kThreadCounts)
{
if (poolSize % static_cast<std::size_t>(threads) != 0)
if (poolSize % threads != 0)
continue;
auto rs = std::make_shared<RunState>();
benchmark::RegisterBenchmark(name, makeRunner(w, cfg, rs))
->Arg(poolSize)
->Iterations(poolSize / static_cast<std::size_t>(threads))
->Threads(threads)
->Iterations(poolSize / threads)
->Threads(static_cast<int>(threads))
->UseRealTime();
}
}
@@ -289,7 +290,7 @@ registerStoreBatch(BackendConfig const& bc)
rs->harness = std::make_unique<BackendHarness>(cfg);
rs->present = makePool(1, poolSize);
rs->avgPayload = averagePayload(rs->present);
std::vector<Batch> const batches = sliceBatches(rs->present, kBatchSize);
std::vector<Batch> const batches = sliceFixedBatches(rs->present, kBatchSize);
if (batches.empty())
{
state.SkipWithError("pool smaller than one batch");

View File

@@ -26,6 +26,7 @@
#include <memory>
#include <numeric>
#include <random>
#include <ranges>
#include <string>
#include <utility>
#include <vector>
@@ -40,18 +41,13 @@ inline void
rngcpy(void* buffer, std::size_t bytes, Generator& g)
{
using result_type = typename Generator::result_type;
while (bytes >= sizeof(result_type))
while (bytes > 0)
{
auto const v = g();
std::memcpy(buffer, &v, sizeof(v));
buffer = reinterpret_cast<std::uint8_t*>(buffer) + sizeof(v);
bytes -= sizeof(v);
}
if (bytes > 0)
{
auto const v = g();
std::memcpy(buffer, &v, bytes);
auto const chunk = std::min(bytes, sizeof(result_type));
std::memcpy(buffer, &v, chunk);
buffer = reinterpret_cast<std::uint8_t*>(buffer) + chunk;
bytes -= chunk;
}
}
@@ -145,7 +141,7 @@ makePool(std::uint8_t prefix, std::size_t count, std::size_t start = 0)
Sequence seq(prefix);
Batch pool;
pool.reserve(count);
for (std::size_t i = 0; i < count; ++i)
for (auto i = 0uz; i < count; ++i)
pool.push_back(seq.obj(start + i));
return pool;
}
@@ -158,7 +154,7 @@ makeMissingKeys(std::size_t count)
Sequence seq(2);
std::vector<uint256> keys;
keys.reserve(count);
for (std::size_t i = 0; i < count; ++i)
for (auto i = 0uz; i < count; ++i)
keys.push_back(seq.key(i));
return keys;
}
@@ -206,16 +202,16 @@ inline std::vector<std::size_t>
makeShuffle(std::size_t size, std::uint64_t seed)
{
std::vector<std::size_t> v(size);
std::iota(v.begin(), v.end(), std::size_t{0});
std::ranges::iota(v, 0uz);
beast::xor_shift_engine gen(seed);
std::shuffle(v.begin(), v.end(), gen);
std::ranges::shuffle(v, gen);
return v;
}
// Partition a pool into fixed-size batches. Any trailing remainder shorter than
// `batchSize` is dropped, so every returned batch has exactly `batchSize`.
inline std::vector<Batch>
sliceBatches(Batch const& pool, std::size_t batchSize)
sliceFixedBatches(Batch const& pool, std::size_t batchSize)
{
std::vector<Batch> batches;
if (batchSize == 0)
@@ -228,13 +224,10 @@ sliceBatches(Batch const& pool, std::size_t batchSize)
/**
* @brief RAII owner of a NodeStore Backend opened on a private temporary directory.
*
* Member declaration order matters: `tempDir` is declared first so it is
* destroyed last, after the backend has closed and released its files.
*/
struct BackendHarness
{
beast::TempDir tempDir;
beast::TempDir tempDir; ///< Declared first so it is destroyed last
DummyScheduler scheduler;
beast::Journal journal{beast::Journal::getNullSink()};
std::unique_ptr<Backend> backend;

View File

@@ -255,7 +255,7 @@ public:
if (ec == boost::asio::error::operation_aborted)
return;
std::vector<beast::IP::Endpoint> addresses;
std::vector<beast::ip::Endpoint> addresses;
auto iter = results.begin();
// If we get an error message back, we don't return any
@@ -283,7 +283,7 @@ public:
// first attempt to parse as an endpoint (IP addr + port).
// If that doesn't succeed, fall back to generic name + port parsing
if (auto const result = beast::IP::Endpoint::fromStringChecked(str))
if (auto const result = beast::ip::Endpoint::fromStringChecked(str))
{
return make_pair(result->address().to_string(), std::to_string(result->port()));
}

View File

@@ -74,7 +74,7 @@ parseUrl(ParsedUrl& pUrl, std::string const& strUrl)
// We need to use Endpoint to parse the domain to
// strip surrounding brackets from IPv6 addresses,
// e.g. [::1] => ::1.
auto const result = beast::IP::Endpoint::fromStringChecked(domain);
auto const result = beast::ip::Endpoint::fromStringChecked(domain);
pUrl.domain = result ? result->address().to_string() : domain;
std::string const port = smMatch[5];
if (!port.empty())

View File

@@ -15,7 +15,7 @@
namespace beast {
std::string
printIdentifiers(SemanticVersion::identifier_list const& list)
printIdentifiers(SemanticVersion::IdentifierList const& list)
{
std::string ret;
@@ -115,7 +115,7 @@ extractIdentifier(std::string& value, bool allowLeadingZeroes, std::string& inpu
bool
extractIdentifiers(
SemanticVersion::identifier_list& identifiers,
SemanticVersion::IdentifierList& identifiers,
bool allowLeadingZeroes,
std::string& input)
{

View File

@@ -207,7 +207,7 @@ private:
static constexpr auto kMaxPacketSize = 1472;
Journal journal_;
IP::Endpoint address_;
ip::Endpoint address_;
std::string prefix_;
boost::asio::io_context ioContext_;
std::optional<boost::asio::executor_work_guard<boost::asio::io_context::executor_type>> work_;
@@ -222,13 +222,13 @@ private:
std::thread thread_;
static boost::asio::ip::udp::endpoint
toEndpoint(IP::Endpoint const& ep)
toEndpoint(ip::Endpoint const& ep)
{
return boost::asio::ip::udp::endpoint(ep.address(), ep.port());
}
public:
StatsDCollectorImp(IP::Endpoint address, std::string prefix, Journal journal)
StatsDCollectorImp(ip::Endpoint address, std::string prefix, Journal journal)
: journal_(journal)
, address_(std::move(address))
, prefix_(std::move(prefix))
@@ -707,7 +707,7 @@ StatsDMeterImpl::doProcess()
//------------------------------------------------------------------------------
std::shared_ptr<StatsDCollector>
StatsDCollector::make(IP::Endpoint const& address, std::string const& prefix, Journal journal)
StatsDCollector::make(ip::Endpoint const& address, std::string const& prefix, Journal journal)
{
return std::make_shared<detail::StatsDCollectorImp>(address, prefix, journal);
}

View File

@@ -5,7 +5,7 @@
#include <boost/asio/ip/address.hpp>
#include <boost/asio/ip/tcp.hpp>
namespace beast::IP {
namespace beast::ip {
Endpoint
fromAsio(boost::asio::ip::address const& address)
@@ -31,4 +31,4 @@ toAsioEndpoint(Endpoint const& endpoint)
return boost::asio::ip::tcp::endpoint{endpoint.address(), endpoint.port()};
}
} // namespace beast::IP
} // namespace beast::ip

View File

@@ -1,6 +1,6 @@
#include <xrpl/beast/net/IPAddressV4.h>
namespace beast::IP {
namespace beast::ip {
bool
isPrivate(AddressV4 const& addr)
@@ -62,4 +62,4 @@ getClass(AddressV4 const& addr)
return kTable[(addr.to_uint() & 0xE0000000) >> 29];
}
} // namespace beast::IP
} // namespace beast::ip

View File

@@ -4,7 +4,7 @@
#include <boost/asio/ip/address_v6.hpp>
namespace beast::IP {
namespace beast::ip {
bool
isPrivate(AddressV6 const& addr)
@@ -58,4 +58,4 @@ isPublic(AddressV6 const& addr)
return true;
}
} // namespace beast::IP
} // namespace beast::ip

View File

@@ -14,7 +14,7 @@
#include <string>
#include <utility>
namespace beast::IP {
namespace beast::ip {
Endpoint::Endpoint() : port_(0)
{
@@ -176,4 +176,4 @@ operator>>(std::istream& is, Endpoint& endpoint)
return is;
}
} // namespace beast::IP
} // namespace beast::ip

View File

@@ -3,9 +3,11 @@
#include <xrpl/basics/contract.h>
#include <xrpl/json/json_value.h>
#include <fast_float/fast_float.h> // IWYU pragma: keep
#include <fast_float/parse_number.h>
#include <algorithm>
#include <cctype>
#include <charconv>
#include <cstdint>
#include <cstdio>
#include <istream>
@@ -605,8 +607,14 @@ Reader::decodeNumber(Token& token)
bool
Reader::decodeDouble(Token& token)
{
// Sanity check to avoid buffer overflow exploits.
if (token.end < token.start)
{
return addError("Unable to parse token length", token);
}
double value = 0;
auto const [ptr, ec] = std::from_chars(token.start, token.end, value);
auto const [ptr, ec] = fast_float::from_chars(token.start, token.end, value);
// Reject anything from_chars could not turn into a finite double:
// - ec != std::errc{}: no valid conversion, or an out-of-range magnitude

View File

@@ -9,6 +9,7 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/VaultHelpers.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/LedgerFormats.h>
@@ -130,6 +131,127 @@ isRounded(Asset const& asset, Number const& value, std::int32_t scale)
roundToAsset(asset, value, scale, Number::RoundingMode::Upward);
}
namespace accrual {
AccountingDeltas
loanOriginationDeltas(Number const& principalRequested, Number const& interestDue)
{
return {.assetsTotalDelta = interestDue, .debtTotalDelta = principalRequested + interestDue};
}
bool
loanOriginationExceedsVaultMaximum(
Number const& vaultMaximum,
Number const& vaultTotal,
Number const& interestDue)
{
return vaultMaximum != 0 && interestDue > vaultMaximum - vaultTotal;
}
/*
XLS-66 section 3.2.3.2, defines the default amount as
DefaultAmount = (Loan.PrincipalOutstanding + Loan.InterestOutstanding)
Which is equivalent to (Loan.TotalValueOutstanding - Loan.ManagementFeeOutstanding)
*/
Number
loanVaultExposure(SLE::const_ref loanSle)
{
return loanSle->at(sfTotalValueOutstanding) - loanSle->at(sfManagementFeeOutstanding);
}
AccountingDeltas
loanPaymentDeltas(LoanPaymentParts const& parts)
{
return {
.assetsTotalDelta = parts.valueChange,
.debtTotalDelta = (parts.principalPaid + parts.interestPaid) - parts.valueChange};
}
} // namespace accrual
namespace cash_basis {
AccountingDeltas
loanOriginationDeltas(Number const& principalRequested)
{
return {.assetsTotalDelta = kNumZero, .debtTotalDelta = principalRequested};
}
/*
* Under CashBasis accounting, Loan default amount is:
*
* DefaultAmount = Loan.PrincipalOutstanding
*/
Number
loanVaultExposure(SLE::const_ref loanSle)
{
return loanSle->at(sfPrincipalOutstanding);
}
AccountingDeltas
loanPaymentDeltas(LoanPaymentParts const& parts)
{
return {.assetsTotalDelta = parts.interestPaid, .debtTotalDelta = parts.principalPaid};
}
} // namespace cash_basis
namespace {
// Cash-basis accounting applies only when featureLendingProtocolV1_1 is
// enabled AND the specific Vault was created under it (LEVersion ==
// VaultVersion::CashBasis). Vaults created before activation keep accrual-basis
// accounting forever, even after the amendment later turns on.
bool
cashBasisEnabled(SLE::const_ref vaultSle)
{
return getVaultVersion(vaultSle) == VaultVersion::CashBasis;
}
} // namespace
AccountingDeltas
loanOriginationDeltas(
SLE::const_ref vaultSle,
Number const& principalRequested,
Number const& interestDue)
{
return cashBasisEnabled(vaultSle)
? cash_basis::loanOriginationDeltas(principalRequested)
: accrual::loanOriginationDeltas(principalRequested, interestDue);
}
bool
loanOriginationExceedsVaultMaximum(
SLE::const_ref vaultSle,
Number const& vaultTotal,
Number const& interestDue)
{
// Cash-basis origination doesn't recognize interest into AssetsTotal, so
// interest due can never push the vault past AssetsMaximum at origination.
if (cashBasisEnabled(vaultSle))
return false;
auto const vaultMaximum = vaultSle->at(sfAssetsMaximum);
return accrual::loanOriginationExceedsVaultMaximum(vaultMaximum, vaultTotal, interestDue);
}
Number
loanVaultExposure(SLE::const_ref vaultSle, SLE::const_ref loanSle)
{
return cashBasisEnabled(vaultSle) ? cash_basis::loanVaultExposure(loanSle)
: accrual::loanVaultExposure(loanSle);
}
AccountingDeltas
loanPaymentDeltas(SLE::const_ref vaultSle, LoanPaymentParts const& parts)
{
return cashBasisEnabled(vaultSle) ? cash_basis::loanPaymentDeltas(parts)
: accrual::loanPaymentDeltas(parts);
}
namespace detail {
void
@@ -1495,7 +1617,7 @@ makeRegularPayment(
LoanPaymentType const paymentType,
beast::Journal j)
{
using namespace Lending;
using namespace lending;
XRPL_ASSERT_PARTS(
paymentType == LoanPaymentType::Regular || paymentType == LoanPaymentType::Overpayment,

View File

@@ -6,6 +6,7 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h> // IWYU pragma: keep
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STLedgerEntry.h>
@@ -13,6 +14,7 @@
#include <cstdint>
#include <optional>
#include <utility>
namespace xrpl {
@@ -137,4 +139,22 @@ isSoleShareholder(ReadView const& view, AccountID const& account, SLE::const_ref
return sleToken->getFieldU64(sfMPTAmount) == outstanding;
}
[[nodiscard]] VaultVersion
getVaultVersion(SLE::const_ref vault)
{
XRPL_ASSERT(vault && vault->getType() == ltVAULT, "xrpl::getVaultVersion : valid Vault sle");
if (!vault->isFieldPresent(sfLEVersion))
return VaultVersion::Legacy;
auto const version = vault->at(sfLEVersion);
if (version > std::to_underlying(VaultVersion::CashBasis))
{
// LCOV_EXCL_START
UNREACHABLE("xrpl::getVaultVersion : invalid vault version");
return VaultVersion::Legacy;
// LCOV_EXCL_STOP
}
return static_cast<VaultVersion>(version);
}
} // namespace xrpl

Some files were not shown because too many files have changed in this diff Show More