Compare commits

..
Author SHA1 Message Date
Bart 9096ec03a7 refactor: Declare the JSON-RPC and ripplerpc version constants
`ripplerpc`, which selects the reply envelope and accepts three values,
and `jsonrpc`, which names the JSON-RPC protocol version, are spelled as
literals at every call site, so neither field has one place stating what
it accepts. Declare `kRippleRpcVersion1/2/3` beside the `api_version`
constants in ApiVersion.h and `kJsonRpcVersion` in a new
protocol/JsonRpc.h, and use them at every production site that reads
a value: the one in RPCCall.cpp that reads the pair, and the three in
ServerHandler.cpp that default, compare and gate on `ripplerpc`. Every
test site spelling a value as a C++ expression follows. A literal inside
a JSON string fixture is left alone, since substituting there means
assembling the JSON by concatenation. The three `ripplerpc` constants
are declared as one set, so the header states every value the field
accepts, and each has a production user in ServerHandler.cpp.

The two fields keep separate constants and separate headers although
both spell "2.0" today. The specification fixes `jsonrpc` at that value
while `ripplerpc` accepts three, and the `ripplerpc` constants go when
support for API versions 1 and 2 goes, where JSON-RPC is a protocol this
server keeps speaking.

ApiVersion.h's header comment named five constants by unprefixed
spellings that no longer exist, and read as a complete map of the file's
version constants, which it stops being here. Two jtx helpers,
`hasEnvelope2` and `setEnvelope2` in TestHelpers.h, replace the
assertion pair and the request pair that every rewritten test site
repeated. No value changes, on the wire or in a test.
2026-10-14 10:14:02 +09:00
Bart 5fec493960 refactor: Let json::Value be compared and constructed from a string view
`json::Value` accepts `char const*`, `std::string` and
`json::StaticString`, so a caller holding a `std::string_view` has to
materialize a `std::string` whose characters are then copied a second
time into the value's own storage. Add the missing constructor, which
the `std::string` one delegates to, and an `operator==` that reads the
value's characters in place.

The comparison is constrained to `std::string_view` exactly rather than
taking a view by plain overload. A string literal converts equally well
to a view and to a Value, so a plain overload makes every
`value == "literal"` ambiguous, and a literal `0`, which converts to a
view through `char const*` as well as to a Value, with it.

No reply changes. The two spellings differ only for a view holding an
embedded NUL, which the Value comparison stops at. The `method: "batch"`
check in `ServerHandler.cpp` is the operator's first production user, so
that comparison stops building a `Value` from the literal on every
request.
2026-10-13 18:45:16 +09:00
Bart a6a4fff2aa refactor: Remove a publish loop nothing can enter
`pubProposedAccountTransaction` declares an `accountHistoryNotify` vector,
never inserts into it, and then tests it and iterates it. The vector is a
local, so the proof is the function itself: between the declaration and the
loop there are exactly two mentions of it, the condition and the loop, and
neither adds an element. Its sibling `pubAccountTransaction` fills its own
copy, which is what makes the empty one here read as live code.

The compiler corroborates it: with the loop gone the message becomes
`MultiApiJson const`, which is possible only because that loop was its sole
mutator. Nothing is lost with the assertion above the loop either. It held
that a `transJson` result carries no member named `account_history_tx_stream`.
No code writes one: outside the two assertions, the token appears in the
`subscribe` and `unsubscribe` request handlers only. The identical assertion
stays where the loop it guards is live.

