mirror of
https://github.com/XRPLF/rippled.git
synced 2026-03-18 10:42:24 +00:00
Compare commits
29 Commits
ripple/was
...
bthomee/io
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f586382622 | ||
|
|
95a45d7442 | ||
|
|
5fc4ab3e37 | ||
|
|
b129b71c33 | ||
|
|
013c2d6a56 | ||
|
|
72f4cb097f | ||
|
|
b523770486 | ||
|
|
a5185890ff | ||
|
|
0a9513e7f3 | ||
|
|
78b2d70a11 | ||
|
|
252c6768df | ||
|
|
5ae97fa8ae | ||
|
|
eff344faf9 | ||
|
|
7e7b71d84c | ||
|
|
ffea3977f0 | ||
|
|
47a235b7be | ||
|
|
f5e2415c98 | ||
|
|
1a4c359351 | ||
|
|
e4dbaf5efc | ||
|
|
983816248a | ||
|
|
b585dc78bb | ||
|
|
918185e18f | ||
|
|
1738a69619 | ||
|
|
1bf9e6e7da | ||
|
|
0446bef7e5 | ||
|
|
7a3bf1692d | ||
|
|
c1d108e565 | ||
|
|
1ba1bf9ade | ||
|
|
7dd3e0b3cc |
37
.clang-tidy
37
.clang-tidy
@@ -8,6 +8,7 @@ Checks: "-*,
|
||||
bugprone-chained-comparison,
|
||||
bugprone-compare-pointer-to-member-virtual-function,
|
||||
bugprone-copy-constructor-init,
|
||||
bugprone-crtp-constructor-accessibility,
|
||||
bugprone-dangling-handle,
|
||||
bugprone-dynamic-static-initializers,
|
||||
bugprone-empty-catch,
|
||||
@@ -59,9 +60,11 @@ Checks: "-*,
|
||||
bugprone-suspicious-string-compare,
|
||||
bugprone-suspicious-stringview-data-usage,
|
||||
bugprone-swapped-arguments,
|
||||
bugprone-switch-missing-default-case,
|
||||
bugprone-terminating-continue,
|
||||
bugprone-throw-keyword-missing,
|
||||
bugprone-too-small-loop-variable,
|
||||
# bugprone-unchecked-optional-access, # see https://github.com/XRPLF/rippled/pull/6502
|
||||
bugprone-undefined-memory-manipulation,
|
||||
bugprone-undelegated-constructor,
|
||||
bugprone-unhandled-exception-at-new,
|
||||
@@ -69,9 +72,16 @@ Checks: "-*,
|
||||
bugprone-unique-ptr-array-mismatch,
|
||||
bugprone-unsafe-functions,
|
||||
bugprone-unused-raii,
|
||||
bugprone-unused-return-value,
|
||||
bugprone-unused-local-non-trivial-variable,
|
||||
bugprone-virtual-near-miss,
|
||||
cppcoreguidelines-init-variables,
|
||||
cppcoreguidelines-misleading-capture-default-by-value,
|
||||
cppcoreguidelines-no-suspend-with-lock,
|
||||
cppcoreguidelines-pro-type-member-init,
|
||||
cppcoreguidelines-pro-type-static-cast-downcast,
|
||||
cppcoreguidelines-rvalue-reference-param-not-moved,
|
||||
cppcoreguidelines-use-default-member-init,
|
||||
cppcoreguidelines-virtual-class-destructor,
|
||||
hicpp-ignored-remove-result,
|
||||
misc-definitions-in-headers,
|
||||
@@ -81,36 +91,25 @@ Checks: "-*,
|
||||
misc-throw-by-value-catch-by-reference,
|
||||
misc-unused-alias-decls,
|
||||
misc-unused-using-decls,
|
||||
readability-duplicate-include,
|
||||
readability-enum-initial-value,
|
||||
readability-misleading-indentation,
|
||||
readability-non-const-parameter,
|
||||
readability-redundant-declaration,
|
||||
readability-reference-to-constructed-temporary,
|
||||
modernize-deprecated-headers,
|
||||
modernize-make-shared,
|
||||
modernize-make-unique,
|
||||
performance-implicit-conversion-in-loop,
|
||||
performance-move-constructor-init,
|
||||
performance-trivially-destructible
|
||||
performance-trivially-destructible,
|
||||
readability-duplicate-include,
|
||||
readability-enum-initial-value,
|
||||
readability-misleading-indentation,
|
||||
readability-non-const-parameter,
|
||||
readability-redundant-declaration,
|
||||
readability-reference-to-constructed-temporary
|
||||
"
|
||||
# ---
|
||||
# checks that have some issues that need to be resolved:
|
||||
#
|
||||
# bugprone-crtp-constructor-accessibility,
|
||||
# bugprone-move-forwarding-reference,
|
||||
# bugprone-switch-missing-default-case,
|
||||
# bugprone-unused-raii,
|
||||
# bugprone-unused-return-value,
|
||||
# bugprone-use-after-move,
|
||||
#
|
||||
# cppcoreguidelines-misleading-capture-default-by-value,
|
||||
# cppcoreguidelines-init-variables,
|
||||
# cppcoreguidelines-pro-type-member-init,
|
||||
# cppcoreguidelines-pro-type-static-cast-downcast,
|
||||
# cppcoreguidelines-use-default-member-init,
|
||||
# cppcoreguidelines-rvalue-reference-param-not-moved,
|
||||
#
|
||||
# llvm-namespace-comment,
|
||||
# misc-const-correctness,
|
||||
# misc-include-cleaner,
|
||||
@@ -195,7 +194,7 @@ CheckOptions:
|
||||
# readability-identifier-naming.PublicMemberSuffix: ""
|
||||
# readability-identifier-naming.FunctionIgnoredRegexp: ".*tag_invoke.*"
|
||||
bugprone-unsafe-functions.ReportMoreUnsafeFunctions: true
|
||||
# bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc
|
||||
bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc
|
||||
# misc-include-cleaner.IgnoreHeaders: '.*/(detail|impl)/.*;.*(expected|unexpected).*;.*ranges_lower_bound\.h;time.h;stdlib.h;__chrono/.*;fmt/chrono.h;boost/uuid/uuid_hash.hpp'
|
||||
#
|
||||
# HeaderFilterRegex: '^.*/(src|tests)/.*\.(h|hpp)$'
|
||||
|
||||
@@ -134,6 +134,7 @@ test.peerfinder > xrpld.core
|
||||
test.peerfinder > xrpld.peerfinder
|
||||
test.peerfinder > xrpl.protocol
|
||||
test.protocol > test.toplevel
|
||||
test.protocol > test.unit_test
|
||||
test.protocol > xrpl.basics
|
||||
test.protocol > xrpl.json
|
||||
test.protocol > xrpl.protocol
|
||||
@@ -171,6 +172,7 @@ test.shamap > xrpl.shamap
|
||||
test.toplevel > test.csf
|
||||
test.toplevel > xrpl.json
|
||||
test.unit_test > xrpl.basics
|
||||
test.unit_test > xrpl.protocol
|
||||
tests.libxrpl > xrpl.basics
|
||||
tests.libxrpl > xrpl.json
|
||||
tests.libxrpl > xrpl.net
|
||||
|
||||
5
.github/workflows/check-pr-title.yml
vendored
5
.github/workflows/check-pr-title.yml
vendored
@@ -1,10 +1,13 @@
|
||||
name: Check PR title
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
types:
|
||||
- checks_requested
|
||||
pull_request:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
branches: [develop]
|
||||
|
||||
jobs:
|
||||
check_title:
|
||||
uses: XRPLF/actions/.github/workflows/check-pr-title.yml@943eb8277e8f4b010fde0c826ce4154c36c39509
|
||||
uses: XRPLF/actions/.github/workflows/check-pr-title.yml@a258ab7b68658d0c0787689a31df23c390051a27
|
||||
|
||||
3
.github/workflows/pre-commit.yml
vendored
3
.github/workflows/pre-commit.yml
vendored
@@ -1,6 +1,9 @@
|
||||
name: Run pre-commit hooks
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
types:
|
||||
- checks_requested
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
|
||||
16
.github/workflows/reusable-build-test-config.yml
vendored
16
.github/workflows/reusable-build-test-config.yml
vendored
@@ -76,7 +76,7 @@ jobs:
|
||||
name: ${{ inputs.config_name }}
|
||||
runs-on: ${{ fromJSON(inputs.runs_on) }}
|
||||
container: ${{ inputs.image != '' && inputs.image || null }}
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 60 }}
|
||||
env:
|
||||
# Use a namespace to keep the objects separate for each configuration.
|
||||
CCACHE_NAMESPACE: ${{ inputs.config_name }}
|
||||
@@ -204,11 +204,17 @@ jobs:
|
||||
|
||||
- name: Set sanitizer options
|
||||
if: ${{ !inputs.build_only && env.SANITIZERS_ENABLED == 'true' }}
|
||||
env:
|
||||
CONFIG_NAME: ${{ inputs.config_name }}
|
||||
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}
|
||||
ASAN_OPTS="include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-asan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp"
|
||||
if [[ "${CONFIG_NAME}" == *gcc* ]]; then
|
||||
ASAN_OPTS="${ASAN_OPTS}:alloc_dealloc_mismatch=0"
|
||||
fi
|
||||
echo "ASAN_OPTIONS=${ASAN_OPTS}" >> ${GITHUB_ENV}
|
||||
echo "TSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-tsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV}
|
||||
echo "UBSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV}
|
||||
echo "LSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-lsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV}
|
||||
|
||||
- name: Run the separate tests
|
||||
if: ${{ !inputs.build_only }}
|
||||
|
||||
@@ -93,7 +93,6 @@ find_package(OpenSSL REQUIRED)
|
||||
find_package(secp256k1 REQUIRED)
|
||||
find_package(SOCI REQUIRED)
|
||||
find_package(SQLite3 REQUIRED)
|
||||
find_package(wasmi REQUIRED)
|
||||
find_package(xxHash REQUIRED)
|
||||
|
||||
target_link_libraries(
|
||||
|
||||
@@ -118,7 +118,7 @@ if(MSVC)
|
||||
NOMINMAX
|
||||
# TODO: Resolve these warnings, don't just silence them
|
||||
_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS
|
||||
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:Debug>>:_CRTDBG_MAP_ALLOC>
|
||||
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:Debug>,$<NOT:$<BOOL:${is_ci}>>>:_CRTDBG_MAP_ALLOC>
|
||||
)
|
||||
target_link_libraries(common INTERFACE -errorreport:none -machine:X64)
|
||||
else()
|
||||
|
||||
@@ -67,7 +67,6 @@ target_link_libraries(
|
||||
Xrpl::opts
|
||||
Xrpl::syslibs
|
||||
secp256k1::secp256k1
|
||||
wasmi::wasmi
|
||||
xrpl.libpb
|
||||
xxHash::xxhash
|
||||
$<$<BOOL:${voidstar}>:antithesis-sdk-cpp>
|
||||
|
||||
@@ -23,7 +23,7 @@ target_compile_definitions(
|
||||
BOOST_FILESYSTEM_NO_DEPRECATED
|
||||
>
|
||||
$<$<NOT:$<BOOL:${boost_show_deprecated}>>:
|
||||
BOOST_COROUTINES_NO_DEPRECATION_WARNING
|
||||
BOOST_COROUTINES2_NO_DEPRECATION_WARNING
|
||||
BOOST_BEAST_ALLOW_DEPRECATED
|
||||
BOOST_FILESYSTEM_DEPRECATED
|
||||
>
|
||||
|
||||
@@ -7,7 +7,7 @@ find_package(
|
||||
COMPONENTS
|
||||
chrono
|
||||
container
|
||||
coroutine
|
||||
context
|
||||
date_time
|
||||
filesystem
|
||||
json
|
||||
@@ -26,7 +26,7 @@ target_link_libraries(
|
||||
Boost::headers
|
||||
Boost::chrono
|
||||
Boost::container
|
||||
Boost::coroutine
|
||||
Boost::context
|
||||
Boost::date_time
|
||||
Boost::filesystem
|
||||
Boost::json
|
||||
@@ -38,23 +38,26 @@ target_link_libraries(
|
||||
if(Boost_COMPILER)
|
||||
target_link_libraries(xrpl_boost INTERFACE Boost::disable_autolinking)
|
||||
endif()
|
||||
if(SANITIZERS_ENABLED AND is_clang)
|
||||
# TODO: gcc does not support -fsanitize-blacklist...can we do something else for gcc ?
|
||||
if(NOT Boost_INCLUDE_DIRS AND TARGET Boost::headers)
|
||||
get_target_property(
|
||||
Boost_INCLUDE_DIRS
|
||||
Boost::headers
|
||||
INTERFACE_INCLUDE_DIRECTORIES
|
||||
)
|
||||
endif()
|
||||
message(STATUS "Adding [${Boost_INCLUDE_DIRS}] to sanitizer blacklist")
|
||||
file(
|
||||
WRITE ${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt
|
||||
"src:${Boost_INCLUDE_DIRS}/*"
|
||||
)
|
||||
target_compile_options(
|
||||
opts
|
||||
INTERFACE # ignore boost headers for sanitizing
|
||||
-fsanitize-blacklist=${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt
|
||||
|
||||
# GCC 14+ has a false positive -Wuninitialized warning in Boost.Coroutine2's
|
||||
# state.hpp when compiled with -O3. This is due to GCC's intentional behavior
|
||||
# change (Bug #98871, #119388) where warnings from inlined system header code
|
||||
# are no longer suppressed by -isystem. The warning occurs in operator|= in
|
||||
# boost/coroutine2/detail/state.hpp when inlined from push_control_block::destroy().
|
||||
# See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119388
|
||||
if(is_gcc AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14)
|
||||
target_compile_options(xrpl_boost INTERFACE -Wno-uninitialized)
|
||||
endif()
|
||||
|
||||
# Boost.Context's ucontext backend has ASAN fiber-switching annotations
|
||||
# (start/finish_switch_fiber) that are compiled in when BOOST_USE_ASAN is defined.
|
||||
# This tells ASAN about coroutine stack switches, preventing false positive
|
||||
# stack-use-after-scope errors. BOOST_USE_UCONTEXT ensures the ucontext backend
|
||||
# is selected (fcontext does not support ASAN annotations).
|
||||
# These defines must match what Boost was compiled with (see conan/profiles/sanitizers).
|
||||
if(enable_asan)
|
||||
target_compile_definitions(
|
||||
xrpl_boost
|
||||
INTERFACE BOOST_USE_ASAN BOOST_USE_UCONTEXT
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"requires": [
|
||||
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1765850150.075",
|
||||
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
|
||||
"wasmi/1.0.6#407c9db14601a8af1c7dd3b388f3e4cd%1768164779.349",
|
||||
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1765850149.926",
|
||||
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1765850149.46",
|
||||
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
|
||||
|
||||
@@ -7,16 +7,21 @@ include(default)
|
||||
{% if compiler == "gcc" %}
|
||||
{% if "address" in sanitizers or "thread" in sanitizers or "undefinedbehavior" in sanitizers %}
|
||||
{% set sanitizer_list = [] %}
|
||||
{% set defines = [] %}
|
||||
{% 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" %}
|
||||
{% set _ = defines.append("BOOST_USE_ASAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% elif "thread" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("thread") %}
|
||||
{% set model_code = "-mcmodel=medium" %}
|
||||
{% set _ = extra_cxxflags.append("-Wno-tsan") %}
|
||||
{% set _ = defines.append("BOOST_USE_TSAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% endif %}
|
||||
|
||||
{% if "undefinedbehavior" in sanitizers %}
|
||||
@@ -29,16 +34,22 @@ include(default)
|
||||
tools.build:cxxflags+=['{{sanitizer_flags}} {{" ".join(extra_cxxflags)}}']
|
||||
tools.build:sharedlinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:exelinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:defines+={{defines}}
|
||||
{% endif %}
|
||||
{% elif compiler == "apple-clang" or compiler == "clang" %}
|
||||
{% if "address" in sanitizers or "thread" in sanitizers or "undefinedbehavior" in sanitizers %}
|
||||
{% set sanitizer_list = [] %}
|
||||
{% set defines = [] %}
|
||||
{% set extra_cxxflags = ["-fno-omit-frame-pointer", "-O1"] %}
|
||||
|
||||
{% if "address" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("address") %}
|
||||
{% set _ = defines.append("BOOST_USE_ASAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% elif "thread" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("thread") %}
|
||||
{% set _ = defines.append("BOOST_USE_TSAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% endif %}
|
||||
|
||||
{% if "undefinedbehavior" in sanitizers %}
|
||||
@@ -52,8 +63,24 @@ include(default)
|
||||
tools.build:cxxflags+=['{{sanitizer_flags}} {{" ".join(extra_cxxflags)}}']
|
||||
tools.build:sharedlinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:exelinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:defines+={{defines}}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags"]
|
||||
tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags", "tools.build:defines"]
|
||||
|
||||
[options]
|
||||
{% if sanitizers %}
|
||||
{% if "address" in sanitizers %}
|
||||
# Build Boost.Context with ucontext backend (not fcontext) so that
|
||||
# ASAN fiber-switching annotations (__sanitizer_start/finish_switch_fiber)
|
||||
# are compiled into the library. fcontext (assembly) has no ASAN support.
|
||||
# define=BOOST_USE_ASAN=1 is critical: it must be defined when building
|
||||
# Boost.Context itself so the ucontext backend compiles in the ASAN annotations.
|
||||
boost/*:extra_b2_flags=context-impl=ucontext address-sanitizer=on define=BOOST_USE_ASAN=1
|
||||
boost/*:without_context=False
|
||||
# Boost stacktrace fails to build with some sanitizers
|
||||
boost/*:without_stacktrace=True
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
14
conanfile.py
14
conanfile.py
@@ -1,4 +1,5 @@
|
||||
import re
|
||||
import os
|
||||
|
||||
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
|
||||
|
||||
@@ -35,7 +36,6 @@ class Xrpl(ConanFile):
|
||||
"openssl/3.5.5",
|
||||
"secp256k1/0.7.1",
|
||||
"soci/4.0.3",
|
||||
"wasmi/1.0.6",
|
||||
"zlib/1.3.1",
|
||||
]
|
||||
|
||||
@@ -58,6 +58,9 @@ class Xrpl(ConanFile):
|
||||
"tests": False,
|
||||
"unity": False,
|
||||
"xrpld": False,
|
||||
"boost/*:without_context": False,
|
||||
"boost/*:without_coroutine": True,
|
||||
"boost/*:without_coroutine2": False,
|
||||
"date/*:header_only": True,
|
||||
"ed25519/*:shared": False,
|
||||
"grpc/*:shared": False,
|
||||
@@ -127,6 +130,12 @@ class Xrpl(ConanFile):
|
||||
if self.settings.compiler in ["clang", "gcc"]:
|
||||
self.options["boost"].without_cobalt = True
|
||||
|
||||
# Check if environment variable exists
|
||||
if "SANITIZERS" in os.environ:
|
||||
sanitizers = os.environ["SANITIZERS"]
|
||||
if "address" in sanitizers.lower():
|
||||
self.default_options["fPIC"] = False
|
||||
|
||||
def requirements(self):
|
||||
# Conan 2 requires transitive headers to be specified
|
||||
transitive_headers_opt = (
|
||||
@@ -197,7 +206,7 @@ class Xrpl(ConanFile):
|
||||
"boost::headers",
|
||||
"boost::chrono",
|
||||
"boost::container",
|
||||
"boost::coroutine",
|
||||
"boost::context",
|
||||
"boost::date_time",
|
||||
"boost::filesystem",
|
||||
"boost::json",
|
||||
@@ -216,7 +225,6 @@ class Xrpl(ConanFile):
|
||||
"soci::soci",
|
||||
"secp256k1::secp256k1",
|
||||
"sqlite3::sqlite",
|
||||
"wasmi::wasmi",
|
||||
"xxhash::xxhash",
|
||||
"zlib::zlib",
|
||||
]
|
||||
|
||||
@@ -99,6 +99,7 @@ words:
|
||||
- endmacro
|
||||
- exceptioned
|
||||
- Falco
|
||||
- fcontext
|
||||
- finalizers
|
||||
- firewalled
|
||||
- fmtdur
|
||||
@@ -111,6 +112,7 @@ words:
|
||||
- gpgcheck
|
||||
- gpgkey
|
||||
- hotwallet
|
||||
- hwaddress
|
||||
- hwrap
|
||||
- ifndef
|
||||
- inequation
|
||||
@@ -293,7 +295,6 @@ words:
|
||||
- venv
|
||||
- vfalco
|
||||
- vinnie
|
||||
- wasmi
|
||||
- wextra
|
||||
- wptr
|
||||
- writeme
|
||||
|
||||
8
docs/build/sanitizers.md
vendored
8
docs/build/sanitizers.md
vendored
@@ -89,8 +89,8 @@ cmake --build . --parallel 4
|
||||
**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"
|
||||
export ASAN_OPTIONS="include=sanitizers/suppressions/runtime-asan-options.txt:suppressions=sanitizers/suppressions/asan.supp"
|
||||
export LSAN_OPTIONS="include=sanitizers/suppressions/runtime-lsan-options.txt:suppressions=sanitizers/suppressions/lsan.supp"
|
||||
|
||||
# Run tests
|
||||
./xrpld --unittest --unittest-jobs=5
|
||||
@@ -108,7 +108,7 @@ export LSAN_OPTIONS="suppressions=path/to/lsan.supp:halt_on_error=0:log_path=lsa
|
||||
### ThreadSanitizer (TSan)
|
||||
|
||||
```bash
|
||||
export TSAN_OPTIONS="suppressions=path/to/tsan.supp halt_on_error=0 log_path=tsan.log"
|
||||
export TSAN_OPTIONS="include=sanitizers/suppressions/runtime-tsan-options.txt:suppressions=sanitizers/suppressions/tsan.supp"
|
||||
|
||||
# Run tests
|
||||
./xrpld --unittest --unittest-jobs=5
|
||||
@@ -129,7 +129,7 @@ More details [here](https://github.com/google/sanitizers/wiki/AddressSanitizerLe
|
||||
### UndefinedBehaviorSanitizer (UBSan)
|
||||
|
||||
```bash
|
||||
export UBSAN_OPTIONS="suppressions=path/to/ubsan.supp:print_stacktrace=1:halt_on_error=0:log_path=ubsan.log"
|
||||
export UBSAN_OPTIONS="include=sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=sanitizers/suppressions/ubsan.supp"
|
||||
|
||||
# Run tests
|
||||
./xrpld --unittest --unittest-jobs=5
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/sanitizers.h>
|
||||
#include <xrpl/beast/type_name.h>
|
||||
|
||||
#include <exception>
|
||||
@@ -23,16 +24,28 @@ LogThrow(std::string const& title);
|
||||
When called from within a catch block, it will pass
|
||||
control to the next matching exception handler, if any.
|
||||
Otherwise, std::terminate will be called.
|
||||
|
||||
ASAN can't handle sudden jumps in control flow very well. This
|
||||
function is marked as XRPL_NO_SANITIZE_ADDRESS to prevent it from
|
||||
triggering false positives, since it throws.
|
||||
*/
|
||||
[[noreturn]] inline void
|
||||
[[noreturn]] XRPL_NO_SANITIZE_ADDRESS inline void
|
||||
Rethrow()
|
||||
{
|
||||
LogThrow("Re-throwing exception");
|
||||
throw;
|
||||
}
|
||||
|
||||
/*
|
||||
Logs and throws an exception of type E.
|
||||
|
||||
ASAN can't handle sudden jumps in control flow very well. This
|
||||
function is marked as XRPL_NO_SANITIZE_ADDRESS to prevent it from
|
||||
triggering false positives, since it throws.
|
||||
*/
|
||||
|
||||
template <class E, class... Args>
|
||||
[[noreturn]] inline void
|
||||
[[noreturn]] XRPL_NO_SANITIZE_ADDRESS inline void
|
||||
Throw(Args&&... args)
|
||||
{
|
||||
static_assert(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -70,4 +72,31 @@ unsafe_cast(Src s) noexcept
|
||||
return unsafe_cast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
|
||||
}
|
||||
|
||||
template <class Dest, class Src>
|
||||
requires std::is_pointer_v<Dest>
|
||||
inline Dest
|
||||
safe_downcast(Src* s) noexcept
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
return static_cast<Dest>(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
|
||||
#else
|
||||
auto* result = dynamic_cast<Dest>(s);
|
||||
XRPL_ASSERT(result != nullptr, "xrpl::safe_downcast : pointer downcast is valid");
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class Dest, class Src>
|
||||
requires std::is_lvalue_reference_v<Dest>
|
||||
inline Dest
|
||||
safe_downcast(Src& s) noexcept
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
XRPL_ASSERT(
|
||||
dynamic_cast<std::add_pointer_t<std::remove_reference_t<Dest>>>(&s) != nullptr,
|
||||
"xrpl::safe_downcast : reference downcast is valid");
|
||||
#endif
|
||||
return static_cast<Dest>(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
13
include/xrpl/basics/sanitizers.h
Normal file
13
include/xrpl/basics/sanitizers.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
// Helper to disable ASan/HwASan for specific functions
|
||||
/*
|
||||
ASAN flags some false positives with sudden jumps in control flow, like
|
||||
exceptions, or when encountering coroutine stack switches. This macro can be used to disable ASAN
|
||||
intrumentation for specific functions.
|
||||
*/
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define XRPL_NO_SANITIZE_ADDRESS __attribute__((no_sanitize("address", "hwaddress")))
|
||||
#else
|
||||
#define XRPL_NO_SANITIZE_ADDRESS
|
||||
#endif
|
||||
@@ -43,8 +43,8 @@ private:
|
||||
template <typename>
|
||||
friend class ListIterator;
|
||||
|
||||
ListNode* m_next;
|
||||
ListNode* m_prev;
|
||||
ListNode* m_next = nullptr;
|
||||
ListNode* m_prev = nullptr;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -567,7 +567,7 @@ private:
|
||||
}
|
||||
|
||||
private:
|
||||
size_type m_size;
|
||||
size_type m_size = 0u;
|
||||
Node m_head;
|
||||
Node m_tail;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <class F>
|
||||
@@ -11,16 +9,18 @@ JobQueue::Coro::Coro(Coro_create_t, JobQueue& jq, JobType type, std::string cons
|
||||
, name_(name)
|
||||
, running_(false)
|
||||
, coro_(
|
||||
// Stack size of 1MB wasn't sufficient for deep calls. ASAN tests flagged the issue. Hence
|
||||
// increasing the size to 1.5MB.
|
||||
boost::context::protected_fixedsize_stack(1536 * 1024),
|
||||
[this, fn = std::forward<F>(f)](
|
||||
boost::coroutines::asymmetric_coroutine<void>::push_type& do_yield) {
|
||||
boost::coroutines2::asymmetric_coroutine<void>::push_type& do_yield) {
|
||||
yield_ = &do_yield;
|
||||
yield();
|
||||
fn(shared_from_this());
|
||||
#ifndef NDEBUG
|
||||
finished_ = true;
|
||||
#endif
|
||||
},
|
||||
boost::coroutines::attributes(megabytes(1)))
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
#include <xrpl/core/detail/Workers.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <boost/coroutine/all.hpp>
|
||||
#include <boost/context/protected_fixedsize_stack.hpp>
|
||||
#include <boost/coroutine2/all.hpp>
|
||||
|
||||
#include <set>
|
||||
|
||||
@@ -48,8 +49,8 @@ public:
|
||||
std::mutex mutex_;
|
||||
std::mutex mutex_run_;
|
||||
std::condition_variable cv_;
|
||||
boost::coroutines::asymmetric_coroutine<void>::pull_type coro_;
|
||||
boost::coroutines::asymmetric_coroutine<void>::push_type* yield_;
|
||||
boost::coroutines2::coroutine<void>::pull_type coro_;
|
||||
boost::coroutines2::coroutine<void>::push_type* yield_;
|
||||
#ifndef NDEBUG
|
||||
bool finished_ = false;
|
||||
#endif
|
||||
|
||||
@@ -421,7 +421,7 @@ private:
|
||||
ObjectValues* map_{nullptr};
|
||||
} value_;
|
||||
ValueType type_ : 8;
|
||||
int allocated_ : 1; // Notes: if declared as bool, bitfield is useless.
|
||||
int allocated_ : 1 {}; // Notes: if declared as bool, bitfield is useless.
|
||||
};
|
||||
|
||||
inline Value
|
||||
|
||||
@@ -108,7 +108,7 @@ private:
|
||||
std::string indentString_;
|
||||
int rightMargin_;
|
||||
int indentSize_;
|
||||
bool addChildValues_;
|
||||
bool addChildValues_{};
|
||||
};
|
||||
|
||||
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
|
||||
@@ -175,7 +175,7 @@ private:
|
||||
std::string indentString_;
|
||||
int rightMargin_;
|
||||
std::string indentation_;
|
||||
bool addChildValues_;
|
||||
bool addChildValues_{};
|
||||
};
|
||||
|
||||
std::string
|
||||
|
||||
@@ -49,7 +49,7 @@ validDomain(ReadView const& view, uint256 domainID, AccountID const& subject);
|
||||
// This function is only called when we about to return tecNO_PERMISSION
|
||||
// because all the checks for the DepositPreauth authorization failed.
|
||||
TER
|
||||
authorizedDepositPreauth(ApplyView const& view, STVector256 const& ctx, AccountID const& dst);
|
||||
authorizedDepositPreauth(ReadView const& view, STVector256 const& ctx, AccountID const& dst);
|
||||
|
||||
// Sort credentials array, return empty set if there are duplicates
|
||||
std::set<std::pair<AccountID, Slice>>
|
||||
@@ -74,7 +74,7 @@ verifyDepositPreauth(
|
||||
ApplyView& view,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
std::shared_ptr<SLE> const& sleDst,
|
||||
std::shared_ptr<SLE const> const& sleDst,
|
||||
beast::Journal j);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -29,6 +29,18 @@ public:
|
||||
bool sslVerify,
|
||||
beast::Journal j);
|
||||
|
||||
/** Destroys the global SSL context created by initializeSSLContext().
|
||||
*
|
||||
* This releases the underlying boost::asio::ssl::context and any
|
||||
* associated OpenSSL resources. Must not be called while any
|
||||
* HTTPClient requests are in flight.
|
||||
*
|
||||
* @note Currently only called from tests during teardown. In production,
|
||||
* the SSL context lives for the lifetime of the process.
|
||||
*/
|
||||
static void
|
||||
cleanupSSLContext();
|
||||
|
||||
static void
|
||||
get(bool bSSL,
|
||||
boost::asio::io_context& io_context,
|
||||
|
||||
@@ -138,6 +138,9 @@ public:
|
||||
/** Returns the number of file descriptors the backend expects to need. */
|
||||
virtual int
|
||||
fdRequired() const = 0;
|
||||
|
||||
/** The number of hardware threads to use for compression of a batch. */
|
||||
static unsigned int const numHardwareThreads;
|
||||
};
|
||||
|
||||
} // namespace NodeStore
|
||||
|
||||
@@ -64,6 +64,49 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Feature names must not exceed this length (in characters, excluding the null terminator).
|
||||
static constexpr std::size_t maxFeatureNameSize = 63;
|
||||
// Reserve this exact feature-name length (in characters/bytes, excluding the null terminator)
|
||||
// so that a 32-byte uint256 (for example, in WASM or other interop contexts) can be used
|
||||
// as a compact, fixed-size feature selector without conflicting with human-readable names.
|
||||
static constexpr std::size_t reservedFeatureNameSize = 32;
|
||||
|
||||
// Both validFeatureNameSize and validFeatureName are consteval functions that can be used in
|
||||
// static_asserts to validate feature names at compile time. They are only used inside
|
||||
// enforceValidFeatureName in Feature.cpp, but are exposed here for testing. The expected
|
||||
// parameter `auto fn` is a constexpr lambda which returns a const char*, making it available
|
||||
// for compile-time evaluation. Read more in https://accu.org/journals/overload/30/172/wu/
|
||||
consteval auto
|
||||
validFeatureNameSize(auto fn) -> bool
|
||||
{
|
||||
constexpr char const* n = fn();
|
||||
// Note, std::strlen is not constexpr, we need to implement our own here.
|
||||
constexpr std::size_t N = [](auto n) {
|
||||
std::size_t ret = 0;
|
||||
for (auto ptr = n; *ptr != '\0'; ret++, ++ptr)
|
||||
;
|
||||
return ret;
|
||||
}(n);
|
||||
return N != reservedFeatureNameSize && //
|
||||
N <= maxFeatureNameSize;
|
||||
}
|
||||
|
||||
consteval auto
|
||||
validFeatureName(auto fn) -> bool
|
||||
{
|
||||
constexpr char const* n = fn();
|
||||
// Prevent the use of visually confusable characters and enforce that feature names
|
||||
// are always valid ASCII. This is needed because C++ allows Unicode identifiers.
|
||||
// Characters below 0x20 are nonprintable control characters, and characters with the 0x80 bit
|
||||
// set are non-ASCII (e.g. UTF-8 encoding of Unicode), so both are disallowed.
|
||||
for (auto ptr = n; *ptr != '\0'; ++ptr)
|
||||
{
|
||||
if (*ptr & 0x80 || *ptr < 0x20)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
enum class VoteBehavior : int { Obsolete = -1, DefaultNo = 0, DefaultYes };
|
||||
enum class AmendmentSupport : int { Retired = -1, Supported = 0, Unsupported };
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ std::size_t constexpr maxDIDDocumentLength = 256;
|
||||
std::size_t constexpr maxDIDURILength = 256;
|
||||
|
||||
/** The maximum length of an Attestation inside a DID */
|
||||
std::size_t constexpr maxDIDAttestationLength = 256;
|
||||
std::size_t constexpr maxDIDDataLength = 256;
|
||||
|
||||
/** The maximum length of a domain */
|
||||
std::size_t constexpr maxDomainLength = 256;
|
||||
|
||||
@@ -44,7 +44,7 @@ protected:
|
||||
// All the constructed public keys are valid, non-empty and contain 33
|
||||
// bytes of data.
|
||||
static constexpr std::size_t size_ = 33;
|
||||
std::uint8_t buf_[size_]; // should be large enough
|
||||
std::uint8_t buf_[size_]{}; // should be large enough
|
||||
|
||||
public:
|
||||
using const_iterator = std::uint8_t const*;
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
STAccount();
|
||||
|
||||
STAccount(SField const& n);
|
||||
STAccount(SField const& n, Buffer&& v);
|
||||
STAccount(SField const& n, Buffer const& v);
|
||||
STAccount(SerialIter& sit, SField const& name);
|
||||
STAccount(SField const& n, AccountID const& v);
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ class STObject : public STBase, public CountedObject<STObject>
|
||||
using list_type = std::vector<detail::STVar>;
|
||||
|
||||
list_type v_;
|
||||
SOTemplate const* mType;
|
||||
SOTemplate const* mType{};
|
||||
|
||||
public:
|
||||
using iterator = boost::transform_iterator<Transform, STObject::list_type::const_iterator>;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
enum TxnSql : char {
|
||||
enum class TxnSql : char {
|
||||
txnSqlNew = 'N',
|
||||
txnSqlConflict = 'C',
|
||||
txnSqlHeld = 'H',
|
||||
@@ -83,6 +83,9 @@ public:
|
||||
std::uint32_t
|
||||
getSeqValue() const;
|
||||
|
||||
AccountID
|
||||
getFeePayer() const;
|
||||
|
||||
boost::container::flat_set<AccountID>
|
||||
getMentionedAccounts() const;
|
||||
|
||||
@@ -122,7 +125,7 @@ public:
|
||||
getMetaSQL(
|
||||
Serializer rawTxn,
|
||||
std::uint32_t inLedger,
|
||||
char status,
|
||||
TxnSql status,
|
||||
std::string const& escapedMetaData) const;
|
||||
|
||||
std::vector<uint256> const&
|
||||
|
||||
@@ -16,8 +16,11 @@ namespace xrpl {
|
||||
/** A secret key. */
|
||||
class SecretKey
|
||||
{
|
||||
public:
|
||||
static constexpr std::size_t size_ = 32;
|
||||
|
||||
private:
|
||||
std::uint8_t buf_[32];
|
||||
std::uint8_t buf_[size_]{};
|
||||
|
||||
public:
|
||||
using const_iterator = std::uint8_t const*;
|
||||
@@ -27,9 +30,14 @@ public:
|
||||
SecretKey&
|
||||
operator=(SecretKey const&) = default;
|
||||
|
||||
bool
|
||||
operator==(SecretKey const&) = delete;
|
||||
bool
|
||||
operator!=(SecretKey const&) = delete;
|
||||
|
||||
~SecretKey();
|
||||
|
||||
SecretKey(std::array<std::uint8_t, 32> const& data);
|
||||
SecretKey(std::array<std::uint8_t, size_> const& data);
|
||||
SecretKey(Slice const& slice);
|
||||
|
||||
std::uint8_t const*
|
||||
@@ -78,16 +86,10 @@ public:
|
||||
};
|
||||
|
||||
inline bool
|
||||
operator==(SecretKey const& lhs, SecretKey const& rhs)
|
||||
{
|
||||
return lhs.size() == rhs.size() && std::memcmp(lhs.data(), rhs.data(), rhs.size()) == 0;
|
||||
}
|
||||
operator==(SecretKey const& lhs, SecretKey const& rhs) = delete;
|
||||
|
||||
inline bool
|
||||
operator!=(SecretKey const& lhs, SecretKey const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
operator!=(SecretKey const& lhs, SecretKey const& rhs) = delete;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace xrpl {
|
||||
class Seed
|
||||
{
|
||||
private:
|
||||
std::array<uint8_t, 16> buf_;
|
||||
std::array<uint8_t, 16> buf_{};
|
||||
|
||||
public:
|
||||
using const_iterator = std::array<uint8_t, 16>::const_iterator;
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace xrpl {
|
||||
static inline std::string const&
|
||||
systemName()
|
||||
{
|
||||
static std::string const name = "ripple";
|
||||
static std::string const name = "xrpld";
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ private:
|
||||
// The largest "small object" we can accommodate
|
||||
static std::size_t constexpr max_size = 72;
|
||||
|
||||
std::aligned_storage<max_size>::type d_;
|
||||
std::aligned_storage<max_size>::type d_ = {};
|
||||
STBase* p_ = nullptr;
|
||||
|
||||
public:
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
operator result_type() noexcept;
|
||||
|
||||
private:
|
||||
char ctx_[96];
|
||||
char ctx_[96]{};
|
||||
};
|
||||
|
||||
/** SHA-512 digest
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
operator result_type() noexcept;
|
||||
|
||||
private:
|
||||
char ctx_[216];
|
||||
char ctx_[216]{};
|
||||
};
|
||||
|
||||
/** SHA-256 digest
|
||||
@@ -86,7 +86,7 @@ public:
|
||||
operator result_type() noexcept;
|
||||
|
||||
private:
|
||||
char ctx_[112];
|
||||
char ctx_[112]{};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -114,8 +114,7 @@ protected:
|
||||
beast::Journal const j_;
|
||||
|
||||
AccountID const account_;
|
||||
XRPAmount mPriorBalance; // Balance before fees.
|
||||
XRPAmount mSourceBalance; // Balance after fees.
|
||||
XRPAmount preFeeBalance_{}; // Balance before fees.
|
||||
|
||||
virtual ~Transactor() = default;
|
||||
Transactor(Transactor const&) = delete;
|
||||
|
||||
@@ -27,6 +27,33 @@ namespace xrpl {
|
||||
* communicate the interface required of any invariant checker. Any invariant
|
||||
* check implementation should implement the public methods documented here.
|
||||
*
|
||||
* ## Rules for implementing `finalize`
|
||||
*
|
||||
* ### Invariants must run regardless of transaction result
|
||||
*
|
||||
* An invariant's `finalize` method MUST perform meaningful checks even when
|
||||
* the transaction has failed (i.e., `!isTesSuccess(tec)`). The following
|
||||
* pattern is almost certainly wrong and must never be used:
|
||||
*
|
||||
* @code
|
||||
* // WRONG: skipping all checks on failure defeats the purpose of invariants
|
||||
* if (!isTesSuccess(tec))
|
||||
* return true;
|
||||
* @endcode
|
||||
*
|
||||
* The entire purpose of invariants is to detect and prevent the impossible.
|
||||
* A bug or exploit could cause a failed transaction to mutate ledger state in
|
||||
* unexpected ways. Invariants are the last line of defense against such
|
||||
* scenarios.
|
||||
*
|
||||
* In general: an invariant that expects a domain-specific state change to
|
||||
* occur (e.g., a new object being created) should only expect that change
|
||||
* when the transaction succeeded. A failed VaultCreate must not have created
|
||||
* a Vault. A failed LoanSet must not have created a Loan.
|
||||
*
|
||||
* Also be aware that failed transactions, regardless of type, carry no
|
||||
* Privileges. Any privilege-gated checks must therefore also be applied to
|
||||
* failed transactions.
|
||||
*/
|
||||
class InvariantChecker_PROTOTYPE
|
||||
{
|
||||
@@ -48,7 +75,11 @@ public:
|
||||
|
||||
/**
|
||||
* @brief called after all ledger entries have been visited to determine
|
||||
* the final status of the check
|
||||
* the final status of the check.
|
||||
*
|
||||
* This method MUST perform meaningful checks even when `tec` indicates a
|
||||
* failed transaction. See the class-level documentation for the rules
|
||||
* governing how failed transactions must be handled.
|
||||
*
|
||||
* @param tx the transaction being applied
|
||||
* @param tec the current TER result of the transaction
|
||||
|
||||
@@ -22,7 +22,7 @@ private:
|
||||
uint256 m_dir;
|
||||
uint256 m_index;
|
||||
std::shared_ptr<SLE> m_entry;
|
||||
Quality m_quality;
|
||||
Quality m_quality{};
|
||||
|
||||
public:
|
||||
/** Create the iterator. */
|
||||
|
||||
@@ -370,7 +370,7 @@ changeSpotPriceQuality(
|
||||
if (!amounts)
|
||||
{
|
||||
JLOG(j.trace()) << "changeSpotPrice calc failed: " << to_string(pool.in) << " "
|
||||
<< to_string(pool.out) << " " << quality << " " << tfee << std::endl;
|
||||
<< to_string(pool.out) << " " << quality << " " << tfee;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -639,9 +639,9 @@ getRoundedAsset(Rules const& rules, STAmount const& balance, A const& frac, IsDe
|
||||
STAmount
|
||||
getRoundedAsset(
|
||||
Rules const& rules,
|
||||
std::function<Number()>&& noRoundCb,
|
||||
std::function<Number()> const& noRoundCb,
|
||||
STAmount const& balance,
|
||||
std::function<Number()>&& productCb,
|
||||
std::function<Number()> const& productCb,
|
||||
IsDeposit isDeposit);
|
||||
|
||||
/** Round AMM deposit/withdrawal LPToken amount. Deposit/withdrawal formulas
|
||||
@@ -672,9 +672,9 @@ getRoundedLPTokens(
|
||||
STAmount
|
||||
getRoundedLPTokens(
|
||||
Rules const& rules,
|
||||
std::function<Number()>&& noRoundCb,
|
||||
std::function<Number()> const& noRoundCb,
|
||||
STAmount const& lptAMMBalance,
|
||||
std::function<Number()>&& productCb,
|
||||
std::function<Number()> const& productCb,
|
||||
IsDeposit isDeposit);
|
||||
|
||||
/* Next two functions adjust asset in/out amount to factor in the adjusted
|
||||
|
||||
@@ -54,7 +54,7 @@ removeToken(
|
||||
ApplyView& view,
|
||||
AccountID const& owner,
|
||||
uint256 const& nftokenID,
|
||||
std::shared_ptr<SLE>&& page);
|
||||
std::shared_ptr<SLE> const& page);
|
||||
|
||||
/** Deletes the given token offer.
|
||||
|
||||
|
||||
@@ -1,24 +1,6 @@
|
||||
# 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)
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
# 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
|
||||
|
||||
8
sanitizers/suppressions/runtime-asan-options.txt
Normal file
8
sanitizers/suppressions/runtime-asan-options.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
detect_container_overflow=false
|
||||
detect_stack_use_after_return=false
|
||||
debug=true
|
||||
halt_on_error=false
|
||||
print_stats=true
|
||||
print_cmdline=true
|
||||
use_sigaltstack=0
|
||||
print_stacktrace=1
|
||||
1
sanitizers/suppressions/runtime-lsan-options.txt
Normal file
1
sanitizers/suppressions/runtime-lsan-options.txt
Normal file
@@ -0,0 +1 @@
|
||||
halt_on_error=false
|
||||
3
sanitizers/suppressions/runtime-tsan-options.txt
Normal file
3
sanitizers/suppressions/runtime-tsan-options.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
halt_on_error=false
|
||||
verbosity=1
|
||||
second_deadlock_stack=1
|
||||
1
sanitizers/suppressions/runtime-ubsan-options.txt
Normal file
1
sanitizers/suppressions/runtime-ubsan-options.txt
Normal file
@@ -0,0 +1 @@
|
||||
halt_on_error=false
|
||||
@@ -27,3 +27,11 @@ 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
|
||||
|
||||
# ASan false positive: stack-use-after-scope in ErrorCodes.h inline functions.
|
||||
# When Clang inlines the StaticString overloads (e.g. invalid_field_error(StaticString)),
|
||||
# ASan scope-poisons the temporary std::string before the inlined callee finishes reading
|
||||
# through the const ref. This corrupts the coroutine stack and crashes the Simulate test.
|
||||
# See asan.supp comments for full explanation and planned fix.
|
||||
[address]
|
||||
src:*ErrorCodes.h
|
||||
|
||||
@@ -182,6 +182,17 @@ signed-integer-overflow:src/test/beast/LexicalCast_test.cpp
|
||||
# External library suppressions
|
||||
unsigned-integer-overflow:nudb/detail/xxhash.hpp
|
||||
|
||||
# Loan_test.cpp intentional underflow in test arithmetic
|
||||
unsigned-integer-overflow:src/test/app/Loan_test.cpp
|
||||
undefined:src/test/app/Loan_test.cpp
|
||||
|
||||
# Source tree restructured paths (libxrpl/tx/transactors/)
|
||||
# These duplicate the xrpld/app/tx/detail entries above for the new layout
|
||||
unsigned-integer-overflow:src/libxrpl/tx/transactors/oracle/SetOracle.cpp
|
||||
undefined:src/libxrpl/tx/transactors/oracle/SetOracle.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/tx/transactors/nft/NFTokenMint.cpp
|
||||
undefined:src/libxrpl/tx/transactors/nft/NFTokenMint.cpp
|
||||
|
||||
# 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
|
||||
|
||||
@@ -51,8 +51,8 @@ extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const&
|
||||
if (archive_write_disk_set_standard_lookup(aw.get()) < ARCHIVE_OK)
|
||||
Throw<std::runtime_error>(archive_error_string(aw.get()));
|
||||
|
||||
int result;
|
||||
struct archive_entry* entry;
|
||||
int result = 0;
|
||||
struct archive_entry* entry = nullptr;
|
||||
while (true)
|
||||
{
|
||||
result = archive_read_next_header(ar.get(), &entry);
|
||||
@@ -67,9 +67,9 @@ extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const&
|
||||
|
||||
if (archive_entry_size(entry) > 0)
|
||||
{
|
||||
void const* buf;
|
||||
size_t sz;
|
||||
la_int64_t offset;
|
||||
void const* buf = nullptr;
|
||||
size_t sz = 0;
|
||||
la_int64_t offset = 0;
|
||||
while (true)
|
||||
{
|
||||
result = archive_read_data_block(ar.get(), &buf, &sz, &offset);
|
||||
|
||||
@@ -66,14 +66,12 @@ concept UnsignedMantissa = std::is_unsigned_v<T> || std::is_same_v<T, uint128_t>
|
||||
|
||||
class Number::Guard
|
||||
{
|
||||
std::uint64_t digits_; // 16 decimal guard digits
|
||||
std::uint8_t xbit_ : 1; // has a non-zero digit been shifted off the end
|
||||
std::uint8_t sbit_ : 1; // the sign of the guard digits
|
||||
std::uint64_t digits_{0}; // 16 decimal guard digits
|
||||
std::uint8_t xbit_ : 1 {0}; // has a non-zero digit been shifted off the end
|
||||
std::uint8_t sbit_ : 1 {0}; // the sign of the guard digits
|
||||
|
||||
public:
|
||||
explicit Guard() : digits_{0}, xbit_{0}, sbit_{0}
|
||||
{
|
||||
}
|
||||
explicit Guard() = default;
|
||||
|
||||
// set & test the sign bit
|
||||
void
|
||||
@@ -258,7 +256,7 @@ Number::Guard::doRoundUp(
|
||||
}
|
||||
bringIntoRange(negative, mantissa, exponent, minMantissa);
|
||||
if (exponent > maxExponent)
|
||||
throw std::overflow_error(location);
|
||||
Throw<std::overflow_error>(std::string(location));
|
||||
}
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
@@ -298,7 +296,7 @@ Number::Guard::doRound(rep& drops, std::string location)
|
||||
// or "(maxRep + 1) / 10", neither of which will round up when
|
||||
// converting to rep, though the latter might overflow _before_
|
||||
// rounding.
|
||||
throw std::overflow_error(location); // LCOV_EXCL_LINE
|
||||
Throw<std::overflow_error>(std::string(location)); // LCOV_EXCL_LINE
|
||||
}
|
||||
++drops;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ namespace xrpl {
|
||||
template <class Derived>
|
||||
class AsyncObject
|
||||
{
|
||||
protected:
|
||||
AsyncObject() : m_pending(0)
|
||||
{
|
||||
}
|
||||
@@ -93,6 +92,8 @@ public:
|
||||
private:
|
||||
// The number of handlers pending.
|
||||
std::atomic<int> m_pending;
|
||||
|
||||
friend Derived;
|
||||
};
|
||||
|
||||
class ResolverAsioImpl : public ResolverAsio, public AsyncObject<ResolverAsioImpl>
|
||||
@@ -108,7 +109,7 @@ public:
|
||||
|
||||
std::condition_variable m_cv;
|
||||
std::mutex m_mut;
|
||||
bool m_asyncHandlersCompleted;
|
||||
bool m_asyncHandlersCompleted{true};
|
||||
|
||||
std::atomic<bool> m_stop_called;
|
||||
std::atomic<bool> m_stopped;
|
||||
@@ -135,7 +136,6 @@ public:
|
||||
, m_io_context(io_context)
|
||||
, m_strand(boost::asio::make_strand(io_context))
|
||||
, m_resolver(io_context)
|
||||
, m_asyncHandlersCompleted(true)
|
||||
, m_stop_called(false)
|
||||
, m_stopped(true)
|
||||
{
|
||||
|
||||
@@ -103,7 +103,7 @@ trim_whitespace(std::string str)
|
||||
std::optional<std::uint64_t>
|
||||
to_uint64(std::string const& s)
|
||||
{
|
||||
std::uint64_t result;
|
||||
std::uint64_t result = 0;
|
||||
if (beast::lexicalCastChecked(result, s))
|
||||
return result;
|
||||
return std::nullopt;
|
||||
|
||||
@@ -116,6 +116,7 @@ encode(void* dest, void const* src, std::size_t len)
|
||||
in += 3;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-switch-missing-default-case)
|
||||
switch (len % 3)
|
||||
{
|
||||
case 2:
|
||||
|
||||
@@ -239,6 +239,7 @@ initAuthenticated(
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-unused-return-value)
|
||||
context.use_certificate_file(cert_file, boost::asio::ssl::context::pem, ec);
|
||||
|
||||
if (ec)
|
||||
@@ -298,6 +299,7 @@ initAuthenticated(
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-unused-return-value)
|
||||
context.use_private_key_file(key_file, boost::asio::ssl::context::pem, ec);
|
||||
|
||||
if (ec)
|
||||
|
||||
@@ -16,7 +16,7 @@ class seconds_clock_thread
|
||||
{
|
||||
using Clock = basic_seconds_clock::Clock;
|
||||
|
||||
bool stop_;
|
||||
bool stop_{false};
|
||||
std::mutex mut_;
|
||||
std::condition_variable cv_;
|
||||
std::thread thread_;
|
||||
@@ -48,8 +48,7 @@ seconds_clock_thread::~seconds_clock_thread()
|
||||
thread_.join();
|
||||
}
|
||||
|
||||
seconds_clock_thread::seconds_clock_thread()
|
||||
: stop_{false}, tp_{Clock::now().time_since_epoch().count()}
|
||||
seconds_clock_thread::seconds_clock_thread() : tp_{Clock::now().time_since_epoch().count()}
|
||||
{
|
||||
thread_ = std::thread(&seconds_clock_thread::run, this);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ print_identifiers(SemanticVersion::identifier_list const& list)
|
||||
bool
|
||||
isNumeric(std::string const& s)
|
||||
{
|
||||
int n;
|
||||
int n = 0;
|
||||
|
||||
// Must be convertible to an integer
|
||||
if (!lexicalCastChecked(n, s))
|
||||
@@ -68,7 +68,7 @@ chopUInt(int& value, int limit, std::string& input)
|
||||
if (item.empty())
|
||||
return false;
|
||||
|
||||
int n;
|
||||
int n = 0;
|
||||
|
||||
// Must be convertible to an integer
|
||||
if (!lexicalCastChecked(n, item))
|
||||
|
||||
@@ -104,8 +104,8 @@ private:
|
||||
|
||||
std::shared_ptr<StatsDCollectorImp> m_impl;
|
||||
std::string m_name;
|
||||
CounterImpl::value_type m_value;
|
||||
bool m_dirty;
|
||||
CounterImpl::value_type m_value{0};
|
||||
bool m_dirty{false};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -162,9 +162,9 @@ private:
|
||||
|
||||
std::shared_ptr<StatsDCollectorImp> m_impl;
|
||||
std::string m_name;
|
||||
GaugeImpl::value_type m_last_value;
|
||||
GaugeImpl::value_type m_value;
|
||||
bool m_dirty;
|
||||
GaugeImpl::value_type m_last_value{0};
|
||||
GaugeImpl::value_type m_value{0};
|
||||
bool m_dirty{false};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -194,8 +194,8 @@ private:
|
||||
|
||||
std::shared_ptr<StatsDCollectorImp> m_impl;
|
||||
std::string m_name;
|
||||
MeterImpl::value_type m_value;
|
||||
bool m_dirty;
|
||||
MeterImpl::value_type m_value{0};
|
||||
bool m_dirty{false};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -470,6 +470,7 @@ public:
|
||||
|
||||
m_io_context.run();
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-unused-return-value)
|
||||
m_socket.shutdown(boost::asio::ip::udp::socket::shutdown_send, ec);
|
||||
|
||||
m_socket.close();
|
||||
@@ -504,7 +505,7 @@ StatsDHookImpl::do_process()
|
||||
StatsDCounterImpl::StatsDCounterImpl(
|
||||
std::string const& name,
|
||||
std::shared_ptr<StatsDCollectorImp> const& impl)
|
||||
: m_impl(impl), m_name(name), m_value(0), m_dirty(false)
|
||||
: m_impl(impl), m_name(name)
|
||||
{
|
||||
m_impl->add(*this);
|
||||
}
|
||||
@@ -586,7 +587,7 @@ StatsDEventImpl::do_notify(EventImpl::value_type const& value)
|
||||
StatsDGaugeImpl::StatsDGaugeImpl(
|
||||
std::string const& name,
|
||||
std::shared_ptr<StatsDCollectorImp> const& impl)
|
||||
: m_impl(impl), m_name(name), m_last_value(0), m_value(0), m_dirty(false)
|
||||
: m_impl(impl), m_name(name)
|
||||
{
|
||||
m_impl->add(*this);
|
||||
}
|
||||
@@ -675,7 +676,7 @@ StatsDGaugeImpl::do_process()
|
||||
StatsDMeterImpl::StatsDMeterImpl(
|
||||
std::string const& name,
|
||||
std::shared_ptr<StatsDCollectorImp> const& impl)
|
||||
: m_impl(impl), m_name(name), m_value(0), m_dirty(false)
|
||||
: m_impl(impl), m_name(name)
|
||||
{
|
||||
m_impl->add(*this);
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ operator>>(std::istream& is, Endpoint& endpoint)
|
||||
|
||||
if (is.rdbuf()->in_avail() > 0)
|
||||
{
|
||||
Port port;
|
||||
Port port = 0;
|
||||
is >> port;
|
||||
if (is.fail())
|
||||
return is;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace beast {
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
PropertyStream::Item::Item(Source* source) : m_source(source)
|
||||
PropertyStream::Item::Item(Source* source) : ListNode(), m_source(source)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -331,7 +331,7 @@ JobQueue::finishJob(JobType type)
|
||||
void
|
||||
JobQueue::processTask(int instance)
|
||||
{
|
||||
JobType type;
|
||||
JobType type = jtINVALID;
|
||||
|
||||
{
|
||||
using namespace std::chrono;
|
||||
|
||||
@@ -198,10 +198,10 @@ char const* RFC1751::s_dictionary[2048] = {
|
||||
unsigned long
|
||||
RFC1751::extract(char const* s, int start, int length)
|
||||
{
|
||||
unsigned char cl;
|
||||
unsigned char cc;
|
||||
unsigned char cr;
|
||||
unsigned long x;
|
||||
unsigned char cl = 0;
|
||||
unsigned char cc = 0;
|
||||
unsigned char cr = 0;
|
||||
unsigned long x = 0;
|
||||
|
||||
XRPL_ASSERT(length <= 11, "xrpl::RFC1751::extract : maximum length");
|
||||
XRPL_ASSERT(start >= 0, "xrpl::RFC1751::extract : minimum start");
|
||||
@@ -226,7 +226,7 @@ void
|
||||
RFC1751::btoe(std::string& strHuman, std::string const& strData)
|
||||
{
|
||||
char caBuffer[9]; /* add in room for the parity 2 bits*/
|
||||
int p, i;
|
||||
int p = 0, i = 0;
|
||||
|
||||
memcpy(caBuffer, strData.c_str(), 8);
|
||||
|
||||
@@ -245,11 +245,11 @@ RFC1751::btoe(std::string& strHuman, std::string const& strData)
|
||||
void
|
||||
RFC1751::insert(char* s, int x, int start, int length)
|
||||
{
|
||||
unsigned char cl;
|
||||
unsigned char cc;
|
||||
unsigned char cr;
|
||||
unsigned long y;
|
||||
int shift;
|
||||
unsigned char cl = 0;
|
||||
unsigned char cc = 0;
|
||||
unsigned char cr = 0;
|
||||
unsigned long y = 0;
|
||||
int shift = 0;
|
||||
|
||||
XRPL_ASSERT(length <= 11, "xrpl::RFC1751::insert : maximum length");
|
||||
XRPL_ASSERT(start >= 0, "xrpl::RFC1751::insert : minimum start");
|
||||
@@ -336,7 +336,7 @@ RFC1751::etob(std::string& strData, std::vector<std::string> vsHuman)
|
||||
if (6 != vsHuman.size())
|
||||
return -1;
|
||||
|
||||
int i, p = 0;
|
||||
int i = 0, p = 0;
|
||||
char b[9] = {0};
|
||||
|
||||
for (auto& strWord : vsHuman)
|
||||
|
||||
@@ -30,7 +30,7 @@ csprng_engine::~csprng_engine()
|
||||
void
|
||||
csprng_engine::mix_entropy(void* buffer, std::size_t count)
|
||||
{
|
||||
std::array<std::random_device::result_type, 128> entropy;
|
||||
std::array<std::random_device::result_type, 128> entropy{};
|
||||
|
||||
{
|
||||
// On every platform we support, std::random_device
|
||||
@@ -71,7 +71,7 @@ csprng_engine::operator()(void* ptr, std::size_t count)
|
||||
csprng_engine::result_type
|
||||
csprng_engine::operator()()
|
||||
{
|
||||
result_type ret;
|
||||
result_type ret = 0;
|
||||
(*this)(&ret, sizeof(result_type));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ private:
|
||||
explicit Collection() = default;
|
||||
|
||||
/** What type of collection are we in? */
|
||||
Writer::CollectionType type;
|
||||
Writer::CollectionType type = Writer::CollectionType::array;
|
||||
|
||||
/** Is this the first entry in a collection?
|
||||
* If false, we have to emit a , before we write the next entry. */
|
||||
|
||||
@@ -94,7 +94,7 @@ Reader::parse(char const* beginDoc, char const* endDoc, Value& root)
|
||||
|
||||
nodes_.push(&root);
|
||||
bool successful = readValue(0);
|
||||
Token token;
|
||||
Token token{};
|
||||
skipCommentTokens(token);
|
||||
|
||||
if (!root.isNull() && !root.isArray() && !root.isObject())
|
||||
@@ -114,7 +114,7 @@ Reader::parse(char const* beginDoc, char const* endDoc, Value& root)
|
||||
bool
|
||||
Reader::readValue(unsigned depth)
|
||||
{
|
||||
Token token;
|
||||
Token token{};
|
||||
skipCommentTokens(token);
|
||||
if (depth > nest_limit)
|
||||
return addError("Syntax error: maximum nesting depth exceeded", token);
|
||||
@@ -395,7 +395,7 @@ Reader::readString()
|
||||
bool
|
||||
Reader::readObject(Token& tokenStart, unsigned depth)
|
||||
{
|
||||
Token tokenName;
|
||||
Token tokenName{};
|
||||
std::string name;
|
||||
currentValue() = Value(objectValue);
|
||||
|
||||
@@ -420,7 +420,7 @@ Reader::readObject(Token& tokenStart, unsigned depth)
|
||||
if (!decodeString(tokenName, name))
|
||||
return recoverFromError(tokenObjectEnd);
|
||||
|
||||
Token colon;
|
||||
Token colon{};
|
||||
|
||||
if (!readToken(colon) || colon.type_ != tokenMemberSeparator)
|
||||
{
|
||||
@@ -440,7 +440,7 @@ Reader::readObject(Token& tokenStart, unsigned depth)
|
||||
if (!ok) // error already set
|
||||
return recoverFromError(tokenObjectEnd);
|
||||
|
||||
Token comma;
|
||||
Token comma{};
|
||||
|
||||
if (!readToken(comma) ||
|
||||
(comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
|
||||
@@ -470,7 +470,7 @@ Reader::readArray(Token& tokenStart, unsigned depth)
|
||||
|
||||
if (*current_ == ']') // empty array
|
||||
{
|
||||
Token endArray;
|
||||
Token endArray{};
|
||||
readToken(endArray);
|
||||
return true;
|
||||
}
|
||||
@@ -487,7 +487,7 @@ Reader::readArray(Token& tokenStart, unsigned depth)
|
||||
if (!ok) // error already set
|
||||
return recoverFromError(tokenArrayEnd);
|
||||
|
||||
Token token;
|
||||
Token token{};
|
||||
// Accept Comment after last item in the array.
|
||||
ok = readToken(token);
|
||||
|
||||
@@ -591,7 +591,7 @@ Reader::decodeDouble(Token& token)
|
||||
{
|
||||
double value = 0;
|
||||
int const bufferSize = 32;
|
||||
int count;
|
||||
int count = 0;
|
||||
int length = int(token.end_ - token.start_);
|
||||
// Sanity check to avoid buffer overflow exploits.
|
||||
if (length < 0)
|
||||
@@ -689,7 +689,7 @@ Reader::decodeString(Token& token, std::string& decoded)
|
||||
break;
|
||||
|
||||
case 'u': {
|
||||
unsigned int unicode;
|
||||
unsigned int unicode = 0;
|
||||
|
||||
if (!decodeUnicodeCodePoint(token, current, end, unicode))
|
||||
return false;
|
||||
@@ -727,7 +727,7 @@ Reader::decodeUnicodeCodePoint(Token& token, Location& current, Location end, un
|
||||
token,
|
||||
current);
|
||||
|
||||
unsigned int surrogatePair;
|
||||
unsigned int surrogatePair = 0;
|
||||
|
||||
if (*current != '\\' || *(current + 1) != 'u')
|
||||
return addError(
|
||||
@@ -796,7 +796,7 @@ bool
|
||||
Reader::recoverFromError(TokenType skipUntilToken)
|
||||
{
|
||||
int errorCount = int(errors_.size());
|
||||
Token skip;
|
||||
Token skip{};
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -867,7 +867,7 @@ Reader::getLocationLineAndColumn(Location location, int& line, int& column) cons
|
||||
std::string
|
||||
Reader::getLocationLineAndColumn(Location location) const
|
||||
{
|
||||
int line, column;
|
||||
int line = 0, column = 0;
|
||||
getLocationLineAndColumn(location, line, column);
|
||||
return "Line " + std::to_string(line) + ", Column " + std::to_string(column);
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ Value::CZString::isStaticString() const
|
||||
* memset( this, 0, sizeof(Value) )
|
||||
* This optimization is used in ValueInternalMap fast allocator.
|
||||
*/
|
||||
Value::Value(ValueType type) : type_(type), allocated_(0)
|
||||
Value::Value(ValueType type) : type_(type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
@@ -225,7 +225,7 @@ Value::Value(std::string const& value) : type_(stringValue), allocated_(true)
|
||||
valueAllocator()->duplicateStringValue(value.c_str(), (unsigned int)value.length());
|
||||
}
|
||||
|
||||
Value::Value(StaticString const& value) : type_(stringValue), allocated_(false)
|
||||
Value::Value(StaticString const& value) : type_(stringValue)
|
||||
{
|
||||
value_.string_ = const_cast<char*>(value.c_str());
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ ApplyStateTable::apply(
|
||||
Mods newMod;
|
||||
for (auto& item : items_)
|
||||
{
|
||||
SField const* type;
|
||||
SField const* type = nullptr;
|
||||
switch (item.second.first)
|
||||
{
|
||||
default:
|
||||
@@ -513,7 +513,7 @@ void
|
||||
ApplyStateTable::threadItem(TxMeta& meta, std::shared_ptr<SLE> const& sle)
|
||||
{
|
||||
key_type prevTxID;
|
||||
LedgerIndex prevLgrID;
|
||||
LedgerIndex prevLgrID = 0;
|
||||
|
||||
if (!sle->thread(meta.getTxID(), meta.getLgrSeq(), prevTxID, prevLgrID))
|
||||
return;
|
||||
|
||||
@@ -197,7 +197,7 @@ validDomain(ReadView const& view, uint256 domainID, AccountID const& subject)
|
||||
}
|
||||
|
||||
TER
|
||||
authorizedDepositPreauth(ApplyView const& view, STVector256 const& credIDs, AccountID const& dst)
|
||||
authorizedDepositPreauth(ReadView const& view, STVector256 const& credIDs, AccountID const& dst)
|
||||
{
|
||||
std::set<std::pair<AccountID, Slice>> sorted;
|
||||
std::vector<std::shared_ptr<SLE const>> lifeExtender;
|
||||
@@ -318,7 +318,7 @@ verifyDepositPreauth(
|
||||
ApplyView& view,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
std::shared_ptr<SLE> const& sleDst,
|
||||
std::shared_ptr<SLE const> const& sleDst,
|
||||
beast::Journal j)
|
||||
{
|
||||
// If depositPreauth is enabled, then an account that requires
|
||||
|
||||
@@ -1321,7 +1321,7 @@ doWithdraw(
|
||||
}
|
||||
else
|
||||
{
|
||||
auto dstSle = view.peek(keylet::account(dstAcct));
|
||||
auto dstSle = view.read(keylet::account(dstAcct));
|
||||
if (auto err = verifyDepositPreauth(tx, view, senderAcct, dstAcct, dstSle, j))
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,12 @@ HTTPClient::initializeSSLContext(
|
||||
httpClientSSLContext.emplace(sslVerifyDir, sslVerifyFile, sslVerify, j);
|
||||
}
|
||||
|
||||
void
|
||||
HTTPClient::cleanupSSLContext()
|
||||
{
|
||||
httpClientSSLContext.reset();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Fetch a web page via http or https.
|
||||
@@ -472,7 +478,7 @@ public:
|
||||
private:
|
||||
using pointer = std::shared_ptr<HTTPClient>;
|
||||
|
||||
bool mSSL;
|
||||
bool mSSL{};
|
||||
AutoSocket mSocket;
|
||||
boost::asio::ip::tcp::resolver mResolver;
|
||||
|
||||
@@ -490,7 +496,7 @@ private:
|
||||
std::string mBody;
|
||||
unsigned short const mPort;
|
||||
std::size_t const maxResponseSize_;
|
||||
int mStatus;
|
||||
int mStatus{};
|
||||
std::function<void(boost::asio::streambuf& sb, std::string const& strHost)> mBuild;
|
||||
std::function<
|
||||
bool(boost::system::error_code const& ecResult, int iStatus, std::string const& strData)>
|
||||
@@ -502,7 +508,7 @@ private:
|
||||
boost::system::error_code mShutdown;
|
||||
|
||||
std::deque<std::string> mDeqSites;
|
||||
std::chrono::seconds mTimeout;
|
||||
std::chrono::seconds mTimeout{};
|
||||
beast::Journal j_;
|
||||
};
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ registerSSLCerts(boost::asio::ssl::context& ctx, boost::system::error_code& ec,
|
||||
SSL_CTX_set_cert_store(ctx.native_handle(), store.release());
|
||||
|
||||
#else
|
||||
// NOLINTNEXTLINE(bugprone-unused-return-value)
|
||||
ctx.set_default_verify_paths(ec);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ BatchWriter::writeBatch()
|
||||
}
|
||||
}
|
||||
|
||||
BatchWriteReport report;
|
||||
BatchWriteReport report{};
|
||||
report.writeCount = set.size();
|
||||
auto const before = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ DatabaseNodeImp::fetchNodeObject(
|
||||
bool duplicate)
|
||||
{
|
||||
std::shared_ptr<NodeObject> nodeObject = nullptr;
|
||||
Status status;
|
||||
Status status = ok;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -101,7 +101,7 @@ DatabaseRotatingImp::fetchNodeObject(
|
||||
bool duplicate)
|
||||
{
|
||||
auto fetch = [&](std::shared_ptr<Backend> const& backend) {
|
||||
Status status;
|
||||
Status status = ok;
|
||||
std::shared_ptr<NodeObject> nodeObject;
|
||||
try
|
||||
{
|
||||
|
||||
18
src/libxrpl/nodestore/backend/Backend.cpp
Normal file
18
src/libxrpl/nodestore/backend/Backend.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include <xrpl/nodestore/Backend.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
namespace xrpl {
|
||||
namespace NodeStore {
|
||||
|
||||
// Initialize the static constant for hardware thread count. The `hardware_concurrency` function can
|
||||
// return 0 on some platforms, in which case we default to 1. We limit the total number of threads
|
||||
// to 8 to avoid contention.
|
||||
unsigned int const Backend::numHardwareThreads = []() {
|
||||
auto const hw = std::thread::hardware_concurrency();
|
||||
return std::min(std::max(hw, 1u), 8u);
|
||||
}();
|
||||
|
||||
} // namespace NodeStore
|
||||
} // namespace xrpl
|
||||
@@ -7,15 +7,21 @@
|
||||
#include <xrpl/nodestore/detail/EncodedBlob.h>
|
||||
#include <xrpl/nodestore/detail/codec.h>
|
||||
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/thread_pool.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <nudb/nudb.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <exception>
|
||||
#include <latch>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
namespace NodeStore {
|
||||
@@ -37,6 +43,7 @@ public:
|
||||
nudb::store db_;
|
||||
std::atomic<bool> deletePath_;
|
||||
Scheduler& scheduler_;
|
||||
boost::asio::thread_pool threadPool_;
|
||||
|
||||
NuDBBackend(
|
||||
size_t keyBytes,
|
||||
@@ -51,6 +58,7 @@ public:
|
||||
, blockSize_(parseBlockSize(name_, keyValues, journal))
|
||||
, deletePath_(false)
|
||||
, scheduler_(scheduler)
|
||||
, threadPool_(numHardwareThreads)
|
||||
{
|
||||
if (name_.empty())
|
||||
Throw<std::runtime_error>("nodestore: Missing path in NuDB backend");
|
||||
@@ -71,6 +79,7 @@ public:
|
||||
, db_(context)
|
||||
, deletePath_(false)
|
||||
, scheduler_(scheduler)
|
||||
, threadPool_(numHardwareThreads)
|
||||
{
|
||||
if (name_.empty())
|
||||
Throw<std::runtime_error>("nodestore: Missing path in NuDB backend");
|
||||
@@ -181,9 +190,10 @@ public:
|
||||
Status
|
||||
fetch(uint256 const& hash, std::shared_ptr<NodeObject>* pno) override
|
||||
{
|
||||
Status status;
|
||||
Status status = ok;
|
||||
pno->reset();
|
||||
nudb::error_code ec;
|
||||
|
||||
db_.fetch(
|
||||
hash.data(),
|
||||
[&hash, pno, &status](void const* data, std::size_t size) {
|
||||
@@ -199,6 +209,7 @@ public:
|
||||
status = ok;
|
||||
},
|
||||
ec);
|
||||
|
||||
if (ec == nudb::error::key_not_found)
|
||||
return notFound;
|
||||
if (ec)
|
||||
@@ -209,18 +220,62 @@ public:
|
||||
std::pair<std::vector<std::shared_ptr<NodeObject>>, Status>
|
||||
fetchBatch(std::vector<uint256> const& hashes) override
|
||||
{
|
||||
std::vector<std::shared_ptr<NodeObject>> results;
|
||||
results.reserve(hashes.size());
|
||||
for (auto const& h : hashes)
|
||||
std::vector<std::shared_ptr<NodeObject>> results(hashes.size());
|
||||
|
||||
// Determine the number of threads to use for data compression from the number of available
|
||||
// cores and the size of the batch. We would like each thread to at least process 4 items,
|
||||
// except for the last thread that might process fewer items.
|
||||
auto const numThreads = std::min(
|
||||
std::max(static_cast<unsigned int>(hashes.size()) / 4u, 1u), numHardwareThreads);
|
||||
|
||||
// If we need only one thread, just do it sequentially.
|
||||
if (numThreads == 1u)
|
||||
{
|
||||
std::shared_ptr<NodeObject> nObj;
|
||||
Status status = fetch(h, &nObj);
|
||||
if (status != ok)
|
||||
results.push_back({});
|
||||
else
|
||||
results.push_back(nObj);
|
||||
for (size_t i = 0; i < hashes.size(); ++i)
|
||||
{
|
||||
std::shared_ptr<NodeObject> nObj;
|
||||
if (fetch(hashes[i], &nObj) == ok)
|
||||
results[i] = nObj;
|
||||
}
|
||||
return {results, ok};
|
||||
}
|
||||
|
||||
// Use a latch to synchronize task completion.
|
||||
std::latch taskCompletion(numThreads);
|
||||
|
||||
// Submit fetch tasks to the thread pool.
|
||||
auto const itemsPerThread = (hashes.size() + numThreads - 1) / numThreads;
|
||||
for (unsigned int t = 0; t < numThreads; ++t)
|
||||
{
|
||||
auto const startIdx = t * itemsPerThread;
|
||||
XRPL_ASSERT(
|
||||
startIdx < hashes.size(),
|
||||
"xrpl::NuDBFactory::fetchBatch : startIdx < hashes.size()");
|
||||
if (startIdx >= hashes.size())
|
||||
{
|
||||
taskCompletion.count_down();
|
||||
continue;
|
||||
}
|
||||
auto const endIdx = std::min(startIdx + itemsPerThread, hashes.size());
|
||||
|
||||
auto task = [this, &hashes, &results, &taskCompletion, startIdx, endIdx]() {
|
||||
// Fetch the items assigned to this task.
|
||||
for (size_t i = startIdx; i < endIdx; ++i)
|
||||
{
|
||||
std::shared_ptr<NodeObject> nObj;
|
||||
if (fetch(hashes[i], &nObj) == ok)
|
||||
results[i] = nObj;
|
||||
}
|
||||
// Signal task completion.
|
||||
taskCompletion.count_down();
|
||||
};
|
||||
|
||||
boost::asio::post(threadPool_, std::move(task));
|
||||
}
|
||||
|
||||
// Wait for all fetch tasks to complete.
|
||||
taskCompletion.wait();
|
||||
|
||||
return {results, ok};
|
||||
}
|
||||
|
||||
@@ -228,9 +283,11 @@ public:
|
||||
do_insert(std::shared_ptr<NodeObject> const& no)
|
||||
{
|
||||
EncodedBlob e(no);
|
||||
nudb::error_code ec;
|
||||
|
||||
nudb::detail::buffer bf;
|
||||
auto const result = nodeobject_compress(e.getData(), e.getSize(), bf);
|
||||
|
||||
nudb::error_code ec;
|
||||
db_.insert(e.getKey(), result.first, result.second, ec);
|
||||
if (ec && ec != nudb::error::key_exists)
|
||||
Throw<nudb::system_error>(ec);
|
||||
@@ -239,10 +296,14 @@ public:
|
||||
void
|
||||
store(std::shared_ptr<NodeObject> const& no) override
|
||||
{
|
||||
BatchWriteReport report;
|
||||
BatchWriteReport report{};
|
||||
report.writeCount = 1;
|
||||
auto const start = std::chrono::steady_clock::now();
|
||||
|
||||
++pendingWrites_;
|
||||
do_insert(no);
|
||||
--pendingWrites_;
|
||||
|
||||
report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - start);
|
||||
scheduler_.onBatchWrite(report);
|
||||
@@ -251,11 +312,112 @@ public:
|
||||
void
|
||||
storeBatch(Batch const& batch) override
|
||||
{
|
||||
BatchWriteReport report;
|
||||
BatchWriteReport report{};
|
||||
report.writeCount = batch.size();
|
||||
auto const start = std::chrono::steady_clock::now();
|
||||
for (auto const& e : batch)
|
||||
do_insert(e);
|
||||
|
||||
pendingWrites_ += static_cast<int>(batch.size());
|
||||
|
||||
// Determine the number of threads to use for data compression from the number of available
|
||||
// cores and the size of the batch. We would like each thread to at least process 4 items,
|
||||
// except for the last thread that might process fewer items.
|
||||
auto const numThreads = std::min(
|
||||
std::max(static_cast<unsigned int>(batch.size()) / 4u, 1u), numHardwareThreads);
|
||||
|
||||
// If we need only one thread, just do it sequentially.
|
||||
if (numThreads == 1u)
|
||||
{
|
||||
for (auto const& e : batch)
|
||||
do_insert(e);
|
||||
pendingWrites_ -= static_cast<int>(batch.size());
|
||||
|
||||
report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - start);
|
||||
scheduler_.onBatchWrite(report);
|
||||
return;
|
||||
}
|
||||
|
||||
// Helper struct that stores actual item data, not pointers, to avoid dangling references
|
||||
// after EncodedBlob and buffer go out of scope in the thread.
|
||||
struct CompressedData
|
||||
{
|
||||
std::vector<std::uint8_t> key;
|
||||
std::vector<std::uint8_t> data;
|
||||
std::exception_ptr eptr;
|
||||
};
|
||||
std::vector<CompressedData> compressed(batch.size());
|
||||
|
||||
// Use a latch to synchronize task completion.
|
||||
std::latch taskCompletion(numThreads);
|
||||
|
||||
// Submit compression tasks to the thread pool.
|
||||
auto const itemsPerThread = (batch.size() + numThreads - 1) / numThreads;
|
||||
for (unsigned int t = 0; t < numThreads; ++t)
|
||||
{
|
||||
auto const startIdx = t * itemsPerThread;
|
||||
XRPL_ASSERT(
|
||||
startIdx < batch.size(), "xrpl::NuDBFactory::storeBatch : startIdx < batch.size()");
|
||||
if (startIdx >= batch.size())
|
||||
{
|
||||
taskCompletion.count_down();
|
||||
continue;
|
||||
}
|
||||
auto const endIdx = std::min(startIdx + itemsPerThread, batch.size());
|
||||
|
||||
auto task =
|
||||
[&batch, &compressed, &taskCompletion, startIdx, endIdx, keyBytes = keyBytes_]() {
|
||||
// Compress the items assigned to this task.
|
||||
for (size_t i = startIdx; i < endIdx; ++i)
|
||||
{
|
||||
auto& item = compressed[i];
|
||||
try
|
||||
{
|
||||
EncodedBlob e(batch[i]);
|
||||
|
||||
// Copy the key data to avoid dangling pointer.
|
||||
auto const* keyPtr = static_cast<std::uint8_t const*>(e.getKey());
|
||||
item.key.assign(keyPtr, keyPtr + keyBytes);
|
||||
|
||||
// Compress and copy the data to avoid dangling pointer.
|
||||
nudb::detail::buffer bf;
|
||||
auto const comp = nodeobject_compress(e.getData(), e.getSize(), bf);
|
||||
auto const* dataPtr = static_cast<std::uint8_t const*>(comp.first);
|
||||
item.data.assign(dataPtr, dataPtr + comp.second);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Store the exception so it can be rethrown in the sequential phase
|
||||
// below.
|
||||
item.eptr = std::current_exception();
|
||||
}
|
||||
}
|
||||
// Signal task completion.
|
||||
taskCompletion.count_down();
|
||||
};
|
||||
|
||||
boost::asio::post(threadPool_, std::move(task));
|
||||
}
|
||||
|
||||
// Wait for all compression tasks to complete.
|
||||
taskCompletion.wait();
|
||||
|
||||
// Insert the compressed data sequentially, since NuDB is designed as an append-only data
|
||||
// store that only supports one writer.
|
||||
for (auto const& item : compressed)
|
||||
{
|
||||
if (item.eptr)
|
||||
{
|
||||
std::rethrow_exception(item.eptr);
|
||||
}
|
||||
|
||||
nudb::error_code ec;
|
||||
db_.insert(item.key.data(), item.data.data(), item.data.size(), ec);
|
||||
if (ec && ec != nudb::error::key_exists)
|
||||
Throw<nudb::system_error>(ec);
|
||||
}
|
||||
|
||||
pendingWrites_ -= static_cast<int>(batch.size());
|
||||
|
||||
report.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - start);
|
||||
scheduler_.onBatchWrite(report);
|
||||
@@ -272,7 +434,7 @@ public:
|
||||
auto const dp = db_.dat_path();
|
||||
auto const kp = db_.key_path();
|
||||
auto const lp = db_.log_path();
|
||||
// auto const appnum = db_.appnum();
|
||||
|
||||
nudb::error_code ec;
|
||||
db_.close(ec);
|
||||
if (ec)
|
||||
@@ -306,7 +468,7 @@ public:
|
||||
int
|
||||
getWriteLoad() override
|
||||
{
|
||||
return 0;
|
||||
return pendingWrites_.load();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -341,6 +503,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<int> pendingWrites_{0};
|
||||
|
||||
static std::size_t
|
||||
parseBlockSize(std::string const& name, Section const& keyValues, beast::Journal journal)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
namespace xrpl {
|
||||
namespace NodeStore {
|
||||
@@ -181,6 +182,41 @@ public:
|
||||
std::string("Unable to set RocksDB options: ") + s.ToString());
|
||||
}
|
||||
|
||||
// Enable pipelined writes for better write concurrency.
|
||||
m_options.enable_pipelined_write = true;
|
||||
|
||||
// Set background job parallelism for better compaction/flush performance to the number of
|
||||
// hardware threads, unless the value is explicitly provided in the config. The default is
|
||||
// 2 (see include/rocksdb/options.h in the Conan dependency directory), so don't use fewer
|
||||
// than that.
|
||||
if (auto v = get<unsigned int>(keyValues, "max_background_jobs", 0); v > 2)
|
||||
{
|
||||
m_options.max_background_jobs = v;
|
||||
}
|
||||
else if (v = numHardwareThreads; v > 2)
|
||||
{
|
||||
m_options.max_background_jobs = v;
|
||||
}
|
||||
|
||||
// Set subcompactions for parallel compaction within a job to the number of hardware
|
||||
// threads, unless the value is explicitly provided in the config. The default is 1 (see
|
||||
// include/rocksdb/options.h in the Conan dependency directory), so don't use fewer
|
||||
// than that if no value is explicitly provided.
|
||||
if (auto v = get<unsigned int>(keyValues, "max_subcompactions", 0); v > 1)
|
||||
{
|
||||
m_options.max_subcompactions = v;
|
||||
}
|
||||
else if (v = numHardwareThreads / 2; v > 1)
|
||||
{
|
||||
m_options.max_subcompactions = v;
|
||||
}
|
||||
|
||||
// Enable direct I/O by default unless explicitly disabled in the config. This bypasses the
|
||||
// OS page cache for better predictable performance on SSDs.
|
||||
m_options.use_direct_reads = get<bool>(keyValues, "use_direct_io", true);
|
||||
m_options.use_direct_io_for_flush_and_compaction =
|
||||
get<bool>(keyValues, "use_direct_io", true);
|
||||
|
||||
std::string s1, s2;
|
||||
rocksdb::GetStringFromDBOptions(&s1, m_options, "; ");
|
||||
rocksdb::GetStringFromColumnFamilyOptions(&s2, m_options, "; ");
|
||||
@@ -253,23 +289,19 @@ public:
|
||||
|
||||
rocksdb::ReadOptions const options;
|
||||
rocksdb::Slice const slice(std::bit_cast<char const*>(hash.data()), m_keyBytes);
|
||||
|
||||
std::string string;
|
||||
|
||||
rocksdb::Status getStatus = m_db->Get(options, slice, &string);
|
||||
|
||||
if (getStatus.ok())
|
||||
{
|
||||
DecodedBlob decoded(hash.data(), string.data(), string.size());
|
||||
|
||||
if (decoded.wasOk())
|
||||
{
|
||||
*pObject = decoded.createObject();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Decoding failed, probably corrupted!
|
||||
//
|
||||
// Decoding failed, probably corrupted.
|
||||
status = dataCorrupt;
|
||||
}
|
||||
}
|
||||
@@ -286,7 +318,6 @@ public:
|
||||
else
|
||||
{
|
||||
status = Status(customCode + unsafe_cast<int>(getStatus.code()));
|
||||
|
||||
JLOG(m_journal.error()) << getStatus.ToString();
|
||||
}
|
||||
}
|
||||
@@ -297,16 +328,43 @@ public:
|
||||
std::pair<std::vector<std::shared_ptr<NodeObject>>, Status>
|
||||
fetchBatch(std::vector<uint256> const& hashes) override
|
||||
{
|
||||
std::vector<std::shared_ptr<NodeObject>> results;
|
||||
results.reserve(hashes.size());
|
||||
XRPL_ASSERT(m_db, "xrpl::NodeStore::RocksDBBackend::fetchBatch : non-null database");
|
||||
|
||||
if (hashes.empty())
|
||||
return {{}, ok};
|
||||
|
||||
// Use MultiGet for parallel reads to allow RocksDB to fetch multiple keys concurrently,
|
||||
// significantly improving throughput compared to sequential fetch() calls.
|
||||
|
||||
std::vector<rocksdb::Slice> keys;
|
||||
keys.reserve(hashes.size());
|
||||
for (auto const& h : hashes)
|
||||
{
|
||||
std::shared_ptr<NodeObject> nObj;
|
||||
Status status = fetch(h, &nObj);
|
||||
if (status != ok)
|
||||
results.push_back({});
|
||||
else
|
||||
results.push_back(nObj);
|
||||
keys.emplace_back(std::bit_cast<char const*>(h.data()), m_keyBytes);
|
||||
}
|
||||
|
||||
rocksdb::ReadOptions options;
|
||||
options.async_io = true; // Enable for better concurrency on supported platforms.
|
||||
std::vector<std::string> values(hashes.size());
|
||||
auto statuses = m_db->MultiGet(options, keys, &values);
|
||||
|
||||
std::vector<std::shared_ptr<NodeObject>> results(hashes.size());
|
||||
for (auto i = 0; i < hashes.size(); ++i)
|
||||
{
|
||||
if (statuses[i].ok())
|
||||
{
|
||||
DecodedBlob decoded(hashes[i].data(), values[i].data(), values[i].size());
|
||||
if (decoded.wasOk())
|
||||
{
|
||||
results[i] = decoded.createObject();
|
||||
}
|
||||
}
|
||||
else if (!statuses[i].IsNotFound())
|
||||
{
|
||||
// Log other errors but continue processing.
|
||||
JLOG(m_journal.warn()) << "fetchBatch: MultiGet error for key "
|
||||
<< keys[i].ToString() << ": " << statuses[i].ToString();
|
||||
}
|
||||
}
|
||||
|
||||
return {results, ok};
|
||||
@@ -321,10 +379,7 @@ public:
|
||||
void
|
||||
storeBatch(Batch const& batch) override
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
m_db,
|
||||
"xrpl::NodeStore::RocksDBBackend::storeBatch : non-null "
|
||||
"database");
|
||||
XRPL_ASSERT(m_db, "xrpl::NodeStore::RocksDBBackend::storeBatch : non-null database");
|
||||
rocksdb::WriteBatch wb;
|
||||
|
||||
for (auto const& e : batch)
|
||||
@@ -336,7 +391,27 @@ public:
|
||||
rocksdb::Slice(std::bit_cast<char const*>(encoded.getData()), encoded.getSize()));
|
||||
}
|
||||
|
||||
rocksdb::WriteOptions const options;
|
||||
// Configure WriteOptions for high throughput.
|
||||
// Note: no_slowdown is intentionally NOT set here. When set to true, RocksDB returns an
|
||||
// error instead of stalling when write buffers are full, which could cause write
|
||||
// failures during high load. We prefer to accept brief stalls over dropped writes.
|
||||
rocksdb::WriteOptions options;
|
||||
|
||||
// Setting `sync = false` improves write throughput significantly by allowing the OS to
|
||||
// batch fsync operations, rather than forcing immediate disk synchronization on every
|
||||
// write. The Write-Ahead Log (WAL) is still written and flushed, so database consistency is
|
||||
// maintained across clean restarts and crashes.
|
||||
//
|
||||
// Note: On hard shutdown up to a few seconds of recent writes (since the last OS-initiated
|
||||
// flush) may be lost from this node. However, since ledger data is replicated across
|
||||
// the network, lost writes can be re-synced from peers during startup.
|
||||
options.sync = false;
|
||||
|
||||
// Keep WAL enabled for crash recovery consistency.
|
||||
options.disableWAL = false;
|
||||
|
||||
// Ensure RocksDB will not aggressive throttle the writes.
|
||||
options.low_pri = false;
|
||||
|
||||
auto ret = m_db->Write(options, &wb);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/beast/core/SemanticVersion.h>
|
||||
#include <xrpl/git/Git.h>
|
||||
#include <xrpl/protocol/BuildInfo.h>
|
||||
#include <xrpl/protocol/SystemParameters.h>
|
||||
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
|
||||
@@ -80,7 +81,7 @@ getVersionString()
|
||||
std::string const&
|
||||
getFullVersionString()
|
||||
{
|
||||
static std::string const value = "rippled-" + getVersionString();
|
||||
static std::string const value = systemName() + "-" + getVersionString();
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -395,10 +395,20 @@ featureToName(uint256 const& f)
|
||||
#pragma push_macro("XRPL_RETIRE_FIX")
|
||||
#undef XRPL_RETIRE_FIX
|
||||
|
||||
consteval auto
|
||||
enforceValidFeatureName(auto fn) -> char const*
|
||||
{
|
||||
static_assert(validFeatureName(fn), "Invalid feature name");
|
||||
static_assert(validFeatureNameSize(fn), "Invalid feature name size");
|
||||
return fn();
|
||||
}
|
||||
|
||||
#define XRPL_FEATURE(name, supported, vote) \
|
||||
uint256 const feature##name = registerFeature(#name, supported, vote);
|
||||
uint256 const feature##name = \
|
||||
registerFeature(enforceValidFeatureName([] { return #name; }), supported, vote);
|
||||
#define XRPL_FIX(name, supported, vote) \
|
||||
uint256 const fix##name = registerFeature("fix" #name, supported, vote);
|
||||
uint256 const fix##name = \
|
||||
registerFeature(enforceValidFeatureName([] { return "fix" #name; }), supported, vote);
|
||||
|
||||
// clang-format off
|
||||
#define XRPL_RETIRE_FEATURE(name) \
|
||||
|
||||
@@ -24,7 +24,7 @@ STAccount::STAccount(SField const& n) : STBase(n), value_(beast::zero), default_
|
||||
{
|
||||
}
|
||||
|
||||
STAccount::STAccount(SField const& n, Buffer&& v) : STAccount(n)
|
||||
STAccount::STAccount(SField const& n, Buffer const& v) : STAccount(n)
|
||||
{
|
||||
if (v.empty())
|
||||
return; // Zero is a valid size for a defaulted STAccount.
|
||||
|
||||
@@ -181,7 +181,7 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name)
|
||||
}
|
||||
|
||||
STAmount::STAmount(SField const& name, std::int64_t mantissa)
|
||||
: STBase(name), mAsset(xrpIssue()), mOffset(0)
|
||||
: STBase(name), mAsset(xrpIssue()), mValue(0), mOffset(0), mIsNegative(false)
|
||||
{
|
||||
set(mantissa);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ STArray::STArray(SerialIter& sit, SField const& f, int depth) : STBase(f)
|
||||
{
|
||||
while (!sit.empty())
|
||||
{
|
||||
int type, field;
|
||||
int type = 0, field = 0;
|
||||
sit.getFieldID(type, field);
|
||||
|
||||
if ((type == STI_ARRAY) && (field == 1))
|
||||
|
||||
@@ -100,7 +100,7 @@ STIssue::add(Serializer& s) const
|
||||
auto const& issue = asset_.get<MPTIssue>();
|
||||
s.addBitString(issue.getIssuer());
|
||||
s.addBitString(noAccount());
|
||||
std::uint32_t sequence;
|
||||
std::uint32_t sequence = 0;
|
||||
memcpy(&sequence, issue.getMptID().data(), sizeof(sequence));
|
||||
s.add32(sequence);
|
||||
}
|
||||
|
||||
@@ -43,14 +43,14 @@ STLedgerEntry::STLedgerEntry(Keylet const& k) : STObject(sfLedgerEntry), key_(k.
|
||||
}
|
||||
|
||||
STLedgerEntry::STLedgerEntry(SerialIter& sit, uint256 const& index)
|
||||
: STObject(sfLedgerEntry), key_(index)
|
||||
: STObject(sfLedgerEntry), key_(index), type_(ltANY)
|
||||
{
|
||||
set(sit);
|
||||
setSLEType();
|
||||
}
|
||||
|
||||
STLedgerEntry::STLedgerEntry(STObject const& object, uint256 const& index)
|
||||
: STObject(object), key_(index)
|
||||
: STObject(object), key_(index), type_(ltANY)
|
||||
{
|
||||
setSLEType();
|
||||
}
|
||||
|
||||
@@ -179,8 +179,8 @@ partsFromString(std::string const& number)
|
||||
|
||||
bool negative = (match[1].matched && (match[1] == "-"));
|
||||
|
||||
std::uint64_t mantissa;
|
||||
int exponent;
|
||||
std::uint64_t mantissa = 0;
|
||||
int exponent = 0;
|
||||
|
||||
if (!match[4].matched) // integer only
|
||||
{
|
||||
|
||||
@@ -46,7 +46,7 @@ STObject::STObject(STObject&& other)
|
||||
{
|
||||
}
|
||||
|
||||
STObject::STObject(SField const& name) : STBase(name), mType(nullptr)
|
||||
STObject::STObject(SField const& name) : STBase(name)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -62,8 +62,7 @@ STObject::STObject(SOTemplate const& type, SerialIter& sit, SField const& name)
|
||||
applyTemplate(type); // May throw
|
||||
}
|
||||
|
||||
STObject::STObject(SerialIter& sit, SField const& name, int depth) noexcept(false)
|
||||
: STBase(name), mType(nullptr)
|
||||
STObject::STObject(SerialIter& sit, SField const& name, int depth) noexcept(false) : STBase(name)
|
||||
{
|
||||
if (depth > 10)
|
||||
Throw<std::runtime_error>("Maximum nesting depth of STObject exceeded");
|
||||
@@ -216,8 +215,8 @@ STObject::set(SerialIter& sit, int depth)
|
||||
// Consume data in the pipe until we run out or reach the end
|
||||
while (!sit.empty())
|
||||
{
|
||||
int type;
|
||||
int field;
|
||||
int type = 0;
|
||||
int field = 0;
|
||||
|
||||
// Get the metadata for the next field
|
||||
sit.getFieldID(type, field);
|
||||
|
||||
@@ -449,7 +449,7 @@ parseLeaf(
|
||||
{
|
||||
auto const str = value.asString();
|
||||
|
||||
std::uint64_t val;
|
||||
std::uint64_t val = 0;
|
||||
|
||||
bool const useBase10 = field.shouldMeta(SField::sMD_BaseTen);
|
||||
|
||||
|
||||
@@ -211,6 +211,20 @@ STTx::getSeqValue() const
|
||||
return getSeqProxy().value();
|
||||
}
|
||||
|
||||
AccountID
|
||||
STTx::getFeePayer() const
|
||||
{
|
||||
// If sfDelegate is present, the delegate account is the payer
|
||||
// note: if a delegate is specified, its authorization to act on behalf of the account is
|
||||
// enforced in `Transactor::checkPermission`
|
||||
// cryptographic signature validity is checked separately (e.g., in `Transactor::checkSign`)
|
||||
if (isFieldPresent(sfDelegate))
|
||||
return getAccountID(sfDelegate);
|
||||
|
||||
// Default payer
|
||||
return getAccountID(sfAccount);
|
||||
}
|
||||
|
||||
void
|
||||
STTx::sign(
|
||||
PublicKey const& publicKey,
|
||||
@@ -351,7 +365,7 @@ STTx::getMetaSQL(std::uint32_t inLedger, std::string const& escapedMetaData) con
|
||||
{
|
||||
Serializer s;
|
||||
add(s);
|
||||
return getMetaSQL(s, inLedger, txnSqlValidated, escapedMetaData);
|
||||
return getMetaSQL(s, inLedger, TxnSql::txnSqlValidated, escapedMetaData);
|
||||
}
|
||||
|
||||
// VFALCO This could be a free function elsewhere
|
||||
@@ -359,7 +373,7 @@ std::string
|
||||
STTx::getMetaSQL(
|
||||
Serializer rawTxn,
|
||||
std::uint32_t inLedger,
|
||||
char status,
|
||||
TxnSql status,
|
||||
std::string const& escapedMetaData) const
|
||||
{
|
||||
static boost::format bfTrans("('%s', '%s', '%s', '%d', '%d', '%c', %s, %s)");
|
||||
@@ -370,8 +384,8 @@ STTx::getMetaSQL(
|
||||
|
||||
return str(
|
||||
boost::format(bfTrans) % to_string(getTransactionID()) % format->getName() %
|
||||
toBase58(getAccountID(sfAccount)) % getFieldU32(sfSequence) % inLedger % status % rTxn %
|
||||
escapedMetaData);
|
||||
toBase58(getAccountID(sfAccount)) % getFieldU32(sfSequence) % inLedger %
|
||||
safe_cast<char>(status) % rTxn % escapedMetaData);
|
||||
}
|
||||
|
||||
static Expected<void, std::string>
|
||||
@@ -694,9 +708,9 @@ invalidMPTAmountInTx(STObject const& tx)
|
||||
{
|
||||
if (auto const& field = tx.peekAtField(e.sField());
|
||||
(field.getSType() == STI_AMOUNT &&
|
||||
static_cast<STAmount const&>(field).holds<MPTIssue>()) ||
|
||||
safe_downcast<STAmount const&>(field).holds<MPTIssue>()) ||
|
||||
(field.getSType() == STI_ISSUE &&
|
||||
static_cast<STIssue const&>(field).holds<MPTIssue>()))
|
||||
safe_downcast<STIssue const&>(field).holds<MPTIssue>()))
|
||||
{
|
||||
if (e.supportMPT() != soeMPTSupported)
|
||||
return true;
|
||||
|
||||
@@ -74,7 +74,7 @@ deriveDeterministicRootKey(Seed const& seed)
|
||||
// buf |----------------|----|
|
||||
// | seed | seq|
|
||||
|
||||
std::array<std::uint8_t, 20> buf;
|
||||
std::array<std::uint8_t, 20> buf{};
|
||||
std::copy(seed.begin(), seed.end(), buf.begin());
|
||||
|
||||
// The odds that this loop executes more than once are negligible
|
||||
@@ -119,7 +119,7 @@ class Generator
|
||||
{
|
||||
private:
|
||||
uint256 root_;
|
||||
std::array<std::uint8_t, 33> generator_;
|
||||
std::array<std::uint8_t, 33> generator_{};
|
||||
|
||||
uint256
|
||||
calculateTweak(std::uint32_t seq) const
|
||||
@@ -133,7 +133,7 @@ private:
|
||||
// buf |---------------------------------|----|----|
|
||||
// | generator | seq| cnt|
|
||||
|
||||
std::array<std::uint8_t, 41> buf;
|
||||
std::array<std::uint8_t, 41> buf{};
|
||||
std::copy(generator_.begin(), generator_.end(), buf.begin());
|
||||
copy_uint32(buf.data() + 33, seq);
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ Seed::Seed(uint128 const& seed)
|
||||
Seed
|
||||
randomSeed()
|
||||
{
|
||||
std::array<std::uint8_t, 16> buffer;
|
||||
std::array<std::uint8_t, 16> buffer{};
|
||||
beast::rngfill(buffer.data(), buffer.size(), crypto_prng());
|
||||
Seed seed(makeSlice(buffer));
|
||||
secure_erase(buffer.data(), buffer.size());
|
||||
|
||||
@@ -206,7 +206,7 @@ Serializer::addVL(void const* ptr, int len)
|
||||
int
|
||||
Serializer::addEncoded(int length)
|
||||
{
|
||||
std::array<std::uint8_t, 4> bytes;
|
||||
std::array<std::uint8_t, 4> bytes{};
|
||||
int numBytes = 0;
|
||||
|
||||
if (length <= 192)
|
||||
@@ -466,7 +466,7 @@ int
|
||||
SerialIter::getVLDataLength()
|
||||
{
|
||||
int b1 = get8();
|
||||
int datLen;
|
||||
int datLen = 0;
|
||||
int lenLen = Serializer::decodeLengthLength(b1);
|
||||
if (lenLen == 1)
|
||||
{
|
||||
|
||||
@@ -329,7 +329,7 @@ decodeBase58Token(std::string const& s, TokenType type)
|
||||
return {};
|
||||
|
||||
// And the checksum must as well.
|
||||
std::array<char, 4> guard;
|
||||
std::array<char, 4> guard{};
|
||||
checksum(guard.data(), ret.data(), ret.size() - guard.size());
|
||||
if (!std::equal(guard.rbegin(), guard.rend(), ret.rbegin()))
|
||||
return {};
|
||||
@@ -610,7 +610,7 @@ encodeBase58Token(
|
||||
std::span<std::uint8_t> out)
|
||||
{
|
||||
constexpr std::size_t tmpBufSize = 128;
|
||||
std::array<std::uint8_t, tmpBufSize> buf;
|
||||
std::array<std::uint8_t, tmpBufSize> buf{};
|
||||
if (input.size() > tmpBufSize - 5)
|
||||
{
|
||||
return Unexpected(TokenCodecErrc::inputTooLarge);
|
||||
@@ -637,7 +637,7 @@ encodeBase58Token(
|
||||
B58Result<std::span<std::uint8_t>>
|
||||
decodeBase58Token(TokenType type, std::string_view s, std::span<std::uint8_t> outBuf)
|
||||
{
|
||||
std::array<std::uint8_t, 64> tmpBuf;
|
||||
std::array<std::uint8_t, 64> tmpBuf{};
|
||||
auto const decodeResult = detail::b58_to_b256_be(s, std::span(tmpBuf.data(), tmpBuf.size()));
|
||||
|
||||
if (!decodeResult)
|
||||
@@ -654,7 +654,7 @@ decodeBase58Token(TokenType type, std::string_view s, std::span<std::uint8_t> ou
|
||||
return Unexpected(TokenCodecErrc::mismatchedTokenType);
|
||||
|
||||
// And the checksum must as well.
|
||||
std::array<std::uint8_t, 4> guard;
|
||||
std::array<std::uint8_t, 4> guard{};
|
||||
checksum(guard.data(), ret.data(), ret.size() - guard.size());
|
||||
if (!std::equal(guard.rbegin(), guard.rend(), ret.rbegin()))
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ getHTTPHeaderTimestamp()
|
||||
// sense. There's no point in doing all this work if this function
|
||||
// gets called multiple times a second.
|
||||
char buffer[96];
|
||||
time_t now;
|
||||
time_t now = 0;
|
||||
time(&now);
|
||||
struct tm now_gmt{};
|
||||
#ifndef _MSC_VER
|
||||
@@ -68,6 +68,7 @@ HTTPReply(int nStatus, std::string const& content, Json::Output const& output, b
|
||||
return;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-switch-missing-default-case)
|
||||
switch (nStatus)
|
||||
{
|
||||
case 200:
|
||||
|
||||
@@ -98,7 +98,7 @@ populate(
|
||||
while (std::getline(ss, ip, ','))
|
||||
{
|
||||
boost::algorithm::trim(ip);
|
||||
bool v4;
|
||||
bool v4 = false;
|
||||
boost::asio::ip::network_v4 v4Net;
|
||||
boost::asio::ip::network_v6 v6Net;
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ initStateDB(soci::session& session, BasicConfig const& config, std::string const
|
||||
LedgerIndex
|
||||
getCanDelete(soci::session& session)
|
||||
{
|
||||
LedgerIndex seq;
|
||||
LedgerIndex seq = 0;
|
||||
session << "SELECT CanDeleteSeq FROM CanDelete WHERE Key = 1;", soci::into(seq);
|
||||
;
|
||||
return seq;
|
||||
|
||||
@@ -25,7 +25,7 @@ doVacuumDB(DatabaseCon::Setup const& setup, beast::Journal j)
|
||||
|
||||
auto txnDB = std::make_unique<DatabaseCon>(setup, TxDBName, setup.txPragma, TxDBInit, j);
|
||||
auto& session = txnDB->getSession();
|
||||
std::uint32_t pageSize;
|
||||
std::uint32_t pageSize = 0;
|
||||
|
||||
// Only the most trivial databases will fit in memory on typical
|
||||
// (recommended) hardware. Force temp files to be written to disk
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <xrpl/basics/TaggedCache.ipp>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/shamap/SHAMap.h>
|
||||
#include <xrpl/shamap/SHAMapAccountStateLeafNode.h>
|
||||
#include <xrpl/shamap/SHAMapNodeID.h>
|
||||
@@ -122,7 +123,7 @@ SHAMap::walkTowardsKey(uint256 const& id, SharedPtrNodeStack* stack) const
|
||||
|
||||
if (stack != nullptr)
|
||||
stack->push({inNode, nodeID});
|
||||
return static_cast<SHAMapLeafNode*>(inNode.get());
|
||||
return safe_downcast<SHAMapLeafNode*>(inNode.get());
|
||||
}
|
||||
|
||||
SHAMapLeafNode*
|
||||
@@ -471,7 +472,7 @@ SHAMap::onlyBelow(SHAMapTreeNode* node) const
|
||||
while (!node->isLeaf())
|
||||
{
|
||||
SHAMapTreeNode* nextNode = nullptr;
|
||||
auto inner = static_cast<SHAMapInnerNode*>(node);
|
||||
auto inner = safe_downcast<SHAMapInnerNode*>(node);
|
||||
for (int i = 0; i < branchFactor; ++i)
|
||||
{
|
||||
if (!inner->isEmptyBranch(i))
|
||||
@@ -496,7 +497,7 @@ SHAMap::onlyBelow(SHAMapTreeNode* node) const
|
||||
|
||||
// An inner node must have at least one leaf
|
||||
// below it, unless it's the root_
|
||||
auto const leaf = static_cast<SHAMapLeafNode const*>(node);
|
||||
auto const leaf = safe_downcast<SHAMapLeafNode const*>(node);
|
||||
XRPL_ASSERT(
|
||||
leaf->peekItem() || (leaf == root_.get()), "xrpl::SHAMap::onlyBelow : valid inner node");
|
||||
return leaf->peekItem();
|
||||
@@ -578,7 +579,7 @@ SHAMap::upper_bound(uint256 const& id) const
|
||||
auto [node, nodeID] = stack.top();
|
||||
if (node->isLeaf())
|
||||
{
|
||||
auto leaf = static_cast<SHAMapLeafNode*>(node.get());
|
||||
auto leaf = safe_downcast<SHAMapLeafNode*>(node.get());
|
||||
if (leaf->peekItem()->key() > id)
|
||||
return const_iterator(this, leaf->peekItem().get(), std::move(stack));
|
||||
}
|
||||
@@ -611,7 +612,7 @@ SHAMap::lower_bound(uint256 const& id) const
|
||||
auto [node, nodeID] = stack.top();
|
||||
if (node->isLeaf())
|
||||
{
|
||||
auto leaf = static_cast<SHAMapLeafNode*>(node.get());
|
||||
auto leaf = safe_downcast<SHAMapLeafNode*>(node.get());
|
||||
if (leaf->peekItem()->key() < id)
|
||||
return const_iterator(this, leaf->peekItem().get(), std::move(stack));
|
||||
}
|
||||
@@ -764,7 +765,7 @@ SHAMap::addGiveItem(SHAMapNodeType type, boost::intrusive_ptr<SHAMapItem const>
|
||||
|
||||
node = intr_ptr::make_shared<SHAMapInnerNode>(node->cowid());
|
||||
|
||||
unsigned int b1, b2;
|
||||
unsigned int b1 = 0, b2 = 0;
|
||||
|
||||
while ((b1 = selectBranch(nodeID, tag)) == (b2 = selectBranch(nodeID, otherItem->key())))
|
||||
{
|
||||
@@ -779,7 +780,7 @@ SHAMap::addGiveItem(SHAMapNodeType type, boost::intrusive_ptr<SHAMapItem const>
|
||||
// we can add the two leaf nodes here
|
||||
XRPL_ASSERT(node->isInner(), "xrpl::SHAMap::addGiveItem : node is inner");
|
||||
|
||||
auto inner = static_cast<SHAMapInnerNode*>(node.get());
|
||||
auto inner = safe_downcast<SHAMapInnerNode*>(node.get());
|
||||
inner->setChild(b1, makeTypedLeaf(type, std::move(item), cowid_));
|
||||
inner->setChild(b2, makeTypedLeaf(type, std::move(otherItem), cowid_));
|
||||
}
|
||||
@@ -1088,7 +1089,7 @@ SHAMap::dump(bool hash) const
|
||||
|
||||
if (node->isInner())
|
||||
{
|
||||
auto inner = static_cast<SHAMapInnerNode*>(node);
|
||||
auto inner = safe_downcast<SHAMapInnerNode*>(node);
|
||||
for (int i = 0; i < branchFactor; ++i)
|
||||
{
|
||||
if (!inner->isEmptyBranch(i))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <xrpl/basics/IntrusivePointer.ipp>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/shamap/SHAMap.h>
|
||||
|
||||
#include <array>
|
||||
@@ -39,7 +40,7 @@ SHAMap::walkBranch(
|
||||
if (node->isInner())
|
||||
{
|
||||
// This is an inner node, add all non-empty branches
|
||||
auto inner = static_cast<SHAMapInnerNode*>(node);
|
||||
auto inner = safe_downcast<SHAMapInnerNode*>(node);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
if (!inner->isEmptyBranch(i))
|
||||
nodeStack.push({descendThrow(inner, i)});
|
||||
@@ -47,7 +48,7 @@ SHAMap::walkBranch(
|
||||
else
|
||||
{
|
||||
// This is a leaf node, process its item
|
||||
auto item = static_cast<SHAMapLeafNode*>(node)->peekItem();
|
||||
auto item = safe_downcast<SHAMapLeafNode*>(node)->peekItem();
|
||||
|
||||
if (emptyBranch || (item->key() != otherMapItem->key()))
|
||||
{
|
||||
@@ -132,8 +133,8 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const
|
||||
if (ourNode->isLeaf() && otherNode->isLeaf())
|
||||
{
|
||||
// two leaves
|
||||
auto ours = static_cast<SHAMapLeafNode*>(ourNode);
|
||||
auto other = static_cast<SHAMapLeafNode*>(otherNode);
|
||||
auto ours = safe_downcast<SHAMapLeafNode*>(ourNode);
|
||||
auto other = safe_downcast<SHAMapLeafNode*>(otherNode);
|
||||
if (ours->peekItem()->key() == other->peekItem()->key())
|
||||
{
|
||||
if (ours->peekItem()->slice() != other->peekItem()->slice())
|
||||
@@ -161,22 +162,22 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const
|
||||
}
|
||||
else if (ourNode->isInner() && otherNode->isLeaf())
|
||||
{
|
||||
auto ours = static_cast<SHAMapInnerNode*>(ourNode);
|
||||
auto other = static_cast<SHAMapLeafNode*>(otherNode);
|
||||
auto ours = safe_downcast<SHAMapInnerNode*>(ourNode);
|
||||
auto other = safe_downcast<SHAMapLeafNode*>(otherNode);
|
||||
if (!walkBranch(ours, other->peekItem(), true, differences, maxCount))
|
||||
return false;
|
||||
}
|
||||
else if (ourNode->isLeaf() && otherNode->isInner())
|
||||
{
|
||||
auto ours = static_cast<SHAMapLeafNode*>(ourNode);
|
||||
auto other = static_cast<SHAMapInnerNode*>(otherNode);
|
||||
auto ours = safe_downcast<SHAMapLeafNode*>(ourNode);
|
||||
auto other = safe_downcast<SHAMapInnerNode*>(otherNode);
|
||||
if (!otherMap.walkBranch(other, ours->peekItem(), false, differences, maxCount))
|
||||
return false;
|
||||
}
|
||||
else if (ourNode->isInner() && otherNode->isInner())
|
||||
{
|
||||
auto ours = static_cast<SHAMapInnerNode*>(ourNode);
|
||||
auto other = static_cast<SHAMapInnerNode*>(otherNode);
|
||||
auto ours = safe_downcast<SHAMapInnerNode*>(ourNode);
|
||||
auto other = safe_downcast<SHAMapInnerNode*>(otherNode);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
if (ours->getChildHash(i) != other->getChildHash(i))
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ SHAMapInnerNode::~SHAMapInnerNode() = default;
|
||||
void
|
||||
SHAMapInnerNode::partialDestructor()
|
||||
{
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>* children;
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>* children = nullptr;
|
||||
// structured bindings can't be captured in c++ 17; use tie instead
|
||||
std::tie(std::ignore, std::ignore, children) = hashesAndChildren_.getHashesAndChildren();
|
||||
iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) { children[indexNum].reset(); });
|
||||
@@ -61,8 +61,8 @@ SHAMapInnerNode::clone(std::uint32_t cowid) const
|
||||
p->hash_ = hash_;
|
||||
p->isBranch_ = isBranch_;
|
||||
p->fullBelowGen_ = fullBelowGen_;
|
||||
SHAMapHash *cloneHashes, *thisHashes;
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>*cloneChildren, *thisChildren;
|
||||
SHAMapHash *cloneHashes = nullptr, *thisHashes = nullptr;
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>*cloneChildren = nullptr, *thisChildren = nullptr;
|
||||
// structured bindings can't be captured in c++ 17; use tie instead
|
||||
std::tie(std::ignore, cloneHashes, cloneChildren) =
|
||||
p->hashesAndChildren_.getHashesAndChildren();
|
||||
@@ -185,8 +185,8 @@ SHAMapInnerNode::updateHash()
|
||||
void
|
||||
SHAMapInnerNode::updateHashDeep()
|
||||
{
|
||||
SHAMapHash* hashes;
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>* children;
|
||||
SHAMapHash* hashes = nullptr;
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>* children = nullptr;
|
||||
// structured bindings can't be captured in c++ 17; use tie instead
|
||||
std::tie(std::ignore, hashes, children) = hashesAndChildren_.getHashesAndChildren();
|
||||
iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user