Compare commits

..

77 Commits

Author SHA1 Message Date
Mayukha Vadari
ed07e19c3d docs: Fix stale facts in AGENTS.md and ARCHITECTURE.md
Describe --unittest-arg correctly, list both gtest binaries, add the payment/ and proposal/ transactor directories, drop the -Dxrpld requirement for the Rust crates, and say what to do when API-CHANGELOG.md has no Unreleased section.
2026-10-14 17:09:30 -04:00
Mayukha Vadari
2ad810ad0e Merge remote-tracking branch 'upstream/develop' into mvadari/codify-agents-md 2026-10-14 17:08:33 -04:00
Timur Yalymov
7237b3321c fix: Charge transfer fee on third-party vault withdrawals (#8267) 2026-10-14 16:35:42 +00:00
Timur Yalymov
84d980d883 fix: Reject VaultClawback with a nonexistent holder (#8470) 2026-10-14 14:29:05 +00:00
Timur Yalymov
791c96faad fix: Enforce pseudo-account directory contents with an invariant (#8181)
Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com>
2026-10-14 13:51:24 +00:00
Ayaz Salikhov
31473e7e6d build: Unify clang variables (#8581) 2026-10-14 13:51:05 +00:00
Timur Yalymov
aa70b368b5 fix: Enforce asset authorization on LoanBrokerDelete (#8204)
Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com>
2026-10-14 13:51:05 +00:00
Vito Tumas
d3db569b34 feat: Book FixedPrecision LoanSet through AssetsDeployed (#8260)
Co-authored-by: Jingchen <a1q123456@users.noreply.github.com>
2026-10-14 13:20:58 +00:00
Vito Tumas
94d6b7daf8 feat: Round LoanBroker cover on the FixedPrecision grid (#8258)
Co-authored-by: Jingchen <a1q123456@users.noreply.github.com>
2026-10-14 12:59:39 +00:00
Vito Tumas
e089abe982 feat: Use FixedPrecision accounting in Vault transactors (#8460)
Co-authored-by: Jingchen <a1q123456@users.noreply.github.com>
2026-10-14 12:52:25 +00:00
Alex Kremer
33289e882b refactor: Migrate handlers to rpc-spec (C) (#8561) 2026-10-14 10:15:32 +00:00
Gregory Tsipenyuk
1ed0bccb07 fix: Clamp invalid XRP amounts in Quality::ceilOut & getBookPage (#8480)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Ed Hennis <ed@ripple.com>
2026-10-14 07:49:27 +00:00
Gregory Tsipenyuk
1da55e3989 fix: Waive CanTransfer on loan default and broker delete (#8231)
Co-authored-by: Ed Hennis <ed@ripple.com>
2026-10-14 07:49:12 +00:00
Valentin Balaschenko
166e97e12b refactor: Decouple LedgerHistory from TaggedCache internals (#5874)
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
2026-10-13 23:33:32 +00:00
Chenna Keshava B S
33e5a6fa7c feat: Add TransactionProposalCancel & Cosign Ticket reservation (#8538)
Co-authored-by: Shawn Xie <35279399+shawnxie999@users.noreply.github.com>
Co-authored-by: Kassaking7 <96991820+Kassaking7@users.noreply.github.com>
Co-authored-by: Shawn Xie <shawnxie@live.ca>
Co-authored-by: Zhiyuan Wang <1830604455@qq.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-10-13 19:07:45 +00:00
Ayaz Salikhov
b1f9dc6919 ci: Fix rpm installation problems (#8575) 2026-10-13 18:46:41 +00:00
Ayaz Salikhov
38965af055 docs: Clarify freeze on large changes 2 weeks before first RC (#8574) 2026-10-13 18:43:24 +00:00
Gregory Tsipenyuk
b010d4a563 refactor: Retire fixAMMv1_1 amendment (#8226)
Co-authored-by: Ed Hennis <ed@ripple.com>
2026-10-13 18:03:10 +00:00
dependabot[bot]
eedd6604d1 chore: [DEPENDABOT] bump pyparsing from 3.3.2 to 3.3.3 in /cmake/scripts/codegen in the codegen-dependencies group (#8572)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-13 17:48:57 +00:00
Mayukha Vadari
59ff04b648 fix: Allow WASM transactions in simulate only in admin mode (#8568) 2026-10-13 17:39:32 +00:00
Gregory Tsipenyuk
fcb3ee6425 fix: Detect int64 overflow in XRPAmount and MPTAmount (#8453)
Co-authored-by: Ed Hennis <ed@ripple.com>
2026-10-13 17:35:35 +00:00
Gregory Tsipenyuk
77eacbb52c fix: Assorted MPT/DEX offer related fixes (#8211)
Co-authored-by: Ed Hennis <ed@ripple.com>
2026-10-13 17:18:47 +00:00
Vito Tumas
5ce85b2799 feat: Add FixedPrecision Vault grid and balance helpers (#8257)
Co-authored-by: Jingchen <a1q123456@users.noreply.github.com>
2026-10-13 16:19:08 +00:00
Ayaz Salikhov
4538d198e1 build: Update codegen requirements (#8570) 2026-10-13 14:29:59 +00:00
Gregory Tsipenyuk
4c39b05f94 fix: Reject vault and lending amounts out of range for the asset (#8527)
Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com>
Co-authored-by: Ayaz Salikhov <mathbunnyru@users.noreply.github.com>
Co-authored-by: Ed Hennis <ed@ripple.com>
2026-10-13 08:46:55 +00:00
Bart
c2478f509c refactor: Open the acquisition seams its tests need (#8081)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-10-13 03:51:12 +00:00
Mayukha Vadari
c908ed7839 fix: Reject malformed conditions in EscrowFinish preflight (#8523) 2026-10-12 20:48:55 +00:00
Bart
fc57311c81 refactor: Expose a SHAMapAddNode verdict as counts (#8080)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-10-12 20:02:36 +00:00
Mayukha Vadari
818de49264 refactor: Use DepositPreauthEntry everywhere (#8364) 2026-10-12 18:56:14 +00:00
dependabot[bot]
1318b1e58b chore: [DEPENDABOT] bump cxx from 1.0.202 to 1.0.203 in /crates in the rust-dependencies group (#8564)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-12 17:23:41 +00:00
Ayaz Salikhov
617c816ec8 chore: Fix readability-trailing-comma (#8545) 2026-10-12 13:57:24 +00:00
dependabot[bot]
34fc86f585 ci: [DEPENDABOT] bump the github-actions group across 1 directory with 2 updates (#8562)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-12 12:59:39 +00:00
Ayaz Salikhov
e071f0a28d chore: Fix misc-const-correctness (#8546) 2026-10-12 11:44:06 +00:00
Ayaz Salikhov
8faa624ade chore: Update XRPLF/actions to use pip from python3.14 explicitly (#8560) 2026-10-12 10:06:06 +00:00
Ayaz Salikhov
6d6ab2d067 Merge remote-tracking branch 'upstream/release/3.4.x' into develop 2026-10-10 00:20:39 +01:00
Ayaz Salikhov
00e6407514 chore: Bump version to 3.4.1 and make pkg_release 2 2026-10-09 23:50:58 +01:00
Ayaz Salikhov
00c06edffb Merge remote-tracking branch 'upstream/release/3.4.x' into develop 2026-10-09 22:45:18 +01:00
Ayaz Salikhov
de5053ae0d build: Reduce number of conan logs (#8548) 2026-10-09 16:33:41 +00:00
Ayaz Salikhov
1940ec5c2a chore: Fix readability-redundant-lambda-parameter-list (#8544) 2026-10-09 16:20:45 +00:00
Denis Angell
19c94c73f4 fix: Reject Batch inner txs with the wrong wrapper (fixBatchV1_2) 2026-10-09 16:04:36 +01:00
Bart
cd005ff60d ci: Update Nexus packaging URL 2026-10-09 16:04:36 +01:00
Gregory Tsipenyuk
578224f2e6 fix: Assorted integer-arithmetic hardening in the payment engine and ledger helpers 2026-10-09 16:04:35 +01:00
Shawn Xie
3857ce21cd fix: Change mpt subscription msg type back to transaction (#8539) 2026-10-09 13:33:13 +00:00
Ayaz Salikhov
aa490df46b chore: Update clang-tidy image to v23 (#8536) 2026-10-09 10:19:07 +00:00
Jingchen
88c1f1e7ac feat: Integrate Permissioned Domain & Credential Checks for Lending Protocol (#6517)
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Co-authored-by: Vito <5780819+Tapanito@users.noreply.github.com>
Co-authored-by: Ed Hennis <ed@ripple.com>
2026-10-09 10:18:31 +00:00
Ayaz Salikhov
ede8af8191 refactor: Group binaries in subdirectories (#8535)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-10-08 12:34:06 +00:00
Alex Kremer
3c24b605a1 chore: Split multiple-in-one into individual tests (#8025) 2026-10-08 09:57:38 +00:00
Alex Kremer
d343b542f1 refactor: Migrate handlers to rpc-spec (B) (#8484) 2026-10-07 13:46:37 +00:00
Ayaz Salikhov
7acd719bf7 build: Use images with Clang 23 except for clang-tidy (#8530) 2026-10-07 13:08:20 +00:00
Ayaz Salikhov
b4564d5301 chore: Update pre-commit hooks and image (#8528) 2026-10-07 10:59:37 +00:00
Shawn Xie
60195e6d37 fix: Fix MPT partial payment overflow (#8302) 2026-10-06 23:16:13 +00:00
Ayaz Salikhov
3d526d456e build: Use LLVM 23 (#8522) 2026-10-06 21:19:57 +00:00
Denis Angell
f05c9f7913 refactor: Extract escrow lock helpers and paychan test helpers (#7882)
Co-authored-by: Mayukha Vadari <mvadari@ripple.com>
2026-10-06 19:58:52 +00:00
Harshit Gupta
c2a4bc3aa1 fix: Validate vetoed parameter type in feature RPC (#7583)
Co-authored-by: Mayukha Vadari <mvadari@ripple.com>
2026-10-06 19:44:56 +00:00
Mayukha Vadari
718185e3b2 refactor: Use CheckEntry everywhere (#8349) 2026-10-06 19:26:35 +00:00
Ayaz Salikhov
1d7783bb86 build: Make conan retry with Conan Center's source backups (#8524) 2026-10-06 19:23:38 +00:00
Mayukha Vadari
9fd2c552f5 refactor: Use AmendmentsEntry everywhere (#8368) 2026-10-06 17:59:44 +00:00
Ayaz Salikhov
70b8fd301b chore: Update docker images; link Conan-built tools with a static runtime (#8520) 2026-10-06 16:47:35 +00:00
Mayukha Vadari
e6564f553d refactor: Use NegativeUNLEntry everywhere (#8365) 2026-10-06 14:23:24 +00:00
Ayaz Salikhov
ed96e60ce3 build: Make clang-tools custom in Nix, to match what's being built (#8521) 2026-10-06 13:59:12 +00:00
Alex Kremer
2ebd745a1b refactor: Migrate handlers to rpc-spec (A) (#8345) 2026-10-06 13:19:20 +00:00
Mayukha Vadari
cfcbe45b60 test: Declare, not define, entry instantiations in SLEBase test (#8357) 2026-10-06 12:12:22 +00:00
yinyiqian1
9cbf78ba99 test: Add more tests for granular permissions (#8459)
Co-authored-by: Bart <bthomee@users.noreply.github.com>
2026-10-05 23:18:58 +00:00
Timur Yalymov
b8d8738f81 fix: Enforce that MPT issuance flags are never cleared (#8152) 2026-10-05 23:17:36 +00:00
Timur Yalymov
63c97e719f feat: Allow lending transactions in Batch (#8244)
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com>
2026-10-05 23:17:26 +00:00
Mayukha Vadari
3ac26f23c7 feat: Add full support for all objects in ledger_entry (#6319)
Co-authored-by: Timur Yalymov <36795566+tyalymov@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com>
Co-authored-by: Ayaz Salikhov <mathbunnyru@users.noreply.github.com>
2026-10-05 23:17:14 +00:00
Bart
ff9410bc56 build: Define XRPL_ASAN, XRPL_TSAN, and XRPL_UBSAN compile definitions (#8483)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-10-05 23:15:08 +00:00
Mayukha Vadari
b4417cb105 Merge remote-tracking branch 'upstream/mvadari/codify-agents-md' into mvadari/codify-agents-md 2026-09-10 11:18:32 -04:00
Mayukha Vadari
ef55ca97c8 docs: Drop redundant repo-level AGENTS.md pointers, shorten Build section
- The harness auto-loads every ancestor AGENTS.md (including root), so
  a nested file telling an agent to "see the repo-level AGENTS.md" is
  pure overhead — it can trigger a re-load/duplication-check/discard
  cycle, and some models just retain the duplicate content in context.
  Drop that pointer from every nested AGENTS.md; keep pointers to
  things that are NOT auto-loaded (README.md, docs/consensus.md,
  non-ancestor AGENTS.md like tx/ -> rpc/).
- Root AGENTS.md's Build section is now a short index pointer to
  BUILD.md and docs/build/nix.md instead of restating Nix setup steps,
  per review feedback that AGENTS.md should mostly index other docs
  rather than duplicate them.

Addresses PR review comments:
https://github.com/XRPLF/rippled/pull/8198#discussion_r3979325914
https://github.com/XRPLF/rippled/pull/8198#discussion_r3979424992
2026-09-10 11:18:15 -04:00
Mayukha Vadari
de1a5f882c Merge branch 'develop' into mvadari/codify-agents-md 2026-09-09 14:00:02 -04:00
Mayukha Vadari
6dc958c136 docs: Fix std::unexpected wording, split UNREACHABLE out of amendment section
- ledger/helpers/AGENTS.md: std::expected<..., TER>/std::unexpected(tec*)
  is already the established idiom here (confirmed in AMMHelpers.cpp,
  CredentialHelpers.cpp), not something to avoid; reword so the rule
  doesn't read as discouraging it.
- transactors/AGENTS.md: the UNREACHABLE/LCOV_EXCL coverage rule isn't
  amendment-specific, so give it its own heading instead of nesting it
  under "Gating amendment-dependent code".
- Root AGENTS.md: comments should explain why, not what/how, since the
  code already shows that.
- Drop the pseudo-account exemption rule from transactors/AGENTS.md —
  needs refining before it's codified.

Addresses PR review comments:
https://github.com/XRPLF/rippled/pull/8198#discussion_r3962352164
https://github.com/XRPLF/rippled/pull/8198#discussion_r3967566278
2026-09-09 13:58:12 -04:00
Mayukha Vadari
6b9db2460c docs: Loosen Nix mandate, extract ARCHITECTURE.md, add docs-upkeep note
- Build section now recommends the Nix devshell rather than requiring
  it; contributors can use their own toolchain/build flow.
- Move the directory-by-directory architecture map out of AGENTS.md
  into a new top-level ARCHITECTURE.md, with a pointer left behind.
- Add a short note asking contributors to keep AGENTS.md/README/
  ARCHITECTURE.md files current when they change what those docs cover.
- ledger/helpers/AGENTS.md also needs the amendment-gating shape,
  per-amendment flag naming, and UNREACHABLE/LCOV_EXCL pairing notes
  (previously only in transactors/AGENTS.md) — the vault/lending
  helper files these were evidenced on live in ledger/helpers/, not
  tx/transactors/.
2026-09-09 13:46:51 -04:00
Mayukha Vadari
40988e1d77 Merge remote-tracking branch 'upstream/mvadari/codify-agents-md' into mvadari/codify-agents-md 2026-09-09 13:39:16 -04:00
Mayukha Vadari
7c642cdf9e docs: Scope AGENTS.md content to the smallest applicable folder
Team decided AGENTS.md content should live in the narrowest folder it
applies to, rather than the broadest, to keep context small for agents
working elsewhere in the repo. Restructure round 1/2's additions
accordingly, with no change in the rules themselves:

- Move the repo-wide API-CHANGELOG.md rule out of root AGENTS.md and
  into rpc/AGENTS.md (its natural home), with a one-line pointer left
  in tx/AGENTS.md for the signing-behavior case.
- Move the amendment-gating *code style* rules (single enabled/disabled
  block, per-amendment flag naming, UNREACHABLE/LCOV_EXCL pairing) from
  tx/AGENTS.md down into transactors/AGENTS.md, since all cited PR
  evidence was in transactor code, not tx/-level files.
- Move the "shared test setup belongs in jtx/" rule out of root
  AGENTS.md into a new src/test/AGENTS.md (+ CLAUDE.md symlink).
- Trim the now-duplicated amendment sentence out of root's Architecture
  bullet in favor of a pointer to tx/AGENTS.md.
2026-09-09 13:38:42 -04:00
Mayukha Vadari
6d2c69986d Merge branch 'develop' into mvadari/codify-agents-md 2026-09-08 17:11:49 -04:00
Mayukha Vadari
0a1708dbfe docs: Add subsystem AGENTS.md files for transactors, ledger helpers, and READMEs
Round 2 of PR-review mining: found two subsystem-specific, unrecorded
conventions (pseudo-account auth/freeze exemption in vault/lending
transactors; SLE-type assertion in ledger helpers) that warrant their
own nested AGENTS.md rather than more root-level bullets.

Also added thin AGENTS.md pointer files (with CLAUDE.md symlinks,
matching the existing convention) for subsystems that already have a
substantial README.md but no AGENTS.md — agent tooling auto-loads
AGENTS.md when working in a directory but not README.md, so those
conventions were otherwise invisible to an agent that didn't think to
look.
2026-09-08 17:05:08 -04:00
Mayukha Vadari
5b6cd13a59 docs: Codify recurring PR review feedback into AGENTS.md
Mined 6 months of merged PR review comments (human reviewers only) for
conventions that recur across multiple PRs/reviewers but aren't already
documented in CONTRIBUTING.md/CodingStyle.md, and folded them in:

- Doxygen comments expected on new public functions/methods.
- API-CHANGELOG.md requirement is repo-wide, not RPC-directory-scoped.
- Shared test setup belongs in jtx/, not copy-pasted across test files.
- Amendment requirement test and a design-considerations checklist,
  sourced from the internal "Considerations for Future Amendments" doc.
- Amendment gating shape (single enabled/disabled block), per-amendment
  flag naming, and UNREACHABLE/LCOV_EXCL pairing, all recurring asks in
  tx/vault/invariant PR reviews.
2026-09-04 23:07:38 -04:00
907 changed files with 50720 additions and 27591 deletions

View File

@@ -5,7 +5,10 @@ Checks: "-*,
-bugprone-exception-escape,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-narrowing-conversions,
-bugprone-signed-bitwise,
-bugprone-std-exception-baseclass,
-bugprone-throwing-static-initialization,
-bugprone-unhandled-code-paths,
cppcoreguidelines-*,
-cppcoreguidelines-avoid-c-arrays,
@@ -14,6 +17,7 @@ Checks: "-*,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-c-copy-assignment-signature,
-cppcoreguidelines-explicit-constructor,
-cppcoreguidelines-interfaces-global-init,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-missing-std-forward,
@@ -32,6 +36,7 @@ Checks: "-*,
llvm-namespace-comment,
misc-*,
-misc-explicit-constructor,
-misc-multiple-inheritance,
-misc-no-recursion,
-misc-non-private-member-variables-in-classes,
@@ -45,6 +50,8 @@ Checks: "-*,
-modernize-avoid-c-style-cast,
-modernize-return-braced-init-list,
-modernize-use-integer-sign-comparison,
-modernize-use-string-view,
-modernize-use-structured-binding,
-modernize-use-trailing-return-type,
performance-*,
@@ -53,6 +60,7 @@ Checks: "-*,
-performance-noexcept-move-constructor,
-performance-unnecessary-copy-initialization,
-performance-unnecessary-value-param,
-performance-use-std-move,
readability-*,
-readability-avoid-const-params-in-decls,
@@ -65,7 +73,10 @@ Checks: "-*,
-readability-named-parameter,
-readability-qualified-auto,
-readability-redundant-access-specifiers,
-readability-redundant-nested-if,
-readability-redundant-qualified-alias,
-readability-static-accessed-through-instance,
-readability-trivial-switch,
-readability-uppercase-literal-suffix
"
# ---

View File

@@ -108,3 +108,75 @@ endfunction()
function(patch_nix_binary target)
endfunction()
function(rpcspec_generate_instantiations)
set(options)
set(oneValueArgs OUT_VAR VALUE_TYPE VIEW_HEADER INCLUDE_DIR)
set(multiValueArgs HANDLERS)
cmake_parse_arguments(
THIS_FUNCTION_PREFIX
"${options}"
"${oneValueArgs}"
"${multiValueArgs}"
${ARGN}
)
endfunction()
function(corrosion_import_crate)
set(options
ALL_FEATURES
NO_DEFAULT_FEATURES
NO_STD
NO_LINKER_OVERRIDE
NO_USES_TERMINAL
LOCKED
FROZEN
)
set(oneValueArgs MANIFEST_PATH PROFILE IMPORTED_CRATES)
set(multiValueArgs
CRATE_TYPES
CRATES
FEATURES
FLAGS
OVERRIDE_CRATE_TYPE
)
cmake_parse_arguments(
THIS_FUNCTION_PREFIX
"${options}"
"${oneValueArgs}"
"${multiValueArgs}"
${ARGN}
)
endfunction()
function(corrosion_set_env_vars target_name env_var)
endfunction()
function(corrosion_add_cxxbridge cxx_target)
set(options)
set(oneValueArgs CRATE)
set(multiValueArgs FILES)
cmake_parse_arguments(
THIS_FUNCTION_PREFIX
"${options}"
"${oneValueArgs}"
"${multiValueArgs}"
${ARGN}
)
endfunction()
function(_unlink_libgcc_s crate)
endfunction()
function(add_xrpl_crate name)
set(options)
set(oneValueArgs CRATE)
set(multiValueArgs FILES)
cmake_parse_arguments(
THIS_FUNCTION_PREFIX
"${options}"
"${oneValueArgs}"
"${multiValueArgs}"
${ARGN}
)
endfunction()

View File

@@ -15,9 +15,9 @@ inputs:
required: false
default: "false"
log_verbosity:
description: "The logging verbosity."
description: 'The logging verbosity ("quiet", "verbose"), or empty to use the Conan defaults.'
required: false
default: "verbose"
default: ""
sanitizers:
description: "The sanitizers to enable."
required: false
@@ -35,6 +35,16 @@ runs:
LOG_VERBOSITY: ${{ inputs.log_verbosity }}
SANITIZERS: ${{ inputs.sanitizers }}
run: |
# By default, leave the verbosity unset, so CMake configure output is
# shown, but compile commands and Boost's b2 debug output (~85k lines
# when "verbose") are not.
VERBOSITY_ARGS=()
if [[ -n "${LOG_VERBOSITY}" ]]; then
VERBOSITY_ARGS=(
--conf:all tools.build:verbosity="${LOG_VERBOSITY}"
--conf:all tools.compilation:verbosity="${LOG_VERBOSITY}"
)
fi
conan install \
--profile:all ci \
--build="${BUILD_OPTION}" \
@@ -42,6 +52,13 @@ runs:
--options:host='&:xrpld=True' \
--settings:all build_type="${BUILD_TYPE}" \
--conf:all tools.build:jobs=${BUILD_NPROC} \
--conf:all tools.build:verbosity="${LOG_VERBOSITY}" \
--conf:all tools.compilation:verbosity="${LOG_VERBOSITY}" \
.
"${VERBOSITY_ARGS[@]}" \
--format=json \
. >"${RUNNER_TEMP}/conan-graph.json"
# Tools that run during the build may only load glibc from the Nix store,
# as their package ID survives a GCC runtime update.
- name: Check build-context packages for Nix store dependencies (Linux)
if: ${{ runner.os == 'Linux' }}
shell: bash
run: ./bin/nix/check-build-context-runtime.sh "${RUNNER_TEMP}/conan-graph.json"

View File

@@ -36,3 +36,18 @@ updates:
rust-dependencies:
patterns:
- "*"
- package-ecosystem: uv
directory: /cmake/scripts/codegen
schedule:
interval: weekly
day: monday
time: "04:00"
timezone: Etc/GMT
commit-message:
prefix: "chore: [DEPENDABOT] "
target-branch: develop
groups:
codegen-dependencies:
patterns:
- "*"

View File

@@ -1,5 +1,5 @@
{
"image_tag": "sha-060957e",
"image_tag": "sha-3d526d4",
"configs": {
"ubuntu": [
{
@@ -74,7 +74,7 @@
"extra_cmake_args": "-Dvalidator_keys=ON",
"package": {
"type": "deb",
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-3a2d19f"
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-e6055dd"
}
},
{
@@ -86,7 +86,7 @@
"extra_cmake_args": "-Dvalidator_keys=ON -Dassert=ON",
"package": {
"type": "deb",
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-3a2d19f",
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-e6055dd",
"variant": "assert"
}
}
@@ -101,7 +101,7 @@
"extra_cmake_args": "-Dvalidator_keys=ON",
"package": {
"type": "rpm",
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-3a2d19f"
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-e6055dd"
}
}
]

View File

@@ -12,8 +12,7 @@ on:
- "!nix/docker/README.md"
- "!nix/devshell.nix"
- "!nix/check-tools/**"
- "bin/default-loader-path.sh"
- "bin/install-sanitizer-libs.sh"
- "bin/nix/default-loader-path.sh"
pull_request:
paths:
- ".github/workflows/build-nix-images.yml"
@@ -25,8 +24,8 @@ on:
- "!nix/devshell.nix"
- "!nix/check-tools/**"
- "bin/check-tools.sh"
- "bin/default-loader-path.sh"
- "bin/install-sanitizer-libs.sh"
- "bin/nix/default-loader-path.sh"
- "bin/install/sanitizer-libs.sh"
workflow_dispatch:
concurrency:

View File

@@ -5,13 +5,12 @@ on:
branches:
- develop
paths:
- ".github/workflows/build-packaging-images.yml"
- "bin/install-packaging-tools.sh"
- "bin/install/packaging-tools.sh"
- "package/images/packaging/**"
pull_request:
paths:
- ".github/workflows/build-packaging-images.yml"
- "bin/install-packaging-tools.sh"
- "bin/install/packaging-tools.sh"
- "package/images/packaging/**"
workflow_dispatch:

View File

@@ -5,7 +5,6 @@ on:
branches:
- develop
paths:
- ".github/workflows/build-pre-commit-image.yml"
- "bin/pre-commit/Dockerfile"
- "rust-toolchain.toml"
pull_request:

View File

@@ -34,7 +34,7 @@ permissions:
jobs:
audit:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
permissions:
contents: read
# Needed to open an issue on scheduled failures.
@@ -66,7 +66,7 @@ jobs:
- name: Create issue
if: ${{ steps.audit.outcome != 'success' && github.event_name == 'schedule' }}
uses: XRPLF/actions/create-issue@2b8bc36af85b88bca0dd7bfac2e2dc05f94ad712
uses: XRPLF/actions/create-issue@b489c21d53928f7dab4874912fec40e4f6c9e85e
with:
title: "cargo audit found vulnerabilities"
body_file: /tmp/cargo-audit-issue.md

View File

@@ -53,7 +53,7 @@ jobs:
- name: Create issue
if: ${{ failure() && github.event_name == 'schedule' && github.repository == 'XRPLF/rippled' }}
uses: XRPLF/actions/create-issue@2b8bc36af85b88bca0dd7bfac2e2dc05f94ad712
uses: XRPLF/actions/create-issue@b489c21d53928f7dab4874912fec40e4f6c9e85e
with:
title: "Releases not merged back into develop"
body_file: ${{ env.ISSUE_FILE }}

View File

@@ -79,7 +79,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: false
@@ -108,7 +108,7 @@ jobs:
- name: Upload regenerated snapshot
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: check-tools-${{ runner.os }}-${{ runner.arch }}
path: ${{ matrix.snapshot }}

View File

@@ -93,9 +93,8 @@ jobs:
.github/workflows/reusable-upload-recipe.yml
.clang-tidy
.codecov.yml
bin/check-nix-store-refs.sh
bin/check-tools.sh
bin/default-loader-path.sh
bin/nix/**
cfg/**
cmake/**
conan/**

View File

@@ -31,9 +31,8 @@ on:
- ".github/workflows/reusable-upload-recipe.yml"
- ".clang-tidy"
- ".codecov.yml"
- "bin/check-nix-store-refs.sh"
- "bin/check-tools.sh"
- "bin/default-loader-path.sh"
- "bin/nix/**"
- "cfg/**"
- "cmake/**"
- "conan/**"

View File

@@ -14,7 +14,7 @@ on:
jobs:
# Call the workflow in the XRPLF/actions repo that runs the pre-commit hooks.
run-hooks:
uses: XRPLF/actions/.github/workflows/pre-commit.yml@279ec358f4a1be4088be3e024b07916fa97c75b6
uses: XRPLF/actions/.github/workflows/pre-commit.yml@d7f2acb47d9662e1e2f8b1f5feffa6d21cc3290a
with:
runs_on: ubuntu-latest
container: '{ "image": "ghcr.io/xrplf/xrpld/pre-commit:sha-473fe44" }'
container: '{ "image": "ghcr.io/xrplf/xrpld/pre-commit:sha-70b8fd3" }'

View File

@@ -41,13 +41,13 @@ env:
jobs:
build:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: false

View File

@@ -129,7 +129,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: ${{ inputs.ccache_enabled }}
@@ -186,9 +186,6 @@ jobs:
with:
build_nproc: ${{ steps.nproc.outputs.nproc }}
build_type: ${{ inputs.build_type }}
# Set the verbosity to "quiet" for Windows to avoid an excessive
# amount of logs. For other OSes, the "verbose" logs are more useful.
log_verbosity: ${{ runner.os == 'Windows' && 'quiet' || 'verbose' }}
sanitizers: ${{ inputs.sanitizers }}
- name: Configure CMake
@@ -257,20 +254,20 @@ jobs:
# cache included, since what it holds is what gets uploaded and reused.
- name: Check the build output for Nix store references (Nix toolchain)
if: ${{ inputs.toolchain == 'nix' }}
run: ./bin/check-nix-store-refs.sh "${BUILD_DIR}"
run: ./bin/nix/check-nix-store-refs.sh "${BUILD_DIR}"
- name: Check the Conan cache for Nix store references (Nix toolchain)
if: ${{ inputs.toolchain == 'nix' }}
run: ./bin/check-nix-store-refs.sh "${CONAN_HOME}"
run: ./bin/nix/check-nix-store-refs.sh "${CONAN_HOME}"
# Only what PatchNixBinary.cmake retargets: the toolchain in the Linux
# images always references the store. Same condition it uses.
- name: Check for Nix store references (Linux)
if: ${{ runner.os == 'Linux' && env.SANITIZERS_ENABLED == 'false' }}
run: |
./bin/check-nix-store-refs.sh "${BUILD_DIR}/xrpld"
./bin/check-nix-store-refs.sh "${BUILD_DIR}/xrpl_tests"
./bin/check-nix-store-refs.sh "${BUILD_DIR}/xrpld_tests"
./bin/nix/check-nix-store-refs.sh "${BUILD_DIR}/xrpld"
./bin/nix/check-nix-store-refs.sh "${BUILD_DIR}/xrpl_tests"
./bin/nix/check-nix-store-refs.sh "${BUILD_DIR}/xrpld_tests"
- name: Show ccache statistics
if: ${{ inputs.ccache_enabled }}
@@ -283,7 +280,7 @@ jobs:
- name: Upload the binary (Linux)
if: ${{ env.PACKAGING_ARTIFACTS_ENABLED == 'true' && runner.os == 'Linux' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: xrpld-${{ inputs.config_name }}
path: ${{ env.BUILD_DIR }}/xrpld
@@ -297,7 +294,7 @@ jobs:
- name: Upload the validator-keys binary
if: ${{ env.PACKAGING_ARTIFACTS_ENABLED == 'true' && env.VALIDATOR_KEYS_ENABLED == 'true' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: validator-keys-${{ inputs.config_name }}
path: |
@@ -308,7 +305,7 @@ jobs:
- name: Upload the test binary (Linux)
if: ${{ github.event.repository.visibility == 'public' && runner.os == 'Linux' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: xrpl_tests-${{ inputs.config_name }}
path: ${{ env.BUILD_DIR }}/xrpl_tests
@@ -324,7 +321,7 @@ jobs:
- name: Upload server definitions
if: ${{ github.event.repository.visibility == 'public' && inputs.config_name == 'ubuntu-gcc-debug-amd64-coverage' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: server-definitions
path: ${{ env.BUILD_DIR }}/server_definitions.json

View File

@@ -34,7 +34,7 @@ jobs:
needs: [determine-files]
if: ${{ needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.need_full_run == 'true' }}
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-060957e"
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-3d526d4"
permissions:
contents: read
issues: write
@@ -43,7 +43,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: false
@@ -73,7 +73,6 @@ jobs:
with:
build_nproc: ${{ steps.nproc.outputs.nproc }}
build_type: ${{ env.BUILD_TYPE }}
log_verbosity: verbose
- name: Configure CMake
working-directory: ${{ env.BUILD_DIR }}
@@ -110,7 +109,7 @@ jobs:
- name: Upload clang-tidy output
if: ${{ github.event.repository.visibility == 'public' && steps.run_clang_tidy.outcome != 'success' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
path: ${{ env.OUTPUT_FILE }}
archive: false
@@ -134,7 +133,7 @@ jobs:
- name: Upload clang-tidy diff output
if: ${{ github.event.repository.visibility == 'public' && steps.files_changed.outcome != 'success' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
path: ${{ env.DIFF_FILE }}
archive: false
@@ -186,7 +185,7 @@ jobs:
- name: Create issue
if: ${{ steps.run_clang_tidy.outcome != 'success' && inputs.create_issue_on_failure }}
uses: XRPLF/actions/create-issue@2b8bc36af85b88bca0dd7bfac2e2dc05f94ad712
uses: XRPLF/actions/create-issue@b489c21d53928f7dab4874912fec40e4f6c9e85e
with:
title: "Clang-tidy check failed"
body_file: ${{ env.ISSUE_FILE }}

View File

@@ -44,7 +44,7 @@ jobs:
# Every package lands in one directory; the step below picks its own,
# which keeps this independent of the artifact names.
- name: Download package artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: "*-pkg"
merge-multiple: true
@@ -95,11 +95,15 @@ jobs:
apt-get update -qq
apt-get install -y "./${PACKAGE}"
# Rocky Linux and AlmaLinux pick a mirror from a mirrorlist, and now and
# then get one that serves the repository metadata at a few hundred kB/s,
# which alone outlasts the job's timeout. dnf only abandons a mirror below
# 1000 B/s for 30 s, so raise that floor to fail over to the next one fast.
- name: Install the RPM
if: ${{ inputs.package_type == 'rpm' }}
env:
PACKAGE: ${{ steps.find.outputs.package }}
run: dnf install -y "./${PACKAGE}"
run: dnf install -y --setopt=minrate=1M --setopt=timeout=10 "./${PACKAGE}"
- name: Run xrpld
run: xrpld --version

View File

@@ -100,18 +100,18 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: false
- name: Download pre-built xrpld binary
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: ${{ matrix.xrpld_artifact_name }}
path: ${{ env.BUILD_DIR }}
- name: Download pre-built validator-keys binary
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: ${{ matrix.validator_keys_artifact_name }}
path: ${{ env.BUILD_DIR }}
@@ -151,7 +151,7 @@ jobs:
# version starts with a digit and a longer name does not, so that one digit
# is what tells 'xrpld-3.4.1-...' from 'xrpld-assert-3.4.1-...'.
- name: Upload package artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: ${{ matrix.xrpld_artifact_name }}-pkg
path: |
@@ -160,7 +160,7 @@ jobs:
if-no-files-found: error
- name: Upload debug symbol artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: ${{ matrix.xrpld_artifact_name }}-pkg-debug
path: |
@@ -225,13 +225,13 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: false
# Both artifacts, so the debug symbols are published alongside the package.
- name: Download package artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: ${{ matrix.xrpld_artifact_name }}-pkg*
merge-multiple: true
@@ -287,7 +287,7 @@ jobs:
run: echo "PUSH=${PUSH}" | tee -a "${GITHUB_ENV}"
- name: Download package artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: "*-pkg"
merge-multiple: true
@@ -295,7 +295,7 @@ jobs:
- name: Download voidstar binary
if: ${{ matrix.target == 'voidstar' }}
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: xrpld-ubuntu-clang-debug-amd64-voidstar
path: ${{ env.CONTEXT }}

View File

@@ -28,7 +28,7 @@ permissions:
jobs:
clippy:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -41,7 +41,7 @@ jobs:
coverage:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -70,7 +70,7 @@ jobs:
doc:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

View File

@@ -40,7 +40,7 @@ defaults:
jobs:
upload:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
env:
REMOTE_NAME: ${{ inputs.remote_name }}
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.remote_username }}
@@ -50,7 +50,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: false

View File

@@ -68,7 +68,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
uses: XRPLF/actions/prepare-runner@8e98d61f011fd2a67f3fde5f0f075662c099a527
with:
enable_ccache: false
@@ -108,14 +108,11 @@ jobs:
build_nproc: ${{ steps.nproc.outputs.nproc }}
build_type: ${{ matrix.build_type }}
force_build: ${{ github.event_name == 'schedule' || github.event.inputs.force_source_build == 'true' }}
# Set the verbosity to "quiet" for Windows to avoid an excessive
# amount of logs. For other OSes, the "verbose" logs are more useful.
log_verbosity: ${{ runner.os == 'Windows' && 'quiet' || 'verbose' }}
sanitizers: ${{ matrix.sanitizers }}
- name: Check the Conan cache for Nix store references (Nix toolchain)
if: ${{ matrix.toolchain == 'nix' }}
run: ./bin/check-nix-store-refs.sh "${CONAN_HOME}"
run: ./bin/nix/check-nix-store-refs.sh "${CONAN_HOME}"
- name: Log into Conan remote
if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}

View File

@@ -60,7 +60,7 @@ repos:
types_or: [c++, c]
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: e2b496dc2bd8340c2524cb9a2d2a943cde1bb6df # frozen: v23.1.1
rev: a9a8a861f30ed207ead7d5a3b7e8032283ba5da7 # frozen: v23.1.2
hooks:
- id: clang-format
args: [--style=file]
@@ -82,9 +82,10 @@ repos:
files: ^crates/.*\.rs$
- repo: https://github.com/BlankSpruce/gersemi-pre-commit
rev: f1c4833f8cf23c6d952673abc73525411a5719e8 # frozen: 0.29.1
rev: 28010ddd6016e1a0f7bd232acb6536ef996ae897 # frozen: 0.29.2
hooks:
- id: gersemi
args: [-i, --warnings-as-errors]
- repo: https://github.com/rbubley/mirrors-prettier
rev: ef4a397f916211b4a39ccf9d3d9cbb6562157251 # frozen: v3.9.9
@@ -95,19 +96,19 @@ repos:
# Scoped to package/: the rest of the repo's Python has pre-existing findings,
# so widening these is its own change.
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: a56c0b927e6465d37cae3e97d35d4d18ab2b96cd # frozen: v0.16.9
rev: f12be1ebaa5351c1fc76472de98db2c3446c8253 # frozen: v0.16.10
hooks:
- id: ruff-check
args: [--fix]
files: ^package/.*\.py$
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 4160603246a6b365d4a2af661c6d71b0a0f50478 # frozen: 26.5.1
rev: 96ae3e5802f3fe2d551e703e18f0a367d1a81ac2 # frozen: 26.10.0
hooks:
- id: black
- repo: https://github.com/pre-commit/mirrors-mypy
rev: 7ff8d35ae36a7d2b968f2f90b4c723e292e594ee # frozen: v2.3.1
rev: 2834ec6639549dd6796205c8f011dedcd587288b # frozen: v2.4.0
hooks:
- id: mypy
args: [--strict]

View File

@@ -4,24 +4,18 @@ This file provides guidance to AI coding agents (Claude Code, and other AGENTS.m
## Build
Required on Linux/macOS: use the Nix devshell, which sets up the compiler, Conan, ccache, and (optionally) Rust automatically.
```bash
nix develop
```
For alternate devshell variants (specific compiler, no-compiler, coverage), see [docs/build/nix.md](./docs/build/nix.md). For the manual build steps, CMake options, and protocol codegen commands, see [BUILD.md](./BUILD.md) (`## Steps`, `## Options`, `## Code generation`).
For the build steps, CMake options, and protocol codegen commands, see [BUILD.md](./BUILD.md) (`## Steps`, `## Options`, `## Code generation`). Nix development shells are available in the repo (not required) — see [docs/build/nix.md](./docs/build/nix.md) for setup and variants.
Rust crate tests (independent of the CMake build): `cargo test --manifest-path crates/Cargo.toml --workspace` (CI uses `cargo nextest`).
## Testing
Unit tests are a custom framework built into the `xrpld` binary itself (not Boost.Test/GTest/Catch); see [CONTRIBUTING.md](./CONTRIBUTING.md#unit-tests) for the basic invocation. Notes not covered there:
The legacy unit tests under `src/test/` are a custom framework (derived from Beast, not Boost.Test/GTest/Catch) built into the `xrpld` binary; see [CONTRIBUTING.md](./CONTRIBUTING.md#unit-tests) for the basic invocation. The gtest suites under `src/tests/` build into the separate `xrpl_tests` and `xrpld_tests` binaries (run with `--gtest_filter=...`). Notes not covered there:
- A suite's `--unittest` name is built from the arguments to its `BEAST_DEFINE_TESTSUITE`/`BEAST_DEFINE_TESTSUITE_PRIO` macro (usually at the bottom of the test file), in reverse order and joined with `.`: `BEAST_DEFINE_TESTSUITE(Credentials, app, xrpl)` → `xrpl.app.Credentials`.
- `--unittest-arg` does nothing — don't use it.
- `--unittest-arg=<string>` passes one string to every suite that runs; a suite reads it with `arg()` and interprets it itself. Only a few suites use it (e.g. `TheoreticalQuality`, the nodestore `import` test); for the rest it has no effect.
- Tests that run offline in under a minute should be automatic `--unittest` suites; anything else is a manual/integration test.
- New tests should be written using `gtest` under `src/tests/` unless that isn't possible, in which case fall back to the legacy Beast framework under `src/test/`. `tests/` (top-level) holds integration tests exercised against `libxrpl`/`xrpld`.
- New tests should be written using `gtest` under `src/tests/` unless that isn't possible, in which case fall back to the legacy Beast framework under `src/test/` (see [src/test/AGENTS.md](./src/test/AGENTS.md) for conventions specific to that directory). `tests/` (top-level) holds integration tests exercised against `libxrpl`/`xrpld`.
## Lint/Format
@@ -29,14 +23,14 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md#pre-commit-hooks) for `pre-commit` setup
## Code Style
New file placement and header levelization: see [CONTRIBUTING.md](./CONTRIBUTING.md#before-making-a-pull-request). Braces, whitespace, member order, and other conventions: see [docs/CodingStyle.md](./docs/CodingStyle.md). `XRPL_ASSERT`/`UNREACHABLE` contracts: see [CONTRIBUTING.md](./CONTRIBUTING.md#contracts-and-instrumentation). Commit messages: see [CONTRIBUTING.md](./CONTRIBUTING.md#good-commit-messages).
New file placement and header levelization: see [CONTRIBUTING.md](./CONTRIBUTING.md#before-making-a-pull-request). Braces, whitespace, member order, and other conventions: see [docs/CodingStyle.md](./docs/CodingStyle.md). `XRPL_ASSERT`/`UNREACHABLE` contracts: see [CONTRIBUTING.md](./CONTRIBUTING.md#contracts-and-instrumentation). Commit messages: see [CONTRIBUTING.md](./CONTRIBUTING.md#good-commit-messages). New public functions/methods need a Doxygen-style comment.
Comments should explain _why_, not _what_/_how_ — the code already shows that. Only describe what/how when the code itself would otherwise be confusing (a non-obvious workaround, a subtle invariant, a surprising constraint).
## Architecture
Paths below reflect the current layout; update this section if modularization moves a subsystem to a different directory.
See [ARCHITECTURE.md](./ARCHITECTURE.md) for the directory-by-directory map of the codebase.
- `include/xrpl/` + `src/libxrpl/` — the core protocol library: ledger, shamap, consensus, crypto, json, resource, nodestore, rdb, peerfinder, and `tx/` (transaction application: `Transactor.cpp`, `applySteps.cpp`, invariants, payment paths). `tx/transactors/` has one file per transaction type, grouped by subsystem: `escrow/`, `vault/`, `lending/`, `sponsor/`, `nft/`, `token/` (MPT), `payment_channel/`, `permissioned_domain/`, `dex/`, `oracle/`, `did/`, `credentials/`, `bridge/`, `check/`, `delegate/`, `account/`, `system/`. Any change to transaction-processing behavior must be gated behind an Amendment.
- `src/xrpld/` — the server application built on top of `libxrpl`: `app`, `core`, `overlay` (P2P networking), `peerfinder`, `perflog`, `rpc`, `shamap`. `main` builds an `ApplicationImp` implementing `Application`; most components hold a reference to it (`app_`), giving broad cross-component access — expect to trace call chains through `Application&`.
- `src/test/` — unit tests mirroring the subsystems above, plus `jtx/` (the transaction-building test DSL — e.g. `jtx/escrow.h`, `jtx/vault.h`, `jtx/sponsor.h`, `jtx/permissioned_dex.h`) and `unit_test/` (the custom test framework itself, derived from Beast).
- `src/tests/` — unit tests for `libxrpl` written in `gtest`, gradually replacing the `src/test` equivalents.
- `crates/` — a Rust workspace (only built with `-Dxrpld -Drust=ON`) bridged into C++ via `cxxbridge`/the `cxx` crate; currently just a `hello_world` interop scaffold. Requires the Rust toolchain pinned in `rust-toolchain.toml` (the Nix devshell provides it automatically).
## Keeping docs current
When you add or change a convention, or touch a subsystem that has its own `AGENTS.md`, `README.md`, or `ARCHITECTURE.md`, update that documentation in the same change rather than leaving it stale.

View File

@@ -28,12 +28,18 @@ Version 3.5.0 is not yet released.
### Additions in 3.5.0
- `subscribe`, `unsubscribe`: Added an optional `mpt_issuances` request field, an array of MPT issuance IDs (hex strings). Subscribers receive a message with `type` `mptTransaction` for each validated transaction whose metadata affects a subscribed issuance; the message has the same fields as the `transactions` stream. MPT issuance subscriptions count toward the per-connection subscription limit. An empty array, a non-array value, or an invalid ID returns `invalidParams`. ([#5671](https://github.com/XRPLF/rippled/pull/5671))
- `subscribe`, `unsubscribe`: Added an optional `mpt_issuances` request field, an array of MPT issuance IDs (hex strings). Subscribers receive the same `transaction` message as the `transactions` stream for each validated transaction whose metadata affects a subscribed issuance. MPT issuance subscriptions count toward the per-connection subscription limit. An empty array, a non-array value, or an invalid ID returns `invalidParams`. ([#5671](https://github.com/XRPLF/rippled/pull/5671))
- `ledger_entry`: Add full support for checks, NFT offers, payment channels, and signer lists. ([#6319](https://github.com/XRPLF/rippled/pull/6319))
### Bugfixes in 3.5.0
- `channel_authorize`: The `channel_id` field now returns an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
- `channel_verify`: The `channel_id` and `signature` fields now return an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
- `account_currencies`, `account_nfts`, `account_offers`: A request with more than one problem may now report a different one of them. An invalid `ledger_hash` or `ledger_index` is reported before an invalid or missing `account`. An invalid `limit` or `marker` is reported before `lgrNotFound` or `actNotFound`, and for `account_offers` also before `actMalformed`. A request with a single problem returns the same error as before. [#8561](https://github.com/XRPLF/rippled/pull/8561)
### 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
@@ -55,6 +61,7 @@ Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta
- `account_lines`: The `peer` field now returns an error if the value is not a string. [#7728](https://github.com/XRPLF/rippled/pull/7728)
- `ledger`: `delivered_amount` is now included in the metadata of successful `AccountDelete` transactions when transactions are expanded (`expand`, or admin-only `full`). Previously it was only added for `Payment` and `CheckCash`, which made `ledger` inconsistent with `tx` and `account_tx`. [#5706](https://github.com/XRPLF/rippled/pull/5706)
- `noripple_check`: The `transactions` field is no longer included in error responses; it is still returned (possibly as an empty array) whenever `transactions` is `true` and the request succeeds. A malformed `account` is now rejected before the ledger is looked up, so that error response no longer carries the `ledger_hash`, `ledger_index`, and `validated` fields ([#6303](https://github.com/XRPLF/rippled/pull/6303)).
- `transaction_entry`: An object or an array in `tx_hash` now returns `malformedRequest`, like any other value that is not a hex hash, instead of an `internal` error.
## XRP Ledger server version 3.3.0

9
ARCHITECTURE.md Normal file
View File

@@ -0,0 +1,9 @@
# Architecture
Paths below reflect the current layout; update this doc if modularization moves a subsystem to a different directory.
- `include/xrpl/` + `src/libxrpl/` — the core protocol library: ledger, shamap, consensus, crypto, json, resource, nodestore, rdb, peerfinder, and `tx/` (transaction application: `Transactor.cpp`, `applySteps.cpp`, invariants, payment paths — see [src/libxrpl/tx/AGENTS.md](./src/libxrpl/tx/AGENTS.md) for amendment-gating conventions). `tx/transactors/` has one file per transaction type, grouped by subsystem: `payment/`, `escrow/`, `vault/`, `lending/`, `sponsor/`, `nft/`, `token/` (MPT), `payment_channel/`, `permissioned_domain/`, `dex/`, `oracle/`, `did/`, `credentials/`, `bridge/`, `check/`, `delegate/`, `proposal/`, `account/`, `system/`.
- `src/xrpld/` — the server application built on top of `libxrpl`: `app`, `core`, `overlay` (P2P networking), `peerfinder`, `perflog`, `rpc`, `shamap`. `main` builds an `ApplicationImp` implementing `Application`; most components hold a reference to it (`app_`), giving broad cross-component access — expect to trace call chains through `Application&`.
- `src/test/` — unit tests mirroring the subsystems above, plus `jtx/` (the transaction-building test DSL — e.g. `jtx/escrow.h`, `jtx/vault.h`, `jtx/sponsor.h`, `jtx/permissioned_dex.h`) and `unit_test/` (the custom test framework itself, derived from Beast).
- `src/tests/` — gtest unit tests, with `libxrpl/` and `xrpld/` subtrees, gradually replacing the `src/test` equivalents.
- `crates/` — a Rust workspace (only built with `-Drust=ON`; today only `xrpl_tests` links it, so `-Dtests=ON` is needed to exercise it) bridged into C++ via `cxxbridge`/the `cxx` crate; currently just a `hello_world` interop scaffold. Requires the Rust toolchain pinned in `rust-toolchain.toml` (the Nix devshell provides it automatically).

View File

@@ -42,7 +42,7 @@ if(is_gcc)
# GCC-specific fixes
add_compile_options(-Wno-unknown-pragmas -Wno-subobject-linkage)
# -Wno-subobject-linkage can be removed when we upgrade GCC version to at least 13.3
elseif(is_clang)
elseif(is_any_clang)
# Clang-specific fixes
add_compile_options(-Wno-unknown-warning-option) # Ignore unknown warning options
elseif(is_msvc)

View File

@@ -34,7 +34,7 @@ so tools can filter them and protection rules can target `release/*` and `stagin
- **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
for fixes that must get into a release after the [code freeze](#release-candidates)
(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.
@@ -49,6 +49,7 @@ develop staging/X.Y.x & release/X.Y.x
│
├── Tag: X.Y.0-b1
├── Tag: X.Y.0-bN
│ (2 weeks before rc1: freeze on large changes starts)
├── Tag: X.Y.0-rc1 ───────────────┐ (branches created)
│ │
│ (development continues) ├── Fixes
@@ -75,16 +76,20 @@ develop staging/X.Y.x & release/X.Y.x
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.
### Freeze on Large Changes
From two weeks before `X.Y.0-rc1` 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`.
### Release Candidates
After branching, the line is in 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.
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.
@@ -106,7 +111,8 @@ one right after the other.
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`.
For a major-minor release, this lifts the [freeze on large changes](#freeze-on-large-changes)
in `develop`.
### Merging Back Into `develop`
@@ -127,7 +133,7 @@ After the merge, `git log develop..<tag>` must be empty.
### Emergency Fixes From `develop`
If a commit was merged to `develop`
and needs to be included in a release after the code freeze:
and needs to be included in a release after the [code freeze](#release-candidates):
1. Create a PR that cherry-picks the commit onto `staging/X.Y.x`.
2. Leave `develop` as it is, with no reverts.

View File

@@ -20,9 +20,11 @@
# development setups, but not in the macOS CI environment. They are checked
# everywhere except when running in CI on macOS.
#
# Tools that Nix also exposes under a version-suffixed name (`clang-tidy-22`,
# `g++-15`, ...) are probed under both names: a suffixed name can break while
# the plain one still works (see mkVersionedToolLinks in nix/packages.nix).
# Tools that Nix also exposes under a version-suffixed name
# (`clang-tidy-<v>`, `g++-<v>`, ...) are probed under both names:
# a suffixed name can break while the plain one still works
# (see mkVersionedToolLinks in nix/packages.nix).
# The suffix is the major version of the plain `clang` / `gcc` on PATH.
#
# Tools scoped to a single dev shell rather than to commonPackages are checked
# only in that shell, keyed off XRPL_DEVSHELL.
@@ -34,9 +36,16 @@
set -uo pipefail
# Version suffixes of the Nix tool links, tracking nix/packages.nix.
gcc_version=15
llvm_version=22
# major_version <compiler>
# Major version of a compiler on PATH, or "unknown" when it isn't there.
major_version() {
local version
version="$("$1" -dumpversion 2>/dev/null)" || version=""
version="${version%%.*}"
printf '%s' "${version:-unknown}"
}
llvm_version="$(major_version clang)"
missing=()
checked=0
@@ -112,6 +121,7 @@ if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
check ClangBuildAnalyzer
check curl
check file
check jq
check less
check make
# net-tools netstat reports "net-tools X.Y"; macOS ships BSD netstat with no
@@ -180,6 +190,7 @@ fi
if [ "${os}" = "linux" ]; then
echo
echo "GCC toolchain:"
gcc_version="$(major_version gcc)"
check gcc
check "gcc-${gcc_version}"
check g++

View File

@@ -0,0 +1,65 @@
#!/usr/bin/env bash
# Fail if a binary of a build-context Conan package loads anything from the Nix
# store other than glibc, or cannot resolve a library at all.
#
# Only binaries linked by the Nix toolchain are checked, i.e. those recording a
# store path as their interpreter or RUNPATH. Prebuilt upstream binaries (such
# as the ones the cmake package ships) use the system loader instead.
#
# Build-context packages provide the tools that run during the build (protoc,
# grpc_cpp_plugin, ...). Their package ID does not change when a Nix toolchain
# update moves the GCC runtime to a new store path, so a cached binary has to
# get by with the pinned glibc alone. See docs/build/nix.md.
#
# Usage: bin/nix/check-build-context-runtime.sh <graph.json>
# <graph.json> is the output of `conan install --format=json`.
set -euo pipefail
if [ "$#" -ne 1 ]; then
echo "usage: $0 <graph.json>" >&2
exit 2
fi
if [ "$(uname -s)" != "Linux" ]; then
echo "$0: Linux only" >&2
exit 2
fi
folders="$(jq -r '.graph.nodes[] | select(.context == "build" and .package_folder) | .package_folder' "$1" | sort -u)"
checked=0
failed=0
while IFS= read -r file; do
case "$(file -b "${file}")" in
ELF*) ;;
*) continue ;;
esac
[[ "$(readelf -ldW "${file}")" == */nix/store/* ]] || continue
checked=$((checked + 1))
# `ldd` lists the interpreter and every library as the loader resolves them.
if deps="$(ldd "${file}" 2>&1)"; then
bad="$(printf '%s\n' "${deps}" |
grep -E 'not found|/nix/store/' |
grep -vE '/nix/store/[^/]+-glibc-[^/]+/' || true)"
else
case "${deps}" in
*"not a dynamic executable"*) continue ;;
esac
bad="${deps}"
fi
if [ -n "${bad}" ]; then
failed=$((failed + 1))
echo "::error file=${file}::loads a library from the Nix store other than glibc"
echo "${file}"
echo "${bad}" | sed 's/^/ /'
fi
done < <(
# shellcheck disable=SC2086 # one folder per line, no spaces in Conan paths
[ -z "${folders}" ] || find ${folders} -type f \( -perm -u+x -o -name '*.so*' \)
)
echo "Build-context packages: checked ${checked} binaries, ${failed} failed."
[ "${failed}" -eq 0 ]

View File

@@ -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

View File

@@ -23,7 +23,7 @@ apt-get clean
rm -rf /var/lib/apt/lists/*
EOF
ARG PRE_COMMIT_VERSION=4.6.0
ARG PRE_COMMIT_VERSION=4.6.2
RUN pip install --no-cache --break-system-packages \
pre-commit==${PRE_COMMIT_VERSION}

View File

@@ -26,8 +26,6 @@ import sys
import tempfile
from pathlib import Path
CLANG_TIDY_VERSION = 22
# Extensions run-clang-tidy can analyse: `.cpp` translation units and, thanks to
# the `verify_headers` build option, `.h`/`.hpp` headers (each has its own
# compile_commands.json entry). `.ipp` fragments have no entry and are skipped.
@@ -39,8 +37,21 @@ TIDY_EXTENSIONS = {".cpp", ".h", ".hpp"}
FILEPATH_RE = re.compile(r"^(\s*(?:-\s+)?FilePath:\s*)'((?:[^']|'')*)'\s*$")
def find_tool(name: str) -> str | None:
for candidate in (f"{name}-{CLANG_TIDY_VERSION}", name):
def clang_tidy_major() -> str | None:
"""Major version of the `clang-tidy` on PATH, which run-clang-tidy invokes."""
if not (clang_tidy := shutil.which("clang-tidy")):
return None
output = subprocess.run(
[clang_tidy, "--version"], capture_output=True, text=True
).stdout
m = re.search(r"LLVM version (\d+)", output)
return m.group(1) if m else None
def find_tool(name: str, version: str | None) -> str | None:
"""Prefer `<name>-<version>`, so a host tool of another version can't win."""
candidates = ([f"{name}-{version}"] if version else []) + [name]
for candidate in candidates:
if path := shutil.which(candidate):
return path
return None
@@ -103,8 +114,9 @@ def main():
if not files:
return 0
run_clang_tidy = find_tool("run-clang-tidy")
clang_apply_replacements = find_tool("clang-apply-replacements")
version = clang_tidy_major()
run_clang_tidy = find_tool("run-clang-tidy", version)
clang_apply_replacements = find_tool("clang-apply-replacements", version)
missing = [
name
for name, path in (
@@ -114,9 +126,10 @@ def main():
if not path
]
if missing:
tried = f" (tried the '-{version}' suffix too)" if version else ""
print(
f"clang-tidy check failed: TIDY is enabled but {' and '.join(missing)} "
f"was not found in PATH (tried the '-{CLANG_TIDY_VERSION}' suffix too).",
f"was not found in PATH{tried}.",
file=sys.stderr,
)
return 1

View File

@@ -9,13 +9,21 @@ include_guard(GLOBAL)
# --------------------------------------------------------------------
# Compiler detection (C++)
# --------------------------------------------------------------------
set(is_clang FALSE)
# is_any_clang is true for both LLVM Clang and AppleClang. Their version numbers
# and feature sets differ, so use is_llvm_clang or is_apple_clang when it matters.
set(is_any_clang FALSE)
set(is_llvm_clang FALSE)
set(is_apple_clang FALSE)
set(is_gcc FALSE)
set(is_msvc FALSE)
set(is_xcode FALSE)
if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") # Clang or AppleClang
set(is_clang TRUE)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(is_any_clang TRUE)
set(is_llvm_clang TRUE)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
set(is_any_clang TRUE)
set(is_apple_clang TRUE)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(is_gcc TRUE)
elseif(MSVC)

View File

@@ -17,7 +17,7 @@
(runtime libraries resolved through the rpath) are skipped too.
Everywhere else `patch_nix_binary` is a no-op.
The default loader is resolved by bin/default-loader-path.sh.
The default loader is resolved by bin/nix/default-loader-path.sh.
#]===================================================================]
include_guard(GLOBAL)
@@ -25,7 +25,7 @@ include_guard(GLOBAL)
include(CompilationEnv)
# Resolves the system default ELF loader path for the current architecture.
set(_loader_path_script "${CMAKE_SOURCE_DIR}/bin/default-loader-path.sh")
set(_loader_path_script "${CMAKE_SOURCE_DIR}/bin/nix/default-loader-path.sh")
if(
is_linux

View File

@@ -130,7 +130,7 @@ else()
INTERFACE
-Wall
-Wdeprecated
$<$<BOOL:${is_clang}>:-Wno-deprecated-declarations>
$<$<BOOL:${is_any_clang}>:-Wno-deprecated-declarations>
$<$<BOOL:${wextra}>:-Wextra
-Wno-unused-parameter>
$<$<BOOL:${werr}>:-Werror>
@@ -177,7 +177,7 @@ else()
if(
is_nix_compiler
AND is_linux
AND is_clang
AND is_any_clang
AND CMAKE_CXX_FLAGS MATCHES "stdlib=libstdc"
)
string(
@@ -204,7 +204,10 @@ if(voidstar)
"Antithesis instrumentation requires Linux, aborting..."
)
elseif(
NOT (is_clang AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0)
NOT (
is_llvm_clang
AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0
)
)
message(
FATAL_ERROR

View File

@@ -292,6 +292,20 @@ 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
account_currencies
account_nfts
account_offers
book_changes
ledger
transaction_entry
)
target_sources(xrpld PRIVATE ${rpcspec_instantiations})
if(tests)
file(
GLOB_RECURSE sources
@@ -322,6 +336,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()

View File

@@ -58,7 +58,7 @@ target_link_options(common INTERFACE ${sanitizers_linker_flags})
# This module appends -fsanitize-ignorelist=<path> for Clang builds.
# The ignorelist path contains CMAKE_SOURCE_DIR, so it must be set here, rather than in the Conan profile.
# GCC does not support -fsanitize-ignorelist.
if(is_clang)
if(is_any_clang)
set(ignorelist_path
"${CMAKE_SOURCE_DIR}/sanitizers/suppressions/sanitizer-ignorelist.txt"
)
@@ -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)

View File

@@ -23,11 +23,8 @@ if(NOT is_multiconfig)
endif()
endif()
if(is_clang) # both Clang and AppleClang
if(
"${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang"
AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.0
)
if(is_llvm_clang)
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.0)
message(FATAL_ERROR "This project requires clang 16 or later")
endif()
elseif(is_gcc)

View File

@@ -62,11 +62,11 @@ if(unity)
set(CMAKE_UNITY_BUILD ON CACHE BOOL "Do a unity build")
endif()
if(is_clang AND is_linux)
if(is_llvm_clang AND is_linux)
option(voidstar "Enable Antithesis instrumentation." OFF)
endif()
if(is_gcc OR is_clang)
if(is_gcc OR is_any_clang)
include(ProcessorCount)
ProcessorCount(PROCESSOR_COUNT)
@@ -125,7 +125,7 @@ else()
set(use_mold OFF CACHE BOOL "mold linker, linux only" FORCE)
endif()
if(is_clang)
if(is_any_clang)
option(use_lld "enables detection of lld linker" ON)
else()
set(use_lld OFF CACHE BOOL "try lld linker, clang only" FORCE)

View File

@@ -7,7 +7,7 @@
pcpp>=1.30
# Parser combinator library - used to parse the macro DSL
pyparsing>=3.0.0
pyparsing>=3.3.3
# Template engine - used to generate C++ code from templates
Mako>=1.2.2
Mako>=1.4.2

View File

@@ -1,8 +1,8 @@
# This file was autogenerated by uv via the following command:
# uv pip compile requirements.in --generate-hashes --output-file requirements.txt
mako==1.3.12 \
--hash=sha256:8f61569480282dbf557145ce441e4ba888be453c30989f879f0d652e39f53ea9 \
--hash=sha256:9f778e93289bd410bb35daadeb4fc66d95a746f0b75777b942088b7fd7af550a
mako==1.4.3 \
--hash=sha256:723296007c870bfd6b3f0c3230dba7198096e5269297ebf5e4eff9e7ffa39d4f \
--hash=sha256:cd6537fe88d5fec315c55c2f8529bc4ce7a9a352ad7db3eeaa6a66e2dd4ec37a
# via -r requirements.in
markupsafe==3.0.3 \
--hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \
@@ -99,7 +99,7 @@ pcpp==1.30 \
--hash=sha256:05fe08292b6da57f385001c891a87f40d6aa7f46787b03e8ba326d20a3297c6e \
--hash=sha256:5af9fbce55f136d7931ae915fae03c34030a3b36c496e72d9636cedc8e2543a1
# via -r requirements.in
pyparsing==3.3.2 \
--hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \
--hash=sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc
pyparsing==3.3.3 \
--hash=sha256:928ae7e20211f3b6f3915a72f06a0cfd29ab9d24279dd6346b6b1a7146397d36 \
--hash=sha256:ece8c00a69cf01b45d0b1dedabb469c90d8caf996d4fda40f147627a122849a4
# via -r requirements.in

View File

@@ -31,7 +31,7 @@ namespace xrpl::ledger_entries {
// builder's STObject and the wrapper's SLE.
TEST(${name}Tests, BuilderSettersRoundTrip)
{
constexpr UInt256 index{1};
UInt256 const index{1u};
% for field in fields:
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
@@ -85,7 +85,7 @@ TEST(${name}Tests, BuilderSettersRoundTrip)
// from that SLE, build a new wrapper, and verify all fields (and validate()).
TEST(${name}Tests, BuilderFromSleRoundTrip)
{
constexpr UInt256 index{2};
UInt256 const index{2u};
% for field in fields:
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
@@ -146,7 +146,7 @@ TEST(${name}Tests, BuilderFromSleRoundTrip)
// 3) Verify wrapper throws when constructed from wrong ledger entry type.
TEST(${name}Tests, WrapperThrowsOnWrongEntryType)
{
constexpr UInt256 index{3};
UInt256 const index{3u};
// Build a valid ledger entry of a different type
// Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq
@@ -177,7 +177,7 @@ TEST(${name}Tests, WrapperThrowsOnWrongEntryType)
// 4) Verify builder throws when constructed from wrong ledger entry type.
TEST(${name}Tests, BuilderThrowsOnWrongEntryType)
{
constexpr UInt256 index{4};
UInt256 const index{4u};
// Build a valid ledger entry of a different type
% if wrong_le_include == "Ticket":
@@ -207,7 +207,7 @@ TEST(${name}Tests, BuilderThrowsOnWrongEntryType)
// 5) Build with only required fields and verify optional fields return nullopt.
TEST(${name}Tests, OptionalFieldsReturnNullopt)
{
constexpr UInt256 index{3};
UInt256 const index{3u};
% for field in required_fields:
auto const ${field["paramName"]}Value = ${canonical_expr(field)};

View File

@@ -3,7 +3,7 @@
"requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1782392402.420688",
"xrpl-rpc-spec/0.1.19#870b2d3abcfbbf13b61c2d3c69495060%1790348286.187549",
"xrpl-rpc-spec/0.1.23#56e8946f5b3fb949a3732e1a51135ca2%1790977482.197086",
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1782392403.185447",
"soci/4.0.3#e726491a03468795453f7c83fc924a96%1782392402.679521",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1782307151.633168",
@@ -20,7 +20,7 @@
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1782392403.066892",
"jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1782392402.791979",
"grpc/1.81.1#b87796a4269034856cbc1a2522db16eb%1788275071.530512",
"grpc/1.81.1#aaa93ab6cda2f2baa6a84490582c8adf%1791284951.826256",
"fast_float/8.2.10#f6f28d6bb22112078e7dbda611caf681%1785888854.601666",
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1782307148.15562",
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1782392402.538492",
@@ -34,11 +34,15 @@
"build_requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
"strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1782395692.540639",
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1782392402.431897",
"protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1782392410.233933",
"openssl/3.6.3#f806de8933e3bf6f01016c6a888cee2e%1783945160.863288",
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1782395690.33162",
"msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649",
"m4/1.4.19#1727f439cf74e83826ec96d0b4904eee%1784541921.659",
"grpc/1.81.1#aaa93ab6cda2f2baa6a84490582c8adf%1791284951.826256",
"cmake/4.3.3#840cf00ea09777e05c2050a50a82c722%1782392418.696091",
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1782392402.681654",
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1782392402.624226",
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86",

View File

@@ -3,5 +3,13 @@
core:non_interactive=True
core.download:parallel={{ os.cpu_count() }}
core.upload:parallel={{ os.cpu_count() }}
tools.files.download:retry=5
# Fall back to Conan Center's source backups when a recipe's upstream URL is down
# (e.g. the GNU FTP mirrors), see
# https://github.com/conan-io/conan-center-index/issues/28147#issuecomment-3183544772
# The backups are only tried once every upstream URL has used up its retries,
# so keep the retries low.
core.sources:download_urls=["origin", "https://c3i.jfrog.io/artifactory/conan-center-backup-sources"]
tools.files.download:retry=1
tools.files.download:retry_wait=10
# Fail fast on unreachable hosts (default connect timeout is 30s), keep the 60s read timeout.
core.net.http:timeout=(5, 60)

View File

@@ -29,9 +29,9 @@ os.version={{ min_macos_version }}
[conf]
{# The Boost recipe builds with b2, which doesn't use Conan's toolchain files. #}
{# Instead it hand-rolls the compiler for user-config.jam, #}
{# and its fallback probes a version-suffixed binary (e.g. `g++-15`) before plain `g++`. #}
{# Inside the Nix shell the wrapper only provides `g++`/`gcc` (no `-15` suffix), #}
{# so on a host that also has a system `g++-15` the probe escapes Nix #}
{# and its fallback probes a version-suffixed binary (e.g. `g++-<major>`) before plain `g++`. #}
{# Inside the Nix shell the wrapper only provides `g++`/`gcc` (no `-<major>` suffix), #}
{# so on a host that also has a system `g++-<major>` the probe escapes Nix #}
{# and picks the system compiler, which is mismatched with the Nix libraries #}
{# and breaks the build (e.g. Boost.Stacktrace link checks fail). #}
{# Pinning the executables here short-circuits that probe so Boost (and the rest of the toolchain) #}
@@ -49,8 +49,31 @@ tools.build:compiler_executables={'c':'{{ cc_exe }}','cpp':'{{ cxx_exe }}'}
user.package:cppstd_version=23
tools.info.package_id:confs+=["user.package:cppstd_version"]
{% if os == "Macos" %}
{% if os == "Linux" and context == "build" %}
{# Build-context executables (protoc, grpc_cpp_plugin, build tools) run during the build #}
{# and would otherwise load libstdc++/libgcc from a Nix store path #}
{# that might change with a Nix toolchain update. #}
{# --as-needed drops the ones they link but don't use, #}
{# such as libatomic for grpc_cpp_plugin on arm64. #}
{% set static_runtime_flags = ["-static-libstdc++", "-static-libgcc", "-Wl,--as-needed"] %}
tools.build:exelinkflags+={{ static_runtime_flags }}
tools.info.package_id:confs+=["tools.build:exelinkflags"]
{% endif %}
{% if os == "Linux" and context == "build" %}
{# b2 links itself with its own script, which ignores exelinkflags #}
{# and only takes CXXFLAGS when use_cxx_env is set (see [buildenv] below). #}
[options]
b2/*:use_cxx_env=True
{% endif %}
[buildenv]
{# gRPC emits thousands of compiler warnings that we cannot act on. #}
{# CMake picks up CXXFLAGS, and unlike tools.build:cxxflags, #}
{# this is not part of the package ID, so binaries stay shareable. #}
grpc/*:CXXFLAGS=-w
{% if os == "Macos" %}
{# os.version adds -mmacosx-version-min to compiler command lines, #}
{# but Boost.Context's b2 assembly (.S) rule ignores it, #}
{# so those objects keep the host SDK version and still warn at link time. #}
@@ -58,3 +81,7 @@ tools.info.package_id:confs+=["user.package:cppstd_version"]
{# Scoped to boost/* since it is the only gap. #}
boost/*:MACOSX_DEPLOYMENT_TARGET={{ min_macos_version }}
{% endif %}
{% if os == "Linux" and context == "build" %}
b2/*:CXXFLAGS={{ static_runtime_flags | join(" ") }}
{% endif %}

View File

@@ -5,6 +5,9 @@ include(default)
{% if not sanitizers %}
{# Sanitizers not configured; no additional settings needed #}
{% elif context == "build" %}
{# Build-context packages are tools we run, not code we test, #}
{# so don't instrument them #}
{% else %}
{% if compiler == "msvc" %}

View File

@@ -40,7 +40,7 @@ class Xrpl(ConanFile):
"nudb/2.0.9",
"openssl/3.6.3",
"soci/4.0.3",
"xrpl-rpc-spec/0.1.19",
"xrpl-rpc-spec/0.1.23",
"zlib/1.3.2",
]
@@ -49,7 +49,8 @@ class Xrpl(ConanFile):
]
tool_requires = [
"protobuf/6.33.5",
"grpc/<host_version>",
"protobuf/<host_version>",
]
default_options = {
@@ -123,6 +124,10 @@ class Xrpl(ConanFile):
"xxhash/*:shared": False,
}
# default_options only reach the host context;
# give tool_requires (and their dependencies) the same dependency options.
default_build_options = {k: v for k, v in default_options.items() if "/" in k}
def set_version(self):
self.version = self.version or DEV_VERSION
@@ -149,7 +154,7 @@ class Xrpl(ConanFile):
self.requires("xxhash/0.8.3", transitive_headers=True)
exports_sources = (
"bin/default-loader-path.sh",
"bin/nix/default-loader-path.sh",
"CMakeLists.txt",
"cfg/*",
"cmake/*",

View File

@@ -80,13 +80,12 @@ function(add_xrpl_crate name)
# `cc` picks its runtime flag from `crt-static` alone, so it compiles a
# crate's C++ with `-MT`; Debug needs `-MTd` (to match cmake/XrplCompiler.cmake).
if(is_msvc)
corrosion_set_env_vars(
${ARG_CRATE}
"$<$<CONFIG:Debug>:CXXFLAGS=-MTd>"
)
corrosion_set_env_vars(${ARG_CRATE} "$<$<CONFIG:Debug>:CXXFLAGS=-MTd>")
endif()
corrosion_add_cxxbridge(${name}_cxxbridge CRATE ${ARG_CRATE} FILES
${ARG_FILES}
corrosion_add_cxxbridge(
${name}_cxxbridge
CRATE ${ARG_CRATE}
FILES ${ARG_FILES}
)
# Generated cxxbridge headers don't exist at configure time; CMake 3.28+
# validates INTERFACE_SOURCES on consuming targets. Clear it to skip the

20
crates/Cargo.lock generated
View File

@@ -57,9 +57,9 @@ dependencies = [
[[package]]
name = "cxx"
version = "1.0.202"
version = "1.0.205"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13f6de320895f42e6e081abb5c7983bedcf0b6d0ff9323de0d33f620c8ac1199"
checksum = "c8a02bfeba9074b2bbb39492c9b0908ec854f455a3cccdcde7a1de55e2315e40"
dependencies = [
"cc",
"cxx-build",
@@ -72,9 +72,9 @@ dependencies = [
[[package]]
name = "cxx-build"
version = "1.0.202"
version = "1.0.205"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fde53ca86b9704a943fef0f1e1d836239a6aedca5de3c65fa9f97ec0bd46d39"
checksum = "bbc2ac76997619d2d888b58ad79488f1c835de41ca4dd3dd7082acf4ecb0a50f"
dependencies = [
"cc",
"codespan-reporting",
@@ -87,9 +87,9 @@ dependencies = [
[[package]]
name = "cxxbridge-cmd"
version = "1.0.202"
version = "1.0.205"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bae89236c811fd4d08ed3441759ac4ac98d752cbfd3de341315ba16ad20ec3"
checksum = "54a486ed16a3f2a053fe7f5b98db4c1a59846a23e8cb9e49f9d1bd48830d51a2"
dependencies = [
"clap",
"codespan-reporting",
@@ -101,15 +101,15 @@ dependencies = [
[[package]]
name = "cxxbridge-flags"
version = "1.0.202"
version = "1.0.205"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49045042e5fced01b80742aba5508de82aa4f13677ed3fa2b4cda709c40c5918"
checksum = "c72569be94e2283764d334cd285980b747dbe221e27e315484eb6bcd93fa808a"
[[package]]
name = "cxxbridge-macro"
version = "1.0.202"
version = "1.0.205"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b181252e2e3d3b5d183afbdc033a3958b445eb3e1a0ae65fc3d4f5259f5da6fd"
checksum = "423e795284a7f7b3f843c956a5322c4bc497b60c2dd1a6339da10313cb18d575"
dependencies = [
"indexmap",
"proc-macro2",

View File

@@ -10,14 +10,15 @@ This document explains how to set one up.
support it — see [compiler support for C++23][cpp23-support].
The versions currently tested in CI are:
| Compiler | Version |
| ----------- | ------------------ |
| GCC | 15.2 |
| Clang | 22 |
| Apple Clang | 21 |
| MSVC | Visual Studio 2026 |
| Compiler | Version |
| ----------- | ------------------------------- |
| GCC | `gccVersion` in [packages.nix] |
| Clang | `llvmVersion` in [packages.nix] |
| Apple Clang | 21 |
| MSVC | Visual Studio 2026 |
LLVM tools (`clang-tidy` and `clang-format`) are also pinned to version 22.
LLVM tools (`clang-tidy` and `clang-format`)
come from the same LLVM release as Clang.
### Older compilers
@@ -156,3 +157,4 @@ version out of the box — run it via `run-clang-tidy`. No separate installation
is needed.
[cpp23-support]: https://en.cppreference.com/w/cpp/compiler_support/23
[packages.nix]: ../../nix/packages.nix

30
docs/build/nix.md vendored
View File

@@ -183,24 +183,36 @@ at link or run time.
> configuration CI covers, and no dependency binaries are published for it.
This is checked rather than assumed.
[`bin/check-nix-store-refs.sh`](../../bin/check-nix-store-refs.sh) takes one file
or directory and fails if a binary under it resolves a store path at run time.
[`bin/nix/check-nix-store-refs.sh`](../../bin/nix/check-nix-store-refs.sh) takes one
file or directory and fails if a binary under it resolves a store path at run time.
CI runs it over the build output and the Conan cache, and again in the upload job
before anything is published. You can run it yourself:
```bash
bin/check-nix-store-refs.sh build
bin/check-nix-store-refs.sh ~/.conan2-nix
bin/nix/check-nix-store-refs.sh build
bin/nix/check-nix-store-refs.sh ~/.conan2-nix
```
It works on Linux too, but asserts something narrower there: the toolchain always
writes the store into `PT_INTERP` and `RUNPATH`, and CI builds inside an image
whose store is fixed for its lifetime, so that is fine. Only the binaries
[`PatchNixBinary.cmake`](../../cmake/PatchNixBinary.cmake) retargets to the
system loader have to be clean, and those are what CI checks:
writes the store into `PT_INTERP` and `RUNPATH`. That is fine for the pinned
glibc, whose path does not move, but not for the GCC runtime, which moves with
every GCC update. So [`conan/profiles/default`](../../conan/profiles/default)
links build-context packages, whose executables run during the build, with
`-static-libstdc++ -static-libgcc -Wl,--as-needed`, and
[`conan/profiles/sanitizers`](../../conan/profiles/sanitizers) does not
instrument them. CI checks that they load nothing from the store but glibc, from
the graph `conan install --format=json` writes:
```bash
bin/check-nix-store-refs.sh build/xrpld
bin/nix/check-build-context-runtime.sh graph.json
```
Only the binaries [`PatchNixBinary.cmake`](../../cmake/PatchNixBinary.cmake)
retargets to the system loader have to be fully clean, and those are what CI
checks:
```bash
bin/nix/check-nix-store-refs.sh build/xrpld
```
### The libresolv stub

View File

@@ -178,11 +178,11 @@ A binary stops starting after a `nix flake update`, or after
dyld[57271]: Library not loaded: /nix/store/…-libresolv-93/lib/libresolv.9.dylib
```
[`bin/check-nix-store-refs.sh`](../../bin/check-nix-store-refs.sh) finds the same
thing without having to run anything, and names the file:
[`bin/nix/check-nix-store-refs.sh`](../../bin/nix/check-nix-store-refs.sh) finds the
same thing without having to run anything, and names the file:
```
$ bin/check-nix-store-refs.sh ~/.conan2-nix
$ bin/nix/check-nix-store-refs.sh ~/.conan2-nix
::error file=/Users/you/.conan2-nix/p/b/c-area24ded30c388c/p/bin/adig::references the Nix store at run time
/Users/you/.conan2-nix/p/b/c-area24ded30c388c/p/bin/adig
/nix/store/p4lp3xq4imd1qzqh08x8vcq2zfhi7rca-libresolv-93/lib/libresolv.9.dylib

View File

@@ -6,7 +6,7 @@ project(antithesis-sdk-cpp VERSION 0.4.4 LANGUAGES CXX)
add_library(antithesis-sdk-cpp INTERFACE antithesis_sdk.h)
# Note, both sections below created by xrpld project
target_include_directories(antithesis-sdk-cpp INTERFACE
target_include_directories(antithesis-sdk-cpp SYSTEM INTERFACE
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
)

View File

@@ -34,7 +34,7 @@ lz4Compress(void const* in, std::size_t inSize, BufferFactory&& bf)
// data
auto compressed = bf(outCapacity);
auto compressedSize = LZ4_compress_default(
auto const compressedSize = LZ4_compress_default(
reinterpret_cast<char const*>(in),
reinterpret_cast<char*>(compressed),
inSize,

View File

@@ -1,31 +1,29 @@
#pragma once
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/beast/type_name.h>
#include <boost/core/type_name.hpp>
#include <algorithm>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace xrpl {
/**
* Manages all counted object types.
*
* Counters register themselves on a lock-free intrusive list maintained
* by this object when constructed. Because counters are never destroyed
* or removed, the ABA problem does not apply.
*
* The registry is iterable as a forward range.
*/
class CountedObjects
{
public:
static CountedObjects&
getInstance() noexcept;
using Entry = std::pair<std::string, int>;
using List = std::vector<Entry>;
[[nodiscard]] List
getCounts(int minimumThreshold) const;
public:
/**
* Implementation for @ref CountedObject.
@@ -35,147 +33,68 @@ public:
class Counter
{
public:
Counter(std::string name) noexcept;
Counter(std::string name) noexcept : name_(std::move(name)), count_(0)
{
// Insert ourselves at the front of the lock-free linked list
CountedObjects& instance = CountedObjects::getInstance();
Counter* head = nullptr;
// Counters are intrusive list nodes whose addresses are published
// in the registry; they must never be copied or moved. The atomic
// members already force this, but we make it explicit.
Counter(Counter const&) = delete;
Counter&
operator=(Counter const&) = delete;
Counter(Counter&&) = delete;
Counter&
operator=(Counter&&) = delete;
do
{
head = instance.head_.load();
next_ = head;
} while (instance.head_.exchange(this) != head);
std::uint32_t
++instance.count_;
}
~Counter() noexcept = default;
int
increment() noexcept
{
auto const newCount = count_.fetch_add(1, std::memory_order::relaxed) + 1;
XRPL_ASSERT(newCount != 0, "xrpl::CountedObjects::Counter::increment : no overflow");
auto maxCount = maxCount_.load(std::memory_order::relaxed);
while (newCount > maxCount &&
!maxCount_.compare_exchange_weak(maxCount, newCount, std::memory_order::relaxed))
{
}
return newCount;
return ++count_;
}
std::uint32_t
int
decrement() noexcept
{
auto const prev = count_.fetch_sub(1, std::memory_order::relaxed);
XRPL_ASSERT(prev != 0, "xrpl::CountedObjects::Counter::decrement : no underflow");
return prev - 1;
return --count_;
}
[[nodiscard]] std::uint32_t
count() const noexcept
[[nodiscard]] int
getCount() const noexcept
{
return count_.load(std::memory_order::relaxed);
return count_.load();
}
[[nodiscard]] std::uint32_t
max() const noexcept
[[nodiscard]] Counter*
getNext() const noexcept
{
return std::max(
count_.load(std::memory_order::relaxed),
maxCount_.load(std::memory_order::relaxed));
return next_;
}
[[nodiscard]] std::string const&
name() const noexcept
getName() const noexcept
{
return name_;
}
private:
friend class CountedObjects;
Counter* next_ = nullptr;
std::atomic<std::uint32_t> count_ = 0;
std::atomic<std::uint32_t> maxCount_ = 0;
std::string const name_;
std::atomic<int> count_;
Counter* next_;
};
class Iterator
{
public:
using value_type = Counter const;
using reference = value_type&;
using pointer = value_type*;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
explicit Iterator(Counter* c = nullptr) noexcept : current_(c)
{
}
reference
operator*() const noexcept
{
return *current_;
}
pointer
operator->() const noexcept
{
return current_;
}
Iterator&
operator++() noexcept
{
current_ = current_->next_;
return *this;
}
Iterator
operator++(int) noexcept
{
auto tmp = *this;
++*this;
return tmp;
}
bool
operator==(Iterator const&) const noexcept = default;
private:
Counter* current_;
};
constexpr CountedObjects() noexcept = default;
[[nodiscard]] auto
begin() const noexcept
{
return Iterator{head_.load(std::memory_order::acquire)};
}
[[nodiscard]] auto
end() const noexcept
{
return Iterator{};
}
private:
std::atomic<Counter*> head_ = nullptr;
CountedObjects() noexcept;
~CountedObjects() noexcept = default;
private:
std::atomic<int> count_;
std::atomic<Counter*> head_;
};
/** The global counted object registry. */
inline constinit CountedObjects gCountedObjects;
inline CountedObjects::Counter::Counter(std::string name) noexcept
: next_(gCountedObjects.head_.load(std::memory_order::relaxed)), name_(std::move(name))
{
while (!gCountedObjects.head_.compare_exchange_weak(
next_, this, std::memory_order::release, std::memory_order::relaxed))
;
}
//------------------------------------------------------------------------------
/**
@@ -184,37 +103,27 @@ inline CountedObjects::Counter::Counter(std::string name) noexcept
* Derived classes have their instances counted automatically. This is used
* for reporting purposes.
*
* The constructors are private and `Object` is befriended so that the
* CRTP parameter must be the deriving class itself: a copy-paste error
* like `class B : public CountedObject<A>` fails to compile instead of
* silently polluting A's count.
*
* @note This class has no move operations by design: a derived class's
* move constructor falls back to the copy constructor for this
* base, so the newly created instance is counted. This keeps the
* invariant that count is the number of outstanding subobjects.
*
* @warning Counted objects constructed during dynamic initialization of
* other translation units may have their increments discarded when
* counter itself is dynamically initialized. Do not create counted
* objects before main() begins.
*
* @ingroup basics
*/
template <class Object>
requires std::is_class_v<Object>
class CountedObject
{
static inline CountedObjects::Counter counter{boost::core::type_name<Object>()};
private:
static auto&
getCounter() noexcept
{
static CountedObjects::Counter kC{beast::typeName<Object>()};
return kC;
}
CountedObject() noexcept
{
counter.increment();
getCounter().increment();
}
CountedObject(CountedObject const&) noexcept
{
counter.increment();
getCounter().increment();
}
CountedObject&
@@ -223,7 +132,7 @@ class CountedObject
public:
~CountedObject() noexcept
{
counter.decrement();
getCounter().decrement();
}
friend Object;

View File

@@ -20,12 +20,14 @@ SharedIntrusive<T>::SharedIntrusive(T* p, TAdoptTag) noexcept : ptr_{p}
template <class T>
SharedIntrusive<T>::SharedIntrusive(SharedIntrusive const& rhs)
: ptr_{[&] {
auto p = rhs.unsafeGetRawPtr();
if (p)
p->addStrongRef();
return p;
}()}
: ptr_{
[&] {
auto p = rhs.unsafeGetRawPtr();
if (p)
p->addStrongRef();
return p;
}(),
}
{
}
@@ -33,12 +35,14 @@ template <class T>
template <class TT>
requires std::convertible_to<TT*, T*>
SharedIntrusive<T>::SharedIntrusive(SharedIntrusive<TT> const& rhs)
: ptr_{[&] {
auto p = rhs.unsafeGetRawPtr();
if (p)
p->addStrongRef();
return p;
}()}
: ptr_{
[&] {
auto p = rhs.unsafeGetRawPtr();
if (p)
p->addStrongRef();
return p;
}(),
}
{
}
@@ -140,12 +144,14 @@ SharedIntrusive<T>::~SharedIntrusive()
template <class T>
template <class TT>
SharedIntrusive<T>::SharedIntrusive(StaticCastTagSharedIntrusive, SharedIntrusive<TT> const& rhs)
: ptr_{[&] {
auto p = static_cast<T*>(rhs.unsafeGetRawPtr());
if (p)
p->addStrongRef();
return p;
}()}
: ptr_{
[&] {
auto p = static_cast<T*>(rhs.unsafeGetRawPtr());
if (p)
p->addStrongRef();
return p;
}(),
}
{
}
@@ -159,12 +165,14 @@ SharedIntrusive<T>::SharedIntrusive(StaticCastTagSharedIntrusive, SharedIntrusiv
template <class T>
template <class TT>
SharedIntrusive<T>::SharedIntrusive(DynamicCastTagSharedIntrusive, SharedIntrusive<TT> const& rhs)
: ptr_{[&] {
auto p = dynamic_cast<T*>(rhs.unsafeGetRawPtr());
if (p)
p->addStrongRef();
return p;
}()}
: ptr_{
[&] {
auto p = dynamic_cast<T*>(rhs.unsafeGetRawPtr());
if (p)
p->addStrongRef();
return p;
}(),
}
{
}
@@ -260,7 +268,7 @@ SharedIntrusive<T>::unsafeReleaseAndStore(T* next)
return;
using enum ReleaseStrongRefAction;
auto action = prev->releaseStrongRef();
auto const action = prev->releaseStrongRef();
switch (action)
{
case NoOp:
@@ -361,7 +369,7 @@ WeakIntrusive<T>::unsafeReleaseNoStore()
return;
using enum ReleaseWeakRefAction;
auto action = ptr_->releaseWeakRef();
auto const action = ptr_->releaseWeakRef();
switch (action)
{
case NoOp:
@@ -580,7 +588,7 @@ SharedWeakUnion<T>::convertToStrong()
auto p = unsafeGetRawPtr();
if (p && p->checkoutStrongRefFromWeak())
{
[[maybe_unused]] auto action = p->releaseWeakRef();
[[maybe_unused]] auto const action = p->releaseWeakRef();
XRPL_ASSERT(
(action == ReleaseWeakRefAction::NoOp),
"xrpl::SharedWeakUnion::convertToStrong : "
@@ -603,7 +611,7 @@ SharedWeakUnion<T>::convertToWeak()
return false;
using enum ReleaseStrongRefAction;
auto action = p->addWeakReleaseStrongRef();
auto const action = p->addWeakReleaseStrongRef();
switch (action)
{
case NoOp:
@@ -670,7 +678,7 @@ SharedWeakUnion<T>::unsafeReleaseNoStore()
if (isStrong())
{
using enum ReleaseStrongRefAction;
auto strongAction = p->releaseStrongRef();
auto const strongAction = p->releaseStrongRef();
switch (strongAction)
{
case NoOp:
@@ -688,7 +696,7 @@ SharedWeakUnion<T>::unsafeReleaseNoStore()
else
{
using enum ReleaseWeakRefAction;
auto weakAction = p->releaseWeakRef();
auto const weakAction = p->releaseWeakRef();
switch (weakAction)
{
case NoOp:

View File

@@ -406,10 +406,10 @@ IntrusiveRefCounts::useCount() const noexcept
inline IntrusiveRefCounts::~IntrusiveRefCounts() noexcept
{
#ifndef NDEBUG
auto v = refCounts_.load(std::memory_order_acquire);
auto const v = refCounts_.load(std::memory_order_acquire);
XRPL_ASSERT(
(!(v & kValueMask)), "xrpl::IntrusiveRefCounts::~IntrusiveRefCounts : count must be zero");
auto t = v & kTagMask;
auto const t = v & kTagMask;
XRPL_ASSERT((!t || t == kTagMask), "xrpl::IntrusiveRefCounts::~IntrusiveRefCounts : valid tag");
#endif
}

View File

@@ -3,9 +3,156 @@
#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;
}
/**
* Multiply two signed 64-bit integers, returning std::nullopt when the exact
* mathematical product is not representable in std::int64_t.
*/
[[nodiscard]] constexpr std::optional<std::int64_t>
checkedMul(std::int64_t a, std::int64_t b) noexcept
{
using L = std::numeric_limits<std::int64_t>;
if (a == 0 || b == 0)
return 0;
if (a > 0)
{
if (b > 0 ? a > L::max() / b : b < L::min() / a)
return std::nullopt;
}
else if (b > 0 ? a < L::min() / b : b < L::max() / a)
{
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());
static_assert(checkedMul(0, std::numeric_limits<std::int64_t>::min()) == 0);
static_assert(checkedMul(std::numeric_limits<std::int64_t>::min(), 0) == 0);
static_assert(checkedMul(-1, -1) == 1);
// a > 0, b > 0
static_assert(
checkedMul(std::numeric_limits<std::int64_t>::max(), 1) ==
std::numeric_limits<std::int64_t>::max());
static_assert(
checkedMul(std::numeric_limits<std::int64_t>::max() / 2, 2) ==
std::numeric_limits<std::int64_t>::max() - 1);
static_assert(!checkedMul((std::numeric_limits<std::int64_t>::max() / 2) + 1, 2).has_value());
static_assert(!checkedMul(2, (std::numeric_limits<std::int64_t>::max() / 2) + 1).has_value());
// a > 0, b < 0
static_assert(
checkedMul(std::numeric_limits<std::int64_t>::max(), -1) ==
-std::numeric_limits<std::int64_t>::max());
static_assert(
checkedMul((std::numeric_limits<std::int64_t>::max() / 2) + 1, -2) ==
std::numeric_limits<std::int64_t>::min());
static_assert(!checkedMul((std::numeric_limits<std::int64_t>::max() / 2) + 2, -2).has_value());
static_assert(
checkedMul(1, std::numeric_limits<std::int64_t>::min()) ==
std::numeric_limits<std::int64_t>::min());
static_assert(
checkedMul(2, std::numeric_limits<std::int64_t>::min() / 2) ==
std::numeric_limits<std::int64_t>::min());
static_assert(!checkedMul(2, (std::numeric_limits<std::int64_t>::min() / 2) - 1).has_value());
// a < 0, b > 0
static_assert(
checkedMul(std::numeric_limits<std::int64_t>::min(), 1) ==
std::numeric_limits<std::int64_t>::min());
static_assert(
checkedMul(std::numeric_limits<std::int64_t>::min() / 2, 2) ==
std::numeric_limits<std::int64_t>::min());
static_assert(!checkedMul((std::numeric_limits<std::int64_t>::min() / 2) - 1, 2).has_value());
static_assert(!checkedMul(std::numeric_limits<std::int64_t>::min(), 2).has_value());
// a < 0, b < 0
static_assert(
checkedMul(-(std::numeric_limits<std::int64_t>::max() / 2), -2) ==
std::numeric_limits<std::int64_t>::max() - 1);
static_assert(!checkedMul(std::numeric_limits<std::int64_t>::min() / 2, -2).has_value());
static_assert(
checkedMul(-1, -std::numeric_limits<std::int64_t>::max()) ==
std::numeric_limits<std::int64_t>::max());
static_assert(!checkedMul(std::numeric_limits<std::int64_t>::min(), -1).has_value());
static_assert(!checkedMul(-1, std::numeric_limits<std::int64_t>::min()).has_value());
/**
* Calculate one number divided by another number in percentage.
* The result is rounded up to the next integer, and capped in the range [0,100]

File diff suppressed because it is too large Load Diff

View File

@@ -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)

View File

@@ -153,6 +153,24 @@ private:
Policy policy,
Callback&& replaceCallback = nullptr);
/**
* canonicalizeImpl variant for callers that already hold `mutex_`.
*
* The unnamed `scoped_lock<MutexType> const&` parameter is proof of
* ownership; this overload does not acquire the lock. Use it from other
* methods (e.g. fetchAndModify) that need to compose canonicalize with
* additional work under a single critical section, without relying on
* recursive-mutex re-entry.
*/
template <class Policy, class Callback = std::nullptr_t>
bool
canonicalizeImpl(
std::scoped_lock<MutexType> const&,
key_type const& key,
CanonicalizeClientPointerType<Policy> data,
Policy policy,
Callback&& replaceCallback = nullptr);
public:
/**
* Replace aliased objects with originals.
@@ -239,9 +257,6 @@ public:
bool
retrieve(key_type const& key, T& data);
MutexType&
peekMutex();
std::vector<key_type>
getKeys() const;
@@ -263,6 +278,22 @@ public:
fetch(key_type const& digest, Handler const& h);
// End CachedSLEs functions.
/**
* Fetch or create an entry and execute a callback while holding the lock.
*
* The entry for the given key is fetched from the cache or created if it
* doesn't exist. The callback is then invoked with a reference to the
* entry while the cache mutex is still held, allowing safe modification
* of the cached object.
*
* @param key The key to fetch or create
* @param callback Function to call with the entry under lock.
* Signature: void(T&)
*/
template <class Callback>
void
fetchAndModify(key_type const& key, Callback&& callback);
private:
SharedPointerType
initialFetch(key_type const& key, std::scoped_lock<MutexType> const& l);

View File

@@ -4,6 +4,7 @@
#include <xrpl/basics/Log.h> // IWYU pragma: keep
#include <xrpl/basics/TaggedCache.h>
#include <xrpl/basics/scope.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <algorithm>
@@ -300,7 +301,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
// Remove from cache, if !valid, remove from map too. Returns true if removed from cache
std::scoped_lock const lock(mutex_);
auto cit = cache_.find(key);
auto const cit = cache_.find(key);
if (cit == cache_.end())
return false;
@@ -335,6 +336,29 @@ template <class Policy, class Callback>
inline bool
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
canonicalizeImpl(
key_type const& key,
CanonicalizeClientPointerType<Policy> data,
Policy policy,
Callback&& replaceCallback)
{
std::scoped_lock<MutexType> const lock(mutex_);
return canonicalizeImpl(lock, key, data, policy, std::forward<Callback>(replaceCallback));
}
template <
class Key,
class T,
bool IsKeyCache,
class SharedWeakUnionPointer,
class SharedPointerType,
class Hash,
class KeyEqual,
class Mutex>
template <class Policy, class Callback>
inline bool
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
canonicalizeImpl(
std::scoped_lock<MutexType> const&,
key_type const& key,
CanonicalizeClientPointerType<Policy> data,
[[maybe_unused]] Policy policy,
@@ -354,9 +378,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
// passing a const argument is a compile error.
constexpr bool replaceCached = std::is_same_v<Policy, detail::ReplaceCached>;
std::scoped_lock const lock(mutex_);
auto cit = cache_.find(key);
auto const cit = cache_.find(key);
if (cit == cache_.end())
{
@@ -371,7 +393,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
Entry& entry = cit->second;
entry.touch(clock_.now());
auto shouldReplaceCached = [&] {
auto const shouldReplaceCached = [&] {
if constexpr (replaceCached)
{
return true;
@@ -400,7 +422,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
return true;
}
auto cachedData = entry.lock();
auto const cachedData = entry.lock();
if (cachedData)
{
@@ -563,7 +585,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
retrieve(key_type const& key, T& data)
{
// retrieve the value of the stored data
auto entry = fetch(key);
auto const entry = fetch(key);
if (!entry)
return false;
@@ -572,22 +594,6 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
return true;
}
template <
class Key,
class T,
bool IsKeyCache,
class SharedWeakUnionPointer,
class SharedPointerType,
class Hash,
class KeyEqual,
class Mutex>
inline auto
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
peekMutex() -> MutexType&
{
return mutex_;
}
template <
class Key,
class T,
@@ -687,7 +693,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
return ret;
}
auto sle = h();
auto const sle = h();
if (!sle)
return {};
@@ -700,6 +706,49 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
}
// End CachedSLEs functions.
template <
class Key,
class T,
bool IsKeyCache,
class SharedWeakUnionPointer,
class SharedPointerType,
class Hash,
class KeyEqual,
class Mutex>
template <class Callback>
inline void
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
fetchAndModify(key_type const& key, Callback&& callback)
{
static_assert(
!IsKeyCache, "fetchAndModify is only supported for value caches, not key-only caches");
static_assert(
std::is_same_v<std::shared_ptr<T>, SharedPointerType> ||
std::is_same_v<intr_ptr::SharedPtr<T>, SharedPointerType>,
"fetchAndModify requires std::shared_ptr<T> or intr_ptr::SharedPtr<T>");
std::scoped_lock<MutexType> const lock(mutex_);
// Same pointer-type dispatch as insert(): the placeholder must have exactly
// SharedPointerType so canonicalizeImpl can write the cached pointer back.
SharedPointerType entry = [] {
if constexpr (std::is_same_v<std::shared_ptr<T>, SharedPointerType>)
{
return std::make_shared<T>();
}
else
{
return intr_ptr::makeShared<T>();
}
}();
canonicalizeImpl(lock, key, entry, detail::ReplaceClient{});
ALWAYS(
entry != nullptr, "xrpl::TaggedCache::fetchAndModify : entry present after canonicalize");
callback(*entry);
}
template <
class Key,
class T,
@@ -713,7 +762,7 @@ inline SharedPointerType
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
initialFetch(key_type const& key, std::scoped_lock<MutexType> const& l)
{
auto cit = cache_.find(key);
auto const cit = cache_.find(key);
if (cit == cache_.end())
return {};
@@ -783,7 +832,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
std::atomic<int>& allRemovals,
std::scoped_lock<std::recursive_mutex> const&)
{
return std::thread([&, this]() {
return std::thread([&, this] {
int cacheRemovals = 0;
int mapRemovals = 0;
@@ -863,7 +912,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
std::atomic<int>& allRemovals,
std::scoped_lock<std::recursive_mutex> const&)
{
return std::thread([&, this]() {
return std::thread([&, this] {
// NOLINTBEGIN https://github.com/XRPLF/rippled/issues/7056
int cacheRemovals = 0;
int mapRemovals = 0;

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,7 @@
#pragma once
#include <xrpl/basics/sanitizers.h>
#include <boost/core/type_name.hpp>
#include <xrpl/beast/type_name.h>
#include <exception>
#include <string>
@@ -56,7 +55,7 @@ Throw(Args&&... args)
std::is_convertible_v<E*, std::exception*>, "Exception must derive from std::exception.");
E e(std::forward<Args>(args)...);
logThrow("Throwing exception of type " + boost::core::type_name<E>() + ": " + e.what());
logThrow(std::string("Throwing exception of type " + beast::typeName<E>() + ": ") + e.what());
throw std::move(e);
}

View File

@@ -1,200 +0,0 @@
#pragma once
#include <concepts>
#include <type_traits>
#include <utility>
namespace xrpl {
/**
* Opt-in bitwise operators for scoped enumerations.
*
* Provides `&`, `|`, `^`, `~` and the corresponding compound assignment forms
* for any scoped enumeration with an unsigned underlying type that opts in by
* specializing @ref OptIn:
*
* @code
* namespace xrpl {
*
* enum class MyFlags : std::uint32_t { a = 1, b = 2, c = 4 };
*
* template <>
* struct enum_bitops::OptIn<MyFlags> : std::true_type {};
*
* } // namespace xrpl
* @endcode
*
* The operators are constexpr and noexcept, and return the enumeration type.
*
* @par Where the specialization may be declared
* In namespace xrpl, or at global scope with full qualification, and after
* the enumeration is defined but before the operators are first used. This
* cannot be done in a nested namespace or at class scope; for enumerations
* nested in a class this means after the class definition.
*
* @par Where the operators are found
* The operators are declared in namespace xrpl, so argument-dependent lookup
* will find them only for enumerations declared in xrpl or nested in a class
* declared directly in xrpl. Enumerations in a nested namespace will only be
* found from code inside xrpl, and only if no enclosing scope declares an
* operator of the same name; elsewhere they require using-declarations.
*/
namespace enum_bitops {
/**
* Types that may be opted in to the bitwise operators.
*
* Satisfied by a scoped enumeration whose underlying type is an unsigned
* integer type.
*
* @tparam T The type to test.
*/
template <typename T>
concept Eligible = std::is_scoped_enum_v<T> && std::unsigned_integral<std::underlying_type_t<T>>;
/**
* Opt-in switch for the bitwise operators.
*
* The primary template derives from std::false_type. Specialize it to derive
* from std::true_type to enable the operators for an @ref Eligible enumeration.
*
* @tparam T The scoped enumeration to opt in.
*/
template <Eligible T>
struct OptIn : std::false_type
{
};
/**
* Enumerations for which the bitwise operators are enabled.
*
* Satisfied when @p T satisfies @ref Eligible and @ref OptIn has been
* specialized for it to derive from std::true_type.
*
* @tparam T The type to test.
*/
template <typename T>
concept Candidate = Eligible<T> && OptIn<T>::value;
} // namespace enum_bitops
// These are declared in xrpl, not in enum_bitops, so that argument-dependent
// lookup finds them for enumerations whose associated namespace is xrpl.
/**
* @name Bitwise operators for opted-in scoped enumerations
*
* Each operator applies the corresponding built-in operator to the
* underlying values and converts the result back to the enumeration type.
* Both operands must have the same enumeration type; there is no implicit
* conversion to or from the underlying type.
*/
/** @{ */
/**
* Bitwise AND.
*
* @param lhs The left operand.
* @param rhs The right operand.
* @return The bits set in both @p lhs and @p rhs.
*/
template <enum_bitops::Candidate T>
constexpr T
operator&(T lhs, T rhs) noexcept
{
return static_cast<T>(std::to_underlying(lhs) & std::to_underlying(rhs));
}
/**
* Bitwise OR.
*
* @param lhs The left operand.
* @param rhs The right operand.
* @return The bits set in @p lhs, in @p rhs, or in both.
*/
template <enum_bitops::Candidate T>
constexpr T
operator|(T lhs, T rhs) noexcept
{
return static_cast<T>(std::to_underlying(lhs) | std::to_underlying(rhs));
}
/**
* Bitwise exclusive OR.
*
* @param lhs The left operand.
* @param rhs The right operand.
* @return The bits set in exactly one of @p lhs and @p rhs.
*/
template <enum_bitops::Candidate T>
constexpr T
operator^(T lhs, T rhs) noexcept
{
return static_cast<T>(std::to_underlying(lhs) ^ std::to_underlying(rhs));
}
/**
* Bitwise complement.
*
* The result has every bit of the underlying type that is clear in
* @p val, including bits that no enumerator names. It is intended for
* clearing flags, as in `flags & ~flag`.
*
* @param val The operand.
* @return The complement of @p val.
*/
template <enum_bitops::Candidate T>
constexpr T
operator~(T val) noexcept
{
return static_cast<T>(~std::to_underlying(val));
}
/**
* Bitwise AND assignment.
*
* @param lhs The value to modify.
* @param rhs The right operand.
* @return A reference to @p lhs.
*/
template <enum_bitops::Candidate T>
constexpr T&
operator&=(T& lhs, T rhs) noexcept
{
lhs = lhs & rhs;
return lhs;
}
/**
* Bitwise OR assignment.
*
* @param lhs The value to modify.
* @param rhs The right operand.
* @return A reference to @p lhs.
*/
template <enum_bitops::Candidate T>
constexpr T&
operator|=(T& lhs, T rhs) noexcept
{
lhs = lhs | rhs;
return lhs;
}
/**
* Bitwise exclusive OR assignment.
*
* @param lhs The value to modify.
* @param rhs The right operand.
* @return A reference to @p lhs.
*/
template <enum_bitops::Candidate T>
constexpr T&
operator^=(T& lhs, T rhs) noexcept
{
lhs = lhs ^ rhs;
return lhs;
}
/** @} */
} // namespace xrpl

View File

@@ -17,7 +17,7 @@
namespace xrpl {
template <typename Key>
static std::size_t
std::size_t
extract(Key const& key)
{
return key;
@@ -365,7 +365,7 @@ public:
size() const
{
std::size_t ret = 0;
for (auto& p : map_)
for (auto const& p : map_)
ret += p.size();
return ret;
}

View File

@@ -2,212 +2,107 @@
#include <xrpl/beast/utility/instrumentation.h> // IWYU pragma: keep
#include <concepts>
#include <cstdint>
#include <limits>
#include <memory>
#include <type_traits>
#include <utility>
namespace xrpl {
/** Every value of @p Src can be represented by @p Dest.
// safe_cast adds compile-time checks to a static_cast to ensure that
// the destination can hold all values of the source. This is particularly
// handy when the source or destination is an enumeration type.
Given two integral types, the cast is safe when the destination can
hold every possible value of the source.
template <class Src, class Dest>
concept SafeToCast = (std::is_integral_v<Src> && std::is_integral_v<Dest>) &&
(std::is_signed_v<Src> || std::is_unsigned_v<Dest>) &&
(std::is_signed_v<Src> != std::is_signed_v<Dest> ? sizeof(Dest) > sizeof(Src)
: sizeof(Dest) >= sizeof(Src));
Comparing the bounds requires care: we use @c std::cmp_less_equal and
@c std::cmp_greater_equal; the plain relational operators would apply
arithmetic conversions, resulting in incorrect results when comparing
across signedness.
Because @c std::cmp_* requires standard signed or unsigned integer
arguments, which excludes character types and bool, we first widen
all type bounds to the maximum-width integer type while preserving
signedness.
@note Extended integer types, like __int128 on gcc, cannot be safely
widened to a standard integer type, so the concept will reject
them.
*/
template <typename Src, typename Dest>
concept SafeToCast = std::is_integral_v<Src> && std::is_integral_v<Dest> && []() consteval {
using WideSrc = std::conditional_t<std::is_signed_v<Src>, std::intmax_t, std::uintmax_t>;
using WideDest = std::conditional_t<std::is_signed_v<Dest>, std::intmax_t, std::uintmax_t>;
// Note: this guard must be an evaluated branch, and not a
// static_assert. The lambda body is outside the immediate
// context, so a substitution-time failure here would be a
// hard error rather than leaving the concept unsatisfied.
if constexpr (sizeof(Src) > sizeof(WideSrc) || sizeof(Dest) > sizeof(WideDest))
{
return false;
}
else
{
return std::cmp_less_equal(
static_cast<WideDest>(std::numeric_limits<Dest>::min()),
static_cast<WideSrc>(std::numeric_limits<Src>::min())) &&
std::cmp_greater_equal(
static_cast<WideDest>(std::numeric_limits<Dest>::max()),
static_cast<WideSrc>(std::numeric_limits<Src>::max()));
}
}();
/** Compile-time-checked static_cast that rejects non-value preserving casts.
@note There is deliberately no enum-to-enum overload, and no overload
returning the underlying type of an enum. For the latter, use
@c std::to_underlying.
*/
/** @{ */
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
safeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
{
static_assert(
SafeToCast<Src, Dest>, "This cast is not value-preserving. Please use unsafeCast instead.");
std::is_signed_v<Dest> || std::is_unsigned_v<Src>, "Cannot cast signed to unsigned");
constexpr unsigned kNotSame = std::is_signed_v<Dest> != std::is_signed_v<Src>;
static_assert(
sizeof(Dest) >= sizeof(Src) + kNotSame,
"Destination is too small to hold all values of source");
return static_cast<Dest>(s);
}
template <typename Dest, typename Src>
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
safeCast(Src s) noexcept
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
{
return static_cast<Dest>(safeCast<std::underlying_type_t<Dest>>(s));
}
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
template <class Dest, class Src>
constexpr Dest
safeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
{
return safeCast<Dest>(std::to_underlying(s));
return safeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
}
/** @} */
/** Integral-to-integral cast that is known to be narrowing or sign-erasing.
// unsafe_cast explicitly flags a static_cast as not necessarily able to hold
// all values of the source. It includes a compile-time check so that if
// underlying types become safe, it can be converted to a safe_cast.
This explicitly flags a conversion that can lose information for some
values of the source type, where the call site accepts that loss (or
truncation is the intended behavior).
The compile-time check ensures the cast remains "unsafe": if the types
involved later change such that the conversion becomes inherently
value-preserving, the static assertion fires with instructions to
migrate the call site to @ref safeCast.
If the conversion's safety depends on a runtime precondition rather
than on the types, or varies across instantiations of generic code,
use @ref checkedCast instead.
*/
/** @{ */
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
unsafeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
{
static_assert(
!SafeToCast<Src, Dest>, "This cast is value-preserving. Please use safeCast instead.");
!SafeToCast<Src, Dest>,
"Only unsafe if casting signed to unsigned or "
"destination is too small");
return static_cast<Dest>(s);
}
template <typename Dest, typename Src>
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
unsafeCast(Src s) noexcept
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
{
return static_cast<Dest>(unsafeCast<std::underlying_type_t<Dest>>(s));
}
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
template <class Dest, class Src>
constexpr Dest
unsafeCast(Src s) noexcept
{
return unsafeCast<Dest>(std::to_underlying(s));
}
/** @} */
/** Integral-to-integral cast when the caller has performed a bounds check.
This documents that a runtime precondition or external invariant, which
is not necessarily visible to the compiler, guarantees that the requested
conversion is value-preserving for the values that can actually occur.
This is primarily meant for generic code, where the same expression may
be value-preserving for one instantiation but not for another, making
both @ref safeCast and @ref unsafeCast unusable.
Unlike @ref safeCast and @ref unsafeCast, this imposes no static check
on the type relationship: the caller's claim is about runtime values,
not about types.
*/
/** @{ */
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
constexpr Dest
checkedCast(Src s) noexcept
{
return static_cast<Dest>(s);
}
template <typename Dest, typename Src>
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
constexpr Dest
checkedCast(Src s) noexcept
{
return static_cast<Dest>(checkedCast<std::underlying_type_t<Dest>>(s));
}
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
constexpr Dest
checkedCast(Src s) noexcept
{
return checkedCast<Dest>(std::to_underlying(s));
return unsafeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
}
/** @} */
/** Downcast within a class hierarchy, verified in debug builds.
Performs a static_cast down a hierarchy, but in debug builds verifies
via dynamic_cast that the object's dynamic type actually permits the
downcast. Both build modes execute the same conversion; debug builds
merely add the check.
The pointer form passes null through unchanged, as dynamic_cast does.
@note The check requires @p Src to be polymorphic; in release builds
an invalid downcast is undefined behavior on use, exactly as
with a bare static_cast.
*/
/** @{ */
template <class Dest, class Src>
requires(
std::is_pointer_v<Dest> && std::is_polymorphic_v<Src> &&
std::derived_from<std::remove_pointer_t<Dest>, Src> &&
(std::is_const_v<std::remove_pointer_t<Dest>> || !std::is_const_v<Src>) &&
(std::is_volatile_v<std::remove_pointer_t<Dest>> || !std::is_volatile_v<Src>))
requires std::is_pointer_v<Dest>
inline Dest
safeDowncast(Src* s) noexcept
{
XRPL_ASSERT(
s == nullptr || dynamic_cast<Dest>(s) != nullptr, "xrpl::safeDowncast : valid downcast");
#ifdef NDEBUG
return static_cast<Dest>(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
#else
auto* result = dynamic_cast<Dest>(s);
XRPL_ASSERT(result != nullptr, "xrpl::safeDowncast : pointer downcast is valid");
return result;
#endif
}
template <class Dest, class Src>
requires(
std::is_lvalue_reference_v<Dest> && std::is_polymorphic_v<Src> &&
std::derived_from<std::remove_reference_t<Dest>, Src>)
requires std::is_lvalue_reference_v<Dest>
inline Dest
safeDowncast(Src& s) noexcept
{
return *safeDowncast<std::add_pointer_t<std::remove_reference_t<Dest>>>(std::addressof(s));
#ifndef NDEBUG
XRPL_ASSERT(
dynamic_cast<std::add_pointer_t<std::remove_reference_t<Dest>>>(&s) != nullptr,
"xrpl::safeDowncast : reference downcast is valid");
#endif
return static_cast<Dest>(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
}
/** @} */
} // namespace xrpl

View File

@@ -4,215 +4,88 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <boost/predef/architecture.h>
#include <atomic>
#include <concepts>
#include <limits>
#include <type_traits>
#if BOOST_ARCH_X86
#ifndef __aarch64__
#include <immintrin.h>
#endif
namespace xrpl {
/** An unsigned integral type suitable for use as a spinlock.
The type must be always lock-free when wrapped in std::atomic, so
that lock operations cannot themselves take a (library-level) lock.
*/
template <typename T>
concept SpinlockValueType = std::is_unsigned_v<T> && std::atomic<T>::is_always_lock_free;
/** A spinlock value type that additionally supports the atomic bitwise
operations required to pack multiple locks into a single integer.
*/
template <typename T>
concept PackedSpinlockValueType = SpinlockValueType<T> && requires(std::atomic<T>& a, T v) {
{ a.fetch_or(v) } -> std::same_as<T>;
{ a.fetch_and(v) } -> std::same_as<T>;
};
namespace detail {
/** Inform the processor that we are in a tight spin-wait loop.
Spinlocks caught in tight loops can result in the processor's pipeline
filling up with comparison operations, resulting in a misprediction at
the time the lock is finally acquired, necessitating pipeline flushing
which is ridiculously expensive and results in very high latency.
This function instructs the processor to "pause" for some architecture
specific amount of time, to prevent this.
/**
* Inform the processor that we are in a tight spin-wait loop.
*
* Spinlocks caught in tight loops can result in the processor's pipeline
* filling up with comparison operations, resulting in a misprediction at
* the time the lock is finally acquired, necessitating pipeline flushing
* which is ridiculously expensive and results in very high latency.
*
* This function instructs the processor to "pause" for some architecture
* specific amount of time, to prevent this.
*/
inline void
spinPause() noexcept
{
#if BOOST_ARCH_X86
_mm_pause();
#elif BOOST_ARCH_ARM
asm volatile("yield" ::: "memory");
#ifdef __aarch64__
asm volatile("yield");
#else
#error No implementation available for spinPause to use
_mm_pause();
#endif
}
} // namespace detail
//------------------------------------------------------------------------------
/** Attempt to acquire a spinlock without blocking.
@note This interface is primarily intended for one-shot attempts to
acquire the lock. Avoid calling this function directly from a
loop and use @ref spinLock instead.
@tparam T An unsigned integral type.
@param lock The atomic variable used as the lock.
@return true if the lock was acquired, false if it was already held.
/** @{ */
/**
* Classes to handle arrays of spinlocks packed into a single atomic integer:
*
* Packed spinlocks allow for tremendously space-efficient lock-sharding
* but they come at a cost.
*
* First, the implementation is necessarily low-level and uses advanced
* features like memory ordering and highly platform-specific tricks to
* maximize performance. This imposes a significant and ongoing cost to
* developers.
*
* Second, and perhaps most important, is that the packing of multiple
* locks into a single integer which, albeit space-efficient, also has
* performance implications stemming from data dependencies, increased
* cache-coherency traffic between processors and heavier loads on the
* processor's load/store units.
*
* To be sure, these locks can have advantages but they are definitely
* not general purpose locks and should not be thought of or used that
* way. The use cases for them are likely few and far between; without
* a compelling reason to use them, backed by profiling data, it might
* be best to use one of the standard locking primitives instead. Note
* that in most common platforms, `std::mutex` is so heavily optimized
* that it can, usually, outperform spinlocks.
*
* @tparam T An unsigned integral type (e.g. std::uint16_t)
*/
template <SpinlockValueType T>
[[nodiscard]] bool
spinTryLock(std::atomic<T>& lock) noexcept
{
// A compare-exchange is required here, not an unconditional exchange:
// a failed attempt must not modify the lock word, in case the atomic
// is shared with PackedSpinlock).
T expected = 0;
return lock.compare_exchange_strong(
expected,
std::numeric_limits<T>::max(),
std::memory_order::acquire,
std::memory_order::relaxed);
;
}
/** Acquire a spinlock, blocking until available.
Uses a TTAS (test-and-test-and-set) pattern, so waiters share the cache
line read-only, helping to avoid unnecessary coherency traffic.
@tparam T An unsigned integral type.
@param lock The atomic variable used as the lock.
/**
* A class that grabs a single packed spinlock from an atomic integer.
*
* This class meets the requirements of Lockable:
* https://en.cppreference.com/w/cpp/named_req/Lockable
*/
template <SpinlockValueType T>
void
spinLock(std::atomic<T>& lock) noexcept
{
do
{
// Relaxed ordering is sufficient for the spin: this load is only
// a filter. The acquire on the successful exchange in spinTryLock
// is what synchronizes the critical section.
while (lock.load(std::memory_order::relaxed) != 0)
detail::spinPause();
} while (!spinTryLock(lock));
}
/** Release a spinlock.
@tparam T An unsigned integral type.
@param lock The atomic variable used as the lock.
*/
template <SpinlockValueType T>
void
spinUnlock(std::atomic<T>& lock) noexcept
{
lock.store(0, std::memory_order::release);
}
//------------------------------------------------------------------------------
/** A Lockable interface to a spinlock implemented on top of an atomic.
@tparam T An unsigned integral type.
@note Using `PackedSpinlock` and `Spinlock` against the same underlying
atomic integer is possible but can result in `Spinlock` not being
able to acquire the lock during periods of high contention due to
the way the two locks operate: `Spinlock` spins and tries to grab
all the bits at once, whereas any given `PackedSpinlock` instance
only tries to grab one bit at a time. Caveat emptor.
This class meets the requirements of Lockable:
https://en.cppreference.com/w/cpp/named_req/Lockable
*/
template <SpinlockValueType T>
class Spinlock
{
std::atomic<T>& lock_;
public:
Spinlock(Spinlock const&) = delete;
Spinlock&
operator=(Spinlock const&) = delete;
/** Construct a spinlock handle.
@param lock The atomic integer to spin against.
@note For performance reasons, you should strive to have `lock` be
on a cacheline by itself.
*/
explicit Spinlock(std::atomic<T>& lock) noexcept : lock_(lock)
{
}
[[nodiscard]] bool
try_lock() noexcept // NOLINT(readability-identifier-naming)
{
return spinTryLock(lock_);
}
void
lock() noexcept
{
spinLock(lock_);
}
void
unlock() noexcept
{
spinUnlock(lock_);
}
};
//------------------------------------------------------------------------------
/** A Lockable interface to a packed spinlock implemented on top of an atomic.
Packed spinlocks offer tremendous space-efficient lock-sharding but
they come at a cost.
First, the implementation is necessarily low-level and uses advanced
features like memory ordering and highly platform-specific tricks to
maximize performance. This imposes a significant and ongoing cost to
developers.
Second, and perhaps most important, is that the packing of multiple
locks into a single integer which, albeit space-efficient, also has
performance implications stemming from data dependencies, increased
cache-coherency traffic between processors and heavier loads on the
processor's load/store units.
To be sure, these locks can have advantages but they are definitely
not general purpose locks and should not be thought of or used that
way. The use cases for them are likely few and far between; without
a compelling reason to use them, backed by profiling data, it might
be best to use one of the standard locking primitives instead. Note
that in most common platforms, `std::mutex` is so heavily optimized
that it can, usually, outperform spinlocks.
@tparam T An unsigned integral type (e.g. std::uint16_t)
This class meets the requirements of Lockable:
https://en.cppreference.com/w/cpp/named_req/Lockable
*/
template <PackedSpinlockValueType T>
template <class T>
class PackedSpinlock
{
// clang-format off
static_assert(std::is_unsigned_v<T>);
static_assert(std::atomic<T>::is_always_lock_free);
static_assert(
std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_or(0)), T> &&
std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_and(0)), T>,
"std::atomic<T>::fetch_and(T) and std::atomic<T>::fetch_and(T) are required by packed_spinlock");
// clang-format on
private:
std::atomic<T>& bits_;
T const mask_;
@@ -221,49 +94,120 @@ public:
PackedSpinlock&
operator=(PackedSpinlock const&) = delete;
/** Construct a packed spinlock handle for a single bit.
@param lock The atomic integer inside which the spinlock is packed.
@param index The index of the spinlock this object acquires.
@note For performance reasons, you should strive to have `lock` be
on a cacheline by itself.
/**
* A single spinlock packed inside the specified atomic
*
* @param lock The atomic integer inside which the spinlock is packed.
* @param index The index of the spinlock this object acquires.
*
* @note For performance reasons, you should strive to have `lock` be
* on a cacheline by itself.
*/
PackedSpinlock(std::atomic<T>& lock, int index) noexcept
: bits_(lock), mask_([index]() {
XRPL_ASSERT(
index >= 0 && index < std::numeric_limits<T>::digits,
"xrpl::PackedSpinlock::PackedSpinlock : valid index");
return static_cast<T>(T{1} << index);
}())
PackedSpinlock(std::atomic<T>& lock, int index) : bits_(lock), mask_(static_cast<T>(1) << index)
{
XRPL_ASSERT(
index >= 0 && (mask_ != 0),
"xrpl::PackedSpinlock::PackedSpinlock : valid index and mask");
}
[[nodiscard]] bool
try_lock() // NOLINT(readability-identifier-naming)
{
return (bits_.fetch_or(mask_, std::memory_order_acquire) & mask_) == 0;
}
void
lock()
{
while (!try_lock())
{
// The use of relaxed memory ordering here is intentional and
// serves to help reduce cache coherency traffic during times
// of contention by avoiding writes that would definitely not
// result in the lock being acquired.
while ((bits_.load(std::memory_order_relaxed) & mask_) != 0)
detail::spinPause();
}
}
void
unlock()
{
bits_.fetch_and(~mask_, std::memory_order_release);
}
};
/**
* A spinlock implemented on top of an atomic integer.
*
* @note Using `packed_spinlock` and `spinlock` against the same underlying
* atomic integer can result in `spinlock` not being able to actually
* acquire the lock during periods of high contention, because of how
* the two locks operate: `spinlock` will spin trying to grab all the
* bits at once, whereas any given `packed_spinlock` will only try to
* grab one bit at a time. Caveat emptor.
*
* This class meets the requirements of Lockable:
* https://en.cppreference.com/w/cpp/named_req/Lockable
*/
template <class T>
class Spinlock
{
static_assert(std::is_unsigned_v<T>);
static_assert(std::atomic<T>::is_always_lock_free);
private:
std::atomic<T>& lock_;
public:
Spinlock(Spinlock const&) = delete;
Spinlock&
operator=(Spinlock const&) = delete;
/**
* Grabs the
*
* @param lock The atomic integer to spin against.
*
* @note For performance reasons, you should strive to have `lock` be
* on a cacheline by itself.
*/
Spinlock(std::atomic<T>& lock) : lock_(lock)
{
}
[[nodiscard]] bool
try_lock() noexcept // NOLINT(readability-identifier-naming)
try_lock() // NOLINT(readability-identifier-naming)
{
return (bits_.fetch_or(mask_, std::memory_order::acquire) & mask_) == 0;
T expected = 0;
return lock_.compare_exchange_weak(
expected,
std::numeric_limits<T>::max(),
std::memory_order_acquire,
std::memory_order_relaxed);
}
void
lock() noexcept
lock()
{
do
while (!try_lock())
{
// The use of relaxed memory ordering here is intentional and
// serves to help reduce cache coherency traffic during times
// of contention by avoiding writes that are unlikely to grab
// the requested lock.
while ((bits_.load(std::memory_order::relaxed) & mask_) != 0)
// of contention by avoiding writes that would definitely not
// result in the lock being acquired.
while (lock_.load(std::memory_order_relaxed) != 0)
detail::spinPause();
} while (!try_lock());
}
}
void
unlock() noexcept
unlock()
{
bits_.fetch_and(~mask_, std::memory_order::release);
lock_.store(0, std::memory_order_release);
}
};
/** @} */
} // namespace xrpl

View File

@@ -1604,7 +1604,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::erase(
beast::detail::AgedContainerIterator<IsConst, Iterator> pos)
requires(!IsBoostReverseIterator<Iterator>::value)
{
unlinkAndDeleteElement(&*((pos++).iterator()));
unlinkAndDeleteElement(&*(pos++).iterator());
return beast::detail::AgedContainerIterator<false, Iterator>(pos.iterator());
}
@@ -1617,7 +1617,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::erase(
requires(!IsBoostReverseIterator<Iterator>::value)
{
for (; first != last;)
unlinkAndDeleteElement(&*((first++).iterator()));
unlinkAndDeleteElement(&*(first++).iterator());
return beast::detail::AgedContainerIterator<false, Iterator>(first.iterator());
}

View File

@@ -2404,7 +2404,7 @@ beast::detail::AgedContainerIterator<false, Iterator>
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::erase(
beast::detail::AgedContainerIterator<IsConst, Iterator> pos)
{
unlinkAndDeleteElement(&*((pos++).iterator()));
unlinkAndDeleteElement(&*(pos++).iterator());
return beast::detail::AgedContainerIterator<false, Iterator>(pos.iterator());
}
@@ -2424,7 +2424,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
beast::detail::AgedContainerIterator<IsConst, Iterator> last)
{
for (; first != last;)
unlinkAndDeleteElement(&*((first++).iterator()));
unlinkAndDeleteElement(&*(first++).iterator());
return beast::detail::AgedContainerIterator<false, Iterator>(first.iterator());
}
@@ -2536,7 +2536,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
return false;
for (auto iter(cbegin()), last(cend()), otherLast(other.cend()); iter != last; ++iter)
{
auto otherIter(other.find(extract(*iter)));
auto const otherIter(other.find(extract(*iter)));
if (otherIter == otherLast)
return false;
}

View File

@@ -68,7 +68,7 @@ struct LexicalCast<Out, std::string_view>
if (first != last && *first == '+')
++first;
auto ret = std::from_chars(first, last, out);
auto const ret = std::from_chars(first, last, out);
return ret.ec == std::errc() && ret.ptr == last;
}

View File

@@ -390,7 +390,7 @@ void
hash_append(Hasher& h, boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept
requires(IsContiguouslyHashable<Key, Hasher>::value)
{
h(&(v.begin()), v.size() * sizeof(Key));
h(&v.begin(), v.size() * sizeof(Key));
}
// tuple

View File

@@ -8,7 +8,7 @@ namespace beast::insight {
class HookImpl : public std::enable_shared_from_this<HookImpl>
{
public:
using HandlerType = std::function<void(void)>;
using HandlerType = std::function<void()>;
virtual ~HookImpl() = 0;
};

View File

@@ -61,7 +61,7 @@ isMulticast(Address const& addr)
inline bool
isPrivate(Address const& addr)
{
return (addr.is_v4()) ? isPrivate(addr.to_v4()) : isPrivate(addr.to_v6());
return addr.is_v4() ? isPrivate(addr.to_v4()) : isPrivate(addr.to_v6());
}
/**
@@ -70,7 +70,7 @@ isPrivate(Address const& addr)
inline bool
isPublic(Address const& addr)
{
return (addr.is_v4()) ? isPublic(addr.to_v4()) : isPublic(addr.to_v6());
return addr.is_v4() ? isPublic(addr.to_v4()) : isPublic(addr.to_v6());
}
} // namespace ip

View File

@@ -83,7 +83,7 @@ trimRight(String const& s)
{
using std::begin;
using std::end;
auto first(begin(s));
auto const first(begin(s));
auto last(end(s));
last = trimRight(first, last);
return {first, last};

View File

@@ -0,0 +1,47 @@
#pragma once
#include <cstdlib>
#include <string>
#include <type_traits>
#include <typeinfo>
#ifndef _MSC_VER
#include <cxxabi.h>
#endif
namespace beast {
template <typename T>
std::string
typeName()
{
using TR = std::remove_reference_t<T>;
std::string name = typeid(TR).name();
#ifndef _MSC_VER
if (auto s = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, nullptr))
{
name = s;
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
std::free(s);
}
#endif
if (std::is_const_v<TR>)
name += " const";
if (std::is_volatile_v<TR>)
name += " volatile";
if (std::is_lvalue_reference_v<T>)
{
name += '&';
}
else if (std::is_rvalue_reference_v<T>)
{
name += "&&";
}
return name;
}
} // namespace beast

View File

@@ -32,7 +32,7 @@ public:
Module,
// Match nothing (used internally)
None
None,
};
private:

View File

@@ -20,7 +20,7 @@ namespace beast::unit_test {
namespace detail {
template <class String>
static std::string
std::string
makeReason(String const& reason, char const* file, int line)
{
std::string s(reason);

View File

@@ -47,8 +47,8 @@ public:
template <class F, class... Args>
explicit Thread(Suite& s, F&& f, Args&&... args) : s_(&s)
{
std::function<void(void)> b = [f = std::forward<F>(f),
... args = std::forward<Args>(args)]() mutable {
std::function<void()> b = [f = std::forward<F>(f),
... args = std::forward<Args>(args)] mutable {
std::invoke(f, args...);
};
t_ = std::thread(&Thread::run, this, std::move(b));
@@ -94,7 +94,7 @@ public:
private:
void
run(std::function<void(void)> f)
run(std::function<void()> f)
{
try
{

View File

@@ -24,7 +24,7 @@ enum class Severity : std::uint8_t {
Fatal = 5,
Disabled = 6,
None = Disabled
None = Disabled,
};
/**

View File

@@ -2,24 +2,26 @@
#pragma once
#include <compare>
#include <concepts>
namespace beast {
/**
* Zero allows classes to offer efficient comparisons to zero.
*
* Zero is a struct to allow classes to efficiently compare with zero without
* requiring an rvalue construction.
*
* It's often the case that we have classes which combine a number and a unit.
* In such cases, comparisons like t > 0 or t != 0 make sense, but comparisons
* like t > 1 or t != 1 do not. Comparing against kZero expresses exactly that,
* without constructing a T.
* like t > 1 or t != 1 do not.
*
* A type T participates if either `t.signum()` or an unqualified `signum(t)`
* found by argument-dependent lookup returns an integer that is negative,
* zero, or positive according to the sign of t. Both `t == kZero` and
* `kZero == t` work, as do all six relational operators in either order.
* The class Zero allows such comparisons to be easily made.
*
* The comparing class T either needs to have a method called signum() which
* returns a positive number, 0, or a negative; or there needs to be a signum
* function which resolves in the namespace which takes an instance of T and
* returns a positive, zero or negative number.
*/
struct Zero
{
explicit Zero() = default;
@@ -28,44 +30,115 @@ struct Zero
inline constexpr Zero kZero{};
/**
* Default implementation of signum: call the member function.
* Default implementation of signum calls the method on the class.
*/
template <class T>
requires requires(T const& t) {
{ t.signum() } -> std::integral;
}
[[nodiscard]] constexpr auto
signum(T const& t) noexcept(noexcept(t.signum()))
template <typename T>
auto
signum(T const& t)
{
return t.signum();
}
namespace detail {
namespace detail::zero_helper {
/**
* A type with a usable signum: either the member-based default above, or a
* `signum(t)` overload in T's own namespace, found by ADL. A user overload
* that is a better match than the template wins, as usual.
*/
// For argument dependent lookup to function properly, calls to signum must
// be made from a namespace that does not include overloads of the function..
template <class T>
concept HasSignum = requires(T const& t) {
{ signum(t) } -> std::integral;
};
} // namespace detail
template <detail::HasSignum T>
[[nodiscard]] constexpr bool
operator==(T const& t, Zero) noexcept(noexcept(signum(t)))
auto
callSignum(T const& t)
{
return signum(t) == 0;
return signum(t);
}
template <detail::HasSignum T>
[[nodiscard]] constexpr std::strong_ordering
operator<=>(T const& t, Zero) noexcept(noexcept(signum(t)))
} // namespace detail::zero_helper
// Handle operators where T is on the left side using signum.
template <typename T>
bool
operator==(T const& t, Zero)
{
return signum(t) <=> 0;
return detail::zero_helper::callSignum(t) == 0;
}
template <typename T>
bool
operator!=(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) != 0;
}
template <typename T>
bool
operator<(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) < 0;
}
template <typename T>
bool
operator>(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) > 0;
}
template <typename T>
bool
operator>=(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) >= 0;
}
template <typename T>
bool
operator<=(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) <= 0;
}
// Handle operators where T is on the right side by
// reversing the operation, so that T is on the left side.
template <typename T>
bool
operator==(Zero, T const& t)
{
return t == kZero;
}
template <typename T>
bool
operator!=(Zero, T const& t)
{
return t != kZero;
}
template <typename T>
bool
operator<(Zero, T const& t)
{
return t > kZero;
}
template <typename T>
bool
operator>(Zero, T const& t)
{
return t < kZero;
}
template <typename T>
bool
operator>=(Zero, T const& t)
{
return t <= kZero;
}
template <typename T>
bool
operator<=(Zero, T const& t)
{
return t >= kZero;
}
} // namespace beast

View File

@@ -18,7 +18,7 @@ enum class Type : std::uint8_t {
PrefixSha256 = 1,
ThresholdSha256 = 2,
RsaSha256 = 3,
Ed25519Sha256 = 4
Ed25519Sha256 = 4,
};
class Condition

View File

@@ -49,7 +49,7 @@ public:
using namespace der;
auto p = parsePreamble(s, ec);
auto const p = parsePreamble(s, ec);
if (ec)
return nullptr;

View File

@@ -21,7 +21,7 @@ enum class Error {
UnexpectedTag,
LongTag,
LargeSize,
PreimageTooLong
PreimageTooLong,
};
std::error_code

View File

@@ -396,7 +396,7 @@ inline bool
getIfExists<bool>(Section const& section, std::string const& name, bool& v)
{
int intVal = 0;
auto stat = getIfExists(section, name, intVal);
auto const stat = getIfExists(section, name, intVal);
if (stat)
v = bool(intVal);
return stat;

View File

@@ -0,0 +1,3 @@
# AGENTS.md — consensus
See [README.md](./README.md) for a short pointer, and [docs/consensus.md](../../../docs/consensus.md) for the full consensus design.

View File

@@ -0,0 +1 @@
AGENTS.md

View File

@@ -93,14 +93,14 @@ public:
void
check(std::vector<TxID> accepted, Predicate&& pred)
{
auto acceptTxid = accepted.begin();
auto const acceptTxid = accepted.begin();
auto const ae = accepted.end();
std::sort(acceptTxid, ae);
// We want to remove all tracking entries for transactions that were
// accepted as well as those which match the predicate.
auto i = removeIfIntersectOrMatch(
auto const i = removeIfIntersectOrMatch(
tracker_.begin(),
tracker_.end(),
accepted.begin(),

View File

@@ -779,7 +779,7 @@ Consensus<Adaptor>::peerProposalInternal(
{
// update current position
auto peerPosIt = currPeerPositions_.find(peerID);
auto const peerPosIt = currPeerPositions_.find(peerID);
if (peerPosIt != currPeerPositions_.end())
{
@@ -1110,7 +1110,7 @@ Consensus<Adaptor>::checkLedger(std::unique_ptr<std::stringstream> const& clog)
{
CLOG(clog) << "checkLedger. ";
auto netLgr = adaptor_.getPrevLedger(prevLedgerID_, previousLedger_, mode_.get());
auto const netLgr = adaptor_.getPrevLedger(prevLedgerID_, previousLedger_, mode_.get());
CLOG(clog) << "network ledgerid " << netLgr << ", "
<< "previous ledger " << prevLedgerID_ << ". ";
@@ -1159,8 +1159,8 @@ Consensus<Adaptor>::phaseOpen(std::unique_ptr<std::stringstream> const& clog)
// it is shortly before ledger close time
bool const anyTransactions = adaptor_.hasOpenTransactions();
auto proposersClosed = currPeerPositions_.size();
auto proposersValidated = adaptor_.proposersValidated(prevLedgerID_);
auto const proposersClosed = currPeerPositions_.size();
auto const proposersValidated = adaptor_.proposersValidated(prevLedgerID_);
openTime_.tick(clock_.now());
@@ -1669,7 +1669,7 @@ Consensus<Adaptor>::haveConsensus(std::unique_ptr<std::stringstream> const& clog
// CHECKME: should possibly count unacquired TX sets as disagreeing
int agree = 0, disagree = 0;
auto ourPosition = result_->position.position();
auto const ourPosition = result_->position.position();
// Count number of agreements/disagreements with our position
for (auto const& [nodeId, peerPos] : currPeerPositions_)
@@ -1686,7 +1686,7 @@ Consensus<Adaptor>::haveConsensus(std::unique_ptr<std::stringstream> const& clog
++disagree;
}
}
auto currentFinished = adaptor_.proposersFinished(previousLedger_, prevLedgerID_);
auto const currentFinished = adaptor_.proposersFinished(previousLedger_, prevLedgerID_);
JLOG(j_.debug()) << "Checking for TX consensus: agree=" << agree << ", disagree=" << disagree;
@@ -1812,7 +1812,7 @@ Consensus<Adaptor>::createDisputes(TxSetT const& o, std::unique_ptr<std::strings
<< ". ";
JLOG(j_.debug()) << "createDisputes " << result_->txns.id() << " to " << o.id();
auto differences = result_->txns.compare(o);
auto const differences = result_->txns.compare(o);
int dc = 0;
@@ -1826,7 +1826,7 @@ Consensus<Adaptor>::createDisputes(TxSetT const& o, std::unique_ptr<std::strings
"xrpl::Consensus::createDisputes : has disputed transactions");
TxT const tx = inThisSet ? result_->txns.find(txId) : o.find(txId);
auto txID = tx.id();
auto const txID = tx.id();
if (result_->disputes.find(txID) != result_->disputes.end())
continue;
@@ -1837,7 +1837,8 @@ Consensus<Adaptor>::createDisputes(TxSetT const& o, std::unique_ptr<std::strings
tx,
result_->txns.exists(txID),
std::max(prevProposers_, currPeerPositions_.size()),
j_};
j_,
};
// Update all of the available peer's votes on the disputed transaction
for (auto const& [nodeId, peerPos] : currPeerPositions_)

View File

@@ -145,18 +145,26 @@ struct ConsensusParms
std::map<AvalancheState, AvalancheCutoff> const avalancheCutoffs{
// {state, {time, percent, nextState}},
// Initial state: 50% of nodes must vote yes
{AvalancheState::Init,
{.consensusTime = 0, .consensusPct = 50, .next = AvalancheState::Mid}},
{
AvalancheState::Init,
{.consensusTime = 0, .consensusPct = 50, .next = AvalancheState::Mid},
},
// mid-consensus starts after 50% of the previous round time, and
// requires 65% yes
{AvalancheState::Mid,
{.consensusTime = 50, .consensusPct = 65, .next = AvalancheState::Late}},
{
AvalancheState::Mid,
{.consensusTime = 50, .consensusPct = 65, .next = AvalancheState::Late},
},
// late consensus starts after 85% time, and requires 70% yes
{AvalancheState::Late,
{.consensusTime = 85, .consensusPct = 70, .next = AvalancheState::Stuck}},
{
AvalancheState::Late,
{.consensusTime = 85, .consensusPct = 70, .next = AvalancheState::Stuck},
},
// we're stuck after 2x time, requires 95% yes votes
{AvalancheState::Stuck,
{.consensusTime = 200, .consensusPct = 95, .next = AvalancheState::Stuck}},
{
AvalancheState::Stuck,
{.consensusTime = 200, .consensusPct = 95, .next = AvalancheState::Stuck},
},
};
/**

View File

@@ -59,7 +59,7 @@ enum class ConsensusMode {
* if we entered the round observing, but is used to indicate we did
* have the wrongLedger at some point.
*/
SwitchedLedger
SwitchedLedger,
};
inline std::string
@@ -222,7 +222,7 @@ medianCloseOffset(ConsensusCloseTimes const& times)
// Accumulate weight in time order; the first bin to reach halfWeight is
// the (lower) weighted median. Returns true once that bin is found.
auto step = [&](time_point t, std::int64_t w) {
auto const step = [&](time_point t, std::int64_t w) {
XRPL_ASSERT(tally < halfWeight, "xrpl::medianCloseOffset::step : median not yet found");
tally += w;
if (tally >= halfWeight)
@@ -267,7 +267,7 @@ enum class ConsensusState {
No, ///< We do not have consensus
MovedOn, ///< The network has consensus without us
Expired, ///< Consensus time limit has hard-expired
Yes ///< We have consensus along with the network
Yes, ///< We have consensus along with the network
};
/**

View File

@@ -266,7 +266,7 @@ template <class Tx, class NodeId>
void
DisputedTx<Tx, NodeId>::unVote(NodeId const& peer)
{
auto it = votes_.find(peer);
auto const it = votes_.find(peer);
if (it != votes_.end())
{

View File

@@ -211,7 +211,7 @@ struct Node
void
erase(Node const* child)
{
auto it = std::ranges::find_if(
auto const it = std::ranges::find_if(
children, [child](std::unique_ptr<Node> const& curr) { return curr.get() == child; });
XRPL_ASSERT(it != children.end(), "xrpl::Node::erase : valid input");
std::swap(*it, children.back());

View File

@@ -184,7 +184,7 @@ enum class ValStatus {
/**
* Multiple validations by a validator for different ledgers
*/
Conflicting
Conflicting,
};
inline std::string
@@ -366,7 +366,7 @@ private:
removeTrie(std::scoped_lock<Mutex> const&, NodeID const& nodeID, Validation const& val)
{
{
auto it = acquiring_.find(std::make_pair(val.seq(), val.ledgerID()));
auto const it = acquiring_.find(std::make_pair(val.seq(), val.ledgerID()));
if (it != acquiring_.end())
{
it->second.erase(nodeID);
@@ -375,7 +375,7 @@ private:
}
}
{
auto it = lastLedger_.find(nodeID);
auto const it = lastLedger_.find(nodeID);
if (it != lastLedger_.end() && it->second.id() == val.ledgerID())
{
trie_.remove(it->second);
@@ -443,7 +443,7 @@ private:
// Clear any prior acquiring ledger for this node
if (prior)
{
auto it = acquiring_.find(*prior);
auto const it = acquiring_.find(*prior);
if (it != acquiring_.end())
{
it->second.erase(nodeID);
@@ -455,7 +455,7 @@ private:
checkAcquired(lock);
std::pair<Seq, ID> const valPair{val.seq(), val.ledgerID()};
auto it = acquiring_.find(valPair);
auto const it = acquiring_.find(valPair);
if (it != acquiring_.end())
{
it->second.insert(nodeID);
@@ -529,7 +529,7 @@ private:
}
else
{
auto cit = typename decltype(current_)::const_iterator{it};
auto const cit = typename decltype(current_)::const_iterator{it};
// contains a live record
f(cit->first, cit->second);
++it;
@@ -554,7 +554,7 @@ private:
void
byLedger(std::scoped_lock<Mutex> const&, ID const& ledgerID, Pre&& pre, F&& f)
{
auto it = byLedger_.find(ledgerID);
auto const it = byLedger_.find(ledgerID);
if (it != byLedger_.end())
{
// Update set time since it is being used
@@ -857,7 +857,7 @@ public:
if (!preferred)
{
// fall back to majority over acquiring ledgers
auto it = std::ranges::max_element(acquiring_, [](auto const& a, auto const& b) {
auto const it = std::ranges::max_element(acquiring_, [](auto const& a, auto const& b) {
std::pair<Seq, ID> const& aKey = a.first;
typename HashSet<NodeID>::size_type const& aSize = a.second.size();
std::pair<Seq, ID> const& bKey = b.first;
@@ -937,7 +937,7 @@ public:
return (preferred->first >= minSeq) ? preferred->second : lcl.id();
// Otherwise, rely on peer ledgers
auto it = std::ranges::max_element(peerCounts, [](auto const& a, auto const& b) {
auto const it = std::ranges::max_element(peerCounts, [](auto const& a, auto const& b) {
// Prefer larger counts, then larger ids on ties
// (max_element expects this to return true if a < b)
return std::tie(a.second, a.first) < std::tie(b.second, b.first);

View File

@@ -156,7 +156,7 @@ public:
{
if (!allClosuresDoneCond_.wait_for(lock, wait, [this] { return closureCount_ == 0; }))
{
if (auto stream = j.error())
if (auto const stream = j.error())
stream << name << " waiting for ClosureCounter::join().";
allClosuresDoneCond_.wait(lock, [this] { return closureCount_ == 0; });
}

View File

@@ -54,7 +54,7 @@ JobQueue::Coro::post()
}
// sp keeps 'this' alive
if (jq_.addJob(type_, name_, [this, sp = shared_from_this()]() { resume(); }))
if (jq_.addJob(type_, name_, [this, sp = shared_from_this()] { resume(); }))
{
return true;
}
@@ -130,7 +130,7 @@ inline void
JobQueue::Coro::join()
{
std::unique_lock<std::mutex> lk(mutexRun_);
cv_.wait(lk, [this]() { return !running_; });
cv_.wait(lk, [this] { return !running_; });
}
} // namespace xrpl

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