No subscriber sees a difference. An account-history subscription is
registered in `subAccountHistory_` alone, so it was never in the map this
function reads, and it receives its transactions from
`pubAccountTransaction`. The same function's guard on three subscription maps
also goes: an earlier return leaves `subRTAccount_` non-empty, so the
condition cannot be false.
2026-10-13 18:45:15 +09:00
BartandBart c2478f509c refactor: Open the acquisition seams its tests need (#8081)
Co-authored-by: Bart <[email protected]>
2026-10-13 03:51:12 +00:00
Mayukha Vadari c908ed7839 fix: Reject malformed conditions in EscrowFinish preflight (#8523) 2026-10-12 20:48:55 +00:00
BartandBart fc57311c81 refactor: Expose a SHAMapAddNode verdict as counts (#8080)
Co-authored-by: Bart <[email protected]>
2026-10-12 20:02:36 +00:00
Mayukha Vadari 818de49264 refactor: Use DepositPreauthEntry everywhere (#8364) 2026-10-12 18:56:14 +00:00
dependabot[bot] 1318b1e58b chore: [DEPENDABOT] bump cxx from 1.0.202 to 1.0.203 in /crates in the rust-dependencies group (#8564)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-12 17:23:41 +00:00
Ayaz Salikhov 617c816ec8 chore: Fix readability-trailing-comma (#8545) 2026-10-12 13:57:24 +00:00
dependabot[bot] 34fc86f585 ci: [DEPENDABOT] bump the github-actions group across 1 directory with 2 updates (#8562)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-12 12:59:39 +00:00
Ayaz Salikhov e071f0a28d chore: Fix misc-const-correctness (#8546) 2026-10-12 11:44:06 +00:00
Ayaz Salikhov 8faa624ade chore: Update XRPLF/actions to use pip from python3.14 explicitly (#8560) 2026-10-12 10:06:06 +00:00
Ayaz Salikhov 6d6ab2d067 Merge remote-tracking branch 'upstream/release/3.4.x' into develop 2026-10-10 00:20:39 +01:00
Ayaz Salikhov 00e6407514 chore: Bump version to 3.4.1 and make pkg_release 2 2026-10-09 23:50:58 +01:00
Ayaz Salikhov 00c06edffb Merge remote-tracking branch 'upstream/release/3.4.x' into develop 2026-10-09 22:45:18 +01:00
Ayaz Salikhov de5053ae0d build: Reduce number of conan logs (#8548) 2026-10-09 16:33:41 +00:00
Ayaz Salikhov 1940ec5c2a chore: Fix readability-redundant-lambda-parameter-list (#8544) 2026-10-09 16:20:45 +00:00
Denis Angell 19c94c73f4 fix: Reject Batch inner txs with the wrong wrapper (fixBatchV1_2) 2026-10-09 16:04:36 +01:00
Bart cd005ff60d ci: Update Nexus packaging URL 2026-10-09 16:04:36 +01:00
Gregory Tsipenyuk 578224f2e6 fix: Assorted integer-arithmetic hardening in the payment engine and ledger helpers 2026-10-09 16:04:35 +01:00
Shawn Xie 3857ce21cd fix: Change mpt subscription msg type back to transaction (#8539) 2026-10-09 13:33:13 +00:00
Ayaz Salikhov aa490df46b chore: Update clang-tidy image to v23 (#8536) 2026-10-09 10:19:07 +00:00
88c1f1e7ac feat: Integrate Permissioned Domain & Credential Checks for Lending Protocol (#6517)
Signed-off-by: JCW <[email protected]>
Co-authored-by: Vito <[email protected]>
Co-authored-by: Ed Hennis <[email protected]>
2026-10-09 10:18:31 +00:00
Ayaz SalikhovandCopilot Autofix powered by AI ede8af8191 refactor: Group binaries in subdirectories (#8535)
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
2026-10-08 12:34:06 +00:00
Alex Kremer 3c24b605a1 chore: Split multiple-in-one into individual tests (#8025) 2026-10-08 09:57:38 +00:00
Alex Kremer d343b542f1 refactor: Migrate handlers to rpc-spec (B) (#8484) 2026-10-07 13:46:37 +00:00
Ayaz Salikhov 7acd719bf7 build: Use images with Clang 23 except for clang-tidy (#8530) 2026-10-07 13:08:20 +00:00
Ayaz Salikhov b4564d5301 chore: Update pre-commit hooks and image (#8528) 2026-10-07 10:59:37 +00:00
Shawn Xie 60195e6d37 fix: Fix MPT partial payment overflow (#8302) 2026-10-06 23:16:13 +00:00
Ayaz Salikhov 3d526d456e build: Use LLVM 23 (#8522) 2026-10-06 21:19:57 +00:00
Denis AngellandMayukha Vadari f05c9f7913 refactor: Extract escrow lock helpers and paychan test helpers (#7882)
Co-authored-by: Mayukha Vadari <[email protected]>
2026-10-06 19:58:52 +00:00
Harshit GuptaandMayukha Vadari c2a4bc3aa1 fix: Validate vetoed parameter type in feature RPC (#7583)
Co-authored-by: Mayukha Vadari <[email protected]>
2026-10-06 19:44:56 +00:00
Mayukha Vadari 718185e3b2 refactor: Use CheckEntry everywhere (#8349) 2026-10-06 19:26:35 +00:00
Ayaz Salikhov 1d7783bb86 build: Make conan retry with Conan Center's source backups (#8524) 2026-10-06 19:23:38 +00:00
Mayukha Vadari 9fd2c552f5 refactor: Use AmendmentsEntry everywhere (#8368) 2026-10-06 17:59:44 +00:00
Ayaz Salikhov 70b8fd301b chore: Update docker images; link Conan-built tools with a static runtime (#8520) 2026-10-06 16:47:35 +00:00
Mayukha Vadari e6564f553d refactor: Use NegativeUNLEntry everywhere (#8365) 2026-10-06 14:23:24 +00:00
Ayaz Salikhov ed96e60ce3 build: Make clang-tools custom in Nix, to match what's being built (#8521) 2026-10-06 13:59:12 +00:00
Alex Kremer 2ebd745a1b refactor: Migrate handlers to rpc-spec (A) (#8345) 2026-10-06 13:19:20 +00:00
Mayukha Vadari cfcbe45b60 test: Declare, not define, entry instantiations in SLEBase test (#8357) 2026-10-06 12:12:22 +00:00
yinyiqian1andBart 9cbf78ba99 test: Add more tests for granular permissions (#8459)
Co-authored-by: Bart <[email protected]>
2026-10-05 23:18:58 +00:00
Timur Yalymov b8d8738f81 fix: Enforce that MPT issuance flags are never cleared (#8152) 2026-10-05 23:17:36 +00:00
63c97e719f feat: Allow lending transactions in Batch (#8244)
Co-authored-by: Cursor <[email protected]>
Co-authored-by: Vito Tumas <[email protected]>
2026-10-05 23:17:26 +00:00
3ac26f23c7 feat: Add full support for all objects in ledger_entry (#6319)
Co-authored-by: Timur Yalymov <[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-authored-by: Vito Tumas <[email protected]>
Co-authored-by: Ayaz Salikhov <[email protected]>
2026-10-05 23:17:14 +00:00
BartandBart ff9410bc56 build: Define XRPL_ASAN, XRPL_TSAN, and XRPL_UBSAN compile definitions (#8483)
Co-authored-by: Bart <[email protected]>
2026-10-05 23:15:08 +00:00
852 changed files with 34845 additions and 26816 deletions

No files matched your search

+11
View File
@@ -5,7 +5,10 @@ Checks: "-*,
-bugprone-exception-escape,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-narrowing-conversions,
-bugprone-signed-bitwise,
-bugprone-std-exception-baseclass,
-bugprone-throwing-static-initialization,
-bugprone-unhandled-code-paths,
cppcoreguidelines-*,
-cppcoreguidelines-avoid-c-arrays,
@@ -14,6 +17,7 @@ Checks: "-*,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-c-copy-assignment-signature,
-cppcoreguidelines-explicit-constructor,
-cppcoreguidelines-interfaces-global-init,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-missing-std-forward,
@@ -32,6 +36,7 @@ Checks: "-*,
llvm-namespace-comment,
misc-*,
-misc-explicit-constructor,
-misc-multiple-inheritance,
-misc-no-recursion,
-misc-non-private-member-variables-in-classes,
@@ -45,6 +50,8 @@ Checks: "-*,
-modernize-avoid-c-style-cast,
-modernize-return-braced-init-list,
-modernize-use-integer-sign-comparison,
-modernize-use-string-view,
-modernize-use-structured-binding,
-modernize-use-trailing-return-type,
performance-*,
@@ -53,6 +60,7 @@ Checks: "-*,
-performance-noexcept-move-constructor,
-performance-unnecessary-copy-initialization,
-performance-unnecessary-value-param,
-performance-use-std-move,
readability-*,
-readability-avoid-const-params-in-decls,
@@ -65,7 +73,10 @@ Checks: "-*,
-readability-named-parameter,
-readability-qualified-auto,
-readability-redundant-access-specifiers,
-readability-redundant-nested-if,
-readability-redundant-qualified-alias,
-readability-static-accessed-through-instance,
-readability-trivial-switch,
-readability-uppercase-literal-suffix
"
# ---
+72
View File
@@ -108,3 +108,75 @@ endfunction()
function(patch_nix_binary target)
endfunction()
function(rpcspec_generate_instantiations)
set(options)
set(oneValueArgs OUT_VAR VALUE_TYPE VIEW_HEADER INCLUDE_DIR)
set(multiValueArgs HANDLERS)
cmake_parse_arguments(
THIS_FUNCTION_PREFIX
"${options}"
"${oneValueArgs}"
"${multiValueArgs}"
${ARGN}
)
endfunction()
function(corrosion_import_crate)
set(options
ALL_FEATURES
NO_DEFAULT_FEATURES
NO_STD
NO_LINKER_OVERRIDE
NO_USES_TERMINAL
LOCKED
FROZEN
)
set(oneValueArgs MANIFEST_PATH PROFILE IMPORTED_CRATES)
set(multiValueArgs
CRATE_TYPES
CRATES
FEATURES
FLAGS
OVERRIDE_CRATE_TYPE
)
cmake_parse_arguments(
THIS_FUNCTION_PREFIX
"${options}"
"${oneValueArgs}"
"${multiValueArgs}"
${ARGN}
)
endfunction()
function(corrosion_set_env_vars target_name env_var)
endfunction()
function(corrosion_add_cxxbridge cxx_target)
set(options)
set(oneValueArgs CRATE)
set(multiValueArgs FILES)
cmake_parse_arguments(
THIS_FUNCTION_PREFIX
"${options}"
"${oneValueArgs}"
"${multiValueArgs}"
${ARGN}
)
endfunction()
function(_unlink_libgcc_s crate)
endfunction()
function(add_xrpl_crate name)
set(options)
set(oneValueArgs CRATE)
set(multiValueArgs FILES)
cmake_parse_arguments(
THIS_FUNCTION_PREFIX
"${options}"
"${oneValueArgs}"
"${multiValueArgs}"
${ARGN}
)
endfunction()
+22 -5
View File
@@ -15,9 +15,9 @@ inputs:
required: false
default: "false"
log_verbosity:
description: "The logging verbosity."
description: 'The logging verbosity ("quiet", "verbose"), or empty to use the Conan defaults.'
required: false
default: "verbose"
default: ""
sanitizers:
description: "The sanitizers to enable."
required: false
@@ -35,6 +35,16 @@ runs:
LOG_VERBOSITY: ${{ inputs.log_verbosity }}
SANITIZERS: ${{ inputs.sanitizers }}
run: |
# By default, leave the verbosity unset, so CMake configure output is
# shown, but compile commands and Boost's b2 debug output (~85k lines
# when "verbose") are not.
VERBOSITY_ARGS=()
if [[ -n "${LOG_VERBOSITY}" ]]; then
VERBOSITY_ARGS=(
--conf:all tools.build:verbosity="${LOG_VERBOSITY}"
--conf:all tools.compilation:verbosity="${LOG_VERBOSITY}"
)
fi
conan install \
--profile:all ci \
--build="${BUILD_OPTION}" \
@@ -42,6 +52,13 @@ runs:
--options:host='&:xrpld=True' \
--settings:all build_type="${BUILD_TYPE}" \
--conf:all tools.build:jobs=${BUILD_NPROC} \
--conf:all tools.build:verbosity="${LOG_VERBOSITY}" \
--conf:all tools.compilation:verbosity="${LOG_VERBOSITY}" \
.
"${VERBOSITY_ARGS[@]}" \
--format=json \
. >"${RUNNER_TEMP}/conan-graph.json"
# Tools that run during the build may only load glibc from the Nix store,
# as their package ID survives a GCC runtime update.
- name: Check build-context packages for Nix store dependencies (Linux)
if: ${{ runner.os == 'Linux' }}
shell: bash
run: ./bin/nix/check-build-context-runtime.sh "${RUNNER_TEMP}/conan-graph.json"
+4 -4
View File
@@ -1,5 +1,5 @@
{
"image_tag": "sha-060957e",
"image_tag": "sha-3d526d4",
"configs": {
"ubuntu": [
{
@@ -74,7 +74,7 @@
"extra_cmake_args": "-Dvalidator_keys=ON",
"package": {
"type": "deb",
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-3a2d19f"
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-e6055dd"
}
},
{
@@ -86,7 +86,7 @@
"extra_cmake_args": "-Dvalidator_keys=ON -Dassert=ON",
"package": {
"type": "deb",
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-3a2d19f",
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-e6055dd",
"variant": "assert"
}
}
@@ -101,7 +101,7 @@
"extra_cmake_args": "-Dvalidator_keys=ON",
"package": {
"type": "rpm",
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-3a2d19f"
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-e6055dd"
}
}
]
+3 -4
View File
@@ -12,8 +12,7 @@ on:
- "!nix/docker/README.md"
- "!nix/devshell.nix"
- "!nix/check-tools/**"
- "bin/default-loader-path.sh"
- "bin/install-sanitizer-libs.sh"
- "bin/nix/default-loader-path.sh"
pull_request:
paths:
- ".github/workflows/build-nix-images.yml"
@@ -25,8 +24,8 @@ on:
- "!nix/devshell.nix"
- "!nix/check-tools/**"
- "bin/check-tools.sh"
- "bin/default-loader-path.sh"
- "bin/install-sanitizer-libs.sh"
- "bin/nix/default-loader-path.sh"
- "bin/install/sanitizer-libs.sh"
workflow_dispatch:
concurrency:
+2 -3
View File
@@ -5,13 +5,12 @@ on:
branches:
- develop
paths:
- ".github/workflows/build-packaging-images.yml"
- "bin/install-packaging-tools.sh"
- "bin/install/packaging-tools.sh"
- "package/images/packaging/**"
pull_request:
paths:
- ".github/workflows/build-packaging-images.yml"
- "bin/install-packaging-tools.sh"
- "bin/install/packaging-tools.sh"
- "package/images/packaging/**"
workflow_dispatch:
@@ -5,7 +5,6 @@ on:
branches:
- develop
paths:
- ".github/workflows/build-pre-commit-image.yml"
- "bin/pre-commit/Dockerfile"
- "rust-toolchain.toml"
pull_request:
+2 -2
View File
@@ -34,7 +34,7 @@ permissions:
jobs:
audit:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
permissions:
contents: read
# Needed to open an issue on scheduled failures.
@@ -66,7 +66,7 @@ jobs:
- name: Create issue
if: ${{ steps.audit.outcome != 'success' && github.event_name == 'schedule' }}
uses: XRPLF/actions/create-issue@2b8bc36af85b88bca0dd7bfac2e2dc05f94ad712
uses: XRPLF/actions/create-issue@b489c21d53928f7dab4874912fec40e4f6c9e85e
with:
title: "cargo audit found vulnerabilities"
body_file: /tmp/cargo-audit-issue.md
+1 -1
View File
@@ -53,7 +53,7 @@ jobs:
- name: Create issue
if: ${{ failure() && github.event_name == 'schedule' && github.repository == 'XRPLF/rippled' }}
uses: XRPLF/actions/create-issue@2b8bc36af85b88bca0dd7bfac2e2dc05f94ad712
uses: XRPLF/actions/create-issue@b489c21d53928f7dab4874912fec40e4f6c9e85e
with:
title: "Releases not merged back into develop"
body_file: ${{ env.ISSUE_FILE }}
+2 -2
View File
@@ -79,7 +79,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: false
@@ -108,7 +108,7 @@ jobs:
- name: Upload regenerated snapshot
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: check-tools-${{ runner.os }}-${{ runner.arch }}
path: ${{ matrix.snapshot }}
+1 -2
View File
@@ -93,9 +93,8 @@ jobs:
.github/workflows/reusable-upload-recipe.yml
.clang-tidy
.codecov.yml
bin/check-nix-store-refs.sh
bin/check-tools.sh
bin/default-loader-path.sh
bin/nix/**
cfg/**
cmake/**
conan/**
+1 -2
View File
@@ -31,9 +31,8 @@ on:
- ".github/workflows/reusable-upload-recipe.yml"
- ".clang-tidy"
- ".codecov.yml"
- "bin/check-nix-store-refs.sh"
- "bin/check-tools.sh"
- "bin/default-loader-path.sh"
- "bin/nix/**"
- "cfg/**"
- "cmake/**"
- "conan/**"
+2 -2
View File
@@ -14,7 +14,7 @@ on:
jobs:
# Call the workflow in the XRPLF/actions repo that runs the pre-commit hooks.
run-hooks:
uses: XRPLF/actions/.github/workflows/pre-commit.yml@279ec358f4a1be4088be3e024b07916fa97c75b6
uses: XRPLF/actions/.github/workflows/pre-commit.yml@d7f2acb47d9662e1e2f8b1f5feffa6d21cc3290a
with:
runs_on: ubuntu-latest
container: '{ "image": "ghcr.io/xrplf/xrpld/pre-commit:sha-473fe44" }'
container: '{ "image": "ghcr.io/xrplf/xrpld/pre-commit:sha-70b8fd3" }'
+2 -2
View File
@@ -41,13 +41,13 @@ env:
jobs:
build:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: false
@@ -129,7 +129,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: ${{ inputs.ccache_enabled }}
@@ -186,9 +186,6 @@ jobs:
with:
build_nproc: ${{ steps.nproc.outputs.nproc }}
build_type: ${{ inputs.build_type }}
# Set the verbosity to "quiet" for Windows to avoid an excessive
# amount of logs. For other OSes, the "verbose" logs are more useful.
log_verbosity: ${{ runner.os == 'Windows' && 'quiet' || 'verbose' }}
sanitizers: ${{ inputs.sanitizers }}
- name: Configure CMake
@@ -257,20 +254,20 @@ jobs:
# cache included, since what it holds is what gets uploaded and reused.
- name: Check the build output for Nix store references (Nix toolchain)
if: ${{ inputs.toolchain == 'nix' }}
run: ./bin/check-nix-store-refs.sh "${BUILD_DIR}"
run: ./bin/nix/check-nix-store-refs.sh "${BUILD_DIR}"
- name: Check the Conan cache for Nix store references (Nix toolchain)
if: ${{ inputs.toolchain == 'nix' }}
run: ./bin/check-nix-store-refs.sh "${CONAN_HOME}"
run: ./bin/nix/check-nix-store-refs.sh "${CONAN_HOME}"
# Only what PatchNixBinary.cmake retargets: the toolchain in the Linux
# images always references the store. Same condition it uses.
- name: Check for Nix store references (Linux)
if: ${{ runner.os == 'Linux' && env.SANITIZERS_ENABLED == 'false' }}
run: |
./bin/check-nix-store-refs.sh "${BUILD_DIR}/xrpld"
./bin/check-nix-store-refs.sh "${BUILD_DIR}/xrpl_tests"
./bin/check-nix-store-refs.sh "${BUILD_DIR}/xrpld_tests"
./bin/nix/check-nix-store-refs.sh "${BUILD_DIR}/xrpld"
./bin/nix/check-nix-store-refs.sh "${BUILD_DIR}/xrpl_tests"
./bin/nix/check-nix-store-refs.sh "${BUILD_DIR}/xrpld_tests"
- name: Show ccache statistics
if: ${{ inputs.ccache_enabled }}
@@ -283,7 +280,7 @@ jobs:
- name: Upload the binary (Linux)
if: ${{ env.PACKAGING_ARTIFACTS_ENABLED == 'true' && runner.os == 'Linux' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: xrpld-${{ inputs.config_name }}
path: ${{ env.BUILD_DIR }}/xrpld
@@ -297,7 +294,7 @@ jobs:
- name: Upload the validator-keys binary
if: ${{ env.PACKAGING_ARTIFACTS_ENABLED == 'true' && env.VALIDATOR_KEYS_ENABLED == 'true' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: validator-keys-${{ inputs.config_name }}
path: |
@@ -308,7 +305,7 @@ jobs:
- name: Upload the test binary (Linux)
if: ${{ github.event.repository.visibility == 'public' && runner.os == 'Linux' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: xrpl_tests-${{ inputs.config_name }}
path: ${{ env.BUILD_DIR }}/xrpl_tests
@@ -324,7 +321,7 @@ jobs:
- name: Upload server definitions
if: ${{ github.event.repository.visibility == 'public' && inputs.config_name == 'ubuntu-gcc-debug-amd64-coverage' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: server-definitions
path: ${{ env.BUILD_DIR }}/server_definitions.json
+5 -6
View File
@@ -34,7 +34,7 @@ jobs:
needs: [determine-files]
if: ${{ needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.need_full_run == 'true' }}
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-060957e"
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-3d526d4"
permissions:
contents: read
issues: write
@@ -43,7 +43,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: false
@@ -73,7 +73,6 @@ jobs:
with:
build_nproc: ${{ steps.nproc.outputs.nproc }}
build_type: ${{ env.BUILD_TYPE }}
log_verbosity: verbose
- name: Configure CMake
working-directory: ${{ env.BUILD_DIR }}
@@ -110,7 +109,7 @@ jobs:
- name: Upload clang-tidy output
if: ${{ github.event.repository.visibility == 'public' && steps.run_clang_tidy.outcome != 'success' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
path: ${{ env.OUTPUT_FILE }}
archive: false
@@ -134,7 +133,7 @@ jobs:
- name: Upload clang-tidy diff output
if: ${{ github.event.repository.visibility == 'public' && steps.files_changed.outcome != 'success' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
path: ${{ env.DIFF_FILE }}
archive: false
@@ -186,7 +185,7 @@ jobs:
- name: Create issue
if: ${{ steps.run_clang_tidy.outcome != 'success' && inputs.create_issue_on_failure }}
uses: XRPLF/actions/create-issue@2b8bc36af85b88bca0dd7bfac2e2dc05f94ad712
uses: XRPLF/actions/create-issue@b489c21d53928f7dab4874912fec40e4f6c9e85e
with:
title: "Clang-tidy check failed"
body_file: ${{ env.ISSUE_FILE }}
@@ -44,7 +44,7 @@ jobs:
# Every package lands in one directory; the step below picks its own,
# which keeps this independent of the artifact names.
- name: Download package artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: "*-pkg"
merge-multiple: true
+9 -9
View File
@@ -100,18 +100,18 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: false
- name: Download pre-built xrpld binary
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: ${{ matrix.xrpld_artifact_name }}
path: ${{ env.BUILD_DIR }}
- name: Download pre-built validator-keys binary
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: ${{ matrix.validator_keys_artifact_name }}
path: ${{ env.BUILD_DIR }}
@@ -151,7 +151,7 @@ jobs:
# version starts with a digit and a longer name does not, so that one digit
# is what tells 'xrpld-3.4.1-...' from 'xrpld-assert-3.4.1-...'.
- name: Upload package artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: ${{ matrix.xrpld_artifact_name }}-pkg
path: |
@@ -160,7 +160,7 @@ jobs:
if-no-files-found: error
- name: Upload debug symbol artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: ${{ matrix.xrpld_artifact_name }}-pkg-debug
path: |
@@ -225,13 +225,13 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: false
# Both artifacts, so the debug symbols are published alongside the package.
- name: Download package artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: ${{ matrix.xrpld_artifact_name }}-pkg*
merge-multiple: true
@@ -287,7 +287,7 @@ jobs:
run: echo "PUSH=${PUSH}" | tee -a "${GITHUB_ENV}"
- name: Download package artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: "*-pkg"
merge-multiple: true
@@ -295,7 +295,7 @@ jobs:
- name: Download voidstar binary
if: ${{ matrix.target == 'voidstar' }}
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: xrpld-ubuntu-clang-debug-amd64-voidstar
path: ${{ env.CONTEXT }}
+3 -3
View File
@@ -28,7 +28,7 @@ permissions:
jobs:
clippy:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -41,7 +41,7 @@ jobs:
coverage:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -70,7 +70,7 @@ jobs:
doc:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+2 -2
View File
@@ -40,7 +40,7 @@ defaults:
jobs:
upload:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
env:
REMOTE_NAME: ${{ inputs.remote_name }}
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.remote_username }}
@@ -50,7 +50,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: false
+2 -5
View File
@@ -68,7 +68,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: false
@@ -108,14 +108,11 @@ jobs:
build_nproc: ${{ steps.nproc.outputs.nproc }}
build_type: ${{ matrix.build_type }}
force_build: ${{ github.event_name == 'schedule' || github.event.inputs.force_source_build == 'true' }}
# Set the verbosity to "quiet" for Windows to avoid an excessive
# amount of logs. For other OSes, the "verbose" logs are more useful.
log_verbosity: ${{ runner.os == 'Windows' && 'quiet' || 'verbose' }}
sanitizers: ${{ matrix.sanitizers }}
- name: Check the Conan cache for Nix store references (Nix toolchain)
if: ${{ matrix.toolchain == 'nix' }}
run: ./bin/check-nix-store-refs.sh "${CONAN_HOME}"
run: ./bin/nix/check-nix-store-refs.sh "${CONAN_HOME}"
- name: Log into Conan remote
if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
+6 -5
View File
@@ -60,7 +60,7 @@ repos:
types_or: [c++, c]
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: e2b496dc2bd8340c2524cb9a2d2a943cde1bb6df # frozen: v23.1.1
rev: a9a8a861f30ed207ead7d5a3b7e8032283ba5da7 # frozen: v23.1.2
hooks:
- id: clang-format
args: [--style=file]
@@ -82,9 +82,10 @@ repos:
files: ^crates/.*\.rs$
- repo: https://github.com/BlankSpruce/gersemi-pre-commit
rev: f1c4833f8cf23c6d952673abc73525411a5719e8 # frozen: 0.29.1
rev: 28010ddd6016e1a0f7bd232acb6536ef996ae897 # frozen: 0.29.2
hooks:
- id: gersemi
args: [-i, --warnings-as-errors]
- repo: https://github.com/rbubley/mirrors-prettier
rev: ef4a397f916211b4a39ccf9d3d9cbb6562157251 # frozen: v3.9.9
@@ -95,19 +96,19 @@ repos:
# Scoped to package/: the rest of the repo's Python has pre-existing findings,
# so widening these is its own change.
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: a56c0b927e6465d37cae3e97d35d4d18ab2b96cd # frozen: v0.16.9
rev: f12be1ebaa5351c1fc76472de98db2c3446c8253 # frozen: v0.16.10
hooks:
- id: ruff-check
args: [--fix]
files: ^package/.*\.py$
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 4160603246a6b365d4a2af661c6d71b0a0f50478 # frozen: 26.5.1
rev: 96ae3e5802f3fe2d551e703e18f0a367d1a81ac2 # frozen: 26.10.0
hooks:
- id: black
- repo: https://github.com/pre-commit/mirrors-mypy
rev: 7ff8d35ae36a7d2b968f2f90b4c723e292e594ee # frozen: v2.3.1
rev: 2834ec6639549dd6796205c8f011dedcd587288b # frozen: v2.4.0
hooks:
- id: mypy
args: [--strict]
+7 -1
View File
@@ -28,13 +28,18 @@ Version 3.5.0 is not yet released.
### Additions in 3.5.0
- `subscribe`, `unsubscribe`: Added an optional `mpt_issuances` request field, an array of MPT issuance IDs (hex strings). Subscribers receive a message with `type` `mptTransaction` for each validated transaction whose metadata affects a subscribed issuance; the message has the same fields as the `transactions` stream. MPT issuance subscriptions count toward the per-connection subscription limit. An empty array, a non-array value, or an invalid ID returns `invalidParams`. ([#5671](https://github.com/XRPLF/rippled/pull/5671))
- `subscribe`, `unsubscribe`: Added an optional `mpt_issuances` request field, an array of MPT issuance IDs (hex strings). Subscribers receive the same `transaction` message as the `transactions` stream for each validated transaction whose metadata affects a subscribed issuance. MPT issuance subscriptions count toward the per-connection subscription limit. An empty array, a non-array value, or an invalid ID returns `invalidParams`. ([#5671](https://github.com/XRPLF/rippled/pull/5671))
- `ledger_entry`: Add full support for checks, NFT offers, payment channels, and signer lists. ([#6319](https://github.com/XRPLF/rippled/pull/6319))
### Bugfixes in 3.5.0
- `channel_authorize`: The `channel_id` field now returns an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
- `channel_verify`: The `channel_id` and `signature` fields now return an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
### Bugfixes in 3.5.0
- `feature`: The admin-only `vetoed` field now returns `invalidParams` unless its value is a boolean. [#7583](https://github.com/XRPLF/rippled/pull/7583)
## XRP Ledger server version 3.4.0
Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta releases.
@@ -55,6 +60,7 @@ Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta
- `account_lines`: The `peer` field now returns an error if the value is not a string. [#7728](https://github.com/XRPLF/rippled/pull/7728)
- `ledger`: `delivered_amount` is now included in the metadata of successful `AccountDelete` transactions when transactions are expanded (`expand`, or admin-only `full`). Previously it was only added for `Payment` and `CheckCash`, which made `ledger` inconsistent with `tx` and `account_tx`. [#5706](https://github.com/XRPLF/rippled/pull/5706)
- `noripple_check`: The `transactions` field is no longer included in error responses; it is still returned (possibly as an empty array) whenever `transactions` is `true` and the request succeeds. A malformed `account` is now rejected before the ledger is looked up, so that error response no longer carries the `ledger_hash`, `ledger_index`, and `validated` fields ([#6303](https://github.com/XRPLF/rippled/pull/6303)).
- `transaction_entry`: An object or an array in `tx_hash` now returns `malformedRequest`, like any other value that is not a hex hash, instead of an `internal` error.
## XRP Ledger server version 3.3.0
+17 -6
View File
@@ -20,9 +20,11 @@
# development setups, but not in the macOS CI environment. They are checked
# everywhere except when running in CI on macOS.
#
# Tools that Nix also exposes under a version-suffixed name (`clang-tidy-22`,
# `g++-15`, ...) are probed under both names: a suffixed name can break while
# the plain one still works (see mkVersionedToolLinks in nix/packages.nix).
# Tools that Nix also exposes under a version-suffixed name
# (`clang-tidy-<v>`, `g++-<v>`, ...) are probed under both names:
# a suffixed name can break while the plain one still works
# (see mkVersionedToolLinks in nix/packages.nix).
# The suffix is the major version of the plain `clang` / `gcc` on PATH.
#
# Tools scoped to a single dev shell rather than to commonPackages are checked
# only in that shell, keyed off XRPL_DEVSHELL.
@@ -34,9 +36,16 @@
set -uo pipefail
# Version suffixes of the Nix tool links, tracking nix/packages.nix.
gcc_version=15
llvm_version=22
# major_version <compiler>
# Major version of a compiler on PATH, or "unknown" when it isn't there.
major_version() {
local version
version="$("$1" -dumpversion 2>/dev/null)" || version=""
version="${version%%.*}"
printf '%s' "${version:-unknown}"
}
llvm_version="$(major_version clang)"
missing=()
checked=0
@@ -112,6 +121,7 @@ if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
check ClangBuildAnalyzer
check curl
check file
check jq
check less
check make
# net-tools netstat reports "net-tools X.Y"; macOS ships BSD netstat with no
@@ -180,6 +190,7 @@ fi
if [ "${os}" = "linux" ]; then
echo
echo "GCC toolchain:"
gcc_version="$(major_version gcc)"
check gcc
check "gcc-${gcc_version}"
check g++
File renamed without changes.
File renamed without changes.
+65
View File
@@ -0,0 +1,65 @@
#!/usr/bin/env bash
# Fail if a binary of a build-context Conan package loads anything from the Nix
# store other than glibc, or cannot resolve a library at all.
#
# Only binaries linked by the Nix toolchain are checked, i.e. those recording a
# store path as their interpreter or RUNPATH. Prebuilt upstream binaries (such
# as the ones the cmake package ships) use the system loader instead.
#
# Build-context packages provide the tools that run during the build (protoc,
# grpc_cpp_plugin, ...). Their package ID does not change when a Nix toolchain
# update moves the GCC runtime to a new store path, so a cached binary has to
# get by with the pinned glibc alone. See docs/build/nix.md.
#
# Usage: bin/nix/check-build-context-runtime.sh <graph.json>
# <graph.json> is the output of `conan install --format=json`.
set -euo pipefail
if [ "$#" -ne 1 ]; then
echo "usage: $0 <graph.json>" >&2
exit 2
fi
if [ "$(uname -s)" != "Linux" ]; then
echo "$0: Linux only" >&2
exit 2
fi
folders="$(jq -r '.graph.nodes[] | select(.context == "build" and .package_folder) | .package_folder' "$1" | sort -u)"
checked=0
failed=0
while IFS= read -r file; do
case "$(file -b "${file}")" in
ELF*) ;;
*) continue ;;
esac
[[ "$(readelf -ldW "${file}")" == */nix/store/* ]] || continue
checked=$((checked + 1))
# `ldd` lists the interpreter and every library as the loader resolves them.
if deps="$(ldd "${file}" 2>&1)"; then
bad="$(printf '%s\n' "${deps}" |
grep -E 'not found|/nix/store/' |
grep -vE '/nix/store/[^/]+-glibc-[^/]+/' || true)"
else
case "${deps}" in
*"not a dynamic executable"*) continue ;;
esac
bad="${deps}"
fi
if [ -n "${bad}" ]; then
failed=$((failed + 1))
echo "::error file=${file}::loads a library from the Nix store other than glibc"
echo "${file}"
echo "${bad}" | sed 's/^/ /'
fi
done < <(
# shellcheck disable=SC2086 # one folder per line, no spaces in Conan paths
[ -z "${folders}" ] || find ${folders} -type f \( -perm -u+x -o -name '*.so*' \)
)
echo "Build-context packages: checked ${checked} binaries, ${failed} failed."
[ "${failed}" -eq 0 ]
@@ -10,7 +10,7 @@
# alone; the scripts in a Conan cache are all git hook samples and autotools
# scratch, 36 false positives to 0 real.
#
# Usage: bin/check-nix-store-refs.sh <path>
# Usage: bin/nix/check-nix-store-refs.sh <path>
set -euo pipefail
File renamed without changes.
+1 -1
View File
@@ -23,7 +23,7 @@ apt-get clean
rm -rf /var/lib/apt/lists/*
EOF
ARG PRE_COMMIT_VERSION=4.6.0
ARG PRE_COMMIT_VERSION=4.6.2
RUN pip install --no-cache --break-system-packages \
pre-commit==${PRE_COMMIT_VERSION}
+20 -7
View File
@@ -26,8 +26,6 @@ import sys
import tempfile
from pathlib import Path
CLANG_TIDY_VERSION = 22
# Extensions run-clang-tidy can analyse: `.cpp` translation units and, thanks to
# the `verify_headers` build option, `.h`/`.hpp` headers (each has its own
# compile_commands.json entry). `.ipp` fragments have no entry and are skipped.
@@ -39,8 +37,21 @@ TIDY_EXTENSIONS = {".cpp", ".h", ".hpp"}
FILEPATH_RE = re.compile(r"^(\s*(?:-\s+)?FilePath:\s*)'((?:[^']|'')*)'\s*$")
def find_tool(name: str) -> str | None:
for candidate in (f"{name}-{CLANG_TIDY_VERSION}", name):
def clang_tidy_major() -> str | None:
"""Major version of the `clang-tidy` on PATH, which run-clang-tidy invokes."""
if not (clang_tidy := shutil.which("clang-tidy")):
return None
output = subprocess.run(
[clang_tidy, "--version"], capture_output=True, text=True
).stdout
m = re.search(r"LLVM version (\d+)", output)
return m.group(1) if m else None
def find_tool(name: str, version: str | None) -> str | None:
"""Prefer `<name>-<version>`, so a host tool of another version can't win."""
candidates = ([f"{name}-{version}"] if version else []) + [name]
for candidate in candidates:
if path := shutil.which(candidate):
return path
return None
@@ -103,8 +114,9 @@ def main():
if not files:
return 0
run_clang_tidy = find_tool("run-clang-tidy")
clang_apply_replacements = find_tool("clang-apply-replacements")
version = clang_tidy_major()
run_clang_tidy = find_tool("run-clang-tidy", version)
clang_apply_replacements = find_tool("clang-apply-replacements", version)
missing = [
name
for name, path in (
@@ -114,9 +126,10 @@ def main():
if not path
]
if missing:
tried = f" (tried the '-{version}' suffix too)" if version else ""
print(
f"clang-tidy check failed: TIDY is enabled but {' and '.join(missing)} "
f"was not found in PATH (tried the '-{CLANG_TIDY_VERSION}' suffix too).",
f"was not found in PATH{tried}.",
file=sys.stderr,
)
return 1
+2 -2
View File
@@ -17,7 +17,7 @@
(runtime libraries resolved through the rpath) are skipped too.
Everywhere else `patch_nix_binary` is a no-op.
The default loader is resolved by bin/default-loader-path.sh.
The default loader is resolved by bin/nix/default-loader-path.sh.
#]===================================================================]
include_guard(GLOBAL)
@@ -25,7 +25,7 @@ include_guard(GLOBAL)
include(CompilationEnv)
# Resolves the system default ELF loader path for the current architecture.
set(_loader_path_script "${CMAKE_SOURCE_DIR}/bin/default-loader-path.sh")
set(_loader_path_script "${CMAKE_SOURCE_DIR}/bin/nix/default-loader-path.sh")
if(
is_linux
+9
View File
@@ -292,6 +292,14 @@ if(xrpld)
)
target_sources(xrpld PRIVATE ${sources})
rpcspec_generate_instantiations(
OUT_VAR rpcspec_instantiations
VALUE_TYPE "::json::Value"
VIEW_HEADER "xrpld/rpc/detail/JsonObjectView.hpp"
HANDLERS book_changes ledger transaction_entry
)
target_sources(xrpld PRIVATE ${rpcspec_instantiations})
if(tests)
file(
GLOB_RECURSE sources
@@ -322,6 +330,7 @@ if(xrpld)
# antithesis_instrumentation.h, which is not exported as INTERFACE
target_include_directories(
xrpld
SYSTEM
PRIVATE ${CMAKE_SOURCE_DIR}/external/antithesis-sdk
)
endif()
+6 -1
View File
@@ -77,19 +77,24 @@ if(is_clang)
message(STATUS " Ignorelist: ${ignorelist_path}")
endif()
# Define SANITIZERS macro for BuildInfo.cpp
# Define the SANITIZERS macro for BuildInfo.cpp, plus one of XRPL_ASAN,
# XRPL_TSAN and XRPL_UBSAN per active sanitizer, so that code can test for a
# specific one with #ifdef instead of parsing the dot-joined SANITIZERS string.
set(sanitizers_list)
if(SANITIZERS MATCHES "address")
set(enable_asan ON)
list(APPEND sanitizers_list "ASAN")
target_compile_definitions(common INTERFACE XRPL_ASAN)
endif()
if(SANITIZERS MATCHES "thread")
set(enable_tsan ON)
list(APPEND sanitizers_list "TSAN")
target_compile_definitions(common INTERFACE XRPL_TSAN)
endif()
if(SANITIZERS MATCHES "undefinedbehavior")
set(enable_ubsan ON)
list(APPEND sanitizers_list "UBSAN")
target_compile_definitions(common INTERFACE XRPL_UBSAN)
endif()
if(sanitizers_list)
@@ -31,7 +31,7 @@ namespace xrpl::ledger_entries {
// builder's STObject and the wrapper's SLE.
TEST(${name}Tests, BuilderSettersRoundTrip)
{
constexpr UInt256 index{1};
UInt256 const index{1u};
% for field in fields:
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
@@ -85,7 +85,7 @@ TEST(${name}Tests, BuilderSettersRoundTrip)
// from that SLE, build a new wrapper, and verify all fields (and validate()).
TEST(${name}Tests, BuilderFromSleRoundTrip)
{
constexpr UInt256 index{2};
UInt256 const index{2u};
% for field in fields:
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
@@ -146,7 +146,7 @@ TEST(${name}Tests, BuilderFromSleRoundTrip)
// 3) Verify wrapper throws when constructed from wrong ledger entry type.
TEST(${name}Tests, WrapperThrowsOnWrongEntryType)
{
constexpr UInt256 index{3};
UInt256 const index{3u};
// Build a valid ledger entry of a different type
// Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq
@@ -177,7 +177,7 @@ TEST(${name}Tests, WrapperThrowsOnWrongEntryType)
// 4) Verify builder throws when constructed from wrong ledger entry type.
TEST(${name}Tests, BuilderThrowsOnWrongEntryType)
{
constexpr UInt256 index{4};
UInt256 const index{4u};
// Build a valid ledger entry of a different type
% if wrong_le_include == "Ticket":
@@ -207,7 +207,7 @@ TEST(${name}Tests, BuilderThrowsOnWrongEntryType)
// 5) Build with only required fields and verify optional fields return nullopt.
TEST(${name}Tests, OptionalFieldsReturnNullopt)
{
constexpr UInt256 index{3};
UInt256 const index{3u};
% for field in required_fields:
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
+6 -2
View File
@@ -3,7 +3,7 @@
"requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1782392402.420688",
"xrpl-rpc-spec/0.1.19#870b2d3abcfbbf13b61c2d3c69495060%1790348286.187549",
"xrpl-rpc-spec/0.1.21#d536f87a2ae7d313452746cfa3ac4404%1790869005.122975",
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1782392403.185447",
"soci/4.0.3#e726491a03468795453f7c83fc924a96%1782392402.679521",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1782307151.633168",
@@ -20,7 +20,7 @@
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1782392403.066892",
"jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1782392402.791979",
"grpc/1.81.1#b87796a4269034856cbc1a2522db16eb%1788275071.530512",
"grpc/1.81.1#aaa93ab6cda2f2baa6a84490582c8adf%1791284951.826256",
"fast_float/8.2.10#f6f28d6bb22112078e7dbda611caf681%1785888854.601666",
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1782307148.15562",
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1782392402.538492",
@@ -34,11 +34,15 @@
"build_requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
"strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1782395692.540639",
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1782392402.431897",
"protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1782392410.233933",
"openssl/3.6.3#f806de8933e3bf6f01016c6a888cee2e%1783945160.863288",
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1782395690.33162",
"msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649",
"m4/1.4.19#1727f439cf74e83826ec96d0b4904eee%1784541921.659",
"grpc/1.81.1#aaa93ab6cda2f2baa6a84490582c8adf%1791284951.826256",
"cmake/4.3.3#840cf00ea09777e05c2050a50a82c722%1782392418.696091",
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1782392402.681654",
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1782392402.624226",
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86",
+9 -1
View File
@@ -3,5 +3,13 @@
core:non_interactive=True
core.download:parallel={{ os.cpu_count() }}
core.upload:parallel={{ os.cpu_count() }}
tools.files.download:retry=5
# Fall back to Conan Center's source backups when a recipe's upstream URL is down
# (e.g. the GNU FTP mirrors), see
# https://github.com/conan-io/conan-center-index/issues/28147#issuecomment-3183544772
# The backups are only tried once every upstream URL has used up its retries,
# so keep the retries low.
core.sources:download_urls=["origin", "https://c3i.jfrog.io/artifactory/conan-center-backup-sources"]
tools.files.download:retry=1
tools.files.download:retry_wait=10
# Fail fast on unreachable hosts (default connect timeout is 30s), keep the 60s read timeout.
core.net.http:timeout=(5, 60)
+31 -4
View File
@@ -29,9 +29,9 @@ os.version={{ min_macos_version }}
[conf]
{# The Boost recipe builds with b2, which doesn't use Conan's toolchain files. #}
{# Instead it hand-rolls the compiler for user-config.jam, #}
{# and its fallback probes a version-suffixed binary (e.g. `g++-15`) before plain `g++`. #}
{# Inside the Nix shell the wrapper only provides `g++`/`gcc` (no `-15` suffix), #}
{# so on a host that also has a system `g++-15` the probe escapes Nix #}
{# and its fallback probes a version-suffixed binary (e.g. `g++-<major>`) before plain `g++`. #}
{# Inside the Nix shell the wrapper only provides `g++`/`gcc` (no `-<major>` suffix), #}
{# so on a host that also has a system `g++-<major>` the probe escapes Nix #}
{# and picks the system compiler, which is mismatched with the Nix libraries #}
{# and breaks the build (e.g. Boost.Stacktrace link checks fail). #}
{# Pinning the executables here short-circuits that probe so Boost (and the rest of the toolchain) #}
@@ -49,8 +49,31 @@ tools.build:compiler_executables={'c':'{{ cc_exe }}','cpp':'{{ cxx_exe }}'}
user.package:cppstd_version=23
tools.info.package_id:confs+=["user.package:cppstd_version"]
{% if os == "Macos" %}
{% if os == "Linux" and context == "build" %}
{# Build-context executables (protoc, grpc_cpp_plugin, build tools) run during the build #}
{# and would otherwise load libstdc++/libgcc from a Nix store path #}
{# that might change with a Nix toolchain update. #}
{# --as-needed drops the ones they link but don't use, #}
{# such as libatomic for grpc_cpp_plugin on arm64. #}
{% set static_runtime_flags = ["-static-libstdc++", "-static-libgcc", "-Wl,--as-needed"] %}
tools.build:exelinkflags+={{ static_runtime_flags }}
tools.info.package_id:confs+=["tools.build:exelinkflags"]
{% endif %}
{% if os == "Linux" and context == "build" %}
{# b2 links itself with its own script, which ignores exelinkflags #}
{# and only takes CXXFLAGS when use_cxx_env is set (see [buildenv] below). #}
[options]
b2/*:use_cxx_env=True
{% endif %}
[buildenv]
{# gRPC emits thousands of compiler warnings that we cannot act on. #}
{# CMake picks up CXXFLAGS, and unlike tools.build:cxxflags, #}
{# this is not part of the package ID, so binaries stay shareable. #}
grpc/*:CXXFLAGS=-w
{% if os == "Macos" %}
{# os.version adds -mmacosx-version-min to compiler command lines, #}
{# but Boost.Context's b2 assembly (.S) rule ignores it, #}
{# so those objects keep the host SDK version and still warn at link time. #}
@@ -58,3 +81,7 @@ tools.info.package_id:confs+=["user.package:cppstd_version"]
{# Scoped to boost/* since it is the only gap. #}
boost/*:MACOSX_DEPLOYMENT_TARGET={{ min_macos_version }}
{% endif %}
{% if os == "Linux" and context == "build" %}
b2/*:CXXFLAGS={{ static_runtime_flags | join(" ") }}
{% endif %}
+3
View File
@@ -5,6 +5,9 @@ include(default)
{% if not sanitizers %}
{# Sanitizers not configured; no additional settings needed #}
{% elif context == "build" %}
{# Build-context packages are tools we run, not code we test, #}
{# so don't instrument them #}
{% else %}
{% if compiler == "msvc" %}
+8 -3
View File
@@ -40,7 +40,7 @@ class Xrpl(ConanFile):
"nudb/2.0.9",
"openssl/3.6.3",
"soci/4.0.3",
"xrpl-rpc-spec/0.1.19",
"xrpl-rpc-spec/0.1.21",
"zlib/1.3.2",
]
@@ -49,7 +49,8 @@ class Xrpl(ConanFile):
]
tool_requires = [
"protobuf/6.33.5",
"grpc/<host_version>",
"protobuf/<host_version>",
]
default_options = {
@@ -123,6 +124,10 @@ class Xrpl(ConanFile):
"xxhash/*:shared": False,
}
# default_options only reach the host context;
# give tool_requires (and their dependencies) the same dependency options.
default_build_options = {k: v for k, v in default_options.items() if "/" in k}
def set_version(self):
self.version = self.version or DEV_VERSION
@@ -149,7 +154,7 @@ class Xrpl(ConanFile):
self.requires("xxhash/0.8.3", transitive_headers=True)
exports_sources = (
"bin/default-loader-path.sh",
"bin/nix/default-loader-path.sh",
"CMakeLists.txt",
"cfg/*",
"cmake/*",
+5 -6
View File
@@ -80,13 +80,12 @@ function(add_xrpl_crate name)
# `cc` picks its runtime flag from `crt-static` alone, so it compiles a
# crate's C++ with `-MT`; Debug needs `-MTd` (to match cmake/XrplCompiler.cmake).
if(is_msvc)
corrosion_set_env_vars(
${ARG_CRATE}
"$<$<CONFIG:Debug>:CXXFLAGS=-MTd>"
)
corrosion_set_env_vars(${ARG_CRATE} "$<$<CONFIG:Debug>:CXXFLAGS=-MTd>")
endif()
corrosion_add_cxxbridge(${name}_cxxbridge CRATE ${ARG_CRATE} FILES
${ARG_FILES}
corrosion_add_cxxbridge(
${name}_cxxbridge
CRATE ${ARG_CRATE}
FILES ${ARG_FILES}
)
# Generated cxxbridge headers don't exist at configure time; CMake 3.28+
# validates INTERFACE_SOURCES on consuming targets. Clear it to skip the
+10 -10
View File
@@ -57,9 +57,9 @@ dependencies = [
[[package]]
name = "cxx"
version = "1.0.202"
version = "1.0.205"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13f6de320895f42e6e081abb5c7983bedcf0b6d0ff9323de0d33f620c8ac1199"
checksum = "c8a02bfeba9074b2bbb39492c9b0908ec854f455a3cccdcde7a1de55e2315e40"
dependencies = [
"cc",
"cxx-build",
@@ -72,9 +72,9 @@ dependencies = [
[[package]]
name = "cxx-build"
version = "1.0.202"
version = "1.0.205"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fde53ca86b9704a943fef0f1e1d836239a6aedca5de3c65fa9f97ec0bd46d39"
checksum = "bbc2ac76997619d2d888b58ad79488f1c835de41ca4dd3dd7082acf4ecb0a50f"
dependencies = [
"cc",
"codespan-reporting",
@@ -87,9 +87,9 @@ dependencies = [
[[package]]
name = "cxxbridge-cmd"
version = "1.0.202"
version = "1.0.205"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bae89236c811fd4d08ed3441759ac4ac98d752cbfd3de341315ba16ad20ec3"
checksum = "54a486ed16a3f2a053fe7f5b98db4c1a59846a23e8cb9e49f9d1bd48830d51a2"
dependencies = [
"clap",
"codespan-reporting",
@@ -101,15 +101,15 @@ dependencies = [
[[package]]
name = "cxxbridge-flags"
version = "1.0.202"
version = "1.0.205"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49045042e5fced01b80742aba5508de82aa4f13677ed3fa2b4cda709c40c5918"
checksum = "c72569be94e2283764d334cd285980b747dbe221e27e315484eb6bcd93fa808a"
[[package]]
name = "cxxbridge-macro"
version = "1.0.202"
version = "1.0.205"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b181252e2e3d3b5d183afbdc033a3958b445eb3e1a0ae65fc3d4f5259f5da6fd"
checksum = "423e795284a7f7b3f843c956a5322c4bc497b60c2dd1a6339da10313cb18d575"
dependencies = [
"indexmap",
"proc-macro2",
+9 -7
View File
@@ -10,14 +10,15 @@ This document explains how to set one up.
support it — see [compiler support for C++23][cpp23-support].
The versions currently tested in CI are:
| Compiler | Version |
| ----------- | ------------------ |
| GCC | 15.2 |
| Clang | 22 |
| Apple Clang | 21 |
| MSVC | Visual Studio 2026 |
| Compiler | Version |
| ----------- | ------------------------------- |
| GCC | `gccVersion` in [packages.nix] |
| Clang | `llvmVersion` in [packages.nix] |
| Apple Clang | 21 |
| MSVC | Visual Studio 2026 |
LLVM tools (`clang-tidy` and `clang-format`) are also pinned to version 22.
LLVM tools (`clang-tidy` and `clang-format`)
come from the same LLVM release as Clang.
### Older compilers
@@ -156,3 +157,4 @@ version out of the box — run it via `run-clang-tidy`. No separate installation
is needed.
[cpp23-support]: https://en.cppreference.com/w/cpp/compiler_support/23
[packages.nix]: ../../nix/packages.nix
+21 -9
View File
@@ -183,24 +183,36 @@ at link or run time.
> configuration CI covers, and no dependency binaries are published for it.
This is checked rather than assumed.
[`bin/check-nix-store-refs.sh`](../../bin/check-nix-store-refs.sh) takes one file
or directory and fails if a binary under it resolves a store path at run time.
[`bin/nix/check-nix-store-refs.sh`](../../bin/nix/check-nix-store-refs.sh) takes one
file or directory and fails if a binary under it resolves a store path at run time.
CI runs it over the build output and the Conan cache, and again in the upload job
before anything is published. You can run it yourself:
```bash
bin/check-nix-store-refs.sh build
bin/check-nix-store-refs.sh ~/.conan2-nix
bin/nix/check-nix-store-refs.sh build
bin/nix/check-nix-store-refs.sh ~/.conan2-nix
```
It works on Linux too, but asserts something narrower there: the toolchain always
writes the store into `PT_INTERP` and `RUNPATH`, and CI builds inside an image
whose store is fixed for its lifetime, so that is fine. Only the binaries
[`PatchNixBinary.cmake`](../../cmake/PatchNixBinary.cmake) retargets to the
system loader have to be clean, and those are what CI checks:
writes the store into `PT_INTERP` and `RUNPATH`. That is fine for the pinned
glibc, whose path does not move, but not for the GCC runtime, which moves with
every GCC update. So [`conan/profiles/default`](../../conan/profiles/default)
links build-context packages, whose executables run during the build, with
`-static-libstdc++ -static-libgcc -Wl,--as-needed`, and
[`conan/profiles/sanitizers`](../../conan/profiles/sanitizers) does not
instrument them. CI checks that they load nothing from the store but glibc, from
the graph `conan install --format=json` writes:
```bash
bin/check-nix-store-refs.sh build/xrpld
bin/nix/check-build-context-runtime.sh graph.json
```
Only the binaries [`PatchNixBinary.cmake`](../../cmake/PatchNixBinary.cmake)
retargets to the system loader have to be fully clean, and those are what CI
checks:
```bash
bin/nix/check-nix-store-refs.sh build/xrpld
```
### The libresolv stub
+3 -3
View File
@@ -178,11 +178,11 @@ A binary stops starting after a `nix flake update`, or after
dyld[57271]: Library not loaded: /nix/store/…-libresolv-93/lib/libresolv.9.dylib
```
[`bin/check-nix-store-refs.sh`](../../bin/check-nix-store-refs.sh) finds the same
thing without having to run anything, and names the file:
[`bin/nix/check-nix-store-refs.sh`](../../bin/nix/check-nix-store-refs.sh) finds the
same thing without having to run anything, and names the file:
```
$ bin/check-nix-store-refs.sh ~/.conan2-nix
$ bin/nix/check-nix-store-refs.sh ~/.conan2-nix
::error file=/Users/you/.conan2-nix/p/b/c-area24ded30c388c/p/bin/adig::references the Nix store at run time
/Users/you/.conan2-nix/p/b/c-area24ded30c388c/p/bin/adig
/nix/store/p4lp3xq4imd1qzqh08x8vcq2zfhi7rca-libresolv-93/lib/libresolv.9.dylib
+1 -1
View File
@@ -6,7 +6,7 @@ project(antithesis-sdk-cpp VERSION 0.4.4 LANGUAGES CXX)
add_library(antithesis-sdk-cpp INTERFACE antithesis_sdk.h)
# Note, both sections below created by xrpld project
target_include_directories(antithesis-sdk-cpp INTERFACE
target_include_directories(antithesis-sdk-cpp SYSTEM INTERFACE
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
)
+1 -1
View File
@@ -34,7 +34,7 @@ lz4Compress(void const* in, std::size_t inSize, BufferFactory&& bf)
// data
auto compressed = bf(outCapacity);
auto compressedSize = LZ4_compress_default(
auto const compressedSize = LZ4_compress_default(
reinterpret_cast<char const*>(in),
reinterpret_cast<char*>(compressed),
inSize,
+56 -147
View File
@@ -1,31 +1,29 @@
#pragma once
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/beast/type_name.h>
#include <boost/core/type_name.hpp>
#include <algorithm>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace xrpl {
/**
* Manages all counted object types.
*
* Counters register themselves on a lock-free intrusive list maintained
* by this object when constructed. Because counters are never destroyed
* or removed, the ABA problem does not apply.
*
* The registry is iterable as a forward range.
*/
class CountedObjects
{
public:
static CountedObjects&
getInstance() noexcept;
using Entry = std::pair<std::string, int>;
using List = std::vector<Entry>;
[[nodiscard]] List
getCounts(int minimumThreshold) const;
public:
/**
* Implementation for @ref CountedObject.
@@ -35,147 +33,68 @@ public:
class Counter
{
public:
Counter(std::string name) noexcept;
Counter(std::string name) noexcept : name_(std::move(name)), count_(0)
{
// Insert ourselves at the front of the lock-free linked list
CountedObjects& instance = CountedObjects::getInstance();
Counter* head = nullptr;
// Counters are intrusive list nodes whose addresses are published
// in the registry; they must never be copied or moved. The atomic
// members already force this, but we make it explicit.
Counter(Counter const&) = delete;
Counter&
operator=(Counter const&) = delete;
Counter(Counter&&) = delete;
Counter&
operator=(Counter&&) = delete;
do
{
head = instance.head_.load();
next_ = head;
} while (instance.head_.exchange(this) != head);
std::uint32_t
++instance.count_;
}
~Counter() noexcept = default;
int
increment() noexcept
{
auto const newCount = count_.fetch_add(1, std::memory_order::relaxed) + 1;
XRPL_ASSERT(newCount != 0, "xrpl::CountedObjects::Counter::increment : no overflow");
auto maxCount = maxCount_.load(std::memory_order::relaxed);
while (newCount > maxCount &&
!maxCount_.compare_exchange_weak(maxCount, newCount, std::memory_order::relaxed))
{
}
return newCount;
return ++count_;
}
std::uint32_t
int
decrement() noexcept
{
auto const prev = count_.fetch_sub(1, std::memory_order::relaxed);
XRPL_ASSERT(prev != 0, "xrpl::CountedObjects::Counter::decrement : no underflow");
return prev - 1;
return --count_;
}
[[nodiscard]] std::uint32_t
count() const noexcept
[[nodiscard]] int
getCount() const noexcept
{
return count_.load(std::memory_order::relaxed);
return count_.load();
}
[[nodiscard]] std::uint32_t
max() const noexcept
[[nodiscard]] Counter*
getNext() const noexcept
{
return std::max(
count_.load(std::memory_order::relaxed),
maxCount_.load(std::memory_order::relaxed));
return next_;
}
[[nodiscard]] std::string const&
name() const noexcept
getName() const noexcept
{
return name_;
}
private:
friend class CountedObjects;
Counter* next_ = nullptr;
std::atomic<std::uint32_t> count_ = 0;
std::atomic<std::uint32_t> maxCount_ = 0;
std::string const name_;
std::atomic<int> count_;
Counter* next_;
};
class Iterator
{
public:
using value_type = Counter const;
using reference = value_type&;
using pointer = value_type*;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
explicit Iterator(Counter* c = nullptr) noexcept : current_(c)
{
}
reference
operator*() const noexcept
{
return *current_;
}
pointer
operator->() const noexcept
{
return current_;
}
Iterator&
operator++() noexcept
{
current_ = current_->next_;
return *this;
}
Iterator
operator++(int) noexcept
{
auto tmp = *this;
++*this;
return tmp;
}
bool
operator==(Iterator const&) const noexcept = default;
private:
Counter* current_;
};
constexpr CountedObjects() noexcept = default;
[[nodiscard]] auto
begin() const noexcept
{
return Iterator{head_.load(std::memory_order::acquire)};
}
[[nodiscard]] auto
end() const noexcept
{
return Iterator{};
}
private:
std::atomic<Counter*> head_ = nullptr;
CountedObjects() noexcept;
~CountedObjects() noexcept = default;
private:
std::atomic<int> count_;
std::atomic<Counter*> head_;
};
/** The global counted object registry. */
inline constinit CountedObjects gCountedObjects;
inline CountedObjects::Counter::Counter(std::string name) noexcept
: next_(gCountedObjects.head_.load(std::memory_order::relaxed)), name_(std::move(name))
{
while (!gCountedObjects.head_.compare_exchange_weak(
next_, this, std::memory_order::release, std::memory_order::relaxed))
;
}
//------------------------------------------------------------------------------
/**
@@ -184,37 +103,27 @@ inline CountedObjects::Counter::Counter(std::string name) noexcept
* Derived classes have their instances counted automatically. This is used
* for reporting purposes.
*
* The constructors are private and `Object` is befriended so that the
* CRTP parameter must be the deriving class itself: a copy-paste error
* like `class B : public CountedObject<A>` fails to compile instead of
* silently polluting A's count.
*
* @note This class has no move operations by design: a derived class's
* move constructor falls back to the copy constructor for this
* base, so the newly created instance is counted. This keeps the
* invariant that count is the number of outstanding subobjects.
*
* @warning Counted objects constructed during dynamic initialization of
* other translation units may have their increments discarded when
* counter itself is dynamically initialized. Do not create counted
* objects before main() begins.
*
* @ingroup basics
*/
template <class Object>
requires std::is_class_v<Object>
class CountedObject
{
static inline CountedObjects::Counter counter{boost::core::type_name<Object>()};
private:
static auto&
getCounter() noexcept
{
static CountedObjects::Counter kC{beast::typeName<Object>()};
return kC;
}
CountedObject() noexcept
{
counter.increment();
getCounter().increment();
}
CountedObject(CountedObject const&) noexcept
{
counter.increment();
getCounter().increment();
}
CountedObject&
@@ -223,7 +132,7 @@ class CountedObject
public:
~CountedObject() noexcept
{
counter.decrement();
getCounter().decrement();
}
friend Object;
+38 -30
View File
@@ -20,12 +20,14 @@ SharedIntrusive<T>::SharedIntrusive(T* p, TAdoptTag) noexcept : ptr_{p}
template <class T>
SharedIntrusive<T>::SharedIntrusive(SharedIntrusive const& rhs)
: ptr_{[&] {
auto p = rhs.unsafeGetRawPtr();
if (p)
p->addStrongRef();
return p;
}()}
: ptr_{
[&] {
auto p = rhs.unsafeGetRawPtr();
if (p)
p->addStrongRef();
return p;
}(),
}
{
}
@@ -33,12 +35,14 @@ template <class T>
template <class TT>
requires std::convertible_to<TT*, T*>
SharedIntrusive<T>::SharedIntrusive(SharedIntrusive<TT> const& rhs)
: ptr_{[&] {
auto p = rhs.unsafeGetRawPtr();
if (p)
p->addStrongRef();
return p;
}()}
: ptr_{
[&] {
auto p = rhs.unsafeGetRawPtr();
if (p)
p->addStrongRef();
return p;
}(),
}
{
}
@@ -140,12 +144,14 @@ SharedIntrusive<T>::~SharedIntrusive()
template <class T>
template <class TT>
SharedIntrusive<T>::SharedIntrusive(StaticCastTagSharedIntrusive, SharedIntrusive<TT> const& rhs)
: ptr_{[&] {
auto p = static_cast<T*>(rhs.unsafeGetRawPtr());
if (p)
p->addStrongRef();
return p;
}()}
: ptr_{
[&] {
auto p = static_cast<T*>(rhs.unsafeGetRawPtr());
if (p)
p->addStrongRef();
return p;
}(),
}
{
}
@@ -159,12 +165,14 @@ SharedIntrusive<T>::SharedIntrusive(StaticCastTagSharedIntrusive, SharedIntrusiv
template <class T>
template <class TT>
SharedIntrusive<T>::SharedIntrusive(DynamicCastTagSharedIntrusive, SharedIntrusive<TT> const& rhs)
: ptr_{[&] {
auto p = dynamic_cast<T*>(rhs.unsafeGetRawPtr());
if (p)
p->addStrongRef();
return p;
}()}
: ptr_{
[&] {
auto p = dynamic_cast<T*>(rhs.unsafeGetRawPtr());
if (p)
p->addStrongRef();
return p;
}(),
}
{
}
@@ -260,7 +268,7 @@ SharedIntrusive<T>::unsafeReleaseAndStore(T* next)
return;
using enum ReleaseStrongRefAction;
auto action = prev->releaseStrongRef();
auto const action = prev->releaseStrongRef();
switch (action)
{
case NoOp:
@@ -361,7 +369,7 @@ WeakIntrusive<T>::unsafeReleaseNoStore()
return;
using enum ReleaseWeakRefAction;
auto action = ptr_->releaseWeakRef();
auto const action = ptr_->releaseWeakRef();
switch (action)
{
case NoOp:
@@ -580,7 +588,7 @@ SharedWeakUnion<T>::convertToStrong()
auto p = unsafeGetRawPtr();
if (p && p->checkoutStrongRefFromWeak())
{
[[maybe_unused]] auto action = p->releaseWeakRef();
[[maybe_unused]] auto const action = p->releaseWeakRef();
XRPL_ASSERT(
(action == ReleaseWeakRefAction::NoOp),
"xrpl::SharedWeakUnion::convertToStrong : "
@@ -603,7 +611,7 @@ SharedWeakUnion<T>::convertToWeak()
return false;
using enum ReleaseStrongRefAction;
auto action = p->addWeakReleaseStrongRef();
auto const action = p->addWeakReleaseStrongRef();
switch (action)
{
case NoOp:
@@ -670,7 +678,7 @@ SharedWeakUnion<T>::unsafeReleaseNoStore()
if (isStrong())
{
using enum ReleaseStrongRefAction;
auto strongAction = p->releaseStrongRef();
auto const strongAction = p->releaseStrongRef();
switch (strongAction)
{
case NoOp:
@@ -688,7 +696,7 @@ SharedWeakUnion<T>::unsafeReleaseNoStore()
else
{
using enum ReleaseWeakRefAction;
auto weakAction = p->releaseWeakRef();
auto const weakAction = p->releaseWeakRef();
switch (weakAction)
{
case NoOp:
+2 -2
View File
@@ -406,10 +406,10 @@ IntrusiveRefCounts::useCount() const noexcept
inline IntrusiveRefCounts::~IntrusiveRefCounts() noexcept
{
#ifndef NDEBUG
auto v = refCounts_.load(std::memory_order_acquire);
auto const v = refCounts_.load(std::memory_order_acquire);
XRPL_ASSERT(
(!(v & kValueMask)), "xrpl::IntrusiveRefCounts::~IntrusiveRefCounts : count must be zero");
auto t = v & kTagMask;
auto const t = v & kTagMask;
XRPL_ASSERT((!t || t == kTagMask), "xrpl::IntrusiveRefCounts::~IntrusiveRefCounts : valid tag");
#endif
}
+75
View File
@@ -3,9 +3,84 @@
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <optional>
namespace xrpl {
/**
* Add two signed 64-bit integers, returning std::nullopt when the exact
* mathematical sum is not representable in std::int64_t.
*/
[[nodiscard]] constexpr std::optional<std::int64_t>
checkedAdd(std::int64_t a, std::int64_t b) noexcept
{
using L = std::numeric_limits<std::int64_t>;
if ((b > 0 && a > L::max() - b) || (b < 0 && a < L::min() - b))
return std::nullopt;
return a + b;
}
/**
* Subtract two signed 64-bit integers, returning std::nullopt when the exact
* mathematical difference is not representable in std::int64_t.
*/
[[nodiscard]] constexpr std::optional<std::int64_t>
checkedSub(std::int64_t a, std::int64_t b) noexcept
{
using L = std::numeric_limits<std::int64_t>;
if ((b > 0 && a < L::min() + b) || (b < 0 && a > L::max() + b))
return std::nullopt;
return a - b;
}
static_assert(checkedAdd(0, 0) == 0);
static_assert(checkedAdd(1, -1) == 0);
static_assert(checkedAdd(-5, 2) == -3);
static_assert(!checkedAdd(std::numeric_limits<std::int64_t>::max(), 1).has_value());
static_assert(!checkedAdd(std::numeric_limits<std::int64_t>::min(), -1).has_value());
static_assert(
checkedAdd(std::numeric_limits<std::int64_t>::max() - 1, 1) ==
std::numeric_limits<std::int64_t>::max());
static_assert(
checkedAdd(
std::numeric_limits<std::int64_t>::min(),
std::numeric_limits<std::int64_t>::max()) == -1);
static_assert(
checkedAdd(
std::numeric_limits<std::int64_t>::max(),
std::numeric_limits<std::int64_t>::min()) == -1);
static_assert(
!checkedAdd(std::numeric_limits<std::int64_t>::max(), std::numeric_limits<std::int64_t>::max())
.has_value());
static_assert(
!checkedAdd(std::numeric_limits<std::int64_t>::min(), std::numeric_limits<std::int64_t>::min())
.has_value());
static_assert(checkedSub(0, 0) == 0);
static_assert(checkedSub(1, 1) == 0);
static_assert(checkedSub(-5, 2) == -7);
static_assert(checkedSub(-5, -2) == -3);
static_assert(!checkedSub(std::numeric_limits<std::int64_t>::min(), 1).has_value());
static_assert(!checkedSub(std::numeric_limits<std::int64_t>::max(), -1).has_value());
static_assert(
checkedSub(std::numeric_limits<std::int64_t>::min() + 1, 1) ==
std::numeric_limits<std::int64_t>::min());
static_assert(
checkedSub(-1, std::numeric_limits<std::int64_t>::max()) ==
std::numeric_limits<std::int64_t>::min());
static_assert(
!checkedSub(std::numeric_limits<std::int64_t>::max(), std::numeric_limits<std::int64_t>::min())
.has_value());
static_assert(
!checkedSub(std::numeric_limits<std::int64_t>::min(), std::numeric_limits<std::int64_t>::max())
.has_value());
/**
* Calculate one number divided by another number in percentage.
* The result is rounded up to the next integer, and capped in the range [0,100]
File diff suppressed because it is too large. Load diff
-23
View File
@@ -253,27 +253,4 @@ makeSlice(std::basic_string_view<char, Traits> s)
return Slice(s.data(), s.size());
}
/**
* Checks whether two byte ranges share at least one byte of storage.
*
* Each slice is treated as the half-open range [data, data + size), so
* ranges that merely touch do not overlap. An empty slice overlaps
* nothing, wherever it points.
*
* @param a The first range.
* @param b The second range.
* @return true if the ranges overlap, false otherwise.
*/
[[nodiscard]] inline bool
overlap(Slice a, Slice b) noexcept
{
if (a.empty() || b.empty())
return false;
auto const a0 = reinterpret_cast<std::uintptr_t>(a.data());
auto const b0 = reinterpret_cast<std::uintptr_t>(b.data());
return a0 < b0 + b.size() && b0 < a0 + a.size();
}
} // namespace xrpl
+2 -3
View File
@@ -1,7 +1,6 @@
#pragma once
#include <xrpl/basics/Blob.h>
#include <xrpl/basics/Slice.h>
#include <boost/utility/string_view.hpp>
@@ -29,7 +28,7 @@ namespace xrpl {
* @return The input, encoded as a blob literal.
*/
std::string
sqlBlobLiteral(Slice blob);
sqlBlobLiteral(Blob const& blob);
namespace detail {
@@ -37,7 +36,7 @@ template <typename T>
concept SomeChar = std::same_as<std::remove_cvref_t<T>, int8_t> ||
std::same_as<std::remove_cvref_t<T>, char> || std::same_as<std::remove_cvref_t<T>, uint8_t>;
inline constexpr std::array<std::optional<int>, 256> const kDigitLookupTable = []() {
inline constexpr std::array<std::optional<int>, 256> const kDigitLookupTable = [] {
std::array<std::optional<int>, 256> t{};
for (int i = 0; i < 10; ++i)
+9 -9
View File
@@ -300,7 +300,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
// Remove from cache, if !valid, remove from map too. Returns true if removed from cache
std::scoped_lock const lock(mutex_);
auto cit = cache_.find(key);
auto const cit = cache_.find(key);
if (cit == cache_.end())
return false;
@@ -356,7 +356,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
std::scoped_lock const lock(mutex_);
auto cit = cache_.find(key);
auto const cit = cache_.find(key);
if (cit == cache_.end())
{
@@ -371,7 +371,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
Entry& entry = cit->second;
entry.touch(clock_.now());
auto shouldReplaceCached = [&] {
auto const shouldReplaceCached = [&] {
if constexpr (replaceCached)
{
return true;
@@ -400,7 +400,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
return true;
}
auto cachedData = entry.lock();
auto const cachedData = entry.lock();
if (cachedData)
{
@@ -563,7 +563,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
retrieve(key_type const& key, T& data)
{
// retrieve the value of the stored data
auto entry = fetch(key);
auto const entry = fetch(key);
if (!entry)
return false;
@@ -687,7 +687,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
return ret;
}
auto sle = h();
auto const sle = h();
if (!sle)
return {};
@@ -713,7 +713,7 @@ inline SharedPointerType
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
initialFetch(key_type const& key, std::scoped_lock<MutexType> const& l)
{
auto cit = cache_.find(key);
auto const cit = cache_.find(key);
if (cit == cache_.end())
return {};
@@ -783,7 +783,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
std::atomic<int>& allRemovals,
std::scoped_lock<std::recursive_mutex> const&)
{
return std::thread([&, this]() {
return std::thread([&, this] {
int cacheRemovals = 0;
int mapRemovals = 0;
@@ -863,7 +863,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
std::atomic<int>& allRemovals,
std::scoped_lock<std::recursive_mutex> const&)
{
return std::thread([&, this]() {
return std::thread([&, this] {
// NOLINTBEGIN https://github.com/XRPLF/rippled/issues/7056
int cacheRemovals = 0;
int mapRemovals = 0;
File diff suppressed because it is too large. Load diff
+2 -3
View File
@@ -1,8 +1,7 @@
#pragma once
#include <xrpl/basics/sanitizers.h>
#include <boost/core/type_name.hpp>
#include <xrpl/beast/type_name.h>
#include <exception>
#include <string>
@@ -56,7 +55,7 @@ Throw(Args&&... args)
std::is_convertible_v<E*, std::exception*>, "Exception must derive from std::exception.");
E e(std::forward<Args>(args)...);
logThrow("Throwing exception of type " + boost::core::type_name<E>() + ": " + e.what());
logThrow(std::string("Throwing exception of type " + beast::typeName<E>() + ": ") + e.what());
throw std::move(e);
}
-200
View File
@@ -1,200 +0,0 @@
#pragma once
#include <concepts>
#include <type_traits>
#include <utility>
namespace xrpl {
/**
* Opt-in bitwise operators for scoped enumerations.
*
* Provides `&`, `|`, `^`, `~` and the corresponding compound assignment forms
* for any scoped enumeration with an unsigned underlying type that opts in by
* specializing @ref OptIn:
*
* @code
* namespace xrpl {
*
* enum class MyFlags : std::uint32_t { a = 1, b = 2, c = 4 };
*
* template <>
* struct enum_bitops::OptIn<MyFlags> : std::true_type {};
*
* } // namespace xrpl
* @endcode
*
* The operators are constexpr and noexcept, and return the enumeration type.
*
* @par Where the specialization may be declared
* In namespace xrpl, or at global scope with full qualification, and after
* the enumeration is defined but before the operators are first used. This
* cannot be done in a nested namespace or at class scope; for enumerations
* nested in a class this means after the class definition.
*
* @par Where the operators are found
* The operators are declared in namespace xrpl, so argument-dependent lookup
* will find them only for enumerations declared in xrpl or nested in a class
* declared directly in xrpl. Enumerations in a nested namespace will only be
* found from code inside xrpl, and only if no enclosing scope declares an
* operator of the same name; elsewhere they require using-declarations.
*/
namespace enum_bitops {
/**
* Types that may be opted in to the bitwise operators.
*
* Satisfied by a scoped enumeration whose underlying type is an unsigned
* integer type.
*
* @tparam T The type to test.
*/
template <typename T>
concept Eligible = std::is_scoped_enum_v<T> && std::unsigned_integral<std::underlying_type_t<T>>;
/**
* Opt-in switch for the bitwise operators.
*
* The primary template derives from std::false_type. Specialize it to derive
* from std::true_type to enable the operators for an @ref Eligible enumeration.
*
* @tparam T The scoped enumeration to opt in.
*/
template <Eligible T>
struct OptIn : std::false_type
{
};
/**
* Enumerations for which the bitwise operators are enabled.
*
* Satisfied when @p T satisfies @ref Eligible and @ref OptIn has been
* specialized for it to derive from std::true_type.
*
* @tparam T The type to test.
*/
template <typename T>
concept Candidate = Eligible<T> && OptIn<T>::value;
} // namespace enum_bitops
// These are declared in xrpl, not in enum_bitops, so that argument-dependent
// lookup finds them for enumerations whose associated namespace is xrpl.
/**
* @name Bitwise operators for opted-in scoped enumerations
*
* Each operator applies the corresponding built-in operator to the
* underlying values and converts the result back to the enumeration type.
* Both operands must have the same enumeration type; there is no implicit
* conversion to or from the underlying type.
*/
/** @{ */
/**
* Bitwise AND.
*
* @param lhs The left operand.
* @param rhs The right operand.
* @return The bits set in both @p lhs and @p rhs.
*/
template <enum_bitops::Candidate T>
constexpr T
operator&(T lhs, T rhs) noexcept
{
return static_cast<T>(std::to_underlying(lhs) & std::to_underlying(rhs));
}
/**
* Bitwise OR.
*
* @param lhs The left operand.
* @param rhs The right operand.
* @return The bits set in @p lhs, in @p rhs, or in both.
*/
template <enum_bitops::Candidate T>
constexpr T
operator|(T lhs, T rhs) noexcept
{
return static_cast<T>(std::to_underlying(lhs) | std::to_underlying(rhs));
}
/**
* Bitwise exclusive OR.
*
* @param lhs The left operand.
* @param rhs The right operand.
* @return The bits set in exactly one of @p lhs and @p rhs.
*/
template <enum_bitops::Candidate T>
constexpr T
operator^(T lhs, T rhs) noexcept
{
return static_cast<T>(std::to_underlying(lhs) ^ std::to_underlying(rhs));
}
/**
* Bitwise complement.
*
* The result has every bit of the underlying type that is clear in
* @p val, including bits that no enumerator names. It is intended for
* clearing flags, as in `flags & ~flag`.
*
* @param val The operand.
* @return The complement of @p val.
*/
template <enum_bitops::Candidate T>
constexpr T
operator~(T val) noexcept
{
return static_cast<T>(~std::to_underlying(val));
}
/**
* Bitwise AND assignment.
*
* @param lhs The value to modify.
* @param rhs The right operand.
* @return A reference to @p lhs.
*/
template <enum_bitops::Candidate T>
constexpr T&
operator&=(T& lhs, T rhs) noexcept
{
lhs = lhs & rhs;
return lhs;
}
/**
* Bitwise OR assignment.
*
* @param lhs The value to modify.
* @param rhs The right operand.
* @return A reference to @p lhs.
*/
template <enum_bitops::Candidate T>
constexpr T&
operator|=(T& lhs, T rhs) noexcept
{
lhs = lhs | rhs;
return lhs;
}
/**
* Bitwise exclusive OR assignment.
*
* @param lhs The value to modify.
* @param rhs The right operand.
* @return A reference to @p lhs.
*/
template <enum_bitops::Candidate T>
constexpr T&
operator^=(T& lhs, T rhs) noexcept
{
lhs = lhs ^ rhs;
return lhs;
}
/** @} */
} // namespace xrpl
@@ -17,7 +17,7 @@
namespace xrpl {
template <typename Key>
static std::size_t
std::size_t
extract(Key const& key)
{
return key;
@@ -365,7 +365,7 @@ public:
size() const
{
std::size_t ret = 0;
for (auto& p : map_)
for (auto const& p : map_)
ret += p.size();
return ret;
}
+46 -151
View File
@@ -2,212 +2,107 @@
#include <xrpl/beast/utility/instrumentation.h> // IWYU pragma: keep
#include <concepts>
#include <cstdint>
#include <limits>
#include <memory>
#include <type_traits>
#include <utility>
namespace xrpl {
/** Every value of @p Src can be represented by @p Dest.
// safe_cast adds compile-time checks to a static_cast to ensure that
// the destination can hold all values of the source. This is particularly
// handy when the source or destination is an enumeration type.
Given two integral types, the cast is safe when the destination can
hold every possible value of the source.
template <class Src, class Dest>
concept SafeToCast = (std::is_integral_v<Src> && std::is_integral_v<Dest>) &&
(std::is_signed_v<Src> || std::is_unsigned_v<Dest>) &&
(std::is_signed_v<Src> != std::is_signed_v<Dest> ? sizeof(Dest) > sizeof(Src)
: sizeof(Dest) >= sizeof(Src));
Comparing the bounds requires care: we use @c std::cmp_less_equal and
@c std::cmp_greater_equal; the plain relational operators would apply
arithmetic conversions, resulting in incorrect results when comparing
across signedness.
Because @c std::cmp_* requires standard signed or unsigned integer
arguments, which excludes character types and bool, we first widen
all type bounds to the maximum-width integer type while preserving
signedness.
@note Extended integer types, like __int128 on gcc, cannot be safely
widened to a standard integer type, so the concept will reject
them.
*/
template <typename Src, typename Dest>
concept SafeToCast = std::is_integral_v<Src> && std::is_integral_v<Dest> && []() consteval {
using WideSrc = std::conditional_t<std::is_signed_v<Src>, std::intmax_t, std::uintmax_t>;
using WideDest = std::conditional_t<std::is_signed_v<Dest>, std::intmax_t, std::uintmax_t>;
// Note: this guard must be an evaluated branch, and not a
// static_assert. The lambda body is outside the immediate
// context, so a substitution-time failure here would be a
// hard error rather than leaving the concept unsatisfied.
if constexpr (sizeof(Src) > sizeof(WideSrc) || sizeof(Dest) > sizeof(WideDest))
{
return false;
}
else
{
return std::cmp_less_equal(
static_cast<WideDest>(std::numeric_limits<Dest>::min()),
static_cast<WideSrc>(std::numeric_limits<Src>::min())) &&
std::cmp_greater_equal(
static_cast<WideDest>(std::numeric_limits<Dest>::max()),
static_cast<WideSrc>(std::numeric_limits<Src>::max()));
}
}();
/** Compile-time-checked static_cast that rejects non-value preserving casts.
@note There is deliberately no enum-to-enum overload, and no overload
returning the underlying type of an enum. For the latter, use
@c std::to_underlying.
*/
/** @{ */
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
safeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
{
static_assert(
SafeToCast<Src, Dest>, "This cast is not value-preserving. Please use unsafeCast instead.");
std::is_signed_v<Dest> || std::is_unsigned_v<Src>, "Cannot cast signed to unsigned");
constexpr unsigned kNotSame = std::is_signed_v<Dest> != std::is_signed_v<Src>;
static_assert(
sizeof(Dest) >= sizeof(Src) + kNotSame,
"Destination is too small to hold all values of source");
return static_cast<Dest>(s);
}
template <typename Dest, typename Src>
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
safeCast(Src s) noexcept
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
{
return static_cast<Dest>(safeCast<std::underlying_type_t<Dest>>(s));
}
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
template <class Dest, class Src>
constexpr Dest
safeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
{
return safeCast<Dest>(std::to_underlying(s));
return safeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
}
/** @} */
/** Integral-to-integral cast that is known to be narrowing or sign-erasing.
// unsafe_cast explicitly flags a static_cast as not necessarily able to hold
// all values of the source. It includes a compile-time check so that if
// underlying types become safe, it can be converted to a safe_cast.
This explicitly flags a conversion that can lose information for some
values of the source type, where the call site accepts that loss (or
truncation is the intended behavior).
The compile-time check ensures the cast remains "unsafe": if the types
involved later change such that the conversion becomes inherently
value-preserving, the static assertion fires with instructions to
migrate the call site to @ref safeCast.
If the conversion's safety depends on a runtime precondition rather
than on the types, or varies across instantiations of generic code,
use @ref checkedCast instead.
*/
/** @{ */
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
unsafeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
{
static_assert(
!SafeToCast<Src, Dest>, "This cast is value-preserving. Please use safeCast instead.");
!SafeToCast<Src, Dest>,
"Only unsafe if casting signed to unsigned or "
"destination is too small");
return static_cast<Dest>(s);
}
template <typename Dest, typename Src>
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
unsafeCast(Src s) noexcept
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
{
return static_cast<Dest>(unsafeCast<std::underlying_type_t<Dest>>(s));
}
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
template <class Dest, class Src>
constexpr Dest
unsafeCast(Src s) noexcept
{
return unsafeCast<Dest>(std::to_underlying(s));
}
/** @} */
/** Integral-to-integral cast when the caller has performed a bounds check.
This documents that a runtime precondition or external invariant, which
is not necessarily visible to the compiler, guarantees that the requested
conversion is value-preserving for the values that can actually occur.
This is primarily meant for generic code, where the same expression may
be value-preserving for one instantiation but not for another, making
both @ref safeCast and @ref unsafeCast unusable.
Unlike @ref safeCast and @ref unsafeCast, this imposes no static check
on the type relationship: the caller's claim is about runtime values,
not about types.
*/
/** @{ */
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
constexpr Dest
checkedCast(Src s) noexcept
{
return static_cast<Dest>(s);
}
template <typename Dest, typename Src>
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
constexpr Dest
checkedCast(Src s) noexcept
{
return static_cast<Dest>(checkedCast<std::underlying_type_t<Dest>>(s));
}
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
constexpr Dest
checkedCast(Src s) noexcept
{
return checkedCast<Dest>(std::to_underlying(s));
return unsafeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
}
/** @} */
/** Downcast within a class hierarchy, verified in debug builds.
Performs a static_cast down a hierarchy, but in debug builds verifies
via dynamic_cast that the object's dynamic type actually permits the
downcast. Both build modes execute the same conversion; debug builds
merely add the check.
The pointer form passes null through unchanged, as dynamic_cast does.
@note The check requires @p Src to be polymorphic; in release builds
an invalid downcast is undefined behavior on use, exactly as
with a bare static_cast.
*/
/** @{ */
template <class Dest, class Src>
requires(
std::is_pointer_v<Dest> && std::is_polymorphic_v<Src> &&
std::derived_from<std::remove_pointer_t<Dest>, Src> &&
(std::is_const_v<std::remove_pointer_t<Dest>> || !std::is_const_v<Src>) &&
(std::is_volatile_v<std::remove_pointer_t<Dest>> || !std::is_volatile_v<Src>))
requires std::is_pointer_v<Dest>
inline Dest
safeDowncast(Src* s) noexcept
{
XRPL_ASSERT(
s == nullptr || dynamic_cast<Dest>(s) != nullptr, "xrpl::safeDowncast : valid downcast");
#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::safeDowncast : pointer downcast is valid");
return result;
#endif
}
template <class Dest, class Src>
requires(
std::is_lvalue_reference_v<Dest> && std::is_polymorphic_v<Src> &&
std::derived_from<std::remove_reference_t<Dest>, Src>)
requires std::is_lvalue_reference_v<Dest>
inline Dest
safeDowncast(Src& s) noexcept
{
return *safeDowncast<std::add_pointer_t<std::remove_reference_t<Dest>>>(std::addressof(s));
#ifndef NDEBUG
XRPL_ASSERT(
dynamic_cast<std::add_pointer_t<std::remove_reference_t<Dest>>>(&s) != nullptr,
"xrpl::safeDowncast : reference downcast is valid");
#endif
return static_cast<Dest>(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
}
/** @} */
} // namespace xrpl
+152 -208
View File
@@ -4,215 +4,88 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <boost/predef/architecture.h>
#include <atomic>
#include <concepts>
#include <limits>
#include <type_traits>
#if BOOST_ARCH_X86
#ifndef __aarch64__
#include <immintrin.h>
#endif
namespace xrpl {
/** An unsigned integral type suitable for use as a spinlock.
The type must be always lock-free when wrapped in std::atomic, so
that lock operations cannot themselves take a (library-level) lock.
*/
template <typename T>
concept SpinlockValueType = std::is_unsigned_v<T> && std::atomic<T>::is_always_lock_free;
/** A spinlock value type that additionally supports the atomic bitwise
operations required to pack multiple locks into a single integer.
*/
template <typename T>
concept PackedSpinlockValueType = SpinlockValueType<T> && requires(std::atomic<T>& a, T v) {
{ a.fetch_or(v) } -> std::same_as<T>;
{ a.fetch_and(v) } -> std::same_as<T>;
};
namespace detail {
/** Inform the processor that we are in a tight spin-wait loop.
Spinlocks caught in tight loops can result in the processor's pipeline
filling up with comparison operations, resulting in a misprediction at
the time the lock is finally acquired, necessitating pipeline flushing
which is ridiculously expensive and results in very high latency.
This function instructs the processor to "pause" for some architecture
specific amount of time, to prevent this.
/**
* Inform the processor that we are in a tight spin-wait loop.
*
* Spinlocks caught in tight loops can result in the processor's pipeline
* filling up with comparison operations, resulting in a misprediction at
* the time the lock is finally acquired, necessitating pipeline flushing
* which is ridiculously expensive and results in very high latency.
*
* This function instructs the processor to "pause" for some architecture
* specific amount of time, to prevent this.
*/
inline void
spinPause() noexcept
{
#if BOOST_ARCH_X86
_mm_pause();
#elif BOOST_ARCH_ARM
asm volatile("yield" ::: "memory");
#ifdef __aarch64__
asm volatile("yield");
#else
#error No implementation available for spinPause to use
_mm_pause();
#endif
}
} // namespace detail
//------------------------------------------------------------------------------
/** Attempt to acquire a spinlock without blocking.
@note This interface is primarily intended for one-shot attempts to
acquire the lock. Avoid calling this function directly from a
loop and use @ref spinLock instead.
@tparam T An unsigned integral type.
@param lock The atomic variable used as the lock.
@return true if the lock was acquired, false if it was already held.
/** @{ */
/**
* Classes to handle arrays of spinlocks packed into a single atomic integer:
*
* Packed spinlocks allow for tremendously space-efficient lock-sharding
* but they come at a cost.
*
* First, the implementation is necessarily low-level and uses advanced
* features like memory ordering and highly platform-specific tricks to
* maximize performance. This imposes a significant and ongoing cost to
* developers.
*
* Second, and perhaps most important, is that the packing of multiple
* locks into a single integer which, albeit space-efficient, also has
* performance implications stemming from data dependencies, increased
* cache-coherency traffic between processors and heavier loads on the
* processor's load/store units.
*
* To be sure, these locks can have advantages but they are definitely
* not general purpose locks and should not be thought of or used that
* way. The use cases for them are likely few and far between; without
* a compelling reason to use them, backed by profiling data, it might
* be best to use one of the standard locking primitives instead. Note
* that in most common platforms, `std::mutex` is so heavily optimized
* that it can, usually, outperform spinlocks.
*
* @tparam T An unsigned integral type (e.g. std::uint16_t)
*/
template <SpinlockValueType T>
[[nodiscard]] bool
spinTryLock(std::atomic<T>& lock) noexcept
{
// A compare-exchange is required here, not an unconditional exchange:
// a failed attempt must not modify the lock word, in case the atomic
// is shared with PackedSpinlock).
T expected = 0;
return lock.compare_exchange_strong(
expected,
std::numeric_limits<T>::max(),
std::memory_order::acquire,
std::memory_order::relaxed);
;
}
/** Acquire a spinlock, blocking until available.
Uses a TTAS (test-and-test-and-set) pattern, so waiters share the cache
line read-only, helping to avoid unnecessary coherency traffic.
@tparam T An unsigned integral type.
@param lock The atomic variable used as the lock.
/**
* A class that grabs a single packed spinlock from an atomic integer.
*
* This class meets the requirements of Lockable:
* https://en.cppreference.com/w/cpp/named_req/Lockable
*/
template <SpinlockValueType T>
void
spinLock(std::atomic<T>& lock) noexcept
{
do
{
// Relaxed ordering is sufficient for the spin: this load is only
// a filter. The acquire on the successful exchange in spinTryLock
// is what synchronizes the critical section.
while (lock.load(std::memory_order::relaxed) != 0)
detail::spinPause();
} while (!spinTryLock(lock));
}
/** Release a spinlock.
@tparam T An unsigned integral type.
@param lock The atomic variable used as the lock.
*/
template <SpinlockValueType T>
void
spinUnlock(std::atomic<T>& lock) noexcept
{
lock.store(0, std::memory_order::release);
}
//------------------------------------------------------------------------------
/** A Lockable interface to a spinlock implemented on top of an atomic.
@tparam T An unsigned integral type.
@note Using `PackedSpinlock` and `Spinlock` against the same underlying
atomic integer is possible but can result in `Spinlock` not being
able to acquire the lock during periods of high contention due to
the way the two locks operate: `Spinlock` spins and tries to grab
all the bits at once, whereas any given `PackedSpinlock` instance
only tries to grab one bit at a time. Caveat emptor.
This class meets the requirements of Lockable:
https://en.cppreference.com/w/cpp/named_req/Lockable
*/
template <SpinlockValueType T>
class Spinlock
{
std::atomic<T>& lock_;
public:
Spinlock(Spinlock const&) = delete;
Spinlock&
operator=(Spinlock const&) = delete;
/** Construct a spinlock handle.
@param lock The atomic integer to spin against.
@note For performance reasons, you should strive to have `lock` be
on a cacheline by itself.
*/
explicit Spinlock(std::atomic<T>& lock) noexcept : lock_(lock)
{
}
[[nodiscard]] bool
try_lock() noexcept // NOLINT(readability-identifier-naming)
{
return spinTryLock(lock_);
}
void
lock() noexcept
{
spinLock(lock_);
}
void
unlock() noexcept
{
spinUnlock(lock_);
}
};
//------------------------------------------------------------------------------
/** A Lockable interface to a packed spinlock implemented on top of an atomic.
Packed spinlocks offer tremendous space-efficient lock-sharding but
they come at a cost.
First, the implementation is necessarily low-level and uses advanced
features like memory ordering and highly platform-specific tricks to
maximize performance. This imposes a significant and ongoing cost to
developers.
Second, and perhaps most important, is that the packing of multiple
locks into a single integer which, albeit space-efficient, also has
performance implications stemming from data dependencies, increased
cache-coherency traffic between processors and heavier loads on the
processor's load/store units.
To be sure, these locks can have advantages but they are definitely
not general purpose locks and should not be thought of or used that
way. The use cases for them are likely few and far between; without
a compelling reason to use them, backed by profiling data, it might
be best to use one of the standard locking primitives instead. Note
that in most common platforms, `std::mutex` is so heavily optimized
that it can, usually, outperform spinlocks.
@tparam T An unsigned integral type (e.g. std::uint16_t)
This class meets the requirements of Lockable:
https://en.cppreference.com/w/cpp/named_req/Lockable
*/
template <PackedSpinlockValueType T>
template <class T>
class PackedSpinlock
{
// clang-format off
static_assert(std::is_unsigned_v<T>);
static_assert(std::atomic<T>::is_always_lock_free);
static_assert(
std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_or(0)), T> &&
std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_and(0)), T>,
"std::atomic<T>::fetch_and(T) and std::atomic<T>::fetch_and(T) are required by packed_spinlock");
// clang-format on
private:
std::atomic<T>& bits_;
T const mask_;
@@ -221,49 +94,120 @@ public:
PackedSpinlock&
operator=(PackedSpinlock const&) = delete;
/** Construct a packed spinlock handle for a single bit.
@param lock The atomic integer inside which the spinlock is packed.
@param index The index of the spinlock this object acquires.
@note For performance reasons, you should strive to have `lock` be
on a cacheline by itself.
/**
* A single spinlock packed inside the specified atomic
*
* @param lock The atomic integer inside which the spinlock is packed.
* @param index The index of the spinlock this object acquires.
*
* @note For performance reasons, you should strive to have `lock` be
* on a cacheline by itself.
*/
PackedSpinlock(std::atomic<T>& lock, int index) noexcept
: bits_(lock), mask_([index]() {
XRPL_ASSERT(
index >= 0 && index < std::numeric_limits<T>::digits,
"xrpl::PackedSpinlock::PackedSpinlock : valid index");
return static_cast<T>(T{1} << index);
}())
PackedSpinlock(std::atomic<T>& lock, int index) : bits_(lock), mask_(static_cast<T>(1) << index)
{
XRPL_ASSERT(
index >= 0 && (mask_ != 0),
"xrpl::PackedSpinlock::PackedSpinlock : valid index and mask");
}
[[nodiscard]] bool
try_lock() // NOLINT(readability-identifier-naming)
{
return (bits_.fetch_or(mask_, std::memory_order_acquire) & mask_) == 0;
}
void
lock()
{
while (!try_lock())
{
// The use of relaxed memory ordering here is intentional and
// serves to help reduce cache coherency traffic during times
// of contention by avoiding writes that would definitely not
// result in the lock being acquired.
while ((bits_.load(std::memory_order_relaxed) & mask_) != 0)
detail::spinPause();
}
}
void
unlock()
{
bits_.fetch_and(~mask_, std::memory_order_release);
}
};
/**
* A spinlock implemented on top of an atomic integer.
*
* @note Using `packed_spinlock` and `spinlock` against the same underlying
* atomic integer can result in `spinlock` not being able to actually
* acquire the lock during periods of high contention, because of how
* the two locks operate: `spinlock` will spin trying to grab all the
* bits at once, whereas any given `packed_spinlock` will only try to
* grab one bit at a time. Caveat emptor.
*
* This class meets the requirements of Lockable:
* https://en.cppreference.com/w/cpp/named_req/Lockable
*/
template <class T>
class Spinlock
{
static_assert(std::is_unsigned_v<T>);
static_assert(std::atomic<T>::is_always_lock_free);
private:
std::atomic<T>& lock_;
public:
Spinlock(Spinlock const&) = delete;
Spinlock&
operator=(Spinlock const&) = delete;
/**
* Grabs the
*
* @param lock The atomic integer to spin against.
*
* @note For performance reasons, you should strive to have `lock` be
* on a cacheline by itself.
*/
Spinlock(std::atomic<T>& lock) : lock_(lock)
{
}
[[nodiscard]] bool
try_lock() noexcept // NOLINT(readability-identifier-naming)
try_lock() // NOLINT(readability-identifier-naming)
{
return (bits_.fetch_or(mask_, std::memory_order::acquire) & mask_) == 0;
T expected = 0;
return lock_.compare_exchange_weak(
expected,
std::numeric_limits<T>::max(),
std::memory_order_acquire,
std::memory_order_relaxed);
}
void
lock() noexcept
lock()
{
do
while (!try_lock())
{
// The use of relaxed memory ordering here is intentional and
// serves to help reduce cache coherency traffic during times
// of contention by avoiding writes that are unlikely to grab
// the requested lock.
while ((bits_.load(std::memory_order::relaxed) & mask_) != 0)
// of contention by avoiding writes that would definitely not
// result in the lock being acquired.
while (lock_.load(std::memory_order_relaxed) != 0)
detail::spinPause();
} while (!try_lock());
}
}
void
unlock() noexcept
unlock()
{
bits_.fetch_and(~mask_, std::memory_order::release);
lock_.store(0, std::memory_order_release);
}
};
/** @} */
} // namespace xrpl
@@ -1604,7 +1604,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::erase(
beast::detail::AgedContainerIterator<IsConst, Iterator> pos)
requires(!IsBoostReverseIterator<Iterator>::value)
{
unlinkAndDeleteElement(&*((pos++).iterator()));
unlinkAndDeleteElement(&*(pos++).iterator());
return beast::detail::AgedContainerIterator<false, Iterator>(pos.iterator());
}
@@ -1617,7 +1617,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::erase(
requires(!IsBoostReverseIterator<Iterator>::value)
{
for (; first != last;)
unlinkAndDeleteElement(&*((first++).iterator()));
unlinkAndDeleteElement(&*(first++).iterator());
return beast::detail::AgedContainerIterator<false, Iterator>(first.iterator());
}
@@ -2404,7 +2404,7 @@ beast::detail::AgedContainerIterator<false, Iterator>
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::erase(
beast::detail::AgedContainerIterator<IsConst, Iterator> pos)
{
unlinkAndDeleteElement(&*((pos++).iterator()));
unlinkAndDeleteElement(&*(pos++).iterator());
return beast::detail::AgedContainerIterator<false, Iterator>(pos.iterator());
}
@@ -2424,7 +2424,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
beast::detail::AgedContainerIterator<IsConst, Iterator> last)
{
for (; first != last;)
unlinkAndDeleteElement(&*((first++).iterator()));
unlinkAndDeleteElement(&*(first++).iterator());
return beast::detail::AgedContainerIterator<false, Iterator>(first.iterator());
}
@@ -2536,7 +2536,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
return false;
for (auto iter(cbegin()), last(cend()), otherLast(other.cend()); iter != last; ++iter)
{
auto otherIter(other.find(extract(*iter)));
auto const otherIter(other.find(extract(*iter)));
if (otherIter == otherLast)
return false;
}
+1 -1
View File
@@ -68,7 +68,7 @@ struct LexicalCast<Out, std::string_view>
if (first != last && *first == '+')
++first;
auto ret = std::from_chars(first, last, out);
auto const ret = std::from_chars(first, last, out);
return ret.ec == std::errc() && ret.ptr == last;
}
+1 -1
View File
@@ -390,7 +390,7 @@ void
hash_append(Hasher& h, boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept
requires(IsContiguouslyHashable<Key, Hasher>::value)
{
h(&(v.begin()), v.size() * sizeof(Key));
h(&v.begin(), v.size() * sizeof(Key));
}
// tuple
+1 -1
View File
@@ -8,7 +8,7 @@ namespace beast::insight {
class HookImpl : public std::enable_shared_from_this<HookImpl>
{
public:
using HandlerType = std::function<void(void)>;
using HandlerType = std::function<void()>;
virtual ~HookImpl() = 0;
};
+2 -2
View File
@@ -61,7 +61,7 @@ isMulticast(Address const& addr)
inline bool
isPrivate(Address const& addr)
{
return (addr.is_v4()) ? isPrivate(addr.to_v4()) : isPrivate(addr.to_v6());
return addr.is_v4() ? isPrivate(addr.to_v4()) : isPrivate(addr.to_v6());
}
/**
@@ -70,7 +70,7 @@ isPrivate(Address const& addr)
inline bool
isPublic(Address const& addr)
{
return (addr.is_v4()) ? isPublic(addr.to_v4()) : isPublic(addr.to_v6());
return addr.is_v4() ? isPublic(addr.to_v4()) : isPublic(addr.to_v6());
}
} // namespace ip
+1 -1
View File
@@ -83,7 +83,7 @@ trimRight(String const& s)
{
using std::begin;
using std::end;
auto first(begin(s));
auto const first(begin(s));
auto last(end(s));
last = trimRight(first, last);
return {first, last};
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include <cstdlib>
#include <string>
#include <type_traits>
#include <typeinfo>
#ifndef _MSC_VER
#include <cxxabi.h>
#endif
namespace beast {
template <typename T>
std::string
typeName()
{
using TR = std::remove_reference_t<T>;
std::string name = typeid(TR).name();
#ifndef _MSC_VER
if (auto s = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, nullptr))
{
name = s;
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
std::free(s);
}
#endif
if (std::is_const_v<TR>)
name += " const";
if (std::is_volatile_v<TR>)
name += " volatile";
if (std::is_lvalue_reference_v<T>)
{
name += '&';
}
else if (std::is_rvalue_reference_v<T>)
{
name += "&&";
}
return name;
}
} // namespace beast
+1 -1
View File
@@ -32,7 +32,7 @@ public:
Module,
// Match nothing (used internally)
None
None,
};
private:
+1 -1
View File
@@ -20,7 +20,7 @@ namespace beast::unit_test {
namespace detail {
template <class String>
static std::string
std::string
makeReason(String const& reason, char const* file, int line)
{
std::string s(reason);
+3 -3
View File
@@ -47,8 +47,8 @@ public:
template <class F, class... Args>
explicit Thread(Suite& s, F&& f, Args&&... args) : s_(&s)
{
std::function<void(void)> b = [f = std::forward<F>(f),
... args = std::forward<Args>(args)]() mutable {
std::function<void()> b = [f = std::forward<F>(f),
... args = std::forward<Args>(args)] mutable {
std::invoke(f, args...);
};
t_ = std::thread(&Thread::run, this, std::move(b));
@@ -94,7 +94,7 @@ public:
private:
void
run(std::function<void(void)> f)
run(std::function<void()> f)
{
try
{
+1 -1
View File
@@ -24,7 +24,7 @@ enum class Severity : std::uint8_t {
Fatal = 5,
Disabled = 6,
None = Disabled
None = Disabled,
};
/**
+109 -36
View File
@@ -2,24 +2,26 @@
#pragma once
#include <compare>
#include <concepts>
namespace beast {
/**
* Zero allows classes to offer efficient comparisons to zero.
*
* Zero is a struct to allow classes to efficiently compare with zero without
* requiring an rvalue construction.
*
* It's often the case that we have classes which combine a number and a unit.
* In such cases, comparisons like t > 0 or t != 0 make sense, but comparisons
* like t > 1 or t != 1 do not. Comparing against kZero expresses exactly that,
* without constructing a T.
* like t > 1 or t != 1 do not.
*
* A type T participates if either `t.signum()` or an unqualified `signum(t)`
* found by argument-dependent lookup returns an integer that is negative,
* zero, or positive according to the sign of t. Both `t == kZero` and
* `kZero == t` work, as do all six relational operators in either order.
* The class Zero allows such comparisons to be easily made.
*
* The comparing class T either needs to have a method called signum() which
* returns a positive number, 0, or a negative; or there needs to be a signum
* function which resolves in the namespace which takes an instance of T and
* returns a positive, zero or negative number.
*/
struct Zero
{
explicit Zero() = default;
@@ -28,44 +30,115 @@ struct Zero
inline constexpr Zero kZero{};
/**
* Default implementation of signum: call the member function.
* Default implementation of signum calls the method on the class.
*/
template <class T>
requires requires(T const& t) {
{ t.signum() } -> std::integral;
}
[[nodiscard]] constexpr auto
signum(T const& t) noexcept(noexcept(t.signum()))
template <typename T>
auto
signum(T const& t)
{
return t.signum();
}
namespace detail {
namespace detail::zero_helper {
/**
* A type with a usable signum: either the member-based default above, or a
* `signum(t)` overload in T's own namespace, found by ADL. A user overload
* that is a better match than the template wins, as usual.
*/
// For argument dependent lookup to function properly, calls to signum must
// be made from a namespace that does not include overloads of the function..
template <class T>
concept HasSignum = requires(T const& t) {
{ signum(t) } -> std::integral;
};
} // namespace detail
template <detail::HasSignum T>
[[nodiscard]] constexpr bool
operator==(T const& t, Zero) noexcept(noexcept(signum(t)))
auto
callSignum(T const& t)
{
return signum(t) == 0;
return signum(t);
}
template <detail::HasSignum T>
[[nodiscard]] constexpr std::strong_ordering
operator<=>(T const& t, Zero) noexcept(noexcept(signum(t)))
} // namespace detail::zero_helper
// Handle operators where T is on the left side using signum.
template <typename T>
bool
operator==(T const& t, Zero)
{
return signum(t) <=> 0;
return detail::zero_helper::callSignum(t) == 0;
}
template <typename T>
bool
operator!=(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) != 0;
}
template <typename T>
bool
operator<(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) < 0;
}
template <typename T>
bool
operator>(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) > 0;
}
template <typename T>
bool
operator>=(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) >= 0;
}
template <typename T>
bool
operator<=(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) <= 0;
}
// Handle operators where T is on the right side by
// reversing the operation, so that T is on the left side.
template <typename T>
bool
operator==(Zero, T const& t)
{
return t == kZero;
}
template <typename T>
bool
operator!=(Zero, T const& t)
{
return t != kZero;
}
template <typename T>
bool
operator<(Zero, T const& t)
{
return t > kZero;
}
template <typename T>
bool
operator>(Zero, T const& t)
{
return t < kZero;
}
template <typename T>
bool
operator>=(Zero, T const& t)
{
return t <= kZero;
}
template <typename T>
bool
operator<=(Zero, T const& t)
{
return t >= kZero;
}
} // namespace beast
+1 -1
View File
@@ -18,7 +18,7 @@ enum class Type : std::uint8_t {
PrefixSha256 = 1,
ThresholdSha256 = 2,
RsaSha256 = 3,
Ed25519Sha256 = 4
Ed25519Sha256 = 4,
};
class Condition
@@ -49,7 +49,7 @@ public:
using namespace der;
auto p = parsePreamble(s, ec);
auto const p = parsePreamble(s, ec);
if (ec)
return nullptr;
+1 -1
View File
@@ -21,7 +21,7 @@ enum class Error {
UnexpectedTag,
LongTag,
LargeSize,
PreimageTooLong
PreimageTooLong,
};
std::error_code
+1 -1
View File
@@ -396,7 +396,7 @@ inline bool
getIfExists<bool>(Section const& section, std::string const& name, bool& v)
{
int intVal = 0;
auto stat = getIfExists(section, name, intVal);
auto const stat = getIfExists(section, name, intVal);
if (stat)
v = bool(intVal);
return stat;
+2 -2
View File
@@ -93,14 +93,14 @@ public:
void
check(std::vector<TxID> accepted, Predicate&& pred)
{
auto acceptTxid = accepted.begin();
auto const acceptTxid = accepted.begin();
auto const ae = accepted.end();
std::sort(acceptTxid, ae);
// We want to remove all tracking entries for transactions that were
// accepted as well as those which match the predicate.
auto i = removeIfIntersectOrMatch(
auto const i = removeIfIntersectOrMatch(
tracker_.begin(),
tracker_.end(),
accepted.begin(),
+10 -9
View File
@@ -779,7 +779,7 @@ Consensus<Adaptor>::peerProposalInternal(
{
// update current position
auto peerPosIt = currPeerPositions_.find(peerID);
auto const peerPosIt = currPeerPositions_.find(peerID);
if (peerPosIt != currPeerPositions_.end())
{
@@ -1110,7 +1110,7 @@ Consensus<Adaptor>::checkLedger(std::unique_ptr<std::stringstream> const& clog)
{
CLOG(clog) << "checkLedger. ";
auto netLgr = adaptor_.getPrevLedger(prevLedgerID_, previousLedger_, mode_.get());
auto const netLgr = adaptor_.getPrevLedger(prevLedgerID_, previousLedger_, mode_.get());
CLOG(clog) << "network ledgerid " << netLgr << ", "
<< "previous ledger " << prevLedgerID_ << ". ";
@@ -1159,8 +1159,8 @@ Consensus<Adaptor>::phaseOpen(std::unique_ptr<std::stringstream> const& clog)
// it is shortly before ledger close time
bool const anyTransactions = adaptor_.hasOpenTransactions();
auto proposersClosed = currPeerPositions_.size();
auto proposersValidated = adaptor_.proposersValidated(prevLedgerID_);
auto const proposersClosed = currPeerPositions_.size();
auto const proposersValidated = adaptor_.proposersValidated(prevLedgerID_);
openTime_.tick(clock_.now());
@@ -1669,7 +1669,7 @@ Consensus<Adaptor>::haveConsensus(std::unique_ptr<std::stringstream> const& clog
// CHECKME: should possibly count unacquired TX sets as disagreeing
int agree = 0, disagree = 0;
auto ourPosition = result_->position.position();
auto const ourPosition = result_->position.position();
// Count number of agreements/disagreements with our position
for (auto const& [nodeId, peerPos] : currPeerPositions_)
@@ -1686,7 +1686,7 @@ Consensus<Adaptor>::haveConsensus(std::unique_ptr<std::stringstream> const& clog
++disagree;
}
}
auto currentFinished = adaptor_.proposersFinished(previousLedger_, prevLedgerID_);
auto const currentFinished = adaptor_.proposersFinished(previousLedger_, prevLedgerID_);
JLOG(j_.debug()) << "Checking for TX consensus: agree=" << agree << ", disagree=" << disagree;
@@ -1812,7 +1812,7 @@ Consensus<Adaptor>::createDisputes(TxSetT const& o, std::unique_ptr<std::strings
<< ". ";
JLOG(j_.debug()) << "createDisputes " << result_->txns.id() << " to " << o.id();
auto differences = result_->txns.compare(o);
auto const differences = result_->txns.compare(o);
int dc = 0;
@@ -1826,7 +1826,7 @@ Consensus<Adaptor>::createDisputes(TxSetT const& o, std::unique_ptr<std::strings
"xrpl::Consensus::createDisputes : has disputed transactions");
TxT const tx = inThisSet ? result_->txns.find(txId) : o.find(txId);
auto txID = tx.id();
auto const txID = tx.id();
if (result_->disputes.find(txID) != result_->disputes.end())
continue;
@@ -1837,7 +1837,8 @@ Consensus<Adaptor>::createDisputes(TxSetT const& o, std::unique_ptr<std::strings
tx,
result_->txns.exists(txID),
std::max(prevProposers_, currPeerPositions_.size()),
j_};
j_,
};
// Update all of the available peer's votes on the disputed transaction
for (auto const& [nodeId, peerPos] : currPeerPositions_)
+16 -8
View File
@@ -145,18 +145,26 @@ struct ConsensusParms
std::map<AvalancheState, AvalancheCutoff> const avalancheCutoffs{
// {state, {time, percent, nextState}},
// Initial state: 50% of nodes must vote yes
{AvalancheState::Init,
{.consensusTime = 0, .consensusPct = 50, .next = AvalancheState::Mid}},
{
AvalancheState::Init,
{.consensusTime = 0, .consensusPct = 50, .next = AvalancheState::Mid},
},
// mid-consensus starts after 50% of the previous round time, and
// requires 65% yes
{AvalancheState::Mid,
{.consensusTime = 50, .consensusPct = 65, .next = AvalancheState::Late}},
{
AvalancheState::Mid,
{.consensusTime = 50, .consensusPct = 65, .next = AvalancheState::Late},
},
// late consensus starts after 85% time, and requires 70% yes
{AvalancheState::Late,
{.consensusTime = 85, .consensusPct = 70, .next = AvalancheState::Stuck}},
{
AvalancheState::Late,
{.consensusTime = 85, .consensusPct = 70, .next = AvalancheState::Stuck},
},
// we're stuck after 2x time, requires 95% yes votes
{AvalancheState::Stuck,
{.consensusTime = 200, .consensusPct = 95, .next = AvalancheState::Stuck}},
{
AvalancheState::Stuck,
{.consensusTime = 200, .consensusPct = 95, .next = AvalancheState::Stuck},
},
};
/**
+3 -3
View File
@@ -59,7 +59,7 @@ enum class ConsensusMode {
* if we entered the round observing, but is used to indicate we did
* have the wrongLedger at some point.
*/
SwitchedLedger
SwitchedLedger,
};
inline std::string
@@ -222,7 +222,7 @@ medianCloseOffset(ConsensusCloseTimes const& times)
// Accumulate weight in time order; the first bin to reach halfWeight is
// the (lower) weighted median. Returns true once that bin is found.
auto step = [&](time_point t, std::int64_t w) {
auto const step = [&](time_point t, std::int64_t w) {
XRPL_ASSERT(tally < halfWeight, "xrpl::medianCloseOffset::step : median not yet found");
tally += w;
if (tally >= halfWeight)
@@ -267,7 +267,7 @@ enum class ConsensusState {
No, ///< We do not have consensus
MovedOn, ///< The network has consensus without us
Expired, ///< Consensus time limit has hard-expired
Yes ///< We have consensus along with the network
Yes, ///< We have consensus along with the network
};
/**
+1 -1
View File
@@ -266,7 +266,7 @@ template <class Tx, class NodeId>
void
DisputedTx<Tx, NodeId>::unVote(NodeId const& peer)
{
auto it = votes_.find(peer);
auto const it = votes_.find(peer);
if (it != votes_.end())
{
+1 -1
View File
@@ -211,7 +211,7 @@ struct Node
void
erase(Node const* child)
{
auto it = std::ranges::find_if(
auto const it = std::ranges::find_if(
children, [child](std::unique_ptr<Node> const& curr) { return curr.get() == child; });
XRPL_ASSERT(it != children.end(), "xrpl::Node::erase : valid input");
std::swap(*it, children.back());
+9 -9
View File
@@ -184,7 +184,7 @@ enum class ValStatus {
/**
* Multiple validations by a validator for different ledgers
*/
Conflicting
Conflicting,
};
inline std::string
@@ -366,7 +366,7 @@ private:
removeTrie(std::scoped_lock<Mutex> const&, NodeID const& nodeID, Validation const& val)
{
{
auto it = acquiring_.find(std::make_pair(val.seq(), val.ledgerID()));
auto const it = acquiring_.find(std::make_pair(val.seq(), val.ledgerID()));
if (it != acquiring_.end())
{
it->second.erase(nodeID);
@@ -375,7 +375,7 @@ private:
}
}
{
auto it = lastLedger_.find(nodeID);
auto const it = lastLedger_.find(nodeID);
if (it != lastLedger_.end() && it->second.id() == val.ledgerID())
{
trie_.remove(it->second);
@@ -443,7 +443,7 @@ private:
// Clear any prior acquiring ledger for this node
if (prior)
{
auto it = acquiring_.find(*prior);
auto const it = acquiring_.find(*prior);
if (it != acquiring_.end())
{
it->second.erase(nodeID);
@@ -455,7 +455,7 @@ private:
checkAcquired(lock);
std::pair<Seq, ID> const valPair{val.seq(), val.ledgerID()};
auto it = acquiring_.find(valPair);
auto const it = acquiring_.find(valPair);
if (it != acquiring_.end())
{
it->second.insert(nodeID);
@@ -529,7 +529,7 @@ private:
}
else
{
auto cit = typename decltype(current_)::const_iterator{it};
auto const cit = typename decltype(current_)::const_iterator{it};
// contains a live record
f(cit->first, cit->second);
++it;
@@ -554,7 +554,7 @@ private:
void
byLedger(std::scoped_lock<Mutex> const&, ID const& ledgerID, Pre&& pre, F&& f)
{
auto it = byLedger_.find(ledgerID);
auto const it = byLedger_.find(ledgerID);
if (it != byLedger_.end())
{
// Update set time since it is being used
@@ -857,7 +857,7 @@ public:
if (!preferred)
{
// fall back to majority over acquiring ledgers
auto it = std::ranges::max_element(acquiring_, [](auto const& a, auto const& b) {
auto const it = std::ranges::max_element(acquiring_, [](auto const& a, auto const& b) {
std::pair<Seq, ID> const& aKey = a.first;
typename HashSet<NodeID>::size_type const& aSize = a.second.size();
std::pair<Seq, ID> const& bKey = b.first;
@@ -937,7 +937,7 @@ public:
return (preferred->first >= minSeq) ? preferred->second : lcl.id();
// Otherwise, rely on peer ledgers
auto it = std::ranges::max_element(peerCounts, [](auto const& a, auto const& b) {
auto const it = std::ranges::max_element(peerCounts, [](auto const& a, auto const& b) {
// Prefer larger counts, then larger ids on ties
// (max_element expects this to return true if a < b)
return std::tie(a.second, a.first) < std::tie(b.second, b.first);
+1 -1
View File
@@ -156,7 +156,7 @@ public:
{
if (!allClosuresDoneCond_.wait_for(lock, wait, [this] { return closureCount_ == 0; }))
{
if (auto stream = j.error())
if (auto const stream = j.error())
stream << name << " waiting for ClosureCounter::join().";
allClosuresDoneCond_.wait(lock, [this] { return closureCount_ == 0; });
}
+2 -2
View File
@@ -54,7 +54,7 @@ JobQueue::Coro::post()
}
// sp keeps 'this' alive
if (jq_.addJob(type_, name_, [this, sp = shared_from_this()]() { resume(); }))
if (jq_.addJob(type_, name_, [this, sp = shared_from_this()] { resume(); }))
{
return true;
}
@@ -130,7 +130,7 @@ inline void
JobQueue::Coro::join()
{
std::unique_lock<std::mutex> lk(mutexRun_);
cv_.wait(lk, [this]() { return !running_; });
cv_.wait(lk, [this] { return !running_; });
}
} // namespace xrpl
+1 -1
View File
@@ -37,7 +37,7 @@ enum class HashRouterFlags : std::uint16_t {
PRIVATE6 = 0x2000,
// Used in apply.cpp
PRIVATE7 = 0x4000,
PRIVATE8 = 0x8000
PRIVATE8 = 0x8000,
};
constexpr HashRouterFlags
+6 -6
View File
@@ -30,12 +30,12 @@ private:
using namespace std::chrono_literals;
int const maxLimit = std::numeric_limits<int>::max();
auto add = [this](
JobType jt,
std::string name,
int limit,
std::chrono::milliseconds avgLatency,
std::chrono::milliseconds peakLatency) {
auto const add = [this](
JobType jt,
std::string name,
int limit,
std::chrono::milliseconds avgLatency,
std::chrono::milliseconds peakLatency) {
XRPL_ASSERT(!map.contains(jt), "xrpl::JobTypes::JobTypes::add : unique job type input");
[[maybe_unused]] auto const inserted =
+4 -3
View File
@@ -177,12 +177,13 @@ measureDurationAndLog(
std::chrono::duration<Rep, Period> maxDelay,
beast::Journal const& journal)
{
auto startTime = std::chrono::high_resolution_clock::now();
auto const startTime = std::chrono::high_resolution_clock::now();
auto result = func();
auto endTime = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
auto const endTime = std::chrono::high_resolution_clock::now();
auto const duration =
std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
if (duration > maxDelay)
{
JLOG(journal.warn()) << actionDescription << " took " << duration.count() << " ms";
+1 -1
View File
@@ -95,7 +95,7 @@ private:
ArraySeparator,
MemberSeparator,
Comment,
Error
Error,
};
class Token
+38 -1
View File
@@ -3,11 +3,13 @@
#include <xrpl/basics/Number.h>
#include <xrpl/json/json_forwards.h>
#include <concepts>
#include <cstring>
#include <iterator>
#include <limits>
#include <map>
#include <string>
#include <string_view>
#include <vector>
/**
@@ -26,7 +28,7 @@ enum class ValueType {
String, ///< UTF-8 string value
Boolean, ///< bool value
Array, ///< array value (ordered list)
Object ///< object value (collection of name/value pairs).
Object, ///< object value (collection of name/value pairs).
};
/**
@@ -198,6 +200,15 @@ public:
*/
Value(StaticString const& value);
Value(std::string const& value);
/**
* @brief Constructs a value from a string view.
*
* The characters are copied, so the view need not outlive the call and need
* not be NUL-terminated.
*
* @param value The characters to copy.
*/
Value(std::string_view value);
Value(bool value);
Value(Value const& other);
~Value();
@@ -472,6 +483,32 @@ toJson(xrpl::Number const& number)
bool
operator==(Value const&, Value const&);
/**
* Compares a value with a string view, reading the value's characters in place
* rather than building a Value from the view.
*
* Constrained to the exact type: a string literal converts equally well to a
* view and to a Value, so a plain overload makes `value == "literal"`
* ambiguous.
*
* @param x The value to compare.
* @param y The characters to compare it against.
* @return Whether `x` is a string whose characters up to its first NUL are
* exactly the characters of `y`.
*/
template <class T>
requires std::same_as<T, std::string_view>
bool
operator==(Value const& x, T y)
{
if (!x.isString())
return false;
// A string `Value` can hold a null pointer, which names no characters, so it equals no view.
char const* const s = x.asCString();
return s != nullptr && std::string_view{s} == y;
}
bool
operator<(Value const&, Value const&);
+1 -1
View File
@@ -165,7 +165,7 @@ public:
beast::Journal j)
{
// Ask implementation what to do
auto actions = doVoting(
auto const actions = doVoting(
lastClosedLedger->rules(),
lastClosedLedger->parentCloseTime(),
getEnabledAmendments(*lastClosedLedger),
+53 -12
View File
@@ -1,7 +1,7 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/enum_bitops.h>
#include <xrpl/basics/safe_cast.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/OwnerCounts.h>
#include <xrpl/ledger/ReadView.h>
@@ -22,39 +22,80 @@
namespace xrpl {
enum class ApplyFlags : std::uint32_t {
None = 0x00,
// Bitwise flag enum with existing operator overloads
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
enum ApplyFlags : std::uint32_t {
TapNone = 0x00,
// This is a local transaction with the
// fail_hard flag set.
FailHard = 0x10,
TapFailHard = 0x10,
// This is not the transaction's last pass
// Transaction can be retried, soft failures allowed
Retry = 0x20,
TapRetry = 0x20,
// Transaction came from a privileged source
Unlimited = 0x400,
TapUnlimited = 0x400,
// Transaction is executing as part of a batch
Batch = 0x800,
TapBatch = 0x800,
// Transaction shouldn't be applied
// Signatures shouldn't be checked
DryRun = 0x1000,
TapDryRun = 0x1000,
// Transaction is being preflighted as the payload of a
// TransactionProposalCreate. Its signatures are collected on-ledger
// afterward, so signature-presence checks (e.g. Batch signer matching)
// are skipped at proposal-creation time (On-Chain Cosigner spec
// §5.3.1.2).
Proposal = 0x2000
TapProposal = 0x2000,
};
template <>
struct enum_bitops::OptIn<ApplyFlags> : std::true_type
constexpr ApplyFlags
operator|(ApplyFlags const& lhs, ApplyFlags const& rhs)
{
};
return safeCast<ApplyFlags>(
safeCast<std::underlying_type_t<ApplyFlags>>(lhs) |
safeCast<std::underlying_type_t<ApplyFlags>>(rhs));
}
static_assert((TapFailHard | TapRetry) == safeCast<ApplyFlags>(0x30u), "ApplyFlags operator |");
static_assert((TapRetry | TapFailHard) == safeCast<ApplyFlags>(0x30u), "ApplyFlags operator |");
constexpr ApplyFlags
operator&(ApplyFlags const& lhs, ApplyFlags const& rhs)
{
return safeCast<ApplyFlags>(
safeCast<std::underlying_type_t<ApplyFlags>>(lhs) &
safeCast<std::underlying_type_t<ApplyFlags>>(rhs));
}
static_assert((TapFailHard & TapRetry) == TapNone, "ApplyFlags operator &");
static_assert((TapRetry & TapFailHard) == TapNone, "ApplyFlags operator &");
constexpr ApplyFlags
operator~(ApplyFlags const& flags)
{
return safeCast<ApplyFlags>(~safeCast<std::underlying_type_t<ApplyFlags>>(flags));
}
static_assert(~TapRetry == safeCast<ApplyFlags>(0xFFFFFFDFu), "ApplyFlags operator ~");
inline ApplyFlags
operator|=(ApplyFlags& lhs, ApplyFlags const& rhs)
{
lhs = lhs | rhs;
return lhs;
}
inline ApplyFlags
operator&=(ApplyFlags& lhs, ApplyFlags const& rhs)
{
lhs = lhs & rhs;
return lhs;
}
//------------------------------------------------------------------------------
+2 -1
View File
@@ -20,7 +20,8 @@ constexpr std::chrono::seconds kLedgerPossibleTimeResolutions[] = {
std::chrono::seconds{30},
std::chrono::seconds{60},
std::chrono::seconds{90},
std::chrono::seconds{120}};
std::chrono::seconds{120},
};
/**
* Initial resolution of ledger close time.
+2 -2
View File
@@ -35,7 +35,7 @@ public:
{
std::scoped_lock const lock(mutex_);
auto it = map_.find(seq);
auto const it = map_.find(seq);
if ((it == map_.end()) || it->second)
{
@@ -88,7 +88,7 @@ public:
std::unique_lock<std::mutex> lock(mutex_);
do
{
auto it = map_.find(seq);
auto const it = map_.find(seq);
if (it == map_.end())
{
+3 -3
View File
@@ -164,7 +164,7 @@ getCandidateLedger(LedgerIndex requested)
areCompatible(
ReadView const& validLedger,
ReadView const& testLedger,
beast::Journal::Stream& s,
beast::Journal::Stream const& s,
char const* reason);
[[nodiscard]] bool
@@ -172,7 +172,7 @@ areCompatible(
UInt256 const& validHash,
LedgerIndex validIndex,
ReadView const& testLedger,
beast::Journal::Stream& s,
beast::Journal::Stream const& s,
char const* reason);
//------------------------------------------------------------------------------
@@ -185,7 +185,7 @@ areCompatible(
dirLink(
ApplyView& view,
AccountID const& owner,
SLE::pointer& object,
SLE::pointer const& object,
SF_UINT64 const& node = sfOwnerNode);
/**
+8 -4
View File
@@ -30,13 +30,17 @@ public:
static constexpr size_t kInitialBufferSize = kilobytes(256);
RawStateTable()
: monotonicResource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
kInitialBufferSize)}
: monotonicResource_{
std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
kInitialBufferSize),
}
, items_{monotonicResource_.get()} {};
RawStateTable(RawStateTable const& rhs)
: monotonicResource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
kInitialBufferSize)}
: monotonicResource_{
std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
kInitialBufferSize),
}
, items_{rhs.items_, monotonicResource_.get()}
, dropsDestroyed_{rhs.dropsDestroyed_} {};
@@ -1,11 +1,17 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <map>
#include <set>
namespace xrpl {
@@ -25,6 +31,52 @@ public:
: Base(keylet::amendments(), view, j)
{
}
/**
* Returns the set of amendments this entry reports as enabled.
*
* @return the set of enabled amendments.
*/
[[nodiscard]] std::set<UInt256>
enabledAmendments() const
{
std::set<UInt256> amendments;
if (this->exists() && (*this)->isFieldPresent(sfAmendments))
{
auto const& v = (*this)->getFieldV256(sfAmendments);
amendments.insert_range(v);
}
return amendments;
}
/**
* Returns a map of amendments that have achieved majority, to the time
* majority was reached.
*
* @return a map of amendment to the time majority was reached.
*/
[[nodiscard]] std::map<UInt256, NetClock::time_point>
majorityAmendments() const
{
std::map<UInt256, NetClock::time_point> ret;
if (this->exists() && (*this)->isFieldPresent(sfMajorities))
{
using TimePoint = NetClock::time_point;
using Duration = TimePoint::duration;
auto const majorities = (*this)->getFieldArray(sfMajorities);
for (auto const& m : majorities)
{
ret[m.getFieldH256(sfAmendment)] = TimePoint(Duration(m.getFieldU32(sfCloseTime)));
}
}
return ret;
}
};
using AmendmentsEntryR = AmendmentsEntry<ReadView>;
+15 -17
View File
@@ -224,7 +224,7 @@ getAMMOfferStartWithTakerGets(
if (nTakerGetsConstraint < *nTakerGets)
nTakerGets = nTakerGetsConstraint;
auto getAmounts = [&pool, &tfee](Number const& nTakerGetsProposed) {
auto const getAmounts = [&pool, &tfee](Number const& nTakerGetsProposed) {
// Round downward to minimize the offer and to maximize the quality.
// This has the most impact when takerGets is integral.
auto const takerGets =
@@ -292,7 +292,7 @@ getAMMOfferStartWithTakerPays(
if (nTakerPaysConstraint < *nTakerPays)
nTakerPays = nTakerPaysConstraint;
auto getAmounts = [&pool, &tfee](Number const& nTakerPaysProposed) {
auto const getAmounts = [&pool, &tfee](Number const& nTakerPaysProposed) {
// Round downward to minimize the offer and to maximize the quality.
// This has the most impact when takerPays is integral.
auto const takerPays =
@@ -353,7 +353,7 @@ changeSpotPriceQuality(
}
if (auto const nTakerPaysPropose = (-b + root2(res)) / (2 * a); nTakerPaysPropose > 0)
{
auto const nTakerPays = [&]() {
auto const nTakerPays = [&] {
// The fee might make the AMM offer quality less than CLOB
// quality. Therefore, AMM offer has to satisfy this constraint:
// o / i >= q. Substituting o with swapAssetIn() gives: i <= O /
@@ -372,8 +372,8 @@ changeSpotPriceQuality(
auto const takerPays =
toAmount<TIn>(getAsset(pool.in), nTakerPays, Number::RoundingMode::Upward);
// should not fail
if (auto amounts = TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
Quality{amounts} < quality &&
auto amounts = TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
if (Quality{amounts} < quality &&
!withinRelativeDistance(Quality{amounts}, quality, Number(1, -7)))
{
JLOG(j.error()) << "changeSpotPriceQuality failed: " << to_string(pool.in) << " "
@@ -382,21 +382,19 @@ changeSpotPriceQuality(
<< " " << to_string(amounts.out);
Throw<std::runtime_error>("changeSpotPriceQuality failed");
}
else
{
JLOG(j.trace()) << "changeSpotPriceQuality succeeded: " << to_string(pool.in) << " "
<< to_string(pool.out) << " "
<< " " << quality << " " << tfee << " " << to_string(amounts.in)
<< " " << to_string(amounts.out);
return amounts;
}
JLOG(j.trace()) << "changeSpotPriceQuality succeeded: " << to_string(pool.in) << " "
<< to_string(pool.out) << " "
<< " " << quality << " " << tfee << " " << to_string(amounts.in) << " "
<< to_string(amounts.out);
return amounts;
}
JLOG(j.trace()) << "changeSpotPriceQuality calc failed: " << to_string(pool.in) << " "
<< to_string(pool.out) << " " << quality << " " << tfee;
return std::nullopt;
}
auto amounts = [&]() {
auto amounts = [&] {
bool const inIntegral = getAsset(pool.in).integral();
bool const outIntegral = getAsset(pool.out).integral();
@@ -872,8 +870,8 @@ deleteAMMAccount(Sandbox& view, Asset const& asset, Asset const& asset2, beast::
*/
void
initializeFeeAuctionVote(
ApplyView& view,
SLE::pointer& ammSle,
ApplyView const& view,
SLE::pointer const& ammSle,
AccountID const& account,
Asset const& lptAsset,
std::uint16_t tfee);
@@ -895,7 +893,7 @@ std::expected<bool, TER>
verifyAndAdjustLPTokenBalance(
Sandbox& sb,
STAmount const& lpTokens,
SLE::pointer& ammSle,
SLE::pointer const& ammSle,
AccountID const& account);
} // namespace xrpl
Loaded 100 of 852 files, more files were not shown because too many files have changed in this diff. Show more