mirror of
https://github.com/XRPLF/rippled.git
synced 2026-01-17 13:15:23 +00:00
Compare commits
30 Commits
develop
...
vlntb/mall
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26aa1536c1 | ||
|
|
98e7d8e3bc | ||
|
|
3b47a677c3 | ||
|
|
b8c56ba83e | ||
|
|
dfd832ba7d | ||
|
|
421f61feef | ||
|
|
99648f7986 | ||
|
|
e57ffcc2e8 | ||
|
|
98cc608889 | ||
|
|
508a9c990d | ||
|
|
523eb17883 | ||
|
|
521abcc476 | ||
|
|
030e64938b | ||
|
|
8973ec16ad | ||
|
|
645fddaf82 | ||
|
|
265ea4b270 | ||
|
|
e77bd4e2d8 | ||
|
|
6a8a1b7e28 | ||
|
|
efe7177d1b | ||
|
|
2b2b361c87 | ||
|
|
ff8b4353bc | ||
|
|
50d606539c | ||
|
|
d85f7073dd | ||
|
|
334382f031 | ||
|
|
2d41bfec05 | ||
|
|
52c83684cd | ||
|
|
72b34e6615 | ||
|
|
a1ed175b66 | ||
|
|
3fdd42af63 | ||
|
|
ac5554e9f5 |
@@ -28,7 +28,6 @@ ignoreRegExpList:
|
|||||||
- /[\['"`]-[DWw][a-zA-Z0-9_-]+['"`\]]/g # compile flags
|
- /[\['"`]-[DWw][a-zA-Z0-9_-]+['"`\]]/g # compile flags
|
||||||
suggestWords:
|
suggestWords:
|
||||||
- xprl->xrpl
|
- xprl->xrpl
|
||||||
- xprld->xrpld
|
|
||||||
- unsynched->unsynced
|
- unsynched->unsynced
|
||||||
- synched->synced
|
- synched->synced
|
||||||
- synch->sync
|
- synch->sync
|
||||||
@@ -62,7 +61,6 @@ words:
|
|||||||
- compr
|
- compr
|
||||||
- conanfile
|
- conanfile
|
||||||
- conanrun
|
- conanrun
|
||||||
- confs
|
|
||||||
- connectability
|
- connectability
|
||||||
- coro
|
- coro
|
||||||
- coros
|
- coros
|
||||||
@@ -92,7 +90,6 @@ words:
|
|||||||
- finalizers
|
- finalizers
|
||||||
- firewalled
|
- firewalled
|
||||||
- fmtdur
|
- fmtdur
|
||||||
- fsanitize
|
|
||||||
- funclets
|
- funclets
|
||||||
- gcov
|
- gcov
|
||||||
- gcovr
|
- gcovr
|
||||||
@@ -129,7 +126,6 @@ words:
|
|||||||
- lseq
|
- lseq
|
||||||
- lsmf
|
- lsmf
|
||||||
- ltype
|
- ltype
|
||||||
- mcmodel
|
|
||||||
- MEMORYSTATUSEX
|
- MEMORYSTATUSEX
|
||||||
- Merkle
|
- Merkle
|
||||||
- Metafuncton
|
- Metafuncton
|
||||||
@@ -239,8 +235,6 @@ words:
|
|||||||
- txn
|
- txn
|
||||||
- txns
|
- txns
|
||||||
- txs
|
- txs
|
||||||
- UBSAN
|
|
||||||
- ubsan
|
|
||||||
- umant
|
- umant
|
||||||
- unacquired
|
- unacquired
|
||||||
- unambiguity
|
- unambiguity
|
||||||
@@ -276,7 +270,6 @@ words:
|
|||||||
- xbridge
|
- xbridge
|
||||||
- xchain
|
- xchain
|
||||||
- ximinez
|
- ximinez
|
||||||
- EXPECT_STREQ
|
|
||||||
- XMACRO
|
- XMACRO
|
||||||
- xrpkuwait
|
- xrpkuwait
|
||||||
- xrpl
|
- xrpl
|
||||||
|
|||||||
6
.github/actions/build-deps/action.yml
vendored
6
.github/actions/build-deps/action.yml
vendored
@@ -18,10 +18,6 @@ inputs:
|
|||||||
description: "The logging verbosity."
|
description: "The logging verbosity."
|
||||||
required: false
|
required: false
|
||||||
default: "verbose"
|
default: "verbose"
|
||||||
sanitizers:
|
|
||||||
description: "The sanitizers to enable."
|
|
||||||
required: false
|
|
||||||
default: ""
|
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: composite
|
using: composite
|
||||||
@@ -33,11 +29,9 @@ runs:
|
|||||||
BUILD_OPTION: ${{ inputs.force_build == 'true' && '*' || 'missing' }}
|
BUILD_OPTION: ${{ inputs.force_build == 'true' && '*' || 'missing' }}
|
||||||
BUILD_TYPE: ${{ inputs.build_type }}
|
BUILD_TYPE: ${{ inputs.build_type }}
|
||||||
LOG_VERBOSITY: ${{ inputs.log_verbosity }}
|
LOG_VERBOSITY: ${{ inputs.log_verbosity }}
|
||||||
SANITIZERS: ${{ inputs.sanitizers }}
|
|
||||||
run: |
|
run: |
|
||||||
echo 'Installing dependencies.'
|
echo 'Installing dependencies.'
|
||||||
conan install \
|
conan install \
|
||||||
--profile ci \
|
|
||||||
--build="${BUILD_OPTION}" \
|
--build="${BUILD_OPTION}" \
|
||||||
--options:host='&:tests=True' \
|
--options:host='&:tests=True' \
|
||||||
--options:host='&:xrpld=True' \
|
--options:host='&:xrpld=True' \
|
||||||
|
|||||||
2
.github/actions/setup-conan/action.yml
vendored
2
.github/actions/setup-conan/action.yml
vendored
@@ -28,7 +28,7 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
echo 'Installing profile.'
|
echo 'Installing profile.'
|
||||||
conan config install conan/profiles/ -tf $(conan config home)/profiles/
|
conan config install conan/profiles/default -tf $(conan config home)/profiles/
|
||||||
|
|
||||||
echo 'Conan profile:'
|
echo 'Conan profile:'
|
||||||
conan profile show
|
conan profile show
|
||||||
|
|||||||
64
.github/scripts/strategy-matrix/generate.py
vendored
64
.github/scripts/strategy-matrix/generate.py
vendored
@@ -20,8 +20,8 @@ class Config:
|
|||||||
Generate a strategy matrix for GitHub Actions CI.
|
Generate a strategy matrix for GitHub Actions CI.
|
||||||
|
|
||||||
On each PR commit we will build a selection of Debian, RHEL, Ubuntu, MacOS, and
|
On each PR commit we will build a selection of Debian, RHEL, Ubuntu, MacOS, and
|
||||||
Windows configurations, while upon merge into the develop or release branches,
|
Windows configurations, while upon merge into the develop, release, or master
|
||||||
we will build all configurations, and test most of them.
|
branches, we will build all configurations, and test most of them.
|
||||||
|
|
||||||
We will further set additional CMake arguments as follows:
|
We will further set additional CMake arguments as follows:
|
||||||
- All builds will have the `tests`, `werr`, and `xrpld` options.
|
- All builds will have the `tests`, `werr`, and `xrpld` options.
|
||||||
@@ -229,7 +229,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
|||||||
if (n := os["compiler_version"]) != "":
|
if (n := os["compiler_version"]) != "":
|
||||||
config_name += f"-{n}"
|
config_name += f"-{n}"
|
||||||
config_name += (
|
config_name += (
|
||||||
f"-{architecture['platform'][architecture['platform'].find('/')+1:]}"
|
f"-{architecture['platform'][architecture['platform'].find('/') + 1 :]}"
|
||||||
)
|
)
|
||||||
config_name += f"-{build_type.lower()}"
|
config_name += f"-{build_type.lower()}"
|
||||||
if "-Dcoverage=ON" in cmake_args:
|
if "-Dcoverage=ON" in cmake_args:
|
||||||
@@ -240,53 +240,17 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
|||||||
# Add the configuration to the list, with the most unique fields first,
|
# Add the configuration to the list, with the most unique fields first,
|
||||||
# so that they are easier to identify in the GitHub Actions UI, as long
|
# so that they are easier to identify in the GitHub Actions UI, as long
|
||||||
# names get truncated.
|
# names get truncated.
|
||||||
# Add Address and Thread (both coupled with UB) sanitizers for specific bookworm distros.
|
configurations.append(
|
||||||
# GCC-Asan rippled-embedded tests are failing because of https://github.com/google/sanitizers/issues/856
|
{
|
||||||
if (
|
"config_name": config_name,
|
||||||
os["distro_version"] == "bookworm"
|
"cmake_args": cmake_args,
|
||||||
and f"{os['compiler_name']}-{os['compiler_version']}" == "clang-20"
|
"cmake_target": cmake_target,
|
||||||
):
|
"build_only": build_only,
|
||||||
# Add ASAN + UBSAN configuration.
|
"build_type": build_type,
|
||||||
configurations.append(
|
"os": os,
|
||||||
{
|
"architecture": architecture,
|
||||||
"config_name": config_name + "-asan-ubsan",
|
}
|
||||||
"cmake_args": cmake_args,
|
)
|
||||||
"cmake_target": cmake_target,
|
|
||||||
"build_only": build_only,
|
|
||||||
"build_type": build_type,
|
|
||||||
"os": os,
|
|
||||||
"architecture": architecture,
|
|
||||||
"sanitizers": "address,undefinedbehavior",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# TSAN is deactivated due to seg faults with latest compilers.
|
|
||||||
activate_tsan = False
|
|
||||||
if activate_tsan:
|
|
||||||
configurations.append(
|
|
||||||
{
|
|
||||||
"config_name": config_name + "-tsan-ubsan",
|
|
||||||
"cmake_args": cmake_args,
|
|
||||||
"cmake_target": cmake_target,
|
|
||||||
"build_only": build_only,
|
|
||||||
"build_type": build_type,
|
|
||||||
"os": os,
|
|
||||||
"architecture": architecture,
|
|
||||||
"sanitizers": "thread,undefinedbehavior",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
configurations.append(
|
|
||||||
{
|
|
||||||
"config_name": config_name,
|
|
||||||
"cmake_args": cmake_args,
|
|
||||||
"cmake_target": cmake_target,
|
|
||||||
"build_only": build_only,
|
|
||||||
"build_type": build_type,
|
|
||||||
"os": os,
|
|
||||||
"architecture": architecture,
|
|
||||||
"sanitizers": "",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return configurations
|
return configurations
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/on-pr.yml
vendored
2
.github/workflows/on-pr.yml
vendored
@@ -125,7 +125,7 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- should-run
|
- should-run
|
||||||
- build-test
|
- build-test
|
||||||
if: ${{ needs.should-run.outputs.go == 'true' && startsWith(github.ref, 'refs/heads/release') }}
|
if: ${{ needs.should-run.outputs.go == 'true' && (startsWith(github.base_ref, 'release') || github.base_ref == 'master') }}
|
||||||
uses: ./.github/workflows/reusable-notify-clio.yml
|
uses: ./.github/workflows/reusable-notify-clio.yml
|
||||||
secrets:
|
secrets:
|
||||||
clio_notify_token: ${{ secrets.CLIO_NOTIFY_TOKEN }}
|
clio_notify_token: ${{ secrets.CLIO_NOTIFY_TOKEN }}
|
||||||
|
|||||||
20
.github/workflows/on-trigger.yml
vendored
20
.github/workflows/on-trigger.yml
vendored
@@ -1,8 +1,9 @@
|
|||||||
# This workflow runs all workflows to build and test the code on various Linux
|
# This workflow runs all workflows to build the dependencies required for the
|
||||||
# flavors, as well as on MacOS and Windows, on a scheduled basis, on merge into
|
# project on various Linux flavors, as well as on MacOS and Windows, on a
|
||||||
# the 'develop' or 'release*' branches, or when requested manually. Upon
|
# scheduled basis, on merge into the 'develop', 'release', or 'master' branches,
|
||||||
# successful completion, it also uploads the built libxrpl package to the Conan
|
# or manually. The missing commits check is only run when the code is merged
|
||||||
# remote.
|
# into the 'develop' or 'release' branches, and the documentation is built when
|
||||||
|
# the code is merged into the 'develop' branch.
|
||||||
name: Trigger
|
name: Trigger
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -10,6 +11,7 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- "develop"
|
- "develop"
|
||||||
- "release*"
|
- "release*"
|
||||||
|
- "master"
|
||||||
paths:
|
paths:
|
||||||
# These paths are unique to `on-trigger.yml`.
|
# These paths are unique to `on-trigger.yml`.
|
||||||
- ".github/workflows/on-trigger.yml"
|
- ".github/workflows/on-trigger.yml"
|
||||||
@@ -68,10 +70,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
# Enable ccache only for events targeting the XRPLF repository, since
|
# Enable ccache only for events targeting the XRPLF repository, since
|
||||||
# other accounts will not have access to our remote cache storage.
|
# other accounts will not have access to our remote cache storage.
|
||||||
# However, we do not enable ccache for events targeting a release branch,
|
# However, we do not enable ccache for events targeting the master or a
|
||||||
# to protect against the rare case that the output produced by ccache is
|
# release branch, to protect against the rare case that the output
|
||||||
# not identical to a regular compilation.
|
# produced by ccache is not identical to a regular compilation.
|
||||||
ccache_enabled: ${{ github.repository_owner == 'XRPLF' && !startsWith(github.ref, 'refs/heads/release') }}
|
ccache_enabled: ${{ github.repository_owner == 'XRPLF' && !(github.base_ref == 'master' || startsWith(github.base_ref, 'release')) }}
|
||||||
os: ${{ matrix.os }}
|
os: ${{ matrix.os }}
|
||||||
strategy_matrix: ${{ github.event_name == 'schedule' && 'all' || 'minimal' }}
|
strategy_matrix: ${{ github.event_name == 'schedule' && 'all' || 'minimal' }}
|
||||||
secrets:
|
secrets:
|
||||||
|
|||||||
4
.github/workflows/pre-commit.yml
vendored
4
.github/workflows/pre-commit.yml
vendored
@@ -3,9 +3,7 @@ name: Run pre-commit hooks
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [develop, release, master]
|
||||||
- "develop"
|
|
||||||
- "release*"
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
19
.github/workflows/reusable-build-test-config.yml
vendored
19
.github/workflows/reusable-build-test-config.yml
vendored
@@ -51,12 +51,6 @@ on:
|
|||||||
type: number
|
type: number
|
||||||
default: 2
|
default: 2
|
||||||
|
|
||||||
sanitizers:
|
|
||||||
description: "The sanitizers to enable."
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
default: ""
|
|
||||||
|
|
||||||
secrets:
|
secrets:
|
||||||
CODECOV_TOKEN:
|
CODECOV_TOKEN:
|
||||||
description: "The Codecov token to use for uploading coverage reports."
|
description: "The Codecov token to use for uploading coverage reports."
|
||||||
@@ -97,7 +91,6 @@ jobs:
|
|||||||
# Determine if coverage and voidstar should be enabled.
|
# Determine if coverage and voidstar should be enabled.
|
||||||
COVERAGE_ENABLED: ${{ contains(inputs.cmake_args, '-Dcoverage=ON') }}
|
COVERAGE_ENABLED: ${{ contains(inputs.cmake_args, '-Dcoverage=ON') }}
|
||||||
VOIDSTAR_ENABLED: ${{ contains(inputs.cmake_args, '-Dvoidstar=ON') }}
|
VOIDSTAR_ENABLED: ${{ contains(inputs.cmake_args, '-Dvoidstar=ON') }}
|
||||||
SANITIZERS_ENABLED: ${{ inputs.sanitizers != '' }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Cleanup workspace (macOS and Windows)
|
- name: Cleanup workspace (macOS and Windows)
|
||||||
if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }}
|
if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }}
|
||||||
@@ -135,13 +128,11 @@ jobs:
|
|||||||
# Set the verbosity to "quiet" for Windows to avoid an excessive
|
# Set the verbosity to "quiet" for Windows to avoid an excessive
|
||||||
# amount of logs. For other OSes, the "verbose" logs are more useful.
|
# amount of logs. For other OSes, the "verbose" logs are more useful.
|
||||||
log_verbosity: ${{ runner.os == 'Windows' && 'quiet' || 'verbose' }}
|
log_verbosity: ${{ runner.os == 'Windows' && 'quiet' || 'verbose' }}
|
||||||
sanitizers: ${{ inputs.sanitizers }}
|
|
||||||
|
|
||||||
- name: Configure CMake
|
- name: Configure CMake
|
||||||
working-directory: ${{ env.BUILD_DIR }}
|
working-directory: ${{ env.BUILD_DIR }}
|
||||||
env:
|
env:
|
||||||
BUILD_TYPE: ${{ inputs.build_type }}
|
BUILD_TYPE: ${{ inputs.build_type }}
|
||||||
SANITIZERS: ${{ inputs.sanitizers }}
|
|
||||||
CMAKE_ARGS: ${{ inputs.cmake_args }}
|
CMAKE_ARGS: ${{ inputs.cmake_args }}
|
||||||
run: |
|
run: |
|
||||||
cmake \
|
cmake \
|
||||||
@@ -183,7 +174,7 @@ jobs:
|
|||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Check linking (Linux)
|
- name: Check linking (Linux)
|
||||||
if: ${{ runner.os == 'Linux' && env.SANITIZERS_ENABLED == 'false' }}
|
if: ${{ runner.os == 'Linux' }}
|
||||||
working-directory: ${{ env.BUILD_DIR }}
|
working-directory: ${{ env.BUILD_DIR }}
|
||||||
run: |
|
run: |
|
||||||
ldd ./xrpld
|
ldd ./xrpld
|
||||||
@@ -200,14 +191,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
./xrpld --version | grep libvoidstar
|
./xrpld --version | grep libvoidstar
|
||||||
|
|
||||||
- name: Set sanitizer options
|
|
||||||
if: ${{ !inputs.build_only && env.SANITIZERS_ENABLED == 'true' }}
|
|
||||||
run: |
|
|
||||||
echo "ASAN_OPTIONS=print_stacktrace=1:detect_container_overflow=0:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp" >> ${GITHUB_ENV}
|
|
||||||
echo "TSAN_OPTIONS=second_deadlock_stack=1:halt_on_error=0:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV}
|
|
||||||
echo "UBSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV}
|
|
||||||
echo "LSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV}
|
|
||||||
|
|
||||||
- name: Run the separate tests
|
- name: Run the separate tests
|
||||||
if: ${{ !inputs.build_only }}
|
if: ${{ !inputs.build_only }}
|
||||||
working-directory: ${{ env.BUILD_DIR }}
|
working-directory: ${{ env.BUILD_DIR }}
|
||||||
|
|||||||
1
.github/workflows/reusable-build-test.yml
vendored
1
.github/workflows/reusable-build-test.yml
vendored
@@ -57,6 +57,5 @@ jobs:
|
|||||||
runs_on: ${{ toJSON(matrix.architecture.runner) }}
|
runs_on: ${{ toJSON(matrix.architecture.runner) }}
|
||||||
image: ${{ contains(matrix.architecture.platform, 'linux') && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-{4}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version, matrix.os.image_sha) || '' }}
|
image: ${{ contains(matrix.architecture.platform, 'linux') && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-{4}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version, matrix.os.image_sha) || '' }}
|
||||||
config_name: ${{ matrix.config_name }}
|
config_name: ${{ matrix.config_name }}
|
||||||
sanitizers: ${{ matrix.sanitizers }}
|
|
||||||
secrets:
|
secrets:
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|||||||
38
BUILD.md
38
BUILD.md
@@ -1,5 +1,5 @@
|
|||||||
| :warning: **WARNING** :warning: |
|
| :warning: **WARNING** :warning:
|
||||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|---|
|
||||||
| These instructions assume you have a C++ development environment ready with Git, Python, Conan, CMake, and a C++ compiler. For help setting one up on Linux, macOS, or Windows, [see this guide](./docs/build/environment.md). |
|
| These instructions assume you have a C++ development environment ready with Git, Python, Conan, CMake, and a C++ compiler. For help setting one up on Linux, macOS, or Windows, [see this guide](./docs/build/environment.md). |
|
||||||
|
|
||||||
> These instructions also assume a basic familiarity with Conan and CMake.
|
> These instructions also assume a basic familiarity with Conan and CMake.
|
||||||
@@ -523,32 +523,18 @@ stored inside the build directory, as either of:
|
|||||||
- file named `coverage.`_extension_, with a suitable extension for the report format, or
|
- file named `coverage.`_extension_, with a suitable extension for the report format, or
|
||||||
- directory named `coverage`, with the `index.html` and other files inside, for the `html-details` or `html-nested` report formats.
|
- directory named `coverage`, with the `index.html` and other files inside, for the `html-details` or `html-nested` report formats.
|
||||||
|
|
||||||
## Sanitizers
|
|
||||||
|
|
||||||
To build dependencies and xrpld with sanitizer instrumentation, set the
|
|
||||||
`SANITIZERS` environment variable (only once before running conan and cmake) and use the `sanitizers` profile in conan:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export SANITIZERS=address,undefinedbehavior
|
|
||||||
|
|
||||||
conan install .. --output-folder . --profile:all sanitizers --build missing --settings build_type=Debug
|
|
||||||
|
|
||||||
cmake -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Debug -Dxrpld=ON -Dtests=ON ..
|
|
||||||
```
|
|
||||||
|
|
||||||
See [Sanitizers docs](./docs/build/sanitizers.md) for more details.
|
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
| Option | Default Value | Description |
|
| Option | Default Value | Description |
|
||||||
| ---------- | ------------- | -------------------------------------------------------------- |
|
| ---------- | ------------- | ------------------------------------------------------------------ |
|
||||||
| `assert` | OFF | Enable assertions. |
|
| `assert` | OFF | Enable assertions. |
|
||||||
| `coverage` | OFF | Prepare the coverage report. |
|
| `coverage` | OFF | Prepare the coverage report. |
|
||||||
| `tests` | OFF | Build tests. |
|
| `san` | N/A | Enable a sanitizer with Clang. Choices are `thread` and `address`. |
|
||||||
| `unity` | OFF | Configure a unity build. |
|
| `tests` | OFF | Build tests. |
|
||||||
| `xrpld` | OFF | Build the xrpld application, and not just the libxrpl library. |
|
| `unity` | OFF | Configure a unity build. |
|
||||||
| `werr` | OFF | Treat compilation warnings as errors |
|
| `xrpld` | OFF | Build the xrpld application, and not just the libxrpl library. |
|
||||||
| `wextra` | OFF | Enable additional compilation warnings |
|
| `werr` | OFF | Treat compilation warnings as errors |
|
||||||
|
| `wextra` | OFF | Enable additional compilation warnings |
|
||||||
|
|
||||||
[Unity builds][5] may be faster for the first build
|
[Unity builds][5] may be faster for the first build
|
||||||
(at the cost of much more memory) since they concatenate sources into fewer
|
(at the cost of much more memory) since they concatenate sources into fewer
|
||||||
|
|||||||
@@ -16,16 +16,14 @@ set(CMAKE_CXX_EXTENSIONS OFF)
|
|||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
include(CompilationEnv)
|
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
|
|
||||||
if(is_gcc)
|
|
||||||
# GCC-specific fixes
|
# GCC-specific fixes
|
||||||
add_compile_options(-Wno-unknown-pragmas -Wno-subobject-linkage)
|
add_compile_options(-Wno-unknown-pragmas -Wno-subobject-linkage)
|
||||||
# -Wno-subobject-linkage can be removed when we upgrade GCC version to at least 13.3
|
# -Wno-subobject-linkage can be removed when we upgrade GCC version to at least 13.3
|
||||||
elseif(is_clang)
|
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
# Clang-specific fixes
|
# Clang-specific fixes
|
||||||
add_compile_options(-Wno-unknown-warning-option) # Ignore unknown warning options
|
add_compile_options(-Wno-unknown-warning-option) # Ignore unknown warning options
|
||||||
elseif(is_msvc)
|
elseif(MSVC)
|
||||||
# MSVC-specific fixes
|
# MSVC-specific fixes
|
||||||
add_compile_options(/wd4068) # Ignore unknown pragmas
|
add_compile_options(/wd4068) # Ignore unknown pragmas
|
||||||
endif()
|
endif()
|
||||||
@@ -79,7 +77,6 @@ if (packages_only)
|
|||||||
return ()
|
return ()
|
||||||
endif ()
|
endif ()
|
||||||
include(XrplCompiler)
|
include(XrplCompiler)
|
||||||
include(XrplSanitizers)
|
|
||||||
include(XrplInterface)
|
include(XrplInterface)
|
||||||
|
|
||||||
option(only_docs "Include only the docs target?" FALSE)
|
option(only_docs "Include only the docs target?" FALSE)
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
# Shared detection of compiler, operating system, and architecture.
|
|
||||||
#
|
|
||||||
# This module centralizes environment detection so that other
|
|
||||||
# CMake modules can use the same variables instead of repeating
|
|
||||||
# checks on CMAKE_* and built-in platform variables.
|
|
||||||
|
|
||||||
# Only run once per configure step.
|
|
||||||
include_guard(GLOBAL)
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
|
||||||
# Compiler detection (C++)
|
|
||||||
# --------------------------------------------------------------------
|
|
||||||
set(is_clang FALSE)
|
|
||||||
set(is_gcc FALSE)
|
|
||||||
set(is_msvc FALSE)
|
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") # Clang or AppleClang
|
|
||||||
set(is_clang TRUE)
|
|
||||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
||||||
set(is_gcc TRUE)
|
|
||||||
elseif(MSVC)
|
|
||||||
set(is_msvc TRUE)
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Unsupported C++ compiler: ${CMAKE_CXX_COMPILER_ID}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
|
||||||
# Operating system detection
|
|
||||||
# --------------------------------------------------------------------
|
|
||||||
set(is_linux FALSE)
|
|
||||||
set(is_windows FALSE)
|
|
||||||
set(is_macos FALSE)
|
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
|
||||||
set(is_linux TRUE)
|
|
||||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
|
||||||
set(is_windows TRUE)
|
|
||||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
|
||||||
set(is_macos TRUE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
|
||||||
# Architecture
|
|
||||||
# --------------------------------------------------------------------
|
|
||||||
set(is_amd64 FALSE)
|
|
||||||
set(is_arm64 FALSE)
|
|
||||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
|
|
||||||
set(is_amd64 TRUE)
|
|
||||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64")
|
|
||||||
set(is_arm64 TRUE)
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
|
||||||
endif()
|
|
||||||
@@ -2,23 +2,16 @@
|
|||||||
setup project-wide compiler settings
|
setup project-wide compiler settings
|
||||||
#]===================================================================]
|
#]===================================================================]
|
||||||
|
|
||||||
include(CompilationEnv)
|
|
||||||
|
|
||||||
#[=========================================================[
|
#[=========================================================[
|
||||||
TODO some/most of these common settings belong in a
|
TODO some/most of these common settings belong in a
|
||||||
toolchain file, especially the ABI-impacting ones
|
toolchain file, especially the ABI-impacting ones
|
||||||
#]=========================================================]
|
#]=========================================================]
|
||||||
add_library (common INTERFACE)
|
add_library (common INTERFACE)
|
||||||
add_library (Xrpl::common ALIAS common)
|
add_library (Xrpl::common ALIAS common)
|
||||||
include(XrplSanitizers)
|
|
||||||
# add a single global dependency on this interface lib
|
# add a single global dependency on this interface lib
|
||||||
link_libraries (Xrpl::common)
|
link_libraries (Xrpl::common)
|
||||||
# Respect CMAKE_POSITION_INDEPENDENT_CODE setting (may be set by Conan toolchain)
|
|
||||||
if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
|
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|
||||||
endif()
|
|
||||||
set_target_properties (common
|
set_target_properties (common
|
||||||
PROPERTIES INTERFACE_POSITION_INDEPENDENT_CODE ${CMAKE_POSITION_INDEPENDENT_CODE})
|
PROPERTIES INTERFACE_POSITION_INDEPENDENT_CODE ON)
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
target_compile_definitions (common
|
target_compile_definitions (common
|
||||||
INTERFACE
|
INTERFACE
|
||||||
@@ -123,8 +116,8 @@ else ()
|
|||||||
# link to static libc/c++ iff:
|
# link to static libc/c++ iff:
|
||||||
# * static option set and
|
# * static option set and
|
||||||
# * NOT APPLE (AppleClang does not support static libc/c++) and
|
# * NOT APPLE (AppleClang does not support static libc/c++) and
|
||||||
# * NOT SANITIZERS (sanitizers typically don't work with static libc/c++)
|
# * NOT san (sanitizers typically don't work with static libc/c++)
|
||||||
$<$<AND:$<BOOL:${static}>,$<NOT:$<BOOL:${APPLE}>>,$<NOT:$<BOOL:${SANITIZERS_ENABLED}>>>:
|
$<$<AND:$<BOOL:${static}>,$<NOT:$<BOOL:${APPLE}>>,$<NOT:$<BOOL:${san}>>>:
|
||||||
-static-libstdc++
|
-static-libstdc++
|
||||||
-static-libgcc
|
-static-libgcc
|
||||||
>)
|
>)
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
xrpld compile options/settings via an interface library
|
xrpld compile options/settings via an interface library
|
||||||
#]===================================================================]
|
#]===================================================================]
|
||||||
|
|
||||||
include(CompilationEnv)
|
|
||||||
|
|
||||||
add_library (opts INTERFACE)
|
add_library (opts INTERFACE)
|
||||||
add_library (Xrpl::opts ALIAS opts)
|
add_library (Xrpl::opts ALIAS opts)
|
||||||
target_compile_definitions (opts
|
target_compile_definitions (opts
|
||||||
@@ -44,6 +42,22 @@ if(jemalloc)
|
|||||||
target_link_libraries(opts INTERFACE jemalloc::jemalloc)
|
target_link_libraries(opts INTERFACE jemalloc::jemalloc)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
if (san)
|
||||||
|
target_compile_options (opts
|
||||||
|
INTERFACE
|
||||||
|
# sanitizers recommend minimum of -O1 for reasonable performance
|
||||||
|
$<$<CONFIG:Debug>:-O1>
|
||||||
|
${SAN_FLAG}
|
||||||
|
-fno-omit-frame-pointer)
|
||||||
|
target_compile_definitions (opts
|
||||||
|
INTERFACE
|
||||||
|
$<$<STREQUAL:${san},address>:SANITIZER=ASAN>
|
||||||
|
$<$<STREQUAL:${san},thread>:SANITIZER=TSAN>
|
||||||
|
$<$<STREQUAL:${san},memory>:SANITIZER=MSAN>
|
||||||
|
$<$<STREQUAL:${san},undefined>:SANITIZER=UBSAN>)
|
||||||
|
target_link_libraries (opts INTERFACE ${SAN_FLAG} ${SAN_LIB})
|
||||||
|
endif ()
|
||||||
|
|
||||||
#[===================================================================[
|
#[===================================================================[
|
||||||
xrpld transitive library deps via an interface library
|
xrpld transitive library deps via an interface library
|
||||||
#]===================================================================]
|
#]===================================================================]
|
||||||
|
|||||||
@@ -1,198 +0,0 @@
|
|||||||
#[===================================================================[
|
|
||||||
Configure sanitizers based on environment variables.
|
|
||||||
|
|
||||||
This module reads the following environment variables:
|
|
||||||
- SANITIZERS: The sanitizers to enable. Possible values:
|
|
||||||
- "address"
|
|
||||||
- "address,undefinedbehavior"
|
|
||||||
- "thread"
|
|
||||||
- "thread,undefinedbehavior"
|
|
||||||
- "undefinedbehavior"
|
|
||||||
|
|
||||||
The compiler type and platform are detected in CompilationEnv.cmake.
|
|
||||||
The sanitizer compile options are applied to the 'common' interface library
|
|
||||||
which is linked to all targets in the project.
|
|
||||||
|
|
||||||
Internal flag variables set by this module:
|
|
||||||
|
|
||||||
- SANITIZER_TYPES: List of sanitizer types to enable (e.g., "address",
|
|
||||||
"thread", "undefined"). And two more flags for undefined behavior sanitizer (e.g., "float-divide-by-zero", "unsigned-integer-overflow").
|
|
||||||
This list is joined with commas and passed to -fsanitize=<list>.
|
|
||||||
|
|
||||||
- SANITIZERS_COMPILE_FLAGS: Compiler flags for sanitizer instrumentation.
|
|
||||||
Includes:
|
|
||||||
* -fno-omit-frame-pointer: Preserves frame pointers for stack traces
|
|
||||||
* -O1: Minimum optimization for reasonable performance
|
|
||||||
* -fsanitize=<types>: Enables sanitizer instrumentation
|
|
||||||
* -fsanitize-ignorelist=<path>: (Clang only) Compile-time ignorelist
|
|
||||||
* -mcmodel=large/medium: (GCC only) Code model for large binaries
|
|
||||||
* -Wno-stringop-overflow: (GCC only) Suppresses false positive warnings
|
|
||||||
* -Wno-tsan: (For GCC TSAN combination only) Suppresses atomic_thread_fence warnings
|
|
||||||
|
|
||||||
- SANITIZERS_LINK_FLAGS: Linker flags for sanitizer runtime libraries.
|
|
||||||
Includes:
|
|
||||||
* -fsanitize=<types>: Links sanitizer runtime libraries
|
|
||||||
* -mcmodel=large/medium: (GCC only) Matches compile-time code model
|
|
||||||
|
|
||||||
- SANITIZERS_RELOCATION_FLAGS: (GCC only) Code model flags for linking.
|
|
||||||
Used to handle large instrumented binaries on x86_64:
|
|
||||||
* -mcmodel=large: For AddressSanitizer (prevents relocation errors)
|
|
||||||
* -mcmodel=medium: For ThreadSanitizer (large model is incompatible)
|
|
||||||
#]===================================================================]
|
|
||||||
|
|
||||||
include(CompilationEnv)
|
|
||||||
|
|
||||||
# Read environment variable
|
|
||||||
set(SANITIZERS $ENV{SANITIZERS})
|
|
||||||
|
|
||||||
# Set SANITIZERS_ENABLED flag for use in other modules
|
|
||||||
if(SANITIZERS MATCHES "address|thread|undefinedbehavior")
|
|
||||||
set(SANITIZERS_ENABLED TRUE)
|
|
||||||
else()
|
|
||||||
set(SANITIZERS_ENABLED FALSE)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Sanitizers are not supported on Windows/MSVC
|
|
||||||
if(is_msvc)
|
|
||||||
message(FATAL_ERROR "Sanitizers are not supported on Windows/MSVC. "
|
|
||||||
"Please unset the SANITIZERS environment variable.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
message(STATUS "Configuring sanitizers: ${SANITIZERS}")
|
|
||||||
|
|
||||||
# Parse SANITIZERS value to determine which sanitizers to enable
|
|
||||||
set(enable_asan FALSE)
|
|
||||||
set(enable_tsan FALSE)
|
|
||||||
set(enable_ubsan FALSE)
|
|
||||||
|
|
||||||
# Normalize SANITIZERS into a list
|
|
||||||
set(san_list "${SANITIZERS}")
|
|
||||||
string(REPLACE "," ";" san_list "${san_list}")
|
|
||||||
separate_arguments(san_list)
|
|
||||||
|
|
||||||
foreach(san IN LISTS san_list)
|
|
||||||
if(san STREQUAL "address")
|
|
||||||
set(enable_asan TRUE)
|
|
||||||
elseif(san STREQUAL "thread")
|
|
||||||
set(enable_tsan TRUE)
|
|
||||||
elseif(san STREQUAL "undefinedbehavior")
|
|
||||||
set(enable_ubsan TRUE)
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Unsupported sanitizer type: ${san}"
|
|
||||||
"Supported: address, thread, undefinedbehavior and their combinations.")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
# Validate sanitizer compatibility
|
|
||||||
if(enable_asan AND enable_tsan)
|
|
||||||
message(FATAL_ERROR "AddressSanitizer and ThreadSanitizer are incompatible and cannot be enabled simultaneously. "
|
|
||||||
"Use 'address' or 'thread', optionally with 'undefinedbehavior'.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Frame pointer is required for meaningful stack traces. Sanitizers recommend minimum of -O1 for reasonable performance
|
|
||||||
set(SANITIZERS_COMPILE_FLAGS "-fno-omit-frame-pointer" "-O1")
|
|
||||||
|
|
||||||
# Build the sanitizer flags list
|
|
||||||
set(SANITIZER_TYPES)
|
|
||||||
|
|
||||||
if(enable_asan)
|
|
||||||
list(APPEND SANITIZER_TYPES "address")
|
|
||||||
elseif(enable_tsan)
|
|
||||||
list(APPEND SANITIZER_TYPES "thread")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(enable_ubsan)
|
|
||||||
# UB sanitizer flags
|
|
||||||
list(APPEND SANITIZER_TYPES "undefined" "float-divide-by-zero")
|
|
||||||
if(is_clang)
|
|
||||||
# Clang supports additional UB checks. More info here https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
|
|
||||||
list(APPEND SANITIZER_TYPES "unsigned-integer-overflow")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Configure code model for GCC on amd64
|
|
||||||
# Use large code model for ASAN to avoid relocation errors
|
|
||||||
# Use medium code model for TSAN (large is not compatible with TSAN)
|
|
||||||
set(SANITIZERS_RELOCATION_FLAGS)
|
|
||||||
|
|
||||||
# Compiler-specific configuration
|
|
||||||
if(is_gcc)
|
|
||||||
# Disable mold, gold and lld linkers for GCC with sanitizers
|
|
||||||
# Use default linker (bfd/ld) which is more lenient with mixed code models
|
|
||||||
# This is needed since the size of instrumented binary exceeds the limits set by mold, lld and gold linkers
|
|
||||||
set(use_mold OFF CACHE BOOL "Use mold linker" FORCE)
|
|
||||||
set(use_gold OFF CACHE BOOL "Use gold linker" FORCE)
|
|
||||||
set(use_lld OFF CACHE BOOL "Use lld linker" FORCE)
|
|
||||||
message(STATUS " Disabled mold, gold, and lld linkers for GCC with sanitizers")
|
|
||||||
|
|
||||||
# Suppress false positive warnings in GCC with stringop-overflow
|
|
||||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-Wno-stringop-overflow")
|
|
||||||
|
|
||||||
if(is_amd64 AND enable_asan)
|
|
||||||
message(STATUS " Using large code model (-mcmodel=large)")
|
|
||||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-mcmodel=large")
|
|
||||||
list(APPEND SANITIZERS_RELOCATION_FLAGS "-mcmodel=large")
|
|
||||||
elseif(enable_tsan)
|
|
||||||
# GCC doesn't support atomic_thread_fence with tsan. Suppress warnings.
|
|
||||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-Wno-tsan")
|
|
||||||
message(STATUS " Using medium code model (-mcmodel=medium)")
|
|
||||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-mcmodel=medium")
|
|
||||||
list(APPEND SANITIZERS_RELOCATION_FLAGS "-mcmodel=medium")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Join sanitizer flags with commas for -fsanitize option
|
|
||||||
list(JOIN SANITIZER_TYPES "," SANITIZER_TYPES_STR)
|
|
||||||
|
|
||||||
# Add sanitizer to compile and link flags
|
|
||||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-fsanitize=${SANITIZER_TYPES_STR}")
|
|
||||||
set(SANITIZERS_LINK_FLAGS "${SANITIZERS_RELOCATION_FLAGS}" "-fsanitize=${SANITIZER_TYPES_STR}")
|
|
||||||
|
|
||||||
elseif(is_clang)
|
|
||||||
# Add ignorelist for Clang (GCC doesn't support this)
|
|
||||||
# Use CMAKE_SOURCE_DIR to get the path to the ignorelist
|
|
||||||
set(IGNORELIST_PATH "${CMAKE_SOURCE_DIR}/sanitizers/suppressions/sanitizer-ignorelist.txt")
|
|
||||||
if(NOT EXISTS "${IGNORELIST_PATH}")
|
|
||||||
message(FATAL_ERROR "Sanitizer ignorelist not found: ${IGNORELIST_PATH}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-fsanitize-ignorelist=${IGNORELIST_PATH}")
|
|
||||||
message(STATUS " Using sanitizer ignorelist: ${IGNORELIST_PATH}")
|
|
||||||
|
|
||||||
# Join sanitizer flags with commas for -fsanitize option
|
|
||||||
list(JOIN SANITIZER_TYPES "," SANITIZER_TYPES_STR)
|
|
||||||
|
|
||||||
# Add sanitizer to compile and link flags
|
|
||||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-fsanitize=${SANITIZER_TYPES_STR}")
|
|
||||||
set(SANITIZERS_LINK_FLAGS "-fsanitize=${SANITIZER_TYPES_STR}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
message(STATUS " Compile flags: ${SANITIZERS_COMPILE_FLAGS}")
|
|
||||||
message(STATUS " Link flags: ${SANITIZERS_LINK_FLAGS}")
|
|
||||||
|
|
||||||
# Apply the sanitizer flags to the 'common' interface library
|
|
||||||
# This is the same library used by XrplCompiler.cmake
|
|
||||||
target_compile_options(common INTERFACE
|
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:${SANITIZERS_COMPILE_FLAGS}>
|
|
||||||
$<$<COMPILE_LANGUAGE:C>:${SANITIZERS_COMPILE_FLAGS}>
|
|
||||||
)
|
|
||||||
|
|
||||||
# Apply linker flags
|
|
||||||
target_link_options(common INTERFACE ${SANITIZERS_LINK_FLAGS})
|
|
||||||
|
|
||||||
# Define SANITIZERS macro for BuildInfo.cpp
|
|
||||||
set(sanitizers_list)
|
|
||||||
if(enable_asan)
|
|
||||||
list(APPEND sanitizers_list "ASAN")
|
|
||||||
endif()
|
|
||||||
if(enable_tsan)
|
|
||||||
list(APPEND sanitizers_list "TSAN")
|
|
||||||
endif()
|
|
||||||
if(enable_ubsan)
|
|
||||||
list(APPEND sanitizers_list "UBSAN")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(sanitizers_list)
|
|
||||||
list(JOIN sanitizers_list "." sanitizers_str)
|
|
||||||
target_compile_definitions(common INTERFACE SANITIZERS=${sanitizers_str})
|
|
||||||
endif()
|
|
||||||
@@ -2,8 +2,6 @@
|
|||||||
sanity checks
|
sanity checks
|
||||||
#]===================================================================]
|
#]===================================================================]
|
||||||
|
|
||||||
include(CompilationEnv)
|
|
||||||
|
|
||||||
get_property(is_multiconfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
get_property(is_multiconfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||||
|
|
||||||
set (CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
|
set (CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
|
||||||
@@ -18,12 +16,14 @@ if (NOT is_multiconfig)
|
|||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (is_clang) # both Clang and AppleClang
|
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES ".*Clang") # both Clang and AppleClang
|
||||||
|
set (is_clang TRUE)
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND
|
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND
|
||||||
CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.0)
|
CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.0)
|
||||||
message (FATAL_ERROR "This project requires clang 16 or later")
|
message (FATAL_ERROR "This project requires clang 16 or later")
|
||||||
endif ()
|
endif ()
|
||||||
elseif (is_gcc)
|
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||||
|
set (is_gcc TRUE)
|
||||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)
|
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)
|
||||||
message (FATAL_ERROR "This project requires GCC 12 or later")
|
message (FATAL_ERROR "This project requires GCC 12 or later")
|
||||||
endif ()
|
endif ()
|
||||||
@@ -40,6 +40,11 @@ if (MSVC AND CMAKE_GENERATOR_PLATFORM STREQUAL "Win32")
|
|||||||
message (FATAL_ERROR "Visual Studio 32-bit build is not supported.")
|
message (FATAL_ERROR "Visual Studio 32-bit build is not supported.")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
if (NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
message (FATAL_ERROR "Xrpld requires a 64 bit target architecture.\n"
|
||||||
|
"The most likely cause of this warning is trying to build xrpld with a 32-bit OS.")
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (APPLE AND NOT HOMEBREW)
|
if (APPLE AND NOT HOMEBREW)
|
||||||
find_program (HOMEBREW brew)
|
find_program (HOMEBREW brew)
|
||||||
endif ()
|
endif ()
|
||||||
|
|||||||
@@ -2,7 +2,11 @@
|
|||||||
declare options and variables
|
declare options and variables
|
||||||
#]===================================================================]
|
#]===================================================================]
|
||||||
|
|
||||||
include(CompilationEnv)
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
|
set (is_linux TRUE)
|
||||||
|
else()
|
||||||
|
set(is_linux FALSE)
|
||||||
|
endif()
|
||||||
|
|
||||||
if("$ENV{CI}" STREQUAL "true" OR "$ENV{CONTINUOUS_INTEGRATION}" STREQUAL "true")
|
if("$ENV{CI}" STREQUAL "true" OR "$ENV{CONTINUOUS_INTEGRATION}" STREQUAL "true")
|
||||||
set(is_ci TRUE)
|
set(is_ci TRUE)
|
||||||
@@ -58,7 +62,7 @@ else()
|
|||||||
set(wextra OFF CACHE BOOL "gcc/clang only" FORCE)
|
set(wextra OFF CACHE BOOL "gcc/clang only" FORCE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(is_linux AND NOT SANITIZER)
|
if(is_linux)
|
||||||
option(BUILD_SHARED_LIBS "build shared xrpl libraries" OFF)
|
option(BUILD_SHARED_LIBS "build shared xrpl libraries" OFF)
|
||||||
option(static "link protobuf, openssl, libc++, and boost statically" ON)
|
option(static "link protobuf, openssl, libc++, and boost statically" ON)
|
||||||
option(perf "Enables flags that assist with perf recording" OFF)
|
option(perf "Enables flags that assist with perf recording" OFF)
|
||||||
@@ -103,6 +107,33 @@ option(local_protobuf
|
|||||||
option(local_grpc
|
option(local_grpc
|
||||||
"Force a local build of gRPC instead of looking for an installed version." OFF)
|
"Force a local build of gRPC instead of looking for an installed version." OFF)
|
||||||
|
|
||||||
|
# this one is a string and therefore can't be an option
|
||||||
|
set(san "" CACHE STRING "On gcc & clang, add sanitizer instrumentation")
|
||||||
|
set_property(CACHE san PROPERTY STRINGS ";undefined;memory;address;thread")
|
||||||
|
if(san)
|
||||||
|
string(TOLOWER ${san} san)
|
||||||
|
set(SAN_FLAG "-fsanitize=${san}")
|
||||||
|
set(SAN_LIB "")
|
||||||
|
if(is_gcc)
|
||||||
|
if(san STREQUAL "address")
|
||||||
|
set(SAN_LIB "asan")
|
||||||
|
elseif(san STREQUAL "thread")
|
||||||
|
set(SAN_LIB "tsan")
|
||||||
|
elseif(san STREQUAL "memory")
|
||||||
|
set(SAN_LIB "msan")
|
||||||
|
elseif(san STREQUAL "undefined")
|
||||||
|
set(SAN_LIB "ubsan")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
set(_saved_CRL ${CMAKE_REQUIRED_LIBRARIES})
|
||||||
|
set(CMAKE_REQUIRED_LIBRARIES "${SAN_FLAG};${SAN_LIB}")
|
||||||
|
check_cxx_compiler_flag(${SAN_FLAG} COMPILER_SUPPORTS_SAN)
|
||||||
|
set(CMAKE_REQUIRED_LIBRARIES ${_saved_CRL})
|
||||||
|
if(NOT COMPILER_SUPPORTS_SAN)
|
||||||
|
message(FATAL_ERROR "${san} sanitizer does not seem to be supported by your compiler")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# the remaining options are obscure and rarely used
|
# the remaining options are obscure and rarely used
|
||||||
option(beast_no_unit_test_inline
|
option(beast_no_unit_test_inline
|
||||||
"Prevents unit test definitions from being inserted into global table"
|
"Prevents unit test definitions from being inserted into global table"
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
include(CompilationEnv)
|
|
||||||
include(XrplSanitizers)
|
|
||||||
|
|
||||||
find_package(Boost REQUIRED
|
find_package(Boost REQUIRED
|
||||||
COMPONENTS
|
COMPONENTS
|
||||||
chrono
|
chrono
|
||||||
@@ -35,7 +32,7 @@ target_link_libraries(xrpl_boost
|
|||||||
if(Boost_COMPILER)
|
if(Boost_COMPILER)
|
||||||
target_link_libraries(xrpl_boost INTERFACE Boost::disable_autolinking)
|
target_link_libraries(xrpl_boost INTERFACE Boost::disable_autolinking)
|
||||||
endif()
|
endif()
|
||||||
if(SANITIZERS_ENABLED AND is_clang)
|
if(san AND is_clang)
|
||||||
# TODO: gcc does not support -fsanitize-blacklist...can we do something else
|
# TODO: gcc does not support -fsanitize-blacklist...can we do something else
|
||||||
# for gcc ?
|
# for gcc ?
|
||||||
if(NOT Boost_INCLUDE_DIRS AND TARGET Boost::headers)
|
if(NOT Boost_INCLUDE_DIRS AND TARGET Boost::headers)
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
|
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
|
||||||
"libarchive/3.8.1#ffee18995c706e02bf96e7a2f7042e0d%1765850144.736",
|
"libarchive/3.8.1#ffee18995c706e02bf96e7a2f7042e0d%1765850144.736",
|
||||||
"jemalloc/5.3.0#e951da9cf599e956cebc117880d2d9f8%1729241615.244",
|
"jemalloc/5.3.0#e951da9cf599e956cebc117880d2d9f8%1729241615.244",
|
||||||
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152",
|
|
||||||
"grpc/1.72.0#f244a57bff01e708c55a1100b12e1589%1765850193.734",
|
"grpc/1.72.0#f244a57bff01e708c55a1100b12e1589%1765850193.734",
|
||||||
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772",
|
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772",
|
||||||
|
"doctest/2.4.12#eb9fb352fb2fdfc8abb17ec270945165%1765850143.95",
|
||||||
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
|
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
|
||||||
"c-ares/1.34.5#5581c2b62a608b40bb85d965ab3ec7c8%1765850144.336",
|
"c-ares/1.34.5#5581c2b62a608b40bb85d965ab3ec7c8%1765850144.336",
|
||||||
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
|
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
include(sanitizers)
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
include(default)
|
|
||||||
{% set compiler, version, compiler_exe = detect_api.detect_default_compiler() %}
|
|
||||||
{% set sanitizers = os.getenv("SANITIZERS") %}
|
|
||||||
|
|
||||||
[conf]
|
|
||||||
{% if sanitizers %}
|
|
||||||
{% if compiler == "gcc" %}
|
|
||||||
{% if "address" in sanitizers or "thread" in sanitizers or "undefinedbehavior" in sanitizers %}
|
|
||||||
{% set sanitizer_list = [] %}
|
|
||||||
{% set model_code = "" %}
|
|
||||||
{% set extra_cxxflags = ["-fno-omit-frame-pointer", "-O1", "-Wno-stringop-overflow"] %}
|
|
||||||
|
|
||||||
{% if "address" in sanitizers %}
|
|
||||||
{% set _ = sanitizer_list.append("address") %}
|
|
||||||
{% set model_code = "-mcmodel=large" %}
|
|
||||||
{% elif "thread" in sanitizers %}
|
|
||||||
{% set _ = sanitizer_list.append("thread") %}
|
|
||||||
{% set model_code = "-mcmodel=medium" %}
|
|
||||||
{% set _ = extra_cxxflags.append("-Wno-tsan") %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if "undefinedbehavior" in sanitizers %}
|
|
||||||
{% set _ = sanitizer_list.append("undefined") %}
|
|
||||||
{% set _ = sanitizer_list.append("float-divide-by-zero") %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% set sanitizer_flags = "-fsanitize=" ~ ",".join(sanitizer_list) ~ " " ~ model_code %}
|
|
||||||
|
|
||||||
tools.build:cxxflags+=['{{sanitizer_flags}} {{" ".join(extra_cxxflags)}}']
|
|
||||||
tools.build:sharedlinkflags+=['{{sanitizer_flags}}']
|
|
||||||
tools.build:exelinkflags+=['{{sanitizer_flags}}']
|
|
||||||
{% endif %}
|
|
||||||
{% elif compiler == "apple-clang" or compiler == "clang" %}
|
|
||||||
{% if "address" in sanitizers or "thread" in sanitizers or "undefinedbehavior" in sanitizers %}
|
|
||||||
{% set sanitizer_list = [] %}
|
|
||||||
{% set extra_cxxflags = ["-fno-omit-frame-pointer", "-O1"] %}
|
|
||||||
|
|
||||||
{% if "address" in sanitizers %}
|
|
||||||
{% set _ = sanitizer_list.append("address") %}
|
|
||||||
{% elif "thread" in sanitizers %}
|
|
||||||
{% set _ = sanitizer_list.append("thread") %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if "undefinedbehavior" in sanitizers %}
|
|
||||||
{% set _ = sanitizer_list.append("undefined") %}
|
|
||||||
{% set _ = sanitizer_list.append("float-divide-by-zero") %}
|
|
||||||
{% set _ = sanitizer_list.append("unsigned-integer-overflow") %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% set sanitizer_flags = "-fsanitize=" ~ ",".join(sanitizer_list) %}
|
|
||||||
|
|
||||||
tools.build:cxxflags+=['{{sanitizer_flags}} {{" ".join(extra_cxxflags)}}']
|
|
||||||
tools.build:sharedlinkflags+=['{{sanitizer_flags}}']
|
|
||||||
tools.build:exelinkflags+=['{{sanitizer_flags}}']
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags"]
|
|
||||||
@@ -39,7 +39,7 @@ class Xrpl(ConanFile):
|
|||||||
]
|
]
|
||||||
|
|
||||||
test_requires = [
|
test_requires = [
|
||||||
"gtest/1.17.0",
|
"doctest/2.4.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
tool_requires = [
|
tool_requires = [
|
||||||
|
|||||||
207
docs/build/sanitizers.md
vendored
207
docs/build/sanitizers.md
vendored
@@ -1,207 +0,0 @@
|
|||||||
# Sanitizer Configuration for Rippled
|
|
||||||
|
|
||||||
This document explains how to properly configure and run sanitizers (AddressSanitizer, undefinedbehaviorSanitizer, ThreadSanitizer) with the xrpld project.
|
|
||||||
Corresponding suppression files are located in the `sanitizers/suppressions` directory.
|
|
||||||
|
|
||||||
- [Sanitizer Configuration for Rippled](#sanitizer-configuration-for-rippled)
|
|
||||||
- [Building with Sanitizers](#building-with-sanitizers)
|
|
||||||
- [Summary](#summary)
|
|
||||||
- [Build steps:](#build-steps)
|
|
||||||
- [Install dependencies](#install-dependencies)
|
|
||||||
- [Call CMake](#call-cmake)
|
|
||||||
- [Build](#build)
|
|
||||||
- [Running Tests with Sanitizers](#running-tests-with-sanitizers)
|
|
||||||
- [AddressSanitizer (ASAN)](#addresssanitizer-asan)
|
|
||||||
- [ThreadSanitizer (TSan)](#threadsanitizer-tsan)
|
|
||||||
- [LeakSanitizer (LSan)](#leaksanitizer-lsan)
|
|
||||||
- [UndefinedBehaviorSanitizer (UBSan)](#undefinedbehaviorsanitizer-ubsan)
|
|
||||||
- [Suppression Files](#suppression-files)
|
|
||||||
- [`asan.supp`](#asansupp)
|
|
||||||
- [`lsan.supp`](#lsansupp)
|
|
||||||
- [`ubsan.supp`](#ubsansupp)
|
|
||||||
- [`tsan.supp`](#tsansupp)
|
|
||||||
- [`sanitizer-ignorelist.txt`](#sanitizer-ignorelisttxt)
|
|
||||||
- [Troubleshooting](#troubleshooting)
|
|
||||||
- ["ASAN is ignoring requested \_\_asan_handle_no_return" warnings](#asan-is-ignoring-requested-__asan_handle_no_return-warnings)
|
|
||||||
- [Sanitizer Mismatch Errors](#sanitizer-mismatch-errors)
|
|
||||||
- [References](#references)
|
|
||||||
|
|
||||||
## Building with Sanitizers
|
|
||||||
|
|
||||||
### Summary
|
|
||||||
|
|
||||||
Follow the same instructions as mentioned in [BUILD.md](../../BUILD.md) but with the following changes:
|
|
||||||
|
|
||||||
1. Make sure you have a clean build directory.
|
|
||||||
2. Set the `SANITIZERS` environment variable before calling conan install and cmake. Only set it once. Make sure both conan and cmake read the same values.
|
|
||||||
Example: `export SANITIZERS=address,undefinedbehavior`
|
|
||||||
3. Optionally use `--profile:all sanitizers` with Conan to build dependencies with sanitizer instrumentation. [!NOTE]Building with sanitizer-instrumented dependencies is slower but produces fewer false positives.
|
|
||||||
4. Set `ASAN_OPTIONS`, `LSAN_OPTIONS`, `UBSAN_OPTIONS` and `TSAN_OPTIONS` environment variables to configure sanitizer behavior when running executables. [More details below](#running-tests-with-sanitizers).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Build steps:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /path/to/rippled
|
|
||||||
rm -rf .build
|
|
||||||
mkdir .build
|
|
||||||
cd .build
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Install dependencies
|
|
||||||
|
|
||||||
The `SANITIZERS` environment variable is used by both Conan and CMake.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export SANITIZERS=address,undefinedbehavior
|
|
||||||
# Standard build (without instrumenting dependencies)
|
|
||||||
conan install .. --output-folder . --build missing --settings build_type=Debug
|
|
||||||
|
|
||||||
# Or with sanitizer-instrumented dependencies (takes longer but fewer false positives)
|
|
||||||
conan install .. --output-folder . --profile:all sanitizers --build missing --settings build_type=Debug
|
|
||||||
```
|
|
||||||
|
|
||||||
[!CAUTION]
|
|
||||||
Do not mix Address and Thread sanitizers - they are incompatible.
|
|
||||||
|
|
||||||
Since you already set the `SANITIZERS` environment variable when running Conan, same values will be read for the next part.
|
|
||||||
|
|
||||||
#### Call CMake
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cmake .. -G Ninja \
|
|
||||||
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \
|
|
||||||
-DCMAKE_BUILD_TYPE=Debug \
|
|
||||||
-Dtests=ON -Dxrpld=ON
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Build
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cmake --build . --parallel 4
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running Tests with Sanitizers
|
|
||||||
|
|
||||||
### AddressSanitizer (ASAN)
|
|
||||||
|
|
||||||
**IMPORTANT**: ASAN with Boost produces many false positives. Use these options:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export ASAN_OPTIONS="print_stacktrace=1:detect_container_overflow=0:suppressions=path/to/asan.supp:halt_on_error=0:log_path=asan.log"
|
|
||||||
export LSAN_OPTIONS="suppressions=path/to/lsan.supp:halt_on_error=0:log_path=lsan.log"
|
|
||||||
|
|
||||||
# Run tests
|
|
||||||
./xrpld --unittest --unittest-jobs=5
|
|
||||||
```
|
|
||||||
|
|
||||||
**Why `detect_container_overflow=0`?**
|
|
||||||
|
|
||||||
- Boost intrusive containers (used in `aged_unordered_container`) trigger false positives
|
|
||||||
- Boost context switching (used in `Workers.cpp`) confuses ASAN's stack tracking
|
|
||||||
- Since we usually don't build Boost (because we don't want to instrument Boost and detect issues in Boost code) with ASAN but use Boost containers in ASAN instrumented rippled code, it generates false positives.
|
|
||||||
- Building dependencies with ASAN instrumentation reduces false positives. But we don't want to instrument dependencies like Boost with ASAN because it is slow (to compile as well as run tests) and not necessary.
|
|
||||||
- See: https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow
|
|
||||||
- More such flags are detailed [here](https://github.com/google/sanitizers/wiki/AddressSanitizerFlags)
|
|
||||||
|
|
||||||
### ThreadSanitizer (TSan)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export TSAN_OPTIONS="suppressions=path/to/tsan.supp halt_on_error=0 log_path=tsan.log"
|
|
||||||
|
|
||||||
# Run tests
|
|
||||||
./xrpld --unittest --unittest-jobs=5
|
|
||||||
```
|
|
||||||
|
|
||||||
More details [here](https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual).
|
|
||||||
|
|
||||||
### LeakSanitizer (LSan)
|
|
||||||
|
|
||||||
LSan is automatically enabled with ASAN. To disable it:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export ASAN_OPTIONS="detect_leaks=0"
|
|
||||||
```
|
|
||||||
|
|
||||||
More details [here](https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer).
|
|
||||||
|
|
||||||
### UndefinedBehaviorSanitizer (UBSan)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export UBSAN_OPTIONS="suppressions=path/to/ubsan.supp:print_stacktrace=1:halt_on_error=0:log_path=ubsan.log"
|
|
||||||
|
|
||||||
# Run tests
|
|
||||||
./xrpld --unittest --unittest-jobs=5
|
|
||||||
```
|
|
||||||
|
|
||||||
More details [here](https://clang.llvm.org/docs/undefinedbehaviorSanitizer.html).
|
|
||||||
|
|
||||||
## Suppression Files
|
|
||||||
|
|
||||||
[!NOTE] Attached files contain more details.
|
|
||||||
|
|
||||||
### [`asan.supp`](../../sanitizers/suppressions/asan.supp)
|
|
||||||
|
|
||||||
- **Purpose**: Suppress AddressSanitizer (ASAN) errors only
|
|
||||||
- **Format**: `interceptor_name:<pattern>` where pattern matches file names. Supported suppression types are:
|
|
||||||
- interceptor_name
|
|
||||||
- interceptor_via_fun
|
|
||||||
- interceptor_via_lib
|
|
||||||
- odr_violation
|
|
||||||
- **More info**: [AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer)
|
|
||||||
- **Note**: Cannot suppress stack-buffer-overflow, container-overflow, etc.
|
|
||||||
|
|
||||||
### [`lsan.supp`](../../sanitizers/suppressions/lsan.supp)
|
|
||||||
|
|
||||||
- **Purpose**: Suppress LeakSanitizer (LSan) errors only
|
|
||||||
- **Format**: `leak:<pattern>` where pattern matches function/file names
|
|
||||||
- **More info**: [LeakSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer)
|
|
||||||
|
|
||||||
### [`ubsan.supp`](../../sanitizers/suppressions/ubsan.supp)
|
|
||||||
|
|
||||||
- **Purpose**: Suppress undefinedbehaviorSanitizer errors
|
|
||||||
- **Format**: `<error_type>:<pattern>` (e.g., `unsigned-integer-overflow:protobuf`)
|
|
||||||
- **Covers**: Intentional overflows in sanitizers/suppressions libraries (protobuf, gRPC, stdlib)
|
|
||||||
- More info [UBSan suppressions](https://clang.llvm.org/docs/SanitizerSpecialCaseList.html).
|
|
||||||
|
|
||||||
### [`tsan.supp`](../../sanitizers/suppressions/tsan.supp)
|
|
||||||
|
|
||||||
- **Purpose**: Suppress ThreadSanitizer data race warnings
|
|
||||||
- **Format**: `race:<pattern>` where pattern matches function/file names
|
|
||||||
- **More info**: [ThreadSanitizer suppressions](https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions)
|
|
||||||
|
|
||||||
### [`sanitizer-ignorelist.txt`](../../sanitizers/suppressions/sanitizer-ignorelist.txt)
|
|
||||||
|
|
||||||
- **Purpose**: Compile-time ignorelist for all sanitizers
|
|
||||||
- **Usage**: Passed via `-fsanitize-ignorelist=absolute/path/to/sanitizer-ignorelist.txt`
|
|
||||||
- **Format**: `<level>:<pattern>` (e.g., `src:Workers.cpp`)
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### "ASAN is ignoring requested \_\_asan_handle_no_return" warnings
|
|
||||||
|
|
||||||
These warnings appear when using Boost context switching and are harmless. They indicate potential false positives.
|
|
||||||
|
|
||||||
### Sanitizer Mismatch Errors
|
|
||||||
|
|
||||||
If you see undefined symbols like `___tsan_atomic_load` when building with ASAN:
|
|
||||||
|
|
||||||
**Problem**: Dependencies were built with a different sanitizer than the main project.
|
|
||||||
|
|
||||||
**Solution**: Rebuild everything with the same sanitizer:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
rm -rf .build
|
|
||||||
# Then follow the build instructions above
|
|
||||||
```
|
|
||||||
|
|
||||||
Then review the log files: `asan.log.*`, `ubsan.log.*`, `tsan.log.*`
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [AddressSanitizer Wiki](https://github.com/google/sanitizers/wiki/AddressSanitizer)
|
|
||||||
- [AddressSanitizer Flags](https://github.com/google/sanitizers/wiki/AddressSanitizerFlags)
|
|
||||||
- [Container Overflow Detection](https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow)
|
|
||||||
- [UndefinedBehavior Sanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html)
|
|
||||||
- [ThreadSanitizer](https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual)
|
|
||||||
75
include/xrpl/basics/MallocTrim.h
Normal file
75
include/xrpl/basics/MallocTrim.h
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#ifndef XRPL_BASICS_MALLOCTRIM_H_INCLUDED
|
||||||
|
#define XRPL_BASICS_MALLOCTRIM_H_INCLUDED
|
||||||
|
|
||||||
|
#include <xrpl/beast/utility/Journal.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace xrpl {
|
||||||
|
|
||||||
|
// cSpell:ignore ptmalloc
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Allocator interaction note:
|
||||||
|
// - This facility invokes glibc's malloc_trim(0) on Linux/glibc to request that
|
||||||
|
// ptmalloc return free heap pages to the OS.
|
||||||
|
// - If an alternative allocator (e.g. jemalloc or tcmalloc) is linked or
|
||||||
|
// preloaded (LD_PRELOAD), calling glibc's malloc_trim typically has no effect
|
||||||
|
// on the *active* heap. The call is harmless but may not reclaim memory
|
||||||
|
// because those allocators manage their own arenas.
|
||||||
|
// - Only glibc sbrk/arena space is eligible for trimming; large mmap-backed
|
||||||
|
// allocations are usually returned to the OS on free regardless of trimming.
|
||||||
|
// - Call at known reclamation points (e.g., after cache sweeps / online delete)
|
||||||
|
// and consider rate limiting to avoid churn.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct MallocTrimReport
|
||||||
|
{
|
||||||
|
bool supported{false};
|
||||||
|
int trimResult{-1};
|
||||||
|
long rssBeforeKB{-1};
|
||||||
|
long rssAfterKB{-1};
|
||||||
|
long long durationUs{-1};
|
||||||
|
long minfltDelta{-1};
|
||||||
|
long majfltDelta{-1};
|
||||||
|
|
||||||
|
[[nodiscard]] long
|
||||||
|
deltaKB() const noexcept
|
||||||
|
{
|
||||||
|
if (rssBeforeKB < 0 || rssAfterKB < 0)
|
||||||
|
return 0;
|
||||||
|
return rssAfterKB - rssBeforeKB;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Attempt to return freed memory to the operating system.
|
||||||
|
*
|
||||||
|
* On Linux with glibc malloc, this issues ::malloc_trim(0), which may release
|
||||||
|
* free space from ptmalloc arenas back to the kernel. On other platforms, or if
|
||||||
|
* a different allocator is in use, this function is a no-op and the report will
|
||||||
|
* indicate that trimming is unsupported or had no effect.
|
||||||
|
*
|
||||||
|
* @param tag Optional identifier for logging/debugging purposes.
|
||||||
|
* @param journal Journal for diagnostic logging.
|
||||||
|
* @return Report containing before/after metrics and the trim result.
|
||||||
|
*
|
||||||
|
* @note If an alternative allocator (jemalloc/tcmalloc) is linked or preloaded,
|
||||||
|
* calling glibc's malloc_trim may have no effect on the active heap. The
|
||||||
|
* call is harmless but typically does not reclaim memory under those
|
||||||
|
* allocators.
|
||||||
|
*
|
||||||
|
* @note Only memory served from glibc's sbrk/arena heaps is eligible for trim.
|
||||||
|
* Large allocations satisfied via mmap are usually returned on free
|
||||||
|
* independently of trimming.
|
||||||
|
*
|
||||||
|
* @note Intended for use after operations that free significant memory (e.g.,
|
||||||
|
* cache sweeps, ledger cleanup, online delete). Consider rate limiting.
|
||||||
|
*/
|
||||||
|
MallocTrimReport
|
||||||
|
mallocTrim(std::optional<std::string> const& tag, beast::Journal journal);
|
||||||
|
|
||||||
|
} // namespace xrpl
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# The idea is to empty this file gradually by fixing the underlying issues and removing suppressions.
|
|
||||||
#
|
|
||||||
# ASAN_OPTIONS="print_stacktrace=1:detect_container_overflow=0:suppressions=sanitizers/suppressions/asan.supp:halt_on_error=0"
|
|
||||||
#
|
|
||||||
# The detect_container_overflow=0 option disables false positives from:
|
|
||||||
# - Boost intrusive containers (slist_iterator.hpp, hashtable.hpp, aged_unordered_container.h)
|
|
||||||
# - Boost context/coroutine stack switching (Workers.cpp, thread.h)
|
|
||||||
#
|
|
||||||
# See: https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow
|
|
||||||
|
|
||||||
# Boost
|
|
||||||
interceptor_name:boost/asio
|
|
||||||
|
|
||||||
# Leaks in Doctest tests: xrpl.test.*
|
|
||||||
interceptor_name:src/libxrpl/net/HTTPClient.cpp
|
|
||||||
interceptor_name:src/libxrpl/net/RegisterSSLCerts.cpp
|
|
||||||
interceptor_name:src/tests/libxrpl/net/HTTPClient.cpp
|
|
||||||
interceptor_name:xrpl/net/AutoSocket.h
|
|
||||||
interceptor_name:xrpl/net/HTTPClient.h
|
|
||||||
interceptor_name:xrpl/net/HTTPClientSSLContext.h
|
|
||||||
interceptor_name:xrpl/net/RegisterSSLCerts.h
|
|
||||||
|
|
||||||
# Suppress false positive stack-buffer errors in thread stack allocation
|
|
||||||
# Related to ASan's __asan_handle_no_return warnings (github.com/google/sanitizers/issues/189)
|
|
||||||
# These occur during multi-threaded test initialization on macOS
|
|
||||||
interceptor_name:memcpy
|
|
||||||
interceptor_name:__bzero
|
|
||||||
interceptor_name:__asan_memset
|
|
||||||
interceptor_name:__asan_memcpy
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# The idea is to empty this file gradually by fixing the underlying issues and removing suppresions.
|
|
||||||
|
|
||||||
# Suppress leaks detected by asan in rippled code.
|
|
||||||
leak:src/libxrpl/net/HTTPClient.cpp
|
|
||||||
leak:src/libxrpl/net/RegisterSSLCerts.cpp
|
|
||||||
leak:src/tests/libxrpl/net/HTTPClient.cpp
|
|
||||||
leak:xrpl/net/AutoSocket.h
|
|
||||||
leak:xrpl/net/HTTPClient.h
|
|
||||||
leak:xrpl/net/HTTPClientSSLContext.h
|
|
||||||
leak:xrpl/net/RegisterSSLCerts.h
|
|
||||||
leak:ripple::HTTPClient
|
|
||||||
leak:ripple::HTTPClientImp
|
|
||||||
|
|
||||||
# Suppress leaks detected by asan in boost code.
|
|
||||||
leak:boost::asio
|
|
||||||
leak:boost/asio
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# We were seeing some false positives and some repeated errors(since these are library files) in following files.
|
|
||||||
# Clang will skip instrumenting the files added here.
|
|
||||||
# We should fix the underlying issues(if any) and remove these entries.
|
|
||||||
|
|
||||||
deadlock:libxrpl/beast/utility/beast_Journal.cpp
|
|
||||||
deadlock:libxrpl/beast/utility/beast_PropertyStream.cpp
|
|
||||||
deadlock:test/beast/beast_PropertyStream_test.cpp
|
|
||||||
deadlock:xrpld/core/detail/Workers.cpp
|
|
||||||
deadlock:xrpld/core/JobQueue.cpp
|
|
||||||
|
|
||||||
race:libxrpl/beast/utility/beast_Journal.cpp
|
|
||||||
race:libxrpl/beast/utility/beast_PropertyStream.cpp
|
|
||||||
race:test/beast/beast_PropertyStream_test.cpp
|
|
||||||
race:xrpld/core/detail/Workers.cpp
|
|
||||||
race:xrpld/core/JobQueue.cpp
|
|
||||||
|
|
||||||
signal:libxrpl/beast/utility/beast_Journal.cpp
|
|
||||||
signal:libxrpl/beast/utility/beast_PropertyStream.cpp
|
|
||||||
signal:test/beast/beast_PropertyStream_test.cpp
|
|
||||||
signal:xrpld/core/detail/Workers.cpp
|
|
||||||
signal:xrpld/core/JobQueue.cpp
|
|
||||||
|
|
||||||
src:beast/utility/beast_Journal.cpp
|
|
||||||
src:beast/utility/beast_PropertyStream.cpp
|
|
||||||
src:core/detail/Workers.cpp
|
|
||||||
src:core/JobQueue.cpp
|
|
||||||
src:libxrpl/beast/utility/beast_Journal.cpp
|
|
||||||
src:test/beast/beast_PropertyStream_test.cpp
|
|
||||||
src:src/test/app/Invariants_test.cpp
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
# The idea is to empty this file gradually by fixing the underlying issues and removing suppresions.
|
|
||||||
|
|
||||||
# Suppress race in Boost ASIO scheduler detected by GCC-15
|
|
||||||
# This is a false positive in Boost's internal pipe() synchronization
|
|
||||||
race:boost/asio/
|
|
||||||
race:boost/context/
|
|
||||||
race:boost/asio/executor.hpp
|
|
||||||
race:boost::asio
|
|
||||||
|
|
||||||
# Suppress tsan related issues in rippled code.
|
|
||||||
race:src/libxrpl/basics/make_SSLContext.cpp
|
|
||||||
race:src/libxrpl/basics/Number.cpp
|
|
||||||
race:src/libxrpl/json/json_value.cpp
|
|
||||||
race:src/libxrpl/json/to_string.cpp
|
|
||||||
race:src/libxrpl/ledger/OpenView.cpp
|
|
||||||
race:src/libxrpl/net/HTTPClient.cpp
|
|
||||||
race:src/libxrpl/nodestore/backend/NuDBFactory.cpp
|
|
||||||
race:src/libxrpl/protocol/InnerObjectFormats.cpp
|
|
||||||
race:src/libxrpl/protocol/STParsedJSON.cpp
|
|
||||||
race:src/libxrpl/resource/ResourceManager.cpp
|
|
||||||
race:src/test/app/Flow_test.cpp
|
|
||||||
race:src/test/app/LedgerReplay_test.cpp
|
|
||||||
race:src/test/app/NFToken_test.cpp
|
|
||||||
race:src/test/app/Offer_test.cpp
|
|
||||||
race:src/test/app/ValidatorSite_test.cpp
|
|
||||||
race:src/test/consensus/NegativeUNL_test.cpp
|
|
||||||
race:src/test/jtx/impl/Env.cpp
|
|
||||||
race:src/test/jtx/impl/JSONRPCClient.cpp
|
|
||||||
race:src/test/jtx/impl/pay.cpp
|
|
||||||
race:src/test/jtx/impl/token.cpp
|
|
||||||
race:src/test/rpc/Book_test.cpp
|
|
||||||
race:src/xrpld/app/ledger/detail/InboundTransactions.cpp
|
|
||||||
race:src/xrpld/app/main/Application.cpp
|
|
||||||
race:src/xrpld/app/main/BasicApp.cpp
|
|
||||||
race:src/xrpld/app/main/GRPCServer.cpp
|
|
||||||
race:src/xrpld/app/misc/detail/AmendmentTable.cpp
|
|
||||||
race:src/xrpld/app/misc/FeeVoteImpl.cpp
|
|
||||||
race:src/xrpld/app/rdb/detail/Wallet.cpp
|
|
||||||
race:src/xrpld/overlay/detail/OverlayImpl.cpp
|
|
||||||
race:src/xrpld/peerfinder/detail/PeerfinderManager.cpp
|
|
||||||
race:src/xrpld/peerfinder/detail/SourceStrings.cpp
|
|
||||||
race:src/xrpld/rpc/detail/ServerHandler.cpp
|
|
||||||
race:xrpl/server/detail/Door.h
|
|
||||||
race:xrpl/server/detail/Spawn.h
|
|
||||||
race:xrpl/server/detail/ServerImpl.h
|
|
||||||
race:xrpl/nodestore/detail/DatabaseNodeImp.h
|
|
||||||
race:src/libxrpl/beast/utility/beast_Journal.cpp
|
|
||||||
race:src/test/beast/LexicalCast_test.cpp
|
|
||||||
race:ripple::ServerHandler
|
|
||||||
|
|
||||||
# More suppressions in external library code.
|
|
||||||
race:crtstuff.c
|
|
||||||
race:pipe
|
|
||||||
|
|
||||||
# Deadlock / lock-order-inversion suppressions
|
|
||||||
# Note: GCC's TSAN may not fully support all deadlock suppression patterns
|
|
||||||
deadlock:src/libxrpl/beast/utility/beast_Journal.cpp
|
|
||||||
deadlock:src/libxrpl/beast/utility/beast_PropertyStream.cpp
|
|
||||||
deadlock:src/test/beast/beast_PropertyStream_test.cpp
|
|
||||||
deadlock:src/xrpld/core/detail/Workers.cpp
|
|
||||||
deadlock:src/xrpld/app/misc/detail/Manifest.cpp
|
|
||||||
deadlock:src/xrpld/app/misc/detail/ValidatorList.cpp
|
|
||||||
deadlock:src/xrpld/app/misc/detail/ValidatorSite.cpp
|
|
||||||
|
|
||||||
signal:src/libxrpl/beast/utility/beast_Journal.cpp
|
|
||||||
signal:src/xrpld/core/detail/Workers.cpp
|
|
||||||
signal:src/xrpld/core/JobQueue.cpp
|
|
||||||
signal:ripple::Workers::Worker
|
|
||||||
|
|
||||||
# Aggressive suppressing of deadlock tsan errors
|
|
||||||
deadlock:pthread_create
|
|
||||||
deadlock:pthread_rwlock_rdlock
|
|
||||||
deadlock:boost::asio
|
|
||||||
|
|
||||||
# Suppress SEGV crashes in TSAN itself during stringbuf operations
|
|
||||||
# This appears to be a GCC-15 TSAN instrumentation issue with basic_stringbuf::str()
|
|
||||||
# Commonly triggered in beast::Journal::ScopedStream destructor
|
|
||||||
signal:std::__cxx11::basic_stringbuf
|
|
||||||
signal:basic_stringbuf
|
|
||||||
signal:basic_ostringstream
|
|
||||||
|
|
||||||
called_from_lib:libclang_rt
|
|
||||||
race:ostreambuf_iterator
|
|
||||||
race:basic_ostream
|
|
||||||
|
|
||||||
# Suppress SEGV in Boost ASIO memory allocation with GCC-15 TSAN
|
|
||||||
signal:boost::asio::aligned_new
|
|
||||||
signal:boost::asio::detail::memory
|
|
||||||
|
|
||||||
# Suppress SEGV in execute_native_thread_routine
|
|
||||||
signal:execute_native_thread_routine
|
|
||||||
|
|
||||||
# Suppress data race in Boost Context fiber management
|
|
||||||
# This is a false positive in Boost's exception state management during fiber context switching
|
|
||||||
race:__cxxabiv1::manage_exception_state
|
|
||||||
race:boost::context::fiber::resume
|
|
||||||
race:boost::asio::detail::spawned_fiber_thread
|
|
||||||
race:boost::asio::detail::spawned_fiber_thread::suspend_with
|
|
||||||
race:boost::asio::detail::spawned_fiber_thread::destroy
|
|
||||||
|
|
||||||
# Suppress data race in __tsan_memcpy called from Boost fiber operations
|
|
||||||
race:__tsan_memcpy
|
|
||||||
@@ -1,237 +0,0 @@
|
|||||||
# The idea is to empty this file gradually by fixing the underlying issues and removing suppresions.
|
|
||||||
|
|
||||||
# Suppress UBSan errors in external code by source file path
|
|
||||||
# This matches any source file under the external/ directory
|
|
||||||
alignment:external
|
|
||||||
bool:external
|
|
||||||
bounds:external
|
|
||||||
cfi:external
|
|
||||||
enum:external
|
|
||||||
float-cast-overflow:external
|
|
||||||
float-divide-by-zero:external
|
|
||||||
function:external
|
|
||||||
implicit-integer-sign-change:external
|
|
||||||
implicit-signed-integer-truncation::external
|
|
||||||
implicit-signed-integer-truncation:external
|
|
||||||
implicit-unsigned-integer-truncation:external
|
|
||||||
integer-divide-by-zero:external
|
|
||||||
invalid-builtin-use:external
|
|
||||||
invalid-objc-cast:external
|
|
||||||
nonnull-attribute:external
|
|
||||||
null:external
|
|
||||||
nullability-arg:external
|
|
||||||
nullability-assign:external
|
|
||||||
nullability-return:external
|
|
||||||
object-size:external
|
|
||||||
pointer-overflow:external
|
|
||||||
return:external
|
|
||||||
returns-nonnull-attribute:external
|
|
||||||
shift-base:external
|
|
||||||
shift-exponent:external
|
|
||||||
signed-integer-overflow:external
|
|
||||||
undefined:external
|
|
||||||
unreachable:external
|
|
||||||
unsigned-integer-overflow:external
|
|
||||||
vla-bound:external
|
|
||||||
vptr_check:external
|
|
||||||
vptr:external
|
|
||||||
|
|
||||||
# Suppress all UBSan errors in Boost libraries
|
|
||||||
# This matches any files containing "boost" in its path or name
|
|
||||||
alignment:boost
|
|
||||||
bool:boost
|
|
||||||
bounds:boost
|
|
||||||
cfi:boost
|
|
||||||
enum:boost
|
|
||||||
float-cast-overflow:boost
|
|
||||||
float-divide-by-zero:boost
|
|
||||||
function:boost
|
|
||||||
implicit-integer-sign-change:boost
|
|
||||||
implicit-signed-integer-truncation:boost
|
|
||||||
implicit-unsigned-integer-truncation:boost
|
|
||||||
integer-divide-by-zero:boost
|
|
||||||
invalid-builtin-use:boost
|
|
||||||
invalid-objc-cast:boost
|
|
||||||
nonnull-attribute:boost
|
|
||||||
null:boost
|
|
||||||
nullability-arg:boost
|
|
||||||
nullability-assign:boost
|
|
||||||
nullability-return:boost
|
|
||||||
object-size:boost
|
|
||||||
pointer-overflow:boost
|
|
||||||
return:boost
|
|
||||||
returns-nonnull-attribute:boost
|
|
||||||
shift-base:boost
|
|
||||||
shift-exponent:boost
|
|
||||||
signed-integer-overflow:boost
|
|
||||||
undefined:boost
|
|
||||||
unreachable:boost
|
|
||||||
unsigned-integer-overflow:boost
|
|
||||||
vla-bound:boost
|
|
||||||
vptr_check:boost
|
|
||||||
vptr:boost
|
|
||||||
|
|
||||||
# Google protobuf
|
|
||||||
undefined:protobuf
|
|
||||||
|
|
||||||
# Suppress UBSan errors in rippled code by source file path
|
|
||||||
undefined:src/libxrpl/basics/base64.cpp
|
|
||||||
undefined:src/libxrpl/basics/Number.cpp
|
|
||||||
undefined:src/libxrpl/beast/utility/beast_Journal.cpp
|
|
||||||
undefined:src/libxrpl/crypto/RFC1751.cpp
|
|
||||||
undefined:src/libxrpl/ledger/ApplyView.cpp
|
|
||||||
undefined:src/libxrpl/ledger/View.cpp
|
|
||||||
undefined:src/libxrpl/protocol/Permissions.cpp
|
|
||||||
undefined:src/libxrpl/protocol/STAmount.cpp
|
|
||||||
undefined:src/libxrpl/protocol/STPathSet.cpp
|
|
||||||
undefined:src/libxrpl/protocol/tokens.cpp
|
|
||||||
undefined:src/libxrpl/shamap/SHAMap.cpp
|
|
||||||
undefined:src/test/app/Batch_test.cpp
|
|
||||||
undefined:src/test/app/Invariants_test.cpp
|
|
||||||
undefined:src/test/app/NFToken_test.cpp
|
|
||||||
undefined:src/test/app/Offer_test.cpp
|
|
||||||
undefined:src/test/app/Path_test.cpp
|
|
||||||
undefined:src/test/basics/XRPAmount_test.cpp
|
|
||||||
undefined:src/test/beast/LexicalCast_test.cpp
|
|
||||||
undefined:src/test/jtx/impl/acctdelete.cpp
|
|
||||||
undefined:src/test/ledger/SkipList_test.cpp
|
|
||||||
undefined:src/test/rpc/Subscribe_test.cpp
|
|
||||||
undefined:src/tests/libxrpl/basics/RangeSet.cpp
|
|
||||||
undefined:src/xrpld/app/main/BasicApp.cpp
|
|
||||||
undefined:src/xrpld/app/main/BasicApp.cpp
|
|
||||||
undefined:src/xrpld/app/misc/detail/AmendmentTable.cpp
|
|
||||||
undefined:src/xrpld/app/misc/NetworkOPs.cpp
|
|
||||||
undefined:src/libxrpl/json/json_value.cpp
|
|
||||||
undefined:src/xrpld/app/paths/detail/StrandFlow.h
|
|
||||||
undefined:src/xrpld/app/tx/detail/NFTokenMint.cpp
|
|
||||||
undefined:src/xrpld/app/tx/detail/SetOracle.cpp
|
|
||||||
undefined:src/xrpld/core/detail/JobQueue.cpp
|
|
||||||
undefined:src/xrpld/core/detail/Workers.cpp
|
|
||||||
undefined:src/xrpld/rpc/detail/Role.cpp
|
|
||||||
undefined:src/xrpld/rpc/handlers/GetAggregatePrice.cpp
|
|
||||||
undefined:xrpl/basics/base_uint.h
|
|
||||||
undefined:xrpl/basics/DecayingSample.h
|
|
||||||
undefined:xrpl/beast/test/yield_to.h
|
|
||||||
undefined:xrpl/beast/xor_shift_engine.h
|
|
||||||
undefined:xrpl/nodestore/detail/varint.h
|
|
||||||
undefined:xrpl/peerfinder/detail/Counts.h
|
|
||||||
undefined:xrpl/protocol/nft.h
|
|
||||||
|
|
||||||
# basic_string.h:483:51: runtime error: unsigned integer overflow
|
|
||||||
unsigned-integer-overflow:basic_string.h
|
|
||||||
unsigned-integer-overflow:bits/chrono.h
|
|
||||||
unsigned-integer-overflow:bits/random.h
|
|
||||||
unsigned-integer-overflow:bits/random.tcc
|
|
||||||
unsigned-integer-overflow:bits/stl_algobase.h
|
|
||||||
unsigned-integer-overflow:bits/uniform_int_dist.h
|
|
||||||
unsigned-integer-overflow:string_view
|
|
||||||
|
|
||||||
# runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'std::size_t' (aka 'unsigned long')
|
|
||||||
unsigned-integer-overflow:src/libxrpl/basics/base64.cpp
|
|
||||||
unsigned-integer-overflow:src/libxrpl/basics/Number.cpp
|
|
||||||
unsigned-integer-overflow:src/libxrpl/crypto/RFC1751.cpp
|
|
||||||
unsigned-integer-overflow:rc/libxrpl/json/json_value.cpp
|
|
||||||
unsigned-integer-overflow:src/libxrpl/ledger/ApplyView.cpp
|
|
||||||
unsigned-integer-overflow:src/libxrpl/ledger/View.cpp
|
|
||||||
unsigned-integer-overflow:src/libxrpl/protocol/Permissions.cpp
|
|
||||||
unsigned-integer-overflow:src/libxrpl/protocol/STAmount.cpp
|
|
||||||
unsigned-integer-overflow:src/libxrpl/protocol/STPathSet.cpp
|
|
||||||
unsigned-integer-overflow:src/libxrpl/protocol/tokens.cpp
|
|
||||||
unsigned-integer-overflow:src/libxrpl/shamap/SHAMap.cpp
|
|
||||||
unsigned-integer-overflow:src/test/app/Batch_test.cpp
|
|
||||||
unsigned-integer-overflow:src/test/app/Invariants_test.cpp
|
|
||||||
unsigned-integer-overflow:src/test/app/NFToken_test.cpp
|
|
||||||
unsigned-integer-overflow:src/test/app/Offer_test.cpp
|
|
||||||
unsigned-integer-overflow:src/test/app/Path_test.cpp
|
|
||||||
unsigned-integer-overflow:src/test/basics/XRPAmount_test.cpp
|
|
||||||
unsigned-integer-overflow:src/test/beast/LexicalCast_test.cpp
|
|
||||||
unsigned-integer-overflow:src/test/jtx/impl/acctdelete.cpp
|
|
||||||
unsigned-integer-overflow:src/test/ledger/SkipList_test.cpp
|
|
||||||
unsigned-integer-overflow:src/test/rpc/Subscribe_test.cpp
|
|
||||||
unsigned-integer-overflow:src/tests/libxrpl/basics/RangeSet.cpp
|
|
||||||
unsigned-integer-overflow:src/xrpld/app/main/BasicApp.cpp
|
|
||||||
unsigned-integer-overflow:src/xrpld/app/misc/detail/AmendmentTable.cpp
|
|
||||||
unsigned-integer-overflow:src/xrpld/app/misc/NetworkOPs.cpp
|
|
||||||
unsigned-integer-overflow:src/xrpld/app/paths/detail/StrandFlow.h
|
|
||||||
unsigned-integer-overflow:src/xrpld/app/tx/detail/NFTokenMint.cpp
|
|
||||||
unsigned-integer-overflow:src/xrpld/app/tx/detail/SetOracle.cpp
|
|
||||||
unsigned-integer-overflow:src/xrpld/rpc/detail/Role.cpp
|
|
||||||
unsigned-integer-overflow:src/xrpld/rpc/handlers/GetAggregatePrice.cpp
|
|
||||||
unsigned-integer-overflow:xrpl/basics/base_uint.h
|
|
||||||
unsigned-integer-overflow:xrpl/basics/DecayingSample.h
|
|
||||||
unsigned-integer-overflow:xrpl/beast/test/yield_to.h
|
|
||||||
unsigned-integer-overflow:xrpl/beast/xor_shift_engine.h
|
|
||||||
unsigned-integer-overflow:xrpl/nodestore/detail/varint.h
|
|
||||||
unsigned-integer-overflow:xrpl/peerfinder/detail/Counts.h
|
|
||||||
unsigned-integer-overflow:xrpl/protocol/nft.h
|
|
||||||
|
|
||||||
# Rippled intentional overflows and operations
|
|
||||||
# STAmount uses intentional negation of INT64_MIN and overflow in arithmetic
|
|
||||||
signed-integer-overflow:src/libxrpl/protocol/STAmount.cpp
|
|
||||||
unsigned-integer-overflow:src/libxrpl/protocol/STAmount.cpp
|
|
||||||
|
|
||||||
# XRPAmount test intentional overflows
|
|
||||||
signed-integer-overflow:src/test/basics/XRPAmount_test.cpp
|
|
||||||
|
|
||||||
# Peerfinder intentional overflow in counter arithmetic
|
|
||||||
unsigned-integer-overflow:src/xrpld/peerfinder/detail/Counts.h
|
|
||||||
|
|
||||||
# Signed integer overflow suppressions
|
|
||||||
signed-integer-overflow:src/test/beast/LexicalCast_test.cpp
|
|
||||||
|
|
||||||
# External library suppressions
|
|
||||||
unsigned-integer-overflow:nudb/detail/xxhash.hpp
|
|
||||||
|
|
||||||
# Protobuf intentional overflows in hash functions
|
|
||||||
# Protobuf uses intentional unsigned overflow for hash computation (stringpiece.h:393)
|
|
||||||
unsigned-integer-overflow:google/protobuf/stubs/stringpiece.h
|
|
||||||
|
|
||||||
# gRPC intentional overflows
|
|
||||||
# gRPC uses intentional overflow in timer calculations
|
|
||||||
unsigned-integer-overflow:grpc
|
|
||||||
unsigned-integer-overflow:timer_manager.cc
|
|
||||||
|
|
||||||
# Standard library intentional overflows
|
|
||||||
# These are intentional overflows in random number generation and character conversion
|
|
||||||
unsigned-integer-overflow:__random/seed_seq.h
|
|
||||||
unsigned-integer-overflow:__charconv/traits.h
|
|
||||||
|
|
||||||
|
|
||||||
# Suppress errors in RocksDB
|
|
||||||
# RocksDB uses intentional unsigned integer overflows in hash functions and CRC calculations
|
|
||||||
unsigned-integer-overflow:rocks*/*/util/xxhash.h
|
|
||||||
unsigned-integer-overflow:rocks*/*/util/xxph3.h
|
|
||||||
unsigned-integer-overflow:rocks*/*/util/hash.cc
|
|
||||||
unsigned-integer-overflow:rocks*/*/util/crc32c.cc
|
|
||||||
unsigned-integer-overflow:rocks*/*/util/crc32c.h
|
|
||||||
unsigned-integer-overflow:rocks*/*/include/rocksdb/utilities/options_type.h
|
|
||||||
unsigned-integer-overflow:rocks*/*/table/format.h
|
|
||||||
unsigned-integer-overflow:rocks*/*/table/format.cc
|
|
||||||
unsigned-integer-overflow:rocks*/*/table/block_based/block_based_table_builder.cc
|
|
||||||
unsigned-integer-overflow:rocks*/*/table/block_based/reader_common.cc
|
|
||||||
unsigned-integer-overflow:rocks*/*/db/version_set.cc
|
|
||||||
|
|
||||||
# RocksDB misaligned loads (intentional for performance on ARM64)
|
|
||||||
alignment:rocks*/*/util/crc32c_arm64.cc
|
|
||||||
|
|
||||||
# nudb intentional overflows in hash functions
|
|
||||||
unsigned-integer-overflow:nudb/detail/xxhash.hpp
|
|
||||||
alignment:nudb/detail/xxhash.hpp
|
|
||||||
|
|
||||||
# Snappy compression library intentional overflows
|
|
||||||
unsigned-integer-overflow:snappy.cc
|
|
||||||
|
|
||||||
# Abseil intentional overflows
|
|
||||||
unsigned-integer-overflow:absl/strings/numbers.cc
|
|
||||||
unsigned-integer-overflow:absl/strings/internal/cord_rep_flat.h
|
|
||||||
unsigned-integer-overflow:absl/base/internal/low_level_alloc.cc
|
|
||||||
unsigned-integer-overflow:absl/hash/internal/hash.h
|
|
||||||
unsigned-integer-overflow:absl/container/internal/raw_hash_set.h
|
|
||||||
|
|
||||||
# Standard library intentional overflows in chrono duration arithmetic
|
|
||||||
unsigned-integer-overflow:__chrono/duration.h
|
|
||||||
|
|
||||||
# Suppress undefined errors in RocksDB and nudb
|
|
||||||
undefined:rocks.*/*/util/crc32c_arm64.cc
|
|
||||||
undefined:rocks.*/*/util/xxhash.h
|
|
||||||
undefined:nudb
|
|
||||||
174
src/libxrpl/basics/MallocTrim.cpp
Normal file
174
src/libxrpl/basics/MallocTrim.cpp
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
#include <xrpl/basics/Log.h>
|
||||||
|
#include <xrpl/basics/MallocTrim.h>
|
||||||
|
|
||||||
|
#include <boost/predef.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <fstream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// Require RUSAGE_THREAD for thread-scoped page fault tracking
|
||||||
|
#ifndef RUSAGE_THREAD
|
||||||
|
#error "MallocTrim rusage instrumentation requires RUSAGE_THREAD on Linux/glibc"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
pid_t const cachedPid = ::getpid();
|
||||||
|
|
||||||
|
bool
|
||||||
|
getRusageThread(struct rusage& ru)
|
||||||
|
{
|
||||||
|
return ::getrusage(RUSAGE_THREAD, &ru) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace xrpl {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||||
|
|
||||||
|
long
|
||||||
|
parseVmRSSkB(std::string const& status)
|
||||||
|
{
|
||||||
|
std::istringstream iss(status);
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
while (std::getline(iss, line))
|
||||||
|
{
|
||||||
|
// Allow leading spaces/tabs before the key.
|
||||||
|
auto const firstNonWs = line.find_first_not_of(" \t");
|
||||||
|
if (firstNonWs == std::string::npos)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
constexpr char key[] = "VmRSS:";
|
||||||
|
constexpr auto keyLen = sizeof(key) - 1;
|
||||||
|
|
||||||
|
// Require the line (after leading whitespace) to start with "VmRSS:".
|
||||||
|
// Check if we have enough characters and the substring matches.
|
||||||
|
if (firstNonWs + keyLen > line.size() ||
|
||||||
|
line.substr(firstNonWs, keyLen) != key)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Move past "VmRSS:" and any following whitespace.
|
||||||
|
auto pos = firstNonWs + keyLen;
|
||||||
|
while (pos < line.size() &&
|
||||||
|
std::isspace(static_cast<unsigned char>(line[pos])))
|
||||||
|
{
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
long value = -1;
|
||||||
|
if (std::sscanf(line.c_str() + pos, "%ld", &value) == 1)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
// Found the key but couldn't parse a number.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No VmRSS line found.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __GLIBC__ && BOOST_OS_LINUX
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
MallocTrimReport
|
||||||
|
mallocTrim(
|
||||||
|
[[maybe_unused]] std::optional<std::string> const& tag,
|
||||||
|
beast::Journal journal)
|
||||||
|
{
|
||||||
|
MallocTrimReport report;
|
||||||
|
|
||||||
|
#if !(defined(__GLIBC__) && BOOST_OS_LINUX)
|
||||||
|
JLOG(journal.debug()) << "malloc_trim not supported on this platform";
|
||||||
|
#else
|
||||||
|
|
||||||
|
report.supported = true;
|
||||||
|
|
||||||
|
// Launch malloc_trim on a separate thread (async, fire-and-forget)
|
||||||
|
std::thread trimThread([tag, journal]() {
|
||||||
|
if (journal.debug())
|
||||||
|
{
|
||||||
|
auto readFile = [](std::string const& path) -> std::string {
|
||||||
|
std::ifstream ifs(path);
|
||||||
|
if (!ifs.is_open())
|
||||||
|
return {};
|
||||||
|
return std::string(
|
||||||
|
std::istreambuf_iterator<char>(ifs),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string const tagStr = tag.value_or("default");
|
||||||
|
std::string const statusPath =
|
||||||
|
"/proc/" + std::to_string(cachedPid) + "/status";
|
||||||
|
|
||||||
|
auto const statusBefore = readFile(statusPath);
|
||||||
|
long const rssBeforeKB = detail::parseVmRSSkB(statusBefore);
|
||||||
|
|
||||||
|
struct rusage ru0
|
||||||
|
{
|
||||||
|
};
|
||||||
|
bool const have_ru0 = getRusageThread(ru0);
|
||||||
|
|
||||||
|
auto const t0 = std::chrono::steady_clock::now();
|
||||||
|
int const trimResult = ::malloc_trim(0);
|
||||||
|
auto const t1 = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
struct rusage ru1
|
||||||
|
{
|
||||||
|
};
|
||||||
|
bool const have_ru1 = getRusageThread(ru1);
|
||||||
|
|
||||||
|
auto const statusAfter = readFile(statusPath);
|
||||||
|
long const rssAfterKB = detail::parseVmRSSkB(statusAfter);
|
||||||
|
|
||||||
|
long long const durationUs =
|
||||||
|
std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
long minfltDelta = -1;
|
||||||
|
long majfltDelta = -1;
|
||||||
|
if (have_ru0 && have_ru1)
|
||||||
|
{
|
||||||
|
minfltDelta = ru1.ru_minflt - ru0.ru_minflt;
|
||||||
|
majfltDelta = ru1.ru_majflt - ru0.ru_majflt;
|
||||||
|
}
|
||||||
|
|
||||||
|
long const deltaKB = (rssBeforeKB < 0 || rssAfterKB < 0)
|
||||||
|
? 0
|
||||||
|
: (rssAfterKB - rssBeforeKB);
|
||||||
|
|
||||||
|
JLOG(journal.debug())
|
||||||
|
<< "malloc_trim tag=" << tagStr << " result=" << trimResult
|
||||||
|
<< " rss_before=" << rssBeforeKB << "kB"
|
||||||
|
<< " rss_after=" << rssAfterKB << "kB"
|
||||||
|
<< " delta=" << deltaKB << "kB"
|
||||||
|
<< " duration_us=" << durationUs
|
||||||
|
<< " minflt_delta=" << minfltDelta
|
||||||
|
<< " majflt_delta=" << majfltDelta;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
::malloc_trim(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
trimThread.detach();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xrpl
|
||||||
@@ -451,8 +451,9 @@ getTrustLineBalance(
|
|||||||
amount.clear(Issue{currency, issuer});
|
amount.clear(Issue{currency, issuer});
|
||||||
}
|
}
|
||||||
|
|
||||||
JLOG(j.trace()) << "getTrustLineBalance:" << " account="
|
JLOG(j.trace()) << "getTrustLineBalance:"
|
||||||
<< to_string(account) << " amount=" << amount.getFullText();
|
<< " account=" << to_string(account)
|
||||||
|
<< " amount=" << amount.getFullText();
|
||||||
|
|
||||||
return view.balanceHook(account, issuer, amount);
|
return view.balanceHook(account, issuer, amount);
|
||||||
}
|
}
|
||||||
@@ -699,7 +700,8 @@ xrpLiquid(
|
|||||||
STAmount const amount =
|
STAmount const amount =
|
||||||
(balance < reserve) ? STAmount{0} : balance - reserve;
|
(balance < reserve) ? STAmount{0} : balance - reserve;
|
||||||
|
|
||||||
JLOG(j.trace()) << "accountHolds:" << " account=" << to_string(id)
|
JLOG(j.trace()) << "accountHolds:"
|
||||||
|
<< " account=" << to_string(id)
|
||||||
<< " amount=" << amount.getFullText()
|
<< " amount=" << amount.getFullText()
|
||||||
<< " fullBalance=" << fullBalance.getFullText()
|
<< " fullBalance=" << fullBalance.getFullText()
|
||||||
<< " balance=" << balance.getFullText()
|
<< " balance=" << balance.getFullText()
|
||||||
@@ -1105,7 +1107,7 @@ adjustOwnerCount(
|
|||||||
std::function<void(SLE::ref)>
|
std::function<void(SLE::ref)>
|
||||||
describeOwnerDir(AccountID const& account)
|
describeOwnerDir(AccountID const& account)
|
||||||
{
|
{
|
||||||
return [account](std::shared_ptr<SLE> const& sle) {
|
return [&account](std::shared_ptr<SLE> const& sle) {
|
||||||
(*sle)[sfOwner] = account;
|
(*sle)[sfOwner] = account;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
#include <xrpl/beast/core/SemanticVersion.h>
|
#include <xrpl/beast/core/SemanticVersion.h>
|
||||||
#include <xrpl/protocol/BuildInfo.h>
|
#include <xrpl/protocol/BuildInfo.h>
|
||||||
|
|
||||||
#include <boost/preprocessor/stringize.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -22,7 +20,7 @@ namespace BuildInfo {
|
|||||||
char const* const versionString = "3.2.0-b0"
|
char const* const versionString = "3.2.0-b0"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
#if defined(DEBUG) || defined(SANITIZERS)
|
#if defined(DEBUG) || defined(SANITIZER)
|
||||||
"+"
|
"+"
|
||||||
#ifdef GIT_COMMIT_HASH
|
#ifdef GIT_COMMIT_HASH
|
||||||
GIT_COMMIT_HASH
|
GIT_COMMIT_HASH
|
||||||
@@ -30,13 +28,13 @@ char const* const versionString = "3.2.0-b0"
|
|||||||
#endif
|
#endif
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
"DEBUG"
|
"DEBUG"
|
||||||
#ifdef SANITIZERS
|
#ifdef SANITIZER
|
||||||
"."
|
"."
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SANITIZERS
|
#ifdef SANITIZER
|
||||||
BOOST_PP_STRINGIZE(SANITIZERS) // cspell: disable-line
|
BOOST_PP_STRINGIZE(SANITIZER) // cspell: disable-line
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
#include <test/jtx/multisign.h>
|
#include <test/jtx/multisign.h>
|
||||||
#include <test/jtx/xchain_bridge.h>
|
#include <test/jtx/xchain_bridge.h>
|
||||||
|
|
||||||
#include <xrpld/app/tx/apply.h>
|
|
||||||
|
|
||||||
#include <xrpl/beast/unit_test.h>
|
#include <xrpl/beast/unit_test.h>
|
||||||
#include <xrpl/json/json_value.h>
|
#include <xrpl/json/json_value.h>
|
||||||
#include <xrpl/protocol/AccountID.h>
|
#include <xrpl/protocol/AccountID.h>
|
||||||
@@ -32,7 +30,6 @@ enum class FieldType {
|
|||||||
CurrencyField,
|
CurrencyField,
|
||||||
HashField,
|
HashField,
|
||||||
HashOrObjectField,
|
HashOrObjectField,
|
||||||
FixedHashField,
|
|
||||||
IssueField,
|
IssueField,
|
||||||
ObjectField,
|
ObjectField,
|
||||||
StringField,
|
StringField,
|
||||||
@@ -89,7 +86,6 @@ getTypeName(FieldType typeID)
|
|||||||
case FieldType::CurrencyField:
|
case FieldType::CurrencyField:
|
||||||
return "Currency";
|
return "Currency";
|
||||||
case FieldType::HashField:
|
case FieldType::HashField:
|
||||||
case FieldType::FixedHashField:
|
|
||||||
return "hex string";
|
return "hex string";
|
||||||
case FieldType::HashOrObjectField:
|
case FieldType::HashOrObjectField:
|
||||||
return "hex string or object";
|
return "hex string or object";
|
||||||
@@ -206,7 +202,6 @@ class LedgerEntry_test : public beast::unit_test::suite
|
|||||||
static auto const& badBlobValues = remove({3, 7, 8, 16});
|
static auto const& badBlobValues = remove({3, 7, 8, 16});
|
||||||
static auto const& badCurrencyValues = remove({14});
|
static auto const& badCurrencyValues = remove({14});
|
||||||
static auto const& badHashValues = remove({2, 3, 7, 8, 16});
|
static auto const& badHashValues = remove({2, 3, 7, 8, 16});
|
||||||
static auto const& badFixedHashValues = remove({1, 2, 3, 4, 7, 8, 16});
|
|
||||||
static auto const& badIndexValues = remove({12, 16, 18, 19});
|
static auto const& badIndexValues = remove({12, 16, 18, 19});
|
||||||
static auto const& badUInt32Values = remove({2, 3});
|
static auto const& badUInt32Values = remove({2, 3});
|
||||||
static auto const& badUInt64Values = remove({2, 3});
|
static auto const& badUInt64Values = remove({2, 3});
|
||||||
@@ -227,8 +222,6 @@ class LedgerEntry_test : public beast::unit_test::suite
|
|||||||
return badHashValues;
|
return badHashValues;
|
||||||
case FieldType::HashOrObjectField:
|
case FieldType::HashOrObjectField:
|
||||||
return badIndexValues;
|
return badIndexValues;
|
||||||
case FieldType::FixedHashField:
|
|
||||||
return badFixedHashValues;
|
|
||||||
case FieldType::IssueField:
|
case FieldType::IssueField:
|
||||||
return badIssueValues;
|
return badIssueValues;
|
||||||
case FieldType::UInt32Field:
|
case FieldType::UInt32Field:
|
||||||
@@ -724,12 +717,7 @@ class LedgerEntry_test : public beast::unit_test::suite
|
|||||||
}
|
}
|
||||||
|
|
||||||
// negative tests
|
// negative tests
|
||||||
testMalformedField(
|
runLedgerEntryTest(env, jss::amendments);
|
||||||
env,
|
|
||||||
Json::Value{},
|
|
||||||
jss::amendments,
|
|
||||||
FieldType::FixedHashField,
|
|
||||||
"malformedRequest");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1550,12 +1538,7 @@ class LedgerEntry_test : public beast::unit_test::suite
|
|||||||
}
|
}
|
||||||
|
|
||||||
// negative tests
|
// negative tests
|
||||||
testMalformedField(
|
runLedgerEntryTest(env, jss::fee);
|
||||||
env,
|
|
||||||
Json::Value{},
|
|
||||||
jss::fee,
|
|
||||||
FieldType::FixedHashField,
|
|
||||||
"malformedRequest");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1578,12 +1561,7 @@ class LedgerEntry_test : public beast::unit_test::suite
|
|||||||
}
|
}
|
||||||
|
|
||||||
// negative tests
|
// negative tests
|
||||||
testMalformedField(
|
runLedgerEntryTest(env, jss::hashes);
|
||||||
env,
|
|
||||||
Json::Value{},
|
|
||||||
jss::hashes,
|
|
||||||
FieldType::FixedHashField,
|
|
||||||
"malformedRequest");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1708,12 +1686,7 @@ class LedgerEntry_test : public beast::unit_test::suite
|
|||||||
}
|
}
|
||||||
|
|
||||||
// negative tests
|
// negative tests
|
||||||
testMalformedField(
|
runLedgerEntryTest(env, jss::nunl);
|
||||||
env,
|
|
||||||
Json::Value{},
|
|
||||||
jss::nunl,
|
|
||||||
FieldType::FixedHashField,
|
|
||||||
"malformedRequest");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -2370,438 +2343,6 @@ class LedgerEntry_test : public beast::unit_test::suite
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test the ledger entry types that don't take parameters
|
|
||||||
void
|
|
||||||
testFixed()
|
|
||||||
{
|
|
||||||
using namespace test::jtx;
|
|
||||||
|
|
||||||
Account const alice{"alice"};
|
|
||||||
Account const bob{"bob"};
|
|
||||||
|
|
||||||
Env env{*this, envconfig([](auto cfg) {
|
|
||||||
cfg->START_UP = Config::FRESH;
|
|
||||||
return cfg;
|
|
||||||
})};
|
|
||||||
|
|
||||||
env.close();
|
|
||||||
|
|
||||||
/** Verifies that the RPC result has the expected data
|
|
||||||
*
|
|
||||||
* @param good: Indicates that the request should have succeeded
|
|
||||||
* and returned a ledger object of `expectedType` type.
|
|
||||||
* @param jv: The RPC result Json value
|
|
||||||
* @param expectedType: The type that the ledger object should
|
|
||||||
* have if "good".
|
|
||||||
* @param expectedError: Optional. The expected error if not
|
|
||||||
* good. Defaults to "entryNotFound".
|
|
||||||
*/
|
|
||||||
auto checkResult =
|
|
||||||
[&](bool good,
|
|
||||||
Json::Value const& jv,
|
|
||||||
Json::StaticString const& expectedType,
|
|
||||||
std::optional<std::string> const& expectedError = {}) {
|
|
||||||
if (good)
|
|
||||||
{
|
|
||||||
BEAST_EXPECTS(
|
|
||||||
jv.isObject() && jv.isMember(jss::result) &&
|
|
||||||
!jv[jss::result].isMember(jss::error) &&
|
|
||||||
jv[jss::result].isMember(jss::node) &&
|
|
||||||
jv[jss::result][jss::node].isMember(
|
|
||||||
sfLedgerEntryType.jsonName) &&
|
|
||||||
jv[jss::result][jss::node]
|
|
||||||
[sfLedgerEntryType.jsonName] == expectedType,
|
|
||||||
to_string(jv));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BEAST_EXPECTS(
|
|
||||||
jv.isObject() && jv.isMember(jss::result) &&
|
|
||||||
jv[jss::result].isMember(jss::error) &&
|
|
||||||
!jv[jss::result].isMember(jss::node) &&
|
|
||||||
jv[jss::result][jss::error] ==
|
|
||||||
expectedError.value_or("entryNotFound"),
|
|
||||||
to_string(jv));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Runs a series of tests for a given fixed-position ledger
|
|
||||||
* entry.
|
|
||||||
*
|
|
||||||
* @param field: The Json request field to use.
|
|
||||||
* @param expectedType: The type that the ledger object should
|
|
||||||
* have if "good".
|
|
||||||
* @param expectedKey: The keylet of the fixed object.
|
|
||||||
* @param good: Indicates whether the object is expected to
|
|
||||||
* exist.
|
|
||||||
*/
|
|
||||||
auto test = [&](Json::StaticString const& field,
|
|
||||||
Json::StaticString const& expectedType,
|
|
||||||
Keylet const& expectedKey,
|
|
||||||
bool good) {
|
|
||||||
testcase << expectedType.c_str() << (good ? "" : " not")
|
|
||||||
<< " found";
|
|
||||||
|
|
||||||
auto const hexKey = strHex(expectedKey.key);
|
|
||||||
|
|
||||||
{
|
|
||||||
// Test bad values
|
|
||||||
// "field":null
|
|
||||||
Json::Value params;
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[field] = Json::nullValue;
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(false, jv, expectedType, "malformedRequest");
|
|
||||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
// "field":"string"
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[field] = "arbitrary string";
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(false, jv, expectedType, "malformedRequest");
|
|
||||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
// "field":false
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[field] = false;
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(false, jv, expectedType, "invalidParams");
|
|
||||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
|
|
||||||
// "field":[incorrect index hash]
|
|
||||||
auto const badKey = strHex(expectedKey.key + uint256{1});
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[field] = badKey;
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(false, jv, expectedType, "entryNotFound");
|
|
||||||
BEAST_EXPECTS(
|
|
||||||
jv[jss::result][jss::index] == badKey, to_string(jv));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
// "index":"field" using API 2
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[jss::index] = field;
|
|
||||||
params[jss::api_version] = 2;
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(false, jv, expectedType, "malformedRequest");
|
|
||||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string const pdIdx = [&]() {
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
// Test good values
|
|
||||||
// Use the "field":true notation
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[field] = true;
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
// Index will always be returned for valid parameters.
|
|
||||||
std::string const pdIdx =
|
|
||||||
jv[jss::result][jss::index].asString();
|
|
||||||
BEAST_EXPECTS(hexKey == pdIdx, to_string(jv));
|
|
||||||
checkResult(good, jv, expectedType);
|
|
||||||
|
|
||||||
return pdIdx;
|
|
||||||
}
|
|
||||||
}();
|
|
||||||
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
// "field":"[index hash]"
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[field] = hexKey;
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(good, jv, expectedType);
|
|
||||||
BEAST_EXPECT(jv[jss::result][jss::index].asString() == hexKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Bad value
|
|
||||||
// Use the "index":"field" notation with API 2
|
|
||||||
Json::Value params;
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[jss::index] = field;
|
|
||||||
params[jss::api_version] = 2;
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(false, jv, expectedType, "malformedRequest");
|
|
||||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
// Use the "index":"field" notation with API 3
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[jss::index] = field;
|
|
||||||
params[jss::api_version] = 3;
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
// Index is correct either way
|
|
||||||
BEAST_EXPECT(jv[jss::result][jss::index].asString() == hexKey);
|
|
||||||
checkResult(good, jv, expectedType);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
// Use the "index":"[index hash]" notation
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[jss::index] = pdIdx;
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
// Index is correct either way
|
|
||||||
BEAST_EXPECT(jv[jss::result][jss::index].asString() == hexKey);
|
|
||||||
checkResult(good, jv, expectedType);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
test(jss::amendments, jss::Amendments, keylet::amendments(), true);
|
|
||||||
test(jss::fee, jss::FeeSettings, keylet::fees(), true);
|
|
||||||
// There won't be an nunl
|
|
||||||
test(jss::nunl, jss::NegativeUNL, keylet::negativeUNL(), false);
|
|
||||||
// Can only get the short skip list this way
|
|
||||||
test(jss::hashes, jss::LedgerHashes, keylet::skip(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
testHashes()
|
|
||||||
{
|
|
||||||
using namespace test::jtx;
|
|
||||||
|
|
||||||
Account const alice{"alice"};
|
|
||||||
Account const bob{"bob"};
|
|
||||||
|
|
||||||
Env env{*this, envconfig([](auto cfg) {
|
|
||||||
cfg->START_UP = Config::FRESH;
|
|
||||||
return cfg;
|
|
||||||
})};
|
|
||||||
|
|
||||||
env.close();
|
|
||||||
|
|
||||||
/** Verifies that the RPC result has the expected data
|
|
||||||
*
|
|
||||||
* @param good: Indicates that the request should have succeeded
|
|
||||||
* and returned a ledger object of `expectedType` type.
|
|
||||||
* @param jv: The RPC result Json value
|
|
||||||
* @param expectedCount: The number of Hashes expected in the
|
|
||||||
* object if "good".
|
|
||||||
* @param expectedError: Optional. The expected error if not
|
|
||||||
* good. Defaults to "entryNotFound".
|
|
||||||
*/
|
|
||||||
auto checkResult =
|
|
||||||
[&](bool good,
|
|
||||||
Json::Value const& jv,
|
|
||||||
int expectedCount,
|
|
||||||
std::optional<std::string> const& expectedError = {}) {
|
|
||||||
if (good)
|
|
||||||
{
|
|
||||||
BEAST_EXPECTS(
|
|
||||||
jv.isObject() && jv.isMember(jss::result) &&
|
|
||||||
!jv[jss::result].isMember(jss::error) &&
|
|
||||||
jv[jss::result].isMember(jss::node) &&
|
|
||||||
jv[jss::result][jss::node].isMember(
|
|
||||||
sfLedgerEntryType.jsonName) &&
|
|
||||||
jv[jss::result][jss::node]
|
|
||||||
[sfLedgerEntryType.jsonName] == jss::LedgerHashes,
|
|
||||||
to_string(jv));
|
|
||||||
BEAST_EXPECTS(
|
|
||||||
jv[jss::result].isMember(jss::node) &&
|
|
||||||
jv[jss::result][jss::node].isMember("Hashes") &&
|
|
||||||
jv[jss::result][jss::node]["Hashes"].size() ==
|
|
||||||
expectedCount,
|
|
||||||
to_string(jv[jss::result][jss::node]["Hashes"].size()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BEAST_EXPECTS(
|
|
||||||
jv.isObject() && jv.isMember(jss::result) &&
|
|
||||||
jv[jss::result].isMember(jss::error) &&
|
|
||||||
!jv[jss::result].isMember(jss::node) &&
|
|
||||||
jv[jss::result][jss::error] ==
|
|
||||||
expectedError.value_or("entryNotFound"),
|
|
||||||
to_string(jv));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Runs a series of tests for a given ledger index.
|
|
||||||
*
|
|
||||||
* @param ledger: The ledger index value of the "hashes" request
|
|
||||||
* parameter. May not necessarily be a number.
|
|
||||||
* @param expectedKey: The expected keylet of the object.
|
|
||||||
* @param good: Indicates whether the object is expected to
|
|
||||||
* exist.
|
|
||||||
* @param expectedCount: The number of Hashes expected in the
|
|
||||||
* object if "good".
|
|
||||||
*/
|
|
||||||
auto test = [&](Json::Value ledger,
|
|
||||||
Keylet const& expectedKey,
|
|
||||||
bool good,
|
|
||||||
int expectedCount = 0) {
|
|
||||||
testcase << "LedgerHashes: seq: " << env.current()->header().seq
|
|
||||||
<< " \"hashes\":" << to_string(ledger)
|
|
||||||
<< (good ? "" : " not") << " found";
|
|
||||||
|
|
||||||
auto const hexKey = strHex(expectedKey.key);
|
|
||||||
|
|
||||||
{
|
|
||||||
// Test bad values
|
|
||||||
// "hashes":null
|
|
||||||
Json::Value params;
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[jss::hashes] = Json::nullValue;
|
|
||||||
auto jv = env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(false, jv, 0, "malformedRequest");
|
|
||||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
// "hashes":"non-uint string"
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[jss::hashes] = "arbitrary string";
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(false, jv, 0, "malformedRequest");
|
|
||||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
// "hashes":"uint string" is invalid, too
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[jss::hashes] = "10";
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(false, jv, 0, "malformedRequest");
|
|
||||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
// "hashes":false
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[jss::hashes] = false;
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(false, jv, 0, "invalidParams");
|
|
||||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
// "hashes":-1
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[jss::hashes] = -1;
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(false, jv, 0, "internal");
|
|
||||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
|
||||||
}
|
|
||||||
|
|
||||||
// "hashes":[incorrect index hash]
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
auto const badKey = strHex(expectedKey.key + uint256{1});
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[jss::hashes] = badKey;
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(false, jv, 0, "entryNotFound");
|
|
||||||
BEAST_EXPECT(jv[jss::result][jss::index] == badKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
// Test good values
|
|
||||||
// Use the "hashes":ledger notation
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[jss::hashes] = ledger;
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(good, jv, expectedCount);
|
|
||||||
// Index will always be returned for valid parameters.
|
|
||||||
std::string const pdIdx =
|
|
||||||
jv[jss::result][jss::index].asString();
|
|
||||||
BEAST_EXPECTS(hexKey == pdIdx, strHex(pdIdx));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
// "hashes":"[index hash]"
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[jss::hashes] = hexKey;
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(good, jv, expectedCount);
|
|
||||||
// Index is correct either way
|
|
||||||
BEAST_EXPECTS(
|
|
||||||
hexKey == jv[jss::result][jss::index].asString(),
|
|
||||||
strHex(jv[jss::result][jss::index].asString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Json::Value params;
|
|
||||||
// Use the "index":"[index hash]" notation
|
|
||||||
params[jss::ledger_index] = jss::validated;
|
|
||||||
params[jss::index] = hexKey;
|
|
||||||
auto const jv =
|
|
||||||
env.rpc("json", "ledger_entry", to_string(params));
|
|
||||||
checkResult(good, jv, expectedCount);
|
|
||||||
// Index is correct either way
|
|
||||||
BEAST_EXPECTS(
|
|
||||||
hexKey == jv[jss::result][jss::index].asString(),
|
|
||||||
strHex(jv[jss::result][jss::index].asString()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// short skip list
|
|
||||||
test(true, keylet::skip(), true, 2);
|
|
||||||
// long skip list at index 0
|
|
||||||
test(1, keylet::skip(1), false);
|
|
||||||
// long skip list at index 1
|
|
||||||
test(1 << 17, keylet::skip(1 << 17), false);
|
|
||||||
|
|
||||||
// Close more ledgers, but stop short of the flag ledger
|
|
||||||
for (auto i = env.current()->seq(); i <= 250; ++i)
|
|
||||||
env.close();
|
|
||||||
|
|
||||||
// short skip list
|
|
||||||
test(true, keylet::skip(), true, 249);
|
|
||||||
// long skip list at index 0
|
|
||||||
test(1, keylet::skip(1), false);
|
|
||||||
// long skip list at index 1
|
|
||||||
test(1 << 17, keylet::skip(1 << 17), false);
|
|
||||||
|
|
||||||
// Close a flag ledger so the first "long" skip list is created
|
|
||||||
for (auto i = env.current()->seq(); i <= 260; ++i)
|
|
||||||
env.close();
|
|
||||||
|
|
||||||
// short skip list
|
|
||||||
test(true, keylet::skip(), true, 256);
|
|
||||||
// long skip list at index 0
|
|
||||||
test(1, keylet::skip(1), true, 1);
|
|
||||||
// long skip list at index 1
|
|
||||||
test(1 << 17, keylet::skip(1 << 17), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
testCLI()
|
testCLI()
|
||||||
{
|
{
|
||||||
@@ -2859,8 +2400,6 @@ public:
|
|||||||
testOracleLedgerEntry();
|
testOracleLedgerEntry();
|
||||||
testMPT();
|
testMPT();
|
||||||
testPermissionedDomain();
|
testPermissionedDomain();
|
||||||
testFixed();
|
|
||||||
testHashes();
|
|
||||||
testCLI();
|
testCLI();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Unit tests
|
# Unit tests
|
||||||
|
|
||||||
This directory contains unit tests for the project. The difference from existing `src/test` folder
|
This directory contains unit tests for the project. The difference from existing `src/test` folder
|
||||||
is that we switch to 3rd party testing framework (`gtest`). We intend to gradually move existing tests
|
is that we switch to 3rd party testing framework (doctest). We intend to gradually move existing tests
|
||||||
from our own framework to `gtest` and such tests will be moved to this new folder.
|
from our own framework to doctest and such tests will be moved to this new folder.
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
include(XrplAddTest)
|
include(XrplAddTest)
|
||||||
|
|
||||||
# Test requirements.
|
# Test requirements.
|
||||||
find_package(GTest REQUIRED)
|
find_package(doctest REQUIRED)
|
||||||
|
|
||||||
# Custom target for all tests defined in this file
|
# Custom target for all tests defined in this file
|
||||||
add_custom_target(xrpl.tests)
|
add_custom_target(xrpl.tests)
|
||||||
|
|
||||||
# Common library dependencies for the rest of the tests.
|
# Common library dependencies for the rest of the tests.
|
||||||
add_library(xrpl.imports.test INTERFACE)
|
add_library(xrpl.imports.test INTERFACE)
|
||||||
target_link_libraries(xrpl.imports.test INTERFACE gtest::gtest xrpl.libxrpl)
|
target_link_libraries(xrpl.imports.test INTERFACE doctest::doctest xrpl.libxrpl)
|
||||||
|
|
||||||
# One test for each module.
|
# One test for each module.
|
||||||
xrpl_add_test(basics)
|
xrpl_add_test(basics)
|
||||||
|
|||||||
207
src/tests/libxrpl/basics/MallocTrim.cpp
Normal file
207
src/tests/libxrpl/basics/MallocTrim.cpp
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
#include <xrpl/basics/MallocTrim.h>
|
||||||
|
|
||||||
|
#include <boost/predef.h>
|
||||||
|
|
||||||
|
#include <doctest/doctest.h>
|
||||||
|
|
||||||
|
using namespace xrpl;
|
||||||
|
|
||||||
|
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||||
|
namespace xrpl::detail {
|
||||||
|
long
|
||||||
|
parseVmRSSkB(std::string const& status);
|
||||||
|
} // namespace xrpl::detail
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TEST_CASE("MallocTrimReport structure")
|
||||||
|
{
|
||||||
|
// Test default construction
|
||||||
|
MallocTrimReport report;
|
||||||
|
CHECK(report.supported == false);
|
||||||
|
CHECK(report.trimResult == -1);
|
||||||
|
CHECK(report.rssBeforeKB == -1);
|
||||||
|
CHECK(report.rssAfterKB == -1);
|
||||||
|
CHECK(report.deltaKB() == 0);
|
||||||
|
|
||||||
|
// Test deltaKB calculation - memory freed
|
||||||
|
report.rssBeforeKB = 1000;
|
||||||
|
report.rssAfterKB = 800;
|
||||||
|
CHECK(report.deltaKB() == -200);
|
||||||
|
|
||||||
|
// Test deltaKB calculation - memory increased
|
||||||
|
report.rssBeforeKB = 500;
|
||||||
|
report.rssAfterKB = 600;
|
||||||
|
CHECK(report.deltaKB() == 100);
|
||||||
|
|
||||||
|
// Test deltaKB calculation - no change
|
||||||
|
report.rssBeforeKB = 1234;
|
||||||
|
report.rssAfterKB = 1234;
|
||||||
|
CHECK(report.deltaKB() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||||
|
TEST_CASE("parseVmRSSkB")
|
||||||
|
{
|
||||||
|
using xrpl::detail::parseVmRSSkB;
|
||||||
|
|
||||||
|
// Test standard format
|
||||||
|
{
|
||||||
|
std::string status = "VmRSS: 123456 kB\n";
|
||||||
|
long result = parseVmRSSkB(status);
|
||||||
|
CHECK(result == 123456);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with multiple lines
|
||||||
|
{
|
||||||
|
std::string status =
|
||||||
|
"Name: xrpld\n"
|
||||||
|
"VmPeak: 1234567 kB\n"
|
||||||
|
"VmSize: 1234567 kB\n"
|
||||||
|
"VmRSS: 987654 kB\n"
|
||||||
|
"VmData: 123456 kB\n";
|
||||||
|
long result = parseVmRSSkB(status);
|
||||||
|
CHECK(result == 987654);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with minimal whitespace
|
||||||
|
{
|
||||||
|
std::string status = "VmRSS: 42 kB";
|
||||||
|
long result = parseVmRSSkB(status);
|
||||||
|
CHECK(result == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with extra whitespace
|
||||||
|
{
|
||||||
|
std::string status = "VmRSS: 999999 kB";
|
||||||
|
long result = parseVmRSSkB(status);
|
||||||
|
CHECK(result == 999999);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with tabs
|
||||||
|
{
|
||||||
|
std::string status = "VmRSS:\t\t12345 kB";
|
||||||
|
long result = parseVmRSSkB(status);
|
||||||
|
// Note: tabs are not explicitly handled as spaces, this documents
|
||||||
|
// current behavior
|
||||||
|
CHECK(result == 12345);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test zero value
|
||||||
|
{
|
||||||
|
std::string status = "VmRSS: 0 kB\n";
|
||||||
|
long result = parseVmRSSkB(status);
|
||||||
|
CHECK(result == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test missing VmRSS
|
||||||
|
{
|
||||||
|
std::string status =
|
||||||
|
"Name: xrpld\n"
|
||||||
|
"VmPeak: 1234567 kB\n"
|
||||||
|
"VmSize: 1234567 kB\n";
|
||||||
|
long result = parseVmRSSkB(status);
|
||||||
|
CHECK(result == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test empty string
|
||||||
|
{
|
||||||
|
std::string status = "";
|
||||||
|
long result = parseVmRSSkB(status);
|
||||||
|
CHECK(result == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test malformed data (VmRSS but no number)
|
||||||
|
{
|
||||||
|
std::string status = "VmRSS: \n";
|
||||||
|
long result = parseVmRSSkB(status);
|
||||||
|
// sscanf should fail to parse and return -1 unchanged
|
||||||
|
CHECK(result == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test malformed data (VmRSS but invalid number)
|
||||||
|
{
|
||||||
|
std::string status = "VmRSS: abc kB\n";
|
||||||
|
long result = parseVmRSSkB(status);
|
||||||
|
// sscanf should fail and return -1 unchanged
|
||||||
|
CHECK(result == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test partial match (should not match "NotVmRSS:")
|
||||||
|
{
|
||||||
|
std::string status = "NotVmRSS: 123456 kB\n";
|
||||||
|
long result = parseVmRSSkB(status);
|
||||||
|
CHECK(result == -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TEST_CASE("mallocTrim basic functionality")
|
||||||
|
{
|
||||||
|
beast::Journal journal{beast::Journal::getNullSink()};
|
||||||
|
|
||||||
|
// Test with no tag
|
||||||
|
{
|
||||||
|
MallocTrimReport report = mallocTrim(std::nullopt, journal);
|
||||||
|
|
||||||
|
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||||
|
// On Linux with glibc, should be supported
|
||||||
|
CHECK(report.supported == true);
|
||||||
|
// trimResult should be 0 or 1 (success indicators)
|
||||||
|
CHECK(report.trimResult >= 0);
|
||||||
|
#else
|
||||||
|
// On other platforms, should be unsupported
|
||||||
|
CHECK(report.supported == false);
|
||||||
|
CHECK(report.trimResult == -1);
|
||||||
|
CHECK(report.rssBeforeKB == -1);
|
||||||
|
CHECK(report.rssAfterKB == -1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with tag
|
||||||
|
{
|
||||||
|
MallocTrimReport report =
|
||||||
|
mallocTrim(std::optional<std::string>("test_tag"), journal);
|
||||||
|
|
||||||
|
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||||
|
CHECK(report.supported == true);
|
||||||
|
CHECK(report.trimResult >= 0);
|
||||||
|
#else
|
||||||
|
CHECK(report.supported == false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("mallocTrim with debug logging")
|
||||||
|
{
|
||||||
|
beast::Journal journal{beast::Journal::getNullSink()};
|
||||||
|
|
||||||
|
MallocTrimReport report =
|
||||||
|
mallocTrim(std::optional<std::string>("debug_test"), journal);
|
||||||
|
|
||||||
|
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||||
|
CHECK(report.supported == true);
|
||||||
|
// The function should complete without crashing
|
||||||
|
#else
|
||||||
|
CHECK(report.supported == false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("mallocTrim repeated calls")
|
||||||
|
{
|
||||||
|
beast::Journal journal{beast::Journal::getNullSink()};
|
||||||
|
|
||||||
|
// Call malloc_trim multiple times to ensure it's safe
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
MallocTrimReport report = mallocTrim(
|
||||||
|
std::optional<std::string>("iteration_" + std::to_string(i)),
|
||||||
|
journal);
|
||||||
|
|
||||||
|
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||||
|
CHECK(report.supported == true);
|
||||||
|
CHECK(report.trimResult >= 0);
|
||||||
|
#else
|
||||||
|
CHECK(report.supported == false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
#include <xrpl/basics/RangeSet.h>
|
#include <xrpl/basics/RangeSet.h>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <doctest/doctest.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
using namespace xrpl;
|
using namespace xrpl;
|
||||||
|
|
||||||
TEST(RangeSet, prevMissing)
|
TEST_SUITE_BEGIN("RangeSet");
|
||||||
|
|
||||||
|
TEST_CASE("prevMissing")
|
||||||
{
|
{
|
||||||
// Set will include:
|
// Set will include:
|
||||||
// [ 0, 5]
|
// [ 0, 5]
|
||||||
@@ -29,78 +31,80 @@ TEST(RangeSet, prevMissing)
|
|||||||
|
|
||||||
expected = ((i % 10) > 6) ? (i - 1) : oneBelowRange;
|
expected = ((i % 10) > 6) ? (i - 1) : oneBelowRange;
|
||||||
}
|
}
|
||||||
EXPECT_EQ(prevMissing(set, i), expected);
|
CHECK(prevMissing(set, i) == expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RangeSet, toString)
|
TEST_CASE("toString")
|
||||||
{
|
{
|
||||||
RangeSet<std::uint32_t> set;
|
RangeSet<std::uint32_t> set;
|
||||||
EXPECT_EQ(to_string(set), "empty");
|
CHECK(to_string(set) == "empty");
|
||||||
|
|
||||||
set.insert(1);
|
set.insert(1);
|
||||||
EXPECT_EQ(to_string(set), "1");
|
CHECK(to_string(set) == "1");
|
||||||
|
|
||||||
set.insert(range(4u, 6u));
|
set.insert(range(4u, 6u));
|
||||||
EXPECT_EQ(to_string(set), "1,4-6");
|
CHECK(to_string(set) == "1,4-6");
|
||||||
|
|
||||||
set.insert(2);
|
set.insert(2);
|
||||||
EXPECT_EQ(to_string(set), "1-2,4-6");
|
CHECK(to_string(set) == "1-2,4-6");
|
||||||
|
|
||||||
set.erase(range(4u, 5u));
|
set.erase(range(4u, 5u));
|
||||||
EXPECT_EQ(to_string(set), "1-2,6");
|
CHECK(to_string(set) == "1-2,6");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RangeSet, fromString)
|
TEST_CASE("fromString")
|
||||||
{
|
{
|
||||||
RangeSet<std::uint32_t> set;
|
RangeSet<std::uint32_t> set;
|
||||||
|
|
||||||
EXPECT_FALSE(from_string(set, ""));
|
CHECK(!from_string(set, ""));
|
||||||
EXPECT_EQ(boost::icl::length(set), 0);
|
CHECK(boost::icl::length(set) == 0);
|
||||||
|
|
||||||
EXPECT_FALSE(from_string(set, "#"));
|
CHECK(!from_string(set, "#"));
|
||||||
EXPECT_EQ(boost::icl::length(set), 0);
|
CHECK(boost::icl::length(set) == 0);
|
||||||
|
|
||||||
EXPECT_FALSE(from_string(set, ","));
|
CHECK(!from_string(set, ","));
|
||||||
EXPECT_EQ(boost::icl::length(set), 0);
|
CHECK(boost::icl::length(set) == 0);
|
||||||
|
|
||||||
EXPECT_FALSE(from_string(set, ",-"));
|
CHECK(!from_string(set, ",-"));
|
||||||
EXPECT_EQ(boost::icl::length(set), 0);
|
CHECK(boost::icl::length(set) == 0);
|
||||||
|
|
||||||
EXPECT_FALSE(from_string(set, "1,,2"));
|
CHECK(!from_string(set, "1,,2"));
|
||||||
EXPECT_EQ(boost::icl::length(set), 0);
|
CHECK(boost::icl::length(set) == 0);
|
||||||
|
|
||||||
EXPECT_TRUE(from_string(set, "1"));
|
CHECK(from_string(set, "1"));
|
||||||
EXPECT_EQ(boost::icl::length(set), 1);
|
CHECK(boost::icl::length(set) == 1);
|
||||||
EXPECT_EQ(boost::icl::first(set), 1);
|
CHECK(boost::icl::first(set) == 1);
|
||||||
|
|
||||||
EXPECT_TRUE(from_string(set, "1,1"));
|
CHECK(from_string(set, "1,1"));
|
||||||
EXPECT_EQ(boost::icl::length(set), 1);
|
CHECK(boost::icl::length(set) == 1);
|
||||||
EXPECT_EQ(boost::icl::first(set), 1);
|
CHECK(boost::icl::first(set) == 1);
|
||||||
|
|
||||||
EXPECT_TRUE(from_string(set, "1-1"));
|
CHECK(from_string(set, "1-1"));
|
||||||
EXPECT_EQ(boost::icl::length(set), 1);
|
CHECK(boost::icl::length(set) == 1);
|
||||||
EXPECT_EQ(boost::icl::first(set), 1);
|
CHECK(boost::icl::first(set) == 1);
|
||||||
|
|
||||||
EXPECT_TRUE(from_string(set, "1,4-6"));
|
CHECK(from_string(set, "1,4-6"));
|
||||||
EXPECT_EQ(boost::icl::length(set), 4);
|
CHECK(boost::icl::length(set) == 4);
|
||||||
EXPECT_EQ(boost::icl::first(set), 1);
|
CHECK(boost::icl::first(set) == 1);
|
||||||
EXPECT_FALSE(boost::icl::contains(set, 2));
|
CHECK(!boost::icl::contains(set, 2));
|
||||||
EXPECT_FALSE(boost::icl::contains(set, 3));
|
CHECK(!boost::icl::contains(set, 3));
|
||||||
EXPECT_TRUE(boost::icl::contains(set, 4));
|
CHECK(boost::icl::contains(set, 4));
|
||||||
EXPECT_TRUE(boost::icl::contains(set, 5));
|
CHECK(boost::icl::contains(set, 5));
|
||||||
EXPECT_EQ(boost::icl::last(set), 6);
|
CHECK(boost::icl::last(set) == 6);
|
||||||
|
|
||||||
EXPECT_TRUE(from_string(set, "1-2,4-6"));
|
CHECK(from_string(set, "1-2,4-6"));
|
||||||
EXPECT_EQ(boost::icl::length(set), 5);
|
CHECK(boost::icl::length(set) == 5);
|
||||||
EXPECT_EQ(boost::icl::first(set), 1);
|
CHECK(boost::icl::first(set) == 1);
|
||||||
EXPECT_TRUE(boost::icl::contains(set, 2));
|
CHECK(boost::icl::contains(set, 2));
|
||||||
EXPECT_TRUE(boost::icl::contains(set, 4));
|
CHECK(boost::icl::contains(set, 4));
|
||||||
EXPECT_EQ(boost::icl::last(set), 6);
|
CHECK(boost::icl::last(set) == 6);
|
||||||
|
|
||||||
EXPECT_TRUE(from_string(set, "1-2,6"));
|
CHECK(from_string(set, "1-2,6"));
|
||||||
EXPECT_EQ(boost::icl::length(set), 3);
|
CHECK(boost::icl::length(set) == 3);
|
||||||
EXPECT_EQ(boost::icl::first(set), 1);
|
CHECK(boost::icl::first(set) == 1);
|
||||||
EXPECT_TRUE(boost::icl::contains(set, 2));
|
CHECK(boost::icl::contains(set, 2));
|
||||||
EXPECT_EQ(boost::icl::last(set), 6);
|
CHECK(boost::icl::last(set) == 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_SUITE_END();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include <xrpl/basics/Slice.h>
|
#include <xrpl/basics/Slice.h>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <doctest/doctest.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -12,35 +12,37 @@ static std::uint8_t const data[] = {
|
|||||||
0x18, 0xb4, 0x70, 0xcb, 0xf5, 0xac, 0x2d, 0x89, 0x4d, 0x19, 0x9c,
|
0x18, 0xb4, 0x70, 0xcb, 0xf5, 0xac, 0x2d, 0x89, 0x4d, 0x19, 0x9c,
|
||||||
0xf0, 0x2c, 0x15, 0xd1, 0xf9, 0x9b, 0x66, 0xd2, 0x30, 0xd3};
|
0xf0, 0x2c, 0x15, 0xd1, 0xf9, 0x9b, 0x66, 0xd2, 0x30, 0xd3};
|
||||||
|
|
||||||
TEST(Slice, equality_and_inequality)
|
TEST_SUITE_BEGIN("Slice");
|
||||||
|
|
||||||
|
TEST_CASE("equality & inequality")
|
||||||
{
|
{
|
||||||
Slice const s0{};
|
Slice const s0{};
|
||||||
|
|
||||||
EXPECT_EQ(s0.size(), 0);
|
CHECK(s0.size() == 0);
|
||||||
EXPECT_EQ(s0.data(), nullptr);
|
CHECK(s0.data() == nullptr);
|
||||||
EXPECT_EQ(s0, s0);
|
CHECK(s0 == s0);
|
||||||
|
|
||||||
// Test slices of equal and unequal size pointing to same data:
|
// Test slices of equal and unequal size pointing to same data:
|
||||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||||
{
|
{
|
||||||
Slice const s1{data, i};
|
Slice const s1{data, i};
|
||||||
|
|
||||||
EXPECT_EQ(s1.size(), i);
|
CHECK(s1.size() == i);
|
||||||
EXPECT_NE(s1.data(), nullptr);
|
CHECK(s1.data() != nullptr);
|
||||||
|
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
EXPECT_EQ(s1, s0);
|
CHECK(s1 == s0);
|
||||||
else
|
else
|
||||||
EXPECT_NE(s1, s0);
|
CHECK(s1 != s0);
|
||||||
|
|
||||||
for (std::size_t j = 0; j != sizeof(data); ++j)
|
for (std::size_t j = 0; j != sizeof(data); ++j)
|
||||||
{
|
{
|
||||||
Slice const s2{data, j};
|
Slice const s2{data, j};
|
||||||
|
|
||||||
if (i == j)
|
if (i == j)
|
||||||
EXPECT_EQ(s1, s2);
|
CHECK(s1 == s2);
|
||||||
else
|
else
|
||||||
EXPECT_NE(s1, s2);
|
CHECK(s1 != s2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,22 +53,22 @@ TEST(Slice, equality_and_inequality)
|
|||||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||||
a[i] = b[i] = data[i];
|
a[i] = b[i] = data[i];
|
||||||
|
|
||||||
EXPECT_EQ(makeSlice(a), makeSlice(b));
|
CHECK(makeSlice(a) == makeSlice(b));
|
||||||
b[7]++;
|
b[7]++;
|
||||||
EXPECT_NE(makeSlice(a), makeSlice(b));
|
CHECK(makeSlice(a) != makeSlice(b));
|
||||||
a[7]++;
|
a[7]++;
|
||||||
EXPECT_EQ(makeSlice(a), makeSlice(b));
|
CHECK(makeSlice(a) == makeSlice(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Slice, indexing)
|
TEST_CASE("indexing")
|
||||||
{
|
{
|
||||||
Slice const s{data, sizeof(data)};
|
Slice const s{data, sizeof(data)};
|
||||||
|
|
||||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||||
EXPECT_EQ(s[i], data[i]);
|
CHECK(s[i] == data[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Slice, advancing)
|
TEST_CASE("advancing")
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < sizeof(data); ++i)
|
for (std::size_t i = 0; i < sizeof(data); ++i)
|
||||||
{
|
{
|
||||||
@@ -75,8 +77,10 @@ TEST(Slice, advancing)
|
|||||||
Slice s(data + i, sizeof(data) - i);
|
Slice s(data + i, sizeof(data) - i);
|
||||||
s += j;
|
s += j;
|
||||||
|
|
||||||
EXPECT_EQ(s.data(), data + i + j);
|
CHECK(s.data() == data + i + j);
|
||||||
EXPECT_EQ(s.size(), sizeof(data) - i - j);
|
CHECK(s.size() == sizeof(data) - i - j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_SUITE_END();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include <xrpl/basics/base64.h>
|
#include <xrpl/basics/base64.h>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <doctest/doctest.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -10,11 +10,11 @@ static void
|
|||||||
check(std::string const& in, std::string const& out)
|
check(std::string const& in, std::string const& out)
|
||||||
{
|
{
|
||||||
auto const encoded = base64_encode(in);
|
auto const encoded = base64_encode(in);
|
||||||
EXPECT_EQ(encoded, out);
|
CHECK(encoded == out);
|
||||||
EXPECT_EQ(base64_decode(encoded), in);
|
CHECK(base64_decode(encoded) == in);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(base64, base64)
|
TEST_CASE("base64")
|
||||||
{
|
{
|
||||||
// cspell: disable
|
// cspell: disable
|
||||||
check("", "");
|
check("", "");
|
||||||
@@ -46,5 +46,5 @@ TEST(base64, base64)
|
|||||||
|
|
||||||
std::string const notBase64 = "not_base64!!";
|
std::string const notBase64 = "not_base64!!";
|
||||||
std::string const truncated = "not";
|
std::string const truncated = "not";
|
||||||
EXPECT_EQ(base64_decode(notBase64), base64_decode(truncated));
|
CHECK(base64_decode(notBase64) == base64_decode(truncated));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
#include <xrpl/basics/contract.h>
|
#include <xrpl/basics/contract.h>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <doctest/doctest.h>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
using namespace xrpl;
|
using namespace xrpl;
|
||||||
|
|
||||||
TEST(contract, contract)
|
TEST_CASE("contract")
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -15,7 +15,7 @@ TEST(contract, contract)
|
|||||||
}
|
}
|
||||||
catch (std::runtime_error const& e1)
|
catch (std::runtime_error const& e1)
|
||||||
{
|
{
|
||||||
EXPECT_STREQ(e1.what(), "Throw test");
|
CHECK(std::string(e1.what()) == "Throw test");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -23,15 +23,15 @@ TEST(contract, contract)
|
|||||||
}
|
}
|
||||||
catch (std::runtime_error const& e2)
|
catch (std::runtime_error const& e2)
|
||||||
{
|
{
|
||||||
EXPECT_STREQ(e2.what(), "Throw test");
|
CHECK(std::string(e2.what()) == "Throw test");
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
FAIL() << "std::runtime_error should have been re-caught";
|
CHECK(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
FAIL() << "std::runtime_error should have been caught the first time";
|
CHECK(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,2 @@
|
|||||||
#include <gtest/gtest.h>
|
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||||
|
#include <doctest/doctest.h>
|
||||||
int
|
|
||||||
main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
|
||||||
return RUN_ALL_TESTS();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,45 +1,45 @@
|
|||||||
#include <xrpl/basics/mulDiv.h>
|
#include <xrpl/basics/mulDiv.h>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <doctest/doctest.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
using namespace xrpl;
|
using namespace xrpl;
|
||||||
|
|
||||||
TEST(mulDiv, mulDiv)
|
TEST_CASE("mulDiv")
|
||||||
{
|
{
|
||||||
auto const max = std::numeric_limits<std::uint64_t>::max();
|
auto const max = std::numeric_limits<std::uint64_t>::max();
|
||||||
std::uint64_t const max32 = std::numeric_limits<std::uint32_t>::max();
|
std::uint64_t const max32 = std::numeric_limits<std::uint32_t>::max();
|
||||||
|
|
||||||
auto result = mulDiv(85, 20, 5);
|
auto result = mulDiv(85, 20, 5);
|
||||||
ASSERT_TRUE(result.has_value());
|
REQUIRE(result);
|
||||||
EXPECT_EQ(*result, 340);
|
CHECK(*result == 340);
|
||||||
result = mulDiv(20, 85, 5);
|
result = mulDiv(20, 85, 5);
|
||||||
ASSERT_TRUE(result.has_value());
|
REQUIRE(result);
|
||||||
EXPECT_EQ(*result, 340);
|
CHECK(*result == 340);
|
||||||
|
|
||||||
result = mulDiv(0, max - 1, max - 3);
|
result = mulDiv(0, max - 1, max - 3);
|
||||||
ASSERT_TRUE(result.has_value());
|
REQUIRE(result);
|
||||||
EXPECT_EQ(*result, 0);
|
CHECK(*result == 0);
|
||||||
result = mulDiv(max - 1, 0, max - 3);
|
result = mulDiv(max - 1, 0, max - 3);
|
||||||
ASSERT_TRUE(result.has_value());
|
REQUIRE(result);
|
||||||
EXPECT_EQ(*result, 0);
|
CHECK(*result == 0);
|
||||||
|
|
||||||
result = mulDiv(max, 2, max / 2);
|
result = mulDiv(max, 2, max / 2);
|
||||||
ASSERT_TRUE(result.has_value());
|
REQUIRE(result);
|
||||||
EXPECT_EQ(*result, 4);
|
CHECK(*result == 4);
|
||||||
result = mulDiv(max, 1000, max / 1000);
|
result = mulDiv(max, 1000, max / 1000);
|
||||||
ASSERT_TRUE(result.has_value());
|
REQUIRE(result);
|
||||||
EXPECT_EQ(*result, 1000000);
|
CHECK(*result == 1000000);
|
||||||
result = mulDiv(max, 1000, max / 1001);
|
result = mulDiv(max, 1000, max / 1001);
|
||||||
ASSERT_TRUE(result.has_value());
|
REQUIRE(result);
|
||||||
EXPECT_EQ(*result, 1001000);
|
CHECK(*result == 1001000);
|
||||||
result = mulDiv(max32 + 1, max32 + 1, 5);
|
result = mulDiv(max32 + 1, max32 + 1, 5);
|
||||||
ASSERT_TRUE(result.has_value());
|
REQUIRE(result);
|
||||||
EXPECT_EQ(*result, 3689348814741910323);
|
CHECK(*result == 3689348814741910323);
|
||||||
|
|
||||||
// Overflow
|
// Overflow
|
||||||
result = mulDiv(max - 1, max - 2, 5);
|
result = mulDiv(max - 1, max - 2, 5);
|
||||||
EXPECT_FALSE(result.has_value());
|
CHECK(!result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#include <xrpl/basics/scope.h>
|
#include <xrpl/basics/scope.h>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <doctest/doctest.h>
|
||||||
|
|
||||||
using namespace xrpl;
|
using namespace xrpl;
|
||||||
|
|
||||||
TEST(scope, scope_exit)
|
TEST_CASE("scope_exit")
|
||||||
{
|
{
|
||||||
// scope_exit always executes the functor on destruction,
|
// scope_exit always executes the functor on destruction,
|
||||||
// unless release() is called
|
// unless release() is called
|
||||||
@@ -12,23 +12,23 @@ TEST(scope, scope_exit)
|
|||||||
{
|
{
|
||||||
scope_exit x{[&i]() { i = 1; }};
|
scope_exit x{[&i]() { i = 1; }};
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 1);
|
CHECK(i == 1);
|
||||||
{
|
{
|
||||||
scope_exit x{[&i]() { i = 2; }};
|
scope_exit x{[&i]() { i = 2; }};
|
||||||
x.release();
|
x.release();
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 1);
|
CHECK(i == 1);
|
||||||
{
|
{
|
||||||
scope_exit x{[&i]() { i += 2; }};
|
scope_exit x{[&i]() { i += 2; }};
|
||||||
auto x2 = std::move(x);
|
auto x2 = std::move(x);
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 3);
|
CHECK(i == 3);
|
||||||
{
|
{
|
||||||
scope_exit x{[&i]() { i = 4; }};
|
scope_exit x{[&i]() { i = 4; }};
|
||||||
x.release();
|
x.release();
|
||||||
auto x2 = std::move(x);
|
auto x2 = std::move(x);
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 3);
|
CHECK(i == 3);
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -39,7 +39,7 @@ TEST(scope, scope_exit)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 5);
|
CHECK(i == 5);
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -51,10 +51,10 @@ TEST(scope, scope_exit)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 5);
|
CHECK(i == 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(scope, scope_fail)
|
TEST_CASE("scope_fail")
|
||||||
{
|
{
|
||||||
// scope_fail executes the functor on destruction only
|
// scope_fail executes the functor on destruction only
|
||||||
// if an exception is unwinding, unless release() is called
|
// if an exception is unwinding, unless release() is called
|
||||||
@@ -62,23 +62,23 @@ TEST(scope, scope_fail)
|
|||||||
{
|
{
|
||||||
scope_fail x{[&i]() { i = 1; }};
|
scope_fail x{[&i]() { i = 1; }};
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 0);
|
CHECK(i == 0);
|
||||||
{
|
{
|
||||||
scope_fail x{[&i]() { i = 2; }};
|
scope_fail x{[&i]() { i = 2; }};
|
||||||
x.release();
|
x.release();
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 0);
|
CHECK(i == 0);
|
||||||
{
|
{
|
||||||
scope_fail x{[&i]() { i = 3; }};
|
scope_fail x{[&i]() { i = 3; }};
|
||||||
auto x2 = std::move(x);
|
auto x2 = std::move(x);
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 0);
|
CHECK(i == 0);
|
||||||
{
|
{
|
||||||
scope_fail x{[&i]() { i = 4; }};
|
scope_fail x{[&i]() { i = 4; }};
|
||||||
x.release();
|
x.release();
|
||||||
auto x2 = std::move(x);
|
auto x2 = std::move(x);
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 0);
|
CHECK(i == 0);
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -89,7 +89,7 @@ TEST(scope, scope_fail)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 5);
|
CHECK(i == 5);
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -101,10 +101,10 @@ TEST(scope, scope_fail)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 5);
|
CHECK(i == 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(scope, scope_success)
|
TEST_CASE("scope_success")
|
||||||
{
|
{
|
||||||
// scope_success executes the functor on destruction only
|
// scope_success executes the functor on destruction only
|
||||||
// if an exception is not unwinding, unless release() is called
|
// if an exception is not unwinding, unless release() is called
|
||||||
@@ -112,23 +112,23 @@ TEST(scope, scope_success)
|
|||||||
{
|
{
|
||||||
scope_success x{[&i]() { i = 1; }};
|
scope_success x{[&i]() { i = 1; }};
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 1);
|
CHECK(i == 1);
|
||||||
{
|
{
|
||||||
scope_success x{[&i]() { i = 2; }};
|
scope_success x{[&i]() { i = 2; }};
|
||||||
x.release();
|
x.release();
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 1);
|
CHECK(i == 1);
|
||||||
{
|
{
|
||||||
scope_success x{[&i]() { i += 2; }};
|
scope_success x{[&i]() { i += 2; }};
|
||||||
auto x2 = std::move(x);
|
auto x2 = std::move(x);
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 3);
|
CHECK(i == 3);
|
||||||
{
|
{
|
||||||
scope_success x{[&i]() { i = 4; }};
|
scope_success x{[&i]() { i = 4; }};
|
||||||
x.release();
|
x.release();
|
||||||
auto x2 = std::move(x);
|
auto x2 = std::move(x);
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 3);
|
CHECK(i == 3);
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -139,7 +139,7 @@ TEST(scope, scope_success)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 3);
|
CHECK(i == 3);
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -151,5 +151,5 @@ TEST(scope, scope_success)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXPECT_EQ(i, 3);
|
CHECK(i == 3);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include <xrpl/basics/tagged_integer.h>
|
#include <xrpl/basics/tagged_integer.h>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <doctest/doctest.h>
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
@@ -102,123 +102,127 @@ static_assert(
|
|||||||
!std::is_convertible<TagUInt2, TagUInt3>::value,
|
!std::is_convertible<TagUInt2, TagUInt3>::value,
|
||||||
"TagUInt2 should not be convertible to a TagUInt3");
|
"TagUInt2 should not be convertible to a TagUInt3");
|
||||||
|
|
||||||
|
TEST_SUITE_BEGIN("tagged_integer");
|
||||||
|
|
||||||
using TagInt = tagged_integer<std::int32_t, Tag1>;
|
using TagInt = tagged_integer<std::int32_t, Tag1>;
|
||||||
|
|
||||||
TEST(tagged_integer, comparison_operators)
|
TEST_CASE("comparison operators")
|
||||||
{
|
{
|
||||||
TagInt const zero(0);
|
TagInt const zero(0);
|
||||||
TagInt const one(1);
|
TagInt const one(1);
|
||||||
|
|
||||||
EXPECT_TRUE(one == one);
|
CHECK(one == one);
|
||||||
EXPECT_FALSE(one == zero);
|
CHECK(!(one == zero));
|
||||||
|
|
||||||
EXPECT_TRUE(one != zero);
|
CHECK(one != zero);
|
||||||
EXPECT_FALSE(one != one);
|
CHECK(!(one != one));
|
||||||
|
|
||||||
EXPECT_TRUE(zero < one);
|
CHECK(zero < one);
|
||||||
EXPECT_FALSE(one < zero);
|
CHECK(!(one < zero));
|
||||||
|
|
||||||
EXPECT_TRUE(one > zero);
|
CHECK(one > zero);
|
||||||
EXPECT_FALSE(zero > one);
|
CHECK(!(zero > one));
|
||||||
|
|
||||||
EXPECT_TRUE(one >= one);
|
CHECK(one >= one);
|
||||||
EXPECT_TRUE(one >= zero);
|
CHECK(one >= zero);
|
||||||
EXPECT_FALSE(zero >= one);
|
CHECK(!(zero >= one));
|
||||||
|
|
||||||
EXPECT_TRUE(zero <= one);
|
CHECK(zero <= one);
|
||||||
EXPECT_TRUE(zero <= zero);
|
CHECK(zero <= zero);
|
||||||
EXPECT_FALSE(one <= zero);
|
CHECK(!(one <= zero));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(tagged_integer, increment_decrement_operators)
|
TEST_CASE("increment / decrement operators")
|
||||||
{
|
{
|
||||||
TagInt const zero(0);
|
TagInt const zero(0);
|
||||||
TagInt const one(1);
|
TagInt const one(1);
|
||||||
TagInt a{0};
|
TagInt a{0};
|
||||||
++a;
|
++a;
|
||||||
EXPECT_EQ(a, one);
|
CHECK(a == one);
|
||||||
--a;
|
--a;
|
||||||
EXPECT_EQ(a, zero);
|
CHECK(a == zero);
|
||||||
a++;
|
a++;
|
||||||
EXPECT_EQ(a, one);
|
CHECK(a == one);
|
||||||
a--;
|
a--;
|
||||||
EXPECT_EQ(a, zero);
|
CHECK(a == zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(tagged_integer, arithmetic_operators)
|
TEST_CASE("arithmetic operators")
|
||||||
{
|
{
|
||||||
TagInt a{-2};
|
TagInt a{-2};
|
||||||
EXPECT_EQ(+a, TagInt{-2});
|
CHECK(+a == TagInt{-2});
|
||||||
EXPECT_EQ(-a, TagInt{2});
|
CHECK(-a == TagInt{2});
|
||||||
EXPECT_EQ(TagInt{-3} + TagInt{4}, TagInt{1});
|
CHECK(TagInt{-3} + TagInt{4} == TagInt{1});
|
||||||
EXPECT_EQ(TagInt{-3} - TagInt{4}, TagInt{-7});
|
CHECK(TagInt{-3} - TagInt{4} == TagInt{-7});
|
||||||
EXPECT_EQ(TagInt{-3} * TagInt{4}, TagInt{-12});
|
CHECK(TagInt{-3} * TagInt{4} == TagInt{-12});
|
||||||
EXPECT_EQ(TagInt{8} / TagInt{4}, TagInt{2});
|
CHECK(TagInt{8} / TagInt{4} == TagInt{2});
|
||||||
EXPECT_EQ(TagInt{7} % TagInt{4}, TagInt{3});
|
CHECK(TagInt{7} % TagInt{4} == TagInt{3});
|
||||||
|
|
||||||
EXPECT_EQ(~TagInt{8}, TagInt{~TagInt::value_type{8}});
|
CHECK(~TagInt{8} == TagInt{~TagInt::value_type{8}});
|
||||||
EXPECT_EQ((TagInt{6} & TagInt{3}), TagInt{2});
|
CHECK((TagInt{6} & TagInt{3}) == TagInt{2});
|
||||||
EXPECT_EQ((TagInt{6} | TagInt{3}), TagInt{7});
|
CHECK((TagInt{6} | TagInt{3}) == TagInt{7});
|
||||||
EXPECT_EQ((TagInt{6} ^ TagInt{3}), TagInt{5});
|
CHECK((TagInt{6} ^ TagInt{3}) == TagInt{5});
|
||||||
|
|
||||||
EXPECT_EQ((TagInt{4} << TagInt{2}), TagInt{16});
|
CHECK((TagInt{4} << TagInt{2}) == TagInt{16});
|
||||||
EXPECT_EQ((TagInt{16} >> TagInt{2}), TagInt{4});
|
CHECK((TagInt{16} >> TagInt{2}) == TagInt{4});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(tagged_integer, assignment_operators)
|
TEST_CASE("assignment operators")
|
||||||
{
|
{
|
||||||
TagInt a{-2};
|
TagInt a{-2};
|
||||||
TagInt b{0};
|
TagInt b{0};
|
||||||
b = a;
|
b = a;
|
||||||
EXPECT_EQ(b, TagInt{-2});
|
CHECK(b == TagInt{-2});
|
||||||
|
|
||||||
// -3 + 4 == 1
|
// -3 + 4 == 1
|
||||||
a = TagInt{-3};
|
a = TagInt{-3};
|
||||||
a += TagInt{4};
|
a += TagInt{4};
|
||||||
EXPECT_EQ(a, TagInt{1});
|
CHECK(a == TagInt{1});
|
||||||
|
|
||||||
// -3 - 4 == -7
|
// -3 - 4 == -7
|
||||||
a = TagInt{-3};
|
a = TagInt{-3};
|
||||||
a -= TagInt{4};
|
a -= TagInt{4};
|
||||||
EXPECT_EQ(a, TagInt{-7});
|
CHECK(a == TagInt{-7});
|
||||||
|
|
||||||
// -3 * 4 == -12
|
// -3 * 4 == -12
|
||||||
a = TagInt{-3};
|
a = TagInt{-3};
|
||||||
a *= TagInt{4};
|
a *= TagInt{4};
|
||||||
EXPECT_EQ(a, TagInt{-12});
|
CHECK(a == TagInt{-12});
|
||||||
|
|
||||||
// 8/4 == 2
|
// 8/4 == 2
|
||||||
a = TagInt{8};
|
a = TagInt{8};
|
||||||
a /= TagInt{4};
|
a /= TagInt{4};
|
||||||
EXPECT_EQ(a, TagInt{2});
|
CHECK(a == TagInt{2});
|
||||||
|
|
||||||
// 7 % 4 == 3
|
// 7 % 4 == 3
|
||||||
a = TagInt{7};
|
a = TagInt{7};
|
||||||
a %= TagInt{4};
|
a %= TagInt{4};
|
||||||
EXPECT_EQ(a, TagInt{3});
|
CHECK(a == TagInt{3});
|
||||||
|
|
||||||
// 6 & 3 == 2
|
// 6 & 3 == 2
|
||||||
a = TagInt{6};
|
a = TagInt{6};
|
||||||
a /= TagInt{3};
|
a /= TagInt{3};
|
||||||
EXPECT_EQ(a, TagInt{2});
|
CHECK(a == TagInt{2});
|
||||||
|
|
||||||
// 6 | 3 == 7
|
// 6 | 3 == 7
|
||||||
a = TagInt{6};
|
a = TagInt{6};
|
||||||
a |= TagInt{3};
|
a |= TagInt{3};
|
||||||
EXPECT_EQ(a, TagInt{7});
|
CHECK(a == TagInt{7});
|
||||||
|
|
||||||
// 6 ^ 3 == 5
|
// 6 ^ 3 == 5
|
||||||
a = TagInt{6};
|
a = TagInt{6};
|
||||||
a ^= TagInt{3};
|
a ^= TagInt{3};
|
||||||
EXPECT_EQ(a, TagInt{5});
|
CHECK(a == TagInt{5});
|
||||||
|
|
||||||
// 4 << 2 == 16
|
// 4 << 2 == 16
|
||||||
a = TagInt{4};
|
a = TagInt{4};
|
||||||
a <<= TagInt{2};
|
a <<= TagInt{2};
|
||||||
EXPECT_EQ(a, TagInt{16});
|
CHECK(a == TagInt{16});
|
||||||
|
|
||||||
// 16 >> 2 == 4
|
// 16 >> 2 == 4
|
||||||
a = TagInt{16};
|
a = TagInt{16};
|
||||||
a >>= TagInt{2};
|
a >>= TagInt{2};
|
||||||
EXPECT_EQ(a, TagInt{4});
|
CHECK(a == TagInt{4});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_SUITE_END();
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
#include <xrpl/crypto/csprng.h>
|
#include <xrpl/crypto/csprng.h>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <doctest/doctest.h>
|
||||||
|
|
||||||
using namespace xrpl;
|
using namespace xrpl;
|
||||||
|
|
||||||
TEST(csprng, get_values)
|
TEST_CASE("get values")
|
||||||
{
|
{
|
||||||
auto& engine = crypto_prng();
|
auto& engine = crypto_prng();
|
||||||
auto rand_val = engine();
|
auto rand_val = engine();
|
||||||
EXPECT_GE(rand_val, engine.min());
|
CHECK(rand_val >= engine.min());
|
||||||
EXPECT_LE(rand_val, engine.max());
|
CHECK(rand_val <= engine.max());
|
||||||
uint16_t twoByte{0};
|
uint16_t twoByte{0};
|
||||||
engine(&twoByte, sizeof(uint16_t));
|
engine(&twoByte, sizeof(uint16_t));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,2 @@
|
|||||||
#include <gtest/gtest.h>
|
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||||
|
#include <doctest/doctest.h>
|
||||||
int
|
|
||||||
main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
|
||||||
return RUN_ALL_TESTS();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,29 +2,31 @@
|
|||||||
#include <xrpl/json/json_reader.h>
|
#include <xrpl/json/json_reader.h>
|
||||||
#include <xrpl/json/json_writer.h>
|
#include <xrpl/json/json_writer.h>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <doctest/doctest.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
using namespace xrpl;
|
using namespace xrpl;
|
||||||
using namespace Json;
|
using namespace Json;
|
||||||
|
|
||||||
|
TEST_SUITE_BEGIN("JsonOutput");
|
||||||
|
|
||||||
static void
|
static void
|
||||||
checkOutput(std::string const& valueDesc)
|
checkOutput(std::string const& valueDesc)
|
||||||
{
|
{
|
||||||
std::string output;
|
std::string output;
|
||||||
Json::Value value;
|
Json::Value value;
|
||||||
ASSERT_TRUE(Json::Reader().parse(valueDesc, value));
|
REQUIRE(Json::Reader().parse(valueDesc, value));
|
||||||
auto out = stringOutput(output);
|
auto out = stringOutput(output);
|
||||||
outputJson(value, out);
|
outputJson(value, out);
|
||||||
|
|
||||||
auto expected = Json::FastWriter().write(value);
|
auto expected = Json::FastWriter().write(value);
|
||||||
EXPECT_EQ(output, expected);
|
CHECK(output == expected);
|
||||||
EXPECT_EQ(output, valueDesc);
|
CHECK(output == valueDesc);
|
||||||
EXPECT_EQ(output, jsonAsString(value));
|
CHECK(output == jsonAsString(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(JsonOutput, output_cases)
|
TEST_CASE("output cases")
|
||||||
{
|
{
|
||||||
checkOutput("{}");
|
checkOutput("{}");
|
||||||
checkOutput("[]");
|
checkOutput("[]");
|
||||||
@@ -34,3 +36,5 @@ TEST(JsonOutput, output_cases)
|
|||||||
checkOutput("[[]]");
|
checkOutput("[[]]");
|
||||||
checkOutput(R"({"array":[{"12":23},{},null,false,0.5]})");
|
checkOutput(R"({"array":[{"12":23},{},null,false,0.5]})");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_SUITE_END();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
#include <xrpl/json/Writer.h>
|
#include <xrpl/json/Writer.h>
|
||||||
|
|
||||||
|
#include <doctest/doctest.h>
|
||||||
#include <google/protobuf/stubs/port.h>
|
#include <google/protobuf/stubs/port.h>
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -9,14 +9,14 @@
|
|||||||
using namespace xrpl;
|
using namespace xrpl;
|
||||||
using namespace Json;
|
using namespace Json;
|
||||||
|
|
||||||
class WriterFixture : public ::testing::Test
|
TEST_SUITE_BEGIN("JsonWriter");
|
||||||
|
|
||||||
|
struct WriterFixture
|
||||||
{
|
{
|
||||||
protected:
|
|
||||||
std::string output;
|
std::string output;
|
||||||
std::unique_ptr<Writer> writer;
|
std::unique_ptr<Writer> writer;
|
||||||
|
|
||||||
void
|
WriterFixture()
|
||||||
SetUp() override
|
|
||||||
{
|
{
|
||||||
writer = std::make_unique<Writer>(stringOutput(output));
|
writer = std::make_unique<Writer>(stringOutput(output));
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ protected:
|
|||||||
void
|
void
|
||||||
expectOutput(std::string const& expected) const
|
expectOutput(std::string const& expected) const
|
||||||
{
|
{
|
||||||
EXPECT_EQ(output, expected);
|
CHECK(output == expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -42,20 +42,20 @@ protected:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(WriterFixture, trivial)
|
TEST_CASE_FIXTURE(WriterFixture, "trivial")
|
||||||
{
|
{
|
||||||
EXPECT_TRUE(output.empty());
|
CHECK(output.empty());
|
||||||
checkOutputAndReset("");
|
checkOutputAndReset("");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WriterFixture, near_trivial)
|
TEST_CASE_FIXTURE(WriterFixture, "near trivial")
|
||||||
{
|
{
|
||||||
EXPECT_TRUE(output.empty());
|
CHECK(output.empty());
|
||||||
writer->output(0);
|
writer->output(0);
|
||||||
checkOutputAndReset("0");
|
checkOutputAndReset("0");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WriterFixture, primitives)
|
TEST_CASE_FIXTURE(WriterFixture, "primitives")
|
||||||
{
|
{
|
||||||
writer->output(true);
|
writer->output(true);
|
||||||
checkOutputAndReset("true");
|
checkOutputAndReset("true");
|
||||||
@@ -79,7 +79,7 @@ TEST_F(WriterFixture, primitives)
|
|||||||
checkOutputAndReset("null");
|
checkOutputAndReset("null");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WriterFixture, empty)
|
TEST_CASE_FIXTURE(WriterFixture, "empty")
|
||||||
{
|
{
|
||||||
writer->startRoot(Writer::array);
|
writer->startRoot(Writer::array);
|
||||||
writer->finish();
|
writer->finish();
|
||||||
@@ -90,7 +90,7 @@ TEST_F(WriterFixture, empty)
|
|||||||
checkOutputAndReset("{}");
|
checkOutputAndReset("{}");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WriterFixture, escaping)
|
TEST_CASE_FIXTURE(WriterFixture, "escaping")
|
||||||
{
|
{
|
||||||
writer->output("\\");
|
writer->output("\\");
|
||||||
checkOutputAndReset(R"("\\")");
|
checkOutputAndReset(R"("\\")");
|
||||||
@@ -108,7 +108,7 @@ TEST_F(WriterFixture, escaping)
|
|||||||
checkOutputAndReset(R"("\b\f\n\r\t")");
|
checkOutputAndReset(R"("\b\f\n\r\t")");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WriterFixture, array)
|
TEST_CASE_FIXTURE(WriterFixture, "array")
|
||||||
{
|
{
|
||||||
writer->startRoot(Writer::array);
|
writer->startRoot(Writer::array);
|
||||||
writer->append(12);
|
writer->append(12);
|
||||||
@@ -116,7 +116,7 @@ TEST_F(WriterFixture, array)
|
|||||||
checkOutputAndReset("[12]");
|
checkOutputAndReset("[12]");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WriterFixture, long_array)
|
TEST_CASE_FIXTURE(WriterFixture, "long array")
|
||||||
{
|
{
|
||||||
writer->startRoot(Writer::array);
|
writer->startRoot(Writer::array);
|
||||||
writer->append(12);
|
writer->append(12);
|
||||||
@@ -126,7 +126,7 @@ TEST_F(WriterFixture, long_array)
|
|||||||
checkOutputAndReset(R"([12,true,"hello"])");
|
checkOutputAndReset(R"([12,true,"hello"])");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WriterFixture, embedded_array_simple)
|
TEST_CASE_FIXTURE(WriterFixture, "embedded array simple")
|
||||||
{
|
{
|
||||||
writer->startRoot(Writer::array);
|
writer->startRoot(Writer::array);
|
||||||
writer->startAppend(Writer::array);
|
writer->startAppend(Writer::array);
|
||||||
@@ -135,7 +135,7 @@ TEST_F(WriterFixture, embedded_array_simple)
|
|||||||
checkOutputAndReset("[[]]");
|
checkOutputAndReset("[[]]");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WriterFixture, object)
|
TEST_CASE_FIXTURE(WriterFixture, "object")
|
||||||
{
|
{
|
||||||
writer->startRoot(Writer::object);
|
writer->startRoot(Writer::object);
|
||||||
writer->set("hello", "world");
|
writer->set("hello", "world");
|
||||||
@@ -143,7 +143,7 @@ TEST_F(WriterFixture, object)
|
|||||||
checkOutputAndReset(R"({"hello":"world"})");
|
checkOutputAndReset(R"({"hello":"world"})");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WriterFixture, complex_object)
|
TEST_CASE_FIXTURE(WriterFixture, "complex object")
|
||||||
{
|
{
|
||||||
writer->startRoot(Writer::object);
|
writer->startRoot(Writer::object);
|
||||||
writer->set("hello", "world");
|
writer->set("hello", "world");
|
||||||
@@ -160,7 +160,7 @@ TEST_F(WriterFixture, complex_object)
|
|||||||
R"({"hello":"world","array":[true,12,[{"goodbye":"cruel world.","subarray":[23.5]}]]})");
|
R"({"hello":"world","array":[true,12,[{"goodbye":"cruel world.","subarray":[23.5]}]]})");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WriterFixture, json_value)
|
TEST_CASE_FIXTURE(WriterFixture, "json value")
|
||||||
{
|
{
|
||||||
Json::Value value(Json::objectValue);
|
Json::Value value(Json::objectValue);
|
||||||
value["foo"] = 23;
|
value["foo"] = 23;
|
||||||
@@ -169,3 +169,5 @@ TEST_F(WriterFixture, json_value)
|
|||||||
writer->finish();
|
writer->finish();
|
||||||
checkOutputAndReset(R"({"hello":{"foo":23}})");
|
checkOutputAndReset(R"({"hello":{"foo":23}})");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_SUITE_END();
|
||||||
|
|||||||
@@ -1,8 +1,2 @@
|
|||||||
#include <gtest/gtest.h>
|
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||||
|
#include <doctest/doctest.h>
|
||||||
int
|
|
||||||
main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
|
||||||
return RUN_ALL_TESTS();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include <boost/beast/http.hpp>
|
#include <boost/beast/http.hpp>
|
||||||
#include <boost/beast/version.hpp>
|
#include <boost/beast/version.hpp>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <doctest/doctest.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -217,7 +217,7 @@ runHTTPTest(
|
|||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
TEST(HTTPClient, case_insensitive_content_length)
|
TEST_CASE("HTTPClient case insensitive Content-Length")
|
||||||
{
|
{
|
||||||
// Test different cases of Content-Length header
|
// Test different cases of Content-Length header
|
||||||
std::vector<std::string> header_cases = {
|
std::vector<std::string> header_cases = {
|
||||||
@@ -249,14 +249,14 @@ TEST(HTTPClient, case_insensitive_content_length)
|
|||||||
result_error);
|
result_error);
|
||||||
|
|
||||||
// Verify results
|
// Verify results
|
||||||
EXPECT_TRUE(test_completed);
|
CHECK(test_completed);
|
||||||
EXPECT_FALSE(result_error);
|
CHECK(!result_error);
|
||||||
EXPECT_EQ(result_status, 200);
|
CHECK(result_status == 200);
|
||||||
EXPECT_EQ(result_data, test_body);
|
CHECK(result_data == test_body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(HTTPClient, basic_http_request)
|
TEST_CASE("HTTPClient basic HTTP request")
|
||||||
{
|
{
|
||||||
TestHTTPServer server;
|
TestHTTPServer server;
|
||||||
std::string test_body = "Test response body";
|
std::string test_body = "Test response body";
|
||||||
@@ -271,13 +271,13 @@ TEST(HTTPClient, basic_http_request)
|
|||||||
bool test_completed = runHTTPTest(
|
bool test_completed = runHTTPTest(
|
||||||
server, "/basic", completed, result_status, result_data, result_error);
|
server, "/basic", completed, result_status, result_data, result_error);
|
||||||
|
|
||||||
EXPECT_TRUE(test_completed);
|
CHECK(test_completed);
|
||||||
EXPECT_FALSE(result_error);
|
CHECK(!result_error);
|
||||||
EXPECT_EQ(result_status, 200);
|
CHECK(result_status == 200);
|
||||||
EXPECT_EQ(result_data, test_body);
|
CHECK(result_data == test_body);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(HTTPClient, empty_response)
|
TEST_CASE("HTTPClient empty response")
|
||||||
{
|
{
|
||||||
TestHTTPServer server;
|
TestHTTPServer server;
|
||||||
server.setResponseBody(""); // Empty body
|
server.setResponseBody(""); // Empty body
|
||||||
@@ -291,13 +291,13 @@ TEST(HTTPClient, empty_response)
|
|||||||
bool test_completed = runHTTPTest(
|
bool test_completed = runHTTPTest(
|
||||||
server, "/empty", completed, result_status, result_data, result_error);
|
server, "/empty", completed, result_status, result_data, result_error);
|
||||||
|
|
||||||
EXPECT_TRUE(test_completed);
|
CHECK(test_completed);
|
||||||
EXPECT_FALSE(result_error);
|
CHECK(!result_error);
|
||||||
EXPECT_EQ(result_status, 200);
|
CHECK(result_status == 200);
|
||||||
EXPECT_TRUE(result_data.empty());
|
CHECK(result_data.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(HTTPClient, different_status_codes)
|
TEST_CASE("HTTPClient different status codes")
|
||||||
{
|
{
|
||||||
std::vector<unsigned int> status_codes = {200, 404, 500};
|
std::vector<unsigned int> status_codes = {200, 404, 500};
|
||||||
|
|
||||||
@@ -320,8 +320,8 @@ TEST(HTTPClient, different_status_codes)
|
|||||||
result_data,
|
result_data,
|
||||||
result_error);
|
result_error);
|
||||||
|
|
||||||
EXPECT_TRUE(test_completed);
|
CHECK(test_completed);
|
||||||
EXPECT_FALSE(result_error);
|
CHECK(!result_error);
|
||||||
EXPECT_EQ(result_status, static_cast<int>(status));
|
CHECK(result_status == static_cast<int>(status));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,2 @@
|
|||||||
#include <gtest/gtest.h>
|
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||||
|
#include <doctest/doctest.h>
|
||||||
int
|
|
||||||
main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
|
||||||
return RUN_ALL_TESTS();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include <xrpld/shamap/NodeFamily.h>
|
#include <xrpld/shamap/NodeFamily.h>
|
||||||
|
|
||||||
#include <xrpl/basics/ByteUtilities.h>
|
#include <xrpl/basics/ByteUtilities.h>
|
||||||
|
#include <xrpl/basics/MallocTrim.h>
|
||||||
#include <xrpl/basics/ResolverAsio.h>
|
#include <xrpl/basics/ResolverAsio.h>
|
||||||
#include <xrpl/basics/random.h>
|
#include <xrpl/basics/random.h>
|
||||||
#include <xrpl/beast/asio/io_latency_probe.h>
|
#include <xrpl/beast/asio/io_latency_probe.h>
|
||||||
@@ -1105,6 +1106,8 @@ public:
|
|||||||
<< "; size after: " << cachedSLEs_.size();
|
<< "; size after: " << cachedSLEs_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mallocTrim(std::optional<std::string>("doSweep"), m_journal);
|
||||||
|
|
||||||
// Set timer to do another sweep later.
|
// Set timer to do another sweep later.
|
||||||
setSweepTimer();
|
setSweepTimer();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include <xrpld/rpc/MPTokenIssuanceID.h>
|
#include <xrpld/rpc/MPTokenIssuanceID.h>
|
||||||
#include <xrpld/rpc/ServerHandler.h>
|
#include <xrpld/rpc/ServerHandler.h>
|
||||||
|
|
||||||
|
#include <xrpl/basics/MallocTrim.h>
|
||||||
#include <xrpl/basics/UptimeClock.h>
|
#include <xrpl/basics/UptimeClock.h>
|
||||||
#include <xrpl/basics/mulDiv.h>
|
#include <xrpl/basics/mulDiv.h>
|
||||||
#include <xrpl/basics/safe_cast.h>
|
#include <xrpl/basics/safe_cast.h>
|
||||||
@@ -2551,10 +2552,14 @@ NetworkOPsImp::setMode(OperatingMode om)
|
|||||||
if (mMode == om)
|
if (mMode == om)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
auto const oldMode = mMode.load(std::memory_order_relaxed);
|
||||||
mMode = om;
|
mMode = om;
|
||||||
|
|
||||||
accounting_.mode(om);
|
accounting_.mode(om);
|
||||||
|
|
||||||
|
if (oldMode != OperatingMode::FULL && om == OperatingMode::FULL)
|
||||||
|
mallocTrim(std::optional<std::string>("SyncComplete"), m_journal);
|
||||||
|
|
||||||
JLOG(m_journal.info()) << "STATE->" << strOperatingMode();
|
JLOG(m_journal.info()) << "STATE->" << strOperatingMode();
|
||||||
pubServer();
|
pubServer();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
|
#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
|
||||||
#include <xrpld/core/ConfigSections.h>
|
#include <xrpld/core/ConfigSections.h>
|
||||||
|
|
||||||
|
#include <xrpl/basics/MallocTrim.h>
|
||||||
#include <xrpl/beast/core/CurrentThreadName.h>
|
#include <xrpl/beast/core/CurrentThreadName.h>
|
||||||
#include <xrpl/nodestore/Scheduler.h>
|
#include <xrpl/nodestore/Scheduler.h>
|
||||||
#include <xrpl/nodestore/detail/DatabaseRotatingImp.h>
|
#include <xrpl/nodestore/detail/DatabaseRotatingImp.h>
|
||||||
@@ -545,6 +546,8 @@ SHAMapStoreImp::clearCaches(LedgerIndex validatedSeq)
|
|||||||
{
|
{
|
||||||
ledgerMaster_->clearLedgerCachePrior(validatedSeq);
|
ledgerMaster_->clearLedgerCachePrior(validatedSeq);
|
||||||
fullBelowCache_->clear();
|
fullBelowCache_->clear();
|
||||||
|
|
||||||
|
mallocTrim(std::optional<std::string>("clearCaches"), journal_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -610,6 +613,8 @@ SHAMapStoreImp::clearPrior(LedgerIndex lastRotated)
|
|||||||
});
|
});
|
||||||
if (healthWait() == stopping)
|
if (healthWait() == stopping)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
mallocTrim(std::optional<std::string>("clearPrior"), journal_);
|
||||||
}
|
}
|
||||||
|
|
||||||
SHAMapStoreImp::HealthResult
|
SHAMapStoreImp::HealthResult
|
||||||
|
|||||||
@@ -18,32 +18,6 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
using FunctionType = std::function<Expected<uint256, Json::Value>(
|
|
||||||
Json::Value const&,
|
|
||||||
Json::StaticString const,
|
|
||||||
unsigned const apiVersion)>;
|
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
|
||||||
parseFixed(
|
|
||||||
Keylet const& keylet,
|
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const& fieldName,
|
|
||||||
unsigned const apiVersion);
|
|
||||||
|
|
||||||
// Helper function to return FunctionType for objects that have a fixed
|
|
||||||
// location. That is, they don't take parameters to compute the index.
|
|
||||||
// e.g. amendments, fees, negative UNL, etc.
|
|
||||||
static FunctionType
|
|
||||||
fixed(Keylet const& keylet)
|
|
||||||
{
|
|
||||||
return [keylet](
|
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
unsigned const apiVersion) -> Expected<uint256, Json::Value> {
|
|
||||||
return parseFixed(keylet, params, fieldName, apiVersion);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseObjectID(
|
parseObjectID(
|
||||||
Json::Value const& params,
|
Json::Value const& params,
|
||||||
@@ -59,33 +33,13 @@ parseObjectID(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseIndex(
|
parseIndex(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (apiVersion > 2u && params.isString())
|
|
||||||
{
|
|
||||||
std::string const index = params.asString();
|
|
||||||
if (index == jss::amendments.c_str())
|
|
||||||
return keylet::amendments().key;
|
|
||||||
if (index == jss::fee.c_str())
|
|
||||||
return keylet::fees().key;
|
|
||||||
if (index == jss::nunl)
|
|
||||||
return keylet::negativeUNL().key;
|
|
||||||
if (index == jss::hashes)
|
|
||||||
// Note this only finds the "short" skip list. Use "hashes":index to
|
|
||||||
// get the long list.
|
|
||||||
return keylet::skip().key;
|
|
||||||
}
|
|
||||||
return parseObjectID(params, fieldName, "hex string");
|
return parseObjectID(params, fieldName, "hex string");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseAccountRoot(
|
parseAccountRoot(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (auto const account = LedgerEntryHelpers::parse<AccountID>(params))
|
if (auto const account = LedgerEntryHelpers::parse<AccountID>(params))
|
||||||
{
|
{
|
||||||
@@ -96,13 +50,14 @@ parseAccountRoot(
|
|||||||
"malformedAddress", fieldName, "AccountID");
|
"malformedAddress", fieldName, "AccountID");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const parseAmendments = fixed(keylet::amendments());
|
static Expected<uint256, Json::Value>
|
||||||
|
parseAmendments(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
|
{
|
||||||
|
return parseObjectID(params, fieldName, "hex string");
|
||||||
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseAMM(
|
parseAMM(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!params.isObject())
|
if (!params.isObject())
|
||||||
{
|
{
|
||||||
@@ -130,10 +85,7 @@ parseAMM(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseBridge(
|
parseBridge(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!params.isMember(jss::bridge))
|
if (!params.isMember(jss::bridge))
|
||||||
{
|
{
|
||||||
@@ -164,19 +116,13 @@ parseBridge(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseCheck(
|
parseCheck(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
return parseObjectID(params, fieldName, "hex string");
|
return parseObjectID(params, fieldName, "hex string");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseCredential(
|
parseCredential(Json::Value const& cred, Json::StaticString const fieldName)
|
||||||
Json::Value const& cred,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!cred.isObject())
|
if (!cred.isObject())
|
||||||
{
|
{
|
||||||
@@ -207,10 +153,7 @@ parseCredential(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseDelegate(
|
parseDelegate(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!params.isObject())
|
if (!params.isObject())
|
||||||
{
|
{
|
||||||
@@ -301,10 +244,7 @@ parseAuthorizeCredentials(Json::Value const& jv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseDepositPreauth(
|
parseDepositPreauth(Json::Value const& dp, Json::StaticString const fieldName)
|
||||||
Json::Value const& dp,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!dp.isObject())
|
if (!dp.isObject())
|
||||||
{
|
{
|
||||||
@@ -357,10 +297,7 @@ parseDepositPreauth(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseDID(
|
parseDID(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
auto const account = LedgerEntryHelpers::parse<AccountID>(params);
|
auto const account = LedgerEntryHelpers::parse<AccountID>(params);
|
||||||
if (!account)
|
if (!account)
|
||||||
@@ -375,8 +312,7 @@ parseDID(
|
|||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseDirectoryNode(
|
parseDirectoryNode(
|
||||||
Json::Value const& params,
|
Json::Value const& params,
|
||||||
Json::StaticString const fieldName,
|
Json::StaticString const fieldName)
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!params.isObject())
|
if (!params.isObject())
|
||||||
{
|
{
|
||||||
@@ -429,10 +365,7 @@ parseDirectoryNode(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseEscrow(
|
parseEscrow(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!params.isObject())
|
if (!params.isObject())
|
||||||
{
|
{
|
||||||
@@ -451,53 +384,20 @@ parseEscrow(
|
|||||||
return keylet::escrow(*id, *seq).key;
|
return keylet::escrow(*id, *seq).key;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const parseFeeSettings = fixed(keylet::fees());
|
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseFixed(
|
parseFeeSettings(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Keylet const& keylet,
|
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const& fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!params.isBool())
|
return parseObjectID(params, fieldName, "hex string");
|
||||||
{
|
|
||||||
return parseObjectID(params, fieldName, "hex string");
|
|
||||||
}
|
|
||||||
if (!params.asBool())
|
|
||||||
{
|
|
||||||
return LedgerEntryHelpers::invalidFieldError(
|
|
||||||
"invalidParams", fieldName, "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
return keylet.key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseLedgerHashes(
|
parseLedgerHashes(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (params.isUInt() || params.isInt())
|
return parseObjectID(params, fieldName, "hex string");
|
||||||
{
|
|
||||||
// If the index doesn't parse as a UInt, throw
|
|
||||||
auto const index = params.asUInt();
|
|
||||||
|
|
||||||
// Return the "long" skip list for the given ledger index.
|
|
||||||
auto const keylet = keylet::skip(index);
|
|
||||||
return keylet.key;
|
|
||||||
}
|
|
||||||
// Return the key in `params` or the "short" skip list, which contains
|
|
||||||
// hashes since the last flag ledger.
|
|
||||||
return parseFixed(keylet::skip(), params, fieldName, apiVersion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseLoanBroker(
|
parseLoanBroker(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!params.isObject())
|
if (!params.isObject())
|
||||||
{
|
{
|
||||||
@@ -517,10 +417,7 @@ parseLoanBroker(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseLoan(
|
parseLoan(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!params.isObject())
|
if (!params.isObject())
|
||||||
{
|
{
|
||||||
@@ -540,10 +437,7 @@ parseLoan(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseMPToken(
|
parseMPToken(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!params.isObject())
|
if (!params.isObject())
|
||||||
{
|
{
|
||||||
@@ -566,8 +460,7 @@ parseMPToken(
|
|||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseMPTokenIssuance(
|
parseMPTokenIssuance(
|
||||||
Json::Value const& params,
|
Json::Value const& params,
|
||||||
Json::StaticString const fieldName,
|
Json::StaticString const fieldName)
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
auto const mptIssuanceID = LedgerEntryHelpers::parse<uint192>(params);
|
auto const mptIssuanceID = LedgerEntryHelpers::parse<uint192>(params);
|
||||||
if (!mptIssuanceID)
|
if (!mptIssuanceID)
|
||||||
@@ -578,30 +471,25 @@ parseMPTokenIssuance(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseNFTokenOffer(
|
parseNFTokenOffer(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
return parseObjectID(params, fieldName, "hex string");
|
return parseObjectID(params, fieldName, "hex string");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseNFTokenPage(
|
parseNFTokenPage(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
return parseObjectID(params, fieldName, "hex string");
|
return parseObjectID(params, fieldName, "hex string");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const parseNegativeUNL = fixed(keylet::negativeUNL());
|
static Expected<uint256, Json::Value>
|
||||||
|
parseNegativeUNL(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
|
{
|
||||||
|
return parseObjectID(params, fieldName, "hex string");
|
||||||
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseOffer(
|
parseOffer(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!params.isObject())
|
if (!params.isObject())
|
||||||
{
|
{
|
||||||
@@ -622,10 +510,7 @@ parseOffer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseOracle(
|
parseOracle(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!params.isObject())
|
if (!params.isObject())
|
||||||
{
|
{
|
||||||
@@ -646,10 +531,7 @@ parseOracle(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parsePayChannel(
|
parsePayChannel(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
return parseObjectID(params, fieldName, "hex string");
|
return parseObjectID(params, fieldName, "hex string");
|
||||||
}
|
}
|
||||||
@@ -657,8 +539,7 @@ parsePayChannel(
|
|||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parsePermissionedDomain(
|
parsePermissionedDomain(
|
||||||
Json::Value const& pd,
|
Json::Value const& pd,
|
||||||
Json::StaticString const fieldName,
|
Json::StaticString const fieldName)
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (pd.isString())
|
if (pd.isString())
|
||||||
{
|
{
|
||||||
@@ -687,8 +568,7 @@ parsePermissionedDomain(
|
|||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseRippleState(
|
parseRippleState(
|
||||||
Json::Value const& jvRippleState,
|
Json::Value const& jvRippleState,
|
||||||
Json::StaticString const fieldName,
|
Json::StaticString const fieldName)
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
Currency uCurrency;
|
Currency uCurrency;
|
||||||
|
|
||||||
@@ -738,19 +618,13 @@ parseRippleState(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseSignerList(
|
parseSignerList(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
return parseObjectID(params, fieldName, "hex string");
|
return parseObjectID(params, fieldName, "hex string");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseTicket(
|
parseTicket(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!params.isObject())
|
if (!params.isObject())
|
||||||
{
|
{
|
||||||
@@ -771,10 +645,7 @@ parseTicket(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseVault(
|
parseVault(Json::Value const& params, Json::StaticString const fieldName)
|
||||||
Json::Value const& params,
|
|
||||||
Json::StaticString const fieldName,
|
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!params.isObject())
|
if (!params.isObject())
|
||||||
{
|
{
|
||||||
@@ -797,8 +668,7 @@ parseVault(
|
|||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseXChainOwnedClaimID(
|
parseXChainOwnedClaimID(
|
||||||
Json::Value const& claim_id,
|
Json::Value const& claim_id,
|
||||||
Json::StaticString const fieldName,
|
Json::StaticString const fieldName)
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!claim_id.isObject())
|
if (!claim_id.isObject())
|
||||||
{
|
{
|
||||||
@@ -823,8 +693,7 @@ parseXChainOwnedClaimID(
|
|||||||
static Expected<uint256, Json::Value>
|
static Expected<uint256, Json::Value>
|
||||||
parseXChainOwnedCreateAccountClaimID(
|
parseXChainOwnedCreateAccountClaimID(
|
||||||
Json::Value const& claim_id,
|
Json::Value const& claim_id,
|
||||||
Json::StaticString const fieldName,
|
Json::StaticString const fieldName)
|
||||||
[[maybe_unused]] unsigned const apiVersion)
|
|
||||||
{
|
{
|
||||||
if (!claim_id.isObject())
|
if (!claim_id.isObject())
|
||||||
{
|
{
|
||||||
@@ -848,6 +717,10 @@ parseXChainOwnedCreateAccountClaimID(
|
|||||||
return keylet.key;
|
return keylet.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using FunctionType = Expected<uint256, Json::Value> (*)(
|
||||||
|
Json::Value const&,
|
||||||
|
Json::StaticString const);
|
||||||
|
|
||||||
struct LedgerEntry
|
struct LedgerEntry
|
||||||
{
|
{
|
||||||
Json::StaticString fieldName;
|
Json::StaticString fieldName;
|
||||||
@@ -880,7 +753,7 @@ doLedgerEntry(RPC::JsonContext& context)
|
|||||||
{jss::ripple_state, parseRippleState, ltRIPPLE_STATE},
|
{jss::ripple_state, parseRippleState, ltRIPPLE_STATE},
|
||||||
});
|
});
|
||||||
|
|
||||||
auto const hasMoreThanOneMember = [&]() {
|
auto hasMoreThanOneMember = [&]() {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
for (auto const& ledgerEntry : ledgerEntryParsers)
|
for (auto const& ledgerEntry : ledgerEntryParsers)
|
||||||
@@ -924,8 +797,8 @@ doLedgerEntry(RPC::JsonContext& context)
|
|||||||
Json::Value const& params = ledgerEntry.fieldName == jss::bridge
|
Json::Value const& params = ledgerEntry.fieldName == jss::bridge
|
||||||
? context.params
|
? context.params
|
||||||
: context.params[ledgerEntry.fieldName];
|
: context.params[ledgerEntry.fieldName];
|
||||||
auto const result = ledgerEntry.parseFunction(
|
auto const result =
|
||||||
params, ledgerEntry.fieldName, context.apiVersion);
|
ledgerEntry.parseFunction(params, ledgerEntry.fieldName);
|
||||||
if (!result)
|
if (!result)
|
||||||
return result.error();
|
return result.error();
|
||||||
|
|
||||||
@@ -956,13 +829,9 @@ doLedgerEntry(RPC::JsonContext& context)
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the computed index regardless of whether the node exists.
|
|
||||||
jvResult[jss::index] = to_string(uNodeIndex);
|
|
||||||
|
|
||||||
if (uNodeIndex.isZero())
|
if (uNodeIndex.isZero())
|
||||||
{
|
{
|
||||||
RPC::inject_error(rpcENTRY_NOT_FOUND, jvResult);
|
return RPC::make_error(rpcENTRY_NOT_FOUND);
|
||||||
return jvResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex));
|
auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex));
|
||||||
@@ -974,14 +843,12 @@ doLedgerEntry(RPC::JsonContext& context)
|
|||||||
if (!sleNode)
|
if (!sleNode)
|
||||||
{
|
{
|
||||||
// Not found.
|
// Not found.
|
||||||
RPC::inject_error(rpcENTRY_NOT_FOUND, jvResult);
|
return RPC::make_error(rpcENTRY_NOT_FOUND);
|
||||||
return jvResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((expectedType != ltANY) && (expectedType != sleNode->getType()))
|
if ((expectedType != ltANY) && (expectedType != sleNode->getType()))
|
||||||
{
|
{
|
||||||
RPC::inject_error(rpcUNEXPECTED_LEDGER_TYPE, jvResult);
|
return RPC::make_error(rpcUNEXPECTED_LEDGER_TYPE);
|
||||||
return jvResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bNodeBinary)
|
if (bNodeBinary)
|
||||||
@@ -991,10 +858,12 @@ doLedgerEntry(RPC::JsonContext& context)
|
|||||||
sleNode->add(s);
|
sleNode->add(s);
|
||||||
|
|
||||||
jvResult[jss::node_binary] = strHex(s.peekData());
|
jvResult[jss::node_binary] = strHex(s.peekData());
|
||||||
|
jvResult[jss::index] = to_string(uNodeIndex);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
jvResult[jss::node] = sleNode->getJson(JsonOptions::none);
|
jvResult[jss::node] = sleNode->getJson(JsonOptions::none);
|
||||||
|
jvResult[jss::index] = to_string(uNodeIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return jvResult;
|
return jvResult;
|
||||||
|
|||||||
Reference in New Issue
Block a user