mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-11 14:18:07 +00:00
Compare commits
131 Commits
ripple/len
...
bthomee/rp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db3764b231 | ||
|
|
5795eb3be2 | ||
|
|
8eae0c338e | ||
|
|
f0d66e2da7 | ||
|
|
212621c9a0 | ||
|
|
8e24943093 | ||
|
|
6d6ab2d067 | ||
|
|
00e6407514 | ||
|
|
00c06edffb | ||
|
|
de5053ae0d | ||
|
|
1940ec5c2a | ||
|
|
19c94c73f4 | ||
|
|
cd005ff60d | ||
|
|
578224f2e6 | ||
|
|
3857ce21cd | ||
|
|
aa490df46b | ||
|
|
88c1f1e7ac | ||
|
|
ede8af8191 | ||
|
|
3c24b605a1 | ||
|
|
d343b542f1 | ||
|
|
7acd719bf7 | ||
|
|
b4564d5301 | ||
|
|
60195e6d37 | ||
|
|
3d526d456e | ||
|
|
f05c9f7913 | ||
|
|
c2a4bc3aa1 | ||
|
|
718185e3b2 | ||
|
|
1d7783bb86 | ||
|
|
9fd2c552f5 | ||
|
|
70b8fd301b | ||
|
|
e6564f553d | ||
|
|
ed96e60ce3 | ||
|
|
2ebd745a1b | ||
|
|
cfcbe45b60 | ||
|
|
9cbf78ba99 | ||
|
|
b8d8738f81 | ||
|
|
63c97e719f | ||
|
|
3ac26f23c7 | ||
|
|
ff9410bc56 | ||
|
|
1798262d7a | ||
|
|
cd633b9ac9 | ||
|
|
5c5f7d315a | ||
|
|
0f7493ce50 | ||
|
|
40f61f828a | ||
|
|
108277f4b7 | ||
|
|
651bb207b4 | ||
|
|
c45363fd8b | ||
|
|
f1744cb76e | ||
|
|
bcbaa4df07 | ||
|
|
3cd357949c | ||
|
|
a9027bb997 | ||
|
|
0a6da4de74 | ||
|
|
6a05339c6c | ||
|
|
0c41a87604 | ||
|
|
f5938e4097 | ||
|
|
c7d10c1b60 | ||
|
|
84e2a155b9 | ||
|
|
cbade49976 | ||
|
|
97fbea23cc | ||
|
|
e6055ddbe1 | ||
|
|
a5c76fde2d | ||
|
|
eed6527946 | ||
|
|
ddbc5f1a24 | ||
|
|
97a1824537 | ||
|
|
b44c87613c | ||
|
|
b3bbf50d40 | ||
|
|
53246e5b15 | ||
|
|
7e82b0660f | ||
|
|
8ef1bdd346 | ||
|
|
3a2d19f980 | ||
|
|
04eca6d6c3 | ||
|
|
768aef30dc | ||
|
|
97cddd6721 | ||
|
|
e4dbd7de3a | ||
|
|
646d2ce620 | ||
|
|
6641896e9f | ||
|
|
d6df33ff03 | ||
|
|
1d7669f528 | ||
|
|
30640a626f | ||
|
|
5a5ad8673a | ||
|
|
8f4e9c25d8 | ||
|
|
d8870162eb | ||
|
|
8b1a2282c3 | ||
|
|
0219c01b33 | ||
|
|
00606bec1a | ||
|
|
2bc17c3e73 | ||
|
|
0229c294a9 | ||
|
|
184fe173fb | ||
|
|
16f7b263fd | ||
|
|
f6b51f0b8b | ||
|
|
07b36871c7 | ||
|
|
638ae08eeb | ||
|
|
24be48ee84 | ||
|
|
e20f448a71 | ||
|
|
f6c80fef68 | ||
|
|
a0c12420b5 | ||
|
|
1245254cac | ||
|
|
b60636169a | ||
|
|
d6022fbc4d | ||
|
|
295b74da1c | ||
|
|
faa2bf583f | ||
|
|
da260fa42b | ||
|
|
53788b193d | ||
|
|
227f1b4d9c | ||
|
|
26b66957ec | ||
|
|
54e62a621f | ||
|
|
53628b70c0 | ||
|
|
6fec2c11bf | ||
|
|
04108a030c | ||
|
|
4a4fded2eb | ||
|
|
c8e767afa4 | ||
|
|
00eeb0a005 | ||
|
|
a18839d92d | ||
|
|
8c594c7ed9 | ||
|
|
ebd810b184 | ||
|
|
3e4e56d6bb | ||
|
|
76da5d4475 | ||
|
|
c0d0fd0d97 | ||
|
|
9aebb5ebea | ||
|
|
796f2f8f1e | ||
|
|
b190f2b14f | ||
|
|
551a19b10d | ||
|
|
b0a940a383 | ||
|
|
e302e4eeed | ||
|
|
1a4a40ebb8 | ||
|
|
7f55dd390c | ||
|
|
9403736199 | ||
|
|
6099940c2c | ||
|
|
0db7b766e6 | ||
|
|
ea6226b8b9 | ||
|
|
eae0a35415 |
25
.clang-tidy
25
.clang-tidy
@@ -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,11 @@ 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-trailing-comma,
|
||||
-readability-trivial-switch,
|
||||
-readability-uppercase-literal-suffix
|
||||
"
|
||||
# ---
|
||||
@@ -81,6 +93,10 @@ CheckOptions:
|
||||
bugprone-unsafe-functions.ReportMoreUnsafeFunctions: true
|
||||
bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc
|
||||
|
||||
# New in clang-tidy 23; disabled until the code is updated
|
||||
misc-const-correctness.AnalyzeAutoVariables: false
|
||||
misc-const-correctness.AnalyzeLambdas: false
|
||||
misc-const-correctness.AnalyzeParameters: false
|
||||
misc-include-cleaner.IgnoreHeaders: ".*/(detail|impl)/.*;.*fwd\\.h(pp)?;time.h;stdlib.h;sqlite3.h;netinet/in\\.h;sys/resource\\.h;sys/sysinfo\\.h;linux/sysinfo\\.h;__chrono/.*;bits/.*;_abort\\.h;boost/.*;openssl/obj_mac\\.h"
|
||||
|
||||
readability-braces-around-statements.ShortStatementLines: 2
|
||||
@@ -90,6 +106,13 @@ CheckOptions:
|
||||
readability-identifier-naming.ClassCase: CamelCase
|
||||
readability-identifier-naming.StructCase: CamelCase
|
||||
readability-identifier-naming.UnionCase: CamelCase
|
||||
readability-identifier-naming.TypeAliasCase: CamelCase
|
||||
readability-identifier-naming.TypedefCase: CamelCase
|
||||
# Member type names mandated by std/Boost concepts. Subtrees that need more
|
||||
# than these carry their own .clang-tidy (include/xrpl/{beast,basics,protocol,
|
||||
# peerfinder}, src/test/beast); one-off cases use an inline NOLINT.
|
||||
readability-identifier-naming.TypeAliasIgnoredRegexp: "^(const_iterator|const_reference|difference_type|duration|iterator|iterator_category|key_type|mapped_type|period|pointer|reference|rep|result_type|size_type|time_point|value_type)$"
|
||||
readability-identifier-naming.TypedefIgnoredRegexp: "^(const_iterator|const_reference|difference_type|duration|iterator|iterator_category|key_type|mapped_type|period|pointer|reference|rep|result_type|size_type|time_point|value_type)$"
|
||||
readability-identifier-naming.EnumCase: CamelCase
|
||||
readability-identifier-naming.EnumConstantCase: CamelCase
|
||||
readability-identifier-naming.ScopedEnumConstantCase: CamelCase
|
||||
@@ -122,5 +145,5 @@ CheckOptions:
|
||||
readability-identifier-naming.GlobalFunctionIgnoredRegexp: "^(to_string|hash_append|tuple_hash)$"
|
||||
|
||||
HeaderFilterRegex: '^.*/(tests?|xrpl|xrpld)/.*\.(h|hpp|ipp)$'
|
||||
ExcludeHeaderFilterRegex: '^.*/protocol_autogen/.*\.(h|hpp)$'
|
||||
ExcludeHeaderFilterRegex: '^.*/protocol_autogen/.*\.(h|hpp)$|^.*\.pb\.h$'
|
||||
WarningsAsErrors: "*"
|
||||
|
||||
@@ -64,6 +64,7 @@ words:
|
||||
- blindings
|
||||
- bookdir
|
||||
- Bougalis
|
||||
- bthomee
|
||||
- Britto
|
||||
- Btrfs
|
||||
- Buildx
|
||||
@@ -141,6 +142,7 @@ words:
|
||||
- hwrap
|
||||
- ifndef
|
||||
- inequation
|
||||
- Injectivity
|
||||
- insuf
|
||||
- insuff
|
||||
- invasively
|
||||
@@ -253,6 +255,7 @@ words:
|
||||
- queuable
|
||||
- Raphson
|
||||
- rcflags
|
||||
- reencrypted
|
||||
- replayer
|
||||
- repodata
|
||||
- repomd
|
||||
|
||||
4
.envrc
4
.envrc
@@ -8,3 +8,7 @@ watch_file rust-toolchain.toml
|
||||
watch_dir conan
|
||||
|
||||
use flake
|
||||
|
||||
# Optional, untracked local overrides. To use a different shell, put e.g.
|
||||
# `use flake .#formal-verification` in .envrc.local.
|
||||
source_env_if_exists .envrc.local
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -5,6 +5,18 @@
|
||||
# This file is sorted in reverse chronological order, with the most recent commits at the top.
|
||||
# The commits listed here are ignored by git blame, which is useful for formatting-only commits that would otherwise obscure the history of changes to a file.
|
||||
|
||||
# chore: CamelCase for typedef/using in `clang-tidy` (#8177)
|
||||
97a1824537d20d82d25bf151a37a7a4532ebf085
|
||||
# chore: Rename CamelCase namespaces to snake_case (#7933)
|
||||
06488c1318d96f56d0536251bee08ac85fa7fdd3
|
||||
# style: Unify style for all Doxygen comments (#7776)
|
||||
73b6852a122854140336e6e6bc30a3a4b41aa5fd
|
||||
# style: More clang-tidy identifier renaming (#7290)
|
||||
a830ab10efed8d3e59ef2fc15d66efdf9c6bb0d8
|
||||
# refactor: Rename static constants (#7120)
|
||||
5b6e8b6f93b19c1e3f6a3467a25639031d9d9a53
|
||||
# chore: More fixes for bad renames (#7092)
|
||||
7afdd71a54d562b32a50b29a5aa00bb997dc9053
|
||||
# refactor: Enable clang-tidy `readability-identifier-naming` check (#6571)
|
||||
8995564ed6b9e453e144bb663303072a3c1ba305
|
||||
# refactor: Enable remaining clang-tidy `cppcoreguidelines` checks (#6538)
|
||||
|
||||
27
.github/actions/build-deps/action.yml
vendored
27
.github/actions/build-deps/action.yml
vendored
@@ -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"
|
||||
|
||||
17
.github/actions/release-info/action.yml
vendored
17
.github/actions/release-info/action.yml
vendored
@@ -15,30 +15,25 @@ outputs:
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
# A tag names its own version. Anything else takes it from BuildInfo.cpp and
|
||||
# appends the commit hash as build metadata, joined with a plus sign because a
|
||||
# Conan version cannot contain two hyphens.
|
||||
# A tag names its own version. Anything else is a development build named by
|
||||
# its commit hash, matching what cmake/XrplVersion.cmake derives: the head of
|
||||
# a pull request rather than the merge commit GitHub creates for it.
|
||||
- name: Determine version
|
||||
id: version
|
||||
shell: bash
|
||||
env:
|
||||
IS_TAG: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
REF_NAME: ${{ github.ref_name }}
|
||||
SHA: ${{ github.sha }}
|
||||
SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
run: |
|
||||
if [[ "${IS_TAG}" == "true" ]]; then
|
||||
version="${REF_NAME}"
|
||||
else
|
||||
version="$(awk -F'"' '/versionString =/ { print $2 }' src/libxrpl/protocol/BuildInfo.cpp)"
|
||||
if [[ -z "${version}" ]]; then
|
||||
echo "Unable to read versionString from BuildInfo.cpp." >&2
|
||||
exit 1
|
||||
fi
|
||||
version="${version}+${SHA:0:7}"
|
||||
version="0.0.0-dev+${SHA:0:7}"
|
||||
fi
|
||||
|
||||
echo "version=${version}" | tee -a "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Determine release channel and package release
|
||||
id: release_info
|
||||
uses: XRPLF/actions/release-info@7cc0e4a8d9d0b838f92c48d312856b190341bbba
|
||||
uses: XRPLF/actions/release-info@a9f2eeca6fb3980ba3a84cf68566f1c69ad30674
|
||||
|
||||
@@ -82,7 +82,6 @@ test.app > xrpl.tx
|
||||
test.basics > test.jtx
|
||||
test.basics > xrpl.basics
|
||||
test.basics > xrpl.core
|
||||
test.basics > xrpld.rpc
|
||||
test.basics > xrpl.json
|
||||
test.basics > xrpl.protocol
|
||||
test.beast > xrpl.basics
|
||||
@@ -113,6 +112,7 @@ test.jtx > xrpl.config
|
||||
test.jtx > xrpl.core
|
||||
test.jtx > xrpld.app
|
||||
test.jtx > xrpld.core
|
||||
test.jtx > xrpld.overlay
|
||||
test.jtx > xrpld.rpc
|
||||
test.jtx > xrpl.json
|
||||
test.jtx > xrpl.ledger
|
||||
@@ -174,6 +174,7 @@ test.server > xrpl.basics
|
||||
test.server > xrpl.config
|
||||
test.server > xrpld.app
|
||||
test.server > xrpld.core
|
||||
test.server > xrpld.rpc
|
||||
test.server > xrpl.json
|
||||
test.server > xrpl.protocol
|
||||
test.server > xrpl.server
|
||||
@@ -194,6 +195,9 @@ tests.libxrpl > xrpl.resource
|
||||
tests.libxrpl > xrpl.server
|
||||
tests.libxrpl > xrpl.shamap
|
||||
tests.libxrpl > xrpl.tx
|
||||
tests.xrpld > xrpld.rpc
|
||||
tests.xrpld > xrpl.json
|
||||
tests.xrpld > xrpl.protocol
|
||||
xrpl.conditions > xrpl.basics
|
||||
xrpl.conditions > xrpl.protocol
|
||||
xrpl.config > xrpl.basics
|
||||
@@ -286,10 +290,10 @@ xrpld.perflog > xrpl.basics
|
||||
xrpld.perflog > xrpl.config
|
||||
xrpld.perflog > xrpl.core
|
||||
xrpld.perflog > xrpld.app
|
||||
xrpld.perflog > xrpld.rpc
|
||||
xrpld.perflog > xrpl.json
|
||||
xrpld.perflog > xrpl.nodestore
|
||||
xrpld.perflog > xrpl.protocol
|
||||
xrpld.perflog > xrpl.server
|
||||
xrpld.rpc > xrpl.basics
|
||||
xrpld.rpc > xrpl.config
|
||||
xrpld.rpc > xrpl.core
|
||||
|
||||
40
.github/scripts/releasing/check-merge-back-commits.sh
vendored
Executable file
40
.github/scripts/releasing/check-merge-back-commits.sh
vendored
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Exit the script as soon as an error occurs.
|
||||
set -euo pipefail
|
||||
|
||||
# This script fails if <head> merges a release back into <base>,
|
||||
# but also adds commits that aren't on a release or staging branch, see RELEASING.md.
|
||||
# Merge commits are allowed.
|
||||
# Usage: .github/scripts/releasing/check-merge-back-commits.sh <base> <head>
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echo "Usage: $0 <base> <head>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BASE=$1
|
||||
HEAD=$2
|
||||
|
||||
SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}")
|
||||
# shellcheck source=.github/scripts/releasing/common.sh
|
||||
source "${SCRIPT_DIR}/common.sh"
|
||||
load_release_branches
|
||||
|
||||
# A PR is a merge-back if some of its commits are on a release or staging branch.
|
||||
PR_COUNT=$(git rev-list --no-merges --count "${BASE}..${HEAD}")
|
||||
NEW_COUNT=$(git rev-list --no-merges --count "${BASE}..${HEAD}" --not "${BRANCHES[@]}")
|
||||
if ((NEW_COUNT == PR_COUNT)); then
|
||||
echo "This PR doesn't merge a release back."
|
||||
exit 0
|
||||
fi
|
||||
if ((NEW_COUNT == 0)); then
|
||||
echo "This merge-back adds no commits of its own."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "This PR merges a release back, but also adds commits that aren't on a release or staging branch:"
|
||||
git log --no-merges --format=' %h %s' "${BASE}..${HEAD}" --not "${BRANCHES[@]}"
|
||||
echo
|
||||
echo "Make these changes in a separate PR, see RELEASING.md."
|
||||
exit 1
|
||||
46
.github/scripts/releasing/check-no-copied-release-commits.sh
vendored
Executable file
46
.github/scripts/releasing/check-no-copied-release-commits.sh
vendored
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Exit the script as soon as an error occurs.
|
||||
set -euo pipefail
|
||||
|
||||
# This script fails if the commits in <base>..<head> copy commits from a release or staging branch
|
||||
# (e.g. by rebasing or cherry-picking them) instead of merging that branch, see RELEASING.md.
|
||||
# Commits are compared by patch-id,
|
||||
# and only against release and staging commits that <head> does not already contain.
|
||||
# Usage: .github/scripts/releasing/check-no-copied-release-commits.sh <base> <head>
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echo "Usage: $0 <base> <head>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BASE=$1
|
||||
HEAD=$2
|
||||
|
||||
patch_ids() {
|
||||
git log --no-merges --patch --no-color --no-ext-diff "$@" | git patch-id --stable | sort
|
||||
}
|
||||
|
||||
SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}")
|
||||
# shellcheck source=.github/scripts/releasing/common.sh
|
||||
source "${SCRIPT_DIR}/common.sh"
|
||||
load_release_branches
|
||||
|
||||
# Each line is "<patch-id> <commit>".
|
||||
RELEASE_PATCHES=$(patch_ids "${BRANCHES[@]}" --not "${HEAD}")
|
||||
PR_PATCHES=$(patch_ids "${BASE}..${HEAD}")
|
||||
|
||||
# Each line is "<patch-id> <release commit> <PR commit>".
|
||||
COPIES=$(join <(echo "${RELEASE_PATCHES}") <(echo "${PR_PATCHES}"))
|
||||
if [ -z "${COPIES}" ]; then
|
||||
echo "No copied release commits found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "These commits copy release commits instead of merging them:"
|
||||
while read -r _ RELEASE_COMMIT PR_COMMIT; do
|
||||
echo " $(git log -1 --format='%h %s' "${PR_COMMIT}") (copies ${RELEASE_COMMIT:0:10})"
|
||||
done <<<"${COPIES}"
|
||||
echo
|
||||
echo "Merge the release tag (or branch) instead, see RELEASING.md."
|
||||
exit 1
|
||||
38
.github/scripts/releasing/check-no-merge-back.sh
vendored
Executable file
38
.github/scripts/releasing/check-no-merge-back.sh
vendored
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Exit the script as soon as an error occurs.
|
||||
set -euo pipefail
|
||||
|
||||
# This script fails if <head> contains release or staging commits that <base> does not,
|
||||
# i.e. if <head> merges a release back into <base>.
|
||||
# Used in the merge queue, which squashes PRs and would drop the merge commit, see RELEASING.md.
|
||||
# Usage: .github/scripts/releasing/check-no-merge-back.sh <base> <head>
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echo "Usage: $0 <base> <head>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BASE=$1
|
||||
HEAD=$2
|
||||
|
||||
SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}")
|
||||
# shellcheck source=.github/scripts/releasing/common.sh
|
||||
source "${SCRIPT_DIR}/common.sh"
|
||||
load_release_branches
|
||||
|
||||
RELEASE_COMMITS=$(git rev-list "${BRANCHES[@]}" --not "${BASE}")
|
||||
HEAD_COMMITS=$(git rev-list "${BASE}..${HEAD}")
|
||||
# The release commits in <head>, newest first.
|
||||
MERGED=$(grep -xF -f <(echo "${RELEASE_COMMITS}") <<<"${HEAD_COMMITS}" || true)
|
||||
if [ -z "${MERGED}" ]; then
|
||||
echo "No release commits are merged back."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "This PR merges $(wc -l <<<"${MERGED}" | tr -d ' ') release commits back, e.g.:"
|
||||
head -5 <<<"${MERGED}" | xargs git log --no-walk --format=' %h %s'
|
||||
echo
|
||||
echo "Merge-backs must not go through the merge queue, which squashes them."
|
||||
echo "Fast-forward develop to the PR branch instead, see RELEASING.md."
|
||||
exit 1
|
||||
46
.github/scripts/releasing/check-releases-merged.sh
vendored
Executable file
46
.github/scripts/releasing/check-releases-merged.sh
vendored
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Exit the script as soon as an error occurs.
|
||||
set -euo pipefail
|
||||
|
||||
# This script fails if a final release (a tag like 3.4.0) on a release branch
|
||||
# is not merged back into <develop> within a few days, see RELEASING.md.
|
||||
# Usage: .github/scripts/releasing/check-releases-merged.sh <develop>
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <develop>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEVELOP=$1
|
||||
GRACE_DAYS=3
|
||||
|
||||
mapfile -t MERGED_ARGS < <(git for-each-ref --format='--merged=%(refname)' 'refs/remotes/*/release/*')
|
||||
if [ "${#MERGED_ARGS[@]}" -eq 0 ]; then
|
||||
echo "Error: No release branches found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Tags on a release branch that <develop> does not contain.
|
||||
TAGS=$(git for-each-ref --format='%(refname:short) %(creatordate:unix)' \
|
||||
"${MERGED_ARGS[@]}" --no-merged="${DEVELOP}" 'refs/tags/[0-9]*')
|
||||
|
||||
MISSING=0
|
||||
while read -r TAG CREATED; do
|
||||
if ! [[ "${TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
continue
|
||||
fi
|
||||
if (($(date +%s) - CREATED < GRACE_DAYS * 86400)); then
|
||||
echo "${TAG}: not merged yet, still within the ${GRACE_DAYS}-day grace period."
|
||||
else
|
||||
echo "${TAG}: not merged into develop."
|
||||
MISSING=1
|
||||
fi
|
||||
done <<<"${TAGS}"
|
||||
|
||||
if [ "${MISSING}" -ne 0 ]; then
|
||||
echo
|
||||
echo "Merge the missing releases back into develop, see RELEASING.md."
|
||||
exit 1
|
||||
fi
|
||||
echo "No releases past the grace period are missing from develop."
|
||||
12
.github/scripts/releasing/common.sh
vendored
Normal file
12
.github/scripts/releasing/common.sh
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# shellcheck shell=bash
|
||||
|
||||
# Helpers shared by the release checks in this directory, see RELEASING.md.
|
||||
|
||||
# Sets BRANCHES to all release and staging branches, and fails if there are none.
|
||||
load_release_branches() {
|
||||
mapfile -t BRANCHES < <(git for-each-ref --format='%(refname)' 'refs/remotes/*/release/*' 'refs/remotes/*/staging/*')
|
||||
if [ "${#BRANCHES[@]}" -eq 0 ]; then
|
||||
echo "Error: No release or staging branches found."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
2
.github/scripts/rename/config.sh
vendored
2
.github/scripts/rename/config.sh
vendored
@@ -62,7 +62,7 @@ ${SED_COMMAND} -i 's@ripple/@xrpld/@g' src/test/core/Config_test.cpp
|
||||
${SED_COMMAND} -i 's/Rippled/File/g' src/test/core/Config_test.cpp
|
||||
|
||||
# Restore the old config file name in the code that maintains support for now.
|
||||
${SED_COMMAND} -i 's/kConfigLegacyName = "xrpld.cfg"/kConfigLegacyName = "rippled.cfg"/g' src/xrpld/core/detail/Config.cpp
|
||||
${SED_COMMAND} -i 's/kConfigLegacyName = "xrpld.cfg"/kConfigLegacyName = "rippled.cfg"/g' src/xrpld/core/Config.h
|
||||
|
||||
# Restore an URL.
|
||||
${SED_COMMAND} -i 's/connect-your-xrpld-to-the-xrp-test-net.html/connect-your-rippled-to-the-xrp-test-net.html/g' cfg/xrpld-example.cfg
|
||||
|
||||
2
.github/scripts/rename/docs.sh
vendored
2
.github/scripts/rename/docs.sh
vendored
@@ -90,7 +90,7 @@ ${SED_COMMAND} -i 's/www.ripple.com/www.xrpl.org/g' src/test/protocol/Seed_test.
|
||||
# Restore specific changes.
|
||||
${SED_COMMAND} -i 's@b5efcc/src/xrpld@b5efcc/src/ripple@' include/xrpl/protocol/README.md
|
||||
${SED_COMMAND} -i 's/dbPrefix_ = "xrpldb"/dbPrefix_ = "rippledb"/' src/xrpld/app/misc/SHAMapStoreImp.h # cspell: disable-line
|
||||
${SED_COMMAND} -i 's/kConfigLegacyName = "xrpld.cfg"/kConfigLegacyName = "rippled.cfg"/' src/xrpld/core/detail/Config.cpp
|
||||
${SED_COMMAND} -i 's/kConfigLegacyName = "xrpld.cfg"/kConfigLegacyName = "rippled.cfg"/' src/xrpld/core/Config.h
|
||||
|
||||
popd
|
||||
echo "Renaming complete."
|
||||
|
||||
46
.github/scripts/strategy-matrix/generate.py
vendored
46
.github/scripts/strategy-matrix/generate.py
vendored
@@ -15,6 +15,14 @@ _BASE_CMAKE_ARGS = [
|
||||
"-Drust=ON",
|
||||
]
|
||||
|
||||
# The package formats a config can be packaged as, each with its own
|
||||
# install-test job in reusable-package.yml.
|
||||
PACKAGE_TYPES = ("deb", "rpm")
|
||||
|
||||
# The package name a variant suffixes, as build_pkg.py's BASE_NAME spells it:
|
||||
# the two have to agree, or the artifact globs miss what was built.
|
||||
BASE_NAME = "xrpld"
|
||||
|
||||
# Maps sanitizer names (as used in cmake) to short config-name suffixes.
|
||||
_SANITIZER_SUFFIX: dict[str, str] = {
|
||||
"address": "asan",
|
||||
@@ -62,10 +70,20 @@ def get_cmake_args(build_type: str, extra_args: str) -> str:
|
||||
class PackageConfig:
|
||||
"""The 'package' map of a config whose binaries are also packaged."""
|
||||
|
||||
type: str # "deb" or "rpm"; has to match what the image provides
|
||||
type: str # has to match what the image provides
|
||||
# The packaging container image: a vanilla distro image, not the nix image
|
||||
# the config itself builds in.
|
||||
image: str
|
||||
# A flavour of the package, named xrpld-<variant>, for a config whose
|
||||
# binaries are not the plain release build. A variant needs no counterpart
|
||||
# in the other format.
|
||||
variant: str = ""
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
assert self.type in PACKAGE_TYPES, (
|
||||
f"unsupported package type {self.type!r}: "
|
||||
f"use one of {', '.join(PACKAGE_TYPES)}."
|
||||
)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@@ -178,6 +196,8 @@ class PackagingEntry:
|
||||
validator_keys_artifact_name: str
|
||||
image: str
|
||||
package_type: str # "deb" or "rpm"; drives the format-specific steps
|
||||
package_variant: str # passed to build_pkg.py --variant; empty for xrpld
|
||||
package_name: str # the name it builds under, which the artifact globs use
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -267,12 +287,32 @@ def expand_linux_packaging(linux: LinuxFile) -> list[PackagingEntry]:
|
||||
validator_keys_artifact_name=f"validator-keys-{name}",
|
||||
image=cfg.package.image,
|
||||
package_type=cfg.package.type,
|
||||
package_variant=cfg.package.variant,
|
||||
package_name=(
|
||||
f"{BASE_NAME}-{cfg.package.variant}"
|
||||
if cfg.package.variant
|
||||
else BASE_NAME
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return entries
|
||||
|
||||
|
||||
def package_names_by_type(entries: list[PackagingEntry]) -> dict[str, list[str]]:
|
||||
"""The names of the packages in 'entries', keyed by format.
|
||||
|
||||
Derived from the packaging matrix rather than listed again, so the packages
|
||||
the install-test jobs look for are the packages that were built.
|
||||
"""
|
||||
return {
|
||||
package_type: sorted(
|
||||
{e.package_name for e in entries if e.package_type == package_type}
|
||||
)
|
||||
for package_type in PACKAGE_TYPES
|
||||
}
|
||||
|
||||
|
||||
def expand_platform_matrix(pf: PlatformFile, minimal: bool) -> list[MatrixEntry]:
|
||||
"""Expand a PlatformFile (macOS or Windows) into matrix entries.
|
||||
|
||||
@@ -341,6 +381,10 @@ if __name__ == "__main__":
|
||||
|
||||
if args.packaging:
|
||||
matrix = expand_linux_packaging(LinuxFile.load(THIS_DIR / "linux.json"))
|
||||
# One list per format, so each install-test job installs the packages its
|
||||
# own format produced.
|
||||
for package_type, names in package_names_by_type(matrix).items():
|
||||
print(f"{package_type}_package_names={json.dumps(names)}")
|
||||
else:
|
||||
if args.config in ("linux", None):
|
||||
matrix += expand_linux_matrix(
|
||||
|
||||
19
.github/scripts/strategy-matrix/linux.json
vendored
19
.github/scripts/strategy-matrix/linux.json
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"image_tag": "sha-473fe44",
|
||||
"image_tag": "sha-3d526d4",
|
||||
"configs": {
|
||||
"ubuntu": [
|
||||
{
|
||||
@@ -74,7 +74,20 @@
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON",
|
||||
"package": {
|
||||
"type": "deb",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-49cdc10"
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-e6055dd"
|
||||
}
|
||||
},
|
||||
{
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"suffix": "assert",
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON -Dassert=ON",
|
||||
"package": {
|
||||
"type": "deb",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-e6055dd",
|
||||
"variant": "assert"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -88,7 +101,7 @@
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON",
|
||||
"package": {
|
||||
"type": "rpm",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-49cdc10"
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-e6055dd"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
15
.github/workflows/build-nix-images.yml
vendored
15
.github/workflows/build-nix-images.yml
vendored
@@ -5,17 +5,14 @@ on:
|
||||
branches:
|
||||
- develop
|
||||
paths:
|
||||
- ".github/workflows/build-nix-images.yml"
|
||||
- "flake.nix"
|
||||
- "flake.lock"
|
||||
- "rust-toolchain.toml"
|
||||
- "nix/**"
|
||||
- "!nix/docker/README.md"
|
||||
- "!nix/devshell.nix"
|
||||
- "!nix/check-tools/*.txt"
|
||||
- "bin/check-tools.sh"
|
||||
- "bin/default-loader-path.sh"
|
||||
- "bin/install-sanitizer-libs.sh"
|
||||
- "!nix/check-tools/**"
|
||||
- "bin/nix/default-loader-path.sh"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/build-nix-images.yml"
|
||||
@@ -25,10 +22,10 @@ on:
|
||||
- "nix/**"
|
||||
- "!nix/docker/README.md"
|
||||
- "!nix/devshell.nix"
|
||||
- "!nix/check-tools/*.txt"
|
||||
- "!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:
|
||||
@@ -60,7 +57,7 @@ jobs:
|
||||
base_image: debian:bookworm
|
||||
- name: rhel
|
||||
base_image: registry.access.redhat.com/ubi9/ubi:latest
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@034e87065fcd0100214cf0672923bd38d193cf78
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@696384b292577293292daed06af0306d1b83bd7d
|
||||
with:
|
||||
image_name: xrpld/nix-${{ matrix.distro.name }}
|
||||
dockerfile: nix/docker/Dockerfile
|
||||
|
||||
13
.github/workflows/build-packaging-images.yml
vendored
13
.github/workflows/build-packaging-images.yml
vendored
@@ -5,14 +5,13 @@ on:
|
||||
branches:
|
||||
- develop
|
||||
paths:
|
||||
- ".github/workflows/build-packaging-images.yml"
|
||||
- "bin/install-packaging-tools.sh"
|
||||
- "package/docker/**"
|
||||
- "bin/install/packaging-tools.sh"
|
||||
- "package/images/packaging/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/build-packaging-images.yml"
|
||||
- "bin/install-packaging-tools.sh"
|
||||
- "package/docker/**"
|
||||
- "bin/install/packaging-tools.sh"
|
||||
- "package/images/packaging/**"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
@@ -41,9 +40,9 @@ jobs:
|
||||
# AlmaLinux rather than UBI, which does not ship rpm-sign.
|
||||
- name: rhel
|
||||
base_image: almalinux:10
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@034e87065fcd0100214cf0672923bd38d193cf78
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@696384b292577293292daed06af0306d1b83bd7d
|
||||
with:
|
||||
image_name: xrpld/packaging-${{ matrix.distro.name }}
|
||||
dockerfile: package/docker/Dockerfile
|
||||
dockerfile: package/images/packaging/Dockerfile
|
||||
base_image: ${{ matrix.distro.base_image }}
|
||||
push: ${{ github.event_name == 'push' }}
|
||||
|
||||
3
.github/workflows/build-pre-commit-image.yml
vendored
3
.github/workflows/build-pre-commit-image.yml
vendored
@@ -5,7 +5,6 @@ on:
|
||||
branches:
|
||||
- develop
|
||||
paths:
|
||||
- ".github/workflows/build-pre-commit-image.yml"
|
||||
- "bin/pre-commit/Dockerfile"
|
||||
- "rust-toolchain.toml"
|
||||
pull_request:
|
||||
@@ -30,7 +29,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@034e87065fcd0100214cf0672923bd38d193cf78
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@696384b292577293292daed06af0306d1b83bd7d
|
||||
with:
|
||||
image_name: xrpld/pre-commit
|
||||
dockerfile: bin/pre-commit/Dockerfile
|
||||
|
||||
2
.github/workflows/cargo-audit.yml
vendored
2
.github/workflows/cargo-audit.yml
vendored
@@ -34,7 +34,7 @@ permissions:
|
||||
jobs:
|
||||
audit:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
|
||||
permissions:
|
||||
contents: read
|
||||
# Needed to open an issue on scheduled failures.
|
||||
|
||||
1
.github/workflows/check-pr-description.yml
vendored
1
.github/workflows/check-pr-description.yml
vendored
@@ -13,7 +13,6 @@ on:
|
||||
- ready_for_review
|
||||
branches:
|
||||
- develop
|
||||
- "release-*"
|
||||
- "release/*"
|
||||
- "staging/*"
|
||||
|
||||
|
||||
1
.github/workflows/check-pr-title.yml
vendored
1
.github/workflows/check-pr-title.yml
vendored
@@ -13,7 +13,6 @@ on:
|
||||
- ready_for_review
|
||||
branches:
|
||||
- develop
|
||||
- "release-*"
|
||||
- "release/*"
|
||||
- "staging/*"
|
||||
|
||||
|
||||
60
.github/workflows/check-releases-merged.yml
vendored
Normal file
60
.github/workflows/check-releases-merged.yml
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
# This workflow checks that every final release on a release branch
|
||||
# has been merged back into develop, see RELEASING.md.
|
||||
name: Check releases merged
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# 06:47 UTC every Monday.
|
||||
- cron: "47 6 * * 1"
|
||||
push:
|
||||
branches:
|
||||
- "develop"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
OUTPUT_FILE: /tmp/releases-merged.txt
|
||||
ISSUE_FILE: /tmp/releases-merged-issue.md
|
||||
|
||||
jobs:
|
||||
releases-merged:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
# Needed to open an issue on scheduled failures.
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
# The check needs the full history of develop, the release branches and tags.
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check releases are merged into develop
|
||||
run: |
|
||||
set -o pipefail
|
||||
.github/scripts/releasing/check-releases-merged.sh origin/develop | tee "${OUTPUT_FILE}"
|
||||
|
||||
- name: Prepare issue body
|
||||
if: ${{ failure() && github.event_name == 'schedule' && github.repository == 'XRPLF/rippled' }}
|
||||
run: |
|
||||
{
|
||||
echo '```'
|
||||
cat "${OUTPUT_FILE}"
|
||||
echo '```'
|
||||
} >"${ISSUE_FILE}"
|
||||
|
||||
- name: Create issue
|
||||
if: ${{ failure() && github.event_name == 'schedule' && github.repository == 'XRPLF/rippled' }}
|
||||
uses: XRPLF/actions/create-issue@2b8bc36af85b88bca0dd7bfac2e2dc05f94ad712
|
||||
with:
|
||||
title: "Releases not merged back into develop"
|
||||
body_file: ${{ env.ISSUE_FILE }}
|
||||
labels: "Bug"
|
||||
53
.github/workflows/on-pr.yml
vendored
53
.github/workflows/on-pr.yml
vendored
@@ -85,6 +85,7 @@ jobs:
|
||||
.github/workflows/reusable-build-test.yml
|
||||
.github/workflows/reusable-check-autogen.yml
|
||||
.github/workflows/reusable-clang-tidy.yml
|
||||
.github/workflows/reusable-package-test-install.yml
|
||||
.github/workflows/reusable-package.yml
|
||||
.github/workflows/reusable-rust.yml
|
||||
.github/workflows/reusable-strategy-matrix.yml
|
||||
@@ -92,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/**
|
||||
@@ -149,6 +149,48 @@ jobs:
|
||||
if: ${{ needs.should-run.outputs.go == 'true' }}
|
||||
uses: ./.github/workflows/reusable-check-rename.yml
|
||||
|
||||
# Runs regardless of the changed files.
|
||||
# PRs into staging branches are skipped, since fixes may be cherry-picked between release lines.
|
||||
check-release-commits:
|
||||
needs: should-run
|
||||
if: ${{ github.event.pull_request.base.ref == 'develop' || github.event.merge_group.base_ref == 'refs/heads/develop' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
# The check needs the full history of the release and staging branches.
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- name: Check for copied release commits
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
env:
|
||||
BASE: ${{ github.event.pull_request.base.sha }}
|
||||
HEAD: ${{ github.event.pull_request.head.sha }}
|
||||
run: .github/scripts/releasing/check-no-copied-release-commits.sh "${BASE}" "${HEAD}"
|
||||
# Runs even if the previous check fails, so that both problems are reported at once.
|
||||
- name: Check merge-back has no new commits
|
||||
if: ${{ !cancelled() && github.event_name == 'pull_request' }}
|
||||
env:
|
||||
BASE: ${{ github.event.pull_request.base.sha }}
|
||||
HEAD: ${{ github.event.pull_request.head.sha }}
|
||||
run: .github/scripts/releasing/check-merge-back-commits.sh "${BASE}" "${HEAD}"
|
||||
# The queue squashes PRs, so check the PR's own branch, named in the queue branch.
|
||||
- name: Check the merge queue doesn't merge a release back
|
||||
if: ${{ github.event_name == 'merge_group' }}
|
||||
env:
|
||||
BASE: ${{ github.event.merge_group.base_sha }}
|
||||
HEAD_REF: ${{ github.event.merge_group.head_ref }}
|
||||
run: |
|
||||
if ! [[ "${HEAD_REF}" =~ /pr-([0-9]+)-[0-9a-f]+$ ]]; then
|
||||
echo "Error: Can't find the PR number in '${HEAD_REF}'."
|
||||
exit 1
|
||||
fi
|
||||
git fetch --no-tags origin "refs/pull/${BASH_REMATCH[1]}/head"
|
||||
.github/scripts/releasing/check-no-merge-back.sh "${BASE}" FETCH_HEAD
|
||||
|
||||
clang-tidy:
|
||||
needs: should-run
|
||||
if: ${{ needs.should-run.outputs.go == 'true' }}
|
||||
@@ -189,6 +231,12 @@ jobs:
|
||||
# matrix (i.e. not yet labeled "Ready to merge" or "Full CI build").
|
||||
if: ${{ needs.should-run.outputs.go == 'true' && (github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'Ready to merge') || contains(github.event.pull_request.labels.*.name, 'Full CI build')) }}
|
||||
uses: ./.github/workflows/reusable-package.yml
|
||||
with:
|
||||
# A pull request builds packages to prove they still build, and publishes
|
||||
# nothing. Stated rather than left to the input's default, so that changing
|
||||
# that default cannot start publishing from pull requests. No secrets are
|
||||
# passed either, which is the second reason a publish here cannot succeed.
|
||||
publish: false
|
||||
|
||||
upload-recipe:
|
||||
needs:
|
||||
@@ -223,6 +271,7 @@ jobs:
|
||||
- check-autogen
|
||||
- check-levelization
|
||||
- check-rename
|
||||
- check-release-commits
|
||||
- clang-tidy
|
||||
- build-test
|
||||
- rust
|
||||
|
||||
10
.github/workflows/on-tag.yml
vendored
10
.github/workflows/on-tag.yml
vendored
@@ -1,9 +1,12 @@
|
||||
# When a versioned tag is pushed, this workflow:
|
||||
#
|
||||
# - uploads the libxrpl recipe to the Conan remote
|
||||
# - builds and tests the release binaries
|
||||
# - uploads the libxrpl recipe to the Conan remote
|
||||
# - builds the DEB and RPM packages
|
||||
# - publishes those packages to the XRPLF package repositories
|
||||
#
|
||||
# Nothing is published unless the build passes, which is also where CMake
|
||||
# rejects a tag that is not a valid version, e.g. 3.2.01.
|
||||
name: Tag
|
||||
|
||||
on:
|
||||
@@ -22,6 +25,7 @@ defaults:
|
||||
jobs:
|
||||
upload-recipe:
|
||||
if: ${{ github.repository == 'XRPLF/rippled' }}
|
||||
needs: build-test
|
||||
uses: ./.github/workflows/reusable-upload-recipe.yml
|
||||
secrets:
|
||||
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
|
||||
@@ -50,3 +54,7 @@ jobs:
|
||||
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
|
||||
signing_key: ${{ secrets.NEXUS_PACKAGES_PRIVATE_KEY }}
|
||||
dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
antithesis_docker_host: ${{ secrets.ANTITHESIS_DOCKER_HOST }}
|
||||
antithesis_docker_path: ${{ secrets.ANTITHESIS_DOCKER_PATH }}
|
||||
antithesis_docker_credentials: ${{ secrets.ANTITHESIS_DOCKER_CREDENTIALS }}
|
||||
|
||||
8
.github/workflows/on-trigger.yml
vendored
8
.github/workflows/on-trigger.yml
vendored
@@ -23,6 +23,7 @@ on:
|
||||
- ".github/workflows/reusable-build-test.yml"
|
||||
- ".github/workflows/reusable-check-autogen.yml"
|
||||
- ".github/workflows/reusable-clang-tidy.yml"
|
||||
- ".github/workflows/reusable-package-test-install.yml"
|
||||
- ".github/workflows/reusable-package.yml"
|
||||
- ".github/workflows/reusable-rust.yml"
|
||||
- ".github/workflows/reusable-strategy-matrix.yml"
|
||||
@@ -30,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/**"
|
||||
@@ -128,3 +128,7 @@ jobs:
|
||||
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
|
||||
signing_key: ${{ secrets.NEXUS_PACKAGES_PRIVATE_KEY }}
|
||||
dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
antithesis_docker_host: ${{ secrets.ANTITHESIS_DOCKER_HOST }}
|
||||
antithesis_docker_path: ${{ secrets.ANTITHESIS_DOCKER_PATH }}
|
||||
antithesis_docker_credentials: ${{ secrets.ANTITHESIS_DOCKER_CREDENTIALS }}
|
||||
|
||||
2
.github/workflows/pre-commit.yml
vendored
2
.github/workflows/pre-commit.yml
vendored
@@ -17,4 +17,4 @@ jobs:
|
||||
uses: XRPLF/actions/.github/workflows/pre-commit.yml@279ec358f4a1be4088be3e024b07916fa97c75b6
|
||||
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
.github/workflows/publish-docs.yml
vendored
2
.github/workflows/publish-docs.yml
vendored
@@ -41,7 +41,7 @@ env:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
31
.github/workflows/reusable-build-test-config.yml
vendored
31
.github/workflows/reusable-build-test-config.yml
vendored
@@ -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
|
||||
@@ -196,6 +193,19 @@ jobs:
|
||||
env:
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
CMAKE_ARGS: ${{ inputs.cmake_args }}
|
||||
# GitHub creates a merge commit for a PR
|
||||
# https://www.kenmuse.com/blog/the-many-shas-of-a-github-pull-request/
|
||||
#
|
||||
# We:
|
||||
# - explicitly provide branch name
|
||||
# - use `github.event.pull_request.head.sha` to get the SHA of last commit in the PR branch
|
||||
#
|
||||
# This way it works both for PRs and pushes to branches.
|
||||
GITHUB_BRANCH_NAME: "${{ github.head_ref || github.ref_name }}"
|
||||
GITHUB_HEAD_SHA: "${{ github.event.pull_request.head.sha || github.sha }}"
|
||||
#
|
||||
# If tag is being pushed, we use that version.
|
||||
FORCE_XRPLD_VERSION: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || '' }}
|
||||
run: |
|
||||
cmake \
|
||||
-G '${{ runner.os == 'Windows' && 'Visual Studio 18 2026' || 'Ninja' }}' \
|
||||
@@ -244,19 +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/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 }}
|
||||
@@ -338,7 +349,9 @@ jobs:
|
||||
- name: Run the separate tests
|
||||
if: ${{ !inputs.build_only }}
|
||||
working-directory: ${{ runner.os == 'Windows' && format('{0}/{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }}
|
||||
run: ./xrpl_tests
|
||||
run: |
|
||||
./xrpl_tests
|
||||
./xrpld_tests
|
||||
|
||||
- name: Run the embedded tests
|
||||
if: ${{ !inputs.build_only }}
|
||||
@@ -439,7 +452,7 @@ jobs:
|
||||
|
||||
- name: Upload coverage report
|
||||
if: ${{ github.repository_owner == 'XRPLF' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }}
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
|
||||
with:
|
||||
disable_search: true
|
||||
disable_telem: true
|
||||
|
||||
3
.github/workflows/reusable-clang-tidy.yml
vendored
3
.github/workflows/reusable-clang-tidy.yml
vendored
@@ -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-473fe44"
|
||||
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-3d526d4"
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
@@ -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 }}
|
||||
|
||||
120
.github/workflows/reusable-package-test-install.yml
vendored
Normal file
120
.github/workflows/reusable-package-test-install.yml
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
# Install one package format on every distro family it targets, one job per
|
||||
# package name and image, and run the binaries there. Called once per format by
|
||||
# reusable-package.yml, which owns the names and the image lists.
|
||||
name: Install packages
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
package_type:
|
||||
description: 'The package format to install ("deb" or "rpm").'
|
||||
required: true
|
||||
type: string
|
||||
package_names:
|
||||
description: "JSON array of package names built for this format."
|
||||
required: true
|
||||
type: string
|
||||
images:
|
||||
description: "JSON array of container images to install in."
|
||||
required: true
|
||||
type: string
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
PACKAGE_DIR: packages
|
||||
|
||||
jobs:
|
||||
install:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package_name: ${{ fromJson(inputs.package_names) }}
|
||||
image: ${{ fromJson(inputs.images) }}
|
||||
name: "${{ matrix.package_name }} on ${{ matrix.image }}"
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ matrix.image }}
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
# 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
|
||||
with:
|
||||
pattern: "*-pkg"
|
||||
merge-multiple: true
|
||||
path: ${{ env.PACKAGE_DIR }}
|
||||
|
||||
- name: Find the package
|
||||
id: find
|
||||
env:
|
||||
PACKAGE_NAME: ${{ matrix.package_name }}
|
||||
PACKAGE_TYPE: ${{ inputs.package_type }}
|
||||
run: |
|
||||
# The version follows the name, separated by '_' in a DEB and '-' in an
|
||||
# RPM. Requiring a digit after it is what keeps 'xrpld' from picking up
|
||||
# another package, such as 'xrpld-assert'.
|
||||
pattern="${PACKAGE_NAME}[_-][0-9]*.${PACKAGE_TYPE}"
|
||||
package="$(find "${PACKAGE_DIR}" -type f -name "${pattern}" -print -quit)"
|
||||
test -n "${package}" || {
|
||||
echo "no ${pattern} found in ${PACKAGE_DIR}" >&2
|
||||
exit 1
|
||||
}
|
||||
echo "package=${package}" >>"${GITHUB_OUTPUT}"
|
||||
|
||||
# Debian 11 went end-of-life on 2026-08-31
|
||||
# (https://www.debian.org/News/2026/20260831) and its packages are
|
||||
# already partly gone from deb.debian.org, so switch to the
|
||||
# snapshot.debian.org entries the image ships commented out in its
|
||||
# sources.list: they are pinned to the snapshot the image was built
|
||||
# from, so they serve every version it needs and never go away.
|
||||
# Snapshots keep their original, long-passed Valid-Until, hence the
|
||||
# disabled check; the retries absorb snapshot.debian.org's throttling.
|
||||
- name: Switch Debian 11 to snapshot.debian.org
|
||||
if: ${{ matrix.image == 'debian:11' }}
|
||||
run: |
|
||||
sed -i 's|^deb |# deb |; s|^# deb http://snapshot|deb http://snapshot|' /etc/apt/sources.list
|
||||
printf '%s\n' \
|
||||
'Acquire::Check-Valid-Until "false";' \
|
||||
'Acquire::Retries "3";' \
|
||||
>/etc/apt/apt.conf.d/99snapshot
|
||||
|
||||
- name: Install the DEB
|
||||
if: ${{ inputs.package_type == 'deb' }}
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
PACKAGE: ${{ steps.find.outputs.package }}
|
||||
run: |
|
||||
# Stock Debian and Ubuntu images carry no package lists, so apt has
|
||||
# nothing to resolve the systemd dependency from until it fetches them.
|
||||
apt-get update -qq
|
||||
apt-get install -y "./${PACKAGE}"
|
||||
|
||||
- name: Install the RPM
|
||||
if: ${{ inputs.package_type == 'rpm' }}
|
||||
env:
|
||||
PACKAGE: ${{ steps.find.outputs.package }}
|
||||
run: dnf install -y "./${PACKAGE}"
|
||||
|
||||
- name: Run xrpld
|
||||
run: xrpld --version
|
||||
|
||||
- name: Run validator-keys
|
||||
run: validator-keys --version
|
||||
|
||||
- name: Run rippled, the legacy compatibility symlink
|
||||
run: rippled --version
|
||||
|
||||
- name: Check the service account
|
||||
run: id xrpld
|
||||
|
||||
- name: Check the state directory
|
||||
run: test -d /var/lib/xrpld
|
||||
|
||||
- name: Check the log directory
|
||||
run: test -d /var/log/xrpld
|
||||
293
.github/workflows/reusable-package.yml
vendored
293
.github/workflows/reusable-package.yml
vendored
@@ -1,12 +1,17 @@
|
||||
# Build, verify and publish Linux packages from the pre-built xrpld and
|
||||
# validator-keys artifacts, in three stages:
|
||||
# validator-keys artifacts, in these stages:
|
||||
#
|
||||
# - 'package' builds and signs one format per config that carries a "package"
|
||||
# map in linux.json; that map names the container image and the format
|
||||
# - 'test-install' installs what was built on a range of distros and runs the
|
||||
# binaries there, so a package that cannot be installed never reaches Nexus
|
||||
# - 'test-install-deb' and 'test-install-rpm' call
|
||||
# reusable-package-test-install.yml to install what was built on a range of
|
||||
# distros and run the binaries there, so a package that cannot be installed
|
||||
# never reaches Nexus
|
||||
# - 'publish' uploads with the image's publish_pkg.py, doing a --dry-run
|
||||
# unless 'publish: true'
|
||||
# - 'docker' builds an Ubuntu image from the tested DEB, and one with the
|
||||
# voidstar binary for Antithesis, pushing them to Docker Hub and to the
|
||||
# Antithesis registry only with 'publish: true'
|
||||
#
|
||||
# Only linux/amd64 is supported; the runner is hardcoded in the jobs below.
|
||||
name: Package
|
||||
@@ -23,7 +28,7 @@ on:
|
||||
description: "The base URL of the Nexus instance hosting the deb and rpm repositories."
|
||||
required: false
|
||||
type: string
|
||||
default: https://packages.xrplf.org
|
||||
default: https://packages-upload.xrplf.org
|
||||
|
||||
secrets:
|
||||
remote_username:
|
||||
@@ -33,7 +38,19 @@ on:
|
||||
description: "The password or token for that Nexus account."
|
||||
required: false
|
||||
signing_key:
|
||||
description: "Armoured PGP private key used to sign the RPMs. Required when publishing."
|
||||
description: "Armoured PGP private key used to sign the RPMs."
|
||||
required: false
|
||||
dockerhub_token:
|
||||
description: "A Docker Hub organization access token for xrplf, with push access to xrplf/xrpld."
|
||||
required: false
|
||||
antithesis_docker_host:
|
||||
description: "The host of the Antithesis container registry, e.g. us-central1-docker.pkg.dev."
|
||||
required: false
|
||||
antithesis_docker_path:
|
||||
description: "The repository path in that registry, the image name excluded."
|
||||
required: false
|
||||
antithesis_docker_credentials:
|
||||
description: "The JSON key of a service account with push access to that repository."
|
||||
required: false
|
||||
|
||||
defaults:
|
||||
@@ -49,6 +66,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.generate.outputs.matrix }}
|
||||
deb_package_names: ${{ steps.generate.outputs.deb_package_names }}
|
||||
rpm_package_names: ${{ steps.generate.outputs.rpm_package_names }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -107,6 +126,7 @@ jobs:
|
||||
- name: Build package
|
||||
env:
|
||||
PACKAGE_TYPE: ${{ matrix.package_type }}
|
||||
PACKAGE_VARIANT: ${{ matrix.package_variant }}
|
||||
PKG_RELEASE: ${{ steps.release_info.outputs.pkg_release }}
|
||||
CHANNEL: ${{ steps.release_info.outputs.channel }}
|
||||
run: |
|
||||
@@ -114,6 +134,7 @@ jobs:
|
||||
--package-type "${PACKAGE_TYPE}" \
|
||||
--build-dir "${BUILD_DIR}" \
|
||||
--pkg-release "${PKG_RELEASE}" \
|
||||
--variant "${PACKAGE_VARIANT}" \
|
||||
--channel "${CHANNEL}"
|
||||
|
||||
# Before the upload, so the artifact, the tested package and the published
|
||||
@@ -125,14 +146,17 @@ jobs:
|
||||
run: ./package/sign_rpm.py --package-dir "${BUILD_DIR}"
|
||||
|
||||
# Split from the debug symbols, which are an order of magnitude larger, so
|
||||
# that test-install downloads only what it installs.
|
||||
# that test-install downloads only what it installs. In the globs below the
|
||||
# version follows the name, separated by '_' in a DEB and '-' in an RPM. A
|
||||
# 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
|
||||
with:
|
||||
name: ${{ matrix.xrpld_artifact_name }}-pkg
|
||||
path: |
|
||||
${{ env.BUILD_DIR }}/debbuild/xrpld_*.deb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/xrpld-[0-9]*.rpm
|
||||
${{ env.BUILD_DIR }}/debbuild/${{ matrix.package_name }}_[0-9]*.deb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/${{ matrix.package_name }}-[0-9]*.rpm
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload debug symbol artifact
|
||||
@@ -140,133 +164,56 @@ jobs:
|
||||
with:
|
||||
name: ${{ matrix.xrpld_artifact_name }}-pkg-debug
|
||||
path: |
|
||||
${{ env.BUILD_DIR }}/debbuild/xrpld-dbgsym_*.deb
|
||||
${{ env.BUILD_DIR }}/debbuild/xrpld-dbgsym_*.ddeb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/xrpld-debuginfo-*.rpm
|
||||
${{ env.BUILD_DIR }}/debbuild/${{ matrix.package_name }}-dbgsym_[0-9]*.deb
|
||||
${{ env.BUILD_DIR }}/debbuild/${{ matrix.package_name }}-dbgsym_[0-9]*.ddeb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/${{ matrix.package_name }}-debuginfo-[0-9]*.rpm
|
||||
if-no-files-found: error
|
||||
|
||||
# Every distro family the packages target, oldest release first, so both ends
|
||||
# of the dependency range they declare are exercised.
|
||||
test-install:
|
||||
needs: [package]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- package_type: deb
|
||||
image: debian:11
|
||||
- package_type: deb
|
||||
image: debian:12
|
||||
- package_type: deb
|
||||
image: debian:13
|
||||
- package_type: deb
|
||||
image: ubuntu:20.04
|
||||
- package_type: deb
|
||||
image: ubuntu:22.04
|
||||
- package_type: deb
|
||||
image: ubuntu:24.04
|
||||
- package_type: deb
|
||||
image: ubuntu:26.04
|
||||
# One call per format, so a variant packaged for one format is installed for
|
||||
# that format alone. The images are every distro family that format targets,
|
||||
# oldest release first, so both ends of the dependency range the packages
|
||||
# declare are exercised.
|
||||
test-install-deb:
|
||||
needs: [generate-matrix, package]
|
||||
name: install deb
|
||||
uses: ./.github/workflows/reusable-package-test-install.yml
|
||||
with:
|
||||
package_type: deb
|
||||
package_names: ${{ needs.generate-matrix.outputs.deb_package_names }}
|
||||
images: |
|
||||
[
|
||||
"debian:11",
|
||||
"debian:12",
|
||||
"debian:13",
|
||||
"ubuntu:20.04",
|
||||
"ubuntu:22.04",
|
||||
"ubuntu:24.04",
|
||||
"ubuntu:26.04"
|
||||
]
|
||||
|
||||
- package_type: rpm
|
||||
image: almalinux:9
|
||||
- package_type: rpm
|
||||
image: almalinux:10
|
||||
- package_type: rpm
|
||||
image: rockylinux/rockylinux:9
|
||||
- package_type: rpm
|
||||
image: rockylinux/rockylinux:10
|
||||
- package_type: rpm
|
||||
image: registry.access.redhat.com/ubi9/ubi
|
||||
- package_type: rpm
|
||||
image: registry.access.redhat.com/ubi10/ubi
|
||||
name: "install ${{ matrix.package_type }} on ${{ matrix.image }}"
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ matrix.image }}
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
# Both formats land in one directory; the step below picks its own by
|
||||
# extension, so this stays independent of the artifact names.
|
||||
- name: Download package artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
pattern: "*-pkg"
|
||||
merge-multiple: true
|
||||
path: ${{ env.PACKAGE_DIR }}
|
||||
|
||||
- name: Find the package
|
||||
id: find
|
||||
env:
|
||||
PACKAGE_TYPE: ${{ matrix.package_type }}
|
||||
run: |
|
||||
package="$(find "${PACKAGE_DIR}" -type f -name "*.${PACKAGE_TYPE}" -print -quit)"
|
||||
test -n "${package}" || {
|
||||
echo "no .${PACKAGE_TYPE} found in ${PACKAGE_DIR}" >&2
|
||||
exit 1
|
||||
}
|
||||
echo "package=${package}" >>"${GITHUB_OUTPUT}"
|
||||
|
||||
# Debian 11 went end-of-life on 2026-08-31
|
||||
# (https://www.debian.org/News/2026/20260831) and its packages are
|
||||
# already partly gone from deb.debian.org, so switch to the
|
||||
# snapshot.debian.org entries the image ships commented out in its
|
||||
# sources.list: they are pinned to the snapshot the image was built
|
||||
# from, so they serve every version it needs and never go away.
|
||||
# Snapshots keep their original, long-passed Valid-Until, hence the
|
||||
# disabled check; the retries absorb snapshot.debian.org's throttling.
|
||||
- name: Switch Debian 11 to snapshot.debian.org
|
||||
if: ${{ matrix.image == 'debian:11' }}
|
||||
run: |
|
||||
sed -i 's|^deb |# deb |; s|^# deb http://snapshot|deb http://snapshot|' /etc/apt/sources.list
|
||||
printf '%s\n' \
|
||||
'Acquire::Check-Valid-Until "false";' \
|
||||
'Acquire::Retries "3";' \
|
||||
>/etc/apt/apt.conf.d/99snapshot
|
||||
|
||||
- name: Install the DEB
|
||||
if: ${{ matrix.package_type == 'deb' }}
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
PACKAGE: ${{ steps.find.outputs.package }}
|
||||
run: |
|
||||
# Stock Debian and Ubuntu images carry no package lists, so apt has
|
||||
# nothing to resolve the systemd dependency from until it fetches them.
|
||||
apt-get update -qq
|
||||
apt-get install -y "./${PACKAGE}"
|
||||
|
||||
- name: Install the RPM
|
||||
if: ${{ matrix.package_type == 'rpm' }}
|
||||
env:
|
||||
PACKAGE: ${{ steps.find.outputs.package }}
|
||||
run: dnf install -y "./${PACKAGE}"
|
||||
|
||||
- name: Run xrpld
|
||||
run: xrpld --version
|
||||
|
||||
- name: Run validator-keys
|
||||
run: validator-keys --version
|
||||
|
||||
- name: Run rippled, the legacy compatibility symlink
|
||||
run: rippled --version
|
||||
|
||||
- name: Check the service account
|
||||
run: id xrpld
|
||||
|
||||
- name: Check the state directory
|
||||
run: test -d /var/lib/xrpld
|
||||
|
||||
- name: Check the log directory
|
||||
run: test -d /var/log/xrpld
|
||||
test-install-rpm:
|
||||
needs: [generate-matrix, package]
|
||||
name: install rpm
|
||||
uses: ./.github/workflows/reusable-package-test-install.yml
|
||||
with:
|
||||
package_type: rpm
|
||||
package_names: ${{ needs.generate-matrix.outputs.rpm_package_names }}
|
||||
images: |
|
||||
[
|
||||
"almalinux:9",
|
||||
"almalinux:10",
|
||||
"rockylinux/rockylinux:9",
|
||||
"rockylinux/rockylinux:10",
|
||||
"registry.access.redhat.com/ubi9/ubi",
|
||||
"registry.access.redhat.com/ubi10/ubi"
|
||||
]
|
||||
|
||||
publish:
|
||||
needs: [generate-matrix, package, test-install]
|
||||
needs: [generate-matrix, package, test-install-deb, test-install-rpm]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
name: "publish ${{ matrix.xrpld_artifact_name }}"
|
||||
name: "publish ${{ matrix.xrpld_artifact_name }}${{ !inputs.publish && ' (dry run)' || '' }}"
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
|
||||
@@ -307,3 +254,95 @@ jobs:
|
||||
--package-dir "${PACKAGE_DIR}" \
|
||||
--nexus-url "${NEXUS_URL}" \
|
||||
${DRY_RUN_OPTION}
|
||||
|
||||
docker:
|
||||
needs: [test-install-deb, test-install-rpm]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: [xrpld, voidstar]
|
||||
name: "docker ${{ matrix.target }}${{ !inputs.publish && ' (dry run)' || '' }}"
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
CONTEXT: image-context
|
||||
IMAGE: ${{ matrix.target == 'voidstar' && 'xrpld-voidstar' || 'xrplf/xrpld' }}:${{ github.ref_type == 'tag' && github.ref_name || 'develop' }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Determine release info
|
||||
id: release_info
|
||||
uses: ./.github/actions/release-info
|
||||
|
||||
# Docker Hub is public, so it only gets builds whose packages are public:
|
||||
# those of a public codebase, and stable releases.
|
||||
# The Antithesis registry is private, so it gets every build.
|
||||
- name: Decide whether to push
|
||||
env:
|
||||
PUSH: ${{ inputs.publish && (matrix.target == 'voidstar' || github.event.repository.visibility == 'public' || steps.release_info.outputs.channel == 'stable') }}
|
||||
run: echo "PUSH=${PUSH}" | tee -a "${GITHUB_ENV}"
|
||||
|
||||
- name: Download package artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
pattern: "*-pkg"
|
||||
merge-multiple: true
|
||||
path: ${{ env.PACKAGE_DIR }}
|
||||
|
||||
- name: Download voidstar binary
|
||||
if: ${{ matrix.target == 'voidstar' }}
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: xrpld-ubuntu-clang-debug-amd64-voidstar
|
||||
path: ${{ env.CONTEXT }}
|
||||
|
||||
- name: Build image
|
||||
env:
|
||||
TARGET: ${{ matrix.target }}
|
||||
run: |
|
||||
mkdir -p "${CONTEXT}"
|
||||
find "${PACKAGE_DIR}" -type f -name 'xrpld_[0-9]*.deb' -exec cp {} "${CONTEXT}/" \;
|
||||
docker build --pull --file package/images/xrpld/Dockerfile --target "${TARGET}" --tag "${IMAGE}" "${CONTEXT}"
|
||||
|
||||
- name: Start the server
|
||||
run: |
|
||||
container="$(docker run --detach "${IMAGE}" --standalone --silent --conf /etc/xrpld/xrpld.cfg)"
|
||||
trap 'docker rm --force "${container}" >/dev/null' EXIT
|
||||
|
||||
for _ in $(seq 30); do
|
||||
output="$(docker exec "${container}" xrpld --conf /etc/xrpld/xrpld.cfg server_info || true)"
|
||||
if [[ "${output}" == *'"status" : "success"'* ]]; then
|
||||
exit 0
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
docker logs "${container}"
|
||||
exit 1
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
if: ${{ env.PUSH == 'true' && matrix.target == 'xrpld' }}
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
username: xrplf
|
||||
password: ${{ secrets.dockerhub_token }}
|
||||
|
||||
- name: Log in to the Antithesis registry
|
||||
if: ${{ env.PUSH == 'true' && matrix.target == 'voidstar' }}
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ${{ secrets.antithesis_docker_host }}
|
||||
username: _json_key
|
||||
password: ${{ secrets.antithesis_docker_credentials }}
|
||||
|
||||
- name: Push image
|
||||
if: ${{ env.PUSH == 'true' }}
|
||||
env:
|
||||
REGISTRY: ${{ matrix.target == 'voidstar' && format('{0}/{1}/', secrets.antithesis_docker_host, secrets.antithesis_docker_path) || '' }}
|
||||
run: |
|
||||
docker tag "${IMAGE}" "${REGISTRY}${IMAGE}"
|
||||
docker push "${REGISTRY}${IMAGE}"
|
||||
|
||||
16
.github/workflows/reusable-rust.yml
vendored
16
.github/workflows/reusable-rust.yml
vendored
@@ -1,8 +1,9 @@
|
||||
# Clippy, coverage and documentation for the Rust crates in crates/. Each runs
|
||||
# as an independent job on a GitHub-hosted runner, but inside the same container
|
||||
# image used to build the crates in the C++/Corrosion path, so the toolchain
|
||||
# (and therefore the lints, coverage instrumentation and the cargo cache) matches
|
||||
# what production builds use.
|
||||
# (and therefore the lints and the cargo cache) matches what production builds
|
||||
# use. Coverage is the exception: it needs the nightly rustc that honours
|
||||
# #[coverage(off)], which the image carries alongside the pinned stable.
|
||||
#
|
||||
# Rust unit tests are deliberately NOT run here. They run as part of the C++
|
||||
# build (reusable-build-test-config.yml), which already compiles the crates on a
|
||||
@@ -27,7 +28,7 @@ permissions:
|
||||
jobs:
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -40,11 +41,14 @@ jobs:
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Use the nightly Rust toolchain
|
||||
run: rust-nightly path >>"${GITHUB_PATH}"
|
||||
|
||||
- name: Use cargo artifacts cache
|
||||
uses: ./.github/actions/cargo-cache
|
||||
|
||||
@@ -53,7 +57,7 @@ jobs:
|
||||
|
||||
- name: Upload coverage report
|
||||
if: ${{ github.repository == 'XRPLF/rippled' }}
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
|
||||
with:
|
||||
disable_search: true
|
||||
disable_telem: true
|
||||
@@ -66,7 +70,7 @@ jobs:
|
||||
|
||||
doc:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
28
.github/workflows/reusable-upload-recipe.yml
vendored
28
.github/workflows/reusable-upload-recipe.yml
vendored
@@ -40,7 +40,7 @@ defaults:
|
||||
jobs:
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
|
||||
env:
|
||||
REMOTE_NAME: ${{ inputs.remote_name }}
|
||||
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.remote_username }}
|
||||
@@ -68,33 +68,17 @@ jobs:
|
||||
run: conan remote login "${REMOTE_NAME}" "${CONAN_LOGIN_USERNAME_XRPLF}" --password "${CONAN_PASSWORD_XRPLF}"
|
||||
|
||||
- name: Upload Conan recipe (version)
|
||||
env:
|
||||
VERSION: ${{ steps.release_info.outputs.version }}
|
||||
run: |
|
||||
conan export . --version=${{ steps.release_info.outputs.version }}
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/${{ steps.release_info.outputs.version }}
|
||||
conan export . --version="${VERSION}"
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" "xrpl/${VERSION}"
|
||||
|
||||
# When this workflow is triggered by a push event, it will always be when merging into the
|
||||
# 'develop' branch, see on-trigger.yml.
|
||||
- name: Upload Conan recipe (develop)
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
if: ${{ github.ref == 'refs/heads/develop' }}
|
||||
run: |
|
||||
conan export . --version=develop
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/develop
|
||||
|
||||
# When this workflow is triggered by a pull request event, it will always be when merging into
|
||||
# one of the 'release' branches, see on-pr.yml.
|
||||
- name: Upload Conan recipe (rc)
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
conan export . --version=rc
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/rc
|
||||
|
||||
# When this workflow is triggered by a push event, it will always be when tagging a final
|
||||
# release, see on-tag.yml.
|
||||
- name: Upload Conan recipe (release)
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
run: |
|
||||
conan export . --version=release
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/release
|
||||
|
||||
outputs:
|
||||
ref: xrpl/${{ steps.release_info.outputs.version }}
|
||||
|
||||
5
.github/workflows/upload-conan-deps.yml
vendored
5
.github/workflows/upload-conan-deps.yml
vendored
@@ -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') }}
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -92,6 +92,9 @@ target/
|
||||
# Direnv's directory
|
||||
/.direnv
|
||||
|
||||
# Direnv's local, per-developer overrides
|
||||
/.envrc.local
|
||||
|
||||
# clangd cache
|
||||
/.cache
|
||||
|
||||
|
||||
@@ -53,9 +53,14 @@ repos:
|
||||
entry: ./bin/pre-commit/check_doxygen_style.py
|
||||
language: python
|
||||
types_or: [c++, c]
|
||||
- id: fix-gtest-names
|
||||
name: "fix gtest names: CamelCase suite, snake_case test case"
|
||||
entry: ./bin/pre-commit/fix_gtest_names.py
|
||||
language: python
|
||||
types_or: [c++, c]
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: f4d7745e17a28aad7eed2f4874ca8d1568c11c4c # frozen: v22.1.8
|
||||
rev: a9a8a861f30ed207ead7d5a3b7e8032283ba5da7 # frozen: v23.1.2
|
||||
hooks:
|
||||
- id: clang-format
|
||||
args: [--style=file]
|
||||
@@ -70,14 +75,20 @@ repos:
|
||||
language: system
|
||||
types: [rust]
|
||||
pass_filenames: false # rustfmt formats the whole workspace
|
||||
- id: check-coverage-attrs
|
||||
name: check Rust coverage attributes
|
||||
entry: ./bin/pre-commit/check_rust_coverage_attrs.py
|
||||
language: python
|
||||
files: ^crates/.*\.rs$
|
||||
|
||||
- repo: https://github.com/BlankSpruce/gersemi-pre-commit
|
||||
rev: e98930bdc210d3387007f9252d8c1694ea7e410f # frozen: 0.27.7
|
||||
rev: 28010ddd6016e1a0f7bd232acb6536ef996ae897 # frozen: 0.29.2
|
||||
hooks:
|
||||
- id: gersemi
|
||||
args: [-i, --warnings-as-errors]
|
||||
|
||||
- repo: https://github.com/rbubley/mirrors-prettier
|
||||
rev: 9337a74165b178ae2c766f60bee7252a0f06f3e8 # frozen: v3.9.5
|
||||
rev: ef4a397f916211b4a39ccf9d3d9cbb6562157251 # frozen: v3.9.9
|
||||
hooks:
|
||||
- id: prettier
|
||||
args: [--end-of-line=auto]
|
||||
@@ -85,26 +96,26 @@ 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: 7c55798a78262d14b2074abf623d8a992ebb70d4 # frozen: v0.16.2
|
||||
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: 41e691678310dfd3833f7ab4e180ddb014310356 # frozen: v2.3.0
|
||||
rev: 2834ec6639549dd6796205c8f011dedcd587288b # frozen: v2.4.0
|
||||
hooks:
|
||||
- id: mypy
|
||||
args: [--strict]
|
||||
files: ^package/.*\.py$
|
||||
|
||||
- repo: https://github.com/scop/pre-commit-shfmt
|
||||
rev: 05c1426671b9237fb5e1444dd63aa5731bec0dfb # frozen: v3.13.1-1
|
||||
rev: 479be5958357ba5ab65ce47172117348516860e9 # frozen: v3.14.1-1
|
||||
hooks:
|
||||
- id: shfmt
|
||||
args: [--write, --indent=4, --case-indent=true]
|
||||
@@ -123,7 +134,7 @@ repos:
|
||||
files: \.md$
|
||||
|
||||
- repo: https://github.com/streetsidesoftware/cspell-cli
|
||||
rev: ea11f9efc0bec520073405bc30552da887ba71bc # frozen: v10.0.1
|
||||
rev: f5c5d72342f35643988a9ffa0705c05bd3ff8383 # frozen: v10.3.0
|
||||
hooks:
|
||||
- id: cspell
|
||||
name: check changed files spelling
|
||||
|
||||
@@ -22,6 +22,33 @@ API version 2 is available in `xrpld` version 2.0.0 and later. See [API-VERSION-
|
||||
|
||||
This version is supported by all `xrpld` versions. For WebSocket and HTTP JSON-RPC requests, it is currently the default API version used when no `api_version` is specified.
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Bugfixes
|
||||
|
||||
- A request echoed back in an error reply now has every credential-bearing field masked: `admin_password`, `admin_user`, `passphrase`, `password`, `secret`, `seed`, `seed_hex`, `url_password`, `url_username` and `username`. Nesting no longer matters, so a credential inside `params` is masked too. The same masking is applied to every request and reply written to the log, and it covers six further names that only a reply carries: `master_key`, `master_seed`, `master_seed_hex`, `validation_key`, `validation_private_key` and `validation_seed`, which is how `wallet_propose` and `validation_create` used to write a live private key to the log. A request or reply written to the log is truncated at 10,000 characters.
|
||||
- The command line client no longer prints a credential the operator did not type. A failing command echoes the request it built under `request_sent`, which carries the `admin_password` the client copies out of `[port_rpc]` in the config, so `./xrpld account_info rBogus` printed that password to stdout and into any captured output. `request_sent` is now masked. The `rpc` member beside it, which echoes the arguments as they were typed, is unchanged. The command line client also no longer writes an unparsed `json` or `ripple_path_find` argument to its trace log before parsing it, where a `secret` inside that argument could not be masked; it logs the parsed request instead, masked. The reply it receives is logged the same way, parsed and masked, where the raw body was written before, a `validation_create` answer included.
|
||||
- A WebSocket frame that does not parse, or exceeds the request size limit, is answered `{"type": "error", "error": "jsonInvalid", "size": <bytes>}`. The frame's body is reported by size rather than echoed back in a `value` member, since a body that does not parse has no fields to mask. A client that read `value` gets `size` instead.
|
||||
- Four error codes that named no HTTP status of their own, and so answered 200 on a reply reporting an error, now name one: `actMalformed`, `alreadyMultisig` and `alreadySingleSig` answer 400, and `actNotFound` answers 404. **No shipped envelope reports these four.** A request sending `ripplerpc: "3.0"` still receives 200 for all four, as it always has, so `account_info` on a malformed account or one the ledger does not hold answers 200 exactly as before.
|
||||
|
||||
## XRP Ledger server version 3.5.0
|
||||
|
||||
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 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.
|
||||
@@ -41,6 +68,8 @@ Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta
|
||||
- `gateway_balances`: The `account` and `ident` fields now return an `invalidParams` error if the value is not a string, instead of an `internal` error. [#7655](https://github.com/XRPLF/rippled/pull/7655)
|
||||
- `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
|
||||
|
||||
|
||||
14
BUILD.md
14
BUILD.md
@@ -41,15 +41,19 @@ branch.
|
||||
git checkout develop
|
||||
```
|
||||
|
||||
For a release candidate, choose the relevant release branch, e.g.
|
||||
`release/3.2.x`.
|
||||
For a release or release candidate, check out its [tag](https://github.com/XRPLF/rippled/releases), e.g.:
|
||||
|
||||
```bash
|
||||
git checkout release/3.2.x
|
||||
git checkout 3.4.0
|
||||
```
|
||||
|
||||
For a stable release, choose one of the [tagged
|
||||
releases](https://github.com/XRPLF/rippled/releases).
|
||||
See [RELEASING.md](./RELEASING.md) for how branches and releases are organized.
|
||||
|
||||
A build reports `0.0.0-dev` with its short commit hash as build metadata, e.g.
|
||||
`0.0.0-dev+0123abc`. Only builds of a release, from a tag in CI or a versioned
|
||||
Conan reference, report a release version. To use another version, set the
|
||||
`FORCE_XRPLD_VERSION` environment variable when running CMake, e.g.
|
||||
`FORCE_XRPLD_VERSION=3.4.0`.
|
||||
|
||||
### Set Up Conan
|
||||
|
||||
|
||||
@@ -77,7 +77,6 @@ endif()
|
||||
include(PatchNixBinary)
|
||||
|
||||
include(XrplSanity)
|
||||
include(XrplVersion)
|
||||
include(XrplSettings)
|
||||
# this check has to remain in the top-level cmake because of the early return statement
|
||||
if(packages_only)
|
||||
@@ -115,6 +114,7 @@ find_package(secp256k1 REQUIRED)
|
||||
find_package(SOCI REQUIRED)
|
||||
find_package(SQLite3 REQUIRED)
|
||||
find_package(xxHash REQUIRED)
|
||||
find_package(xrpl-rpc-spec REQUIRED)
|
||||
|
||||
target_link_libraries(
|
||||
xrpl_libs
|
||||
@@ -175,6 +175,7 @@ include(XrplPackaging)
|
||||
if(tests)
|
||||
include(CTest)
|
||||
add_subdirectory(src/tests/libxrpl)
|
||||
add_subdirectory(src/tests/xrpld)
|
||||
endif()
|
||||
|
||||
if(benchmark)
|
||||
|
||||
614
CONTRIBUTING.md
614
CONTRIBUTING.md
@@ -14,9 +14,13 @@ The following branches exist in the main project repository:
|
||||
|
||||
- `develop`: The latest set of unreleased features, and the most common
|
||||
starting point for contributions.
|
||||
- `release/*` (e.g. `release/3.2.x`): Release branches, one per release line,
|
||||
holding the latest release candidate, or stable release for that line.
|
||||
Stable releases are published as [tagged releases](https://github.com/XRPLF/rippled/releases).
|
||||
- `staging/*` (e.g. `staging/3.4.x`): Staging branches, one per release line,
|
||||
where fixes for that line are developed.
|
||||
- `release/*` (e.g. `release/3.4.x`): Release branches, one per release line,
|
||||
holding the latest public release candidate or release for that line.
|
||||
Releases are published as [tagged releases](https://github.com/XRPLF/rippled/releases).
|
||||
|
||||
See [RELEASING.md](./RELEASING.md) for how these branches are used.
|
||||
|
||||
The tip of each branch must be signed. In order for GitHub to sign a
|
||||
squashed commit that it builds from your pull request, GitHub must know
|
||||
@@ -145,8 +149,8 @@ tl;dr
|
||||
|
||||
In general, pull requests use `develop` as the base branch.
|
||||
|
||||
The exceptions are fixes, improvements, and hotfixes for an existing release,
|
||||
which use that release's branch (e.g. `release/3.2.x`) as the base.
|
||||
The exceptions are fixes for an existing release line,
|
||||
which use that line's staging branch (e.g. `staging/3.4.x`) as the base.
|
||||
|
||||
If your changes are not quite ready, but you want to make it easily available
|
||||
for preliminary examination or review, you can create a "Draft" pull request.
|
||||
@@ -479,8 +483,12 @@ exists, then no other unit test will be executed, apart from `TestSuiteName`.
|
||||
elsewhere in the codebase.
|
||||
12. Use clear and self-explanatory names for functions, variables,
|
||||
structs and classes.
|
||||
13. Use TitleCase for classes, structs and filenames, camelCase for
|
||||
function and variable names, lower case for namespaces and folders.
|
||||
13. Use TitleCase for classes, structs, type aliases and filenames,
|
||||
camelCase for function and variable names, lower case for namespaces and
|
||||
folders. The exception is a type alias that generic code looks up by name
|
||||
(`value_type`, `iterator`, `result_type`, and the rest of the standard
|
||||
container, hash and clock members), which keeps its snake_case spelling;
|
||||
`.clang-tidy` lists the names that are allowed.
|
||||
14. Provide as many comments as you feel that a competent programmer
|
||||
would need to understand what your code does.
|
||||
|
||||
@@ -587,15 +595,16 @@ the suggested commit message, or modify it as needed.
|
||||
|
||||
#### Slightly more complicated pull requests
|
||||
|
||||
Some pull requests need to be pushed to `develop` as more than one
|
||||
commit. A PR author may _request_ to merge as separate commits. They
|
||||
Some pull requests need to be pushed to their base branch (usually `develop`)
|
||||
as more than one commit.
|
||||
A PR author may _request_ to merge as separate commits. They
|
||||
must _justify_ why separate commits are needed, and _specify_ how they
|
||||
would like the commits to be merged. If you disagree with the author,
|
||||
discuss it with them directly.
|
||||
|
||||
If the process is reasonable, follow it. The simplest option is to do a
|
||||
fast forward only merge (`--ff-only`) on the command line and push to
|
||||
`develop`.
|
||||
fast forward only merge (`--ff-only`) on the command line
|
||||
and push to the base branch.
|
||||
|
||||
Some examples of when separate commits are worthwhile are:
|
||||
|
||||
@@ -608,9 +617,10 @@ Some examples of when separate commits are worthwhile are:
|
||||
|
||||
Either way, check that:
|
||||
|
||||
- The commits are based on the current tip of `develop`.
|
||||
- The commits are clean: No merge commits (except when reverse
|
||||
merging), no "[FOLD]" or "fixup!" messages.
|
||||
- The commits are based on the current tip of the base branch.
|
||||
- The commits are clean:
|
||||
No merge commits (except when merging a release, see [RELEASING.md](./RELEASING.md)),
|
||||
no "[FOLD]" or "fixup!" messages.
|
||||
- All commits are signed. If the commits are not signed by the author, use
|
||||
`git commit --amend -S` to sign them yourself.
|
||||
- At least one (but preferably all) of the commits has the PR number
|
||||
@@ -622,578 +632,8 @@ use them!**
|
||||
|
||||
### Releases
|
||||
|
||||
All releases, including release candidates and betas, are handled
|
||||
differently from typical PRs. Most importantly, never use
|
||||
the Github UI to merge a release.
|
||||
|
||||
Xrpld uses a linear workflow model that can be summarized as:
|
||||
|
||||
1. In between releases, developers work against the `develop` branch.
|
||||
2. Periodically, a maintainer will build and tag a beta version from
|
||||
`develop`, which is pushed to `release`.
|
||||
- Betas are usually released every two to three weeks, though that
|
||||
schedule can vary depending on progress, availability, and other
|
||||
factors.
|
||||
3. When the changes in `develop` are considered stable and mature enough
|
||||
to be ready to release, a release candidate (RC) is built and tagged
|
||||
from `develop`, and merged to `release`.
|
||||
- Further development for that release (primarily fixes) then
|
||||
continues against `release`, while other development continues on
|
||||
`develop`. Effectively, `release` is forked from `develop`. Changes
|
||||
to `release` must be reverse merged to `develop`.
|
||||
4. When the candidate has passed testing and is ready for release, the
|
||||
final release is merged to `master`.
|
||||
5. If any issues are found post-release, a hotfix / point release may be
|
||||
created, which is merged to `master`, and then reverse merged to
|
||||
`develop`.
|
||||
|
||||
#### Betas, and the first release candidate
|
||||
|
||||
##### Preparing the `develop` branch
|
||||
|
||||
1. Optimally, the `develop` branch will be ready to go, with all
|
||||
relevant PRs already merged.
|
||||
2. If there are any PRs pending, merge them **BEFORE** preparing the beta.
|
||||
1. If only one or two PRs need to be merged, merge those PRs [as
|
||||
normal](#when-and-how-to-merge-pull-requests), updating the second
|
||||
one, and waiting for CI to finish in between.
|
||||
2. If there are several pending PRs, do not use the Github UI,
|
||||
because the delays waiting for CI in between each merge will be
|
||||
unnecessarily onerous. (Incidentally, this process can also be
|
||||
used to merge if the Github UI has issues.) Merge each PR branch
|
||||
directly to a `release-next` on your local machine and create a single
|
||||
PR, then push your branch to `develop`.
|
||||
1. Squash the changes from each PR, one commit each (unless more
|
||||
are needed), being sure to sign each commit and update the
|
||||
commit message to include the PR number. You may be able to use
|
||||
a fast-forward merge for the first PR.
|
||||
2. Push your branch.
|
||||
3. Continue to [Making the release](#making-the-release) to update
|
||||
the version number, etc.
|
||||
|
||||
The workflow may look something like:
|
||||
|
||||
```
|
||||
git fetch --multiple upstreams user1 user2 user3 [...]
|
||||
git checkout -B release-next --no-track upstream/develop
|
||||
|
||||
# Only do an ff-only merge if pr-branch1 is either already
|
||||
# squashed, or needs to be merged with separate commits,
|
||||
# and has no merge commits.
|
||||
# Use -S on the ff-only merge if pr-branch1 isn't signed.
|
||||
git merge [-S] --ff-only user1/pr-branch1
|
||||
|
||||
git merge --squash user2/pr-branch2
|
||||
git commit -S # Use the commit message provided on the PR
|
||||
|
||||
git merge --squash user3/pr-branch3
|
||||
git commit -S # Use the commit message provided on the PR
|
||||
|
||||
[...]
|
||||
|
||||
# Make sure the commits look right
|
||||
git log --show-signature "upstream/develop..HEAD"
|
||||
|
||||
git push --set-upstream origin
|
||||
|
||||
# Continue to "Making the release" to update the version number, so
|
||||
# everything can be done in one PR.
|
||||
```
|
||||
|
||||
You can also use the [squash-branches] script.
|
||||
|
||||
You may also need to manually close the open PRs after the changes are
|
||||
merged to `develop`. Be sure to include the commit ID.
|
||||
|
||||
##### Making the release
|
||||
|
||||
This includes, betas, and the first release candidate (RC).
|
||||
|
||||
1. If you didn't create one [preparing the `develop`
|
||||
branch](#preparing-the-develop-branch), Ensure there is no old
|
||||
`release-next` branch hanging around. Then make a `release-next`
|
||||
branch that only changes the version number. e.g.
|
||||
|
||||
```
|
||||
git fetch upstreams
|
||||
|
||||
git checkout --no-track -B release-next upstream/develop
|
||||
|
||||
v="A.B.C-bD"
|
||||
build=$( find -name BuildInfo.cpp )
|
||||
sed 's/\(^.*versionString =\).*$/\1 "'${v}'"/' ${build} > version.cpp && mv -vi version.cpp ${build}
|
||||
|
||||
git diff
|
||||
|
||||
git add ${build}
|
||||
|
||||
git commit -S -m "Set version to ${v}"
|
||||
|
||||
# You could use your "origin" repo, but some CI tests work better on upstream.
|
||||
git push upstream-push
|
||||
git fetch upstreams
|
||||
git branch --set-upstream-to=upstream/release-next
|
||||
```
|
||||
|
||||
You can also use the [update-version] script. 2. Create a Pull Request for `release-next` with **`develop`** as
|
||||
the base branch.
|
||||
|
||||
1. Use the title "[TRIVIAL] Set version to X.X.X-bX".
|
||||
2. Instead of the default description template, use the following:
|
||||
|
||||
```
|
||||
## High Level Overview of Change
|
||||
|
||||
This PR only changes the version number. It will be merged as
|
||||
soon as Github CI actions successfully complete.
|
||||
```
|
||||
|
||||
3. Wait for CI to successfully complete, and get someone to approve
|
||||
the PR. (It is safe to ignore known CI issues.)
|
||||
4. Push the updated `develop` branch using your `release-next`
|
||||
branch. **Do not use the Github UI. It's important to preserve
|
||||
commit IDs.**
|
||||
|
||||
```
|
||||
git push upstream-push release-next:develop
|
||||
```
|
||||
|
||||
5. In the unlikely event that the push fails because someone has merged
|
||||
something else in the meantime, rebase your branch onto the updated
|
||||
`develop` branch, push again, and go back to step 3.
|
||||
6. Ensure that your PR against `develop` is closed. Github should do it
|
||||
automatically.
|
||||
7. Once this is done, forward progress on `develop` can continue
|
||||
(other PRs may be merged).
|
||||
8. Now create a Pull Request for `release-next` with **`release`** as
|
||||
the base branch. Instead of the default template, reuse and update
|
||||
the message from the previous release. Include the following verbiage
|
||||
somewhere in the description:
|
||||
|
||||
```
|
||||
The base branch is `release`. [All releases (including
|
||||
betas)](https://github.com/XRPLF/rippled/blob/develop/CONTRIBUTING.md#before-you-start)
|
||||
go in `release`. This PR branch will be pushed directly to `release` (not
|
||||
squashed or rebased, and not using the GitHub UI).
|
||||
```
|
||||
|
||||
7. Sign-offs for the three platforms (Linux, Mac, Windows) usually occur
|
||||
offline, but at least one approval will be needed on the PR.
|
||||
- If issues are discovered during testing, simply abandon the
|
||||
release. It's easy to start a new release, it should be easy to
|
||||
abandon one. **DO NOT REUSE THE VERSION NUMBER.** e.g. If you
|
||||
abandon 2.4.0-b1, the next attempt will be 2.4.0-b2.
|
||||
8. Once everything is ready to go, push to `release`.
|
||||
|
||||
```
|
||||
git fetch upstreams
|
||||
|
||||
# Just to be safe, do a dry run first:
|
||||
git push --dry-run upstream-push release-next:release
|
||||
|
||||
# If everything looks right, push the branch
|
||||
git push upstream-push release-next:release
|
||||
|
||||
# Check that all of the branches are updated
|
||||
git fetch upstreams
|
||||
git log -1 --oneline
|
||||
# The output should look like:
|
||||
# 0123456789 (HEAD -> upstream/release-next, upstream/release,
|
||||
# upstream/develop) Set version to 2.4.0-b1
|
||||
# Note that upstream/develop may not be on this commit, but
|
||||
# upstream/release must be.
|
||||
# Other branches, including some from upstream-push, may also be
|
||||
# present.
|
||||
```
|
||||
|
||||
9. Tag the release, too.
|
||||
|
||||
```
|
||||
git tag <version number>
|
||||
git push upstream-push <version number>
|
||||
```
|
||||
|
||||
10. Delete the `release-next` branch on the repo. Use the Github UI or:
|
||||
|
||||
```
|
||||
git push --delete upstream-push release-next
|
||||
```
|
||||
|
||||
11. Finally [create a new release on
|
||||
Github](https://github.com/XRPLF/rippled/releases).
|
||||
|
||||
#### Release candidates after the first
|
||||
|
||||
Once the first release candidate is [merged into
|
||||
release](#making-the-release), then `release` and `develop` _are allowed
|
||||
to diverge_.
|
||||
|
||||
If a bug or issue is discovered in a version that has a release
|
||||
candidate being tested, any fix and new version will need to be applied
|
||||
against `release`, then reverse-merged to `develop`. This helps keep git
|
||||
history as linear as possible.
|
||||
|
||||
A `release-next` branch will be created from `release`, and any further
|
||||
work for that release must be based on `release-next`. Specifically,
|
||||
PRs must use `release-next` as the base, and those PRs will be merged
|
||||
directly to `release-next` when approved. Changes should be restricted
|
||||
to bug fixes, but other changes may be necessary from time to time.
|
||||
|
||||
1. Open any PRs for the pending release using `release-next` as the base,
|
||||
so they can be merged directly in to it. Unlike `develop`, though,
|
||||
`release-next` can be thrown away and recreated if necessary.
|
||||
2. Once a new release candidate is ready, create a version commit as in
|
||||
step 1 [above](#making-the-release) on `release-next`. You can use
|
||||
the [update-version] script for this, too.
|
||||
3. Jump to step 8 ("Now create a Pull Request for `release-next` with
|
||||
**`release`** as the base") from the process
|
||||
[above](#making-the-release) to merge `release-next` into `release`.
|
||||
|
||||
##### Follow up: reverse merge
|
||||
|
||||
Once the RC is merged and tagged, it needs to be reverse merged into
|
||||
`develop` as soon as possible.
|
||||
|
||||
1. Create a branch, based on `upstream/develop`.
|
||||
The branch name is not important, but could include "mergeNNNrcN".
|
||||
E.g. For release A.B.C-rcD, use `mergeABCrcD`.
|
||||
|
||||
```
|
||||
git fetch upstreams
|
||||
|
||||
git checkout --no-track -b mergeABCrcD upstream/develop
|
||||
```
|
||||
|
||||
2. Merge `release` into your branch.
|
||||
|
||||
```
|
||||
# I like the "--edit --log --verbose" parameters, but they are
|
||||
# not required.
|
||||
git merge upstream/release
|
||||
```
|
||||
|
||||
3. `BuildInfo.cpp` will have a conflict with the version number.
|
||||
Resolve it with the version from `develop` - the higher version.
|
||||
4. Push your branch to your repo (or `upstream` if you have permission),
|
||||
and open a normal PR against `develop`. The "High level overview" can
|
||||
simply indicate that this is a merge of the RC. The "Context" should
|
||||
summarize the changes from the RC. Include the following text
|
||||
prominently:
|
||||
|
||||
```
|
||||
This PR must be merged manually using a push. Do not use the Github UI.
|
||||
```
|
||||
|
||||
5. Depending on the complexity of the changes, and/or merge conflicts,
|
||||
the PR may need a thorough review, or just a sign-off that the
|
||||
merge was done correctly.
|
||||
6. If `develop` is updated before this PR is merged, do not merge
|
||||
`develop` back into your branch. Instead rebase preserving merges,
|
||||
or do the merge again. (See also the `rerere` git config setting.)
|
||||
|
||||
```
|
||||
git rebase --rebase-merges upstream/develop
|
||||
# OR
|
||||
git reset --hard upstream/develop
|
||||
git merge upstream/release
|
||||
```
|
||||
|
||||
7. When the PR is ready, push it to `develop`.
|
||||
|
||||
```
|
||||
git fetch upstreams
|
||||
|
||||
# Make sure the commits look right
|
||||
git log --show-signature "upstream/develop^..HEAD"
|
||||
|
||||
git push upstream-push mergeABCrcD:develop
|
||||
|
||||
git fetch upstreams
|
||||
```
|
||||
|
||||
Development on `develop` can proceed as normal.
|
||||
|
||||
#### Final releases
|
||||
|
||||
A final release is any release that is not a beta or RC, such as 2.2.0.
|
||||
|
||||
Only code that has already been tested and vetted across all three
|
||||
platforms should be included in a final release. Most of the time, that
|
||||
means that the commit immediately preceding the commit setting the
|
||||
version number will be an RC. Occasionally, there may be last-minute bug
|
||||
fixes included as well. If so, those bug fixes must have been tested
|
||||
internally as if they were RCs (at minimum, ensuring unit tests pass,
|
||||
and the app starts, syncs, and stops cleanly across all three
|
||||
platforms.)
|
||||
|
||||
_If in doubt, make an RC first._
|
||||
|
||||
The process for building a final release is very similar to [the process
|
||||
for building a beta](#making-the-release), except the code will be
|
||||
moving from `release` to `master` instead of from `develop` to
|
||||
`release`, and both branches will be pushed at the same time.
|
||||
|
||||
1. Ensure there is no old `master-next` branch hanging around.
|
||||
Then make a `master-next` branch that only changes the version
|
||||
number. As above, or using the
|
||||
[update-version] script.
|
||||
2. Create a Pull Request for `master-next` with **`master`** as
|
||||
the base branch. Instead of the default template, reuse and update
|
||||
the message from the previous final release. Include the following verbiage
|
||||
somewhere in the description:
|
||||
|
||||
```
|
||||
The base branch is `master`. This PR branch will be pushed directly to
|
||||
`release` and `master` (not squashed or rebased, and not using the
|
||||
GitHub UI).
|
||||
```
|
||||
|
||||
7. Sign-offs for the three platforms (Linux, Mac, Windows) usually occur
|
||||
offline, but at least one approval will be needed on the PR.
|
||||
- If issues are discovered during testing, close the PR, delete
|
||||
`master-next`, and move development back to `release`, [issuing
|
||||
more RCs as necessary](#release-candidates-after-the-first)
|
||||
8. Once everything is ready to go, push to `release` and `master`.
|
||||
|
||||
```
|
||||
git fetch upstreams
|
||||
|
||||
# Just to be safe, do dry runs first:
|
||||
git push --dry-run upstream-push master-next:release
|
||||
git push --dry-run upstream-push master-next:master
|
||||
|
||||
# If everything looks right, push the branch
|
||||
git push upstream-push master-next:release
|
||||
git push upstream-push master-next:master
|
||||
|
||||
# Check that all of the branches are updated
|
||||
git fetch upstreams
|
||||
git log -1 --oneline
|
||||
# The output should look like:
|
||||
# 0123456789 (HEAD -> upstream/master-next, upstream/master,
|
||||
# upstream/release) Set version to A.B.0
|
||||
# Note that both upstream/release and upstream/master must be on this
|
||||
# commit.
|
||||
# Other branches, including some from upstream-push, may also be
|
||||
# present.
|
||||
```
|
||||
|
||||
9. Tag the release, too.
|
||||
|
||||
```
|
||||
git tag <version number>
|
||||
git push upstream-push <version number>
|
||||
```
|
||||
|
||||
10. Delete the `master-next` branch on the repo. Use the Github UI or:
|
||||
|
||||
```
|
||||
git push --delete upstream-push master-next
|
||||
```
|
||||
|
||||
11. [Create a new release on
|
||||
Github](https://github.com/XRPLF/rippled/releases). Be sure that
|
||||
"Set as the latest release" is checked.
|
||||
12. Open a PR to update the [API-CHANGELOG](API-CHANGELOG.md) and `API-VERSION-[n].md` with the changes for this release (if any are missing).
|
||||
13. Finally, [reverse merge the release into `develop`](#follow-up-reverse-merge).
|
||||
|
||||
#### Special cases: point releases, hotfixes, etc.
|
||||
|
||||
On occasion, a bug or issue is discovered in a version that already
|
||||
had a final release. Most of the time, development will have started
|
||||
on the next version, and will usually have changes in `develop`
|
||||
and often in `release`.
|
||||
|
||||
Because git history is kept as linear as possible, any fix and new
|
||||
version will need to be applied against `master`.
|
||||
|
||||
The process for building a hotfix release is very similar to [the
|
||||
process for building release candidates after the
|
||||
first](#release-candidates-after-the-first) and [for building a final
|
||||
release](#final-releases), except the changes will be done against
|
||||
`master` instead of `release`.
|
||||
|
||||
If there is only a single issue for the hotfix, the work can be done in
|
||||
any branch. When it's ready to merge, jump to step 3 using your branch
|
||||
instead of `master-next`.
|
||||
|
||||
1. Create a `master-next` branch from `master`.
|
||||
|
||||
```
|
||||
git checkout --no-track -b master-next upstream/master
|
||||
git push upstream-push
|
||||
git fetch upstreams
|
||||
```
|
||||
|
||||
2. Open any PRs for the pending hotfix using `master-next` as the base,
|
||||
so they can be merged directly in to it. Unlike `develop`, though,
|
||||
`master-next` can be thrown away and recreated if necessary.
|
||||
3. Once the hotfix is ready, create a version commit using the same
|
||||
steps as above, or use the
|
||||
[update-version] script.
|
||||
4. Create a Pull Request for `master-next` with **`master`** as
|
||||
the base branch. Instead of the default template, reuse and update
|
||||
the message from the previous final release. Include the following verbiage
|
||||
somewhere in the description:
|
||||
|
||||
```
|
||||
The base branch is `master`. This PR branch will be pushed directly to
|
||||
`master` (not squashed or rebased, and not using the GitHub UI).
|
||||
```
|
||||
|
||||
7. Sign-offs for the three platforms (Linux, Mac, Windows) usually occur
|
||||
offline, but at least one approval will be needed on the PR.
|
||||
- If issues are discovered during testing, update `master-next` as
|
||||
needed, but ensure that the changes are properly squashed, and the
|
||||
version setting commit remains last
|
||||
8. Once everything is ready to go, push to `master` **only**.
|
||||
|
||||
```
|
||||
git fetch upstreams
|
||||
|
||||
# Just to be safe, do a dry run first:
|
||||
git push --dry-run upstream-push master-next:master
|
||||
|
||||
# If everything looks right, push the branch
|
||||
git push upstream-push master-next:master
|
||||
|
||||
# Check that all of the branches are updated
|
||||
git fetch upstreams
|
||||
git log -1 --oneline
|
||||
# The output should look like:
|
||||
# 0123456789 (HEAD -> upstream/master-next, upstream/master) Set version
|
||||
# to 2.4.1
|
||||
# Note that upstream/master must be on this commit. upstream/release and
|
||||
# upstream/develop should not.
|
||||
# Other branches, including some from upstream-push, may also be
|
||||
# present.
|
||||
```
|
||||
|
||||
9. Tag the release, too.
|
||||
|
||||
```
|
||||
git tag <version number>
|
||||
git push upstream-push <version number>
|
||||
```
|
||||
|
||||
9. Delete the `master-next` branch on the repo.
|
||||
|
||||
```
|
||||
git push --delete upstream-push master-next
|
||||
```
|
||||
|
||||
10. [Create a new release on
|
||||
Github](https://github.com/XRPLF/rippled/releases). Be sure that
|
||||
"Set as the latest release" is checked.
|
||||
|
||||
Once the hotfix is released, it needs to be reverse merged into
|
||||
`develop` as soon as possible. It may also need to be merged into
|
||||
`release` if a release candidate is under development.
|
||||
|
||||
1. Create a branch in your own repo, based on `upstream/develop`.
|
||||
The branch name is not important, but could include "mergeNNN".
|
||||
E.g. For release 2.2.3, use `merge223`.
|
||||
|
||||
```
|
||||
git fetch upstreams
|
||||
|
||||
git checkout --no-track -b merge223 upstream/develop
|
||||
```
|
||||
|
||||
2. Merge master into your branch.
|
||||
|
||||
```
|
||||
# I like the "--edit --log --verbose" parameters, but they are
|
||||
# not required.
|
||||
git merge upstream/master
|
||||
```
|
||||
|
||||
3. `BuildInfo.cpp` will have a conflict with the version number.
|
||||
Resolve it with the version from `develop` - the higher version.
|
||||
4. Push your branch to your repo, and open a normal PR against
|
||||
`develop`. The "High level overview" can simply indicate that this
|
||||
is a merge of the hotfix version. The "Context" should summarize
|
||||
the changes from the hotfix. Include the following text
|
||||
prominently:
|
||||
|
||||
```
|
||||
This PR must be merged manually using a --ff-only merge. Do not use the Github UI.
|
||||
```
|
||||
|
||||
5. Depending on the complexity of the hotfix, and/or merge conflicts,
|
||||
the PR may need a thorough review, or just a sign-off that the
|
||||
merge was done correctly.
|
||||
6. If `develop` is updated before this PR is merged, do not merge
|
||||
`develop` back into your branch. Instead rebase preserving merges,
|
||||
or do the merge again. (See also the `rerere` git config setting.)
|
||||
|
||||
```
|
||||
git rebase --rebase-merges upstream/develop
|
||||
# OR
|
||||
git reset --hard upstream/develop
|
||||
git merge upstream/master
|
||||
```
|
||||
|
||||
7. When the PR is ready, push it to `develop`.
|
||||
|
||||
```
|
||||
git fetch upstreams
|
||||
|
||||
# Make sure the commits look right
|
||||
git log --show-signature "upstream/develop..HEAD"
|
||||
|
||||
git push upstream-push HEAD:develop
|
||||
```
|
||||
|
||||
Development on `develop` can proceed as normal. It is recommended to
|
||||
create a beta (or RC) immediately to ensure that everything worked as
|
||||
expected.
|
||||
|
||||
##### An even rarer scenario: A hotfix on an old release
|
||||
|
||||
Historically, once a final release is tagged and packages are released,
|
||||
versions older than the latest final release are no longer supported.
|
||||
However, there is a possibility that a very high severity bug may occur
|
||||
in a non-amendment blocked version that is still being run by
|
||||
a significant fraction of users, which would necessitate a hotfix / point
|
||||
release to that version as well as any later versions.
|
||||
|
||||
This scenario would follow the same basic procedure as above,
|
||||
except that _none_ of `develop`, `release`, or `master`
|
||||
would be touched during the release process.
|
||||
|
||||
In this example, consider if version 2.1.1 needed to be patched.
|
||||
|
||||
1. Create two branches in the main (`upstream`) repo.
|
||||
|
||||
```
|
||||
git fetch upstreams
|
||||
|
||||
# Create a base branch off the tag
|
||||
git checkout --no-track -b master-2.1.2 2.1.1
|
||||
git push upstream-push
|
||||
|
||||
# Create a working branch
|
||||
git checkout --no-track -b master212-next master-2.1.2
|
||||
git push upstream-push
|
||||
|
||||
git fetch upstreams
|
||||
```
|
||||
|
||||
2. Work continues as above, except using `master-2.1.2`as
|
||||
the base branch for any merging, packaging, etc.
|
||||
3. After the release is tagged and packages are built, you could
|
||||
potentially delete both branches, e.g. `master-2.1.2` and
|
||||
`master212-next`. However, it may be useful to keep `master-2.1.2`
|
||||
around indefinitely for reference.
|
||||
4. Assuming that a hotfix is also released for the latest
|
||||
version in parallel with this one, or if the issue is
|
||||
already fixed in the latest version, do no do any
|
||||
reverse merges. However, if it is not, it probably makes
|
||||
sense to reverse merge `master-2.1.2` into `master`,
|
||||
release a hotfix for _that_ version, then reverse merge
|
||||
from `master` to `develop`. (Please don't do this unless absolutely
|
||||
necessary.)
|
||||
Releases, release branches, and merging releases back into `develop`
|
||||
are described in [RELEASING.md](./RELEASING.md).
|
||||
|
||||
[contrib]: https://docs.github.com/en/get-started/quickstart/contributing-to-projects
|
||||
[squash]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-commits
|
||||
@@ -1201,5 +641,3 @@ git fetch upstreams
|
||||
[xrpld]: https://github.com/XRPLF/rippled
|
||||
[signing]: https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification
|
||||
[setup-upstreams]: ./bin/git/setup-upstreams.sh
|
||||
[squash-branches]: ./bin/git/squash-branches.sh
|
||||
[update-version]: ./bin/git/update-version.sh
|
||||
|
||||
152
RELEASING.md
Normal file
152
RELEASING.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# Branching and Release Management
|
||||
|
||||
This document describes how we branch, release, and merge releases back into `develop`.
|
||||
It does not define version naming
|
||||
(e.g., what constitutes a major-minor, patch, or beta release).
|
||||
|
||||
Examples use `X.Y` for a release line:
|
||||
`X` and `Y` are placeholders, while the trailing `x` is literal,
|
||||
e.g., `release/X.Y.x` is `release/3.4.x` for the `3.4` line.
|
||||
|
||||
## Branches
|
||||
|
||||
| Branch | Purpose |
|
||||
| :-------------- | :---------------------------------------------------------------------------------------- |
|
||||
| `develop` | Main development branch. Betas and the first RC of a major-minor release are tagged here. |
|
||||
| `staging/X.Y.x` | Where fixes for the `X.Y` line are developed and RCs are prepared. |
|
||||
| `release/X.Y.x` | The last public RC or release of the `X.Y` line. |
|
||||
|
||||
A release line is named `X.Y.x`
|
||||
because one branch serves every patch release of that line (`X.Y.0`, `X.Y.1`, `X.Y.2`, ...).
|
||||
The `/` groups branches hierarchically,
|
||||
so tools can filter them and protection rules can target `release/*` and `staging/*`.
|
||||
|
||||
## Principles
|
||||
|
||||
- **Releases are merged back into `develop`, never cherry-picked or rebased onto it.**
|
||||
Cherry-picked and rebased commits get new hashes,
|
||||
so Git can't tell that `develop` already has them.
|
||||
Future merges then replay them and produce artificial conflicts,
|
||||
and it's hard to verify that every fix actually reached `develop`.
|
||||
Merging keeps a single history:
|
||||
Git knows exactly which release commits `develop` contains,
|
||||
and no fix is left behind.
|
||||
- **Branches only move forward.**
|
||||
`develop`, `staging/X.Y.x`, and `release/X.Y.x` are never rewritten.
|
||||
- **Cherry-picking only goes from `develop` to a staging branch**,
|
||||
for fixes that must get into a release after the code freeze
|
||||
(see [Emergency Fixes From `develop`](#emergency-fixes-from-develop)).
|
||||
- **Security fixes are prepared privately and published with the release that contains them**,
|
||||
so vulnerabilities are not disclosed prematurely.
|
||||
|
||||
## Release Lifecycle
|
||||
|
||||
This diagram shows a major-minor release and its first patch release.
|
||||
The staging and release branches are drawn as one line.
|
||||
|
||||
```text
|
||||
develop staging/X.Y.x & release/X.Y.x
|
||||
│
|
||||
├── Tag: X.Y.0-b1
|
||||
├── Tag: X.Y.0-bN
|
||||
├── Tag: X.Y.0-rc1 ───────────────┐ (branches created)
|
||||
│ │
|
||||
│ (development continues) ├── Fixes
|
||||
│ ├── Tag: X.Y.0-rcN
|
||||
│ ├── Tag: X.Y.0 (final)
|
||||
◀──── (merge) ────────────────────┤
|
||||
│ ├── Fixes
|
||||
│ ├── Tag: X.Y.1-rcN
|
||||
│ ├── Tag: X.Y.1 (final)
|
||||
◀──── (merge) ────────────────────┤
|
||||
│
|
||||
▼
|
||||
```
|
||||
|
||||
### Betas, First RC & Branching
|
||||
|
||||
> [!NOTE]
|
||||
> This phase applies only to a new major-minor release (e.g., `X.Y.0`).
|
||||
> Patch releases work on the existing branches of the line.
|
||||
|
||||
1. **Betas:** All beta versions (e.g., `X.Y.0-b1`) are built and tagged directly on `develop`.
|
||||
2. **First RC:** We release the first RC (`X.Y.0-rc1`)
|
||||
once everything that should be included in the release has been merged.
|
||||
3. **Branches:** `staging/X.Y.x` and `release/X.Y.x` are created from `develop`
|
||||
at the commit tagged `X.Y.0-rc1`.
|
||||
The first RC also kicks off the QE process.
|
||||
4. **Code freeze:** No new features or unrelated changes are pulled from `develop`
|
||||
into `staging/X.Y.x` or `release/X.Y.x`.
|
||||
Only critical stabilization fixes go into the line.
|
||||
5. **No large changes on `develop`:** Until `X.Y.0` is [merged back](#merging-back-into-develop),
|
||||
large changes (e.g., big refactors, moving or renaming many files, mass reformatting)
|
||||
are not merged into `develop`,
|
||||
so that fixes on the line and the merge back don't run into conflicts.
|
||||
|
||||
### Release Candidates
|
||||
|
||||
1. Fixes are developed against `staging/X.Y.x`.
|
||||
2. When ready, a new RC is created on `staging/X.Y.x`,
|
||||
and `release/X.Y.x` is fast-forwarded to it.
|
||||
|
||||
RCs that contain unpublished security fixes are not published,
|
||||
and don't touch the public branches.
|
||||
|
||||
RCs are not merged back into `develop`:
|
||||
`X.Y.0-rc1` is tagged on `develop` itself,
|
||||
and all later changes reach `develop` with the [final release](#final-release).
|
||||
|
||||
### Final Release
|
||||
|
||||
Security fixes become public as soon as they reach the public repo,
|
||||
so these steps happen only once the release is ready to be published,
|
||||
one right after the other.
|
||||
|
||||
1. Unpublished security fixes, if any, are merged into `staging/X.Y.x`.
|
||||
2. `release/X.Y.x` is fast-forwarded to `staging/X.Y.x`,
|
||||
and the release is tagged on it.
|
||||
3. The release is immediately [merged back into `develop`](#merging-back-into-develop).
|
||||
For a major-minor release, this lifts the freeze on large changes in `develop`.
|
||||
|
||||
### Merging Back Into `develop`
|
||||
|
||||
The merge back is a regular PR into `develop`
|
||||
whose branch contains a real merge commit of the release tag:
|
||||
|
||||
1. Create a branch from `develop`, run `git merge --no-ff <tag>`, and resolve any conflicts.
|
||||
2. Once the PR is approved, `develop` is fast-forwarded to the PR branch,
|
||||
so the merge commit lands as it is.
|
||||
Never squash or rebase it.
|
||||
Never add it to the merge queue either: the queue squashes PRs, which would drop the merge commit.
|
||||
3. If `develop` moves while the PR is open, redo the merge on top of the new `develop`.
|
||||
|
||||
After the merge, `git log develop..<tag>` must be empty.
|
||||
|
||||
## Special Cases
|
||||
|
||||
### Emergency Fixes From `develop`
|
||||
|
||||
If a commit was merged to `develop`
|
||||
and needs to be included in a release after the code freeze:
|
||||
|
||||
1. Create a PR that cherry-picks the commit onto `staging/X.Y.x`.
|
||||
2. Leave `develop` as it is, with no reverts.
|
||||
3. Follow the [release candidates](#release-candidates) process as usual.
|
||||
|
||||
When the release is later merged back into `develop`,
|
||||
both sides already contain the same change,
|
||||
so the merge usually resolves it cleanly or with a trivial conflict.
|
||||
From Git's perspective, the cherry-picked commit then becomes part of `develop` too.
|
||||
|
||||
### Several Supported Lines
|
||||
|
||||
When a fix must ship in more than one supported line (e.g., `X.Y` and `X.(Y+1)`),
|
||||
the lines are merged upwards rather than cherry-picked between:
|
||||
|
||||
1. The fix goes into the oldest line first.
|
||||
2. After that line's release,
|
||||
its `release/X.Y.x` is merged into the staging branch of the next newer line,
|
||||
and so on up to the newest line.
|
||||
3. The newest line is merged back into `develop` as usual.
|
||||
|
||||
This way every newer line, and eventually `develop`, contains the history of the older lines.
|
||||
@@ -20,19 +20,32 @@
|
||||
# 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.
|
||||
#
|
||||
# Environment variables:
|
||||
# CI if set, skip the tools above when on macOS.
|
||||
# CHECK_TOOLS_SKIP_CLONE if set, skip the git-over-HTTPS connectivity check.
|
||||
# XRPL_DEVSHELL active dev shell; selects shell-specific tools.
|
||||
|
||||
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
|
||||
@@ -108,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
|
||||
@@ -158,15 +172,25 @@ if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
|
||||
check cargo-nextest cargo nextest --version
|
||||
check clippy-driver
|
||||
check rust-analyzer
|
||||
check rust-nightly rust-nightly run rustc --version
|
||||
check rustc
|
||||
check rustfmt
|
||||
fi
|
||||
|
||||
# Lean4 is in the formal-verification shell only, not in commonPackages.
|
||||
if [ "${XRPL_DEVSHELL:-}" = "formal-verification" ]; then
|
||||
echo
|
||||
echo "Formal verification toolchain:"
|
||||
check lean
|
||||
check lake
|
||||
fi
|
||||
|
||||
# GCC is the default compiler on Linux. macOS uses the system Apple Clang
|
||||
# instead, so GCC/g++/gcov are not expected there.
|
||||
if [ "${os}" = "linux" ]; then
|
||||
echo
|
||||
echo "GCC toolchain:"
|
||||
gcc_version="$(major_version gcc)"
|
||||
check gcc
|
||||
check "gcc-${gcc_version}"
|
||||
check g++
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ $# -ne 3 || "$1" == "--help" || "$1" = "-h" ]]; then
|
||||
name=$(basename $0)
|
||||
cat <<-USAGE
|
||||
Usage: $name workbranch base/branch version
|
||||
|
||||
* workbranch will be created locally from base/branch. If it exists,
|
||||
it will be reused, so make sure you don't overwrite any work.
|
||||
* base/branch may be specified as user:branch to allow easy copying
|
||||
from Github PRs.
|
||||
USAGE
|
||||
exit 0
|
||||
fi
|
||||
|
||||
work="$1"
|
||||
shift
|
||||
|
||||
base=$(echo "$1" | sed "s/:/\//")
|
||||
shift
|
||||
|
||||
version=$1
|
||||
shift
|
||||
|
||||
set -e
|
||||
|
||||
git fetch upstreams
|
||||
|
||||
git checkout -B "${work}" --no-track "${base}"
|
||||
|
||||
push=$(git rev-parse --abbrev-ref --symbolic-full-name '@{push}' \
|
||||
2>/dev/null) || true
|
||||
if [[ "${push}" != "" ]]; then
|
||||
echo "Warning: ${push} may already exist."
|
||||
fi
|
||||
|
||||
build=$(find -name BuildInfo.cpp)
|
||||
sed 's/\(^.*versionString =\).*$/\1 "'${version}'"/' ${build} >version.cpp &&
|
||||
diff "${build}" version.cpp && exit 1 ||
|
||||
mv -vi version.cpp ${build}
|
||||
|
||||
git diff
|
||||
|
||||
git add ${build}
|
||||
|
||||
git commit -S -m "Set version to ${version}"
|
||||
|
||||
git log --oneline --first-parent ${base}^..
|
||||
|
||||
cat <<PUSH
|
||||
|
||||
-------------------------------------------------------------------
|
||||
This script will not push. Verify everything is correct, then push
|
||||
to your repo, and create a PR as described in CONTRIBUTING.md.
|
||||
-------------------------------------------------------------------
|
||||
PUSH
|
||||
65
bin/nix/check-build-context-runtime.sh
Executable file
65
bin/nix/check-build-context-runtime.sh
Executable 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
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
149
bin/pre-commit/check_rust_coverage_attrs.py
Executable file
149
bin/pre-commit/check_rust_coverage_attrs.py
Executable file
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Check that Rust unit tests stay out of the coverage report.
|
||||
|
||||
cargo-llvm-cov instruments the test code along with everything else, so a test
|
||||
module that is not excluded counts its own body as covered and inflates the
|
||||
reported number. Excluding it takes two attributes:
|
||||
|
||||
* every `#[cfg(test)]` module carries
|
||||
`#[cfg_attr(coverage_nightly, coverage(off))]`;
|
||||
* every crate root (lib.rs, main.rs) carries
|
||||
`#![cfg_attr(coverage_nightly, feature(coverage_attribute))]`, which the
|
||||
attribute above needs in order to compile.
|
||||
|
||||
Both are inert outside the coverage job: cargo-llvm-cov defines
|
||||
`coverage_nightly` only when it runs on a nightly toolchain.
|
||||
|
||||
The crate-root gate is checked even in a crate that has no tests yet, because
|
||||
that is what lets the first test module added later carry the attribute without
|
||||
a build failure. Missing it is a hard error, so it cannot go unnoticed; a
|
||||
missing `coverage(off)` fails open, which is why this check exists.
|
||||
|
||||
Matching is on exact attribute text, which works because `cargo fmt` runs over
|
||||
the whole workspace in the hook ahead of this one: rustfmt puts every attribute
|
||||
on its own line and normalizes what is inside it, turning `#[cfg( test )]`
|
||||
and `#[cfg(test,)]` alike into `#[cfg(test)]`. So there is nothing here that
|
||||
parses Rust. The price is that a cfg this file does not spell out literally --
|
||||
`all(test, ...)`, `any(test, ...)`, `not(test)` -- is reported rather than
|
||||
classified, on the grounds that guessing at coverage semantics is how a check
|
||||
like this ends up quietly wrong.
|
||||
|
||||
Usage: ./bin/pre-commit/check_rust_coverage_attrs.py <file1> <file2> ...
|
||||
|
||||
Exit status is non-zero if any violation is found.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
CRATE_ROOTS = {"lib.rs", "main.rs"}
|
||||
|
||||
FEATURE_ATTR = "#![cfg_attr(coverage_nightly, feature(coverage_attribute))]"
|
||||
COVERAGE_OFF_ATTR = "#[cfg_attr(coverage_nightly, coverage(off))]"
|
||||
CFG_TEST_ATTR = "#[cfg(test)]"
|
||||
|
||||
# Any other cfg that mentions `test`. String literals are blanked before this
|
||||
# runs, so `feature = "test"` does not read as the `test` cfg.
|
||||
RE_CFG_MENTIONS_TEST = re.compile(r"^#\[cfg\(.*\btest\b.*\)\]$")
|
||||
RE_STRING = re.compile(r'"(?:[^"\\]|\\.)*"')
|
||||
RE_MOD = re.compile(r"^(?:pub(?:\([^)]*\))?\s+)?mod\s+([A-Za-z_]\w*)")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Finding:
|
||||
line: int
|
||||
label: str
|
||||
message: str
|
||||
|
||||
|
||||
def _check_module(attrs: list[str], line: int, name: str) -> list[Finding]:
|
||||
"""Findings for one module, given the attributes attached to it."""
|
||||
if COVERAGE_OFF_ATTR in attrs:
|
||||
return [] # excluded from coverage; which cfg gates it does not matter
|
||||
if CFG_TEST_ATTR in attrs:
|
||||
return [
|
||||
Finding(
|
||||
line,
|
||||
"missing-coverage-off",
|
||||
f"`mod {name}` is #[cfg(test)] but not excluded from coverage; "
|
||||
f"add {COVERAGE_OFF_ATTR}",
|
||||
)
|
||||
]
|
||||
unclassified = [
|
||||
attr for attr in attrs if RE_CFG_MENTIONS_TEST.match(RE_STRING.sub('""', attr))
|
||||
]
|
||||
if unclassified:
|
||||
return [
|
||||
Finding(
|
||||
line,
|
||||
"unclassified-cfg",
|
||||
f"`mod {name}` is gated on {unclassified[0]}, which this check "
|
||||
f"cannot tell apart from a module that ships in the library; "
|
||||
f"add {COVERAGE_OFF_ATTR} if it is test-only, or teach this "
|
||||
f"check the cfg if it is not",
|
||||
)
|
||||
]
|
||||
return []
|
||||
|
||||
|
||||
def _check_test_modules(lines: list[str]) -> list[Finding]:
|
||||
"""Findings for every test module that is not excluded from coverage."""
|
||||
findings: list[Finding] = []
|
||||
attrs: list[str] = []
|
||||
attrs_line = 0
|
||||
for number, raw in enumerate(lines, start=1):
|
||||
stripped = raw.strip()
|
||||
# Blank lines and comments are allowed between an attribute and its item.
|
||||
if not stripped or stripped.startswith("//"):
|
||||
continue
|
||||
if stripped.startswith("#["):
|
||||
if not attrs:
|
||||
attrs_line = number
|
||||
attrs.append(stripped)
|
||||
continue
|
||||
module = RE_MOD.match(stripped)
|
||||
if module is not None and attrs:
|
||||
findings += _check_module(attrs, attrs_line, module.group(1))
|
||||
attrs = []
|
||||
return findings
|
||||
|
||||
|
||||
def _check_crate_root(name: str, lines: list[str]) -> list[Finding]:
|
||||
"""A finding if a crate root is missing the coverage_attribute feature gate."""
|
||||
if name not in CRATE_ROOTS:
|
||||
return []
|
||||
if any(line.strip() == FEATURE_ATTR for line in lines):
|
||||
return []
|
||||
return [
|
||||
Finding(
|
||||
1,
|
||||
"missing-feature-gate",
|
||||
f"crate root is missing {FEATURE_ATTR}",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def check_source(name: str, text: str) -> list[Finding]:
|
||||
"""Findings for one file's contents; `name` is its base name (lib.rs, ...)."""
|
||||
lines = text.splitlines()
|
||||
return _check_crate_root(name, lines) + _check_test_modules(lines)
|
||||
|
||||
|
||||
def check_file(path: Path) -> list[Finding]:
|
||||
return check_source(path.name, path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
total = 0
|
||||
for path in (Path(name) for name in sys.argv[1:]):
|
||||
for finding in check_file(path):
|
||||
total += 1
|
||||
print(f"{path}:{finding.line}: {finding.label}: {finding.message}")
|
||||
return 1 if total else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -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
|
||||
|
||||
144
bin/pre-commit/fix_gtest_names.py
Executable file
144
bin/pre-commit/fix_gtest_names.py
Executable file
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Rewrites gtest names to the required style in this project: the suite name is
|
||||
CamelCase, the test-case name is snake_case.
|
||||
|
||||
TEST(SuiteName, test_case_name)
|
||||
|
||||
The gtest `DISABLED_` prefix is kept verbatim on either name.
|
||||
|
||||
Both conversions fold acronyms the way a reader expects:
|
||||
`SetAndResetAccountTxnID` -> `set_and_reset_account_txn_id`, not
|
||||
`set_and_reset_account_txn_i_d`.
|
||||
|
||||
The first argument of `TEST_F`, `TEST_P`, `TYPED_TEST` and `TYPED_TEST_P` is a
|
||||
fixture class rather than a free identifier, so rewriting it here would leave
|
||||
the class it names behind. Those are reported for a human to rename (clang-tidy
|
||||
checks the class declaration itself, via readability-identifier-naming).
|
||||
|
||||
Usage: ./bin/pre-commit/fix_gtest_names.py <file1> <file2> ...
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
# A test-case definition, `MACRO(SuiteOrFixture, TestName)`, anchored at the
|
||||
# start of a line so that commented-out definitions and project macros that
|
||||
# merely look similar (`TEST_EXPECT(...)`) are left alone. The `\s*` between
|
||||
# arguments allows for a definition clang-format wrapped over several lines.
|
||||
PATTERN = re.compile(
|
||||
r"(?P<head>^[ \t]*(?P<macro>TYPED_TEST_P|TYPED_TEST|TEST_F|TEST_P|TEST)\s*\(\s*)"
|
||||
r"(?P<suite>\w+)(?P<mid>\s*,\s*)(?P<name>\w+)(?P<tail>\s*\))",
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
# The macros whose first argument names a fixture class, not a free identifier.
|
||||
FIXTURE_MACROS = ("TEST_F", "TEST_P", "TYPED_TEST", "TYPED_TEST_P")
|
||||
|
||||
DISABLED = "DISABLED_"
|
||||
|
||||
ACRONYM_BOUNDARY = re.compile(r"([A-Z]+)([A-Z][a-z])")
|
||||
WORD_BOUNDARY = re.compile(r"([a-z\d])([A-Z])")
|
||||
|
||||
|
||||
def _split_disabled(name: str) -> tuple[str, str]:
|
||||
"""Splits off gtest's `DISABLED_` prefix, which is kept verbatim."""
|
||||
if name.startswith(DISABLED):
|
||||
return DISABLED, name[len(DISABLED) :]
|
||||
return "", name
|
||||
|
||||
|
||||
def snake_case(name: str) -> str:
|
||||
"""Returns the name in snake_case, leaving acronyms whole.
|
||||
|
||||
`SetAndResetAccountTxnID` -> `set_and_reset_account_txn_id`,
|
||||
`parseStatRSSkB` -> `parse_stat_rs_sk_b`.
|
||||
"""
|
||||
prefix, core = _split_disabled(name)
|
||||
core = ACRONYM_BOUNDARY.sub(r"\1_\2", core)
|
||||
return prefix + WORD_BOUNDARY.sub(r"\1_\2", core).lower()
|
||||
|
||||
|
||||
def camel_case(name: str) -> str:
|
||||
"""Returns the name in CamelCase, capitalizing each underscored word.
|
||||
|
||||
Only the letters that have to change are touched, so acronyms survive: a
|
||||
conversion that went via snake_case would turn `SHAMapTest` into
|
||||
`ShaMapTest`, whereas here it is already CamelCase and stays put.
|
||||
`json_value` -> `JsonValue`, `parseStatRSSkB` -> `ParseStatRSSkB`.
|
||||
"""
|
||||
prefix, core = _split_disabled(name)
|
||||
return prefix + "".join(w[:1].upper() + w[1:] for w in core.split("_") if w)
|
||||
|
||||
|
||||
def _corrected(match: re.Match) -> tuple[str, str]:
|
||||
"""Returns the suite and test-case names this definition should end up with."""
|
||||
suite = match["suite"]
|
||||
return (
|
||||
suite if match["macro"] in FIXTURE_MACROS else camel_case(suite),
|
||||
snake_case(match["name"]),
|
||||
)
|
||||
|
||||
|
||||
def fix_source(text: str) -> tuple[str, list[str]]:
|
||||
"""Returns the corrected text and one `line: message` report per bad name."""
|
||||
# gtest joins the suite and test names into one class name, so two test
|
||||
# cases whose joined names agree cannot coexist: `TEST(a, b_c)` and
|
||||
# `TEST(a_b, c)` both define `a_b_c_Test`. A rename that would introduce
|
||||
# such a clash is reported for a human instead of applied.
|
||||
joined = Counter("_".join(_corrected(m)) for m in PATTERN.finditer(text))
|
||||
reports = []
|
||||
|
||||
def rewrite(match: re.Match) -> str:
|
||||
suite, name = match["suite"], match["name"]
|
||||
new_suite, new_name = _corrected(match)
|
||||
line = text.count("\n", 0, match.start()) + 1
|
||||
|
||||
if match["macro"] in FIXTURE_MACROS and camel_case(suite) != suite:
|
||||
reports.append(
|
||||
f"{line}: fixture '{suite}' is not CamelCase: rename the class "
|
||||
f"to '{camel_case(suite)}' by hand"
|
||||
)
|
||||
if (new_suite, new_name) == (suite, name):
|
||||
return match[0]
|
||||
if joined[f"{new_suite}_{new_name}"] > 1:
|
||||
reports.append(
|
||||
f"{line}: cannot rename '{suite}, {name}' to '{new_suite}, "
|
||||
f"{new_name}': another test case already generates that name"
|
||||
)
|
||||
return match[0]
|
||||
if new_suite != suite:
|
||||
reports.append(f"{line}: renamed suite '{suite}' to '{new_suite}'")
|
||||
if new_name != name:
|
||||
reports.append(f"{line}: renamed test case '{name}' to '{new_name}'")
|
||||
return match["head"] + new_suite + match["mid"] + new_name + match["tail"]
|
||||
|
||||
return PATTERN.sub(rewrite, text), reports
|
||||
|
||||
|
||||
def fix_names(path: Path) -> bool:
|
||||
"""Corrects one file's gtest names, reporting each on stdout."""
|
||||
original = path.read_text(encoding="utf-8")
|
||||
fixed, reports = fix_source(original)
|
||||
for report in reports:
|
||||
print(f"{path}:{report}")
|
||||
if fixed != original:
|
||||
path.write_text(fixed, encoding="utf-8")
|
||||
return not reports
|
||||
|
||||
|
||||
def main() -> int:
|
||||
files = [Path(f) for f in sys.argv[1:]]
|
||||
success = True
|
||||
|
||||
for path in files:
|
||||
success &= fix_names(path)
|
||||
|
||||
return 0 if success else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
259
bin/pre-commit/test_fix_gtest_names.py
Executable file
259
bin/pre-commit/test_fix_gtest_names.py
Executable file
@@ -0,0 +1,259 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Tests for fix_gtest_names.py.
|
||||
|
||||
Run directly (no test framework needed):
|
||||
./bin/pre-commit/test_fix_gtest_names.py
|
||||
or under pytest:
|
||||
pytest bin/pre-commit/test_fix_gtest_names.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
from fix_gtest_names import camel_case, fix_source, snake_case
|
||||
|
||||
|
||||
def dedent(text: str) -> str:
|
||||
"""Removes a fixture's common indentation and its leading newline.
|
||||
|
||||
Lets fixtures be written as indented triple-quoted here-docs while keeping
|
||||
honest 1-based line numbers.
|
||||
"""
|
||||
return textwrap.dedent(text).lstrip("\n")
|
||||
|
||||
|
||||
def fixed(text: str) -> str:
|
||||
return fix_source(dedent(text))[0]
|
||||
|
||||
|
||||
def reports(text: str) -> list[str]:
|
||||
return fix_source(dedent(text))[1]
|
||||
|
||||
|
||||
# --- conversion --------------------------------------------------------------
|
||||
|
||||
|
||||
def test_snake_case_conversion() -> None:
|
||||
assert snake_case("BadInputs") == "bad_inputs"
|
||||
assert snake_case("mulDiv") == "mul_div"
|
||||
assert snake_case("already_snake") == "already_snake"
|
||||
assert snake_case("base64") == "base64"
|
||||
|
||||
|
||||
def test_snake_case_keeps_acronyms_whole() -> None:
|
||||
assert snake_case("SetAndResetAccountTxnID") == "set_and_reset_account_txn_id"
|
||||
assert snake_case("XRPToIOU") == "xrp_to_iou"
|
||||
assert snake_case("STAmountMath") == "st_amount_math"
|
||||
|
||||
|
||||
def test_camel_case_conversion() -> None:
|
||||
assert camel_case("json_value") == "JsonValue"
|
||||
assert camel_case("mulDiv") == "MulDiv"
|
||||
assert camel_case("scope") == "Scope"
|
||||
assert camel_case("base64") == "Base64"
|
||||
|
||||
|
||||
def test_camel_case_leaves_acronyms_alone() -> None:
|
||||
# A snake_case round-trip would give `ShaMapTest` / `ParseStatmRsSkB` here.
|
||||
assert camel_case("SHAMapTest") == "SHAMapTest"
|
||||
assert camel_case("parseStatmRSSkB") == "ParseStatmRSSkB"
|
||||
assert camel_case("XRPAmount") == "XRPAmount"
|
||||
assert camel_case("CSPRNG") == "CSPRNG"
|
||||
|
||||
|
||||
def test_disabled_prefix_preserved() -> None:
|
||||
assert snake_case("DISABLED_FooBar") == "DISABLED_foo_bar"
|
||||
assert snake_case("DISABLED_foo_bar") == "DISABLED_foo_bar"
|
||||
assert snake_case("DISABLED_") == "DISABLED_"
|
||||
assert camel_case("DISABLED_foo_bar") == "DISABLED_FooBar"
|
||||
assert camel_case("DISABLED_") == "DISABLED_"
|
||||
|
||||
|
||||
# --- what counts as a test definition ---------------------------------------
|
||||
|
||||
|
||||
def test_all_macros_recognized() -> None:
|
||||
code = """
|
||||
TEST(Suite, oneName)
|
||||
TEST_F(Fixture, twoName)
|
||||
TEST_P(Fixture, threeName)
|
||||
TYPED_TEST(Fixture, fourName)
|
||||
TYPED_TEST_P(Fixture, fiveName)
|
||||
"""
|
||||
assert fixed(code) == dedent("""
|
||||
TEST(Suite, one_name)
|
||||
TEST_F(Fixture, two_name)
|
||||
TEST_P(Fixture, three_name)
|
||||
TYPED_TEST(Fixture, four_name)
|
||||
TYPED_TEST_P(Fixture, five_name)
|
||||
""")
|
||||
|
||||
|
||||
def test_conforming_definitions_untouched() -> None:
|
||||
code = """
|
||||
TEST(AccountSet, bad_inputs)
|
||||
TEST_F(MutexMakeTest, default_constructor)
|
||||
TEST(SHAMap, DISABLED_slow_path)
|
||||
"""
|
||||
assert reports(code) == []
|
||||
assert fixed(code) == dedent(code)
|
||||
|
||||
|
||||
def test_lookalikes_ignored() -> None:
|
||||
code = """
|
||||
// TEST(Suite, notATest)
|
||||
TEST_EXPECT(someCall())
|
||||
TEST_EXPECTS(amount == value, amount.getText())
|
||||
INSTANTIATE_TEST_SUITE_P(Prefix, Fixture, testValues());
|
||||
auto x = TEST(Suite, notATest);
|
||||
TYPED_TEST_SUITE(Fixture, MyTypes);
|
||||
"""
|
||||
assert reports(code) == []
|
||||
assert fixed(code) == dedent(code)
|
||||
|
||||
|
||||
def test_indented_and_wrapped_definitions() -> None:
|
||||
code = """
|
||||
namespace ripple {
|
||||
TEST(Suite, indentedName)
|
||||
}
|
||||
TEST_F(
|
||||
SomeVeryLongFixtureName,
|
||||
wrappedName)
|
||||
"""
|
||||
assert fixed(code) == dedent("""
|
||||
namespace ripple {
|
||||
TEST(Suite, indented_name)
|
||||
}
|
||||
TEST_F(
|
||||
SomeVeryLongFixtureName,
|
||||
wrapped_name)
|
||||
""")
|
||||
|
||||
|
||||
# --- rewriting --------------------------------------------------------------
|
||||
|
||||
|
||||
def test_only_the_two_names_are_rewritten() -> None:
|
||||
code = """
|
||||
TEST(mulDiv, mulDiv)
|
||||
{
|
||||
auto const mulDiv = 1; // mulDiv stays
|
||||
}
|
||||
"""
|
||||
assert fixed(code) == dedent("""
|
||||
TEST(MulDiv, mul_div)
|
||||
{
|
||||
auto const mulDiv = 1; // mulDiv stays
|
||||
}
|
||||
""")
|
||||
|
||||
|
||||
def test_suite_name_camel_cased() -> None:
|
||||
code = """
|
||||
TEST(json_value, limits)
|
||||
TEST(scope, ScopeExit)
|
||||
"""
|
||||
assert reports(code) == [
|
||||
"1: renamed suite 'json_value' to 'JsonValue'",
|
||||
"2: renamed suite 'scope' to 'Scope'",
|
||||
"2: renamed test case 'ScopeExit' to 'scope_exit'",
|
||||
]
|
||||
assert fixed(code) == dedent("""
|
||||
TEST(JsonValue, limits)
|
||||
TEST(Scope, scope_exit)
|
||||
""")
|
||||
|
||||
|
||||
def test_fixture_reported_but_not_renamed() -> None:
|
||||
# The first argument names a class, so only a human (or clang-tidy) can
|
||||
# rename it; the test-case name is still fixed.
|
||||
code = """
|
||||
TEST_F(my_fixture, someTest)
|
||||
"""
|
||||
assert reports(code) == [
|
||||
"1: fixture 'my_fixture' is not CamelCase: rename the class to "
|
||||
"'MyFixture' by hand",
|
||||
"1: renamed test case 'someTest' to 'some_test'",
|
||||
]
|
||||
assert fixed(code) == dedent("""
|
||||
TEST_F(my_fixture, some_test)
|
||||
""")
|
||||
|
||||
|
||||
def test_reports_carry_line_numbers() -> None:
|
||||
code = """
|
||||
#include <foo.h>
|
||||
|
||||
TEST(Suite, firstName)
|
||||
|
||||
TEST(Suite, secondName)
|
||||
"""
|
||||
assert reports(code) == [
|
||||
"3: renamed test case 'firstName' to 'first_name'",
|
||||
"5: renamed test case 'secondName' to 'second_name'",
|
||||
]
|
||||
|
||||
|
||||
# --- collisions -------------------------------------------------------------
|
||||
|
||||
|
||||
def test_collision_reported_and_not_applied() -> None:
|
||||
# Both would define `Suite_mul_div_Test`.
|
||||
code = """
|
||||
TEST(Suite, mulDiv)
|
||||
TEST(Suite, mul_div)
|
||||
"""
|
||||
assert reports(code) == [
|
||||
"1: cannot rename 'Suite, mulDiv' to 'Suite, mul_div': another test "
|
||||
"case already generates that name"
|
||||
]
|
||||
assert fixed(code) == dedent(code)
|
||||
|
||||
|
||||
def test_collision_between_converging_suites() -> None:
|
||||
# Both suites camel-case to `SuiteA`, so both would define
|
||||
# `SuiteA_one_test_Test`.
|
||||
code = """
|
||||
TEST(SuiteA, oneTest)
|
||||
TEST(Suite_a, one_test)
|
||||
"""
|
||||
assert [r.split(":")[1].strip() for r in reports(code)] == [
|
||||
"cannot rename 'SuiteA, oneTest' to 'SuiteA, one_test'",
|
||||
"cannot rename 'Suite_a, one_test' to 'SuiteA, one_test'",
|
||||
]
|
||||
assert fixed(code) == dedent(code)
|
||||
|
||||
|
||||
def test_same_name_in_different_suites_is_not_a_collision() -> None:
|
||||
code = """
|
||||
TEST(SuiteOne, mulDiv)
|
||||
TEST(SuiteTwo, mulDiv)
|
||||
"""
|
||||
assert fixed(code) == dedent("""
|
||||
TEST(SuiteOne, mul_div)
|
||||
TEST(SuiteTwo, mul_div)
|
||||
""")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
tests = sorted(
|
||||
(name, fn)
|
||||
for name, fn in globals().items()
|
||||
if name.startswith("test_") and callable(fn)
|
||||
)
|
||||
failed = 0
|
||||
for name, fn in tests:
|
||||
try:
|
||||
fn()
|
||||
print(f"PASS {name}")
|
||||
except AssertionError as exc:
|
||||
failed += 1
|
||||
print(f"FAIL {name}: {exc!r}")
|
||||
print(f"\n{len(tests) - failed}/{len(tests)} passed")
|
||||
return 1 if failed else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -1,28 +0,0 @@
|
||||
include_guard()
|
||||
|
||||
set(GIT_BUILD_BRANCH "")
|
||||
set(GIT_COMMIT_HASH "")
|
||||
|
||||
find_package(Git)
|
||||
if(NOT Git_FOUND)
|
||||
message(WARNING "Git not found. Git branch and commit hash will be empty.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(GIT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/.git)
|
||||
|
||||
execute_process(
|
||||
COMMAND
|
||||
${GIT_EXECUTABLE} --git-dir=${GIT_DIRECTORY} rev-parse --abbrev-ref HEAD
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
OUTPUT_VARIABLE GIT_BUILD_BRANCH
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} --git-dir=${GIT_DIRECTORY} rev-parse HEAD
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
OUTPUT_VARIABLE GIT_COMMIT_HASH
|
||||
)
|
||||
|
||||
message(STATUS "Git branch: ${GIT_BUILD_BRANCH}")
|
||||
message(STATUS "Git commit hash: ${GIT_COMMIT_HASH}")
|
||||
@@ -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
|
||||
|
||||
@@ -81,7 +81,7 @@ include(target_link_modules)
|
||||
add_module(xrpl beast)
|
||||
target_link_libraries(xrpl.libxrpl.beast PUBLIC xrpl.imports.main)
|
||||
|
||||
include(GitInfo)
|
||||
include(XrplVersion)
|
||||
add_module(xrpl git)
|
||||
target_compile_definitions(
|
||||
xrpl.libxrpl.git
|
||||
@@ -111,6 +111,11 @@ target_link_libraries(
|
||||
xrpl.libxrpl.protocol
|
||||
PUBLIC xrpl.libxrpl.crypto xrpl.libxrpl.git xrpl.libxrpl.json
|
||||
)
|
||||
# Only on BuildInfo.cpp, so a new version does not rebuild the whole module.
|
||||
set_source_files_properties(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/libxrpl/protocol/BuildInfo.cpp
|
||||
PROPERTIES COMPILE_DEFINITIONS XRPLD_VERSION="${XRPLD_VERSION}"
|
||||
)
|
||||
|
||||
# Level 05
|
||||
add_module(xrpl protocol_autogen)
|
||||
@@ -287,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
|
||||
@@ -296,7 +309,14 @@ if(xrpld)
|
||||
target_sources(xrpld PRIVATE ${sources})
|
||||
endif()
|
||||
|
||||
target_link_libraries(xrpld Xrpl::boost Xrpl::opts Xrpl::libs xrpl.libxrpl)
|
||||
target_link_libraries(
|
||||
xrpld
|
||||
Xrpl::boost
|
||||
Xrpl::opts
|
||||
Xrpl::libs
|
||||
xrpl.libxrpl
|
||||
rpcspec::rpcspec
|
||||
)
|
||||
exclude_if_included(xrpld)
|
||||
# define a macro for tests that might need to
|
||||
# be excluded or run differently in CI environment
|
||||
@@ -310,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()
|
||||
|
||||
@@ -48,7 +48,7 @@ setup_target_for_coverage_gcovr(
|
||||
"include/xrpl/beast/test"
|
||||
"include/xrpl/beast/unit_test"
|
||||
"${CMAKE_BINARY_DIR}/pb-xrpl.libpb"
|
||||
DEPENDENCIES xrpld xrpl_tests
|
||||
DEPENDENCIES xrpld xrpl_tests xrpld_tests
|
||||
)
|
||||
|
||||
add_code_coverage_to_target(opts INTERFACE)
|
||||
|
||||
@@ -44,12 +44,18 @@ else()
|
||||
set(pkg_type rpm)
|
||||
endif()
|
||||
|
||||
# Unquoted below, so an empty value adds no argument at all.
|
||||
set(pkg_variant_option "")
|
||||
if(assert)
|
||||
set(pkg_variant_option --variant=assert)
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
package
|
||||
COMMAND
|
||||
${CMAKE_SOURCE_DIR}/package/build_pkg.py --package-type=${pkg_type}
|
||||
--build-dir=${CMAKE_BINARY_DIR} --pkg-release=${pkg_release}
|
||||
--channel=UNRELEASED
|
||||
${pkg_variant_option} --channel=UNRELEASED
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
DEPENDS xrpld validator-keys
|
||||
COMMENT "Building Linux ${pkg_type} package"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,15 +1,75 @@
|
||||
#[===================================================================[
|
||||
read version from source
|
||||
#]===================================================================]
|
||||
find_package(Git)
|
||||
|
||||
file(STRINGS src/libxrpl/protocol/BuildInfo.cpp BUILD_INFO)
|
||||
foreach(line_ ${BUILD_INFO})
|
||||
if(line_ MATCHES "versionString[ ]*=[ ]*\"(.+)\"")
|
||||
set(xrpld_version ${CMAKE_MATCH_1})
|
||||
endif()
|
||||
endforeach()
|
||||
if(xrpld_version)
|
||||
message(STATUS "xrpld version: ${xrpld_version}")
|
||||
else()
|
||||
message(FATAL_ERROR "unable to determine xrpld version")
|
||||
set(GIT_BUILD_BRANCH "")
|
||||
set(GIT_COMMIT_HASH "")
|
||||
|
||||
if(DEFINED ENV{GITHUB_BRANCH_NAME})
|
||||
set(GIT_BUILD_BRANCH $ENV{GITHUB_BRANCH_NAME})
|
||||
set(GIT_COMMIT_HASH $ENV{GITHUB_HEAD_SHA})
|
||||
elseif(Git_FOUND AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/../.git")
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/..
|
||||
OUTPUT_VARIABLE GIT_BUILD_BRANCH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/..
|
||||
OUTPUT_VARIABLE GIT_COMMIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
endif()
|
||||
|
||||
message(STATUS "Git branch: ${GIT_BUILD_BRANCH}")
|
||||
message(STATUS "Git commit hash: ${GIT_COMMIT_HASH}")
|
||||
|
||||
if(
|
||||
DEFINED ENV{FORCE_XRPLD_VERSION}
|
||||
AND NOT "$ENV{FORCE_XRPLD_VERSION}" STREQUAL ""
|
||||
)
|
||||
message(
|
||||
STATUS
|
||||
"Using explicitly provided '$ENV{FORCE_XRPLD_VERSION}' as xrpld version"
|
||||
)
|
||||
|
||||
set(XRPLD_VERSION "$ENV{FORCE_XRPLD_VERSION}")
|
||||
|
||||
# The rules beast::SemanticVersion::parse applies, so that an invalid version
|
||||
# fails here rather than when xrpld starts. It reads each number as an int.
|
||||
set(SEMVER_NUMBER "(0|[1-9][0-9]*)")
|
||||
set(SEMVER_PRE_RELEASE "[A-Za-z1-9-][A-Za-z0-9-]*")
|
||||
set(SEMVER_METADATA "[A-Za-z0-9-]+")
|
||||
set(SEMVER_NUMBER_MAX 2147483647)
|
||||
if(
|
||||
NOT XRPLD_VERSION
|
||||
MATCHES
|
||||
"^${SEMVER_NUMBER}\\.${SEMVER_NUMBER}\\.${SEMVER_NUMBER}(-${SEMVER_PRE_RELEASE}(\\.${SEMVER_PRE_RELEASE})*)?(\\+${SEMVER_METADATA}(\\.${SEMVER_METADATA})*)?$"
|
||||
OR CMAKE_MATCH_1 GREATER SEMVER_NUMBER_MAX
|
||||
OR CMAKE_MATCH_2 GREATER SEMVER_NUMBER_MAX
|
||||
OR CMAKE_MATCH_3 GREATER SEMVER_NUMBER_MAX
|
||||
)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"FORCE_XRPLD_VERSION '${XRPLD_VERSION}' is not a semantic version xrpld accepts, see https://semver.org"
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Using '0.0.0-dev+<git short rev>' as xrpld version")
|
||||
|
||||
if(GIT_COMMIT_HASH STREQUAL "")
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"Unable to determine xrpld version without git, set FORCE_XRPLD_VERSION"
|
||||
)
|
||||
endif()
|
||||
|
||||
string(SUBSTRING ${GIT_COMMIT_HASH} 0 7 GIT_COMMIT_HASH_SHORT)
|
||||
|
||||
set(XRPLD_VERSION "0.0.0-dev+${GIT_COMMIT_HASH_SHORT}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Build version: ${XRPLD_VERSION}")
|
||||
|
||||
@@ -59,7 +59,7 @@ def create_transaction_parser():
|
||||
# the members that differ from these.
|
||||
SETTING_DEFAULTS = {
|
||||
"delegable": "Delegation::NotDelegable",
|
||||
"amendment": "uint256{}",
|
||||
"amendment": "UInt256{}",
|
||||
"privileges": "Privilege::NoPriv",
|
||||
}
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ ${field['typeData']['setter_type']} ${field['paramName']}${',' if i < len(requir
|
||||
* @return The constructed ledger entry wrapper.
|
||||
*/
|
||||
${name}
|
||||
build(uint256 const& index)
|
||||
build(UInt256 const& index)
|
||||
{
|
||||
return ${name}{std::make_shared<SLE>(std::move(object_), index)};
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace xrpl::ledger_entries {
|
||||
// builder's STObject and the wrapper's SLE.
|
||||
TEST(${name}Tests, BuilderSettersRoundTrip)
|
||||
{
|
||||
uint256 const index{1u};
|
||||
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)
|
||||
{
|
||||
uint256 const index{2u};
|
||||
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)
|
||||
{
|
||||
uint256 const index{3u};
|
||||
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)
|
||||
{
|
||||
uint256 const index{4u};
|
||||
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)
|
||||
{
|
||||
uint256 const index{3u};
|
||||
UInt256 const index{3u};
|
||||
|
||||
% for field in required_fields:
|
||||
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
|
||||
|
||||
13
conan.lock
13
conan.lock
@@ -3,6 +3,7 @@
|
||||
"requires": [
|
||||
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
|
||||
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1782392402.420688",
|
||||
"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",
|
||||
@@ -19,8 +20,8 @@
|
||||
"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#f729f6d75992d20f9c72828e9142d62f%1783945160.094135",
|
||||
"fast_float/8.2.10#f6f28d6bb22112078e7dbda611caf681%1782494504.298",
|
||||
"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",
|
||||
"corrosion/0.6.1#bfa292df0a957bc70a450ff316cd9435%1786119416.131296",
|
||||
@@ -33,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",
|
||||
@@ -45,6 +50,10 @@
|
||||
],
|
||||
"python_requires": [],
|
||||
"overrides": {
|
||||
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c": [
|
||||
null,
|
||||
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c"
|
||||
],
|
||||
"protobuf/[>=5.27.0 <7]": [
|
||||
"protobuf/6.33.5"
|
||||
],
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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" %}
|
||||
|
||||
33
conanfile.py
33
conanfile.py
@@ -1,9 +1,13 @@
|
||||
import os
|
||||
import re
|
||||
|
||||
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
|
||||
from conan.tools.env import Environment
|
||||
|
||||
from conan import ConanFile
|
||||
|
||||
DEV_VERSION = "0.0.0-dev"
|
||||
|
||||
|
||||
class Xrpl(ConanFile):
|
||||
name = "xrpl"
|
||||
@@ -36,6 +40,7 @@ class Xrpl(ConanFile):
|
||||
"nudb/2.0.9",
|
||||
"openssl/3.6.3",
|
||||
"soci/4.0.3",
|
||||
"xrpl-rpc-spec/0.1.21",
|
||||
"zlib/1.3.2",
|
||||
]
|
||||
|
||||
@@ -44,7 +49,8 @@ class Xrpl(ConanFile):
|
||||
]
|
||||
|
||||
tool_requires = [
|
||||
"protobuf/6.33.5",
|
||||
"grpc/<host_version>",
|
||||
"protobuf/<host_version>",
|
||||
]
|
||||
|
||||
default_options = {
|
||||
@@ -114,17 +120,16 @@ class Xrpl(ConanFile):
|
||||
"soci/*:shared": False,
|
||||
"soci/*:with_sqlite3": True,
|
||||
"soci/*:with_boost": True,
|
||||
"xrpl-rpc-spec/*:server": "xrpld",
|
||||
"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):
|
||||
if self.version is None:
|
||||
path = f"{self.recipe_folder}/src/libxrpl/protocol/BuildInfo.cpp"
|
||||
regex = r"versionString\s?=\s?\"(.*)\""
|
||||
with open(path, encoding="utf-8") as file:
|
||||
matches = (re.search(regex, line) for line in file)
|
||||
match = next(m for m in matches if m)
|
||||
self.version = match.group(1)
|
||||
self.version = self.version or DEV_VERSION
|
||||
|
||||
def configure(self):
|
||||
if self.settings.compiler == "apple-clang":
|
||||
@@ -149,6 +154,7 @@ class Xrpl(ConanFile):
|
||||
self.requires("xxhash/0.8.3", transitive_headers=True)
|
||||
|
||||
exports_sources = (
|
||||
"bin/nix/default-loader-path.sh",
|
||||
"CMakeLists.txt",
|
||||
"cfg/*",
|
||||
"cmake/*",
|
||||
@@ -166,6 +172,17 @@ class Xrpl(ConanFile):
|
||||
generators = "CMakeDeps"
|
||||
|
||||
def generate(self):
|
||||
# The sources in the Conan cache have no git history, so the version
|
||||
# comes from the reference, unless it is not one, like 'develop'.
|
||||
if not os.path.exists(os.path.join(self.source_folder, ".git")):
|
||||
version = str(self.version)
|
||||
env = Environment()
|
||||
env.define(
|
||||
"FORCE_XRPLD_VERSION",
|
||||
version if re.match(r"\d+\.\d+\.\d+", version) else DEV_VERSION,
|
||||
)
|
||||
env.vars(self).save_script("xrpld_version")
|
||||
|
||||
tc = CMakeToolchain(self)
|
||||
tc.variables["tests"] = self.options.tests
|
||||
tc.variables["benchmark"] = self.options.benchmark
|
||||
|
||||
@@ -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
|
||||
|
||||
20
crates/Cargo.lock
generated
20
crates/Cargo.lock
generated
@@ -57,9 +57,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.199"
|
||||
version = "1.0.202"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "824894a4a85dca76d4c95c2b9098c036f5a29f627b30c12780774f6654e60974"
|
||||
checksum = "13f6de320895f42e6e081abb5c7983bedcf0b6d0ff9323de0d33f620c8ac1199"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxx-build",
|
||||
@@ -72,9 +72,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.199"
|
||||
version = "1.0.202"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1ae0b651ea5b0000b19513aef5a03f194d7e3486f2d9258b658da8677fe9036"
|
||||
checksum = "4fde53ca86b9704a943fef0f1e1d836239a6aedca5de3c65fa9f97ec0bd46d39"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@@ -87,9 +87,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-cmd"
|
||||
version = "1.0.199"
|
||||
version = "1.0.202"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb05f91d3fb8435d9bab6ac5ce6ac1868be774325fb7fb2a91be39393b21388e"
|
||||
checksum = "07bae89236c811fd4d08ed3441759ac4ac98d752cbfd3de341315ba16ad20ec3"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"codespan-reporting",
|
||||
@@ -101,15 +101,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.199"
|
||||
version = "1.0.202"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf293202e0e3e98495785745389e8d0755b217e66f19194a5c695c25e03282ef"
|
||||
checksum = "49045042e5fced01b80742aba5508de82aa4f13677ed3fa2b4cda709c40c5918"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.199"
|
||||
version = "1.0.202"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca001d746947c7249ed9d332a10f7a59daedbafeb0ec68c5c18a7db7a93f6ccc"
|
||||
checksum = "b181252e2e3d3b5d183afbdc033a3958b445eb3e1a0ae65fc3d4f5259f5da6fd"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"proc-macro2",
|
||||
|
||||
@@ -8,6 +8,9 @@ cxx = { version = "1.0.198", features = ["c++20"] }
|
||||
[workspace.package]
|
||||
edition = "2024"
|
||||
|
||||
[workspace.lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = [ 'cfg(coverage)', 'cfg(coverage_nightly)' ] }
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
overflow-checks = true
|
||||
|
||||
@@ -8,3 +8,6 @@ crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
cxx.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
|
||||
|
||||
#[cxx::bridge(namespace = "rs::hello_world")]
|
||||
mod ffi {
|
||||
extern "Rust" {
|
||||
@@ -8,3 +10,14 @@ mod ffi {
|
||||
pub fn hello_world() -> String {
|
||||
"hello_world".to_string()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg_attr(coverage_nightly, coverage(off))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn hello_world_returns_hello_world() {
|
||||
assert_eq!(hello_world(), "hello_world")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# `xrpld` Docker Image
|
||||
|
||||
- Some info relating to Docker containers can be found here: [../Builds/containers](../Builds/containers)
|
||||
- Images for building and testing xrpld can be found here: [thejohnfreeman/rippled-docker](https://github.com/thejohnfreeman/rippled-docker/)
|
||||
- These images do not have xrpld. They have all the tools necessary to build xrpld.
|
||||
`xrpld` is published to Docker Hub as [`xrplf/xrpld`](https://hub.docker.com/r/xrplf/xrpld):
|
||||
the `xrpld` DEB package installed on Ubuntu 26.04, running as the `xrpld` user.
|
||||
Each release is tagged with its version, `xrplf/xrpld:<version>`, and
|
||||
`xrplf/xrpld:develop` follows the `develop` branch.
|
||||
See [`package/README.md`](../package/README.md#docker-images) for how it is built
|
||||
and tagged.
|
||||
|
||||
```bash
|
||||
docker run --detach --name xrpld \
|
||||
--volume xrpld-db:/var/lib/xrpld \
|
||||
--publish 2459:2459 \
|
||||
xrplf/xrpld:develop
|
||||
```
|
||||
|
||||
The admin ports (5005, 6006 and 50051) listen on `127.0.0.1` in the shipped
|
||||
configuration; to reach them from outside the container, mount your own over
|
||||
`/etc/xrpld/xrpld.cfg`.
|
||||
|
||||
16
docs/build/environment.md
vendored
16
docs/build/environment.md
vendored
@@ -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
|
||||
|
||||
30
docs/build/nix.md
vendored
30
docs/build/nix.md
vendored
@@ -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
|
||||
|
||||
6
docs/build/nix_troubleshooting.md
vendored
6
docs/build/nix_troubleshooting.md
vendored
@@ -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
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
|
||||
`xrpld` is published as DEB and RPM packages for 64-bit x86 Linux.
|
||||
Use APT on Debian-based distributions such as Debian and Ubuntu,
|
||||
and YUM on Red Hat-based distributions such as RHEL, AlmaLinux, and Rocky Linux.
|
||||
and DNF on Red Hat-based distributions such as RHEL, AlmaLinux, and Rocky Linux,
|
||||
where `yum` is a symlink to `dnf`.
|
||||
To build from source instead, see [BUILD.md](../BUILD.md).
|
||||
|
||||
## Release channels
|
||||
@@ -81,7 +82,7 @@ wherever it appears in the repository configuration.
|
||||
sudo apt -y install xrpld
|
||||
```
|
||||
|
||||
### With the YUM package manager
|
||||
### With the DNF package manager
|
||||
|
||||
1. Add the XRPL Foundation package-signing key:
|
||||
|
||||
@@ -109,9 +110,23 @@ wherever it appears in the repository configuration.
|
||||
3. Install the `xrpld` package:
|
||||
|
||||
```bash
|
||||
sudo yum install -y xrpld
|
||||
sudo dnf install -y xrpld
|
||||
```
|
||||
|
||||
### Optional: the assert-enabled build
|
||||
|
||||
Every channel also carries `xrpld-assert` as a DEB, the same build with assertions
|
||||
enabled, for diagnosing a problem on a non-production server.
|
||||
It installs the same files as `xrpld` and replaces it, so install one or the other:
|
||||
|
||||
```bash
|
||||
sudo apt -y install xrpld-assert # APT removes xrpld itself
|
||||
```
|
||||
|
||||
Switching stops the service, since it is a removal and an installation rather than an upgrade,
|
||||
and APT starts it again.
|
||||
Install `xrpld` the same way to switch back.
|
||||
|
||||
## The xrpld service
|
||||
|
||||
Both package managers install a systemd unit and enable it, so `xrpld` starts on boot.
|
||||
@@ -121,7 +136,7 @@ Check whether it is already running:
|
||||
systemctl status xrpld.service
|
||||
```
|
||||
|
||||
The APT packages start it immediately as well; the YUM packages do not, so start it yourself:
|
||||
The DEB packages start it immediately as well; the RPM packages do not, so start it yourself:
|
||||
|
||||
```bash
|
||||
sudo systemctl start xrpld.service
|
||||
|
||||
2
external/antithesis-sdk/CMakeLists.txt
vendored
2
external/antithesis-sdk/CMakeLists.txt
vendored
@@ -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}>
|
||||
)
|
||||
|
||||
12
flake.lock
generated
12
flake.lock
generated
@@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1781173989,
|
||||
"narHash": "sha256-fnzKKPvS+oieI/pTzotA5tkoM47EB1NpaBcgk4R97hE=",
|
||||
"lastModified": 1791130267,
|
||||
"narHash": "sha256-1sjwcQMcgAbmiip/VivBSiK8p2bTkQGOvCuO7vnwfx4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "8c91a71d13451abc40eb9dae8910f972f979852f",
|
||||
"rev": "9013764fcc0ea99fa16cf7aa7decf8a5c3889dd2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -47,11 +47,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1784611586,
|
||||
"narHash": "sha256-OfqgY+0hp/zseZB7uyH0U8kIDPS4scZZCyAurEplvG0=",
|
||||
"lastModified": 1791189933,
|
||||
"narHash": "sha256-Y8eN7ki0Urx4Ojf4w1xSpWg8uk/3lJqK+E0Oth77rH0=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "14f58845249f3552a89b07772626b8d3c632fa86",
|
||||
"rev": "e60029353d0c48d216bc4b065168ccd4079c166f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
11
include/xrpl/basics/.clang-tidy
Normal file
11
include/xrpl/basics/.clang-tidy
Normal file
@@ -0,0 +1,11 @@
|
||||
# basics' container adapters (TaggedCache, partitioned_unordered_map,
|
||||
# base_uint) model std concepts, which mandate
|
||||
# snake_case member type names.
|
||||
#
|
||||
# clang-tidy replaces rather than merges this option, so the list is the
|
||||
# top-level one restated plus the 6 name(s) needed only here:
|
||||
# allocator_type const_pointer const_reverse_iterator hasher key_equal reverse_iterator
|
||||
InheritParentConfig: true
|
||||
CheckOptions:
|
||||
readability-identifier-naming.TypeAliasIgnoredRegexp: "^(allocator_type|const_iterator|const_pointer|const_reference|const_reverse_iterator|difference_type|duration|hasher|iterator|iterator_category|key_equal|key_type|mapped_type|period|pointer|reference|rep|result_type|reverse_iterator|size_type|time_point|value_type)$"
|
||||
readability-identifier-naming.TypedefIgnoredRegexp: "^(allocator_type|const_iterator|const_pointer|const_reference|const_reverse_iterator|difference_type|duration|hasher|iterator|iterator_category|key_equal|key_type|mapped_type|period|pointer|reference|rep|result_type|reverse_iterator|size_type|time_point|value_type)$"
|
||||
@@ -618,13 +618,16 @@ SharedWeakUnion<T>::convertToWeak()
|
||||
unsafeSetRawPtr(nullptr);
|
||||
return true; // Should never happen
|
||||
// LCOV_EXCL_STOP
|
||||
case PartialDestroy:
|
||||
// This is a weird case. We just converted the last strong
|
||||
// pointer to a weak pointer.
|
||||
case PartialDestroy: {
|
||||
// We just converted the last strong pointer to a weak pointer.
|
||||
// The weak ref we now hold keeps the object from being fully
|
||||
// destroyed, so `p` stays valid; only the copy passed to
|
||||
// `partialDestructorFinished` is nulled.
|
||||
p->partialDestructor();
|
||||
partialDestructorFinished(&p);
|
||||
// p is null and may no longer be used
|
||||
auto finished = p;
|
||||
partialDestructorFinished(&finished);
|
||||
break;
|
||||
}
|
||||
}
|
||||
unsafeSetRawPtr(p, RefStrength::Weak);
|
||||
return true;
|
||||
|
||||
@@ -77,21 +77,13 @@ struct IntrusiveRefCounts
|
||||
std::size_t
|
||||
useCount() const noexcept;
|
||||
|
||||
// This function MUST be called after a partial destructor finishes running.
|
||||
// Calling this function may cause other threads to delete the object
|
||||
// pointed to by `o`, so `o` should never be used after calling this
|
||||
// function. The parameter will be set to a `nullptr` after calling this
|
||||
// function to emphasize that it should not be used.
|
||||
// Note: This is intentionally NOT called at the end of `partialDestructor`.
|
||||
// The reason for this is if new classes are written to support this smart
|
||||
// pointer class, they need to write their own `partialDestructor` function
|
||||
// and ensure `partialDestructorFinished` is called at the end. Putting this
|
||||
// call inside the smart pointer class itself is expected to be less error
|
||||
// prone.
|
||||
// Note: The "two-star" programming is intentional. It emphasizes that `o`
|
||||
// may be deleted and the unergonomic API is meant to signal the special
|
||||
// nature of this function call to callers.
|
||||
// Note: This is a template to support incompletely defined classes.
|
||||
// MUST be called after `partialDestructor` returns. Another thread may
|
||||
// then delete the object, so `*o` is nulled and must not be used after
|
||||
// (unless the caller holds its own weak ref, e.g.
|
||||
// SharedWeakUnion::convertToWeak). Called by the smart pointers, not
|
||||
// `partialDestructor`, so custom partial destructors can't forget it.
|
||||
// The two-star API signals that `*o` may be deleted. Templated to
|
||||
// support incomplete types.
|
||||
template <class T>
|
||||
friend void
|
||||
partialDestructorFinished(T** o);
|
||||
@@ -334,15 +326,11 @@ IntrusiveRefCounts::addWeakReleaseStrongRef() const
|
||||
ReleaseStrongRefAction action = NoOp;
|
||||
if (prevVal.strong == 1)
|
||||
{
|
||||
if (prevVal.weak == 0)
|
||||
{
|
||||
action = NoOp;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextIntVal |= kPartialDestroyStartedMask;
|
||||
action = PartialDestroy;
|
||||
}
|
||||
// The weak ref added here keeps the weak count non-zero, so
|
||||
// releasing the last strong ref always starts a partial destroy,
|
||||
// regardless of the previous weak count.
|
||||
nextIntVal |= kPartialDestroyStartedMask;
|
||||
action = PartialDestroy;
|
||||
}
|
||||
if (refCounts_.compare_exchange_weak(prevIntVal, nextIntVal, std::memory_order_acq_rel))
|
||||
{
|
||||
@@ -358,24 +346,26 @@ IntrusiveRefCounts::addWeakReleaseStrongRef() const
|
||||
inline ReleaseWeakRefAction
|
||||
IntrusiveRefCounts::releaseWeakRef() const
|
||||
{
|
||||
auto prevIntVal = refCounts_.fetch_sub(kWeakDelta, std::memory_order_acq_rel);
|
||||
RefCountPair prev = prevIntVal;
|
||||
auto const prevIntVal = refCounts_.fetch_sub(kWeakDelta, std::memory_order_acq_rel);
|
||||
RefCountPair const prev = prevIntVal;
|
||||
if (prev.weak == 1 && prev.strong == 0)
|
||||
{
|
||||
// `wait` blocks while the value equals its argument, so it must be
|
||||
// given the value as it is after the decrement above.
|
||||
auto curIntVal = prevIntVal - kWeakDelta;
|
||||
if (prev.partialDestroyStartedBit == 0u)
|
||||
{
|
||||
// This case should only be hit if the partialDestroyStartedBit is
|
||||
// set non-atomically (and even then very rarely). The code is kept
|
||||
// in case we need to set the flag non-atomically for perf reasons.
|
||||
refCounts_.wait(prevIntVal, std::memory_order_acquire);
|
||||
prevIntVal = refCounts_.load(std::memory_order_acquire);
|
||||
prev = RefCountPair{prevIntVal};
|
||||
refCounts_.wait(curIntVal, std::memory_order_acquire);
|
||||
curIntVal = refCounts_.load(std::memory_order_acquire);
|
||||
}
|
||||
if (prev.partialDestroyFinishedBit == 0u)
|
||||
if (RefCountPair{curIntVal}.partialDestroyFinishedBit == 0u)
|
||||
{
|
||||
// partial destroy MUST finish before running a full destroy (when
|
||||
// using weak pointers)
|
||||
refCounts_.wait(prevIntVal - kWeakDelta, std::memory_order_acquire);
|
||||
refCounts_.wait(curIntVal, std::memory_order_acquire);
|
||||
}
|
||||
return ReleaseWeakRefAction::Destroy;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
using KeyCache = TaggedCache<uint256, int, true>;
|
||||
using KeyCache = TaggedCache<UInt256, int, true>;
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -110,10 +110,10 @@ static_assert(
|
||||
*
|
||||
* However, it does not have sufficient precision to represent the full integer
|
||||
* range of int64_t values (-2^63 to 2^63-1), which are needed for XRP and MPT
|
||||
* values. The implementation of SingleAssetVault, and LendingProtocol need to
|
||||
* represent those integer values accurately and precisely, both for the
|
||||
* STNumber field type, and for internal calculations. That necessitated the
|
||||
* "large" scale.
|
||||
* values. The implementation of SingleAssetVault, LendingProtocol, and
|
||||
* MPTokensV2 need to represent those integer values accurately and precisely,
|
||||
* both for the STNumber field type, and for internal calculations. That
|
||||
* necessitated the "large" scale.
|
||||
*
|
||||
* The "Large" scales are intended to represent all values that can be represented
|
||||
* by an STAmount - IOUs, XRP, and MPTs. It has a min value of 10^18, and a max
|
||||
@@ -134,8 +134,8 @@ struct MantissaRange final
|
||||
// NOLINTBEGIN(readability-enum-initial-value)
|
||||
// The values don't matter, except for Large
|
||||
enum class MantissaScale {
|
||||
// Small can be removed when either featureSingleAssetVault or featureLendingProtocol are
|
||||
// retired
|
||||
// Small can be removed when any of featureSingleAssetVault, featureLendingProtocol, or
|
||||
// featureMPTokensV2 are retired
|
||||
Small,
|
||||
// LargeLegacy can be removed when fixCleanup3_2_0 is retired
|
||||
LargeLegacy,
|
||||
@@ -311,10 +311,10 @@ concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::u
|
||||
*
|
||||
* The mantissa range may be changed at runtime via setMantissaScale(). The
|
||||
* default mantissa range is "large". The range is updated whenever transaction
|
||||
* processing begins, based on whether SingleAssetVault or LendingProtocol are
|
||||
* enabled. If either is enabled, the mantissa range is set to "large". If not,
|
||||
* it is set to "small", preserving backward compatibility and correct
|
||||
* "amendment-gating".
|
||||
* processing begins, based on whether SingleAssetVault, LendingProtocol, or
|
||||
* MPTokensV2 are enabled. If any is enabled, the mantissa range is set to
|
||||
* "large". If not, it is set to "small", preserving backward compatibility and
|
||||
* correct "amendment-gating".
|
||||
*
|
||||
* It is extremely unlikely that any more calls to setMantissaScale() will be
|
||||
* needed outside of unit tests.
|
||||
@@ -344,16 +344,16 @@ concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::u
|
||||
* set/getMantissaScale() functions may be most appropriate. However, if the
|
||||
* test has anything to do with transaction processing, it should enable or
|
||||
* disable the amendments that control the mantissa range choice
|
||||
* (SingleAssetVault and LendingProtocol), and/or check if either of those
|
||||
* amendments are enabled to determine which result to expect.
|
||||
* (SingleAssetVault, LendingProtocol, and MPTokensV2), and/or check if any of
|
||||
* those amendments are enabled to determine which result to expect.
|
||||
*/
|
||||
class Number final
|
||||
{
|
||||
using rep = std::int64_t;
|
||||
using internalrep = MantissaRange::rep;
|
||||
using InternalRep = MantissaRange::rep;
|
||||
|
||||
bool negative_{false};
|
||||
internalrep mantissa_{0};
|
||||
InternalRep mantissa_{0};
|
||||
int exponent_{std::numeric_limits<int>::lowest()};
|
||||
|
||||
public:
|
||||
@@ -361,10 +361,10 @@ public:
|
||||
static constexpr int kMinExponent = -32768;
|
||||
static constexpr int kMaxExponent = 32768;
|
||||
|
||||
static constexpr internalrep kMaxRep = std::numeric_limits<rep>::max();
|
||||
static constexpr InternalRep kMaxRep = std::numeric_limits<rep>::max();
|
||||
static_assert(kMaxRep == 9'223'372'036'854'775'807);
|
||||
static_assert(-kMaxRep == std::numeric_limits<rep>::min() + 1);
|
||||
static constexpr internalrep kMaxRepUp = ((kMaxRep / 10) + 1) * 10;
|
||||
static constexpr InternalRep kMaxRepUp = ((kMaxRep / 10) + 1) * 10;
|
||||
static_assert(kMaxRepUp == 9'223'372'036'854'775'810ULL);
|
||||
|
||||
// May need to make unchecked private
|
||||
@@ -388,15 +388,15 @@ public:
|
||||
explicit Number(rep mantissa, int exponent);
|
||||
explicit constexpr Number(
|
||||
bool negative,
|
||||
internalrep mantissa,
|
||||
InternalRep mantissa,
|
||||
int exponent,
|
||||
Unchecked) noexcept;
|
||||
// Assume unsigned values are... unsigned. i.e. positive
|
||||
explicit constexpr Number(internalrep mantissa, int exponent, Unchecked) noexcept;
|
||||
explicit constexpr Number(InternalRep mantissa, int exponent, Unchecked) noexcept;
|
||||
// Only unit tests are expected to use this ctor
|
||||
explicit Number(bool negative, internalrep mantissa, int exponent, Normalized);
|
||||
explicit Number(bool negative, InternalRep mantissa, int exponent, Normalized);
|
||||
// Assume unsigned values are... unsigned. i.e. positive
|
||||
explicit Number(internalrep mantissa, int exponent, Normalized);
|
||||
explicit Number(InternalRep mantissa, int exponent, Normalized);
|
||||
|
||||
[[nodiscard]] constexpr rep
|
||||
mantissa() const noexcept;
|
||||
@@ -558,13 +558,13 @@ public:
|
||||
static void
|
||||
setMantissaScale(MantissaRange::MantissaScale scale);
|
||||
|
||||
static internalrep
|
||||
static InternalRep
|
||||
minMantissa()
|
||||
{
|
||||
return kRange.get().min;
|
||||
}
|
||||
|
||||
static internalrep
|
||||
static InternalRep
|
||||
maxMantissa()
|
||||
{
|
||||
return kRange.get().max;
|
||||
@@ -591,7 +591,7 @@ public:
|
||||
// is negative, returns the positive value. This takes a little extra work
|
||||
// because converting std::numeric_limits<std::int64_t>::min() flirts with
|
||||
// UB, and can vary across compilers.
|
||||
static internalrep
|
||||
static InternalRep
|
||||
externalToInternal(rep mantissa);
|
||||
|
||||
private:
|
||||
@@ -625,8 +625,8 @@ private:
|
||||
bool& negative,
|
||||
T& mantissa,
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa,
|
||||
InternalRep const& minMantissa,
|
||||
InternalRep const& maxMantissa,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFix);
|
||||
|
||||
template <class T>
|
||||
@@ -650,25 +650,25 @@ private:
|
||||
shiftExponent(int exponentDelta) const;
|
||||
};
|
||||
|
||||
constexpr Number::Number(bool negative, internalrep mantissa, int exponent, Unchecked) noexcept
|
||||
constexpr Number::Number(bool negative, InternalRep mantissa, int exponent, Unchecked) noexcept
|
||||
: negative_(negative), mantissa_{mantissa}, exponent_{exponent}
|
||||
{
|
||||
}
|
||||
|
||||
constexpr Number::Number(internalrep mantissa, int exponent, Unchecked) noexcept
|
||||
constexpr Number::Number(InternalRep mantissa, int exponent, Unchecked) noexcept
|
||||
: Number(false, mantissa, exponent, Unchecked{})
|
||||
{
|
||||
}
|
||||
|
||||
static constexpr Number kNumZero{};
|
||||
|
||||
inline Number::Number(bool negative, internalrep mantissa, int exponent, Normalized)
|
||||
inline Number::Number(bool negative, InternalRep mantissa, int exponent, Normalized)
|
||||
: Number(negative, mantissa, exponent, Unchecked{})
|
||||
{
|
||||
normalize(kRange);
|
||||
}
|
||||
|
||||
inline Number::Number(internalrep mantissa, int exponent, Normalized)
|
||||
inline Number::Number(InternalRep mantissa, int exponent, Normalized)
|
||||
: Number(false, mantissa, exponent, Normalized{})
|
||||
{
|
||||
}
|
||||
@@ -853,7 +853,7 @@ Number::normalizeToRange() const
|
||||
static_assert((kMAX + 1) / 10 == kMIN);
|
||||
|
||||
bool negative = negative_;
|
||||
internalrep mantissa = mantissa_;
|
||||
InternalRep mantissa = mantissa_;
|
||||
int exponent = exponent_;
|
||||
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
|
||||
@@ -21,11 +21,11 @@ The module xrpl/basics should contain no dependencies on other modules.
|
||||
- `std::set`
|
||||
- For sorted containers.
|
||||
|
||||
- `xrpl::hash_set`
|
||||
- `xrpl::HashSet`
|
||||
- Where inserts and contains need to be O(1).
|
||||
- For "small" sets, `std::set` might be faster and smaller.
|
||||
|
||||
- `xrpl::hardened_hash_set`
|
||||
- `xrpl::HardenedHashSet`
|
||||
- For data sets where the key could be manipulated by an attacker
|
||||
in an attempt to mount an algorithmic complexity attack: see
|
||||
http://en.wikipedia.org/wiki/Algorithmic_complexity_attack
|
||||
@@ -33,5 +33,5 @@ The module xrpl/basics should contain no dependencies on other modules.
|
||||
The following container is deprecated
|
||||
|
||||
- `std::unordered_set`
|
||||
- Use `xrpl::hash_set` instead, which uses a better hashing algorithm.
|
||||
- Or use `xrpl::hardened_hash_set` to prevent algorithmic complexity attacks.
|
||||
- Use `xrpl::HashSet` instead, which uses a better hashing algorithm.
|
||||
- Or use `xrpl::HardenedHashSet` to prevent algorithmic complexity attacks.
|
||||
|
||||
@@ -14,20 +14,20 @@ namespace xrpl {
|
||||
|
||||
class SHAMapHash
|
||||
{
|
||||
uint256 hash_;
|
||||
UInt256 hash_;
|
||||
|
||||
public:
|
||||
SHAMapHash() = default;
|
||||
explicit SHAMapHash(uint256 const& hash) : hash_(hash)
|
||||
explicit SHAMapHash(UInt256 const& hash) : hash_(hash)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] uint256 const&
|
||||
[[nodiscard]] UInt256 const&
|
||||
asUInt256() const
|
||||
{
|
||||
return hash_;
|
||||
}
|
||||
uint256&
|
||||
UInt256&
|
||||
asUInt256()
|
||||
{
|
||||
return hash_;
|
||||
|
||||
@@ -36,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)
|
||||
@@ -162,4 +162,89 @@ toUInt64(std::string const& s);
|
||||
bool
|
||||
isProperlyFormedTomlDomain(std::string_view domain);
|
||||
|
||||
/**
|
||||
* Whether a view can be passed on as a C string.
|
||||
*
|
||||
* A reader given only data() stops at the first null, so the view must reach the
|
||||
* terminating null. The test rebuilds the view from data() and compares: a view
|
||||
* that stops earlier rebuilds longer, and so compares unequal.
|
||||
*
|
||||
* consteval because reading the byte after the view is only defined when @p str
|
||||
* points into storage holding a null at or after its end, such as a string
|
||||
* literal. An unterminated view is then a compile error, not an out-of-bounds
|
||||
* read.
|
||||
*
|
||||
* @param str The view to test.
|
||||
* @return Whether @p str is null-terminated. A view with no data is not.
|
||||
*/
|
||||
consteval bool
|
||||
isNullTerminated(std::string_view str)
|
||||
{
|
||||
if (str.data() == nullptr)
|
||||
return false;
|
||||
|
||||
// Reading past the view is the point, so the usual data() warning does not
|
||||
// apply.
|
||||
// NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
|
||||
return std::string_view{str.data()} == str;
|
||||
}
|
||||
|
||||
/**
|
||||
* A string that is known to reach its terminating null.
|
||||
*
|
||||
* Converts to std::string_view, so it compares and hashes as one. Unlike a
|
||||
* view, asCString() may be handed to a reader that expects a C string, such
|
||||
* as json::StaticString.
|
||||
*
|
||||
* The only constructor is consteval and rejects a view that stops before the
|
||||
* null, so the property holds by construction and no caller asserts it.
|
||||
*/
|
||||
class NullTerminatedView
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Build a view from one that reaches its terminating null.
|
||||
*
|
||||
* Explicit, so that a plain view cannot become a proof of termination by
|
||||
* accident. The conversion the other way stays implicit.
|
||||
*
|
||||
* @param view The string to hold. Rejected at compile time if it stops
|
||||
* before its terminating null, or has no data.
|
||||
*/
|
||||
explicit consteval NullTerminatedView(std::string_view view)
|
||||
: data_(view.data()), size_(view.size())
|
||||
{
|
||||
if (!isNullTerminated(view))
|
||||
throw "xrpl::NullTerminatedView : view does not reach a null";
|
||||
}
|
||||
|
||||
constexpr
|
||||
operator std::string_view() const noexcept
|
||||
{
|
||||
return view();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The string as a view.
|
||||
*/
|
||||
[[nodiscard]] constexpr std::string_view
|
||||
view() const noexcept
|
||||
{
|
||||
return {data_, size_};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The string as a C string. Never null.
|
||||
*/
|
||||
[[nodiscard]] constexpr char const*
|
||||
asCString() const noexcept
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
private:
|
||||
char const* data_;
|
||||
std::size_t size_;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -59,34 +59,34 @@ template <
|
||||
class T,
|
||||
bool IsKeyCache = false,
|
||||
class SharedWeakUnionPointerType = SharedWeakCachePointer<T>,
|
||||
class SharedPointerType = std::shared_ptr<T>,
|
||||
class SharedPointer = std::shared_ptr<T>,
|
||||
class Hash = HardenedHash<>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Mutex = std::recursive_mutex>
|
||||
class TaggedCache
|
||||
{
|
||||
public:
|
||||
using mutex_type = Mutex;
|
||||
using MutexType = Mutex;
|
||||
using key_type = Key;
|
||||
using mapped_type = T;
|
||||
using clock_type = beast::AbstractClock<std::chrono::steady_clock>;
|
||||
using shared_weak_combo_pointer_type = SharedWeakUnionPointerType;
|
||||
using shared_pointer_type = SharedPointerType;
|
||||
using ClockType = beast::AbstractClock<std::chrono::steady_clock>;
|
||||
using SharedWeakComboPointerType = SharedWeakUnionPointerType;
|
||||
using SharedPointerType = SharedPointer;
|
||||
|
||||
public:
|
||||
TaggedCache(
|
||||
std::string const& name,
|
||||
int size,
|
||||
clock_type::duration expiration,
|
||||
clock_type& clock,
|
||||
ClockType::duration expiration,
|
||||
ClockType& clock,
|
||||
beast::Journal journal,
|
||||
beast::insight::Collector::ptr const& collector = beast::insight::NullCollector::make());
|
||||
beast::insight::Collector::Ptr const& collector = beast::insight::NullCollector::make());
|
||||
|
||||
public:
|
||||
/**
|
||||
* Return the clock associated with the cache.
|
||||
*/
|
||||
clock_type&
|
||||
ClockType&
|
||||
clock();
|
||||
|
||||
/**
|
||||
@@ -239,7 +239,7 @@ public:
|
||||
bool
|
||||
retrieve(key_type const& key, T& data);
|
||||
|
||||
mutex_type&
|
||||
MutexType&
|
||||
peekMutex();
|
||||
|
||||
std::vector<key_type>
|
||||
@@ -265,7 +265,7 @@ public:
|
||||
|
||||
private:
|
||||
SharedPointerType
|
||||
initialFetch(key_type const& key, std::scoped_lock<mutex_type> const& l);
|
||||
initialFetch(key_type const& key, std::scoped_lock<MutexType> const& l);
|
||||
|
||||
void
|
||||
collectMetrics();
|
||||
@@ -277,7 +277,7 @@ private:
|
||||
Stats(
|
||||
std::string const& prefix,
|
||||
Handler const& handler,
|
||||
beast::insight::Collector::ptr const& collector)
|
||||
beast::insight::Collector::Ptr const& collector)
|
||||
: hook(collector->makeHook(handler))
|
||||
, size(collector->makeGauge(prefix, "size"))
|
||||
, hitRate(collector->makeGauge(prefix, "hit_rate"))
|
||||
@@ -296,14 +296,14 @@ private:
|
||||
class KeyOnlyEntry
|
||||
{
|
||||
public:
|
||||
clock_type::time_point lastAccess;
|
||||
ClockType::time_point lastAccess;
|
||||
|
||||
explicit KeyOnlyEntry(clock_type::time_point const& lastAccess) : lastAccess(lastAccess)
|
||||
explicit KeyOnlyEntry(ClockType::time_point const& lastAccess) : lastAccess(lastAccess)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
touch(clock_type::time_point const& now)
|
||||
touch(ClockType::time_point const& now)
|
||||
{
|
||||
lastAccess = now;
|
||||
}
|
||||
@@ -312,10 +312,10 @@ private:
|
||||
class ValueEntry
|
||||
{
|
||||
public:
|
||||
shared_weak_combo_pointer_type ptr;
|
||||
clock_type::time_point lastAccess;
|
||||
SharedWeakComboPointerType ptr;
|
||||
ClockType::time_point lastAccess;
|
||||
|
||||
ValueEntry(clock_type::time_point const& lastAccess, shared_pointer_type const& ptr)
|
||||
ValueEntry(ClockType::time_point const& lastAccess, SharedPointerType const& ptr)
|
||||
: ptr(ptr), lastAccess(lastAccess)
|
||||
{
|
||||
}
|
||||
@@ -343,7 +343,7 @@ private:
|
||||
return ptr.lock();
|
||||
}
|
||||
void
|
||||
touch(clock_type::time_point const& now)
|
||||
touch(ClockType::time_point const& now)
|
||||
{
|
||||
lastAccess = now;
|
||||
}
|
||||
@@ -351,35 +351,35 @@ private:
|
||||
|
||||
using Entry = std::conditional_t<IsKeyCache, KeyOnlyEntry, ValueEntry>;
|
||||
|
||||
using KeyOnlyCacheType = hardened_partitioned_hash_map<key_type, KeyOnlyEntry, Hash, KeyEqual>;
|
||||
using KeyOnlyCacheType = HardenedPartitionedHashMap<key_type, KeyOnlyEntry, Hash, KeyEqual>;
|
||||
|
||||
using KeyValueCacheType = hardened_partitioned_hash_map<key_type, ValueEntry, Hash, KeyEqual>;
|
||||
using KeyValueCacheType = HardenedPartitionedHashMap<key_type, ValueEntry, Hash, KeyEqual>;
|
||||
|
||||
using cache_type = hardened_partitioned_hash_map<key_type, Entry, Hash, KeyEqual>;
|
||||
using CacheType = HardenedPartitionedHashMap<key_type, Entry, Hash, KeyEqual>;
|
||||
|
||||
[[nodiscard]] std::thread
|
||||
sweepHelper(
|
||||
clock_type::time_point const& whenExpire,
|
||||
[[maybe_unused]] clock_type::time_point const& now,
|
||||
KeyValueCacheType::map_type& partition,
|
||||
ClockType::time_point const& whenExpire,
|
||||
[[maybe_unused]] ClockType::time_point const& now,
|
||||
KeyValueCacheType::MapType& partition,
|
||||
SweptPointersVector& stuffToSweep,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::scoped_lock<std::recursive_mutex> const&);
|
||||
|
||||
[[nodiscard]] std::thread
|
||||
sweepHelper(
|
||||
clock_type::time_point const& whenExpire,
|
||||
clock_type::time_point const& now,
|
||||
KeyOnlyCacheType::map_type& partition,
|
||||
ClockType::time_point const& whenExpire,
|
||||
ClockType::time_point const& now,
|
||||
KeyOnlyCacheType::MapType& partition,
|
||||
SweptPointersVector&,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::scoped_lock<std::recursive_mutex> const&);
|
||||
|
||||
beast::Journal journal_;
|
||||
clock_type& clock_;
|
||||
ClockType& clock_;
|
||||
Stats stats_;
|
||||
|
||||
mutex_type mutable mutex_;
|
||||
MutexType mutable mutex_;
|
||||
|
||||
// Used for logging
|
||||
std::string name_;
|
||||
@@ -388,11 +388,11 @@ private:
|
||||
int const targetSize_;
|
||||
|
||||
// Desired maximum cache age
|
||||
clock_type::duration const targetAge_;
|
||||
ClockType::duration const targetAge_;
|
||||
|
||||
// Number of items cached
|
||||
int cacheCount_{0};
|
||||
cache_type cache_; // Hold strong reference to recent objects
|
||||
CacheType cache_; // Hold strong reference to recent objects
|
||||
std::uint64_t hits_{0};
|
||||
std::uint64_t misses_{0};
|
||||
};
|
||||
|
||||
@@ -54,10 +54,10 @@ inline TaggedCache<
|
||||
TaggedCache(
|
||||
std::string const& name,
|
||||
int size,
|
||||
clock_type::duration expiration,
|
||||
clock_type& clock,
|
||||
ClockType::duration expiration,
|
||||
ClockType& clock,
|
||||
beast::Journal journal,
|
||||
beast::insight::Collector::ptr const& collector)
|
||||
beast::insight::Collector::Ptr const& collector)
|
||||
: journal_(journal)
|
||||
, clock_(clock)
|
||||
, stats_(
|
||||
@@ -81,7 +81,7 @@ template <
|
||||
class Mutex>
|
||||
inline auto
|
||||
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
|
||||
clock() -> clock_type&
|
||||
clock() -> ClockType&
|
||||
{
|
||||
return clock_;
|
||||
}
|
||||
@@ -237,8 +237,8 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
|
||||
// is destroyed but still within the main cache lock.
|
||||
std::vector<SweptPointersVector> allStuffToSweep(cache_.partitions());
|
||||
|
||||
clock_type::time_point const now(clock_.now());
|
||||
clock_type::time_point whenExpire;
|
||||
ClockType::time_point const now(clock_.now());
|
||||
ClockType::time_point whenExpire;
|
||||
|
||||
auto const start = std::chrono::steady_clock::now();
|
||||
{
|
||||
@@ -252,7 +252,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
|
||||
{
|
||||
whenExpire = now - (targetAge_ * targetSize_ / cache_.size());
|
||||
|
||||
clock_type::duration const minimumAge(std::chrono::seconds(1));
|
||||
ClockType::duration const minimumAge(std::chrono::seconds(1));
|
||||
if (whenExpire > (now - minimumAge))
|
||||
whenExpire = now - minimumAge;
|
||||
|
||||
@@ -487,7 +487,7 @@ inline SharedPointerType
|
||||
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
|
||||
fetch(key_type const& key)
|
||||
{
|
||||
std::scoped_lock<mutex_type> const l(mutex_);
|
||||
std::scoped_lock<MutexType> const l(mutex_);
|
||||
auto ret = initialFetch(key, l);
|
||||
if (!ret)
|
||||
++misses_;
|
||||
@@ -541,7 +541,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
|
||||
requires IsKeyCache
|
||||
{
|
||||
std::scoped_lock const lock(mutex_);
|
||||
clock_type::time_point const now(clock_.now());
|
||||
ClockType::time_point const now(clock_.now());
|
||||
auto [it, inserted] = cache_.emplace(
|
||||
std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(now));
|
||||
if (!inserted)
|
||||
@@ -583,7 +583,7 @@ template <
|
||||
class Mutex>
|
||||
inline auto
|
||||
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
|
||||
peekMutex() -> mutex_type&
|
||||
peekMutex() -> MutexType&
|
||||
{
|
||||
return mutex_;
|
||||
}
|
||||
@@ -711,7 +711,7 @@ template <
|
||||
class Mutex>
|
||||
inline SharedPointerType
|
||||
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
|
||||
initialFetch(key_type const& key, std::scoped_lock<mutex_type> const& l)
|
||||
initialFetch(key_type const& key, std::scoped_lock<MutexType> const& l)
|
||||
{
|
||||
auto cit = cache_.find(key);
|
||||
if (cit == cache_.end())
|
||||
@@ -776,14 +776,14 @@ template <
|
||||
inline std::thread
|
||||
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
|
||||
sweepHelper(
|
||||
clock_type::time_point const& whenExpire,
|
||||
[[maybe_unused]] clock_type::time_point const& now,
|
||||
KeyValueCacheType::map_type& partition,
|
||||
ClockType::time_point const& whenExpire,
|
||||
[[maybe_unused]] ClockType::time_point const& now,
|
||||
KeyValueCacheType::MapType& partition,
|
||||
SweptPointersVector& stuffToSweep,
|
||||
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;
|
||||
|
||||
@@ -856,14 +856,14 @@ template <
|
||||
inline std::thread
|
||||
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
|
||||
sweepHelper(
|
||||
clock_type::time_point const& whenExpire,
|
||||
clock_type::time_point const& now,
|
||||
KeyOnlyCacheType::map_type& partition,
|
||||
ClockType::time_point const& whenExpire,
|
||||
ClockType::time_point const& now,
|
||||
KeyOnlyCacheType::MapType& partition,
|
||||
SweptPointersVector&,
|
||||
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;
|
||||
|
||||
@@ -33,7 +33,7 @@ template <
|
||||
class Hash = beast::Uhash<>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, Value>>>
|
||||
using hash_map = std::unordered_map<Key, Value, Hash, Pred, Allocator>;
|
||||
using HashMap = std::unordered_map<Key, Value, Hash, Pred, Allocator>;
|
||||
|
||||
template <
|
||||
class Key,
|
||||
@@ -41,62 +41,62 @@ template <
|
||||
class Hash = beast::Uhash<>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, Value>>>
|
||||
using hash_multimap = std::unordered_multimap<Key, Value, Hash, Pred, Allocator>;
|
||||
using HashMultimap = std::unordered_multimap<Key, Value, Hash, Pred, Allocator>;
|
||||
|
||||
template <
|
||||
class Value,
|
||||
class Hash = beast::Uhash<>,
|
||||
class Pred = std::equal_to<Value>,
|
||||
class Allocator = std::allocator<Value>>
|
||||
using hash_set = std::unordered_set<Value, Hash, Pred, Allocator>;
|
||||
using HashSet = std::unordered_set<Value, Hash, Pred, Allocator>;
|
||||
|
||||
template <
|
||||
class Value,
|
||||
class Hash = beast::Uhash<>,
|
||||
class Pred = std::equal_to<Value>,
|
||||
class Allocator = std::allocator<Value>>
|
||||
using hash_multiset = std::unordered_multiset<Value, Hash, Pred, Allocator>;
|
||||
using HashMultiset = std::unordered_multiset<Value, Hash, Pred, Allocator>;
|
||||
|
||||
// hardened_hash containers
|
||||
|
||||
using strong_hash = beast::Xxhasher;
|
||||
using StrongHash = beast::Xxhasher;
|
||||
|
||||
template <
|
||||
class Key,
|
||||
class Value,
|
||||
class Hash = HardenedHash<strong_hash>,
|
||||
class Hash = HardenedHash<StrongHash>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, Value>>>
|
||||
using hardened_hash_map = std::unordered_map<Key, Value, Hash, Pred, Allocator>;
|
||||
using HardenedHashMap = std::unordered_map<Key, Value, Hash, Pred, Allocator>;
|
||||
|
||||
template <
|
||||
class Key,
|
||||
class Value,
|
||||
class Hash = HardenedHash<strong_hash>,
|
||||
class Hash = HardenedHash<StrongHash>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, Value>>>
|
||||
using hardened_partitioned_hash_map = PartitionedUnorderedMap<Key, Value, Hash, Pred, Allocator>;
|
||||
using HardenedPartitionedHashMap = PartitionedUnorderedMap<Key, Value, Hash, Pred, Allocator>;
|
||||
|
||||
template <
|
||||
class Key,
|
||||
class Value,
|
||||
class Hash = HardenedHash<strong_hash>,
|
||||
class Hash = HardenedHash<StrongHash>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, Value>>>
|
||||
using hardened_hash_multimap = std::unordered_multimap<Key, Value, Hash, Pred, Allocator>;
|
||||
using HardenedHashMultimap = std::unordered_multimap<Key, Value, Hash, Pred, Allocator>;
|
||||
|
||||
template <
|
||||
class Value,
|
||||
class Hash = HardenedHash<strong_hash>,
|
||||
class Hash = HardenedHash<StrongHash>,
|
||||
class Pred = std::equal_to<Value>,
|
||||
class Allocator = std::allocator<Value>>
|
||||
using hardened_hash_set = std::unordered_set<Value, Hash, Pred, Allocator>;
|
||||
using HardenedHashSet = std::unordered_set<Value, Hash, Pred, Allocator>;
|
||||
|
||||
template <
|
||||
class Value,
|
||||
class Hash = HardenedHash<strong_hash>,
|
||||
class Hash = HardenedHash<StrongHash>,
|
||||
class Pred = std::equal_to<Value>,
|
||||
class Allocator = std::allocator<Value>>
|
||||
using hardened_hash_multiset = std::unordered_multiset<Value, Hash, Pred, Allocator>;
|
||||
using HardenedHashMultiset = std::unordered_multiset<Value, Hash, Pred, Allocator>;
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
using const_iterator = const_pointer;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using tag_type = Tag;
|
||||
using TagType = Tag;
|
||||
|
||||
pointer
|
||||
data()
|
||||
@@ -518,7 +518,7 @@ public:
|
||||
* The input must be precisely `2 * bytes` hexadecimal characters
|
||||
* long, with one exception: the value '0'.
|
||||
*
|
||||
* @param sv A null-terminated string of hexadecimal characters
|
||||
* @param sv A string of hexadecimal characters
|
||||
* @return true if the input was parsed properly; false otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool
|
||||
@@ -575,10 +575,16 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
using uint128 = BaseUInt<128>;
|
||||
using uint160 = BaseUInt<160>;
|
||||
using uint256 = BaseUInt<256>;
|
||||
using uint192 = BaseUInt<192>;
|
||||
using UInt128 = BaseUInt<128>;
|
||||
using UInt160 = BaseUInt<160>;
|
||||
using UInt256 = BaseUInt<256>;
|
||||
using UInt192 = BaseUInt<192>;
|
||||
|
||||
// TODO [#8340]: Legacy names still used by external consumers of libxrpl (e.g. rpc-spec).
|
||||
// Do not use in new code; remove once those consumers have migrated.
|
||||
using uint160 = UInt160; // NOLINT(readability-identifier-naming)
|
||||
using uint256 = UInt256; // NOLINT(readability-identifier-naming)
|
||||
using uint192 = UInt192; // NOLINT(readability-identifier-naming)
|
||||
|
||||
template <std::size_t Bits, class Tag>
|
||||
[[nodiscard]] constexpr std::strong_ordering
|
||||
@@ -670,7 +676,7 @@ operator<<(std::ostream& out, BaseUInt<Bits, Tag> const& u)
|
||||
|
||||
template <>
|
||||
inline std::size_t
|
||||
extract(uint256 const& key)
|
||||
extract(UInt256 const& key)
|
||||
{
|
||||
std::size_t result = 0;
|
||||
// Use memcpy to avoid unaligned UB
|
||||
@@ -680,10 +686,10 @@ extract(uint256 const& key)
|
||||
}
|
||||
|
||||
#ifndef __INTELLISENSE__
|
||||
static_assert(sizeof(uint128) == 128 / 8, "There should be no padding bytes");
|
||||
static_assert(sizeof(uint160) == 160 / 8, "There should be no padding bytes");
|
||||
static_assert(sizeof(uint192) == 192 / 8, "There should be no padding bytes");
|
||||
static_assert(sizeof(uint256) == 256 / 8, "There should be no padding bytes");
|
||||
static_assert(sizeof(UInt128) == 128 / 8, "There should be no padding bytes");
|
||||
static_assert(sizeof(UInt160) == 160 / 8, "There should be no padding bytes");
|
||||
static_assert(sizeof(UInt192) == 192 / 8, "There should be no padding bytes");
|
||||
static_assert(sizeof(UInt256) == 256 / 8, "There should be no padding bytes");
|
||||
#endif
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -16,10 +16,10 @@ namespace xrpl {
|
||||
|
||||
// A few handy aliases
|
||||
|
||||
using days =
|
||||
using Days =
|
||||
std::chrono::duration<int, std::ratio_multiply<std::chrono::hours::period, std::ratio<24>>>;
|
||||
|
||||
using weeks = std::chrono::duration<int, std::ratio_multiply<days::period, std::ratio<7>>>;
|
||||
using Weeks = std::chrono::duration<int, std::ratio_multiply<Days::period, std::ratio<7>>>;
|
||||
|
||||
/**
|
||||
* Clock for measuring the network time.
|
||||
|
||||
@@ -11,10 +11,10 @@ namespace xrpl {
|
||||
|
||||
namespace detail {
|
||||
|
||||
using seed_pair = std::pair<std::uint64_t, std::uint64_t>;
|
||||
using SeedPair = std::pair<std::uint64_t, std::uint64_t>;
|
||||
|
||||
template <bool = true>
|
||||
seed_pair
|
||||
SeedPair
|
||||
makeSeedPair() noexcept
|
||||
{
|
||||
struct StateT
|
||||
@@ -71,7 +71,7 @@ template <class HashAlgorithm = beast::Xxhasher>
|
||||
class HardenedHash
|
||||
{
|
||||
private:
|
||||
detail::seed_pair seeds_{detail::makeSeedPair<>()};
|
||||
detail::SeedPair seeds_{detail::makeSeedPair<>()};
|
||||
|
||||
public:
|
||||
using result_type = HashAlgorithm::result_type;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
namespace xrpl {
|
||||
|
||||
template <typename Key>
|
||||
static std::size_t
|
||||
std::size_t
|
||||
extract(Key const& key)
|
||||
{
|
||||
return key;
|
||||
@@ -53,19 +53,19 @@ public:
|
||||
using const_reference = value_type const&;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = value_type const*;
|
||||
using map_type = std::unordered_map<key_type, mapped_type, hasher, key_equal, allocator_type>;
|
||||
using partition_map_type = std::vector<map_type>;
|
||||
using MapType = std::unordered_map<key_type, mapped_type, hasher, key_equal, allocator_type>;
|
||||
using PartitionMapType = std::vector<MapType>;
|
||||
|
||||
struct Iterator
|
||||
{
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
partition_map_type* map{nullptr};
|
||||
partition_map_type::iterator ait{};
|
||||
map_type::iterator mit;
|
||||
PartitionMapType* map{nullptr};
|
||||
PartitionMapType::iterator ait{};
|
||||
MapType::iterator mit;
|
||||
|
||||
Iterator() = default;
|
||||
|
||||
Iterator(partition_map_type* m) : map(m)
|
||||
Iterator(PartitionMapType* m) : map(m)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -122,13 +122,13 @@ public:
|
||||
{
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
partition_map_type* map{nullptr};
|
||||
partition_map_type::iterator ait{};
|
||||
map_type::iterator mit;
|
||||
PartitionMapType* map{nullptr};
|
||||
PartitionMapType::iterator ait{};
|
||||
MapType::iterator mit;
|
||||
|
||||
ConstIterator() = default;
|
||||
|
||||
ConstIterator(partition_map_type* m) : map(m)
|
||||
ConstIterator(PartitionMapType* m) : map(m)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ public:
|
||||
return partitions_;
|
||||
}
|
||||
|
||||
partition_map_type&
|
||||
PartitionMapType&
|
||||
map()
|
||||
{
|
||||
return map_;
|
||||
@@ -377,7 +377,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
mutable partition_map_type map_{};
|
||||
mutable PartitionMapType map_{};
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -14,13 +14,13 @@ namespace xrpl {
|
||||
#ifndef __INTELLISENSE__
|
||||
static_assert(
|
||||
// NOLINTNEXTLINE(misc-redundant-expression)
|
||||
std::is_integral_v<beast::xor_shift_engine::result_type> &&
|
||||
std::is_unsigned_v<beast::xor_shift_engine::result_type>,
|
||||
std::is_integral_v<beast::XorShiftEngine::result_type> &&
|
||||
std::is_unsigned_v<beast::XorShiftEngine::result_type>,
|
||||
"The XRPL default PRNG engine must return an unsigned integral type.");
|
||||
|
||||
static_assert(
|
||||
// NOLINTNEXTLINE(misc-redundant-expression)
|
||||
std::numeric_limits<beast::xor_shift_engine::result_type>::max() >=
|
||||
std::numeric_limits<beast::XorShiftEngine::result_type>::max() >=
|
||||
std::numeric_limits<std::uint64_t>::max(),
|
||||
"The XRPL default PRNG engine return must be at least 64 bits wide.");
|
||||
#endif
|
||||
@@ -30,7 +30,7 @@ namespace detail {
|
||||
// Determines if a type can be called like an Engine
|
||||
// NOLINTNEXTLINE(readability-redundant-typename): typename required by MSVC
|
||||
template <class Engine, class Result = typename Engine::result_type>
|
||||
using is_engine = std::is_invocable_r<Result, Engine>;
|
||||
using IsEngine = std::is_invocable_r<Result, Engine>;
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
@@ -44,28 +44,28 @@ using is_engine = std::is_invocable_r<Result, Engine>;
|
||||
* Each thread gets its own instance of the engine which
|
||||
* will be randomly seeded.
|
||||
*/
|
||||
inline beast::xor_shift_engine&
|
||||
inline beast::XorShiftEngine&
|
||||
defaultPrng()
|
||||
{
|
||||
// This is used to seed the thread-specific PRNGs on demand
|
||||
static beast::xor_shift_engine kSeeder = [] {
|
||||
static beast::XorShiftEngine kSeeder = [] {
|
||||
std::random_device rng;
|
||||
std::uniform_int_distribution<std::uint64_t> distribution{1};
|
||||
return beast::xor_shift_engine(distribution(rng));
|
||||
return beast::XorShiftEngine(distribution(rng));
|
||||
}();
|
||||
|
||||
// This protects the seeder
|
||||
static std::mutex kM;
|
||||
|
||||
// The thread-specific PRNGs:
|
||||
thread_local beast::xor_shift_engine kEngine = [] {
|
||||
thread_local beast::XorShiftEngine kEngine = [] {
|
||||
std::uint64_t seed = 0;
|
||||
{
|
||||
std::scoped_lock const lk(kM);
|
||||
std::uniform_int_distribution<std::uint64_t> distribution{1};
|
||||
seed = distribution(kSeeder);
|
||||
}
|
||||
return beast::xor_shift_engine{seed};
|
||||
return beast::XorShiftEngine{seed};
|
||||
}();
|
||||
|
||||
return kEngine;
|
||||
@@ -95,7 +95,7 @@ defaultPrng()
|
||||
template <class Engine, class Integral>
|
||||
Integral
|
||||
randInt(Engine& engine, Integral min, Integral max)
|
||||
requires(std::is_integral_v<Integral> && detail::is_engine<Engine>::value)
|
||||
requires(std::is_integral_v<Integral> && detail::IsEngine<Engine>::value)
|
||||
{
|
||||
XRPL_ASSERT(max > min, "xrpl::randInt : max over min inputs");
|
||||
|
||||
@@ -116,7 +116,7 @@ randInt(Integral min, Integral max)
|
||||
template <class Engine, class Integral>
|
||||
Integral
|
||||
randInt(Engine& engine, Integral max)
|
||||
requires(std::is_integral_v<Integral> && detail::is_engine<Engine>::value)
|
||||
requires(std::is_integral_v<Integral> && detail::IsEngine<Engine>::value)
|
||||
{
|
||||
return randInt(engine, Integral(0), max);
|
||||
}
|
||||
@@ -132,7 +132,7 @@ randInt(Integral max)
|
||||
template <class Integral, class Engine>
|
||||
Integral
|
||||
randInt(Engine& engine)
|
||||
requires(std::is_integral_v<Integral> && detail::is_engine<Engine>::value)
|
||||
requires(std::is_integral_v<Integral> && detail::IsEngine<Engine>::value)
|
||||
{
|
||||
return randInt(engine, std::numeric_limits<Integral>::max());
|
||||
}
|
||||
@@ -155,7 +155,7 @@ Byte
|
||||
randByte(Engine& engine)
|
||||
requires(
|
||||
(std::is_same_v<Byte, unsigned char> || std::is_same_v<Byte, std::uint8_t>) &&
|
||||
detail::is_engine<Engine>::value)
|
||||
detail::IsEngine<Engine>::value)
|
||||
{
|
||||
return static_cast<Byte>(randInt<Engine, std::uint32_t>(
|
||||
engine, std::numeric_limits<Byte>::min(), std::numeric_limits<Byte>::max()));
|
||||
|
||||
@@ -40,7 +40,7 @@ private:
|
||||
|
||||
public:
|
||||
using value_type = Int;
|
||||
using tag_type = Tag;
|
||||
using TagType = Tag;
|
||||
|
||||
TaggedInteger() = default;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user