diff --git a/.clang-tidy b/.clang-tidy index a444957a0e..88dd6f4e57 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,167 +1,100 @@ --- Checks: "-*, - bugprone-argument-comment, - bugprone-assert-side-effect, - bugprone-bad-signal-to-kill-thread, - bugprone-bool-pointer-implicit-conversion, - bugprone-capturing-this-in-member-variable, - bugprone-casting-through-void, - bugprone-chained-comparison, - bugprone-compare-pointer-to-member-virtual-function, - bugprone-copy-constructor-init, - bugprone-crtp-constructor-accessibility, - bugprone-dangling-handle, - bugprone-dynamic-static-initializers, - bugprone-empty-catch, - bugprone-fold-init-type, - bugprone-forward-declaration-namespace, - bugprone-inaccurate-erase, - bugprone-inc-dec-in-conditions, - bugprone-incorrect-enable-if, - bugprone-incorrect-roundings, - bugprone-infinite-loop, - bugprone-integer-division, - bugprone-lambda-function-name, - bugprone-macro-parentheses, - bugprone-macro-repeated-side-effects, - bugprone-misleading-setter-of-reference, - bugprone-misplaced-operator-in-strlen-in-alloc, - bugprone-misplaced-pointer-arithmetic-in-alloc, - bugprone-misplaced-widening-cast, - bugprone-move-forwarding-reference, - bugprone-multi-level-implicit-pointer-conversion, - bugprone-multiple-new-in-one-expression, - bugprone-multiple-statement-macro, - bugprone-no-escape, - bugprone-non-zero-enum-to-bool-conversion, - bugprone-optional-value-conversion, - bugprone-parent-virtual-call, - bugprone-pointer-arithmetic-on-polymorphic-object, - bugprone-posix-return, - bugprone-redundant-branch-condition, - bugprone-reserved-identifier, - bugprone-return-const-ref-from-parameter, - bugprone-shared-ptr-array-mismatch, - bugprone-signal-handler, - bugprone-signed-char-misuse, - bugprone-sizeof-container, - bugprone-sizeof-expression, - bugprone-spuriously-wake-up-functions, - bugprone-standalone-empty, - bugprone-string-constructor, - bugprone-string-integer-assignment, - bugprone-string-literal-with-embedded-nul, - bugprone-stringview-nullptr, - bugprone-suspicious-enum-usage, - bugprone-suspicious-include, - bugprone-suspicious-memory-comparison, - bugprone-suspicious-memset-usage, - bugprone-suspicious-missing-comma, - bugprone-suspicious-realloc-usage, - bugprone-suspicious-semicolon, - bugprone-suspicious-string-compare, - bugprone-suspicious-stringview-data-usage, - bugprone-swapped-arguments, - bugprone-switch-missing-default-case, - bugprone-terminating-continue, - bugprone-throw-keyword-missing, - bugprone-too-small-loop-variable, - bugprone-unchecked-optional-access, - bugprone-undefined-memory-manipulation, - bugprone-undelegated-constructor, - bugprone-unhandled-exception-at-new, - bugprone-unhandled-self-assignment, - bugprone-unique-ptr-array-mismatch, - bugprone-unsafe-functions, - bugprone-unused-local-non-trivial-variable, - bugprone-unused-raii, - bugprone-unused-return-value, - bugprone-use-after-move, - bugprone-virtual-near-miss, - cppcoreguidelines-init-variables, - cppcoreguidelines-misleading-capture-default-by-value, - cppcoreguidelines-no-suspend-with-lock, - cppcoreguidelines-pro-type-member-init, - cppcoreguidelines-pro-type-static-cast-downcast, - cppcoreguidelines-rvalue-reference-param-not-moved, - cppcoreguidelines-use-default-member-init, - cppcoreguidelines-use-enum-class, - cppcoreguidelines-virtual-class-destructor, - hicpp-ignored-remove-result, + bugprone-*, + -bugprone-easily-swappable-parameters, + -bugprone-exception-escape, + -bugprone-implicit-widening-of-multiplication-result, + -bugprone-narrowing-conversions, + -bugprone-throwing-static-initialization, + + cppcoreguidelines-*, + -cppcoreguidelines-avoid-c-arrays, + -cppcoreguidelines-avoid-const-or-ref-data-members, + -cppcoreguidelines-avoid-do-while, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-avoid-non-const-global-variables, + -cppcoreguidelines-c-copy-assignment-signature, + -cppcoreguidelines-interfaces-global-init, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-missing-std-forward, + -cppcoreguidelines-narrowing-conversions, + -cppcoreguidelines-noexcept-move-operations, + -cppcoreguidelines-non-private-member-variables-in-classes, + -cppcoreguidelines-owning-memory, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-bounds-avoid-unchecked-container-access, + -cppcoreguidelines-pro-bounds-constant-array-index, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-pro-type-reinterpret-cast, + -cppcoreguidelines-pro-type-union-access, + -cppcoreguidelines-special-member-functions, + llvm-namespace-comment, - misc-const-correctness, - misc-definitions-in-headers, - misc-header-include-cycle, - misc-include-cleaner, - misc-misplaced-const, - misc-redundant-expression, - misc-static-assert, - misc-throw-by-value-catch-by-reference, - misc-unused-alias-decls, - misc-unused-using-decls, - modernize-concat-nested-namespaces, - modernize-deprecated-headers, - modernize-make-shared, - modernize-make-unique, - modernize-pass-by-value, - modernize-type-traits, - modernize-use-designated-initializers, - modernize-use-emplace, - modernize-use-equals-default, - modernize-use-equals-delete, - modernize-use-nodiscard, - modernize-use-override, - modernize-use-ranges, - modernize-use-scoped-lock, - modernize-use-starts-ends-with, - modernize-use-std-numbers, - modernize-use-using, - performance-faster-string-find, - performance-for-range-copy, - performance-implicit-conversion-in-loop, - performance-inefficient-vector-operation, - performance-move-const-arg, - performance-move-constructor-init, - performance-no-automatic-move, - performance-trivially-destructible, - readability-ambiguous-smartptr-reset-call, - readability-avoid-nested-conditional-operator, - readability-avoid-return-with-void-value, - readability-braces-around-statements, - readability-const-return-type, - readability-container-contains, - readability-container-size-empty, - readability-convert-member-functions-to-static, - readability-duplicate-include, - readability-else-after-return, - readability-enum-initial-value, - readability-identifier-naming, - readability-implicit-bool-conversion, - readability-make-member-function-const, - readability-math-missing-parentheses, - readability-misleading-indentation, - readability-non-const-parameter, - readability-redundant-casting, - readability-redundant-declaration, - readability-redundant-inline-specifier, - readability-redundant-member-init, - readability-redundant-string-init, - readability-reference-to-constructed-temporary, - readability-simplify-boolean-expr, - readability-static-definition-in-anonymous-namespace, - readability-suspicious-call-argument, - readability-use-std-min-max + + misc-*, + -misc-multiple-inheritance, + -misc-no-recursion, + -misc-non-private-member-variables-in-classes, + -misc-override-with-different-visibility, + -misc-unused-parameters, + -misc-use-anonymous-namespace, + -misc-use-internal-linkage, + + modernize-*, + -modernize-avoid-c-arrays, + -modernize-avoid-c-style-cast, + -modernize-return-braced-init-list, + -modernize-use-integer-sign-comparison, + -modernize-use-trailing-return-type, + + performance-*, + -performance-avoid-endl, + -performance-enum-size, + -performance-noexcept-move-constructor, + -performance-unnecessary-copy-initialization, + -performance-unnecessary-value-param, + + readability-*, + -readability-avoid-const-params-in-decls, + -readability-avoid-unconditional-preprocessor-if, + -readability-container-data-pointer, + -readability-delete-null-pointer, + -readability-function-cognitive-complexity, + -readability-function-size, + -readability-identifier-length, + -readability-inconsistent-declaration-parameter-name, + -readability-isolate-declaration, + -readability-magic-numbers, + -readability-misplaced-array-index, + -readability-named-parameter, + -readability-operators-representation, + -readability-qualified-auto, + -readability-redundant-access-specifiers, + -readability-redundant-control-flow, + -readability-redundant-function-ptr-dereference, + -readability-redundant-preprocessor, + -readability-redundant-smartptr-get, + -readability-redundant-string-cstr, + -readability-simplify-subscript-expr, + -readability-static-accessed-through-instance, + -readability-string-compare, + -readability-uniqueptr-delete-release, + -readability-uppercase-literal-suffix, + -readability-use-anyofallof, + -readability-use-concise-preprocessor-directives " # --- -# readability-inconsistent-declaration-parameter-name, # in this codebase this check will break a lot of arg names -# readability-static-accessed-through-instance, # this check is probably unnecessary. it makes the code less readable +# bugprone-narrowing-conversions, # This will break a lot of code but we should enable it in the future because it can eliminate a lot of bugs +# misc-override-with-different-visibility, # Will be addressed in a future PR, but for now it generates too many warnings +# readability-inconsistent-declaration-parameter-name, # In this codebase this check will break a lot of arg names +# readability-static-accessed-through-instance, # this check is probably unnecessary. It makes the code less readable # --- CheckOptions: bugprone-unsafe-functions.ReportMoreUnsafeFunctions: true bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc - misc-include-cleaner.IgnoreHeaders: ".*/(detail|impl)/.*;.*fwd\\.h(pp)?;time.h;stdlib.h;sqlite3.h;netinet/in\\.h;sys/resource\\.h;sys/sysinfo\\.h;linux/sysinfo\\.h;__chrono/.*;bits/.*;_abort\\.h;boost/uuid/uuid_hash.hpp;boost/beast/core/flat_buffer\\.hpp;boost/beast/http/field\\.hpp;boost/beast/http/dynamic_body\\.hpp;boost/beast/http/message\\.hpp;boost/beast/http/read\\.hpp;boost/beast/http/write\\.hpp;openssl/obj_mac\\.h" + misc-include-cleaner.IgnoreHeaders: ".*/(detail|impl)/.*;.*fwd\\.h(pp)?;time.h;stdlib.h;sqlite3.h;netinet/in\\.h;sys/resource\\.h;sys/sysinfo\\.h;linux/sysinfo\\.h;__chrono/.*;bits/.*;_abort\\.h;boost/.*;openssl/obj_mac\\.h" readability-braces-around-statements.ShortStatementLines: 2 readability-identifier-naming.MacroDefinitionCase: UPPER_CASE @@ -191,11 +124,14 @@ CheckOptions: readability-identifier-naming.ParameterCase: camelBack readability-identifier-naming.FunctionCase: camelBack readability-identifier-naming.MemberCase: camelBack + readability-identifier-naming.PrivateMemberCase: camelBack readability-identifier-naming.PrivateMemberSuffix: _ + readability-identifier-naming.ProtectedMemberCase: camelBack readability-identifier-naming.ProtectedMemberSuffix: _ + readability-identifier-naming.PublicMemberCase: camelBack readability-identifier-naming.PublicMemberSuffix: "" readability-identifier-naming.GlobalFunctionIgnoredRegexp: "^(to_string|hash_append|tuple_hash)$" -HeaderFilterRegex: '^.*/(test|xrpl|xrpld)/.*\.(h|hpp|ipp)$' +HeaderFilterRegex: '^.*/(tests?|xrpl|xrpld)/.*\.(h|hpp|ipp)$' ExcludeHeaderFilterRegex: '^.*/protocol_autogen/.*\.(h|hpp)$' WarningsAsErrors: "*" diff --git a/cspell.config.yaml b/.cspell.config.yaml similarity index 93% rename from cspell.config.yaml rename to .cspell.config.yaml index 275df41f58..c558fc0984 100644 --- a/cspell.config.yaml +++ b/.cspell.config.yaml @@ -36,9 +36,7 @@ overrides: - /'[^']*'/g # single-quoted strings - /`[^`]*`/g # backtick strings suggestWords: - - xprl->xrpl - - xprld->xrpld # cspell: disable-line not sure what this problem is.... - - unsynched->unsynced # cspell: disable-line not sure what this problem is.... + - unsynched->unsynced - synched->synced - synch->sync words: @@ -50,6 +48,7 @@ words: - AMMXRP - amt - amts + - archs - asnode - asynchrony - attestation @@ -59,6 +58,7 @@ words: - autobridging - bimap - bindir + - blindings - bookdir - Bougalis - Britto @@ -83,6 +83,7 @@ words: - coro - coros - cowid + - cpack - cryptocondition - cryptoconditional - cryptoconditions @@ -93,6 +94,8 @@ words: - daria - dcmake - dearmor + - decryptor + - dedented - deleteme - demultiplexer - deserializaton @@ -103,9 +106,11 @@ words: - distro - doxyfile - dxrpl + - elgamal - enabled - enablerepo - endmacro + - envrc - exceptioned - EXPECT_STREQ - Falco @@ -115,6 +120,7 @@ words: - fmtdur - fsanitize - funclets + - Gamal - gcov - gcovr - ghead @@ -133,6 +139,7 @@ words: - iou - ious - isrdc + - isystem - itype - jemalloc - jlog @@ -199,16 +206,19 @@ words: - nonxrp - noreplace - noripple + - nostdinc - notifempty - nudb - nullptr - nunl - Nyffenegger + - onlatest - ostr - pargs - partitioner - paychan - paychans + - Pedersen - permdex - perminute - permissioned @@ -227,9 +237,15 @@ words: - pyenv - pyparsing - qalloc + - qbsprofile - queuable - Raphson + - rcflags - replayer + - rerandomize + - rerandomization + - rerandomized + - rerandomizes - rerere - retriable - RIPD @@ -246,6 +262,7 @@ words: - sahyadri - Satoshi - scons + - Schnorr - secp - sendq - seqit @@ -254,6 +271,7 @@ words: - sfields - shamap - shamapitem + - shfmt - shlibs - sidechain - SIGGOOD @@ -275,6 +293,7 @@ words: - stvar - stvector - stxchainattestations + - summands - superpeer - superpeers - takergets @@ -291,6 +310,7 @@ words: - txs - ubsan - UBSAN + - ufdio - umant - unacquired - unambiguity @@ -298,6 +318,7 @@ words: - unauthorizing - unergonomic - unfetched + - unfindable - unflatten - unfund - unimpair diff --git a/.envrc b/.envrc new file mode 100644 index 0000000000..3550a30f2d --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gersemi/definitions.cmake b/.gersemi/definitions.cmake index a16e330ffa..aa63076c8b 100644 --- a/.gersemi/definitions.cmake +++ b/.gersemi/definitions.cmake @@ -11,9 +11,6 @@ endfunction() function(create_symbolic_link target link) endfunction() -function(xrpl_add_test name) -endfunction() - macro(exclude_from_default target_) endmacro() @@ -51,6 +48,12 @@ endfunction() function(add_module parent name) endfunction() +function(verify_target_headers target headers_dir) +endfunction() + +function(_verify_add_headers target dir) +endfunction() + function(setup_protocol_autogen) endfunction() @@ -99,3 +102,6 @@ function(verbose_find_path variable name) ${ARGN} ) endfunction() + +function(patch_nix_binary target) +endfunction() diff --git a/.github/actions/build-deps/action.yml b/.github/actions/build-deps/action.yml index 9d52be1998..044c264ef0 100644 --- a/.github/actions/build-deps/action.yml +++ b/.github/actions/build-deps/action.yml @@ -35,14 +35,13 @@ runs: LOG_VERBOSITY: ${{ inputs.log_verbosity }} SANITIZERS: ${{ inputs.sanitizers }} run: | - echo 'Installing dependencies.' conan install \ - --profile ci \ - --build="${BUILD_OPTION}" \ - --options:host='&:tests=True' \ - --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}" \ - . + --profile:all ci \ + --build="${BUILD_OPTION}" \ + --options:host='&:tests=True' \ + --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}" \ + . diff --git a/.github/actions/generate-version/action.yml b/.github/actions/generate-version/action.yml index 8edb7920c6..50b3166596 100644 --- a/.github/actions/generate-version/action.yml +++ b/.github/actions/generate-version/action.yml @@ -15,7 +15,7 @@ runs: shell: bash env: VERSION: ${{ github.ref_name }} - run: echo "VERSION=${VERSION}" >> "${GITHUB_ENV}" + run: echo "VERSION=${VERSION}" >>"${GITHUB_ENV}" # When a tag is not pushed, then the version (e.g. 1.2.3-b0) is extracted # from the BuildInfo.cpp file and the shortened commit hash appended to it. @@ -28,17 +28,17 @@ runs: echo 'Extracting version from BuildInfo.cpp.' VERSION="$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')" if [[ -z "${VERSION}" ]]; then - echo 'Unable to extract version from BuildInfo.cpp.' - exit 1 + echo 'Unable to extract version from BuildInfo.cpp.' + exit 1 fi echo 'Appending shortened commit hash to version.' SHA='${{ github.sha }}' VERSION="${VERSION}+${SHA:0:7}" - echo "VERSION=${VERSION}" >> "${GITHUB_ENV}" + echo "VERSION=${VERSION}" >>"${GITHUB_ENV}" - name: Output version id: version shell: bash - run: echo "version=${VERSION}" >> "${GITHUB_OUTPUT}" + run: echo "version=${VERSION}" >>"${GITHUB_OUTPUT}" diff --git a/.github/actions/set-compiler-env/action.yml b/.github/actions/set-compiler-env/action.yml new file mode 100644 index 0000000000..a16dde2b30 --- /dev/null +++ b/.github/actions/set-compiler-env/action.yml @@ -0,0 +1,34 @@ +name: Set compiler environment +description: "Set CC and CXX environment variables for the given compiler." + +inputs: + compiler: + description: 'The compiler to use ("gcc" or "clang").' + required: true + +runs: + using: composite + + steps: + - name: Set CC and CXX for gcc + if: ${{ inputs.compiler == 'gcc' }} + shell: bash + run: | + echo "CC=gcc" >>"${GITHUB_ENV}" + echo "CXX=g++" >>"${GITHUB_ENV}" + + - name: Set CC and CXX for clang + if: ${{ inputs.compiler == 'clang' }} + shell: bash + run: | + echo "CC=clang" >>"${GITHUB_ENV}" + echo "CXX=clang++" >>"${GITHUB_ENV}" + + - name: Fail on unknown compiler + if: ${{ inputs.compiler != 'gcc' && inputs.compiler != 'clang' }} + shell: bash + env: + COMPILER: ${{ inputs.compiler }} + run: | + echo "Unknown compiler: $COMPILER" >&2 + exit 1 diff --git a/.github/actions/setup-conan/action.yml b/.github/actions/setup-conan/action.yml index 9d834884d2..e8a548cfce 100644 --- a/.github/actions/setup-conan/action.yml +++ b/.github/actions/setup-conan/action.yml @@ -9,38 +9,41 @@ inputs: remote_url: description: "The URL of the Conan endpoint to use." required: false - default: https://conan.ripplex.io + default: https://conan.xrplf.org/repository/conan/ runs: using: composite steps: - - name: Set up Conan configuration + - name: Apply custom configuration to global.conf shell: bash run: | - echo 'Installing configuration.' cat conan/global.conf ${{ runner.os == 'Linux' && '>>' || '>' }} $(conan config home)/global.conf - echo 'Conan configuration:' - conan config show '*' - - - name: Set up Conan profile + - name: Show global configuration + shell: bash + run: | + conan config show '*' + + - name: Install profiles shell: bash run: | - echo 'Installing profile.' conan config install conan/profiles/ -tf $(conan config home)/profiles/ - echo 'Conan profile:' + - name: Show CI profile + shell: bash + run: | conan profile show --profile ci - - name: Set up Conan remote + - name: Add a remote shell: bash env: REMOTE_NAME: ${{ inputs.remote_name }} REMOTE_URL: ${{ inputs.remote_url }} run: | - echo "Adding Conan remote '${REMOTE_NAME}' at '${REMOTE_URL}'." conan remote add --index 0 --force "${REMOTE_NAME}" "${REMOTE_URL}" - echo 'Listing Conan remotes.' + - name: List remotes + shell: bash + run: | conan remote list diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0e6b840fe7..da7a30dc77 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,40 +1,12 @@ version: 2 updates: - package-ecosystem: github-actions - directory: / - schedule: - interval: weekly - day: monday - time: "04:00" - timezone: Etc/GMT - commit-message: - prefix: "ci: [DEPENDABOT] " - target-branch: develop - - - package-ecosystem: github-actions - directory: .github/actions/build-deps/ - schedule: - interval: weekly - day: monday - time: "04:00" - timezone: Etc/GMT - commit-message: - prefix: "ci: [DEPENDABOT] " - target-branch: develop - - - package-ecosystem: github-actions - directory: .github/actions/generate-version/ - schedule: - interval: weekly - day: monday - time: "04:00" - timezone: Etc/GMT - commit-message: - prefix: "ci: [DEPENDABOT] " - target-branch: develop - - - package-ecosystem: github-actions - directory: .github/actions/setup-conan/ + directories: + - / + - .github/actions/build-deps/ + - .github/actions/generate-version/ + - .github/actions/set-compiler-env/ + - .github/actions/setup-conan/ schedule: interval: weekly day: monday diff --git a/.github/scripts/format-inline-bash.py b/.github/scripts/format-inline-bash.py new file mode 100755 index 0000000000..423c78109c --- /dev/null +++ b/.github/scripts/format-inline-bash.py @@ -0,0 +1,403 @@ +#!/usr/bin/env python3 + +""" +Format embedded shell snippets using the shfmt hook configured in +.pre-commit-config.yaml. + +Two shapes are recognised: + +* YAML workflow/action files: literal block-scalar runs (`run: |`) and + single-line runs (`run: some command`). A single-line run is upgraded to + a `run: |` block scalar if shfmt's output spans multiple lines. + +* Markdown files: ``` ```bash ``` fenced code blocks. + +Any block that shfmt cannot parse is skipped with a warning on stderr, so +the file is left untouched and surrounding blocks still get formatted. + +For each occurrence the body is dedented, written to a temp .sh file, +formatted via `pre-commit run shfmt --files ` (falling back to +`prek`), then re-indented and written back in place. + +When invoked without arguments, every .yml/.yaml under .github/ plus every +.md file in the repo is scanned. When invoked with file arguments (the +pre-commit case), only those files are processed. +""" + +from __future__ import annotations + +import re +import shutil +import subprocess +import sys +import tempfile +from dataclasses import dataclass +from pathlib import Path +from typing import Union + +REPO = Path(__file__).resolve().parents[2] + +_HOOK_RUNNER = next((cmd for cmd in ("pre-commit", "prek") if shutil.which(cmd)), None) +if _HOOK_RUNNER is None: + sys.exit("error: neither `pre-commit` nor `prek` found on PATH") + +RUN_BLOCK_RE = re.compile(r"^(?P[ \t]*(?:- )?)run:[ \t]*\|[+-]?[ \t]*$") +RUN_INLINE_RE = re.compile( + r"^(?P[ \t]*(?:- )?)run:[ \t]+" r"(?P(?!\|[+-]?[ \t]*$)\S.*?)[ \t]*$" +) +MD_BASH_OPEN_RE = re.compile(r"^(?P[ ]{0,3})`{3}bash[ \t]*$") +MD_FENCE_CLOSE_RE = re.compile(r"^[ ]{0,3}`{3,}[ \t]*$") + + +@dataclass(frozen=True) +class BlockRun: + """A `run: |` block scalar; `body_start:body_end` slices into `lines`.""" + + body_start: int + body_end: int + body_indent: int + + +@dataclass(frozen=True) +class InlineRun: + """A single-line `run: value` at `line_idx`.""" + + line_idx: int + prefix: str + value: str + + +@dataclass(frozen=True) +class MdBashBlock: + """A markdown ``` ```bash ``` fenced code block. + + `body_start:body_end` slices into the file's lines; `open_line_idx` + points at the opening fence line. + """ + + open_line_idx: int + body_start: int + body_end: int + body_indent: int + + +RunItem = Union[BlockRun, InlineRun] + + +def _scan_block_body( + lines: list[str], body_start: int, run_col: int +) -> tuple[int | None, int]: + """Locate the body of a `run: |` block scalar starting at `body_start`. + + Returns `(body_indent, scan_end)`. `scan_end` is the line index where the + outer scanner should resume. `body_indent` is `None` when no body is + present (the scalar is empty, or the next non-blank line has indent + `<= run_col`). + """ + body_indent: int | None = None + scan_end = len(lines) + for idx in range(body_start, len(lines)): + line = lines[idx] + if line.strip() == "": + continue + indent = len(line) - len(line.lstrip(" ")) + if body_indent is None: + if indent > run_col: + body_indent = indent + else: + scan_end = idx + break + elif indent < body_indent: + scan_end = idx + break + if body_indent is not None: + while scan_end > body_start and lines[scan_end - 1].strip() == "": + scan_end -= 1 + if scan_end <= body_start: + body_indent = None + return body_indent, scan_end + + +def find_run_blocks(lines: list[str]) -> list[RunItem]: + """Return run items in document order.""" + items: list[RunItem] = [] + line_idx = 0 + while line_idx < len(lines): + line = lines[line_idx] + if block_match := RUN_BLOCK_RE.match(line): + run_col = len(block_match.group("prefix")) + body_start = line_idx + 1 + body_indent, scan_end = _scan_block_body(lines, body_start, run_col) + if body_indent is not None: + items.append( + BlockRun( + body_start=body_start, + body_end=scan_end, + body_indent=body_indent, + ) + ) + line_idx = scan_end + continue + if inline_match := RUN_INLINE_RE.match(line): + items.append( + InlineRun( + line_idx=line_idx, + prefix=inline_match.group("prefix"), + value=inline_match.group("value"), + ) + ) + line_idx += 1 + return items + + +def find_md_bash_blocks(lines: list[str]) -> list[MdBashBlock]: + """Return ``` ```bash ``` fenced code blocks in document order.""" + blocks: list[MdBashBlock] = [] + line_idx = 0 + while line_idx < len(lines): + open_match = MD_BASH_OPEN_RE.match(lines[line_idx]) + if not open_match: + line_idx += 1 + continue + body_start = line_idx + 1 + close_idx = next( + ( + j + for j in range(body_start, len(lines)) + if MD_FENCE_CLOSE_RE.match(lines[j]) + ), + None, + ) + if close_idx is None: + line_idx = body_start + continue + body = lines[body_start:close_idx] + non_blank = [b for b in body if b.strip()] + body_indent = ( + min(len(b) - len(b.lstrip(" ")) for b in non_blank) + if non_blank + else len(open_match.group("indent")) + ) + blocks.append( + MdBashBlock( + open_line_idx=line_idx, + body_start=body_start, + body_end=close_idx, + body_indent=body_indent, + ) + ) + line_idx = close_idx + 1 + return blocks + + +def dedent(lines: list[str], n: int) -> list[str]: + pad = " " * n + return [ + ( + "" + if line.strip() == "" + else (line[n:] if line.startswith(pad) else line.lstrip(" ")) + ) + for line in lines + ] + + +def reindent(lines: list[str], n: int) -> list[str]: + pad = " " * n + return [pad + line if line else "" for line in lines] + + +_SHFMT_ERR_RE = re.compile(r"\.sh:\d+:\d+:\s") +_GHA_EXPR_RE = re.compile(r"\$\{\{.*?\}\}", re.DOTALL) +_GHA_PLACEHOLDER_RE = re.compile(r"__GHA_EXPR_(\d+)__") + + +def _encode_gha_exprs(text: str) -> tuple[str, list[str]]: + """Replace `${{ ... }}` expressions with bash-safe placeholder identifiers.""" + exprs: list[str] = [] + + def repl(match: re.Match[str]) -> str: + exprs.append(match.group(0)) + return f"__GHA_EXPR_{len(exprs) - 1}__" + + return _GHA_EXPR_RE.sub(repl, text), exprs + + +def _decode_gha_exprs(text: str, exprs: list[str]) -> str: + """Restore `${{ ... }}` expressions from placeholder identifiers.""" + return _GHA_PLACEHOLDER_RE.sub(lambda m: exprs[int(m.group(1))], text) + + +def shfmt_via_hook(tmp_path: Path) -> tuple[bool, str]: + # `${{ ... }}` is not valid shell, so swap it for a placeholder identifier + # that shfmt can parse, then restore it after formatting. + encoded, exprs = _encode_gha_exprs(tmp_path.read_text()) + if exprs: + tmp_path.write_text(encoded) + res = subprocess.run( + [_HOOK_RUNNER, "run", "shfmt", "--files", str(tmp_path)], + cwd=REPO, + capture_output=True, + text=True, + ) + output = res.stdout + res.stderr + # shfmt emits parse errors as "::: ". + parse_err = bool(_SHFMT_ERR_RE.search(output)) + # A non-zero exit that is neither a parse error nor pre-commit's "I had + # to modify files" signal means the hook itself failed to run (missing + # binary, install failure, bad config, ...). Surface that loudly rather + # than silently treating it as a no-op. + if ( + res.returncode != 0 + and not parse_err + and "files were modified by this hook" not in output + ): + sys.exit( + f"error: `{_HOOK_RUNNER} run shfmt` failed with exit {res.returncode}:\n{output}" + ) + if exprs and not parse_err: + tmp_path.write_text(_decode_gha_exprs(tmp_path.read_text(), exprs)) + return not parse_err, output + + +def _skip(path: Path, where: int, kind: str, output: str) -> None: + print( + f" shfmt could not parse {kind} at {path}:{where + 1} — skipped", + file=sys.stderr, + ) + print(f" {output.strip()}", file=sys.stderr) + + +def process_yaml_file(path: Path, tmp_path: Path) -> int: + text = path.read_text() + had_nl = text.endswith("\n") + lines = text.split("\n") + if had_nl: + lines = lines[:-1] + items = find_run_blocks(lines) + if not items: + return 0 + changed = 0 + # Process in reverse so earlier indices remain valid as we splice. + for item in reversed(items): + if isinstance(item, BlockRun): + body = lines[item.body_start : item.body_end] + tmp_path.write_text("\n".join(dedent(body, item.body_indent)) + "\n") + ok, output = shfmt_via_hook(tmp_path) + if not ok: + _skip(path, item.body_start, "block", output) + continue + formatted = tmp_path.read_text().rstrip("\n") + new_body = reindent(formatted.split("\n"), item.body_indent) + if new_body != body: + lines[item.body_start : item.body_end] = new_body + changed += 1 + else: + tmp_path.write_text(item.value + "\n") + ok, output = shfmt_via_hook(tmp_path) + if not ok: + _skip(path, item.line_idx, "inline run", output) + continue + formatted = tmp_path.read_text().rstrip("\n") + if formatted == item.value: + continue + formatted_lines = formatted.split("\n") + if len(formatted_lines) == 1: + lines[item.line_idx] = f"{item.prefix}run: {formatted}" + else: + body_indent = len(item.prefix) + 2 + lines[item.line_idx : item.line_idx + 1] = [ + f"{item.prefix}run: |", + *reindent(formatted_lines, body_indent), + ] + changed += 1 + new_text = "\n".join(lines) + ("\n" if had_nl else "") + if new_text != text: + path.write_text(new_text) + return changed + + +def process_md_file(path: Path, tmp_path: Path) -> int: + text = path.read_text() + had_nl = text.endswith("\n") + lines = text.split("\n") + if had_nl: + lines = lines[:-1] + blocks = find_md_bash_blocks(lines) + if not blocks: + return 0 + changed = 0 + for block in reversed(blocks): + body = lines[block.body_start : block.body_end] + tmp_path.write_text("\n".join(dedent(body, block.body_indent)) + "\n") + ok, output = shfmt_via_hook(tmp_path) + if not ok: + _skip(path, block.open_line_idx, "```bash block", output) + continue + formatted = tmp_path.read_text().rstrip("\n") + formatted_lines = formatted.split("\n") if formatted else [] + new_body = reindent(formatted_lines, block.body_indent) + if new_body != body: + lines[block.body_start : block.body_end] = new_body + changed += 1 + new_text = "\n".join(lines) + ("\n" if had_nl else "") + if new_text != text: + path.write_text(new_text) + return changed + + +def process_file(path: Path, tmp_path: Path) -> int: + if path.suffix in (".yml", ".yaml"): + return process_yaml_file(path, tmp_path) + if path.suffix == ".md": + return process_md_file(path, tmp_path) + return 0 + + +def gather_files(argv: list[str]) -> list[Path]: + """Return YAML workflow/action files and markdown files that we should + process — either the paths in `argv` or, when `argv` is empty, every + such file in the repo (skipping `external/`).""" + if argv: + candidates: list[Path] = [ + (REPO / a).resolve() if not Path(a).is_absolute() else Path(a) for a in argv + ] + else: + gh = REPO / ".github" + candidates = [ + *gh.rglob("*.yml"), + *gh.rglob("*.yaml"), + *( + p + for p in REPO.rglob("*.md") + if "external" not in p.relative_to(REPO).parts + ), + ] + return sorted( + p + for p in candidates + if p.exists() + and ( + (p.suffix in (".yml", ".yaml") and ".github" in p.parts) + or p.suffix == ".md" + ) + ) + + +def main(argv: list[str]) -> int: + files = gather_files(argv) + if not files: + return 0 + with tempfile.TemporaryDirectory(prefix="format-inline-bash-") as tmpdir: + tmp_path = Path(tmpdir) / "shfmt.sh" + total = 0 + for f in files: + n = process_file(f, tmp_path) + if n: + print(f"{f.relative_to(REPO)}: reformatted {n} block(s)") + total += n + return 1 if total else 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/.github/scripts/levelization/generate.py b/.github/scripts/levelization/generate.py old mode 100644 new mode 100755 diff --git a/.github/scripts/levelization/results/loops.txt b/.github/scripts/levelization/results/loops.txt index fb449441e3..cf70468e32 100644 --- a/.github/scripts/levelization/results/loops.txt +++ b/.github/scripts/levelization/results/loops.txt @@ -1,9 +1,3 @@ -Loop: test.jtx test.toplevel - test.toplevel > test.jtx - -Loop: test.jtx test.unit_test - test.unit_test ~= test.jtx - Loop: xrpld.app xrpld.overlay xrpld.app > xrpld.overlay diff --git a/.github/scripts/levelization/results/ordering.txt b/.github/scripts/levelization/results/ordering.txt index c2000d1768..aee6f4c579 100644 --- a/.github/scripts/levelization/results/ordering.txt +++ b/.github/scripts/levelization/results/ordering.txt @@ -1,6 +1,8 @@ libxrpl.basics > xrpl.basics libxrpl.conditions > xrpl.basics libxrpl.conditions > xrpl.conditions +libxrpl.config > xrpl.basics +libxrpl.config > xrpl.config libxrpl.core > xrpl.basics libxrpl.core > xrpl.core libxrpl.core > xrpl.json @@ -12,11 +14,11 @@ libxrpl.ledger > xrpl.json libxrpl.ledger > xrpl.ledger libxrpl.ledger > xrpl.nodestore libxrpl.ledger > xrpl.protocol -libxrpl.ledger > xrpl.server libxrpl.ledger > xrpl.shamap libxrpl.net > xrpl.basics libxrpl.net > xrpl.net libxrpl.nodestore > xrpl.basics +libxrpl.nodestore > xrpl.config libxrpl.nodestore > xrpl.json libxrpl.nodestore > xrpl.nodestore libxrpl.nodestore > xrpl.protocol @@ -24,6 +26,7 @@ libxrpl.protocol > xrpl.basics libxrpl.protocol > xrpl.json libxrpl.protocol > xrpl.protocol libxrpl.rdb > xrpl.basics +libxrpl.rdb > xrpl.config libxrpl.rdb > xrpl.core libxrpl.rdb > xrpl.rdb libxrpl.resource > xrpl.basics @@ -31,6 +34,7 @@ libxrpl.resource > xrpl.json libxrpl.resource > xrpl.protocol libxrpl.resource > xrpl.resource libxrpl.server > xrpl.basics +libxrpl.server > xrpl.config libxrpl.server > xrpl.core libxrpl.server > xrpl.json libxrpl.server > xrpl.protocol @@ -52,6 +56,7 @@ libxrpl.tx > xrpl.tx test.app > test.jtx test.app > test.unit_test test.app > xrpl.basics +test.app > xrpl.config test.app > xrpl.core test.app > xrpld.app test.app > xrpld.consensus @@ -67,7 +72,6 @@ test.app > xrpl.server test.app > xrpl.shamap test.app > xrpl.tx test.basics > test.jtx -test.basics > test.unit_test test.basics > xrpl.basics test.basics > xrpl.core test.basics > xrpld.rpc @@ -78,7 +82,6 @@ test.conditions > xrpl.basics test.conditions > xrpl.conditions test.consensus > test.csf test.consensus > test.jtx -test.consensus > test.toplevel test.consensus > test.unit_test test.consensus > xrpl.basics test.consensus > xrpld.app @@ -90,6 +93,7 @@ test.consensus > xrpl.tx test.core > test.jtx test.core > test.unit_test test.core > xrpl.basics +test.core > xrpl.config test.core > xrpl.core test.core > xrpld.core test.core > xrpl.json @@ -100,10 +104,11 @@ test.csf > xrpl.basics test.csf > xrpld.consensus test.csf > xrpl.json test.csf > xrpl.ledger -test.csf > xrpl.protocol test.json > test.jtx test.json > xrpl.json +test.jtx > test.unit_test test.jtx > xrpl.basics +test.jtx > xrpl.config test.jtx > xrpl.core test.jtx > xrpld.app test.jtx > xrpld.core @@ -126,6 +131,7 @@ test.ledger > xrpl.protocol test.nodestore > test.jtx test.nodestore > test.unit_test test.nodestore > xrpl.basics +test.nodestore > xrpl.config test.nodestore > xrpld.core test.nodestore > xrpl.nodestore test.nodestore > xrpl.protocol @@ -133,6 +139,7 @@ test.nodestore > xrpl.rdb test.overlay > test.jtx test.overlay > test.unit_test test.overlay > xrpl.basics +test.overlay > xrpl.config test.overlay > xrpld.app test.overlay > xrpld.core test.overlay > xrpld.overlay @@ -154,11 +161,9 @@ test.protocol > test.unit_test test.protocol > xrpl.basics test.protocol > xrpl.json test.protocol > xrpl.protocol -test.resource > test.unit_test -test.resource > xrpl.basics -test.resource > xrpl.resource test.rpc > test.jtx test.rpc > xrpl.basics +test.rpc > xrpl.config test.rpc > xrpl.core test.rpc > xrpld.app test.rpc > xrpld.core @@ -173,21 +178,16 @@ test.rpc > xrpl.tx test.server > test.jtx test.server > test.unit_test test.server > xrpl.basics +test.server > xrpl.config test.server > xrpld.app test.server > xrpld.core test.server > xrpl.json test.server > xrpl.protocol test.server > xrpl.server -test.shamap > test.unit_test -test.shamap > xrpl.basics -test.shamap > xrpl.nodestore -test.shamap > xrpl.protocol -test.shamap > xrpl.shamap -test.toplevel > test.csf -test.toplevel > xrpl.json test.unit_test > xrpl.basics test.unit_test > xrpl.protocol tests.libxrpl > xrpl.basics +tests.libxrpl > xrpl.config tests.libxrpl > xrpl.core tests.libxrpl > xrpl.json tests.libxrpl > xrpl.ledger @@ -195,21 +195,26 @@ tests.libxrpl > xrpl.net tests.libxrpl > xrpl.nodestore tests.libxrpl > xrpl.protocol tests.libxrpl > xrpl.protocol_autogen +tests.libxrpl > xrpl.resource tests.libxrpl > xrpl.server tests.libxrpl > xrpl.shamap tests.libxrpl > xrpl.tx xrpl.conditions > xrpl.basics xrpl.conditions > xrpl.protocol +xrpl.config > xrpl.basics xrpl.core > xrpl.basics xrpl.core > xrpl.json xrpl.core > xrpl.protocol xrpl.json > xrpl.basics xrpl.ledger > xrpl.basics +xrpl.ledger > xrpl.json +xrpl.ledger > xrpl.nodestore xrpl.ledger > xrpl.protocol -xrpl.ledger > xrpl.server xrpl.ledger > xrpl.shamap xrpl.net > xrpl.basics xrpl.nodestore > xrpl.basics +xrpl.nodestore > xrpl.config +xrpl.nodestore > xrpl.json xrpl.nodestore > xrpl.protocol xrpl.protocol > xrpl.basics xrpl.protocol > xrpl.json @@ -227,7 +232,6 @@ xrpl.server > xrpl.json xrpl.server > xrpl.protocol xrpl.server > xrpl.rdb xrpl.server > xrpl.resource -xrpl.server > xrpl.shamap xrpl.shamap > xrpl.basics xrpl.shamap > xrpl.nodestore xrpl.shamap > xrpl.protocol @@ -237,6 +241,7 @@ xrpl.tx > xrpl.ledger xrpl.tx > xrpl.protocol xrpld.app > test.unit_test xrpld.app > xrpl.basics +xrpld.app > xrpl.config xrpld.app > xrpl.core xrpld.app > xrpld.consensus xrpld.app > xrpld.core @@ -255,11 +260,13 @@ xrpld.consensus > xrpl.json xrpld.consensus > xrpl.ledger xrpld.consensus > xrpl.protocol xrpld.core > xrpl.basics +xrpld.core > xrpl.config xrpld.core > xrpl.core xrpld.core > xrpl.net xrpld.core > xrpl.protocol xrpld.core > xrpl.rdb xrpld.overlay > xrpl.basics +xrpld.overlay > xrpl.config xrpld.overlay > xrpl.core xrpld.overlay > xrpld.consensus xrpld.overlay > xrpld.core @@ -272,15 +279,20 @@ xrpld.overlay > xrpl.server xrpld.overlay > xrpl.shamap xrpld.overlay > xrpl.tx xrpld.peerfinder > xrpl.basics +xrpld.peerfinder > xrpl.config xrpld.peerfinder > xrpld.core xrpld.peerfinder > xrpl.protocol xrpld.peerfinder > xrpl.rdb xrpld.perflog > xrpl.basics +xrpld.perflog > xrpl.config xrpld.perflog > xrpl.core +xrpld.perflog > xrpld.app xrpld.perflog > xrpld.rpc xrpld.perflog > xrpl.json +xrpld.perflog > xrpl.nodestore xrpld.perflog > xrpl.protocol xrpld.rpc > xrpl.basics +xrpld.rpc > xrpl.config xrpld.rpc > xrpl.core xrpld.rpc > xrpld.core xrpld.rpc > xrpl.json @@ -295,5 +307,6 @@ xrpld.rpc > xrpl.shamap xrpld.rpc > xrpl.tx xrpld.shamap > xrpl.basics xrpld.shamap > xrpld.core +xrpld.shamap > xrpl.nodestore xrpld.shamap > xrpl.protocol xrpld.shamap > xrpl.shamap diff --git a/.github/scripts/rename/binary.sh b/.github/scripts/rename/binary.sh index cdce6db4ba..89d884538c 100755 --- a/.github/scripts/rename/binary.sh +++ b/.github/scripts/rename/binary.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi diff --git a/.github/scripts/rename/cmake.sh b/.github/scripts/rename/cmake.sh index 9c91e8f277..3539f563e0 100755 --- a/.github/scripts/rename/cmake.sh +++ b/.github/scripts/rename/cmake.sh @@ -8,12 +8,12 @@ set -e SED_COMMAND=sed HEAD_COMMAND=head if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi SED_COMMAND=gsed - if ! command -v ghead &> /dev/null; then + if ! command -v ghead &>/dev/null; then echo "Error: ghead is not installed. Please install it using 'brew install coreutils'." exit 1 fi @@ -43,9 +43,6 @@ pushd "${DIRECTORY}" # Rename the files. find cmake -type f -name 'Rippled*.cmake' -exec bash -c 'mv "${1}" "${1/Rippled/Xrpl}"' - {} \; find cmake -type f -name 'Ripple*.cmake' -exec bash -c 'mv "${1}" "${1/Ripple/Xrpl}"' - {} \; -if [ -e cmake/xrpl_add_test.cmake ]; then - mv cmake/xrpl_add_test.cmake cmake/XrplAddTest.cmake -fi if [ -e include/xrpl/proto/ripple.proto ]; then mv include/xrpl/proto/ripple.proto include/xrpl/proto/xrpl.proto fi @@ -60,7 +57,6 @@ find cmake -type f -name '*.cmake' | while read -r FILE; do done ${SED_COMMAND} -i -E 's/Rippled?/Xrpl/g' CMakeLists.txt ${SED_COMMAND} -i 's/ripple/xrpl/g' CMakeLists.txt -${SED_COMMAND} -i 's/include(xrpl_add_test)/include(XrplAddTest)/' src/tests/libxrpl/CMakeLists.txt ${SED_COMMAND} -i 's/ripple.pb.h/xrpl.pb.h/' include/xrpl/protocol/messages.h ${SED_COMMAND} -i 's/ripple.pb.h/xrpl.pb.h/' BUILD.md ${SED_COMMAND} -i 's/ripple.pb.h/xrpl.pb.h/' BUILD.md @@ -74,10 +70,10 @@ if grep -q '"xrpld"' cmake/XrplCore.cmake; then # The script has been rerun, so just restore the name of the binary. ${SED_COMMAND} -i 's/"xrpld"/"rippled"/' cmake/XrplCore.cmake elif ! grep -q '"rippled"' cmake/XrplCore.cmake; then - ${HEAD_COMMAND} -n -1 cmake/XrplCore.cmake > cmake.tmp - echo ' # For the time being, we will keep the name of the binary as it was.' >> cmake.tmp - echo ' set_target_properties(xrpld PROPERTIES OUTPUT_NAME "rippled")' >> cmake.tmp - tail -1 cmake/XrplCore.cmake >> cmake.tmp + ${HEAD_COMMAND} -n -1 cmake/XrplCore.cmake >cmake.tmp + echo ' # For the time being, we will keep the name of the binary as it was.' >>cmake.tmp + echo ' set_target_properties(xrpld PROPERTIES OUTPUT_NAME "rippled")' >>cmake.tmp + tail -1 cmake/XrplCore.cmake >>cmake.tmp mv cmake.tmp cmake/XrplCore.cmake fi diff --git a/.github/scripts/rename/config.sh b/.github/scripts/rename/config.sh index 81edcc73d6..ac9debb154 100755 --- a/.github/scripts/rename/config.sh +++ b/.github/scripts/rename/config.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi diff --git a/.github/scripts/rename/copyright.sh b/.github/scripts/rename/copyright.sh index 9ebdad1e89..09bc5a8926 100755 --- a/.github/scripts/rename/copyright.sh +++ b/.github/scripts/rename/copyright.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi @@ -62,37 +62,37 @@ done # restoring the verbiage that is already present in LICENSE.md. Ensure that if # the script is run multiple times, duplicate notices are not added. if ! grep -q 'Raw Material Software' include/xrpl/beast/core/CurrentThreadName.h; then - echo -e "// Portions of this file are from JUCE (http://www.juce.com).\n// Copyright (c) 2013 - Raw Material Software Ltd.\n// Please visit http://www.juce.com\n\n$(cat include/xrpl/beast/core/CurrentThreadName.h)" > include/xrpl/beast/core/CurrentThreadName.h + echo -e "// Portions of this file are from JUCE (http://www.juce.com).\n// Copyright (c) 2013 - Raw Material Software Ltd.\n// Please visit http://www.juce.com\n\n$(cat include/xrpl/beast/core/CurrentThreadName.h)" >include/xrpl/beast/core/CurrentThreadName.h fi if ! grep -q 'Dev Null' src/test/app/NetworkID_test.cpp; then - echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/NetworkID_test.cpp)" > src/test/app/NetworkID_test.cpp + echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/NetworkID_test.cpp)" >src/test/app/NetworkID_test.cpp fi if ! grep -q 'Dev Null' src/test/app/tx/apply_test.cpp; then - echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/tx/apply_test.cpp)" > src/test/app/tx/apply_test.cpp + echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/tx/apply_test.cpp)" >src/test/app/tx/apply_test.cpp fi if ! grep -q 'Dev Null' src/test/rpc/ManifestRPC_test.cpp; then - echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ManifestRPC_test.cpp)" > src/test/rpc/ManifestRPC_test.cpp + echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ManifestRPC_test.cpp)" >src/test/rpc/ManifestRPC_test.cpp fi if ! grep -q 'Dev Null' src/test/rpc/ValidatorInfo_test.cpp; then - echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ValidatorInfo_test.cpp)" > src/test/rpc/ValidatorInfo_test.cpp + echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ValidatorInfo_test.cpp)" >src/test/rpc/ValidatorInfo_test.cpp fi if ! grep -q 'Dev Null' src/xrpld/rpc/handlers/server_info/Manifest.cpp; then - echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/server_info/Manifest.cpp)" > src/xrpld/rpc/handlers/server_info/Manifest.cpp + echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/server_info/Manifest.cpp)" >src/xrpld/rpc/handlers/server_info/Manifest.cpp fi if ! grep -q 'Dev Null' src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp; then - echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp)" > src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp + echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp)" >src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp fi if ! grep -q 'Bougalis' include/xrpl/basics/SlabAllocator.h; then - echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/SlabAllocator.h)" > include/xrpl/basics/SlabAllocator.h # cspell: ignore Nikolaos Bougalis nikb + echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/SlabAllocator.h)" >include/xrpl/basics/SlabAllocator.h # cspell: ignore Nikolaos Bougalis nikb fi if ! grep -q 'Bougalis' include/xrpl/basics/spinlock.h; then - echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/spinlock.h)" > include/xrpl/basics/spinlock.h # cspell: ignore Nikolaos Bougalis nikb + echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/spinlock.h)" >include/xrpl/basics/spinlock.h # cspell: ignore Nikolaos Bougalis nikb fi if ! grep -q 'Bougalis' include/xrpl/basics/tagged_integer.h; then - echo -e "// Copyright (c) 2014, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/tagged_integer.h)" > include/xrpl/basics/tagged_integer.h # cspell: ignore Nikolaos Bougalis nikb + echo -e "// Copyright (c) 2014, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/tagged_integer.h)" >include/xrpl/basics/tagged_integer.h # cspell: ignore Nikolaos Bougalis nikb fi if ! grep -q 'Ritchford' include/xrpl/beast/utility/Zero.h; then - echo -e "// Copyright (c) 2014, Tom Ritchford \n\n$(cat include/xrpl/beast/utility/Zero.h)" > include/xrpl/beast/utility/Zero.h # cspell: ignore Ritchford + echo -e "// Copyright (c) 2014, Tom Ritchford \n\n$(cat include/xrpl/beast/utility/Zero.h)" >include/xrpl/beast/utility/Zero.h # cspell: ignore Ritchford fi # Restore newlines and tabs in string literals in the affected file. diff --git a/.github/scripts/rename/definitions.sh b/.github/scripts/rename/definitions.sh index 5e004afe39..daa5d01e80 100755 --- a/.github/scripts/rename/definitions.sh +++ b/.github/scripts/rename/definitions.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi diff --git a/.github/scripts/rename/docs.sh b/.github/scripts/rename/docs.sh index 8b7a362405..9f080b06e5 100755 --- a/.github/scripts/rename/docs.sh +++ b/.github/scripts/rename/docs.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi diff --git a/.github/scripts/rename/namespace.sh b/.github/scripts/rename/namespace.sh index aba193b0cf..bb186bc8bc 100755 --- a/.github/scripts/rename/namespace.sh +++ b/.github/scripts/rename/namespace.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi diff --git a/.github/scripts/strategy-matrix/generate.py b/.github/scripts/strategy-matrix/generate.py index 6eccfcc6be..a269cb25d4 100755 --- a/.github/scripts/strategy-matrix/generate.py +++ b/.github/scripts/strategy-matrix/generate.py @@ -1,384 +1,322 @@ #!/usr/bin/env python3 import argparse +import dataclasses import itertools import json -from dataclasses import dataclass from pathlib import Path THIS_DIR = Path(__file__).parent.resolve() +_BASE_CMAKE_ARGS = ["-Dtests=ON", "-Dwerr=ON", "-Dxrpld=ON", "-Dwextra=ON"] -@dataclass -class Config: - architecture: list[dict] - os: list[dict] - build_type: list[str] - cmake_args: list[str] +# Maps sanitizer names (as used in cmake) to short config-name suffixes. +_SANITIZER_SUFFIX: dict[str, str] = { + "address": "asan", + "undefinedbehavior": "ubsan", + "thread": "tsan", +} -""" -Generate a strategy matrix for GitHub Actions CI. - -On each PR commit we will build a selection of Debian, RHEL, Ubuntu, MacOS, and -Windows configurations, while upon merge into the develop or release branches, -we will build all configurations, and test most of them. - -We will further set additional CMake arguments as follows: -- All builds will have the `tests`, `werr`, and `xrpld` options. -- All builds will have the `wextra` option except for GCC 12 and Clang 16. -- All release builds will have the `assert` option. -- Certain Debian Bookworm configurations will change the reference fee, enable - codecov, and enable voidstar in PRs. -""" +def get_cmake_args(build_type: str, extra_args: str) -> str: + """Get the full list of CMake arguments for a config.""" + args = _BASE_CMAKE_ARGS.copy() + if extra_args: + args.extend(extra_args.split()) + return " ".join(args) -def build_config_name(os_entry: dict[str, str], platform: str, build_type: str) -> str: - parts = [os_entry["distro_name"]] - for key in ("distro_version", "compiler_name", "compiler_version"): - if value := os_entry[key]: - parts.append(value) - parts.append("arm64" if "arm64" in platform else "amd64") - parts.append(build_type.lower()) - return "-".join(parts) +def runs_on_event(exclude_event_types: list[str], event: str | None) -> bool: + """Whether a config should run for the current event. - -def generate_packaging_matrix(config: Config) -> list[dict]: - """Emit one entry per os entry with `package: true`. Architecture is - hardcoded to linux/amd64 here (and the runner is hardcoded at the - workflow level) until arm64 packaging is ready. + 'exclude_event_types' is a list of GitHub event names (e.g. + ["pull_request"]) on which the config should NOT run; an empty list means + the config runs on every event. When no event is given (event is None), no + filtering is applied. """ - return [ - { - "artifact_name": f"xrpld-{build_config_name(os, 'linux/amd64', 'Release')}", - "os": os, - } - for os in config.os - if os.get("package", False) - ] + if event is None: + return True + return event not in exclude_event_types -def generate_strategy_matrix(all: bool, config: Config) -> list[dict]: - configurations = [] - for architecture, os, build_type, cmake_args in itertools.product( - config.architecture, config.os, config.build_type, config.cmake_args - ): - # The default CMake target is 'all' for Linux and MacOS and 'install' - # for Windows, but it can get overridden for certain configurations. - cmake_target = "install" if os["distro_name"] == "windows" else "all" +# --------------------------------------------------------------------------- +# Input types — shapes of the JSON config files +# --------------------------------------------------------------------------- - # We build and test all configurations by default, except for Windows in - # Debug, because it is too slow, as well as when code coverage is - # enabled as that mode already runs the tests. - build_only = False - if os["distro_name"] == "windows" and build_type == "Debug": - build_only = True - # Only generate a subset of configurations in PRs. - if not all: - # Debian: - # - Bookworm using GCC 13: Debug on linux/amd64, set the reference - # fee to 500 and enable code coverage (which will be done below). - # - Bookworm using GCC 15: Debug on linux/amd64, enable Address and - # UB sanitizers (which will be done below). - # - Bookworm using Clang 16: Debug on linux/amd64, enable voidstar. - # - Bookworm using Clang 17: Release on linux/amd64, set the - # reference fee to 1000. - # - Bookworm using Clang 20: Debug on linux/amd64, enable Address - # and UB sanitizers (which will be done below). - if os["distro_name"] == "debian": - skip = True - if os["distro_version"] == "bookworm": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-13" - and build_type == "Debug" - and architecture["platform"] == "linux/amd64" - ): - cmake_args = f"-DUNIT_TEST_REFERENCE_FEE=500 {cmake_args}" - skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15" - and build_type == "Release" - and architecture["platform"] == "linux/amd64" - ): - skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-16" - and build_type == "Debug" - and architecture["platform"] == "linux/amd64" - ): - cmake_args = f"-Dvoidstar=ON {cmake_args}" - skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-17" - and build_type == "Release" - and architecture["platform"] == "linux/amd64" - ): - cmake_args = f"-DUNIT_TEST_REFERENCE_FEE=1000 {cmake_args}" - skip = False - elif os["distro_version"] == "trixie": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-22" - and build_type == "Debug" - and architecture["platform"] == "linux/amd64" - ): - skip = False - if skip: - continue +@dataclasses.dataclass +class LinuxConfig: + """One entry in linux.json's 'configs' or 'package_configs' arrays.""" - # RHEL: - # - 9 using GCC 12: Debug and Release on linux/amd64 - # (Release is required for RPM packaging). - # - 10 using Clang: Release on linux/amd64. - if os["distro_name"] == "rhel": - skip = True - if os["distro_version"] == "9": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12" - and build_type in ["Debug", "Release"] - and architecture["platform"] == "linux/amd64" - ): - skip = False - elif os["distro_version"] == "10": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-any" - and build_type == "Release" - and architecture["platform"] == "linux/amd64" - ): - skip = False - if skip: - continue + compiler: list[str] + build_type: list[str] + arch: list[str] + sanitizers: list[str] = dataclasses.field(default_factory=list) + suffix: str = "" + extra_cmake_args: str = "" + image: str = "" # only used by package_configs entries + # List of GitHub event names (e.g. "pull_request") on which this config + # should NOT run. Empty means it runs on every event. + exclude_event_types: list[str] = dataclasses.field(default_factory=list) - # Ubuntu: - # - Jammy using GCC 12: Debug on linux/arm64, Release on - # linux/amd64 (Release is required for DEB packaging). - # - Noble using GCC 14: Release on linux/amd64. - # - Noble using Clang 18: Debug on linux/amd64. - # - Noble using Clang 19: Release on linux/arm64. - if os["distro_name"] == "ubuntu": - skip = True - if os["distro_version"] == "jammy": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12" - and build_type == "Debug" - and architecture["platform"] == "linux/arm64" - ): - skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12" - and build_type == "Release" - and architecture["platform"] == "linux/amd64" - ): - skip = False - elif os["distro_version"] == "noble": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-14" - and build_type == "Release" - and architecture["platform"] == "linux/amd64" - ): - skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-18" - and build_type == "Debug" - and architecture["platform"] == "linux/amd64" - ): - skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-19" - and build_type == "Release" - and architecture["platform"] == "linux/arm64" - ): - skip = False - if skip: - continue - # MacOS: - # - Debug on macos/arm64. - if os["distro_name"] == "macos" and not ( - build_type == "Debug" and architecture["platform"] == "macos/arm64" - ): +@dataclasses.dataclass +class LinuxFile: + """Shape of linux.json.""" + + image_tag: str + configs: dict[str, list[LinuxConfig]] # distro → configs + package_configs: dict[str, list[LinuxConfig]] # distro → packaging configs + + @classmethod + def load(cls, path: Path) -> "LinuxFile": + data = json.loads(path.read_text()) + + def parse(section: dict) -> dict[str, list[LinuxConfig]]: + return { + distro: [LinuxConfig(**c) for c in cfgs] + for distro, cfgs in section.items() + } + + return cls( + image_tag=data["image_tag"], + configs=parse(data["configs"]), + package_configs=parse(data.get("package_configs", {})), + ) + + +@dataclasses.dataclass +class PlatformConfig: + """One entry in macos.json's or windows.json's 'configs' array.""" + + build_type: list[str] + build_only: bool = False # if true, skip tests (e.g. macos/Windows Debug) + extra_cmake_args: str = "" + # List of GitHub event names (e.g. "pull_request") on which this config + # should NOT run. Empty means it runs on every event. + exclude_event_types: list[str] = dataclasses.field(default_factory=list) + + def __post_init__(self) -> None: + if isinstance(self.build_type, str): + self.build_type = [self.build_type] + + +@dataclasses.dataclass +class PlatformFile: + """Shape of macos.json and windows.json.""" + + platform: str # e.g. "macos/arm64" or "windows/amd64" + runner: list[str] # GitHub Actions runner labels + configs: list[PlatformConfig] + + @classmethod + def load(cls, path: Path) -> "PlatformFile": + data = json.loads(path.read_text()) + return cls( + platform=data["platform"], + runner=data["runner"], + configs=[PlatformConfig(**c) for c in data["configs"]], + ) + + +# --------------------------------------------------------------------------- +# Output types — shapes of the generated GitHub Actions matrix entries +# --------------------------------------------------------------------------- + + +@dataclasses.dataclass +class Architecture: + platform: str + runner: list[str] + + +@dataclasses.dataclass +class MatrixEntry: + """One entry in the generated build/test strategy matrix.""" + + config_name: str + cmake_args: str + cmake_target: str + build_only: bool + build_type: str + architecture: Architecture + sanitizers: str + image: str = "" # container image; empty for macOS/Windows (runs natively) + compiler: str = "" # compiler name ("gcc" or "clang"); empty for macOS/Windows + + +@dataclasses.dataclass +class PackagingEntry: + """One entry in the generated packaging strategy matrix.""" + + artifact_name: str + image: str + distro: str # e.g. "debian" or "rhel"; drives package-format-specific steps + + +# --------------------------------------------------------------------------- +# Matrix expansion +# --------------------------------------------------------------------------- + +_ARCHS: dict[str, Architecture] = { + "amd64": Architecture( + platform="linux/amd64", runner=["self-hosted", "Linux", "X64", "heavy"] + ), + "arm64": Architecture( + platform="linux/arm64", + runner=["self-hosted", "Linux", "ARM64", "heavy-arm64"], + ), +} + + +def expand_linux_matrix( + linux: LinuxFile, event: str | None = None +) -> list[MatrixEntry]: + """Expand a LinuxFile into a flat list of matrix entries. + + Each config entry is expanded over the cross-product of its + compiler, build_type, sanitizers, and architecture lists. Configs that + exclude the current event are skipped. + """ + entries: list[MatrixEntry] = [] + + for distro, configs in linux.configs.items(): + for cfg in configs: + if not runs_on_event(cfg.exclude_event_types, event): continue + # An empty sanitizers list means "one entry with no sanitizer". + effective_sanitizers = cfg.sanitizers or [""] + effective_archs = {arch: _ARCHS[arch] for arch in cfg.arch} - # Windows: - # - Release on windows/amd64. - if os["distro_name"] == "windows" and not ( - build_type == "Release" and architecture["platform"] == "windows/amd64" + for compiler, build_type, sanitizer, (arch, arch_info) in itertools.product( + cfg.compiler, + cfg.build_type, + effective_sanitizers, + effective_archs.items(), ): - continue + name = f"{distro}-{compiler}-{build_type.lower()}-{arch}" + suffix_parts = [ + s for s in [cfg.suffix, _SANITIZER_SUFFIX.get(sanitizer, "")] if s + ] + if suffix_parts: + name += "-" + "-".join(suffix_parts) - # Additional CMake arguments. - cmake_args = f"{cmake_args} -Dtests=ON -Dwerr=ON -Dxrpld=ON" - if not f"{os['compiler_name']}-{os['compiler_version']}" in [ - "gcc-12", - "clang-16", - ]: - cmake_args = f"{cmake_args} -Dwextra=ON" - if build_type == "Release": - cmake_args = f"{cmake_args} -Dassert=ON" - - # We skip all RHEL on arm64 due to a build failure that needs further - # investigation. - if os["distro_name"] == "rhel" and architecture["platform"] == "linux/arm64": - continue - - # We skip all clang 20+ on arm64 due to Boost build error. - if ( - os["compiler_name"] == "clang" - and os["compiler_version"].isdigit() - and int(os["compiler_version"]) >= 20 - and architecture["platform"] == "linux/arm64" - ): - continue - - # Enable code coverage for Debian Bookworm using GCC 13 in Debug on - # linux/amd64. - if ( - f"{os['distro_name']}-{os['distro_version']}" == "debian-bookworm" - and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-13" - and build_type == "Debug" - and architecture["platform"] == "linux/amd64" - ): - cmake_args = f"{cmake_args} -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0" - - # Enable unity build for Ubuntu Jammy using GCC 12 in Debug on - # linux/amd64. - if ( - f"{os['distro_name']}-{os['distro_version']}" == "ubuntu-jammy" - and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12" - and build_type == "Debug" - and architecture["platform"] == "linux/amd64" - ): - cmake_args = f"{cmake_args} -Dunity=ON" - - # Generate a unique name for the configuration, e.g. macos-arm64-debug - # or debian-bookworm-gcc-12-amd64-release. - config_name = build_config_name(os, architecture["platform"], build_type) - if "-Dcoverage=ON" in cmake_args: - config_name += "-coverage" - if "-Dunity=ON" in cmake_args: - config_name += "-unity" - - # Add the configuration to the list, with the most unique fields first, - # so that they are easier to identify in the GitHub Actions UI, as long - # names get truncated. - # Add Address and UB sanitizers as separate configurations for specific - # bookworm distros. Thread sanitizer is currently disabled (see below). - # GCC-Asan xrpld-embedded tests are failing because of https://github.com/google/sanitizers/issues/856 - if ( - os["distro_version"] == "bookworm" - and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15" - ) or ( - os["distro_version"] == "trixie" - and f"{os['compiler_name']}-{os['compiler_version']}" == "clang-22" - ): - # Add ASAN and UBSAN configurations for both gcc-15 and clang-22 - configurations.append( - { - "config_name": config_name + "-asan", - "cmake_args": cmake_args, - "cmake_target": cmake_target, - "build_only": build_only, - "build_type": build_type, - "os": os, - "architecture": architecture, - "sanitizers": "address", - } - ) - configurations.append( - { - "config_name": config_name + "-ubsan", - "cmake_args": cmake_args, - "cmake_target": cmake_target, - "build_only": build_only, - "build_type": build_type, - "os": os, - "architecture": architecture, - "sanitizers": "undefinedbehavior", - } - ) - # TSAN is deactivated due to seg faults with latest compilers. - activate_tsan = False - if activate_tsan: - configurations.append( - { - "config_name": config_name + "-tsan-ubsan", - "cmake_args": cmake_args, - "cmake_target": cmake_target, - "build_only": build_only, - "build_type": build_type, - "os": os, - "architecture": architecture, - "sanitizers": "thread,undefinedbehavior", - } + entries.append( + MatrixEntry( + config_name=name, + image=f"ghcr.io/xrplf/xrpld/nix-{distro}:{linux.image_tag}", + cmake_args=get_cmake_args(build_type, cfg.extra_cmake_args), + cmake_target="all", + build_only=False, + build_type=build_type, + architecture=arch_info, + sanitizers=sanitizer, + compiler=compiler, + ) + ) + + return entries + + +def expand_linux_packaging(linux: LinuxFile) -> list[PackagingEntry]: + """Generate the packaging matrix from a LinuxFile's package_configs section. + + Packaging uses vanilla distro images (debian:bookworm, ubi9, …) instead of + the nix-based build images, because deb/rpm tooling (debhelper, rpm-build) + is taken from the distro's archive rather than from nixpkgs. Each config + entry carries its own 'image'. + """ + entries = [] + for distro, configs in linux.package_configs.items(): + for cfg in configs: + for compiler, build_type in itertools.product(cfg.compiler, cfg.build_type): + entries.append( + PackagingEntry( + artifact_name=f"xrpld-{distro}-{compiler}-{build_type.lower()}-amd64", + image=cfg.image, + distro=distro, + ) + ) + + return entries + + +def expand_platform_matrix( + pf: PlatformFile, event: str | None = None +) -> list[MatrixEntry]: + """Expand a PlatformFile (macOS or Windows) into matrix entries. + + Configs that exclude the current event are skipped. + """ + platform_name, arch = pf.platform.split("/") + is_windows = platform_name == "windows" + + entries: list[MatrixEntry] = [] + for cfg in pf.configs: + if not runs_on_event(cfg.exclude_event_types, event): + continue + for build_type in cfg.build_type: + entries.append( + MatrixEntry( + config_name=f"{platform_name}-{arch}-{build_type.lower()}", + cmake_args=get_cmake_args(build_type, cfg.extra_cmake_args), + cmake_target="install" if is_windows else "all", + build_only=cfg.build_only, + build_type=build_type, + architecture=Architecture(platform=pf.platform, runner=pf.runner), + sanitizers="", ) - else: - configurations.append( - { - "config_name": config_name, - "cmake_args": cmake_args, - "cmake_target": cmake_target, - "build_only": build_only, - "build_type": build_type, - "os": os, - "architecture": architecture, - "sanitizers": "", - } ) - - return configurations + return entries -def read_config(file: Path) -> Config: - config = json.loads(file.read_text()) - if ( - config["architecture"] is None - or config["os"] is None - or config["build_type"] is None - or config["cmake_args"] is None - ): - raise Exception("Invalid configuration file.") - - return Config(**config) +# --------------------------------------------------------------------------- +# Entry point +# --------------------------------------------------------------------------- if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument( - "-a", - "--all", - help="Set to generate all configurations (generally used when merging a PR) or leave unset to generate a subset of configurations (generally used when committing to a PR).", - action="store_true", + parser = argparse.ArgumentParser( + description="Generate a CI strategy matrix for all platforms or a specific one." ) parser.add_argument( "-c", "--config", - help="Path to the JSON file containing the strategy matrix configurations.", - required=False, - type=Path, + help="Platform to generate for ('linux', 'macos', or 'windows'). Defaults to all platforms.", + choices=["linux", "macos", "windows"], + default=None, ) parser.add_argument( "-p", "--packaging", - help="Emit the packaging matrix (derived from the 'package' field on os entries) instead of the build/test matrix.", + help="Emit the Linux packaging matrix instead of the build/test matrix.", action="store_true", ) + parser.add_argument( + "-e", + "--event", + help="The GitHub event name that triggered the workflow (e.g. 'push', " + "'pull_request'). Configs are filtered by their 'event_type'. If " + "omitted, no filtering is applied.", + default=None, + ) args = parser.parse_args() - matrix = [] - if args.packaging: - config_path = args.config if args.config else THIS_DIR / "linux.json" - matrix += generate_packaging_matrix(read_config(config_path)) - elif args.config is None or args.config == "": - matrix += generate_strategy_matrix( - args.all, read_config(THIS_DIR / "linux.json") - ) - matrix += generate_strategy_matrix( - args.all, read_config(THIS_DIR / "macos.json") - ) - matrix += generate_strategy_matrix( - args.all, read_config(THIS_DIR / "windows.json") - ) - else: - matrix += generate_strategy_matrix(args.all, read_config(args.config)) + matrix: list[MatrixEntry] | list[PackagingEntry] = [] - # Generate the strategy matrix. - print(f"matrix={json.dumps({'include': matrix})}") + if args.packaging: + matrix = expand_linux_packaging(LinuxFile.load(THIS_DIR / "linux.json")) + else: + if args.config in ("linux", None): + matrix += expand_linux_matrix( + LinuxFile.load(THIS_DIR / "linux.json"), args.event + ) + if args.config in ("macos", None): + matrix += expand_platform_matrix( + PlatformFile.load(THIS_DIR / "macos.json"), args.event + ) + if args.config in ("windows", None): + matrix += expand_platform_matrix( + PlatformFile.load(THIS_DIR / "windows.json"), args.event + ) + + print(f"matrix={json.dumps({'include': [dataclasses.asdict(e) for e in matrix]})}") diff --git a/.github/scripts/strategy-matrix/linux.json b/.github/scripts/strategy-matrix/linux.json index 4f090a81a3..863b910dda 100644 --- a/.github/scripts/strategy-matrix/linux.json +++ b/.github/scripts/strategy-matrix/linux.json @@ -1,221 +1,84 @@ { - "architecture": [ - { - "platform": "linux/amd64", - "runner": ["self-hosted", "Linux", "X64", "heavy"] - }, - { - "platform": "linux/arm64", - "runner": ["self-hosted", "Linux", "ARM64", "heavy-arm64"] - } - ], - "os": [ - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "gcc", - "compiler_version": "12", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "gcc", - "compiler_version": "13", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "gcc", - "compiler_version": "15", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "clang", - "compiler_version": "16", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "clang", - "compiler_version": "17", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "clang", - "compiler_version": "18", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "clang", - "compiler_version": "19", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "clang", - "compiler_version": "20", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "trixie", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "trixie", - "compiler_name": "gcc", - "compiler_version": "15", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "trixie", - "compiler_name": "clang", - "compiler_version": "20", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "trixie", - "compiler_name": "clang", - "compiler_version": "21", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "trixie", - "compiler_name": "clang", - "compiler_version": "22", - "image_sha": "4c086b9" - }, - { - "distro_name": "rhel", - "distro_version": "8", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "4c086b9" - }, - { - "distro_name": "rhel", - "distro_version": "8", - "compiler_name": "clang", - "compiler_version": "any", - "image_sha": "4c086b9" - }, - { - "distro_name": "rhel", - "distro_version": "9", - "compiler_name": "gcc", - "compiler_version": "12", - "image_sha": "4c086b9", - "package": true - }, - { - "distro_name": "rhel", - "distro_version": "9", - "compiler_name": "gcc", - "compiler_version": "13", - "image_sha": "4c086b9" - }, - { - "distro_name": "rhel", - "distro_version": "9", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "4c086b9" - }, - { - "distro_name": "rhel", - "distro_version": "9", - "compiler_name": "clang", - "compiler_version": "any", - "image_sha": "4c086b9" - }, - { - "distro_name": "rhel", - "distro_version": "10", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "4c086b9" - }, - { - "distro_name": "rhel", - "distro_version": "10", - "compiler_name": "clang", - "compiler_version": "any", - "image_sha": "4c086b9" - }, - { - "distro_name": "ubuntu", - "distro_version": "jammy", - "compiler_name": "gcc", - "compiler_version": "12", - "image_sha": "4c086b9", - "package": true - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "gcc", - "compiler_version": "13", - "image_sha": "4c086b9" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "4c086b9" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "clang", - "compiler_version": "16", - "image_sha": "4c086b9" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "clang", - "compiler_version": "17", - "image_sha": "4c086b9" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "clang", - "compiler_version": "18", - "image_sha": "4c086b9" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "clang", - "compiler_version": "19", - "image_sha": "4c086b9" - } - ], - "build_type": ["Debug", "Release"], - "cmake_args": [""] + "image_tag": "sha-e29b523", + "configs": { + "ubuntu": [ + { + "compiler": ["gcc", "clang"], + "build_type": ["Debug", "Release"], + "arch": ["amd64", "arm64"] + }, + + { + "compiler": ["gcc", "clang"], + "build_type": ["Debug", "Release"], + "arch": ["amd64"], + "sanitizers": ["address", "undefinedbehavior"] + }, + + { + "compiler": ["gcc"], + "build_type": ["Debug"], + "arch": ["amd64"], + "suffix": "coverage", + "extra_cmake_args": "-DUNIT_TEST_REFERENCE_FEE=500 -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0" + }, + { + "compiler": ["clang"], + "build_type": ["Debug"], + "arch": ["amd64"], + "suffix": "voidstar", + "extra_cmake_args": "-Dvoidstar=ON" + }, + { + "compiler": ["clang"], + "build_type": ["Release"], + "arch": ["amd64"], + "suffix": "reffee", + "extra_cmake_args": "-DUNIT_TEST_REFERENCE_FEE=1000" + }, + { + "compiler": ["gcc"], + "build_type": ["Debug"], + "arch": ["amd64"], + "suffix": "unity", + "extra_cmake_args": "-Dunity=ON", + "exclude_event_types": ["pull_request"] + } + ], + + "debian": [ + { + "compiler": ["gcc"], + "build_type": ["Release"], + "arch": ["amd64"] + } + ], + + "rhel": [ + { + "compiler": ["gcc"], + "build_type": ["Release"], + "arch": ["amd64"] + } + ] + }, + "package_configs": { + "debian": [ + { + "compiler": ["gcc"], + "build_type": ["Release"], + "arch": ["amd64"], + "image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-577d745" + } + ], + + "rhel": [ + { + "compiler": ["gcc"], + "build_type": ["Release"], + "arch": ["amd64"], + "image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-577d745" + } + ] + } } diff --git a/.github/scripts/strategy-matrix/macos.json b/.github/scripts/strategy-matrix/macos.json index 6fc44d0f80..2d3cc75c7b 100644 --- a/.github/scripts/strategy-matrix/macos.json +++ b/.github/scripts/strategy-matrix/macos.json @@ -1,19 +1,16 @@ { - "architecture": [ + "platform": "macos/arm64", + "runner": ["self-hosted", "macOS", "ARM64", "macos-26-apple-clang-21"], + "configs": [ { - "platform": "macos/arm64", - "runner": ["self-hosted", "macOS", "ARM64", "mac-runner-m1"] - } - ], - "os": [ + "build_type": "Release", + "extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5" + }, { - "distro_name": "macos", - "distro_version": "", - "compiler_name": "", - "compiler_version": "", - "image_sha": "" + "build_type": "Debug", + "extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5", + "build_only": true, + "exclude_event_types": ["pull_request"] } - ], - "build_type": ["Debug", "Release"], - "cmake_args": ["-DCMAKE_POLICY_VERSION_MINIMUM=3.5"] + ] } diff --git a/.github/scripts/strategy-matrix/windows.json b/.github/scripts/strategy-matrix/windows.json index 8c536c70f2..370e9f5bc7 100644 --- a/.github/scripts/strategy-matrix/windows.json +++ b/.github/scripts/strategy-matrix/windows.json @@ -1,19 +1,12 @@ { - "architecture": [ + "platform": "windows/amd64", + "runner": ["self-hosted", "Windows", "dev-box-windows-2026"], + "configs": [ + { "build_type": "Release" }, { - "platform": "windows/amd64", - "runner": ["self-hosted", "Windows", "devbox"] + "build_type": "Debug", + "build_only": true, + "exclude_event_types": ["pull_request"] } - ], - "os": [ - { - "distro_name": "windows", - "distro_version": "", - "compiler_name": "", - "compiler_version": "", - "image_sha": "" - } - ], - "build_type": ["Debug", "Release"], - "cmake_args": [""] + ] } diff --git a/.github/workflows/build-nix-image.yml b/.github/workflows/build-nix-image.yml deleted file mode 100644 index 6554cf6c08..0000000000 --- a/.github/workflows/build-nix-image.yml +++ /dev/null @@ -1,101 +0,0 @@ -name: Build Nix Docker image - -on: - push: - branches: - - develop - paths: - - ".github/workflows/build-nix-image.yml" - - "docker/nix.Dockerfile" - - "flake.nix" - - "flake.lock" - - "nix/**" - pull_request: - paths: - - ".github/workflows/build-nix-image.yml" - - "docker/nix.Dockerfile" - - "flake.nix" - - "flake.lock" - - "nix/**" - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -defaults: - run: - shell: bash - -env: - UBUNTU_VERSION: "20.04" - RHEL_VERSION: "9" - DEBIAN_VERSION: "bookworm" - -jobs: - build: - name: Build and push Nix image (${{ matrix.distro }}) - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - strategy: - matrix: - include: - - distro: nixos - - distro: ubuntu - - distro: rhel - - distro: debian - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Determine base image - id: vars - run: | - case "${{ matrix.distro }}" in - nixos) - echo "base_image=nixos/nix:latest" >> $GITHUB_OUTPUT - ;; - ubuntu) - echo "base_image=ubuntu:${UBUNTU_VERSION}" >> $GITHUB_OUTPUT - ;; - rhel) - echo "base_image=registry.access.redhat.com/ubi${RHEL_VERSION}/ubi:latest" >> $GITHUB_OUTPUT - ;; - debian) - echo "base_image=debian:${DEBIAN_VERSION}" >> $GITHUB_OUTPUT - ;; - esac - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - - - name: Login to GitHub Container Registry - if: github.event_name == 'push' - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Docker metadata - id: meta - uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 - with: - images: ghcr.io/xrplf/ci/nix-${{ matrix.distro }} - tags: | - type=sha,prefix=sha-,format=short - type=raw,value=latest - - - name: Build and push - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 - with: - context: . - file: docker/nix.Dockerfile - platforms: linux/amd64 - push: ${{ github.event_name == 'push' }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - build-args: BASE_IMAGE=${{ steps.vars.outputs.base_image }} diff --git a/.github/workflows/build-nix-images.yml b/.github/workflows/build-nix-images.yml new file mode 100644 index 0000000000..54911ef6e0 --- /dev/null +++ b/.github/workflows/build-nix-images.yml @@ -0,0 +1,62 @@ +name: Build Nix Docker images + +on: + push: + branches: + - develop + paths: + - ".github/workflows/build-nix-images.yml" + - "flake.nix" + - "flake.lock" + - "nix/**" + - "!nix/docker/README.md" + - "!nix/devshell.nix" + - "bin/check-tools.sh" + - "bin/install-sanitizer-libs.sh" + pull_request: + paths: + - ".github/workflows/build-nix-images.yml" + - "flake.nix" + - "flake.lock" + - "nix/**" + - "!nix/docker/README.md" + - "!nix/devshell.nix" + - "bin/check-tools.sh" + - "bin/install-sanitizer-libs.sh" + workflow_dispatch: + +concurrency: + # Read `on-trigger.yml` for the rationale behind this concurrency group name. + group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' && github.sha || github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + build-merge: + name: Build and push nix-${{ matrix.distro.name }} + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + # The base images are the oldest supported version of each distro + # that we want to build images for. + distro: + - name: nixos + base_image: nixos/nix:latest + - name: ubuntu + base_image: ubuntu:20.04 + - name: debian + base_image: debian:bookworm + - name: rhel + base_image: registry.access.redhat.com/ubi9/ubi:latest + uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@ee03d31bcc4501d7599dc1b1ecd7a34af582ad1c + with: + image_name: xrpld/nix-${{ matrix.distro.name }} + dockerfile: nix/docker/Dockerfile + base_image: ${{ matrix.distro.base_image }} + push: ${{ github.event_name == 'push' }} diff --git a/.github/workflows/build-packaging-images.yml b/.github/workflows/build-packaging-images.yml new file mode 100644 index 0000000000..3633847ef3 --- /dev/null +++ b/.github/workflows/build-packaging-images.yml @@ -0,0 +1,46 @@ +name: Build packaging Docker images + +on: + push: + branches: + - develop + paths: + - ".github/workflows/build-packaging-images.yml" + - "package/Dockerfile" + - "package/install-packaging-tools.sh" + pull_request: + paths: + - ".github/workflows/build-packaging-images.yml" + - "package/Dockerfile" + - "package/install-packaging-tools.sh" + workflow_dispatch: + +concurrency: + # Read `on-trigger.yml` for the rationale behind this concurrency group name. + group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' && github.sha || github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + build-merge: + name: Build and push packaging-${{ matrix.distro.name }} + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + distro: + - name: debian + base_image: debian:bookworm + - name: rhel + base_image: registry.access.redhat.com/ubi9/ubi:latest + uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@ee03d31bcc4501d7599dc1b1ecd7a34af582ad1c + with: + image_name: xrpld/packaging-${{ matrix.distro.name }} + dockerfile: package/Dockerfile + base_image: ${{ matrix.distro.base_image }} + push: ${{ github.event_name == 'push' }} diff --git a/.github/workflows/check-pr-description.yml b/.github/workflows/check-pr-description.yml index f6eee50291..744449f216 100644 --- a/.github/workflows/check-pr-description.yml +++ b/.github/workflows/check-pr-description.yml @@ -5,8 +5,17 @@ on: types: - checks_requested pull_request: - types: [opened, edited, reopened, synchronize, ready_for_review] - branches: [develop] + types: + - opened + - edited + - reopened + - synchronize + - ready_for_review + branches: + - develop + - "release-*" + - "release/*" + - "staging/*" jobs: check_description: @@ -14,17 +23,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Write PR body to file env: PR_BODY: ${{ github.event.pull_request.body }} if: ${{ github.event_name == 'pull_request' }} - run: printenv PR_BODY > pr_body.md + run: printenv PR_BODY >pr_body.md - name: Check PR description differs from template if: ${{ github.event_name == 'pull_request' }} - run: > - python .github/scripts/check-pr-description.py - --template-file .github/pull_request_template.md - --pr-body-file pr_body.md + run: | + python .github/scripts/check-pr-description.py \ + --template-file .github/pull_request_template.md \ + --pr-body-file pr_body.md diff --git a/.github/workflows/check-pr-title.yml b/.github/workflows/check-pr-title.yml index 5631950df6..4b5f679df1 100644 --- a/.github/workflows/check-pr-title.yml +++ b/.github/workflows/check-pr-title.yml @@ -5,10 +5,19 @@ on: types: - checks_requested pull_request: - types: [opened, edited, reopened, synchronize, ready_for_review] - branches: [develop] + types: + - opened + - edited + - reopened + - synchronize + - ready_for_review + branches: + - develop + - "release-*" + - "release/*" + - "staging/*" jobs: check_title: if: ${{ github.event.pull_request.draft != true }} - uses: XRPLF/actions/.github/workflows/check-pr-title.yml@291206777251b4d493641b5afbdf7c23009d2988 + uses: XRPLF/actions/.github/workflows/check-pr-title.yml@cba1f0891650baf1a9c88624dc2d72573be2eb81 diff --git a/.github/workflows/conflicting-pr.yml b/.github/workflows/conflicting-pr.yml index 6e667632a0..772d46fd7d 100644 --- a/.github/workflows/conflicting-pr.yml +++ b/.github/workflows/conflicting-pr.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check if PRs are dirty - uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3 + uses: eps1lon/actions-label-merge-conflict@0273be72a0bbd58fcd71d0d6c02c209b50d1e5e1 # v3.1.0 with: dirtyLabel: "PR: has conflicts" repoToken: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index ca715e0376..19fb170b92 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Determine changed files # This step checks whether any files have changed that should # cause the next jobs to run. We do it this way rather than @@ -70,6 +70,7 @@ jobs: .github/workflows/reusable-upload-recipe.yml .clang-tidy .codecov.yml + bin/check-tools.sh cfg/** cmake/** conan/** @@ -98,7 +99,7 @@ jobs: READY: ${{ contains(github.event.pull_request.labels.*.name, 'Ready to merge') }} MERGE: ${{ github.event_name == 'merge_group' }} run: | - echo "go=${{ (env.DRAFT != 'true' && env.READY == 'true') || env.FILES == 'true' || env.MERGE == 'true' }}" >> "${GITHUB_OUTPUT}" + echo "go=${{ (env.DRAFT != 'true' && env.READY == 'true') || env.FILES == 'true' || env.MERGE == 'true' }}" >>"${GITHUB_OUTPUT}" cat "${GITHUB_OUTPUT}" outputs: go: ${{ steps.go.outputs.go == 'true' }} @@ -121,7 +122,6 @@ jobs: issues: write contents: read with: - check_only_changed: true create_issue_on_failure: false build-test: @@ -153,8 +153,8 @@ jobs: if: ${{ github.repository == 'XRPLF/rippled' && needs.should-run.outputs.go == 'true' && github.event_name == 'pull_request' && startsWith(github.event.pull_request.base.ref, 'release') }} uses: ./.github/workflows/reusable-upload-recipe.yml secrets: - remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} - remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} + remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }} + remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }} notify-clio: needs: upload-recipe @@ -168,9 +168,9 @@ jobs: PR_URL: ${{ github.event.pull_request.html_url }} run: | gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \ - /repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \ - -F "client_payload[ref]=${{ needs.upload-recipe.outputs.recipe_ref }}" \ - -F "client_payload[pr_url]=${PR_URL}" + /repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \ + -F "client_payload[ref]=${{ needs.upload-recipe.outputs.recipe_ref }}" \ + -F "client_payload[pr_url]=${PR_URL}" passed: if: failure() || cancelled() diff --git a/.github/workflows/on-tag.yml b/.github/workflows/on-tag.yml index b7517ccf11..abedc13d69 100644 --- a/.github/workflows/on-tag.yml +++ b/.github/workflows/on-tag.yml @@ -20,8 +20,8 @@ jobs: if: ${{ github.repository == 'XRPLF/rippled' }} uses: ./.github/workflows/reusable-upload-recipe.yml secrets: - remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} - remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} + remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }} + remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }} build-test: if: ${{ github.repository == 'XRPLF/rippled' }} @@ -33,7 +33,6 @@ jobs: with: ccache_enabled: false os: ${{ matrix.os }} - strategy_matrix: minimal secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index 803ba3c87b..49a93d2746 100644 --- a/.github/workflows/on-trigger.yml +++ b/.github/workflows/on-trigger.yml @@ -27,6 +27,7 @@ on: - ".github/workflows/reusable-upload-recipe.yml" - ".clang-tidy" - ".codecov.yml" + - "bin/check-tools.sh" - "cfg/**" - "cmake/**" - "conan/**" @@ -71,7 +72,6 @@ jobs: issues: write contents: read with: - check_only_changed: false create_issue_on_failure: ${{ github.event_name == 'schedule' }} build-test: @@ -88,7 +88,6 @@ jobs: # not identical to a regular compilation. ccache_enabled: ${{ github.repository_owner == 'XRPLF' && !startsWith(github.ref, 'refs/heads/release') }} os: ${{ matrix.os }} - strategy_matrix: ${{ github.event_name == 'schedule' && 'all' || 'minimal' }} secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} @@ -98,8 +97,8 @@ jobs: if: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' && github.ref == 'refs/heads/develop' }} uses: ./.github/workflows/reusable-upload-recipe.yml secrets: - remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} - remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} + remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }} + remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }} package: needs: build-test diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 2f15b82266..1acd28208e 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -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@5e942d61bf32f7557a7c159cfac4712a687b3e3a + uses: XRPLF/actions/.github/workflows/pre-commit.yml@1bde119a1ab71305ba5d3716e7a82cea1c7bdede with: runs_on: ubuntu-latest container: '{ "image": "ghcr.io/xrplf/ci/tools-rippled-pre-commit:sha-41ec7c1" }' diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index d619be5543..1ac8d61655 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -41,13 +41,13 @@ env: jobs: build: runs-on: ubuntu-latest - container: ghcr.io/xrplf/ci/tools-rippled-documentation:sha-a8c7be1 + container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-e29b523 steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Prepare runner - uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab + uses: XRPLF/actions/prepare-runner@64ec3cf3b152b4444638f470bbd6df7a7a30c81c with: enable_ccache: false @@ -57,19 +57,11 @@ jobs: with: subtract: ${{ env.NPROC_SUBTRACT }} - - name: Check configuration - run: | - echo 'Checking path.' - echo ${PATH} | tr ':' '\n' + - name: Print build environment + uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574 - echo 'Checking environment variables.' - env | sort - - echo 'Checking CMake version.' - cmake --version - - echo 'Checking Doxygen version.' - doxygen --version + - name: Check Doxygen version + run: doxygen --version - name: Build documentation env: diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index 4c7c41fd0c..e7a88a0e66 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -57,6 +57,12 @@ on: type: string default: "" + compiler: + description: 'The compiler to use ("gcc" or "clang"). Leave empty for macOS/Windows (uses system default).' + required: false + type: string + default: "" + secrets: CODECOV_TOKEN: description: "The Codecov token to use for uploading coverage reports." @@ -76,7 +82,7 @@ jobs: name: ${{ inputs.config_name }} runs-on: ${{ fromJSON(inputs.runs_on) }} container: ${{ inputs.image != '' && inputs.image || null }} - timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 60 }} + timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 180 }} env: # Use a namespace to keep the objects separate for each configuration. CCACHE_NAMESPACE: ${{ inputs.config_name }} @@ -104,16 +110,21 @@ jobs: uses: XRPLF/actions/cleanup-workspace@c7d9ce5ebb03c752a354889ecd870cadfc2b1cd4 - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Prepare runner - uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab + uses: XRPLF/actions/prepare-runner@64ec3cf3b152b4444638f470bbd6df7a7a30c81c with: enable_ccache: ${{ inputs.ccache_enabled }} - name: Set ccache log file if: ${{ inputs.ccache_enabled && runner.debug == '1' }} - run: echo "CCACHE_LOGFILE=${{ runner.temp }}/ccache.log" >> "${GITHUB_ENV}" + run: echo "CCACHE_LOGFILE=${{ runner.temp }}/ccache.log" >>"${GITHUB_ENV}" + + - name: Check tools + env: + CHECK_TOOLS_SKIP_CLONE: "1" + run: ./bin/check-tools.sh - name: Print build environment uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574 @@ -124,6 +135,12 @@ jobs: with: subtract: ${{ inputs.nproc_subtract }} + - name: Set compiler environment (Linux) + if: ${{ runner.os == 'Linux' }} + uses: ./.github/actions/set-compiler-env + with: + compiler: ${{ inputs.compiler }} + - name: Setup Conan env: SANITIZERS: ${{ inputs.sanitizers }} @@ -146,11 +163,32 @@ jobs: CMAKE_ARGS: ${{ inputs.cmake_args }} run: | cmake \ - -G '${{ runner.os == 'Windows' && 'Visual Studio 17 2022' || 'Ninja' }}' \ - -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ - -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ - ${CMAKE_ARGS} \ - .. + -G '${{ runner.os == 'Windows' && 'Visual Studio 18 2026' || 'Ninja' }}' \ + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ + -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ + ${CMAKE_ARGS} \ + .. + + # Export the sanitizer options before any instrumented binary runs. The + # protocol code-gen and build steps below invoke instrumented dependency + # tools (protoc, grpc), so setting UBSAN_OPTIONS here lets the UBSan + # suppression list silence their diagnostics too, not just at test time. + # GITHUB_WORKSPACE (not the github.workspace context) is used so the path + # resolves correctly inside the container job. + - name: Set sanitizer options + if: ${{ !inputs.build_only && env.SANITIZERS_ENABLED == 'true' }} + env: + CONFIG_NAME: ${{ inputs.config_name }} + run: | + SUPP="${GITHUB_WORKSPACE}/sanitizers/suppressions" + ASAN_OPTS="include=${SUPP}/runtime-asan-options.txt:suppressions=${SUPP}/asan.supp" + if [[ "${CONFIG_NAME}" == *gcc* ]]; then + ASAN_OPTS="${ASAN_OPTS}:alloc_dealloc_mismatch=0" + fi + echo "ASAN_OPTIONS=${ASAN_OPTS}" >>${GITHUB_ENV} + echo "TSAN_OPTIONS=include=${SUPP}/runtime-tsan-options.txt:suppressions=${SUPP}/tsan.supp" >>${GITHUB_ENV} + echo "UBSAN_OPTIONS=include=${SUPP}/runtime-ubsan-options.txt:suppressions=${SUPP}/ubsan.supp" >>${GITHUB_ENV} + echo "LSAN_OPTIONS=include=${SUPP}/runtime-lsan-options.txt:suppressions=${SUPP}/lsan.supp" >>${GITHUB_ENV} - name: Check protocol autogen files are up-to-date working-directory: ${{ env.BUILD_DIR }} @@ -172,32 +210,32 @@ jobs: cmake --build . --target code_gen DIFF=$(git -C .. status --porcelain -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen) if [ -n "${DIFF}" ]; then - echo "::error::Generated protocol files are out of date" - git -C .. diff -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen - echo "${MESSAGE}" - exit 1 + echo "::error::Generated protocol files are out of date" + git -C .. diff -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen + echo "${MESSAGE}" + exit 1 fi - name: Build the binary working-directory: ${{ env.BUILD_DIR }} env: - BUILD_NPROC: ${{ runner.os == 'Linux' && '16' || steps.nproc.outputs.nproc }} + BUILD_NPROC: ${{ steps.nproc.outputs.nproc }} BUILD_TYPE: ${{ inputs.build_type }} CMAKE_TARGET: ${{ inputs.cmake_target }} run: | cmake \ - --build . \ - --config "${BUILD_TYPE}" \ - --parallel "${BUILD_NPROC}" \ - --target "${CMAKE_TARGET}" + --build . \ + --config "${BUILD_TYPE}" \ + --parallel "${BUILD_NPROC}" \ + --target "${CMAKE_TARGET}" - name: Show ccache statistics if: ${{ inputs.ccache_enabled }} run: | ccache --show-stats -vv if [ '${{ runner.debug }}' = '1' ]; then - cat "${CCACHE_LOGFILE}" - curl ${CCACHE_REMOTE_STORAGE%|*}/status || true + cat "${CCACHE_LOGFILE}" + curl ${CCACHE_REMOTE_STORAGE%|*}/status || true fi - name: Upload the binary (Linux) @@ -209,15 +247,24 @@ jobs: retention-days: 3 if-no-files-found: error + - name: Upload the test binary (Linux) + if: ${{ github.event.repository.visibility == 'public' && runner.os == 'Linux' }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: xrpl_tests-${{ inputs.config_name }} + path: ${{ env.BUILD_DIR }}/xrpl_tests + retention-days: 3 + if-no-files-found: error + - name: Export server definitions if: ${{ runner.os != 'Windows' && !inputs.build_only && env.VOIDSTAR_ENABLED != 'true' }} working-directory: ${{ env.BUILD_DIR }} run: | set -o pipefail - ./xrpld --definitions | python3 -m json.tool > server_definitions.json + ./xrpld --definitions | python3 -m json.tool >server_definitions.json - name: Upload server definitions - if: ${{ github.event.repository.visibility == 'public' && inputs.config_name == 'debian-bookworm-gcc-13-amd64-release' }} + if: ${{ github.event.repository.visibility == 'public' && inputs.config_name == 'debian-gcc-release-amd64' }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: server-definitions @@ -231,10 +278,10 @@ jobs: run: | ldd ./xrpld if [ "$(ldd ./xrpld | grep -E '(libstdc\+\+|libgcc)' | wc -l)" -eq 0 ]; then - echo 'The binary is statically linked.' + echo 'The binary is statically linked.' else - echo 'The binary is dynamically linked.' - exit 1 + echo 'The binary is dynamically linked.' + exit 1 fi - name: Verify presence of instrumentation (Linux) @@ -243,32 +290,10 @@ jobs: run: | ./xrpld --version | grep libvoidstar - - name: Set sanitizer options - if: ${{ !inputs.build_only && env.SANITIZERS_ENABLED == 'true' }} - env: - CONFIG_NAME: ${{ inputs.config_name }} - run: | - ASAN_OPTS="include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-asan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp" - if [[ "${CONFIG_NAME}" == *gcc* ]]; then - ASAN_OPTS="${ASAN_OPTS}:alloc_dealloc_mismatch=0" - fi - echo "ASAN_OPTIONS=${ASAN_OPTS}" >> ${GITHUB_ENV} - echo "TSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-tsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV} - echo "UBSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV} - echo "LSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-lsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV} - - name: Run the separate tests if: ${{ !inputs.build_only }} - working-directory: ${{ env.BUILD_DIR }} - # Windows locks some of the build files while running tests, and parallel jobs can collide - env: - BUILD_TYPE: ${{ inputs.build_type }} - PARALLELISM: ${{ runner.os == 'Windows' && '1' || steps.nproc.outputs.nproc }} - run: | - ctest \ - --output-on-failure \ - -C "${BUILD_TYPE}" \ - -j "${PARALLELISM}" + working-directory: ${{ runner.os == 'Windows' && format('{0}/{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }} + run: ./xrpl_tests - name: Run the embedded tests if: ${{ !inputs.build_only }} @@ -278,8 +303,26 @@ jobs: run: | set -o pipefail # Coverage builds are slower due to instrumentation; use fewer parallel jobs to avoid flakiness - [ "$COVERAGE_ENABLED" = "true" ] && BUILD_NPROC=$(( BUILD_NPROC - 2 )) - ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log + [ "$COVERAGE_ENABLED" = "true" ] && BUILD_NPROC=$((BUILD_NPROC - 2)) + + # The resolver/preload workaround is only correct for the ASan build: + # a regular build doesn't hit the __dn_expand interceptor bug, and must + # NOT have libasan injected. So only preload when xrpld is ASan-built. + # + # libresolv hosts getaddrinfo's resolver helpers (dn_expand, res_*). Under ASan + # these are intercepted via dlsym(RTLD_NEXT, ...), which yields a NULL pointer + # and crashes DNS resolution if libresolv isn't loaded. Linking it guarantees + # the symbols are present; it's a harmless no-op on glibc >= 2.34 (merged into + # libc) and is what the compiler driver already does for sanitizer builds. + # https://github.com/llvm/llvm-project/issues/59007 + # https://github.com/google/sanitizers/issues/1592 + if ldd ./xrpld | grep -q libasan; then + PRELOAD="$(gcc -print-file-name=libasan.so):/usr/lib/x86_64-linux-gnu/libresolv.so.2" + else + PRELOAD="" + fi + + LD_PRELOAD="$PRELOAD" ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log - name: Show test failure summary if: ${{ failure() && !inputs.build_only }} @@ -287,19 +330,19 @@ jobs: WORKING_DIR: ${{ runner.os == 'Windows' && format('{0}\{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }} run: | if [ ! -d "${WORKING_DIR}" ]; then - echo "Working directory '${WORKING_DIR}' does not exist." - exit 0 + echo "Working directory '${WORKING_DIR}' does not exist." + exit 0 fi cd "${WORKING_DIR}" if [ ! -f unittest.log ]; then - echo "unittest.log not found; embedded tests may not have run." - exit 0 + echo "unittest.log not found; embedded tests may not have run." + exit 0 fi if ! grep -E "failed" unittest.log; then - echo "Log present but no failure lines found in unittest.log." + echo "Log present but no failure lines found in unittest.log." fi - name: Debug failure (Linux) if: ${{ failure() && runner.os == 'Linux' && !inputs.build_only }} @@ -317,14 +360,14 @@ jobs: BUILD_TYPE: ${{ inputs.build_type }} run: | cmake \ - --build . \ - --config "${BUILD_TYPE}" \ - --parallel "${BUILD_NPROC}" \ - --target coverage + --build . \ + --config "${BUILD_TYPE}" \ + --parallel "${BUILD_NPROC}" \ + --target coverage - name: Upload coverage report if: ${{ github.repository == 'XRPLF/rippled' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }} - uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: disable_search: true disable_telem: true diff --git a/.github/workflows/reusable-build-test.yml b/.github/workflows/reusable-build-test.yml index 0086cbbfb5..4b64c53521 100644 --- a/.github/workflows/reusable-build-test.yml +++ b/.github/workflows/reusable-build-test.yml @@ -19,13 +19,6 @@ on: required: true type: string - strategy_matrix: - # TODO: Support additional strategies, e.g. "ubuntu" for generating all Ubuntu configurations. - description: 'The strategy matrix to use for generating the configurations ("minimal", "all").' - required: false - type: string - default: "minimal" - secrets: CODECOV_TOKEN: description: "The Codecov token to use for uploading coverage reports." @@ -37,7 +30,6 @@ jobs: uses: ./.github/workflows/reusable-strategy-matrix.yml with: os: ${{ inputs.os }} - strategy_matrix: ${{ inputs.strategy_matrix }} # Build and test the binary for each configuration. build-test-config: @@ -47,7 +39,6 @@ jobs: strategy: fail-fast: ${{ github.event_name == 'merge_group' }} matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} - max-parallel: 10 with: build_only: ${{ matrix.build_only }} build_type: ${{ matrix.build_type }} @@ -55,8 +46,9 @@ jobs: cmake_args: ${{ matrix.cmake_args }} cmake_target: ${{ matrix.cmake_target }} runs_on: ${{ toJSON(matrix.architecture.runner) }} - image: ${{ contains(matrix.architecture.platform, 'linux') && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-{4}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version, matrix.os.image_sha) || '' }} + image: ${{ matrix.image || '' }} config_name: ${{ matrix.config_name }} sanitizers: ${{ matrix.sanitizers }} + compiler: ${{ matrix.compiler || '' }} secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/reusable-check-levelization.yml b/.github/workflows/reusable-check-levelization.yml index 4efe3e1138..88c95ac3ba 100644 --- a/.github/workflows/reusable-check-levelization.yml +++ b/.github/workflows/reusable-check-levelization.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Check levelization run: python .github/scripts/levelization/generate.py - name: Check for differences @@ -38,9 +38,9 @@ jobs: run: | DIFF=$(git status --porcelain) if [ -n "${DIFF}" ]; then - # Print the differences to give the contributor a hint about what to - # expect when running levelization on their own machine. - git diff - echo "${MESSAGE}" - exit 1 + # Print the differences to give the contributor a hint about what to + # expect when running levelization on their own machine. + git diff + echo "${MESSAGE}" + exit 1 fi diff --git a/.github/workflows/reusable-check-rename.yml b/.github/workflows/reusable-check-rename.yml index 56a1a3e637..9a91e98ee3 100644 --- a/.github/workflows/reusable-check-rename.yml +++ b/.github/workflows/reusable-check-rename.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Check definitions run: .github/scripts/rename/definitions.sh . - name: Check copyright notices @@ -48,9 +48,9 @@ jobs: run: | DIFF=$(git status --porcelain) if [ -n "${DIFF}" ]; then - # Print the differences to give the contributor a hint about what to - # expect when running the renaming scripts on their own machine. - git diff - echo "${MESSAGE}" - exit 1 + # Print the differences to give the contributor a hint about what to + # expect when running the renaming scripts on their own machine. + git diff + echo "${MESSAGE}" + exit 1 fi diff --git a/.github/workflows/reusable-clang-tidy.yml b/.github/workflows/reusable-clang-tidy.yml index e01a50cf6d..a6bbe669ce 100644 --- a/.github/workflows/reusable-clang-tidy.yml +++ b/.github/workflows/reusable-clang-tidy.yml @@ -3,10 +3,6 @@ name: Run clang-tidy on files on: workflow_call: inputs: - check_only_changed: - description: "Check only changed files in PR. If false, checks all files in the repository." - type: boolean - default: false create_issue_on_failure: description: "Whether to create an issue if the check failed" type: boolean @@ -20,32 +16,34 @@ env: BUILD_DIR: build BUILD_TYPE: Debug # Debug so that ASSERTS and such participate in clang-tidy check - OUTPUT_FILE: clang-tidy-output.txt - DIFF_FILE: clang-tidy-git-diff.txt - ISSUE_FILE: clang-tidy-issue.md + OUTPUT_FILE: /tmp/clang-tidy-output.txt + FILTERED_OUTPUT_FILE: /tmp/clang-tidy-filtered-output.txt + DIFF_FILE: /tmp/clang-tidy-git-diff.txt + ISSUE_FILE: /tmp/clang-tidy-issue.md + + COMPILER: clang jobs: determine-files: - if: ${{ inputs.check_only_changed }} permissions: contents: read - uses: XRPLF/actions/.github/workflows/determine-tidy-files.yml@224f3c48d3014d082a1129237b8291ff0b0a331f + uses: XRPLF/actions/.github/workflows/determine-tidy-files.yml@d041ac9f1fa9f07a4ba335eb4c1c82233fb3fef6 run-clang-tidy: name: Run clang tidy needs: [determine-files] - if: ${{ always() && !cancelled() && (!inputs.check_only_changed || needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.clang_tidy_config_changed == 'true') }} + 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/ci/debian-trixie:clang-21-sha-53033a2" + container: "ghcr.io/xrplf/xrpld/nix-debian:sha-e29b523" permissions: contents: read issues: write steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Prepare runner - uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab + uses: XRPLF/actions/prepare-runner@64ec3cf3b152b4444638f470bbd6df7a7a30c81c with: enable_ccache: false @@ -56,6 +54,11 @@ jobs: uses: XRPLF/actions/get-nproc@cf0433aa74563aead044a1e395610c96d65a37cf id: nproc + - name: Set compiler environment + uses: ./.github/actions/set-compiler-env + with: + compiler: ${{ env.COMPILER }} + - name: Setup Conan uses: ./.github/actions/setup-conan @@ -70,13 +73,14 @@ jobs: working-directory: ${{ env.BUILD_DIR }} run: | cmake \ - -G 'Ninja' \ - -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ - -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ - -Dtests=ON \ - -Dwerr=ON \ - -Dxrpld=ON \ - .. + -G 'Ninja' \ + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ + -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ + -Dtests=ON \ + -Dwerr=ON \ + -Dxrpld=ON \ + -Dverify_headers=ON \ + .. # clang-tidy needs headers generated from proto files - name: Build libxrpl.libpb @@ -88,15 +92,15 @@ jobs: id: run_clang_tidy continue-on-error: true env: - TARGETS: ${{ (needs.determine-files.outputs.clang_tidy_config_changed != 'true' && inputs.check_only_changed) && needs.determine-files.outputs.cpp_changed_files || 'src tests' }} + TARGETS: ${{ needs.determine-files.outputs.need_full_run != 'true' && needs.determine-files.outputs.cpp_changed_files || 'include src tests' }} run: | set -o pipefail run-clang-tidy -j ${{ steps.nproc.outputs.nproc }} -p "${BUILD_DIR}" -quiet -fix -allow-no-checks ${TARGETS} 2>&1 | tee "${OUTPUT_FILE}" - - name: Print errors + - name: Print filtered clang-tidy errors if: ${{ steps.run_clang_tidy.outcome != 'success' }} run: | - sed '/error\||/!d' "${OUTPUT_FILE}" + bin/filter-clang-tidy.py "${OUTPUT_FILE}" - name: Upload clang-tidy output if: ${{ github.event.repository.visibility == 'public' && steps.run_clang_tidy.outcome != 'success' }} @@ -133,41 +137,41 @@ jobs: - name: Write issue header if: ${{ steps.run_clang_tidy.outcome != 'success' }} run: | - cat > "${ISSUE_FILE}" <"${ISSUE_FILE}" < filtered-output.txt || true + # Filter to the unique errors with their source context. + bin/filter-clang-tidy.py "${OUTPUT_FILE}" >"${FILTERED_OUTPUT_FILE}" || true - # If filtered output is empty, use original (might be a different error format) - if [ ! -s filtered-output.txt ]; then - cp "${OUTPUT_FILE}" filtered-output.txt - fi + # If filtered output is empty, use original (might be a different error format) + if [ ! -s "${FILTERED_OUTPUT_FILE}" ]; then + cp "${OUTPUT_FILE}" "${FILTERED_OUTPUT_FILE}" + fi - # Truncate if too large - head -c 60000 filtered-output.txt >> "${ISSUE_FILE}" - if [ "$(wc -c < filtered-output.txt)" -gt 60000 ]; then - echo "" >> "${ISSUE_FILE}" - echo "... (output truncated, see artifacts for full output)" >> "${ISSUE_FILE}" - fi + # Truncate if too large + head -c 60000 "${FILTERED_OUTPUT_FILE}" >>"${ISSUE_FILE}" + if [ "$(wc -c <"${FILTERED_OUTPUT_FILE}")" -gt 60000 ]; then + echo "" >>"${ISSUE_FILE}" + echo "... (output truncated, see artifacts for full output)" >>"${ISSUE_FILE}" + fi - rm filtered-output.txt + rm "${FILTERED_OUTPUT_FILE}" else - echo "No output file found" >> "${ISSUE_FILE}" + echo "No output file found" >>"${ISSUE_FILE}" fi - name: Append issue footer if: ${{ steps.run_clang_tidy.outcome != 'success' }} run: | - cat >> "${ISSUE_FILE}" <>"${ISSUE_FILE}" <> "${GITHUB_OUTPUT}" - - generate-version: - runs-on: ubuntu-latest - outputs: - version: ${{ steps.version.outputs.version }} - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - sparse-checkout: | - .github/actions/generate-version - src/libxrpl/protocol/BuildInfo.cpp - - name: Generate version - id: version - uses: ./.github/actions/generate-version + run: ./generate.py --packaging >>"${GITHUB_OUTPUT}" package: - needs: [generate-matrix, generate-version] + needs: [generate-matrix] if: ${{ github.event.repository.visibility == 'public' }} strategy: fail-fast: false @@ -66,12 +49,12 @@ jobs: permissions: contents: read runs-on: ["self-hosted", "Linux", "X64", "heavy"] - container: ${{ format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-{4}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version, matrix.os.image_sha) }} + container: ${{ matrix.image }} timeout-minutes: 30 steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Download pre-built binary uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -84,14 +67,13 @@ jobs: - name: Build package env: - PKG_VERSION: ${{ needs.generate-version.outputs.version }} PKG_RELEASE: ${{ inputs.pkg_release }} run: ./package/build_pkg.sh - name: Upload package artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: ${{ matrix.artifact_name }}-pkg-${{ needs.generate-version.outputs.version }} + name: ${{ matrix.artifact_name }}-pkg path: | ${{ env.BUILD_DIR }}/debbuild/*.deb ${{ env.BUILD_DIR }}/debbuild/*.ddeb diff --git a/.github/workflows/reusable-strategy-matrix.yml b/.github/workflows/reusable-strategy-matrix.yml index b1232a138f..690aa3d423 100644 --- a/.github/workflows/reusable-strategy-matrix.yml +++ b/.github/workflows/reusable-strategy-matrix.yml @@ -4,15 +4,9 @@ on: workflow_call: inputs: os: - description: 'The operating system to use for the build ("linux", "macos", "windows").' + description: 'The operating system to use for the build ("linux", "macos", "windows", or empty for all).' required: false type: string - strategy_matrix: - # TODO: Support additional strategies, e.g. "ubuntu" for generating all Ubuntu configurations. - description: 'The strategy matrix to use for generating the configurations ("minimal", "all").' - required: false - type: string - default: "minimal" outputs: matrix: description: "The generated strategy matrix." @@ -29,17 +23,17 @@ jobs: matrix: ${{ steps.generate.outputs.matrix }} steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: - python-version: 3.13 + python-version: "3.13" - name: Generate strategy matrix working-directory: .github/scripts/strategy-matrix id: generate env: - GENERATE_CONFIG: ${{ inputs.os != '' && format('--config={0}.json', inputs.os) || '' }} - GENERATE_OPTION: ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} - run: ./generate.py ${GENERATE_OPTION} ${GENERATE_CONFIG} >> "${GITHUB_OUTPUT}" + GENERATE_CONFIG: ${{ inputs.os != '' && format('--config={0}', inputs.os) || '' }} + GENERATE_EVENT: ${{ github.event_name }} + run: ./generate.py ${GENERATE_CONFIG} --event="${GENERATE_EVENT}" >>"${GITHUB_OUTPUT}" diff --git a/.github/workflows/reusable-upload-recipe.yml b/.github/workflows/reusable-upload-recipe.yml index d3fe0f356b..feeee0a621 100644 --- a/.github/workflows/reusable-upload-recipe.yml +++ b/.github/workflows/reusable-upload-recipe.yml @@ -14,7 +14,7 @@ on: description: "The URL of the Conan endpoint to use." required: false type: string - default: https://conan.ripplex.io + default: https://conan.xrplf.org/repository/conan/ secrets: remote_username: @@ -40,10 +40,14 @@ defaults: jobs: upload: runs-on: ubuntu-latest - container: ghcr.io/xrplf/ci/ubuntu-noble:gcc-13-sha-5dd7158 + container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-e29b523 + env: + REMOTE_NAME: ${{ inputs.remote_name }} + CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.remote_username }} + CONAN_PASSWORD_XRPLF: ${{ secrets.remote_password }} steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate build version number id: version @@ -56,15 +60,9 @@ jobs: remote_url: ${{ inputs.remote_url }} - name: Log into Conan remote - env: - REMOTE_NAME: ${{ inputs.remote_name }} - REMOTE_USERNAME: ${{ secrets.remote_username }} - REMOTE_PASSWORD: ${{ secrets.remote_password }} - run: conan remote login "${REMOTE_NAME}" "${REMOTE_USERNAME}" --password "${REMOTE_PASSWORD}" + run: conan remote login "${REMOTE_NAME}" "${CONAN_LOGIN_USERNAME_XRPLF}" --password "${CONAN_PASSWORD_XRPLF}" - name: Upload Conan recipe (version) - env: - REMOTE_NAME: ${{ inputs.remote_name }} run: | conan export . --version=${{ steps.version.outputs.version }} conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/${{ steps.version.outputs.version }} @@ -73,8 +71,6 @@ jobs: # 'develop' branch, see on-trigger.yml. - name: Upload Conan recipe (develop) if: ${{ github.event_name == 'push' }} - env: - REMOTE_NAME: ${{ inputs.remote_name }} run: | conan export . --version=develop conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/develop @@ -83,8 +79,6 @@ jobs: # one of the 'release' branches, see on-pr.yml. - name: Upload Conan recipe (rc) if: ${{ github.event_name == 'pull_request' }} - env: - REMOTE_NAME: ${{ inputs.remote_name }} run: | conan export . --version=rc conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/rc @@ -93,8 +87,6 @@ jobs: # release, see on-tag.yml. - name: Upload Conan recipe (release) if: ${{ startsWith(github.ref, 'refs/tags/') }} - env: - REMOTE_NAME: ${{ inputs.remote_name }} run: | conan export . --version=release conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/release diff --git a/.github/workflows/upload-conan-deps.yml b/.github/workflows/upload-conan-deps.yml index 34dce28334..22ad36d98f 100644 --- a/.github/workflows/upload-conan-deps.yml +++ b/.github/workflows/upload-conan-deps.yml @@ -30,10 +30,11 @@ on: - ".github/scripts/strategy-matrix/**" - conanfile.py - conan.lock + - conan/profiles/** env: CONAN_REMOTE_NAME: xrplf - CONAN_REMOTE_URL: https://conan.ripplex.io + CONAN_REMOTE_URL: https://conan.xrplf.org/repository/conan/ NPROC_SUBTRACT: 2 concurrency: @@ -48,8 +49,6 @@ jobs: # Generate the strategy matrix to be used by the following job. generate-matrix: uses: ./.github/workflows/reusable-strategy-matrix.yml - with: - strategy_matrix: ${{ github.event_name == 'pull_request' && 'minimal' || 'all' }} # Build and upload the dependencies for each configuration. run-upload-conan-deps: @@ -58,19 +57,18 @@ jobs: strategy: fail-fast: false matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} - max-parallel: 10 runs-on: ${{ matrix.architecture.runner }} - container: ${{ contains(matrix.architecture.platform, 'linux') && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-{4}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version, matrix.os.image_sha) || null }} + container: ${{ matrix.image || null }} steps: - name: Cleanup workspace (macOS and Windows) if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }} uses: XRPLF/actions/cleanup-workspace@c7d9ce5ebb03c752a354889ecd870cadfc2b1cd4 - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Prepare runner - uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab + uses: XRPLF/actions/prepare-runner@64ec3cf3b152b4444638f470bbd6df7a7a30c81c with: enable_ccache: false @@ -83,6 +81,12 @@ jobs: with: subtract: ${{ env.NPROC_SUBTRACT }} + - name: Set compiler environment (Linux) + if: ${{ runner.os == 'Linux' }} + uses: ./.github/actions/set-compiler-env + with: + compiler: ${{ matrix.compiler }} + - name: Setup Conan env: SANITIZERS: ${{ matrix.sanitizers }} @@ -104,10 +108,12 @@ jobs: - name: Log into Conan remote if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }} - run: conan remote login "${CONAN_REMOTE_NAME}" "${{ secrets.CONAN_REMOTE_USERNAME }}" --password "${{ secrets.CONAN_REMOTE_PASSWORD }}" + run: conan remote login "${CONAN_REMOTE_NAME}" "${{ secrets.NEXUS_REMOTE_USERNAME }}" --password "${{ secrets.NEXUS_REMOTE_PASSWORD }}" - name: Upload Conan packages if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }} env: FORCE_OPTION: ${{ github.event.inputs.force_upload == 'true' && '--force' || '' }} + CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.NEXUS_REMOTE_USERNAME }} + CONAN_PASSWORD_XRPLF: ${{ secrets.NEXUS_REMOTE_PASSWORD }} run: conan upload "*" --remote="${CONAN_REMOTE_NAME}" --confirm ${FORCE_OPTION} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1313ad567c..910bda8d4c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,6 +15,7 @@ repos: hooks: - id: check-added-large-files args: [--maxkb=400, --enforce-all] + - id: check-executables-have-shebangs - id: trailing-whitespace - id: end-of-file-fixer - id: check-merge-conflict @@ -27,62 +28,81 @@ repos: entry: ./bin/pre-commit/clang_tidy_check.py language: python types_or: [c++, c] - exclude: ^include/xrpl/protocol_autogen - pass_filenames: false # script determines the staged files itself + # .ipp fragments are included by their owning header rather than compiled + # as standalone translation units, so they have no compile_commands.json + # entry to lint (verify_headers checks them transitively). + exclude: '^include/xrpl/protocol_autogen|\.ipp$' - id: fix-include-style name: fix include style entry: ./bin/pre-commit/fix_include_style.py language: python types_or: [c++, c] exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/ + - id: fix-pragma-once + name: fix missing '#pragma once' declarations in header files + language: python + entry: ./bin/pre-commit/fix_pragma_once.py + files: \.(h|hpp)$ - repo: https://github.com/pre-commit/mirrors-clang-format - rev: cd481d7b0bfb5c7b3090c21846317f9a8262e891 # frozen: v22.1.0 + rev: dd18dad857d6133e90bbe478f4f2f22ec0030269 # frozen: v22.1.5 hooks: - id: clang-format args: [--style=file] - "types_or": [c++, c, proto] + types_or: [c++, c, proto] exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/ - - repo: https://github.com/BlankSpruce/gersemi - rev: 0.26.0 + - repo: https://github.com/BlankSpruce/gersemi-pre-commit + rev: e98930bdc210d3387007f9252d8c1694ea7e410f # frozen: 0.27.7 hooks: - id: gersemi - repo: https://github.com/rbubley/mirrors-prettier - rev: c2bc67fe8f8f549cc489e00ba8b45aa18ee713b1 # frozen: v3.8.1 + rev: 39e2973981e6d2f9b6c543b0086a2d2393abdc89 # frozen: v3.9.4 hooks: - id: prettier args: [--end-of-line=auto] - repo: https://github.com/psf/black-pre-commit-mirror - rev: ea488cebbfd88a5f50b8bd95d5c829d0bb76feb8 # frozen: 26.1.0 + rev: 4160603246a6b365d4a2af661c6d71b0a0f50478 # frozen: 26.5.1 hooks: - id: black - - repo: https://github.com/openstack/bashate - rev: 5798d24d571676fc407e81df574c1ef57b520f23 # frozen: 2.1.1 + - repo: https://github.com/scop/pre-commit-shfmt + rev: 05c1426671b9237fb5e1444dd63aa5731bec0dfb # frozen: v3.13.1-1 hooks: - - id: bashate - args: ["--ignore=E006"] + - id: shfmt + args: [--write, --indent=4, --case-indent=true] + + - repo: local + hooks: + - id: format-inline-bash-workflows + name: "format `run:` blocks in workflows/actions" + entry: ./.github/scripts/format-inline-bash.py + language: python + files: ^\.github/(workflows|actions)/.*\.ya?ml$ + - id: format-inline-bash-markdown + name: "format ```bash blocks in markdown" + entry: ./.github/scripts/format-inline-bash.py + language: python + files: \.md$ - repo: https://github.com/streetsidesoftware/cspell-cli - rev: a42085ade523f591dca134379a595e7859986445 # frozen: v9.7.0 + rev: ea11f9efc0bec520073405bc30552da887ba71bc # frozen: v10.0.1 hooks: - - id: cspell # Spell check changed files + - id: cspell + name: check changed files spelling exclude: | (?x)^( - .config/cspell.config.yaml| + \.cspell\.config\.yaml| include/xrpl/protocol_autogen/(transactions|ledger_entries)/.* )$ - - id: cspell # Spell check the commit message + - id: cspell name: check commit message spelling args: - --no-must-find-files - --no-progress - --no-summary - - --files - - .git/COMMIT_EDITMSG stages: [commit-msg] - repo: local diff --git a/BUILD.md b/BUILD.md index 632832c002..a15c94edc9 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,26 +1,57 @@ -| :warning: **WARNING** :warning: | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| These instructions assume you have a C++ development environment ready with Git, Python, Conan, CMake, and a C++ compiler. For help setting one up on Linux, macOS, or Windows, [see this guide](./docs/build/environment.md). | +| :warning: **WARNING** :warning: | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| These instructions assume you have a C++ development environment ready with Git, Python, Conan, CMake, and a C++ compiler. For help setting one up on Linux, macOS, or Windows, [see this guide](./docs/build/environment.md).

These instructions also assume a basic familiarity with Conan and CMake. If you are unfamiliar with Conan, you can read our [crash course](./docs/build/conan.md) or the official [Getting Started][conan-getting-started] walkthrough. | -> These instructions also assume a basic familiarity with Conan and CMake. -> If you are unfamiliar with Conan, you can read our -> [crash course](./docs/build/conan.md) or the official [Getting Started][3] -> walkthrough. +## Minimum Requirements -## Branches +See [System Requirements](https://xrpl.org/system-requirements.html). -For a stable release, choose the `master` branch or one of the [tagged -releases](https://github.com/XRPLF/rippled/releases). +Building xrpld generally requires Git, Python, Conan, CMake, and a C++ +compiler. + +- [Python](https://www.python.org/downloads/) +- [Conan](https://conan.io/downloads.html) +- [CMake](https://cmake.org/download/) + +You can verify that the required tools are installed and runnable with: ```bash -git checkout master +./bin/check-tools.sh ``` -For the latest release candidate, choose the `release` branch. +`xrpld` is written in the C++23 dialect. The [tested compiler versions][cpp23-support] are: -```bash -git checkout release -``` +| Compiler | Version | +| ----------- | --------------- | +| GCC | 15.2 | +| Clang | 22 | +| Apple Clang | 21 | +| MSVC | 19.44[^windows] | + +## Operating Systems + +Please see the [environment setup guide](./docs/build/environment.md) for detailed instructions for all platforms. + +### Linux + +The Ubuntu Linux distribution has received the highest level of quality +assurance, testing, and support. We also support Red Hat and use Debian +internally. +Our Linux CI tooling is distro-independent and uses a Nix-based environment, so it should be possible to build on other Linux distributions as well, although we have not tested them. + +### macOS + +Many `xrpld` engineers use macOS for development. + +### Windows + +Windows is used by some engineers for development only. + +[^windows]: Windows is not recommended for production use. + +## Steps + +### Branches For the latest set of untested features, or to contribute, choose the `develop` branch. @@ -29,55 +60,15 @@ branch. git checkout develop ``` -## Minimum Requirements +For a release candidate, choose the relevant release branch, e.g. +`release/3.2.x`. -See [System Requirements](https://xrpl.org/system-requirements.html). +```bash +git checkout release/3.2.x +``` -Building xrpld generally requires git, Python, Conan, CMake, and a C++ -compiler. Some guidance on setting up such a [C++ development environment can be -found here](./docs/build/environment.md). - -- [Python 3.11](https://www.python.org/downloads/), or higher -- [Conan 2.17](https://conan.io/downloads.html)[^1], or higher -- [CMake 3.22](https://cmake.org/download/), or higher - -[^1]: - It is possible to build with Conan 1.60+, but the instructions are - significantly different, which is why we are not recommending it. - -`xrpld` is written in the C++20 dialect and includes the `` header. -The [minimum compiler versions][2] required are: - -| Compiler | Version | -| ----------- | --------- | -| GCC | 12 | -| Clang | 16 | -| Apple Clang | 16 | -| MSVC | 19.44[^3] | - -### Linux - -The Ubuntu Linux distribution has received the highest level of quality -assurance, testing, and support. We also support Red Hat and use Debian -internally. - -Here are [sample instructions for setting up a C++ development environment on -Linux](./docs/build/environment.md#linux). - -### Mac - -Many xrpld engineers use macOS for development. - -Here are [sample instructions for setting up a C++ development environment on -macOS](./docs/build/environment.md#macos). - -### Windows - -Windows is used by some engineers for development only. - -[^3]: Windows is not recommended for production use. - -## Steps +For a stable release, choose one of the [tagged +releases](https://github.com/XRPLF/rippled/releases). ### Set Up Conan @@ -86,18 +77,11 @@ Conan, CMake, and a C++ compiler, you may need to set up your Conan profile. These instructions assume a basic familiarity with Conan and CMake. If you are unfamiliar with Conan, then please read [this crash course](./docs/build/conan.md) or the official -[Getting Started][3] walkthrough. +[Getting Started][conan-getting-started] walkthrough. -#### Conan lockfile +#### Profiles -To achieve reproducible dependencies, we use a [Conan lockfile](https://docs.conan.io/2/tutorial/versioning/lockfiles.html), -which has to be updated every time dependencies change. - -Please see the [instructions on how to regenerate the lockfile](conan/lockfile/README.md). - -#### Default profile - -We recommend that you import the provided `conan/profiles/default` profile: +We recommend that you install our Conan profiles: ```bash conan config install conan/profiles/ -tf $(conan config home)/profiles/ @@ -109,222 +93,15 @@ You can check your Conan profile by running: conan profile show ``` -#### Custom profile +If the default profile is not suitable for your environment, you can create a custom profile and pass it to Conan. +More information on customizing Conan can be found in the [Advanced Conan configuration](./docs/build/advanced_conan.md). -If the default profile does not work for you and you do not yet have a Conan -profile, you can create one by running: +#### Add xrplf remote + +Run the following command to add the `xrplf` remote, which hosts some of our dependencies: ```bash -conan profile detect -``` - -You may need to make changes to the profile to suit your environment. You can -refer to the provided `conan/profiles/default` profile for inspiration, and you -may also need to apply the required [tweaks](#conan-profile-tweaks) to this -default profile. - -### Patched recipes - -Occasionally, we need patched recipes or recipes not present in Conan Center. -We maintain a fork of the Conan Center Index -[here](https://github.com/XRPLF/conan-center-index/) containing the modified and newly added recipes. - -To ensure our patched recipes are used, you must add our Conan remote at a -higher index than the default Conan Center remote, so it is consulted first. You -can do this by running: - -```bash -conan remote add --index 0 xrplf https://conan.ripplex.io -``` - -Alternatively, you can pull our recipes from the repository and export them locally: - -```bash -# Define which recipes to export. -recipes=('abseil' 'ed25519' 'mpt-crypto' 'openssl' 'secp256k1' 'snappy' 'soci' 'wasm-xrplf' 'wasmi') - -# Selectively check out the recipes from our CCI fork. -cd external -mkdir -p conan-center-index -cd conan-center-index -git init -git remote add origin git@github.com:XRPLF/conan-center-index.git -git sparse-checkout init -for recipe in "${recipes[@]}"; do - echo "Checking out recipe '${recipe}'..." - git sparse-checkout add recipes/${recipe} -done -git fetch origin master -git checkout master - -./export_all.sh -cd ../../ -``` - -In the case we switch to a newer version of a dependency that still requires a -patch or add a new dependency, it will be necessary for you to pull in the changes and re-export the -updated dependencies with the newer version. However, if we switch to a newer -version that no longer requires a patch, no action is required on your part, as -the new recipe will be automatically pulled from the official Conan Center. - -> [!NOTE] -> You might need to add `--lockfile=""` to your `conan install` command -> to avoid automatic use of the existing `conan.lock` file when you run -> `conan export` manually on your machine -> -> This is not recommended though, as you might end up using different revisions of recipes. - -### Conan profile tweaks - -#### Missing compiler version - -If you see an error similar to the following after running `conan profile show`: - -```bash -ERROR: Invalid setting '17' is not a valid 'settings.compiler.version' value. -Possible values are ['5.0', '5.1', '6.0', '6.1', '7.0', '7.3', '8.0', '8.1', -'9.0', '9.1', '10.0', '11.0', '12.0', '13', '13.0', '13.1', '14', '14.0', '15', -'15.0', '16', '16.0'] -Read "http://docs.conan.io/2/knowledge/faq.html#error-invalid-setting" -``` - -you need to add your compiler to the list of compiler versions in -`$(conan config home)/settings_user.yml`, by adding the required version number(s) -to the `version` array specific for your compiler. For example: - -```yaml -compiler: - apple-clang: - version: ["17.0"] -``` - -#### Multiple compilers - -If you have multiple compilers installed, make sure to select the one to use in -your default Conan configuration **before** running `conan profile detect`, by -setting the `CC` and `CXX` environment variables. - -For example, if you are running MacOS and have [homebrew -LLVM@18](https://formulae.brew.sh/formula/llvm@18), and want to use it as a -compiler in the new Conan profile: - -```bash -export CC=$(brew --prefix llvm@18)/bin/clang -export CXX=$(brew --prefix llvm@18)/bin/clang++ -conan profile detect -``` - -You should also explicitly set the path to the compiler in the profile file, -which helps to avoid errors when `CC` and/or `CXX` are set and disagree with the -selected Conan profile. For example: - -```text -[conf] -tools.build:compiler_executables={'c':'/usr/bin/gcc','cpp':'/usr/bin/g++'} -``` - -#### Multiple profiles - -You can manage multiple Conan profiles in the directory -`$(conan config home)/profiles`, for example renaming `default` to a different -name and then creating a new `default` profile for a different compiler. - -#### Select language - -The default profile created by Conan will typically select different C++ dialect -than C++20 used by this project. You should set `20` in the profile line -starting with `compiler.cppstd=`. For example: - -```bash -sed -i.bak -e 's|^compiler\.cppstd=.*$|compiler.cppstd=20|' $(conan config home)/profiles/default -``` - -#### Select standard library in Linux - -**Linux** developers will commonly have a default Conan [profile][] that -compiles with GCC and links with libstdc++. If you are linking with libstdc++ -(see profile setting `compiler.libcxx`), then you will need to choose the -`libstdc++11` ABI: - -```bash -sed -i.bak -e 's|^compiler\.libcxx=.*$|compiler.libcxx=libstdc++11|' $(conan config home)/profiles/default -``` - -#### Select architecture and runtime in Windows - -**Windows** developers may need to use the x64 native build tools. An easy way -to do that is to run the shortcut "x64 Native Tools Command Prompt" for the -version of Visual Studio that you have installed. - -Windows developers must also build `xrpld` and its dependencies for the x64 -architecture: - -```bash -sed -i.bak -e 's|^arch=.*$|arch=x86_64|' $(conan config home)/profiles/default -``` - -**Windows** developers also must select static runtime: - -```bash -sed -i.bak -e 's|^compiler\.runtime=.*$|compiler.runtime=static|' $(conan config home)/profiles/default -``` - -#### Clang workaround for grpc - -If your compiler is clang, version 19 or later, or apple-clang, version 17 or -later, you may encounter a compilation error while building the `grpc` -dependency: - -```text -In file included from .../lib/promise/try_seq.h:26: -.../lib/promise/detail/basic_seq.h:499:38: error: a template argument list is expected after a name prefixed by the template keyword [-Wmissing-template-arg-list-after-template-kw] - 499 | Traits::template CallSeqFactory(f_, *cur_, std::move(arg))); - | ^ -``` - -The workaround for this error is to add two lines to profile: - -```text -[conf] -tools.build:cxxflags=['-Wno-missing-template-arg-list-after-template-kw'] -``` - -#### Workaround for gcc 12 - -If your compiler is gcc, version 12, and you have enabled `werr` option, you may -encounter a compilation error such as: - -```text -/usr/include/c++/12/bits/char_traits.h:435:56: error: 'void* __builtin_memcpy(void*, const void*, long unsigned int)' accessing 9223372036854775810 or more bytes at offsets [2, 9223372036854775807] and 1 may overlap up to 9223372036854775813 bytes at offset -3 [-Werror=restrict] - 435 | return static_cast(__builtin_memcpy(__s1, __s2, __n)); - | ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~ -cc1plus: all warnings being treated as errors -``` - -The workaround for this error is to add two lines to your profile: - -```text -[conf] -tools.build:cxxflags=['-Wno-restrict'] -``` - -#### Workaround for clang 16 - -If your compiler is clang, version 16, you may encounter compilation error such -as: - -```text -In file included from .../boost/beast/websocket/stream.hpp:2857: -.../boost/beast/websocket/impl/read.hpp:695:17: error: call to 'async_teardown' is ambiguous - async_teardown(impl.role, impl.stream(), - ^~~~~~~~~~~~~~ -``` - -The workaround for this error is to add two lines to your profile: - -```text -[conf] -tools.build:cxxflags=['-DBOOST_ASIO_DISABLE_CONCEPTS'] +conan remote add --index 0 --force xrplf https://conan.xrplf.org/repository/conan/ ``` ### Set Up Ccache @@ -333,14 +110,7 @@ To speed up repeated compilations, we recommend that you install [ccache](https://ccache.dev), a tool that wraps your compiler so that it can cache build objects locally. -#### Linux - -You can install it using the package manager, e.g. `sudo apt install ccache` -(Ubuntu) or `sudo dnf install ccache` (RHEL). - -#### macOS - -You can install it using Homebrew, i.e. `brew install ccache`. +On Linux and macOS, `ccache` is included in the [Nix development shell](./docs/build/nix.md). #### Windows @@ -427,16 +197,19 @@ install ccache --version 4.11.3 --allow-downgrade`. Single-config generators: ``` - cmake --build . + cmake --build . --parallel N ``` Multi-config generators: ``` - cmake --build . --config Release - cmake --build . --config Debug + cmake --build . --config Release --parallel N + cmake --build . --config Debug --parallel N ``` + Replace the `--parallel` parameter N with the desired number of parallel jobs. A common starting point is half of the number of available CPU + cores. + 5. Test xrpld. Single-config generators: @@ -544,21 +317,41 @@ See [Sanitizers docs](./docs/build/sanitizers.md) for more details. ## Options -| Option | Default Value | Description | -| ---------- | ------------- | -------------------------------------------------------------- | -| `assert` | OFF | Enable assertions. | -| `coverage` | OFF | Prepare the coverage report. | -| `tests` | OFF | Build tests. | -| `unity` | OFF | Configure a unity build. | -| `xrpld` | OFF | Build the xrpld application, and not just the libxrpl library. | -| `werr` | OFF | Treat compilation warnings as errors | -| `wextra` | OFF | Enable additional compilation warnings | +| Option | Default Value | Description | +| ---------------- | ------------- | ----------------------------------------------------------------------------- | +| `assert` | OFF | Force enabling assertions. | +| `coverage` | OFF | Prepare the coverage report. | +| `tests` | OFF | Build tests. | +| `unity` | OFF | Configure a unity build. | +| `verify_headers` | ON | Make the `verify-headers` target available to compile each header on its own. | +| `xrpld` | OFF | Build the xrpld application, and not just the libxrpl library. | +| `werr` | OFF | Treat compilation warnings as errors | +| `wextra` | OFF | Enable additional compilation warnings | -[Unity builds][5] may be faster for the first build (at the cost of much more +[Unity builds][unity-build] may be faster for the first build (at the cost of much more memory) since they concatenate sources into fewer translation units. Non-unity builds may be faster for incremental builds, and can be helpful for detecting `#include` omissions. +### Verifying headers + +The regular build only compiles `.cpp` files, so a header is only ever checked +through whatever translation unit happens to include it. A header that forgets +an `#include` is not caught as long as every `.cpp` that uses it includes its +missing dependency first. The `verify_headers` option (ON by default) adds a +`verify-headers` target that compiles every header on its own, which fails if a +header is not self-contained: + +```bash +cmake --build . --target verify-headers +``` + +The per-header objects are excluded from the `all` target, so a normal build +never compiles them; they are built only through `verify-headers`. The generated +translation units do appear in `compile_commands.json`, so clang-tidy (and +clangd and IDEs) can lint each header on its own. Pass `-Dverify_headers=OFF` to +omit them entirely. + ## Troubleshooting ### Conan @@ -580,14 +373,14 @@ After any updates or changes to dependencies, you may need to do the following: conan remove '*' ``` -3. Re-run [conan export](#patched-recipes) if needed. -4. [Regenerate lockfile](#conan-lockfile). +3. Re-run [conan export](./docs/build/advanced_conan.md#patched-recipes) if needed. +4. [Regenerate lockfile](./docs/build/advanced_conan.md#conan-lockfile). 5. Re-run [conan install](#build-and-test). #### ERROR: Package not resolved If you're seeing an error like `ERROR: Package 'snappy/1.1.10' not resolved: Unable to find 'snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1756234314.246' in remotes.`, -please add `xrplf` remote or re-run `conan export` for [patched recipes](#patched-recipes). +please [add `xrplf` remote](#add-xrplf-remote) or re-run `conan export` for [patched recipes](./docs/build/advanced_conan.md#patched-recipes). ### `protobuf/port_def.inc` file not found @@ -607,28 +400,9 @@ For example, if you want to build Debug: 1. For conan install, pass `--settings build_type=Debug` 2. For cmake, pass `-DCMAKE_BUILD_TYPE=Debug` -## Add a Dependency - -If you want to experiment with a new package, follow these steps: - -1. Search for the package on [Conan Center](https://conan.io/center/). -2. Modify [`conanfile.py`](./conanfile.py): - - Add a version of the package to the `requires` property. - - Change any default options for the package by adding them to the - `default_options` property (with syntax `'$package:$option': $value`). -3. Modify [`CMakeLists.txt`](./CMakeLists.txt): - - Add a call to `find_package($package REQUIRED)`. - - Link a library from the package to the target `xrpl_libs` - (search for the existing call to `target_link_libraries(xrpl_libs INTERFACE ...)`). -4. Start coding! Don't forget to include whatever headers you need from the package. - -[1]: https://github.com/conan-io/conan-center-index/issues/13168 -[2]: https://en.cppreference.com/w/cpp/compiler_support/20 -[3]: https://docs.conan.io/en/latest/getting_started.html -[5]: https://en.wikipedia.org/wiki/Unity_build -[6]: https://github.com/boostorg/beast/issues/2648 -[7]: https://github.com/boostorg/beast/issues/2661 +[cpp23-support]: https://en.cppreference.com/w/cpp/compiler_support/23 +[conan-getting-started]: https://docs.conan.io/en/latest/getting_started.html +[unity-build]: https://en.wikipedia.org/wiki/Unity_build [gcovr]: https://gcovr.com/en/stable/getting-started.html [python-pip]: https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/ [build_type]: https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html -[profile]: https://docs.conan.io/en/latest/reference/profiles.html diff --git a/CMakeLists.txt b/CMakeLists.txt index d315a5dcec..1e8befcc8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") project(xrpl) set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -57,6 +57,8 @@ if(target) ) endif() +include(PatchNixBinary) + include(XrplSanity) include(XrplVersion) include(XrplSettings) @@ -88,6 +90,7 @@ find_package(ed25519 REQUIRED) find_package(gRPC REQUIRED) find_package(LibArchive REQUIRED) find_package(lz4 REQUIRED) +find_package(mpt-crypto REQUIRED) find_package(nudb REQUIRED) find_package(OpenSSL REQUIRED) find_package(secp256k1 REQUIRED) @@ -100,6 +103,7 @@ target_link_libraries( INTERFACE ed25519::ed25519 lz4::lz4 + mpt-crypto::mpt-crypto OpenSSL::Crypto OpenSSL::SSL secp256k1::secp256k1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 25dd7ac059..fc93223925 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,9 +14,9 @@ The following branches exist in the main project repository: - `develop`: The latest set of unreleased features, and the most common starting point for contributions. -- `release`: The latest beta release or release candidate. -- `master`: The latest stable release. -- `gh-pages`: The documentation for this project, built by Doxygen. +- `release/*` (e.g. `release/3.2.x`): Release branches, one per release line, + holding the latest release candidate, or stable release for that line. + Stable releases are published as [tagged releases](https://github.com/XRPLF/rippled/releases). The tip of each branch must be signed. In order for GitHub to sign a squashed commit that it builds from your pull request, GitHub must know @@ -130,11 +130,9 @@ tl;dr ## Pull requests In general, pull requests use `develop` as the base branch. -The exceptions are -- Fixes and improvements to a release candidate use `release` as the - base. -- Hotfixes use `master` as the base. +The exceptions are fixes, improvements, and hotfixes for an existing release, +which use that release's branch (e.g. `release/3.2.x`) as the base. If your changes are not quite ready, but you want to make it easily available for preliminary examination or review, you can create a "Draft" pull request. @@ -216,7 +214,7 @@ coherent rather than a set of _thou shalt not_ commandments. ## Formatting -All code must conform to `clang-format` version 21, +All code must conform to `clang-format` version 22, according to the settings in [`.clang-format`](./.clang-format), unless the result would be unreasonably difficult to read or maintain. To demarcate lines that should be left as-is, surround them with comments like @@ -261,7 +259,7 @@ This ensures that configuration changes don't introduce new warnings across the ### Installing clang-tidy -See the [environment setup guide](./docs/build/environment.md#clang-tidy) for platform-specific installation instructions. +See the [environment setup guide](./docs/build/environment.md#clang-tidy) for how to get clang-tidy. ### Running clang-tidy locally diff --git a/bin/check-tools.sh b/bin/check-tools.sh new file mode 100755 index 0000000000..808f384d5b --- /dev/null +++ b/bin/check-tools.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash +# +# check-tools.sh — verify the xrpld development tooling is present and runnable. +# +# Works on Linux, macOS, and Windows (Git Bash / MSYS). For every expected tool +# it runs a version probe, collecting anything that is missing or fails to run, +# and prints a summary at the end (exiting non-zero if anything is missing). +# +# The tool set is platform-aware: +# - Linux: the full Nix CI environment (see nix/packages.nix, nix/ci-env.nix), +# with GCC, Clang and the sanitizer/coverage tooling. This script is +# run during the Nix Docker image build (nix/docker/Dockerfile), so +# the Linux list is kept in sync with that environment. +# - macOS: the same tooling, minus GCC/g++/gcov/mold +# - Windows: the core build tools only (CMake, Conan, Git, Python). +# MSVC is expected to be provided separately and is not checked here. +# +# Some tools (clang-format, doxygen, gcovr, gh, git-cliff, gpg, pre-commit, +# run-clang-tidy) are present in our Linux CI images and in local development +# setups, but not in the macOS CI environment. They are checked everywhere +# except when running in CI on macOS. +# +# Environment variables: +# CI if set, skip the tools above when on macOS. +# CHECK_TOOLS_SKIP_CLONE if set, skip the git-over-HTTPS connectivity check. + +set -uo pipefail + +missing=() +checked=0 + +# check [probe-command...] +# Runs the probe (default: " --version") quietly. Records as +# missing if the command is not found or exits non-zero. +check() { + local name="$1" + shift + local -a probe=("$@") + if [ "${#probe[@]}" -eq 0 ]; then + probe=("${name}" --version) + fi + + echo "Checking ${name}..." + checked=$((checked + 1)) + if "${probe[@]}" | head -n 1; then + printf ' [ ok ] %s\n' "${name}" + else + printf ' [MISS] %s\n' "${name}" + missing+=("${name}") + fi +} + +case "$(uname -s)" in + Linux*) os=linux ;; + Darwin*) os=macos ;; + MINGW* | MSYS* | CYGWIN*) os=windows ;; + *) + echo "Unknown OS: $(uname -s)" >&2 + exit 1 + ;; +esac + +echo "Detected OS: ${os} ($(uname -s) $(uname -m))" +echo +echo "Core build tools:" +check cmake +check conan +check git +if [ "${os}" = "windows" ]; then + check python python --version +else + check python3 +fi + +# The full development toolchain. Available from Nix on Linux and macOS; on +# Windows these are typically not installed, so they are skipped. +if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then + echo + echo "Development tooling:" + check ccache + check clang + check clang++ + check ClangBuildAnalyzer + check curl + check file + check less + check make + check netstat which netstat + check ninja + check perl + check pkg-config + check vim + check zip + + # These tools are present in our Linux CI images and in local development + # setups, but not in the macOS CI environment. So check them everywhere + # except when running in CI on macOS. + if [ "${os}" = "linux" ] || [ -z "${CI:-}" ]; then + check clang-format + check dot + check doxygen + check gcovr + check gh + check git-cliff + check git-lfs + check gpg + # pre-commit, or its alternative implementation prek + check pre-commit sh -c 'pre-commit --version || prek --version' + check run-clang-tidy run-clang-tidy --help + fi +fi + +# GCC is the default compiler on Linux. macOS uses the system Apple Clang +# instead, so GCC/g++/gcov are not expected there. +if [ "${os}" = "linux" ]; then + echo + echo "GCC toolchain:" + check gcc + check g++ + check gcov + + echo + echo "Mold:" + check mold +fi + +if [ "${os}" = "windows" ]; then + echo + echo "Note: on Windows the C++ compiler is MSVC, which is provided" + echo " separately (e.g. via Visual Studio) and is not checked here." +fi + +# A simple test to verify that git can clone a repository over HTTPS +# (i.e. the CA bundle is wired up). Clone to a temp dir and clean up. +if [ -n "${CHECK_TOOLS_SKIP_CLONE:-}" ]; then + echo + echo "Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set)." +else + echo + echo "Connectivity check:" + checked=$((checked + 1)) + tmp_clone="$(mktemp -d)" + if git clone --depth 1 https://github.com/XRPLF/actions.git "${tmp_clone}/actions" >/dev/null 2>&1; then + printf ' [ ok ] git clone over HTTPS\n' + else + printf ' [MISS] git clone over HTTPS\n' + missing+=("git-https-clone") + fi + rm -rf "${tmp_clone}" +fi + +echo +if [ "${#missing[@]}" -eq 0 ]; then + echo "All ${checked} checked tools are present and runnable." +else + echo "Missing or non-functional tools (${#missing[@]} of ${checked}):" >&2 + for tool in "${missing[@]}"; do + echo " - ${tool}" >&2 + done + exit 1 +fi diff --git a/bin/filter-clang-tidy.py b/bin/filter-clang-tidy.py new file mode 100755 index 0000000000..204a4e36f3 --- /dev/null +++ b/bin/filter-clang-tidy.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 + +""" +Reduce run-clang-tidy output to its unique errors. + +It does two things: + + 1. Filters the raw output down to diagnostics and their source-context lines + (the indented " 103 | ..." / " | ^" lines clang-tidy prints), + matching the "path:line:col: error:" diagnostic shape. + + 2. Deduplicates. The same diagnostic in a header is reported once per + translation unit that includes it, so identical error blocks are collapsed + to their first occurrence. + +An "error block" is an "error:" line together with the indented context lines +and any "note:" lines that follow it (up to the next "error:" line). Blocks are +compared as a whole, so an error stays attached to its own context, and +first-occurrence order is preserved. + +The deduplicated output goes to stdout; a summary of unique error counts per +check is printed to stderr. + +Usage: + bin/filter-clang-tidy.py [INPUT_FILE] # read from file, or + run-clang-tidy ... | bin/filter-clang-tidy.py # read from stdin +""" + +import re +import sys +from collections import Counter + +# A clang-tidy diagnostic line looks like "path:line:col: error: msg [check]". +# Matching on that shape (rather than a loose "error" substring) avoids treating +# progress lines whose paths contain "error" as diagnostics, e.g. +# [284/850][0.7s] /nix/.../clang-tidy ... src/.../error.cpp +DIAG_RE = re.compile(r":\d+:\d+: (?:error|warning|note):") +ERROR_RE = re.compile(r":\d+:\d+: error:") +CHECK_RE = re.compile(r" error: .*\[([^\],]+)") + + +def filter_and_dedup(lines: list[str]) -> list[str]: + """Keep diagnostics with their context, then drop duplicate error blocks.""" + blocks: list[str] = [] + seen: set[str] = set() + current: list[str] = [] + + def flush() -> None: + if not current: + return + block = "".join(current) + if block not in seen: + seen.add(block) + blocks.append(block) + + for line in lines: + # Keep only diagnostics and their indented source-context lines; drop + # progress/status output and blank lines. + if not (DIAG_RE.search(line) or line[:1] in (" ", "\t")): + continue + # An "error:" line starts a new block; its context and any following + # "note:" lines (and their context) belong to it. + if ERROR_RE.search(line): + flush() + current = [] + current.append(line) + flush() + + return blocks + + +def summarize(blocks: list[str]) -> Counter[str]: + """Count unique errors per check name (e.g. "bugprone-branch-clone").""" + counts: Counter[str] = Counter() + for block in blocks: + # The error line is the first line of the block. + match = CHECK_RE.search(block.splitlines()[0]) + if match: + counts[match.group(1)] += 1 + return counts + + +def main() -> int: + if len(sys.argv) > 1 and sys.argv[1] != "-": + with open(sys.argv[1], encoding="utf-8") as f: + lines = f.readlines() + else: + lines = sys.stdin.readlines() + + blocks = filter_and_dedup(lines) + # Blank line between blocks so distinct errors are easy to tell apart. + sys.stdout.write("\n".join(blocks)) + + print("\nUnique errors per check:", file=sys.stderr) + for check, count in summarize(blocks).most_common(): + print(f"{count:>4} {check}", file=sys.stderr) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bin/git/setup-upstreams.sh b/bin/git/setup-upstreams.sh index 57c3f935f9..97c84f5507 100755 --- a/bin/git/setup-upstreams.sh +++ b/bin/git/setup-upstreams.sh @@ -1,8 +1,8 @@ #!/bin/bash if [[ $# -ne 1 || "$1" == "--help" || "$1" == "-h" ]]; then - name=$( basename $0 ) - cat <<- USAGE + name=$(basename $0) + cat <<-USAGE Usage: $name Where is the Github username of the upstream repo. e.g. XRPLF @@ -14,7 +14,7 @@ fi shift user="$1" # Get the origin URL. Expect it be an SSH-style URL -origin=$( git remote get-url origin ) +origin=$(git remote get-url origin) if [[ "${origin}" == "" ]]; then echo Invalid origin remote >&2 exit 1 @@ -22,11 +22,11 @@ fi # echo "Origin: ${origin}" # Parse the origin ifs_orig="${IFS}" -IFS=':' read remote originpath <<< "${origin}" +IFS=':' read remote originpath <<<"${origin}" # echo "Remote: ${remote}, Originpath: ${originpath}" -IFS='@' read sshuser server <<< "${remote}" +IFS='@' read sshuser server <<<"${remote}" # echo "SSHUser: ${sshuser}, Server: ${server}" -IFS='/' read originuser repo <<< "${originpath}" +IFS='/' read originuser repo <<<"${originpath}" # echo "Originuser: ${originuser}, Repo: ${repo}" if [[ "${sshuser}" == "" || "${server}" == "" || "${originuser}" == "" || "${repo}" == "" ]]; then echo "Can't parse origin URL: ${origin}" >&2 @@ -35,9 +35,9 @@ fi upstream="https://${server}/${user}/${repo}" upstreampush="${remote}:${user}/${repo}" upstreamgroup="upstream upstream-push" -current=$( git remote get-url upstream 2>/dev/null ) -currentpush=$( git remote get-url upstream-push 2>/dev/null ) -currentgroup=$( git config remotes.upstreams ) +current=$(git remote get-url upstream 2>/dev/null) +currentpush=$(git remote get-url upstream-push 2>/dev/null) +currentgroup=$(git config remotes.upstreams) if [[ "${current}" == "${upstream}" ]]; then echo "Upstream already set up correctly. Skip" elif [[ -n "${current}" && "${current}" != "${upstream}" && "${current}" != "${upstreampush}" ]]; then @@ -45,9 +45,9 @@ elif [[ -n "${current}" && "${current}" != "${upstream}" && "${current}" != "${u else if [[ "${current}" == "${upstreampush}" ]]; then echo "Upstream set to dangerous push URL. Update." - _run git remote rename upstream upstream-push || \ - _run git remote remove upstream - currentpush=$( git remote get-url upstream-push 2>/dev/null ) + _run git remote rename upstream upstream-push || + _run git remote remove upstream + currentpush=$(git remote get-url upstream-push 2>/dev/null) fi _run git remote add upstream "${upstream}" fi diff --git a/bin/git/squash-branches.sh b/bin/git/squash-branches.sh index eb4aefe23c..ba63f9c148 100755 --- a/bin/git/squash-branches.sh +++ b/bin/git/squash-branches.sh @@ -1,8 +1,8 @@ #!/bin/bash if [[ $# -lt 3 || "$1" == "--help" || "$1" = "-h" ]]; then - name=$( basename $0 ) - cat <<- USAGE + name=$(basename $0) + cat <<-USAGE Usage: $name workbranch base/branch user/branch [user/branch [...]] * workbranch will be created locally from base/branch @@ -16,7 +16,7 @@ fi work="$1" shift -branches=( $( echo "${@}" | sed "s/:/\//" ) ) +branches=($(echo "${@}" | sed "s/:/\//")) base="${branches[0]}" unset branches[0] @@ -24,10 +24,10 @@ set -e users=() for b in "${branches[@]}"; do - users+=( $( echo $b | cut -d/ -f1 ) ) + users+=($(echo $b | cut -d/ -f1)) done -users=( $( printf '%s\n' "${users[@]}" | sort -u ) ) +users=($(printf '%s\n' "${users[@]}" | sort -u)) git fetch --multiple upstreams "${users[@]}" git checkout -B "$work" --no-track "$base" @@ -40,7 +40,7 @@ done # Make sure the commits look right git log --show-signature "$base..HEAD" -parts=( $( echo $base | sed "s/\// /" ) ) +parts=($(echo $base | sed "s/\// /")) repo="${parts[0]}" b="${parts[1]}" push=$repo @@ -50,7 +50,7 @@ fi if [[ "$repo" == "upstream" ]]; then repo="upstreams" fi -cat << PUSH +cat </dev/null ) || true +push=$(git rev-parse --abbrev-ref --symbolic-full-name '@{push}' \ + 2>/dev/null) || true if [[ "${push}" != "" ]]; then echo "Warning: ${push} may already exist." fi -build=$( find -name BuildInfo.cpp ) -sed 's/\(^.*versionString =\).*$/\1 "'${version}'"/' ${build} > version.cpp && \ -diff "${build}" version.cpp && exit 1 || \ -mv -vi version.cpp ${build} +build=$(find -name BuildInfo.cpp) +sed 's/\(^.*versionString =\).*$/\1 "'${version}'"/' ${build} >version.cpp && + diff "${build}" version.cpp && exit 1 || + mv -vi version.cpp ${build} git diff @@ -47,7 +47,7 @@ git commit -S -m "Set version to ${version}" git log --oneline --first-parent ${base}^.. -cat << PUSH +cat <&2 + exit 1 +fi + +# shellcheck source=/dev/null +. /etc/os-release + +echo "Detected OS: ${ID} ${VERSION_ID:-}" + +case "${ID}" in + ubuntu | debian | rhel | centos | rocky | almalinux) + echo "Supported OS detected: ${ID}" + ;; + *) + echo "ERROR: unsupported OS '${ID}'. Supported: debian, ubuntu, rhel-family" >&2 + exit 1 + ;; +esac + +function preinstall() { + case "${ID}" in + ubuntu) + apt-get update -y + apt-get install -y --no-install-recommends \ + gnupg \ + software-properties-common + add-apt-repository -y ppa:ubuntu-toolchain-r/test + ;; + esac +} + +function install() { + case "${ID}" in + debian | ubuntu) + apt-get update -y + apt-get install -y --no-install-recommends \ + libasan8 \ + libtsan2 \ + libubsan1 + ;; + + rhel | centos | rocky | almalinux) + dnf install -y \ + libasan8 \ + libtsan2 \ + libubsan + ;; + esac +} + +function postinstall() { + # Don't clear cache in non-CI environments + if [ -z "${CI:-}" ]; then + echo "Not running in CI environment; skipping cache cleanup" + return + fi + + case "${ID}" in + debian | ubuntu) + apt-get clean + rm -rf /var/lib/apt/lists/* + ;; + + rhel | centos | rocky | almalinux) + dnf clean -y all + rm -rf /var/cache/dnf/* + ;; + esac +} + +function verify() { + # Verify that every expected library is now resolvable by the dynamic linker. + missing=0 + for lib in libasan.so.8 libtsan.so.2 libubsan.so.1; do + if ldconfig -p | grep -q "${lib}"; then + echo "OK: ${lib} found" + else + echo "ERROR: ${lib} not found after installation" >&2 + missing=$((missing + 1)) + fi + done + + if [ "${missing}" -ne 0 ]; then + echo "ERROR: ${missing} library/libraries missing" >&2 + exit 1 + fi +} + +preinstall +install +postinstall +verify + +echo "All sanitizer runtime libraries installed successfully." diff --git a/bin/pre-commit/clang_tidy_check.py b/bin/pre-commit/clang_tidy_check.py index 7fb51d1c46..5b5792b405 100755 --- a/bin/pre-commit/clang_tidy_check.py +++ b/bin/pre-commit/clang_tidy_check.py @@ -1,24 +1,27 @@ #!/usr/bin/env python3 -"""Pre-commit hook that runs clang-tidy on changed files using run-clang-tidy.""" +"""Pre-commit hook that runs clang-tidy on changed files using run-clang-tidy. + +The set of files is chosen by pre-commit (see .pre-commit-config.yaml), which +filters to C/C++ sources and excludes `.ipp` fragments. Headers are linted +directly: the `verify_headers` build option (ON by default) compiles every +`.h`/`.hpp` on its own, so each header is the main file of its own +compile_commands.json entry and run-clang-tidy can analyse it just like a +`.cpp`. +""" from __future__ import annotations -import json import os -import re import shutil import subprocess import sys -from collections import defaultdict from pathlib import Path -HEADER_EXTENSIONS = {".h", ".hpp", ".ipp"} -SOURCE_EXTENSIONS = {".cpp"} -INCLUDE_RE = re.compile(r"^\s*#\s*include\s*[<\"]([^>\"]+)[>\"]") +CLANG_TIDY_VERSION = 22 def find_run_clang_tidy() -> str | None: - for candidate in ("run-clang-tidy-21", "run-clang-tidy"): + for candidate in (f"run-clang-tidy-{CLANG_TIDY_VERSION}", "run-clang-tidy"): if path := shutil.which(candidate): return path return None @@ -32,156 +35,30 @@ def find_build_dir(repo_root: Path) -> Path | None: return None -def build_include_graph(build_dir: Path, repo_root: Path) -> tuple[dict, set]: - """ - Scan all files reachable from compile_commands.json and build an inverted include graph. - - Returns: - inverted: header_path -> set of files that include it - source_files: set of all TU paths from compile_commands.json - """ - with open(build_dir / "compile_commands.json") as f: - db = json.load(f) - - source_files = {Path(e["file"]).resolve() for e in db} - include_roots = [repo_root / "include", repo_root / "src"] - inverted: dict[Path, set[Path]] = defaultdict(set) - - to_scan: set[Path] = set(source_files) - scanned: set[Path] = set() - - while to_scan: - file = to_scan.pop() - if file in scanned or not file.exists(): - continue - scanned.add(file) - - content = file.read_text() - - for line in content.splitlines(): - m = INCLUDE_RE.match(line) - if not m: - continue - for root in include_roots: - candidate = (root / m.group(1)).resolve() - if candidate.exists(): - inverted[candidate].add(file) - if candidate not in scanned: - to_scan.add(candidate) - break - - return inverted, source_files - - -def find_tus_for_headers( - headers: list[Path], - inverted: dict[Path, set[Path]], - source_files: set[Path], -) -> set[Path]: - """ - For each header, pick one TU that transitively includes it. - Prefers a TU whose stem matches the header's stem, otherwise picks the first found. - """ - result: set[Path] = set() - - for header in headers: - preferred: Path | None = None - visited: set[Path] = {header} - stack: list[Path] = [header] - - while stack: - h = stack.pop() - for inc in inverted.get(h, ()): - if inc in source_files: - if inc.stem == header.stem: - preferred = inc - break - if preferred is None: - preferred = inc - if inc not in visited: - visited.add(inc) - stack.append(inc) - if preferred is not None and preferred.stem == header.stem: - break - - if preferred is not None: - result.add(preferred) - - return result - - -def resolve_files( - input_files: list[str], build_dir: Path, repo_root: Path -) -> list[str]: - """ - Split input into source files and headers. Source files are passed through; - headers are resolved to the TUs that transitively include them. - """ - sources: list[Path] = [] - headers: list[Path] = [] - - for f in input_files: - p = Path(f).resolve() - if p.suffix in SOURCE_EXTENSIONS: - sources.append(p) - elif p.suffix in HEADER_EXTENSIONS: - headers.append(p) - - if not headers: - return [str(p) for p in sources] - - print( - f"Resolving {len(headers)} header(s) to compilation units...", file=sys.stderr - ) - inverted, source_files = build_include_graph(build_dir, repo_root) - tus = find_tus_for_headers(headers, inverted, source_files) - - if not tus: - print( - "Warning: no compilation units found that include the modified headers; " - "skipping clang-tidy for headers.", - file=sys.stderr, - ) - - return sorted({str(p) for p in (*sources, *tus)}) - - -def staged_files(repo_root: Path) -> list[str]: - result = subprocess.run( - ["git", "diff", "--staged", "--name-only", "--diff-filter=d"], - capture_output=True, - text=True, - cwd=repo_root, - ) - if result.returncode != 0: - print( - "clang-tidy check failed: 'git diff --staged' command failed.", - file=sys.stderr, - ) - if result.stderr: - print(result.stderr, file=sys.stderr) - sys.exit(result.returncode or 1) - return [str(repo_root / p) for p in result.stdout.splitlines() if p] - - def main(): if not os.environ.get("TIDY"): return 0 - repo_root = Path(__file__).parent.parent - files = staged_files(repo_root) + files = sys.argv[1:] if not files: return 0 run_clang_tidy = find_run_clang_tidy() if not run_clang_tidy: print( - "clang-tidy check failed: TIDY is enabled but neither " - "'run-clang-tidy-21' nor 'run-clang-tidy' was found in PATH.", + f"clang-tidy check failed: TIDY is enabled but neither " + f"'run-clang-tidy-{CLANG_TIDY_VERSION}' nor 'run-clang-tidy' was found in PATH.", file=sys.stderr, ) return 1 + repo_root = Path( + subprocess.check_output( + ["git", "rev-parse", "--show-toplevel"], + cwd=Path(__file__).parent, + text=True, + ).strip() + ) build_dir = find_build_dir(repo_root) if not build_dir: print( @@ -191,13 +68,9 @@ def main(): ) return 1 - tidy_files = resolve_files(files, build_dir, repo_root) - if not tidy_files: - return 0 - result = subprocess.run( [run_clang_tidy, "-quiet", "-p", str(build_dir), "-fix", "-allow-no-checks"] - + tidy_files + + files ) return result.returncode diff --git a/bin/pre-commit/fix_pragma_once.py b/bin/pre-commit/fix_pragma_once.py new file mode 100755 index 0000000000..08a505b6d0 --- /dev/null +++ b/bin/pre-commit/fix_pragma_once.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +""" +Adds "#pragma once" to the top of header files that don't already have it. + +Usage: ./bin/pre-commit/fix_pragma_once.py ... +""" + +import sys +from pathlib import Path + +PRAGMA_ONCE = "#pragma once\n\n" + + +def fix_pragma_once(path: Path) -> bool: + original = path.read_text(encoding="utf-8") + if PRAGMA_ONCE not in original: + path.write_text(PRAGMA_ONCE + original, encoding="utf-8") + return False + return True + + +def main() -> int: + files = [Path(f) for f in sys.argv[1:]] + success = True + + for path in files: + success &= fix_pragma_once(path) + + return 0 if success else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/cfg/xrpld-example.cfg b/cfg/xrpld-example.cfg index effc62c274..9e334e6f4f 100644 --- a/cfg/xrpld-example.cfg +++ b/cfg/xrpld-example.cfg @@ -953,6 +953,21 @@ # # Optional keys for NuDB and RocksDB: # +# cache_size Size of cache for database records. Default is 16384. +# Setting this value to 0 will use the default value. +# +# cache_age Length of time in minutes to keep database records +# cached. Default is 5 minutes. Setting this value to +# 0 will use the default value. +# +# Note: if cache_size or cache_age is not specified, +# default values will be used for the unspecified +# parameter. +# +# Note: the cache will not be created if online_delete +# is specified, because the rotating NodeStore does +# not use this cache). +# # fast_load Boolean. If set, load the last persisted ledger # from disk upon process start before syncing to # the network. This is likely to improve performance diff --git a/cmake/CompilationEnv.cmake b/cmake/CompilationEnv.cmake index 0d44f90974..8e69a4dfdd 100644 --- a/cmake/CompilationEnv.cmake +++ b/cmake/CompilationEnv.cmake @@ -56,3 +56,16 @@ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64") else() message(FATAL_ERROR "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}") endif() + +# -------------------------------------------------------------------- +# Sanitizers +# -------------------------------------------------------------------- +# SANITIZERS is injected by the Conan toolchain when a sanitizer build is +# requested (see conan/profiles/sanitizers). The flags are applied to the +# 'common' target in XrplSanitizers; this flag lets other modules know a +# sanitizer build is active without depending on that module. +if(DEFINED SANITIZERS) + set(SANITIZERS_ENABLED TRUE) +else() + set(SANITIZERS_ENABLED FALSE) +endif() diff --git a/cmake/PatchNixBinary.cmake b/cmake/PatchNixBinary.cmake new file mode 100644 index 0000000000..79ca0b150c --- /dev/null +++ b/cmake/PatchNixBinary.cmake @@ -0,0 +1,53 @@ +#[===================================================================[ + Patch executables to run in non-Nix environments. + + The Nix-based CI image links binaries against an ELF interpreter (loader) + that lives in the Nix store, so the resulting binaries don't run elsewhere + (including once installed from the .deb package). `patch_nix_binary` adds a + POST_BUILD step that resets the interpreter to the system default loader and + drops the rpath. + + This is only active inside the Nix-based image, detected by the presence of + /tmp/loader-path.sh (shipped by that image, resolves the default loader). It + is skipped for sanitizer builds, whose runtime libraries are resolved through + the rpath. Everywhere else `patch_nix_binary` is a no-op. +#]===================================================================] + +include_guard(GLOBAL) + +include(CompilationEnv) + +# Provided by the Nix-based CI image; prints the system default ELF loader path. +set(_loader_path_script "/tmp/loader-path.sh") + +if(is_linux AND NOT SANITIZERS_ENABLED AND EXISTS "${_loader_path_script}") + execute_process( + COMMAND "${_loader_path_script}" + OUTPUT_VARIABLE DEFAULT_LOADER_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY + ) + find_program(PATCHELF_COMMAND patchelf REQUIRED) + set(PATCH_NIX_BINARIES TRUE) + message( + STATUS + "Binaries will be patched to use loader '${DEFAULT_LOADER_PATH}'" + ) +else() + set(PATCH_NIX_BINARIES FALSE) +endif() + +function(patch_nix_binary target) + if(NOT PATCH_NIX_BINARIES) + return() + endif() + add_custom_command( + TARGET ${target} + POST_BUILD + COMMAND + "${PATCHELF_COMMAND}" --set-interpreter "${DEFAULT_LOADER_PATH}" + --remove-rpath "$" + COMMENT "Patching ${target}: set default loader, remove rpath" + VERBATIM + ) +endfunction() diff --git a/cmake/XrplAddTest.cmake b/cmake/XrplAddTest.cmake deleted file mode 100644 index 2f1209e03c..0000000000 --- a/cmake/XrplAddTest.cmake +++ /dev/null @@ -1,22 +0,0 @@ -include(isolate_headers) - -function(xrpl_add_test name) - set(target ${PROJECT_NAME}.test.${name}) - - file( - GLOB_RECURSE sources - CONFIGURE_DEPENDS - "${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/${name}.cpp" - ) - add_executable(${target} ${ARGN} ${sources}) - - isolate_headers( - ${target} - "${CMAKE_SOURCE_DIR}" - "${CMAKE_SOURCE_DIR}/tests/${name}" - PRIVATE - ) - - add_test(NAME ${target} COMMAND ${target}) -endfunction() diff --git a/cmake/XrplCompiler.cmake b/cmake/XrplCompiler.cmake index 0b77ff3525..cb4e797137 100644 --- a/cmake/XrplCompiler.cmake +++ b/cmake/XrplCompiler.cmake @@ -145,13 +145,48 @@ else() INTERFACE -rdynamic $<$:-Wl,-z,relro,-z,now,--build-id> - # link to static libc/c++ iff: * static option set and * NOT APPLE (AppleClang does not support static - # libc/c++) and * NOT SANITIZERS (sanitizers typically don't work with static libc/c++) - $<$,$>,$>>: + # link to static libc/c++ if: + # * static option set and + # * NOT APPLE (AppleClang does not support static libc/c++) + $<$,$>>: -static-libstdc++ -static-libgcc > ) + + # On aarch64, libatomic is required for atomic operations. It is not needed on x86_64. + # Linking it statically on Linux + if(is_arm64 AND is_linux) + target_link_options( + common + INTERFACE -Wl,--push-state -Wl,-Bstatic -latomic -Wl,--pop-state + ) + endif() + + # Keep -stdlib=libstdc++ off the compile commands, but preserve it for linking. + # + # Conan turns `compiler.libcxx=libstdc++` into `-stdlib=libstdc++` and puts it in + # CMAKE_CXX_FLAGS, which CMake passes to BOTH compile and link steps. On a normal Clang + # the compile step consumes it while choosing the C++ stdlib include paths. The Nixpkgs + # Clang wrapper supplies those paths itself (via -nostdinc++), so at compile time the + # flag is unused -> Clang errors under our -Werror. At link time the flag IS consumed + # (it selects the C++ runtime), so we move it there instead of dropping it entirely. + get_filename_component(_cxx_real "${CMAKE_CXX_COMPILER}" REALPATH) + if( + _cxx_real MATCHES "^/nix/store/" + AND is_linux + AND is_clang + AND CMAKE_CXX_FLAGS MATCHES "stdlib=libstdc" + ) + string( + REPLACE "-stdlib=libstdc++" + "" + CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS}" + ) + string(STRIP "${CMAKE_CXX_FLAGS}" CMAKE_CXX_FLAGS) + add_link_options($<$:-stdlib=libstdc++>) + endif() endif() # Antithesis instrumentation will only be built and deployed using machines running Linux. diff --git a/cmake/XrplCore.cmake b/cmake/XrplCore.cmake index 9b1dc74049..3e49267715 100644 --- a/cmake/XrplCore.cmake +++ b/cmake/XrplCore.cmake @@ -94,6 +94,9 @@ add_module(xrpl basics) target_link_libraries(xrpl.libxrpl.basics PUBLIC xrpl.libxrpl.beast) # Level 03 +add_module(xrpl config) +target_link_libraries(xrpl.libxrpl.config PUBLIC xrpl.libxrpl.basics) + add_module(xrpl json) target_link_libraries(xrpl.libxrpl.json PUBLIC xrpl.libxrpl.basics) @@ -120,6 +123,7 @@ target_link_libraries( xrpl.libxrpl.core PUBLIC xrpl.libxrpl.basics + xrpl.libxrpl.config xrpl.libxrpl.json xrpl.libxrpl.protocol xrpl.libxrpl.protocol_autogen @@ -143,7 +147,11 @@ target_link_libraries( add_module(xrpl nodestore) target_link_libraries( xrpl.libxrpl.nodestore - PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json xrpl.libxrpl.protocol + PUBLIC + xrpl.libxrpl.basics + xrpl.libxrpl.config + xrpl.libxrpl.json + xrpl.libxrpl.protocol ) add_module(xrpl shamap) @@ -159,13 +167,14 @@ target_link_libraries( add_module(xrpl rdb) target_link_libraries( xrpl.libxrpl.rdb - PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.core + PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.config xrpl.libxrpl.core ) add_module(xrpl server) target_link_libraries( xrpl.libxrpl.server PUBLIC + xrpl.libxrpl.config xrpl.libxrpl.protocol xrpl.libxrpl.core xrpl.libxrpl.rdb @@ -210,6 +219,7 @@ target_link_modules( basics beast conditions + config core crypto git @@ -237,6 +247,7 @@ target_link_modules( if(xrpld) add_executable(xrpld) + patch_nix_binary(xrpld) if(tests) target_compile_definitions(xrpld PUBLIC ENABLE_TESTS) target_compile_definitions( @@ -282,4 +293,13 @@ if(xrpld) PRIVATE ${CMAKE_SOURCE_DIR}/external/antithesis-sdk ) endif() + + # The xrpld headers are not built with add_module, so verify them against + # the executable's own compile environment. + if(verify_headers) + verify_target_headers(xrpld "${CMAKE_CURRENT_SOURCE_DIR}/src/xrpld") + if(tests) + verify_target_headers(xrpld "${CMAKE_CURRENT_SOURCE_DIR}/src/test") + endif() + endif() endif() diff --git a/cmake/XrplCov.cmake b/cmake/XrplCov.cmake index d81d7e689f..86ba534a88 100644 --- a/cmake/XrplCov.cmake +++ b/cmake/XrplCov.cmake @@ -47,7 +47,7 @@ setup_target_for_coverage_gcovr( "include/xrpl/beast/test" "include/xrpl/beast/unit_test" "${CMAKE_BINARY_DIR}/pb-xrpl.libpb" - DEPENDENCIES xrpld xrpl.tests + DEPENDENCIES xrpld xrpl_tests ) add_code_coverage_to_target(opts INTERFACE) diff --git a/cmake/XrplPackaging.cmake b/cmake/XrplPackaging.cmake index fe885c200c..8e3861925d 100644 --- a/cmake/XrplPackaging.cmake +++ b/cmake/XrplPackaging.cmake @@ -28,7 +28,6 @@ endif() set(package_env SRC_DIR=${CMAKE_SOURCE_DIR} BUILD_DIR=${CMAKE_BINARY_DIR} - PKG_VERSION=${xrpld_version} PKG_RELEASE=${pkg_release} ) diff --git a/cmake/XrplSanitizers.cmake b/cmake/XrplSanitizers.cmake index 64f1841bfb..893c880374 100644 --- a/cmake/XrplSanitizers.cmake +++ b/cmake/XrplSanitizers.cmake @@ -14,11 +14,9 @@ include_guard(GLOBAL) include(CompilationEnv) -if(NOT DEFINED SANITIZERS) - set(SANITIZERS_ENABLED FALSE) +if(NOT SANITIZERS_ENABLED) return() endif() -set(SANITIZERS_ENABLED TRUE) message(STATUS "=== Configuring Sanitizers ===") message(STATUS " SANITIZERS: ${SANITIZERS}") diff --git a/cmake/XrplSettings.cmake b/cmake/XrplSettings.cmake index 44a727a994..757a596096 100644 --- a/cmake/XrplSettings.cmake +++ b/cmake/XrplSettings.cmake @@ -30,6 +30,23 @@ if(tests) endif() endif() +# Enabled by default so every header is compiled on its own as the main file of +# its own compile_commands.json entry - this is what lets clang-tidy (and clangd +# and IDEs) analyse a header's own includes directly. The per-header objects are +# EXCLUDE_FROM_ALL (see cmake/verify_headers.cmake) and the aggregate target +# below is not part of `all`, so a normal `cmake --build` never compiles them. +option( + verify_headers + "Compile every header on its own to verify it is self-contained." + ON +) +if(verify_headers) + # Aggregate target that builds every per-module header-verification library + # created by add_module (see cmake/verify_headers.cmake). Build it with: + # cmake --build . --target verify-headers + add_custom_target(verify-headers) +endif() + option(unity "Creates a build using UNITY support in cmake." OFF) if(unity) if(NOT is_ci) diff --git a/cmake/add_module.cmake b/cmake/add_module.cmake index 316d6c627b..b72d1077bb 100644 --- a/cmake/add_module.cmake +++ b/cmake/add_module.cmake @@ -1,4 +1,5 @@ include(isolate_headers) +include(verify_headers) # Create an OBJECT library target named # @@ -37,4 +38,20 @@ function(add_module parent name) "${CMAKE_CURRENT_SOURCE_DIR}/src/lib${parent}/${name}" PRIVATE ) + # protocol_autogen contains generated headers that are deliberately exempt + # from clang-tidy (see ExcludeHeaderFilterRegex in .clang-tidy), so we do not + # verify them either. + if( + verify_headers + AND NOT "${parent}/${name}" STREQUAL "xrpl/protocol_autogen" + ) + verify_target_headers( + ${target} + "${CMAKE_CURRENT_SOURCE_DIR}/include/${parent}/${name}" + ) + verify_target_headers( + ${target} + "${CMAKE_CURRENT_SOURCE_DIR}/src/lib${parent}/${name}" + ) + endif() endfunction() diff --git a/cmake/scripts/codegen/requirements.in b/cmake/scripts/codegen/requirements.in new file mode 100644 index 0000000000..d799fd60fd --- /dev/null +++ b/cmake/scripts/codegen/requirements.in @@ -0,0 +1,13 @@ +# Python dependencies for XRP Ledger code generation scripts +# +# These packages are required to run the code generation scripts that +# parse macro files and generate C++ wrapper classes. + +# C preprocessor for Python - used to preprocess macro files +pcpp>=1.30 + +# Parser combinator library - used to parse the macro DSL +pyparsing>=3.0.0 + +# Template engine - used to generate C++ code from templates +Mako>=1.2.2 diff --git a/cmake/scripts/codegen/requirements.txt b/cmake/scripts/codegen/requirements.txt index d799fd60fd..ff37548c7b 100644 --- a/cmake/scripts/codegen/requirements.txt +++ b/cmake/scripts/codegen/requirements.txt @@ -1,13 +1,105 @@ -# Python dependencies for XRP Ledger code generation scripts -# -# These packages are required to run the code generation scripts that -# parse macro files and generate C++ wrapper classes. - -# C preprocessor for Python - used to preprocess macro files -pcpp>=1.30 - -# Parser combinator library - used to parse the macro DSL -pyparsing>=3.0.0 - -# Template engine - used to generate C++ code from templates -Mako>=1.2.2 +# 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 + # via -r requirements.in +markupsafe==3.0.3 \ + --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ + --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ + --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ + --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ + --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ + --hash=sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c \ + --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ + --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \ + --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \ + --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \ + --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ + --hash=sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26 \ + --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \ + --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \ + --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \ + --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ + --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \ + --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ + --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \ + --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \ + --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ + --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \ + --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \ + --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ + --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \ + --hash=sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758 \ + --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \ + --hash=sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8 \ + --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \ + --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ + --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ + --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ + --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \ + --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ + --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \ + --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ + --hash=sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2 \ + --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ + --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \ + --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ + --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ + --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ + --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \ + --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \ + --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ + --hash=sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e \ + --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \ + --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \ + --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \ + --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \ + --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \ + --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ + --hash=sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b \ + --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \ + --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \ + --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \ + --hash=sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d \ + --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \ + --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \ + --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \ + --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ + --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ + --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \ + --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \ + --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \ + --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ + --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \ + --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \ + --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \ + --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \ + --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \ + --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \ + --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ + --hash=sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7 \ + --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \ + --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \ + --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \ + --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ + --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ + --hash=sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42 \ + --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ + --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ + --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ + --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ + --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ + --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \ + --hash=sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc \ + --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ + --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 + # via mako +pcpp==1.30 \ + --hash=sha256:05fe08292b6da57f385001c891a87f40d6aa7f46787b03e8ba326d20a3297c6e \ + --hash=sha256:5af9fbce55f136d7931ae915fae03c34030a3b36c496e72d9636cedc8e2543a1 + # via -r requirements.in +pyparsing==3.3.2 \ + --hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \ + --hash=sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc + # via -r requirements.in diff --git a/cmake/scripts/codegen/templates/LedgerEntry.h.mako b/cmake/scripts/codegen/templates/LedgerEntry.h.mako index 31029cd311..63f5f39ef9 100644 --- a/cmake/scripts/codegen/templates/LedgerEntry.h.mako +++ b/cmake/scripts/codegen/templates/LedgerEntry.h.mako @@ -33,7 +33,7 @@ public: * @brief Construct a ${name} ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit ${name}(std::shared_ptr sle) + explicit ${name}(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -168,7 +168,7 @@ ${field['typeData']['setter_type']} ${field['paramName']}${',' if i < len(requir * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - ${name}Builder(std::shared_ptr sle) + ${name}Builder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ${tag}) { diff --git a/cmake/verify_headers.cmake b/cmake/verify_headers.cmake new file mode 100644 index 0000000000..2c36869441 --- /dev/null +++ b/cmake/verify_headers.cmake @@ -0,0 +1,84 @@ +# Our normal build only ever compiles `.cpp` files, so a header is only ever +# checked through whatever translation unit happens to include it. A header that +# is missing an `#include` is never caught as long as every `.cpp` that uses it +# includes its missing dependency first. To check a header on its own we compile +# it directly as a translation unit. +# +# Compiling the header itself - rather than a `.cpp` wrapper that includes it - +# gives two checks at once: +# * the compiler fails if the header is not self-contained, i.e. it uses a +# declaration that is not available (directly or transitively); and +# * the header is the *main file* of its `compile_commands.json` entry, so +# clang-tidy's misc-include-cleaner analyses (and can --fix) the header's own +# includes - flagging a dependency that is only available transitively, which +# a plain compile cannot catch. A wrapper would be the main file instead, and +# include-cleaner never looks inside the headers a main file includes. +# +# The objects are never linked anywhere; we build them only for these checks. + +# Verify that the headers under headers_dir compile on their own, using the +# compile environment of an existing target so each header is compiled exactly as +# that target compiles it. This works for both add_module libraries and the xrpld +# and test binaries: a library's isolated public and private include directories +# and a binary's `-I src` both live in its INCLUDE_DIRECTORIES, and the modules or +# libraries it links live in its LINK_LIBRARIES. We copy those usage requirements +# through generator expressions (rather than linking ${target}, which is +# impossible for an executable), evaluated at generation time so they capture +# requirements the caller adds after this runs. The verify library is created +# once; call this repeatedly to add more header directories. +# +# verify_target_headers(target headers_dir) +function(verify_target_headers target headers_dir) + set(verify ${target}.verify) + if(NOT TARGET ${verify}) + add_library(${verify} OBJECT EXCLUDE_FROM_ALL) + # A unity build would concatenate the headers into a single translation + # unit, where a header missing an include could be satisfied by one that + # precedes it in the blob - exactly the bug we want to catch. + set_target_properties(${verify} PROPERTIES UNITY_BUILD OFF) + target_include_directories( + ${verify} + PRIVATE $ + ) + target_compile_definitions( + ${verify} + PRIVATE $ + ) + target_compile_options( + ${verify} + PRIVATE $ + ) + target_link_libraries( + ${verify} + PRIVATE $ + ) + add_dependencies(verify-headers ${verify}) + endif() + _verify_add_headers(${verify} "${headers_dir}") +endfunction() + +# Add every .h/.hpp under dir to target as a directly-compiled C++ translation +# unit. .ipp files are inline-implementation fragments included by their owning +# header (often after a class declaration), so they are not self-contained on +# their own and are verified transitively when that header is verified. +function(_verify_add_headers target dir) + file(GLOB_RECURSE headers CONFIGURE_DEPENDS "${dir}/*.h" "${dir}/*.hpp") + if(NOT headers) + return() + endif() + # `-xc++` forces the header to be compiled as a C++ translation unit; a lone + # `.h` is otherwise treated as a header to precompile. `#pragma once` is + # harmless (and warns) when the header is the main file, so silence it. + # Compiled on its own, a header legitimately defines constants and static or + # template functions that nothing in this single translation unit uses (they + # exist for the files that include it), so the resulting unused-entity + # warnings are expected and must not fail the build under -Werror. + set_source_files_properties( + ${headers} + PROPERTIES + LANGUAGE CXX + COMPILE_OPTIONS + "-xc++;-Wno-pragma-once-outside-header;-Wno-unused-const-variable;-Wno-unused-function" + ) + target_sources(${target} PRIVATE ${headers}) +endfunction() diff --git a/conan.lock b/conan.lock index e2eb8d871a..b6ddfa4e58 100644 --- a/conan.lock +++ b/conan.lock @@ -1,43 +1,44 @@ { "version": "0.5", "requires": [ - "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503", - "xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987", - "sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1776096494.149", - "soci/4.0.3#fe32b9ad5eb47e79ab9e45a68f363945%1774450067.231", - "snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878", - "secp256k1/0.7.1#481881709eb0bdd0185a12b912bbe8ad%1770910500.329", - "rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1765850186.86", - "re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1774398111.888", - "protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12", - "openssl/3.6.2#4789bbf131b77d0515d15e094c8f697f%1778071755.506", - "nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1775040983.408", - "lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914", - "libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492", - "libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03", - "libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1776147552.838", + "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708", + "xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1782392402.420688", + "sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1782392403.185447", + "soci/4.0.3#e726491a03468795453f7c83fc924a96%1782392402.679521", + "snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1782307151.633168", + "secp256k1/0.7.1#b1f450b7f78a36fff75bb6934a356f3a%1782338841.3729", + "rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1782392413.075713", + "re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1782392402.431897", + "protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1782392410.233933", + "openssl/3.6.3#1163d4ddc603907084d08a6a0c6e580f%1782307150.583886", + "nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1782392402.297166", + "mpt-crypto/0.4.0-rc2#a580f2f9ad0e795de696aa62d54fb9af%1782425834.488828", + "lz4/1.10.0#982d9b673900f665a1da109e09c17cab%1782392402.164188", + "libiconv/1.17#9923bc6dc6f106646d6967e0039a5ada%1782392792.775744", + "libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1782392402.420732", + "libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1782392403.066892", "jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228", - "gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152", - "grpc/1.78.1#b1a9e74b145cc471bed4dc64dc6eb2c1%1774467387.342", - "ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772", - "date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772", - "c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1774439234.681", - "bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837", - "boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778050991.9", - "abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196" + "gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1782392402.791979", + "grpc/1.81.1#5217e6ef0544c42b46f4af35d5e7f649%1782307148.845616", + "ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1782307148.15562", + "date/3.0.4#862e11e80030356b53c2c38599ceb32b%1782392402.538492", + "c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1782392402.681654", + "bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1782392402.296732", + "boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1782392419.475605", + "abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1782307147.395833" ], "build_requires": [ - "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503", - "strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1774447376.964", - "protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12", - "nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1765850144.707", + "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708", + "strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1782395692.540639", + "protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1782392410.233933", + "nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1782395690.33162", "msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649", - "m4/1.4.19#4523e4347b55cd26ae918bd5770cab9a%1778062762.471", - "cmake/4.3.0#b939a42e98f593fb34d3a8c5cc860359%1774439249.183", - "b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1774439233.447", + "m4/1.4.19#34c4bbc3eeebe98ca6edf2f52d602e7d%1777282960.259", + "cmake/4.3.3#840cf00ea09777e05c2050a50a82c722%1782392418.696091", + "b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1782392402.624226", "automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56", "autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86", - "abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196" + "abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1782307147.395833" ], "python_requires": [], "overrides": { @@ -57,7 +58,7 @@ "boost/1.91.0" ], "lz4/[>=1.9.4 <2]": [ - "lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504" + "lz4/1.10.0#982d9b673900f665a1da109e09c17cab" ] }, "config_requires": [] diff --git a/conan/lockfile/linux.profile b/conan/lockfile/linux.profile index 25ad5988c5..ea9f66de69 100644 --- a/conan/lockfile/linux.profile +++ b/conan/lockfile/linux.profile @@ -2,7 +2,7 @@ arch=x86_64 build_type=Release compiler=gcc -compiler.cppstd=20 +compiler.cppstd=23 compiler.libcxx=libstdc++11 compiler.version=13 os=Linux diff --git a/conan/lockfile/macos.profile b/conan/lockfile/macos.profile index 332a0c143d..f223627c26 100644 --- a/conan/lockfile/macos.profile +++ b/conan/lockfile/macos.profile @@ -2,7 +2,7 @@ arch=armv8 build_type=Release compiler=apple-clang -compiler.cppstd=20 +compiler.cppstd=23 compiler.libcxx=libc++ compiler.version=17.0 os=Macos diff --git a/conan/lockfile/regenerate.sh b/conan/lockfile/regenerate.sh index 1aa47628f0..98ee6f7c99 100755 --- a/conan/lockfile/regenerate.sh +++ b/conan/lockfile/regenerate.sh @@ -14,7 +14,7 @@ export CONAN_HOME="$TEMP_DIR" # Ensure that the xrplf remote is the first to be consulted, so any recipes we # patched are used. We also add it there to not created huge diff when the # official Conan Center Index is updated. -conan remote add --force --index 0 xrplf https://conan.ripplex.io +conan remote add --force --index 0 xrplf https://conan.xrplf.org/repository/conan/ # Delete any existing lockfile. rm -f conan.lock diff --git a/conan/lockfile/windows.profile b/conan/lockfile/windows.profile index 4bb266a62e..b3a8fed4f3 100644 --- a/conan/lockfile/windows.profile +++ b/conan/lockfile/windows.profile @@ -2,7 +2,7 @@ arch=x86_64 build_type=Release compiler=msvc -compiler.cppstd=20 +compiler.cppstd=23 compiler.runtime=dynamic compiler.runtime_type=Release compiler.version=194 diff --git a/conan/profiles/ci b/conan/profiles/ci index ae93187026..9422addfe3 100644 --- a/conan/profiles/ci +++ b/conan/profiles/ci @@ -1 +1,8 @@ +{% set os = detect_api.detect_os() %} include(sanitizers) + +[conf] +{% if os == "Linux" %} +user.package:libc_version=2.31 +tools.info.package_id:confs+=["user.package:libc_version"] +{% endif %} diff --git a/conan/profiles/default b/conan/profiles/default index cde59f7f3b..ae6e23c3c3 100644 --- a/conan/profiles/default +++ b/conan/profiles/default @@ -10,16 +10,18 @@ os={{ os }} arch={{ arch }} build_type=Debug -compiler={{compiler}} +compiler={{ compiler }} compiler.version={{ compiler_version }} -compiler.cppstd=20 +compiler.cppstd=23 {% if os == "Windows" %} compiler.runtime=static {% else %} -compiler.libcxx={{detect_api.detect_libcxx(compiler, version, compiler_exe)}} +compiler.libcxx={{ detect_api.detect_libcxx(compiler, version, compiler_exe) }} {% endif %} [conf] -{% if compiler == "gcc" and compiler_version < 13 %} -tools.build:cxxflags+=['-Wno-restrict'] -{% endif %} +{# By default, Conan tries to reuse binaries built with different cppstd versions. #} +{# We want to avoid that to improve reproduceability, so we add the cppstd version to the package ID. #} +{# More info: https://docs.conan.io/2/reference/extensions/binary_compatibility.html #} +user.package:cppstd_version=23 +tools.info.package_id:confs+=["user.package:cppstd_version"] diff --git a/conan/profiles/sanitizers b/conan/profiles/sanitizers index 4a05fda734..09e6aef02b 100644 --- a/conan/profiles/sanitizers +++ b/conan/profiles/sanitizers @@ -52,52 +52,50 @@ include(default) {% endif %} {# Frame pointer required for meaningful stack traces; -O1 for reasonable performance #} -{% set compile_flags = ["-fno-omit-frame-pointer", "-O1"] %} +{% set sanitizer_compiler_flags = ["-fno-omit-frame-pointer", "-O1"] %} {% if compiler == "gcc" %} {# Suppress false positive warnings with GCC #} - {% set _ = compile_flags.append("-Wno-stringop-overflow") %} + {% set _ = sanitizer_compiler_flags.append("-Wno-stringop-overflow") %} {% set relocation_flags = [] %} {% if arch == "x86_64" and enable_asan %} {# Large code model prevents relocation errors in instrumented ASAN binaries #} - {% set _ = compile_flags.append("-mcmodel=large") %} + {% set _ = sanitizer_compiler_flags.append("-mcmodel=large") %} {% set _ = relocation_flags.append("-mcmodel=large") %} {% elif enable_tsan %} {# GCC doesn't support atomic_thread_fence with TSAN; suppress warnings #} - {% set _ = compile_flags.append("-Wno-tsan") %} + {% set _ = sanitizer_compiler_flags.append("-Wno-tsan") %} {% if arch == "x86_64" %} {# Medium code model for TSAN; large is incompatible #} - {% set _ = compile_flags.append("-mcmodel=medium") %} + {% set _ = sanitizer_compiler_flags.append("-mcmodel=medium") %} {% set _ = relocation_flags.append("-mcmodel=medium") %} {% endif %} {% endif %} {% set fsanitize = "-fsanitize=" ~ ",".join(sanitizer_types) %} - {% set _ = compile_flags.append(fsanitize) %} + {% set _ = sanitizer_compiler_flags.append(fsanitize) %} {% set _ = relocation_flags.append(fsanitize) %} - {% set sanitizer_compiler_flags = " ".join(compile_flags) %} - {% set sanitizer_linker_flags = " ".join(relocation_flags) %} + {% set sanitizer_linker_flags = relocation_flags %} {% elif compiler == "clang" or compiler == "apple-clang" %} {% set fsanitize = "-fsanitize=" ~ ",".join(sanitizer_types) %} - {% set _ = compile_flags.append(fsanitize) %} + {% set _ = sanitizer_compiler_flags.append(fsanitize) %} - {% set sanitizer_compiler_flags = " ".join(compile_flags) %} - {% set sanitizer_linker_flags = fsanitize %} + {% set sanitizer_linker_flags = [fsanitize] %} {% endif %} [conf] -tools.build:defines+={{defines}} -tools.build:cxxflags+=['{{sanitizer_compiler_flags}}'] -tools.build:sharedlinkflags+=['{{sanitizer_linker_flags}}'] -tools.build:exelinkflags+=['{{sanitizer_linker_flags}}'] +tools.build:defines+={{ defines }} +tools.build:cxxflags+={{ sanitizer_compiler_flags }} +tools.build:sharedlinkflags+={{ sanitizer_linker_flags }} +tools.build:exelinkflags+={{ sanitizer_linker_flags }} tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags", "tools.build:defines"] # &: means "apply only to the consumer/root package" -&:tools.cmake.cmaketoolchain:extra_variables={"SANITIZERS": "{{sanitizers}}", "SANITIZERS_COMPILER_FLAGS": "{{sanitizer_compiler_flags}}", "SANITIZERS_LINKER_FLAGS": "{{sanitizer_linker_flags}}"} +&:tools.cmake.cmaketoolchain:extra_variables={"SANITIZERS": "{{ sanitizers }}", "SANITIZERS_COMPILER_FLAGS": "{{ sanitizer_compiler_flags | join(' ') }}", "SANITIZERS_LINKER_FLAGS": "{{ sanitizer_linker_flags | join(' ') }}"} [options] {% if enable_asan %} diff --git a/conanfile.py b/conanfile.py index 2cf5aefbc2..db12dcb585 100644 --- a/conanfile.py +++ b/conanfile.py @@ -28,11 +28,10 @@ class Xrpl(ConanFile): requires = [ "ed25519/2015.03", - "grpc/1.78.1", + "grpc/1.81.1", "libarchive/3.8.7", "nudb/2.0.9", - "openssl/3.6.2", - "secp256k1/0.7.1", + "openssl/3.6.3", "soci/4.0.3", "zlib/1.3.2", ] @@ -132,13 +131,15 @@ class Xrpl(ConanFile): def requirements(self): self.requires("boost/1.91.0", force=True, transitive_headers=True) self.requires("date/3.0.4", transitive_headers=True) - self.requires("lz4/1.10.0", force=True) - self.requires("protobuf/6.33.5", force=True) - self.requires("sqlite3/3.53.0", force=True) if self.options.jemalloc: self.requires("jemalloc/5.3.1") + self.requires("lz4/1.10.0", force=True) + self.requires("mpt-crypto/0.4.0-rc2", transitive_headers=True) + self.requires("protobuf/6.33.5", force=True) if self.options.rocksdb: self.requires("rocksdb/10.5.1") + self.requires("secp256k1/0.7.1", transitive_headers=True) + self.requires("sqlite3/3.53.0", force=True) self.requires("xxhash/0.8.3", transitive_headers=True) exports_sources = ( @@ -208,6 +209,7 @@ class Xrpl(ConanFile): "grpc::grpc++", "libarchive::libarchive", "lz4::lz4", + "mpt-crypto::mpt-crypto", "nudb::nudb", "openssl::crypto", "protobuf::libprotobuf", diff --git a/docker/nix.Dockerfile b/docker/nix.Dockerfile deleted file mode 100644 index 52faa8b8dc..0000000000 --- a/docker/nix.Dockerfile +++ /dev/null @@ -1,66 +0,0 @@ -ARG BASE_IMAGE=nixos/nix:latest - -# Nix builder -FROM nixos/nix:latest AS builder-source - -RUN mkdir -p ~/.config/nix && \ - echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf - -# Copy our source and setup our working dir. -COPY nix/ci-env.nix /tmp/build/nix/ci-env.nix -COPY nix/packages.nix /tmp/build/nix/packages.nix -COPY nix/utils.nix /tmp/build/nix/utils.nix -COPY flake.nix /tmp/build/ -COPY flake.lock /tmp/build/ -WORKDIR /tmp/build - -FROM builder-source AS builder - -# Build our Nix CI environment (all build tools in a single store path) -RUN nix \ - --option filter-syscalls false \ - build - -# Copy the Nix store closure into a directory. The Nix store closure is the -# entire set of Nix store values that we need for our build. -RUN mkdir /tmp/nix-store-closure && \ - cp -R $(nix-store -qR result/) /tmp/nix-store-closure - -# Final image -FROM ${BASE_IMAGE} - -# bash is not located at /bin/bash in nixos/nix, so we need to create a symlink to it. -RUN if [ -d /nix ]; then \ - ln -s /root/.nix-profile/bin/bash /bin/bash; \ - fi - -# Use Bash as the default shell for RUN commands, using the options -# `set -o errexit -o pipefail`, and as the entrypoint. -SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"] -ENTRYPOINT ["/bin/bash"] - -# Copy /nix/store and the env symlink tree -COPY --from=builder /tmp/nix-store-closure /nix/store -COPY --from=builder /tmp/build/result /nix/ci-env - -ENV PATH="/nix/ci-env/bin:$PATH" - -RUN < [!NOTE] +> You might need to add `--lockfile=""` to your `conan install` command +> to avoid automatic use of the existing `conan.lock` file when you run +> `conan export` manually on your machine +> +> This is not recommended though, as you might end up using different revisions of recipes. + +## Conan profile tweaks + +### Missing compiler version + +If you see an error similar to the following after running `conan profile show`: + +```text +ERROR: Invalid setting '17' is not a valid 'settings.compiler.version' value. +Possible values are ['5.0', '5.1', '6.0', '6.1', '7.0', '7.3', '8.0', '8.1', +'9.0', '9.1', '10.0', '11.0', '12.0', '13', '13.0', '13.1', '14', '14.0', '15', +'15.0', '16', '16.0'] +Read "http://docs.conan.io/2/knowledge/faq.html#error-invalid-setting" +``` + +you need to create `$(conan config home)/settings_user.yml` file if it doesn't exist and add the required version number(s) +to the `version` array specific for your compiler. For example: + +```yaml +compiler: + apple-clang: + version: ["17.0"] +``` + +### Multiple compilers + +If you have multiple compilers installed, make sure to select the one to use in +your default Conan configuration **before** running `conan profile detect`, by +setting the `CC` and `CXX` environment variables. + +For example, if you are running MacOS and have [homebrew +LLVM@18](https://formulae.brew.sh/formula/llvm@18), and want to use it as a +compiler in the new Conan profile: + +```bash +export CC=$(brew --prefix llvm@18)/bin/clang +export CXX=$(brew --prefix llvm@18)/bin/clang++ +conan profile detect +``` + +You should also explicitly set the path to the compiler in the profile file, +which helps to avoid errors when `CC` and/or `CXX` are set and disagree with the +selected Conan profile. For example: + +```text +[conf] +tools.build:compiler_executables={'c':'/usr/bin/gcc','cpp':'/usr/bin/g++'} +``` + +### Multiple profiles + +You can manage multiple Conan profiles in the directory +`$(conan config home)/profiles`, for example renaming `default` to a different +name and then creating a new `default` profile for a different compiler. + +### Select language + +The default profile created by Conan will typically select different C++ dialect +than C++23 used by this project. You should set `23` in the profile line +starting with `compiler.cppstd=`. For example: + +```bash +sed -i.bak -e 's|^compiler\.cppstd=.*$|compiler.cppstd=23|' $(conan config home)/profiles/default +``` + +### Select standard library in Linux + +**Linux** developers will commonly have a default Conan [profile][] that +compiles with GCC and links with libstdc++. If you are linking with libstdc++ +(see profile setting `compiler.libcxx`), then you will need to choose the +`libstdc++11` ABI: + +```bash +sed -i.bak -e 's|^compiler\.libcxx=.*$|compiler.libcxx=libstdc++11|' $(conan config home)/profiles/default +``` + +### Select architecture and runtime in Windows + +**Windows** developers may need to use the x64 native build tools. An easy way +to do that is to run the shortcut "x64 Native Tools Command Prompt" for the +version of Visual Studio that you have installed. + +Windows developers must also build `xrpld` and its dependencies for the x64 +architecture: + +```bash +sed -i.bak -e 's|^arch=.*$|arch=x86_64|' $(conan config home)/profiles/default +``` + +**Windows** developers also must select static runtime: + +```bash +sed -i.bak -e 's|^compiler\.runtime=.*$|compiler.runtime=static|' $(conan config home)/profiles/default +``` + +## Add a Dependency + +If you want to experiment with a new package, follow these steps: + +1. Search for the package on [Conan Center](https://conan.io/center/). +2. Modify [`conanfile.py`](../../conanfile.py): + - Add a version of the package to the `requires` property. + - Change any default options for the package by adding them to the + `default_options` property (with syntax `'$package:$option': $value`). +3. Regenerate the [Conan lockfile](../../conan/lockfile/README.md) so the new + dependency is captured: + + ```bash + ./conan/lockfile/regenerate.sh + ``` + +4. Modify [`CMakeLists.txt`](../../CMakeLists.txt): + - Add a call to `find_package($package REQUIRED)`. + - Link a library from the package to the target `xrpl_libs` + (search for the existing call to `target_link_libraries(xrpl_libs INTERFACE ...)`). +5. Start coding! Don't forget to include whatever headers you need from the package. + +[profile]: https://docs.conan.io/2/reference/config_files/profiles.html diff --git a/docs/build/conan.md b/docs/build/conan.md index 9dcd2c8f1c..22c25a0bf9 100644 --- a/docs/build/conan.md +++ b/docs/build/conan.md @@ -115,7 +115,7 @@ By default, Conan will use the profile named "default". [find_package]: https://cmake.org/cmake/help/latest/command/find_package.html [pcf]: https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#package-configuration-file [prefix_path]: https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html -[profile]: https://docs.conan.io/en/latest/reference/profiles.html +[profile]: https://docs.conan.io/2/reference/config_files/profiles.html [pvf]: https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#package-version-file [runtime]: https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html [search]: https://cmake.org/cmake/help/latest/command/find_package.html#search-procedure diff --git a/docs/build/environment.md b/docs/build/environment.md index fb1ebde8bc..e639ed2d5f 100644 --- a/docs/build/environment.md +++ b/docs/build/environment.md @@ -1,69 +1,74 @@ Our [build instructions][BUILD.md] assume you have a C++ development environment complete with Git, Python, Conan, CMake, and a C++ compiler. -This document exists to help readers set one up on any of the Big Three -platforms: Linux, macOS, or Windows. - -As an alternative to system packages, the Nix development shell can be used to provide a development environment. See [using nix development shell](./nix.md) for more details. +This document explains how to set one up. [BUILD.md]: ../../BUILD.md -## Linux +## Tested compiler versions -Package ecosystems vary across Linux distributions, -so there is no one set of instructions that will work for every Linux user. -The instructions below are written for Debian 12 (Bookworm). +`xrpld` is built in the **C++23** dialect by default. +Make sure your toolchain is recent enough — the compiler versions currently tested in CI are: -``` -export GCC_RELEASE=12 -sudo apt update -sudo apt install --yes gcc-${GCC_RELEASE} g++-${GCC_RELEASE} python3-pip \ - python-is-python3 python3-venv python3-dev curl wget ca-certificates \ - git build-essential cmake ninja-build libc6-dev -sudo pip install --break-system-packages conan +| Compiler | Version | +| ----------- | ------- | +| GCC | 15.2 | +| Clang | 22 | +| Apple Clang | 17 | +| MSVC | 19.44 | -sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-${GCC_RELEASE} 999 -sudo update-alternatives --install \ - /usr/bin/gcc gcc /usr/bin/gcc-${GCC_RELEASE} 100 \ - --slave /usr/bin/g++ g++ /usr/bin/g++-${GCC_RELEASE} \ - --slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-${GCC_RELEASE} \ - --slave /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-${GCC_RELEASE} \ - --slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-${GCC_RELEASE} \ - --slave /usr/bin/gcov gcov /usr/bin/gcov-${GCC_RELEASE} \ - --slave /usr/bin/gcov-tool gcov-tool /usr/bin/gcov-tool-${GCC_RELEASE} \ - --slave /usr/bin/gcov-dump gcov-dump /usr/bin/gcov-dump-${GCC_RELEASE} \ - --slave /usr/bin/lto-dump lto-dump /usr/bin/lto-dump-${GCC_RELEASE} -sudo update-alternatives --auto cc -sudo update-alternatives --auto gcc +LLVM tools (`clang-tidy` and `clang-format`) are also pinned to version 22. + +Older compilers may fail to build the latest `develop` code: the codebase now +relies on C++23 features and has been adjusted for `clang-tidy`. +If the latest code doesn't build for you, update your build toolchain first. + +## Linux and macOS + +The **recommended way** to get a development environment on Linux and macOS is +the Nix development shell. It provides the exact tooling used in CI — `git`, +`python`, `conan`, `cmake`, `clang-tidy`, `clang-format`, and everything else — +with a single command and without installing anything system-wide: + +```bash +nix --experimental-features 'nix-command flakes' develop ``` -If you use different Linux distribution, hope the instruction above can guide -you in the right direction. We try to maintain compatibility with all recent -compiler releases, so if you use a rolling distribution like e.g. Arch or CentOS -then there is a chance that everything will "just work". +On **Linux**, Nix also provides the compiler (GCC); on **macOS**, it provides +Clang. If you instead opt to use your system-wide Apple Clang (via +`nix develop .#apple-clang`), you need to manage its version yourself (see +below). -## macOS +See [Using the Nix development shell](./nix.md) for installation and usage +details, including how to select a different compiler. -Open a Terminal and enter the below command to bring up a dialog to install -the command line developer tools. -Once it is finished, this command should return a version greater than the -minimum required (see [BUILD.md][]). +> [!NOTE] +> Using Nix is not mandatory. Any custom environment (Homebrew packages or +> anything else) will continue to work, but then it is up to you to keep it in +> sync with the environment used in CI. Nix unifies the development environment +> for everyone and synchronizes updates, which is why we recommend it. -``` +### macOS: managing the Apple Clang version + +If you use your system-wide Apple Clang on macOS (via `nix develop .#apple-clang`), +the compiler version is whatever your installed Xcode (or Command Line Tools) +provides. The following command should return a version greater than or equal to +the [minimum required](#tested-compiler-versions): + +```bash clang --version ``` -### Install Xcode Specific Version (Optional) - -If you develop other applications using XCode you might be consistently updating to the newest version of Apple Clang. -This will likely cause issues building xrpld. You may want to install a specific version of Xcode: +If you develop other applications using Xcode, you might be consistently +updating to the newest version of Apple Clang, which will likely cause issues +building xrpld. You may want to install and pin a specific version of Xcode: 1. **Download Xcode** - Visit [Apple Developer Downloads](https://developer.apple.com/download/more/) - Sign in with your Apple Developer account - - Search for an Xcode version that includes **Apple Clang (Expected Version)** + - Search for an Xcode version that includes the expected Apple Clang version - Download the `.xip` file -2. **Install and Configure Xcode** +2. **Install and configure Xcode** ```bash # Extract the .xip file and rename for version management @@ -79,62 +84,28 @@ This will likely cause issues building xrpld. You may want to install a specific export DEVELOPER_DIR=/Applications/Xcode_16.2.app/Contents/Developer ``` -The command line developer tools should include Git too: +## Windows -``` -git --version -``` +Nix is not available on Windows, so the required tools have to be installed +manually: -Install [Homebrew][], -use it to install [pyenv][], -use it to install Python, -and use it to install Conan: +- [Visual Studio 2022](https://visualstudio.microsoft.com/) with the + **"Desktop development with C++"** workload — this provides MSVC and the + "x64 Native Tools Command Prompt". +- [Git for Windows](https://git-scm.com/download/win) +- [Python 3.11](https://www.python.org/downloads/), or higher +- [Conan 2.17](https://conan.io/downloads.html), or higher +- [CMake 3.22](https://cmake.org/download/), or higher -[Homebrew]: https://brew.sh/ -[pyenv]: https://github.com/pyenv/pyenv - -``` -/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" -brew update -brew install xz -brew install pyenv -pyenv install 3.11 -pyenv global 3.11 -eval "$(pyenv init -)" -pip install 'conan' -``` - -Install CMake with Homebrew too: - -``` -brew install cmake -``` +> [!NOTE] +> Windows is used for development only and is not recommended for production. ## Clang-tidy -Clang-tidy is required to run static analysis checks locally (see [CONTRIBUTING.md](../../CONTRIBUTING.md)). -It is not required to build the project. Currently this project uses clang-tidy version 21. +`clang-tidy` is required to run static analysis checks locally (see +[CONTRIBUTING.md](../../CONTRIBUTING.md)). It is not required to build the +project. This project currently uses `clang-tidy` version 22. -### Linux - -LLVM 21 is not available in the default Debian 12 (Bookworm) repositories. -Install it using the official LLVM apt installer: - -``` -wget https://apt.llvm.org/llvm.sh -chmod +x llvm.sh -sudo ./llvm.sh 21 -sudo apt install --yes clang-tidy-21 -``` - -Then use `run-clang-tidy-21` when running clang-tidy locally. - -### macOS - -Install LLVM 21 via Homebrew: - -``` -brew install llvm@21 -``` - -Then use `run-clang-tidy` from the LLVM 21 Homebrew prefix when running clang-tidy locally. +On Linux and macOS, the [Nix development shell](./nix.md) provides `clang-tidy` +22 out of the box — run it via `run-clang-tidy`. No separate installation is +needed. diff --git a/docs/build/nix.md b/docs/build/nix.md index 33bb3711d0..d6e53a254a 100644 --- a/docs/build/nix.md +++ b/docs/build/nix.md @@ -2,11 +2,14 @@ This guide explains how to use Nix to set up a reproducible development environment for xrpld. Using Nix eliminates the need to manually install utilities and ensures consistent tooling across different machines. +**The Nix development shell is the recommended way to develop xrpld.** It unifies the development environment for everyone and synchronizes updates: the same tooling and compiler versions are used both here and in CI. Any custom environment (Homebrew packages or anything else) will continue to work, but then it is up to you to keep it in sync with the environment used in CI. + ## Benefits of Using Nix - **Reproducible environment**: Everyone gets the same versions of tools and compilers +- **Matches CI**: The Linux CI runs in Docker images built from this exact Nix environment - **No system pollution**: Dependencies are isolated and don't affect your system packages -- **Multiple compiler versions**: Easily switch between different GCC and Clang versions +- **Consistent compilers**: The GCC and Clang shells use the same versions as CI - **Quick setup**: Get started with a single command - **Works on Linux and macOS**: Consistent experience across platforms @@ -28,11 +31,22 @@ This will: - Download and set up all required development tools (CMake, Ninja, Conan, etc.) - Configure the appropriate compiler for your platform: - - **macOS**: Apple Clang (default system compiler) - - **Linux**: GCC 15 + - **Linux**: GCC (provided by Nix) + - **macOS**: Clang (provided by Nix) The first time you run this command, it will take a few minutes to download and build the environment. Subsequent runs will be much faster. +### Platform notes + +- **Linux**: `nix develop` gives you a shell with all the tooling necessary to + develop xrpld and with GCC 15.2 (also provided by Nix). There are no caveats. +- **macOS**: `nix develop` gives you a full environment too, with Clang (and + every other tool, including Conan) provided by Nix. To use your system-wide + Apple Clang instead, enter `nix develop .#apple-clang`. Conan has no binary in + the Nix cache for macOS, so it is built from source the first time you enter + the shell, which makes the initial setup slower (this is handled + automatically; see [`nix/devshell.nix`](../../nix/devshell.nix)). + > [!TIP] > To avoid typing `--experimental-features 'nix-command flakes'` every time, you can permanently enable flakes by creating `~/.config/nix/nix.conf`: > @@ -48,19 +62,21 @@ The first time you run this command, it will take a few minutes to download and ### Choosing a different compiler -A compiler can be chosen by providing its name with the `.#` prefix, e.g. `nix develop .#gcc15`. +A compiler can be chosen by providing its name with the `.#` prefix, e.g. `nix develop .#clang`. +The `.#gcc` and `.#clang` shells provide the same GCC and Clang versions used in CI +(pinned in [`nix/packages.nix`](../../nix/packages.nix)). Use `nix flake show` to see all the available development shells. -Use `nix develop .#no_compiler` to use the compiler from your system. +Use `nix develop .#no-compiler` to use the compiler from your system. ### Example Usage ```bash -# Use GCC 14 -nix develop .#gcc14 +# Use GCC (same version as CI) +nix develop .#gcc -# Use Clang 19 -nix develop .#clang19 +# Use Clang (same version as CI) +nix develop .#clang # Use default for your platform nix develop @@ -68,12 +84,28 @@ nix develop ### Using a different shell -`nix develop` opens bash by default. If you want to use another shell this could be done by adding `-c` flag. For example: +`nix develop` opens bash by default. To use another shell, pass it with the `-c` flag — this works with any shell, e.g. `zsh` or `fish`: ```bash +# Use zsh nix develop -c zsh + +# Use fish +nix develop -c fish + +# Use your login shell +nix develop -c "$SHELL" ``` +> [!WARNING] +> Your shell's interactive startup files (e.g. `config.fish`, `.zshrc`) may prepend other directories — most commonly Homebrew — to `$PATH`, which can shadow the tools provided by the Nix shell. After entering, verify that tools resolve into the Nix store: +> +> ```bash +> command -v cmake # should print a /nix/store/... path +> ``` +> +> If it doesn't, either adjust your shell configuration so it doesn't override `$PATH`, or use [direnv](#automatic-activation-with-direnv) (below), which loads the environment _after_ your shell config and so takes precedence regardless of the shell you use. + ## Building xrpld with Nix Once inside the Nix development shell, follow the standard [build instructions](../../BUILD.md#steps). The Nix shell provides all necessary tools (CMake, Ninja, Conan, etc.). @@ -82,6 +114,16 @@ Once inside the Nix development shell, follow the standard [build instructions]( [direnv](https://direnv.net/) or [nix-direnv](https://github.com/nix-community/nix-direnv) can automatically activate the Nix development shell when you enter the repository directory. +This is also the most robust way to use the environment from **any shell** (bash, zsh, fish, …): direnv stays in your current shell and loads the environment _after_ your shell's startup files have run, so the Nix-provided tools take precedence over anything your shell configuration adds to `$PATH`. + +The repository already ships an `.envrc` at its root that activates the Nix flake development shell, so you don't need to create one. To use it: + +1. [Install direnv](https://direnv.net/docs/installation.html) and [hook it into your shell](https://direnv.net/docs/hook.html) (bash, zsh, fish, …). Installing [nix-direnv](https://github.com/nix-community/nix-direnv) as well is recommended: it caches the shell so that activation is near-instant after the first run. +2. Run `direnv allow` once in the repository root. direnv will then load (and reload) the Nix development shell automatically whenever you enter the directory. + +> [!NOTE] +> direnv only caches the `.direnv` directory (already listed in `.gitignore`); no other repository files are affected. + ## Conan and Prebuilt Packages Please note that there is no guarantee that binaries from conan cache will work when using nix. If you encounter any errors, please use `--build '*'` to force conan to compile everything from source: @@ -93,3 +135,8 @@ conan install .. --output-folder . --build '*' --settings build_type=Release ## Updating `flake.lock` file To update `flake.lock` to the latest revision use `nix flake update` command. + +## Troubleshooting + +See [Troubleshooting Nix problems](./nix_troubleshooting.md) for common issues, +such as `nix develop` failing inside Git worktrees. diff --git a/docs/build/nix_troubleshooting.md b/docs/build/nix_troubleshooting.md new file mode 100644 index 0000000000..ae5cb8059a --- /dev/null +++ b/docs/build/nix_troubleshooting.md @@ -0,0 +1,61 @@ +# Troubleshooting Nix problems + +Common issues encountered when using the [Nix development shell](./nix.md), and +how to resolve them. + +## Git worktrees + +If `nix develop` fails with an error like: + +``` +error: + … while fetching the input 'git+file:///path/to/rippled' + + error: opening Git repository "/path/to/rippled": unsupported extension name extensions.relativeworktrees (libgit2 error code = 6) +``` + +then your Nix is linked against a libgit2 older than **1.9.4**. Git 2.48+ writes +the `extensions.relativeWorktrees` config entry when a worktree is created with +relative paths (`git worktree add --relative-paths`, or with +`worktree.useRelativePaths=true`), and older libgit2 versions refuse to open a +repository that uses it. Nix uses libgit2 to read the flake, so evaluation +fails. + +> [!IMPORTANT] +> This entry is written to the **shared** repository config, so once any +> relative worktree exists, `nix develop` fails in the main checkout too — not +> just inside the worktree. + +### Workarounds + +These work today, with any Nix version: + +- bypass libgit2 with a `path:` flakeref: `nix develop "path:$PWD"` + (note: this copies the working tree to the store and ignores `.gitignore`); or +- create worktrees with absolute paths (omit `--relative-paths`); or +- clear the extension if you don't need relative worktrees: + `git config --unset extensions.relativeWorktrees`. + +### Permanent fix + +The fix is in [libgit2 1.9.4](https://github.com/libgit2/libgit2/releases/tag/v1.9.4), +so the real solution is a Nix that links against libgit2 `1.9.4` or newer. Check +which version yours links against: + +```bash +nix-store -qR "$(readlink -f "$(command -v nix)")" | grep libgit2 +``` + +> [!WARNING] +> `nix upgrade-nix` does **not** help yet. It installs the build from the +> official [`nix-fallback-paths`](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/installer/tools/nix-fallback-paths.nix), +> which is still linked against libgit2 `1.9.2` — there is no new upstream Nix +> release with the fix. (On some systems that build is even the exact store path +> you already have, making the upgrade a no-op.) + +nixpkgs has already rebuilt Nix against the fixed libgit2 (e.g. `nix-2.34.7+1`), +so the cleanest path is to reinstall Nix using your usual installation method +once it picks up that rebuild, then re-run the `grep libgit2` check above to +confirm it reports `1.9.4` or newer. + +Until then, prefer the workarounds above. diff --git a/flake.lock b/flake.lock index 5ec053975d..80243ccf15 100644 --- a/flake.lock +++ b/flake.lock @@ -2,20 +2,21 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1777954456, - "narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=", + "lastModified": 1781173989, + "narHash": "sha256-fnzKKPvS+oieI/pTzotA5tkoM47EB1NpaBcgk4R97hE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1", + "rev": "8c91a71d13451abc40eb9dae8910f972f979852f", "type": "github" }, "original": { - "id": "nixpkgs", - "ref": "nixos-unstable", - "type": "indirect" + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" } }, - "nixpkgs-glibc231": { + "nixpkgs-custom-glibc": { "flake": false, "locked": { "lastModified": 1593520194, @@ -35,7 +36,7 @@ "root": { "inputs": { "nixpkgs": "nixpkgs", - "nixpkgs-glibc231": "nixpkgs-glibc231" + "nixpkgs-custom-glibc": "nixpkgs-custom-glibc" } } }, diff --git a/flake.nix b/flake.nix index 18671bdf31..c52f4d050e 100644 --- a/flake.nix +++ b/flake.nix @@ -1,21 +1,21 @@ { description = "Nix related things for xrpld"; inputs = { - nixpkgs.url = "nixpkgs/nixos-unstable"; + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; # nixpkgs snapshot (2020-06-30) that shipped glibc 2.31 as the primary # version — matches the system libc on Ubuntu 20.04 LTS. Imported # manually (flake = false) because this revision predates nixpkgs' # own flake.nix. - nixpkgs-glibc231 = { + nixpkgs-custom-glibc = { url = "github:NixOS/nixpkgs/9cd98386a38891d1074fc18036b842dc4416f562"; flake = false; }; }; outputs = - { nixpkgs, nixpkgs-glibc231, ... }: + { nixpkgs, nixpkgs-custom-glibc, ... }: let - forEachSystem = import ./nix/utils.nix { inherit nixpkgs nixpkgs-glibc231; }; + forEachSystem = import ./nix/utils.nix { inherit nixpkgs nixpkgs-custom-glibc; }; in { devShells = forEachSystem (import ./nix/devshell.nix); diff --git a/include/xrpl/basics/Buffer.h b/include/xrpl/basics/Buffer.h index 59968a4fa4..c0ae8ef56e 100644 --- a/include/xrpl/basics/Buffer.h +++ b/include/xrpl/basics/Buffer.h @@ -6,6 +6,7 @@ #include #include #include +#include namespace xrpl { diff --git a/include/xrpl/basics/CompressionAlgorithms.h b/include/xrpl/basics/CompressionAlgorithms.h index e24c490337..a5ec8645b6 100644 --- a/include/xrpl/basics/CompressionAlgorithms.h +++ b/include/xrpl/basics/CompressionAlgorithms.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff --git a/include/xrpl/basics/DecayingSample.h b/include/xrpl/basics/DecayingSample.h index d1861ebc4a..86a8baa62e 100644 --- a/include/xrpl/basics/DecayingSample.h +++ b/include/xrpl/basics/DecayingSample.h @@ -2,6 +2,7 @@ #include #include +#include namespace xrpl { @@ -12,8 +13,8 @@ template class DecayingSample { public: - using value_type = typename Clock::duration::rep; - using time_point = typename Clock::time_point; + using value_type = Clock::duration::rep; + using time_point = Clock::time_point; DecayingSample() = delete; @@ -93,7 +94,7 @@ template class DecayWindow { public: - using time_point = typename Clock::time_point; + using time_point = Clock::time_point; explicit DecayWindow(time_point now) : when_(now) { diff --git a/include/xrpl/basics/Expected.h b/include/xrpl/basics/Expected.h deleted file mode 100644 index 3796151777..0000000000 --- a/include/xrpl/basics/Expected.h +++ /dev/null @@ -1,248 +0,0 @@ -#pragma once - -#include - -#include - -#include - -namespace xrpl { - -/** Expected is an approximation of std::expected (hoped for in C++23) - - See: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html - - The implementation is entirely based on boost::outcome_v2::result. -*/ - -// Exception thrown by an invalid access to Expected. -struct BadExpectedAccess : public std::runtime_error -{ - BadExpectedAccess() : runtime_error("bad expected access") - { - } -}; - -namespace detail { - -// Custom policy for Expected. Always throw on an invalid access. -struct ThrowPolicy : public boost::outcome_v2::policy::base -{ - template - static constexpr void - // NOLINTNEXTLINE(readability-identifier-naming) - wide_value_check(Impl&& self) - { - if (!base::_has_value(std::forward(self))) - Throw(); - } - - template - static constexpr void - // NOLINTNEXTLINE(readability-identifier-naming) - wide_error_check(Impl&& self) - { - if (!base::_has_error(std::forward(self))) - Throw(); - } - - template - static constexpr void - // NOLINTNEXTLINE(readability-identifier-naming) - wide_exception_check(Impl&& self) - { - if (!base::_has_exception(std::forward(self))) - Throw(); - } -}; - -} // namespace detail - -// Definition of Unexpected, which is used to construct the unexpected -// return type of an Expected. -template -class Unexpected -{ -public: - static_assert(!std::is_same_v, "E must not be void"); - - Unexpected() = delete; - - constexpr explicit Unexpected(E const& e) : val_(e) - { - } - - constexpr explicit Unexpected(E&& e) : val_(std::move(e)) - { - } - - [[nodiscard]] constexpr E const& - value() const& - { - return val_; - } - - constexpr E& - value() & - { - return val_; - } - - constexpr E&& - value() && - { - return std::move(val_); - } - - [[nodiscard]] constexpr E const&& - value() const&& - { - return std::move(val_); - } - -private: - E val_; -}; - -// Unexpected deduction guide that converts array to const*. -template -Unexpected(E (&)[N]) -> Unexpected; - -// Definition of Expected. All of the machinery comes from boost::result. -template -class [[nodiscard]] Expected : private boost::outcome_v2::result -{ - using Base = boost::outcome_v2::result; - -public: - template - requires std::convertible_to - constexpr Expected(U&& r) : Base(boost::outcome_v2::in_place_type_t{}, std::forward(r)) - { - } - - template - requires std::convertible_to && (!std::is_reference_v) - constexpr Expected(Unexpected e) - : Base(boost::outcome_v2::in_place_type_t{}, std::move(e.value())) - { - } - - [[nodiscard]] constexpr bool - // NOLINTNEXTLINE(readability-identifier-naming) - has_value() const - { - return Base::has_value(); - } - - [[nodiscard]] constexpr T const& - value() const - { - return Base::value(); - } - - constexpr T& - value() - { - return Base::value(); - } - - [[nodiscard]] constexpr E const& - error() const& - { - return Base::error(); - } - - [[nodiscard]] constexpr E& - error() & - { - return Base::error(); - } - - [[nodiscard]] constexpr E&& - error() && - { - return std::move(Base::error()); - } - - constexpr explicit - operator bool() const - { - return has_value(); - } - - // Add operator* and operator-> so the Expected API looks a bit more like - // what std::expected is likely to look like. See: - // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html - [[nodiscard]] constexpr T& - operator*() - { - return this->value(); - } - - [[nodiscard]] constexpr T const& - operator*() const - { - return this->value(); - } - - [[nodiscard]] constexpr T* - operator->() - { - return &this->value(); - } - - [[nodiscard]] constexpr T const* - operator->() const - { - return &this->value(); - } -}; - -// Specialization of Expected. Allows returning either success -// (without a value) or the reason for the failure. -template -class [[nodiscard]] -Expected : private boost::outcome_v2::result -{ - using Base = boost::outcome_v2::result; - -public: - // The default constructor makes a successful Expected. - // This aligns with std::expected behavior proposed in P0323R10. - constexpr Expected() : Base(boost::outcome_v2::success()) - { - } - - template - requires std::convertible_to && (!std::is_reference_v) - constexpr Expected(Unexpected e) : Base(E(std::move(e.value()))) - { - } - - [[nodiscard]] constexpr E const& - error() const& - { - return Base::error(); - } - - [[nodiscard]] constexpr E& - error() & - { - return Base::error(); - } - - [[nodiscard]] constexpr E&& - error() && - { - return std::move(Base::error()); - } - - constexpr explicit - operator bool() const - { - return Base::has_value(); - } -}; - -} // namespace xrpl diff --git a/include/xrpl/basics/FileUtilities.h b/include/xrpl/basics/FileUtilities.h index 8cf7e4893f..c7a427b8a9 100644 --- a/include/xrpl/basics/FileUtilities.h +++ b/include/xrpl/basics/FileUtilities.h @@ -3,7 +3,9 @@ #include #include +#include #include +#include namespace xrpl { diff --git a/include/xrpl/basics/IntrusivePointer.h b/include/xrpl/basics/IntrusivePointer.h index d66c340d3f..c23d6afb85 100644 --- a/include/xrpl/basics/IntrusivePointer.h +++ b/include/xrpl/basics/IntrusivePointer.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include diff --git a/include/xrpl/basics/IntrusivePointer.ipp b/include/xrpl/basics/IntrusivePointer.ipp index 8344a3e613..67d43b05d6 100644 --- a/include/xrpl/basics/IntrusivePointer.ipp +++ b/include/xrpl/basics/IntrusivePointer.ipp @@ -641,6 +641,9 @@ template T* SharedWeakUnion::unsafeGetRawPtr() const { + // tp_ packs a raw pointer together with a strength bit; recovering the + // pointer inherently requires an integer-to-pointer cast. + // NOLINTNEXTLINE(performance-no-int-to-ptr) return reinterpret_cast(tp_ & kPtrMask); } diff --git a/include/xrpl/basics/IntrusiveRefCounts.h b/include/xrpl/basics/IntrusiveRefCounts.h index 0b00f1d5b1..5eb1422541 100644 --- a/include/xrpl/basics/IntrusiveRefCounts.h +++ b/include/xrpl/basics/IntrusiveRefCounts.h @@ -3,6 +3,7 @@ #include #include +#include #include namespace xrpl { diff --git a/include/xrpl/basics/Log.h b/include/xrpl/basics/Log.h index 6bafbc7c54..4e3437fe71 100644 --- a/include/xrpl/basics/Log.h +++ b/include/xrpl/basics/Log.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -11,7 +10,9 @@ #include #include #include +#include #include +#include namespace xrpl { @@ -206,8 +207,7 @@ private: #ifndef JLOG #define JLOG(x) \ if (!(x)) \ - { \ - } \ + ; \ else \ x #endif diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index e67f1f534d..073da12f89 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -2,12 +2,20 @@ #include +#include +#include +#include #include #include #include #include #include +#include +#include #include +#include +#include +#include namespace xrpl { @@ -38,17 +46,58 @@ isPowerOfTen(T value) return logTen(value).has_value(); } +namespace detail { + +/** Builds a table of the powers of 10 + * + * This function is marked consteval, so it can only be run in + * a constexpr context. This assures that it is and can only be run at + * compile time. Doing it at runtime would be pretty wasteful and + * inefficient. + */ +constexpr std::size_t kInt64Digits = 20; +consteval std::array +buildPowersOfTen() +{ + std::array result{}; + + std::uint64_t power = 1; + std::size_t exponent = 0; + // end the loop early so it doesn't overflow; + for (; exponent < result.size() - 1; ++exponent, power *= 10) + { + result[exponent] = power; + if (power > std::numeric_limits::max() / 10) + throw std::logic_error("Power of 10 table is too big"); + } + result[exponent] = power; + if (power < std::numeric_limits::max() / 10) + throw std::logic_error("Power of 10 table is not big enough for the uint64_t type"); + + return result; +} + +} // namespace detail + +constexpr std::array kPowerOfTen = detail::buildPowersOfTen(); + +static_assert(kPowerOfTen[0] == 1); +static_assert(kPowerOfTen[1] == 10); +static_assert(kPowerOfTen[10] == 10'000'000'000); +static_assert( + isPowerOfTen(kPowerOfTen.back()) && *logTen(kPowerOfTen.back()) == detail::kInt64Digits - 1); + /** MantissaRange defines a range for the mantissa of a normalized Number. * * The mantissa is in the range [min, max], where * * min is a power of 10, and * * max = min * 10 - 1. * - * The mantissa_scale enum indicates whether the range is "small" or "large". - * This intentionally restricts the number of MantissaRanges that can be - * instantiated to two: one for each scale. + * The MantissaScale enum indicates properties of the range: size, and some behavioral + * options. This intentionally restricts the number of unique MantissaRanges that can + * be instantiated: one for each scale. * - * The "small" scale is based on the behavior of STAmount for IOUs. It has a min + * The "Small" scale is based on the behavior of STAmount for IOUs. It has a min * value of 10^15, and a max value of 10^16-1. This was sufficient for * uses before Lending Protocol was implemented, mostly related to AMM. * @@ -59,46 +108,100 @@ isPowerOfTen(T value) * STNumber field type, and for internal calculations. That necessitated the * "large" scale. * - * The "large" scale is intended to represent all values that can be represented + * The "Large" scales are intended to represent all values that can be represented * by an STAmount - IOUs, XRP, and MPTs. It has a min value of 10^18, and a max - * value of 10^19-1. + * value of 10^19-1. "LargeLegacy" is like "Large", but preserves + * a rounding error when a computation results in a mantissa of + * Number::kMaxRep that needs to be rounded up, but rounds down + * instead. It will maintain consistent behavior until the fixCleanup3_2_0 + * amendment is enabled. * * Note that if the mentioned amendments are eventually retired, this class - * should be left in place, but the "small" scale option should be removed. This + * should be left in place, but the "Small" scale option should be removed. This * will allow for future expansion beyond 64-bits if it is ever needed. */ -struct MantissaRange +struct MantissaRange final { using rep = std::uint64_t; - enum class MantissaScale { Small, Large }; - explicit constexpr MantissaRange(MantissaScale scale) - : min(getMin(scale)), log(logTen(min).value_or(-1)), scale(scale) + enum class MantissaScale { + Small, + // LargeLegacy can be removed when fixCleanup3_2_0 is retired + LargeLegacy, + Large, + }; + + // This entire enum can be removed when fixCleanup3_2_0 is retired + enum class CuspRoundingFix : bool { + Disabled = false, + Enabled = true, + }; + + explicit constexpr MantissaRange(MantissaScale sc) : scale(sc) { } - rep min; - rep max{(min * 10) - 1}; - int log; - MantissaScale scale; + MantissaScale const scale; + int const log{getExponent(scale)}; + rep const min{getMin(scale, log)}; + rep const max{(min * 10) - 1}; + CuspRoundingFix const cuspRoundingFixEnabled{isCuspFixEnabled(scale)}; + + static MantissaRange const& + getMantissaRange(MantissaScale scale); + + static std::set const& + getAllScales(); private: - static constexpr rep - getMin(MantissaScale scale) + static constexpr int + getExponent(MantissaScale scale) { switch (scale) { case MantissaScale::Small: - return 1'000'000'000'000'000ULL; + return 15; + case MantissaScale::LargeLegacy: case MantissaScale::Large: - return 1'000'000'000'000'000'000ULL; + return 18; + // LCOV_EXCL_START default: - // Since this can never be called outside a non-constexpr - // context, this throw assures that the build fails if an + // If called in a constexpr context, this throw assures that the build fails if an // invalid scale is used. throw std::runtime_error("Unknown mantissa scale"); + // LCOV_EXCL_STOP } } + + // Keep this function for future use with different ways to compute + // the ranges. + static constexpr rep + getMin(MantissaScale scale, int exponent) + { + if (exponent < 0 || exponent >= kPowerOfTen.size()) + throw std::runtime_error("Invalid exponent"); // LCOV_EXCL_LINE + return kPowerOfTen[exponent]; + } + + static constexpr CuspRoundingFix + isCuspFixEnabled(MantissaScale scale) + { + switch (scale) + { + case MantissaScale::Small: + case MantissaScale::LargeLegacy: + return CuspRoundingFix::Disabled; + case MantissaScale::Large: + return CuspRoundingFix::Enabled; + default: + // If called in a constexpr context, this throw assures that the build fails if an + // invalid scale is used. + throw std::runtime_error("Unknown mantissa scale"); // LCOV_EXCL_LINE + } + } + + static std::unordered_map const& + getRanges(); }; // Like std::integral, but only 64-bit integral types. @@ -203,7 +306,7 @@ concept Integral64 = std::is_same_v || std::is_same_v 0; + // Both have same sign and the left is zero: both must be non-negative. + // If the right is greater than 0, then it is larger, so the comparison is true. + if (l.mantissa_ == 0) + return r.mantissa_ > 0; - // Both have same sign, the right is zero and the left is non-zero. - if (y.mantissa_ == 0) + // Both have same sign, the right is zero and the left is non-zero, so the left must be + // positive, and thus is larger, so the comparison is false. + if (r.mantissa_ == 0) return false; // Both have the same sign, compare by exponents: - if (x.exponent_ > y.exponent_) + if (l.exponent_ > r.exponent_) return lneg; - if (x.exponent_ < y.exponent_) + if (l.exponent_ < r.exponent_) return !lneg; - // If equal exponents, compare mantissas - return x.mantissa_ < y.mantissa_; + // If equal signs and exponents, compare mantissas. + if (lneg) + { + // If negative, the operator is reversed. + return l.mantissa_ > r.mantissa_; + } + + return l.mantissa_ < r.mantissa_; } /** Return the sign of the amount */ @@ -424,49 +534,28 @@ public: return kRange.get().log; } - /// oneSmall is needed because the ranges are private - static constexpr Number - oneSmall(); - /// oneLarge is needed because the ranges are private - static constexpr Number - oneLarge(); - - // And one is needed because it needs to choose between oneSmall and - // oneLarge based on the current range static Number one(); - template + template < + auto MinMantissa, + auto MaxMantissa, + Integral64 T = std::decay_t> [[nodiscard]] std::pair - normalizeToRange(T minMantissa, T maxMantissa) const; + normalizeToRange() const; private: static thread_local RoundingMode mode; // The available ranges for mantissa - static constexpr MantissaRange kSmallRange{MantissaRange::MantissaScale::Small}; - static_assert(isPowerOfTen(kSmallRange.min)); - static_assert(kSmallRange.min == 1'000'000'000'000'000LL); - static_assert(kSmallRange.max == 9'999'999'999'999'999LL); - static_assert(kSmallRange.log == 15); - static_assert(kSmallRange.min < kMaxRep); - static_assert(kSmallRange.max < kMaxRep); - static constexpr MantissaRange kLargeRange{MantissaRange::MantissaScale::Large}; - static_assert(isPowerOfTen(kLargeRange.min)); - static_assert(kLargeRange.min == 1'000'000'000'000'000'000ULL); - static_assert(kLargeRange.max == internalrep(9'999'999'999'999'999'999ULL)); - static_assert(kLargeRange.log == 18); - static_assert(kLargeRange.min < kMaxRep); - static_assert(kLargeRange.max > kMaxRep); - // The range for the mantissa when normalized. // Use reference_wrapper to avoid making copies, and prevent accidentally // changing the values inside the range. static thread_local std::reference_wrapper kRange; void - normalize(); + normalize(MantissaRange const& range); /** Normalize Number components to an arbitrary range. * @@ -481,7 +570,8 @@ private: T& mantissa, int& exponent, internalrep const& minMantissa, - internalrep const& maxMantissa); + internalrep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled); template friend void @@ -490,7 +580,9 @@ private: T& mantissa, int& exponent, MantissaRange::rep const& minMantissa, - MantissaRange::rep const& maxMantissa); + MantissaRange::rep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled, + bool dropped); [[nodiscard]] bool isnormal() const noexcept; @@ -526,7 +618,7 @@ static constexpr Number kNumZero{}; inline Number::Number(bool negative, internalrep mantissa, int exponent, Normalized) : Number(negative, mantissa, exponent, Unchecked{}) { - normalize(); + normalize(kRange); } inline Number::Number(internalrep mantissa, int exponent, Normalized) @@ -696,10 +788,21 @@ Number::isnormal() const noexcept kMinExponent <= exponent_ && exponent_ <= kMaxExponent); } -template +template std::pair -Number::normalizeToRange(T minMantissa, T maxMantissa) const +Number::normalizeToRange() const { + static_assert(std::is_same_v || std::is_same_v); + static_assert(std::is_same_v>); + static_assert(std::is_same_v>); + auto constexpr kMIN = static_cast(MinMantissa); + auto constexpr kMAX = static_cast(MaxMantissa); + static_assert(kMIN > 0); + static_assert(kMIN % 10 == 0); + static_assert(isPowerOfTen(kMIN)); + static_assert(kMAX % 10 == 9); + static_assert((kMAX + 1) / 10 == kMIN); + bool negative = negative_; internalrep mantissa = mantissa_; int exponent = exponent_; @@ -711,7 +814,10 @@ Number::normalizeToRange(T minMantissa, T maxMantissa) const "xrpl::Number::normalizeToRange", "Number is non-negative for unsigned range."); } - Number::normalize(negative, mantissa, exponent, minMantissa, maxMantissa); + // Don't need to worry about the cuspRounding fix because rounding up will never take the + // mantissa over maxMantissa with a ones digit value other than 0. 0 can safely be truncated. + Number::normalize( + negative, mantissa, exponent, kMIN, kMAX, MantissaRange::CuspRoundingFix::Disabled); auto const sign = negative ? -1 : 1; return std::make_pair(static_cast(sign * mantissa), exponent); @@ -763,6 +869,8 @@ to_string(MantissaRange::MantissaScale const& scale) { case MantissaRange::MantissaScale::Small: return "small"; + case MantissaRange::MantissaScale::LargeLegacy: + return "largeLegacy"; case MantissaRange::MantissaScale::Large: return "large"; default: diff --git a/include/xrpl/basics/RangeSet.h b/include/xrpl/basics/RangeSet.h index e1cee8b6c4..2ed543b376 100644 --- a/include/xrpl/basics/RangeSet.h +++ b/include/xrpl/basics/RangeSet.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include diff --git a/include/xrpl/basics/Resolver.h b/include/xrpl/basics/Resolver.h index 3b6a950247..d48958b76d 100644 --- a/include/xrpl/basics/Resolver.h +++ b/include/xrpl/basics/Resolver.h @@ -3,6 +3,7 @@ #include #include +#include #include namespace xrpl { diff --git a/include/xrpl/basics/ResolverAsio.h b/include/xrpl/basics/ResolverAsio.h index 2764777327..0b78b9747c 100644 --- a/include/xrpl/basics/ResolverAsio.h +++ b/include/xrpl/basics/ResolverAsio.h @@ -5,6 +5,8 @@ #include +#include + namespace xrpl { class ResolverAsio : public Resolver diff --git a/include/xrpl/basics/SHAMapHash.h b/include/xrpl/basics/SHAMapHash.h index 76d9d4fa3d..3c3d525022 100644 --- a/include/xrpl/basics/SHAMapHash.h +++ b/include/xrpl/basics/SHAMapHash.h @@ -3,7 +3,9 @@ #include #include +#include #include +#include namespace xrpl { diff --git a/include/xrpl/basics/SharedWeakCachePointer.h b/include/xrpl/basics/SharedWeakCachePointer.h index c2c3239eea..a143647a1e 100644 --- a/include/xrpl/basics/SharedWeakCachePointer.h +++ b/include/xrpl/basics/SharedWeakCachePointer.h @@ -1,5 +1,7 @@ #pragma once +#include +#include #include #include diff --git a/include/xrpl/basics/SlabAllocator.h b/include/xrpl/basics/SlabAllocator.h index 0172b1ade2..8e741991f6 100644 --- a/include/xrpl/basics/SlabAllocator.h +++ b/include/xrpl/basics/SlabAllocator.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #if BOOST_OS_LINUX diff --git a/include/xrpl/basics/Slice.h b/include/xrpl/basics/Slice.h index 948d012958..f87ca063b8 100644 --- a/include/xrpl/basics/Slice.h +++ b/include/xrpl/basics/Slice.h @@ -211,15 +211,17 @@ operator<<(Stream& s, Slice const& v) } template -std::enable_if_t || std::is_same_v, Slice> +Slice makeSlice(std::array const& a) + requires(std::is_same_v || std::is_same_v) { return Slice(a.data(), a.size()); } template -std::enable_if_t || std::is_same_v, Slice> +Slice makeSlice(std::vector const& v) + requires(std::is_same_v || std::is_same_v) { return Slice(v.data(), v.size()); } diff --git a/include/xrpl/basics/StringUtilities.h b/include/xrpl/basics/StringUtilities.h index 28421626aa..97df43d68f 100644 --- a/include/xrpl/basics/StringUtilities.h +++ b/include/xrpl/basics/StringUtilities.h @@ -1,17 +1,19 @@ #pragma once #include -#include #include #include #include #include +#include #include #include #include +#include #include +#include namespace xrpl { @@ -95,13 +97,7 @@ strUnHex(std::size_t strSize, Iterator begin, Iterator end) } inline std::optional -strUnHex(std::string const& strSrc) -{ - return strUnHex(strSrc.size(), strSrc.cbegin(), strSrc.cend()); -} - -inline std::optional -strViewUnHex(std::string_view strSrc) +strUnHex(std::string_view strSrc) { return strUnHex(strSrc.size(), strSrc.cbegin(), strSrc.cend()); } diff --git a/include/xrpl/basics/TaggedCache.h b/include/xrpl/basics/TaggedCache.h index 52b866cdad..16c87cf833 100644 --- a/include/xrpl/basics/TaggedCache.h +++ b/include/xrpl/basics/TaggedCache.h @@ -1,22 +1,46 @@ #pragma once -#include -#include -#include +#include +#include // IWYU pragma: keep #include #include #include -#include +#include +#include +#include +#include +#include #include +#include +#include +#include #include +#include #include +#include #include #include #include namespace xrpl { +namespace detail { + +// Replace-policy tags selecting how TaggedCache::canonicalizeImpl resolves a +// collision when the key already exists (defined in TaggedCache.ipp): +// - ReplaceCached: always replace the cached value with `data`. `data` is +// never written back and may be const. +// - ReplaceClient: keep the cached value and write it back into `data` (the +// client's pointer), which must therefore be writable. +// - ReplaceDynamically: call the supplied callback to decide per call; `data` +// is written back when the cached value is kept, so it must be writable. +struct ReplaceCached; +struct ReplaceClient; +struct ReplaceDynamically; + +} // namespace detail + /** Map/cache combination. This class implements a cache and a map. The cache keeps objects alive in the map. The map allows multiple code paths that reference objects @@ -96,6 +120,32 @@ public: bool del(key_type const& key, bool valid); +private: + // Selects the `data` parameter type of canonicalizeImpl from the replace + // policy: const for detail::ReplaceCached (never written back), otherwise + // writable. + template + using CanonicalizeClientPointerType = std::conditional_t< + std::is_same_v, + SharedPointerType const&, + SharedPointerType&>; + + /** Shared implementation of the canonicalize family. + + `policy` selects how a collision is resolved when `key` already exists: + detail::ReplaceCached, detail::ReplaceClient or + detail::ReplaceDynamically. For ReplaceDynamically `replaceCallback` is + invoked with the existing strong pointer and returns whether to replace + the cached value with `data`; for the tag policies it is unused. + */ + template + bool + canonicalizeImpl( + key_type const& key, + CanonicalizeClientPointerType data, + Policy policy, + Callback&& replaceCallback = nullptr); + public: /** Replace aliased objects with originals. @@ -104,19 +154,52 @@ public: This routine eliminates the duplicate and performs a replacement on the callers shared pointer if needed. + `replaceCallback` is a callable taking the existing strong pointer and + returning whether to replace the cached value with `data` (true) or to + keep the cached value and write it back into `data` (false). Because the + write-back case mutates `data`, `data` must be writable. + @param key The key corresponding to the object @param data A shared pointer to the data corresponding to the object. - @param replace Function that decides if cache should be replaced + @param replaceCallback A callable (existing strong pointer -> bool). - @return `true` If the key already existed. - */ - template + @return `true` if an existing live entry was found and used; `false` if a new entry was + inserted or an expired tracked entry was re-cached. + **/ + template bool - canonicalize(key_type const& key, SharedPointerType& data, R&& replaceCallback); + canonicalize(key_type const& key, SharedPointerType& data, Callback&& replaceCallback); + /** Insert/update the canonical entry for `key`, always replacing the + cached value with `data`. + + If an entry already exists for `key`, the cached value is unconditionally + replaced with `data`; otherwise `data` is inserted. `data` is never + written back, so it may be const. + + @param key The key corresponding to the object. + @param data A shared pointer to the data corresponding to the object. + + @return `true` if an existing live entry was found and used; `false` if a new entry was + inserted or an expired tracked entry was re-cached. + **/ bool canonicalizeReplaceCache(key_type const& key, SharedPointerType const& data); + /** Insert the canonical entry for `key`, keeping any existing cached value. + + If an entry already exists for `key`, the cached value is kept and + written back into `data` so the caller ends up with the canonical + object; otherwise `data` is inserted. Because `data` may be overwritten + it must be writable. + + @param key The key corresponding to the object. + @param data A shared pointer to the data corresponding to the object; + updated to the canonical value when one already exists. + + @return `true` if an existing live entry was found and used; `false` if a new entry was + inserted or an expired tracked entry was re-cached. + **/ bool canonicalizeReplaceClient(key_type const& key, SharedPointerType& data); @@ -129,11 +212,13 @@ public: */ template auto - insert(key_type const& key, T const& value) -> std::enable_if_t; + insert(key_type const& key, T const& value) -> ReturnType + requires(!IsKeyCache); template auto - insert(key_type const& key) -> std::enable_if_t; + insert(key_type const& key) -> ReturnType + requires IsKeyCache; // VFALCO NOTE It looks like this returns a copy of the data in // the output parameter 'data'. This could be expensive. @@ -157,7 +242,7 @@ public: /** Fetch an item from the cache. If the digest was not found, Handler will be called with this signature: - std::shared_ptr(void) + SLE::const_pointer(void) */ template SharedPointerType @@ -181,14 +266,14 @@ private: beast::insight::Collector::ptr const& collector) : hook(collector->makeHook(handler)) , size(collector->makeGauge(prefix, "size")) - , hit_rate(collector->makeGauge(prefix, "hit_rate")) + , hitRate(collector->makeGauge(prefix, "hit_rate")) { } beast::insight::Hook hook; beast::insight::Gauge size; - beast::insight::Gauge hit_rate; + beast::insight::Gauge hitRate; std::size_t hits{0}; std::size_t misses{0}; @@ -197,16 +282,16 @@ private: class KeyOnlyEntry { public: - clock_type::time_point last_access; + clock_type::time_point lastAccess; - explicit KeyOnlyEntry(clock_type::time_point const& lastAccess) : last_access(lastAccess) + explicit KeyOnlyEntry(clock_type::time_point const& lastAccess) : lastAccess(lastAccess) { } void touch(clock_type::time_point const& now) { - last_access = now; + lastAccess = now; } }; @@ -214,10 +299,10 @@ private: { public: shared_weak_combo_pointer_type ptr; - clock_type::time_point last_access; + clock_type::time_point lastAccess; ValueEntry(clock_type::time_point const& lastAccess, shared_pointer_type const& ptr) - : ptr(ptr), last_access(lastAccess) + : ptr(ptr), lastAccess(lastAccess) { } @@ -246,7 +331,7 @@ private: void touch(clock_type::time_point const& now) { - last_access = now; + lastAccess = now; } }; @@ -262,7 +347,7 @@ private: sweepHelper( clock_type::time_point const& whenExpire, [[maybe_unused]] clock_type::time_point const& now, - typename KeyValueCacheType::map_type& partition, + KeyValueCacheType::map_type& partition, SweptPointersVector& stuffToSweep, std::atomic& allRemovals, std::scoped_lock const&); @@ -271,7 +356,7 @@ private: sweepHelper( clock_type::time_point const& whenExpire, clock_type::time_point const& now, - typename KeyOnlyCacheType::map_type& partition, + KeyOnlyCacheType::map_type& partition, SweptPointersVector&, std::atomic& allRemovals, std::scoped_lock const&); @@ -286,13 +371,13 @@ private: std::string name_; // Desired number of cache entries (0 = ignore) - int const target_size_; + int const targetSize_; // Desired maximum cache age - clock_type::duration const target_age_; + clock_type::duration const targetAge_; // Number of items cached - int cache_count_{0}; + int cacheCount_{0}; cache_type cache_; // Hold strong reference to recent objects std::uint64_t hits_{0}; std::uint64_t misses_{0}; diff --git a/include/xrpl/basics/TaggedCache.ipp b/include/xrpl/basics/TaggedCache.ipp index 8ef9e16570..b79561c71a 100644 --- a/include/xrpl/basics/TaggedCache.ipp +++ b/include/xrpl/basics/TaggedCache.ipp @@ -1,10 +1,35 @@ #pragma once #include +#include // IWYU pragma: keep #include namespace xrpl { +namespace detail { + +// Replace-policy tags selecting how TaggedCache::canonicalizeImpl resolves a +// collision when the key already exists: +// - ReplaceCached: always replace the cached value with `data`. `data` is +// never written back and may be const. +// - ReplaceClient: keep the cached value and write it back into `data` (the +// client's pointer), which must therefore be writable. +// - ReplaceDynamically: call the supplied callback to decide per call; `data` +// is written back when the cached value is kept, so it must be writable. +struct ReplaceCached +{ +}; + +struct ReplaceClient +{ +}; + +struct ReplaceDynamically +{ +}; + +} // namespace detail + template < class Key, class T, @@ -32,10 +57,13 @@ inline TaggedCache< beast::insight::Collector::ptr const& collector) : journal_(journal) , clock_(clock) - , stats_(name, std::bind(&TaggedCache::collectMetrics, this), collector) + , stats_( + name, + [this] { collectMetrics(); }, + collector) , name_(name) - , target_size_(size) - , target_age_(expiration) + , targetSize_(size) + , targetAge_(expiration) { } @@ -86,7 +114,7 @@ TaggedCache(cache_.size()) <= target_size_)) + if (targetSize_ == 0 || (static_cast(cache_.size()) <= targetSize_)) { - whenExpire = now - target_age_; + whenExpire = now - targetAge_; } else { - whenExpire = now - (target_age_ * target_size_ / cache_.size()); + whenExpire = now - (targetAge_ * targetSize_ / cache_.size()); clock_type::duration const minimumAge(std::chrono::seconds(1)); if (whenExpire > (now - minimumAge)) whenExpire = now - minimumAge; JLOG(journal_.trace()) - << name_ << " is growing fast " << cache_.size() << " of " << target_size_ - << " aging at " << (now - whenExpire).count() << " of " << target_age_.count(); + << name_ << " is growing fast " << cache_.size() << " of " << targetSize_ + << " aging at " << (now - whenExpire).count() << " of " << targetAge_.count(); } std::vector workers; @@ -242,7 +270,7 @@ TaggedCache -template +template inline bool TaggedCache:: - canonicalize(key_type const& key, SharedPointerType& data, R&& replaceCallback) + canonicalizeImpl( + key_type const& key, + CanonicalizeClientPointerType data, + [[maybe_unused]] Policy policy, + [[maybe_unused]] Callback&& replaceCallback) { // Return canonical value, store if needed, refresh in cache // Return values: true=we had the data already + + // `Policy` is one of: + // - detail::ReplaceCached: always replace the cached value with `data`; + // `data` is never written back and may be const. + // - detail::ReplaceClient: keep the cached value and write it back into + // `data` (the client's pointer), which must therefore be writable. + // - detail::ReplaceDynamically: call `replaceCallback` to decide at run + // time; `data` must be writable. + // For the latter two the write-back below requires a mutable `data`, so + // passing a const argument is a compile error. + constexpr bool replaceCached = std::is_same_v; + std::scoped_lock const lock(mutex_); auto cit = cache_.find(key); @@ -317,20 +361,21 @@ TaggedCachesecond; entry.touch(clock_.now()); - auto shouldReplace = [&] { - if constexpr (std::is_invocable_r_v) + auto shouldReplaceCached = [&] { + if constexpr (replaceCached) { - // The reason for this extra complexity is for intrusive - // strong/weak combo getting a strong is relatively expensive - // and not needed for many cases. - return replaceCallback(); + return true; + } + else if constexpr (std::is_same_v) + { + return false; } else { @@ -340,11 +385,11 @@ TaggedCache +template +inline bool +TaggedCache:: + canonicalize(key_type const& key, SharedPointerType& data, Callback&& replaceCallback) +{ + return canonicalizeImpl( + key, data, detail::ReplaceDynamically{}, std::forward(replaceCallback)); +} + template < class Key, class T, @@ -389,7 +452,7 @@ inline bool TaggedCache:: canonicalizeReplaceCache(key_type const& key, SharedPointerType const& data) { - return canonicalize(key, const_cast(data), []() { return true; }); + return canonicalizeImpl(key, data, detail::ReplaceCached{}); } template < @@ -405,7 +468,7 @@ inline bool TaggedCache:: canonicalizeReplaceClient(key_type const& key, SharedPointerType& data) { - return canonicalize(key, data, []() { return false; }); + return canonicalizeImpl(key, data, detail::ReplaceClient{}); } template < @@ -440,7 +503,8 @@ template < template inline auto TaggedCache:: - insert(key_type const& key, T const& value) -> std::enable_if_t + insert(key_type const& key, T const& value) -> ReturnType + requires(!IsKeyCache) { static_assert( std::is_same_v, SharedPointerType> || @@ -470,14 +534,15 @@ template < template inline auto TaggedCache:: - insert(key_type const& key) -> std::enable_if_t + insert(key_type const& key) -> ReturnType + requires IsKeyCache { std::scoped_lock const lock(mutex_); clock_type::time_point const now(clock_.now()); auto [it, inserted] = cache_.emplace( std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(now)); if (!inserted) - it->second.last_access = now; + it->second.lastAccess = now; return inserted; } @@ -626,7 +691,7 @@ TaggedCache& allRemovals, std::scoped_lock const&) @@ -706,7 +771,7 @@ TaggedCachesecond.last_access <= whenExpire) + else if (cit->second.lastAccess <= whenExpire) { // strong, expired ++cacheRemovals; @@ -756,7 +821,7 @@ TaggedCache& allRemovals, std::scoped_lock const&) @@ -773,12 +838,12 @@ TaggedCachesecond.last_access > now) + if (cit->second.lastAccess > now) { - cit->second.last_access = now; + cit->second.lastAccess = now; ++cit; } - else if (cit->second.last_access <= whenExpire) + else if (cit->second.lastAccess <= whenExpire) { cit = partition.erase(cit); } diff --git a/include/xrpl/basics/ToString.h b/include/xrpl/basics/ToString.h index 7764c1e3e3..e9f8f43633 100644 --- a/include/xrpl/basics/ToString.h +++ b/include/xrpl/basics/ToString.h @@ -12,8 +12,9 @@ namespace xrpl { */ template -std::enable_if_t, std::string> +std::string to_string(T t) // NOLINT(readability-identifier-naming) + requires(std::is_arithmetic_v) { return std::to_string(t); } diff --git a/include/xrpl/basics/UnorderedContainers.h b/include/xrpl/basics/UnorderedContainers.h index 5a417d5045..e0700c4055 100644 --- a/include/xrpl/basics/UnorderedContainers.h +++ b/include/xrpl/basics/UnorderedContainers.h @@ -2,12 +2,14 @@ #include #include -#include #include #include +#include +#include #include #include +#include /** * Use hash_* containers for keys that do not need a cryptographically secure diff --git a/include/xrpl/basics/base64.h b/include/xrpl/basics/base64.h index ed30e40a36..24fd660e65 100644 --- a/include/xrpl/basics/base64.h +++ b/include/xrpl/basics/base64.h @@ -34,8 +34,10 @@ #pragma once +#include #include #include +#include namespace xrpl { @@ -43,7 +45,7 @@ std::string base64Encode(std::uint8_t const* data, std::size_t len); inline std::string -base64Encode(std::string const& s) +base64Encode(std::string_view s) { return base64Encode(reinterpret_cast(s.data()), s.size()); } diff --git a/include/xrpl/basics/base_uint.h b/include/xrpl/basics/base_uint.h index 93a9ced15e..481a7dbd77 100644 --- a/include/xrpl/basics/base_uint.h +++ b/include/xrpl/basics/base_uint.h @@ -5,12 +5,12 @@ #pragma once -#include #include #include #include #include #include +#include #include #include @@ -19,7 +19,17 @@ #include #include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include #include namespace xrpl { @@ -87,7 +97,7 @@ public: // static constexpr std::size_t kBytes = Bits / 8; - static_assert(sizeof(data_) == kBytes, ""); + static_assert(sizeof(data_) == kBytes); using size_type = std::size_t; using difference_type = std::ptrdiff_t; @@ -177,7 +187,7 @@ private: BadChar, }; - constexpr Expected + constexpr std::expected parseFromStringView(std::string_view sv) noexcept { // Local lambda that converts a single hex char to four bits and @@ -216,7 +226,7 @@ private: } if (sv.size() != size() * 2) - return Unexpected(ParseResult::BadLength); + return std::unexpected(ParseResult::BadLength); std::size_t i = 0u; auto in = sv.begin(); @@ -227,7 +237,7 @@ private: { if (auto const result = hexCharToUInt(*in++, shift, accum); result != ParseResult::Okay) - return Unexpected(result); + return std::unexpected(result); } ret[i++] = accum; } @@ -270,12 +280,11 @@ public: { } - template < - class Container, - class = std::enable_if_t< - detail::IsContiguousContainer::value && - std::is_trivially_copyable_v>> + template explicit BaseUInt(Container const& c) + requires( + detail::IsContiguousContainer::value && + std::is_trivially_copyable_v) { // Use AlwaysFalseT so the static_assert condition is dependent // and only triggers when this constructor template is instantiated. @@ -285,13 +294,12 @@ public: "Use base_uint::fromRaw instead."); } - template < - class Container, - class = std::enable_if_t< - detail::IsContiguousContainer::value && - std::is_trivially_copyable_v>> + template static BaseUInt fromRaw(Container const& c) + requires( + detail::IsContiguousContainer::value && + std::is_trivially_copyable_v) { BaseUInt result; XRPL_ASSERT( @@ -302,11 +310,11 @@ public: } template - std::enable_if_t< - detail::IsContiguousContainer::value && - std::is_trivially_copyable_v, - BaseUInt&> + BaseUInt& operator=(Container const& c) + requires( + detail::IsContiguousContainer::value && + std::is_trivially_copyable_v) { XRPL_ASSERT( c.size() * sizeof(typename Container::value_type) == size(), diff --git a/include/xrpl/basics/chrono.h b/include/xrpl/basics/chrono.h index 5d6de06248..61246fc699 100644 --- a/include/xrpl/basics/chrono.h +++ b/include/xrpl/basics/chrono.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace xrpl { diff --git a/include/xrpl/basics/comparators.h b/include/xrpl/basics/comparators.h deleted file mode 100644 index 0e21d38d6b..0000000000 --- a/include/xrpl/basics/comparators.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include - -namespace xrpl { - -#ifdef _MSC_VER - -/* - * MSVC 2019 version 16.9.0 added [[nodiscard]] to the std comparison - * operator() functions. boost::bimap checks that the comparator is a - * BinaryFunction, in part by calling the function and ignoring the value. - * These two things don't play well together. These wrapper classes simply - * strip [[nodiscard]] from operator() for use in boost::bimap. - * - * See also: - * https://www.boost.org/doc/libs/1_75_0/libs/bimap/doc/html/boost_bimap/the_tutorial/controlling_collection_types.html - */ - -template -struct less -{ - using result_type = bool; - - constexpr bool - operator()(T const& left, T const& right) const - { - return std::less()(left, right); - } -}; - -template -struct equal_to -{ - using result_type = bool; - - constexpr bool - operator()(T const& left, T const& right) const - { - return std::equal_to()(left, right); - } -}; - -#else - -template -using less = std::less; - -template -using equal_to = std::equal_to; - -#endif - -} // namespace xrpl diff --git a/include/xrpl/basics/hardened_hash.h b/include/xrpl/basics/hardened_hash.h index efc77e058b..5a855736b3 100644 --- a/include/xrpl/basics/hardened_hash.h +++ b/include/xrpl/basics/hardened_hash.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -75,7 +74,7 @@ private: detail::seed_pair seeds_{detail::makeSeedPair<>()}; public: - using result_type = typename HashAlgorithm::result_type; + using result_type = HashAlgorithm::result_type; HardenedHash() = default; diff --git a/include/xrpl/basics/join.h b/include/xrpl/basics/join.h index 0fb00aaf82..492e4f3122 100644 --- a/include/xrpl/basics/join.h +++ b/include/xrpl/basics/join.h @@ -1,12 +1,15 @@ #pragma once +#include #include +#include +#include namespace xrpl { template Stream& -join(Stream& s, Iter iter, Iter end, std::string const& delimiter) +join(Stream& s, Iter iter, Iter end, std::string_view delimiter) { if (iter == end) return s; diff --git a/include/xrpl/basics/make_SSLContext.h b/include/xrpl/basics/make_SSLContext.h index 46f6a15e84..45ac637c36 100644 --- a/include/xrpl/basics/make_SSLContext.h +++ b/include/xrpl/basics/make_SSLContext.h @@ -2,6 +2,7 @@ #include +#include #include namespace xrpl { diff --git a/include/xrpl/basics/partitioned_unordered_map.h b/include/xrpl/basics/partitioned_unordered_map.h index 3bf64985e5..e78043e252 100644 --- a/include/xrpl/basics/partitioned_unordered_map.h +++ b/include/xrpl/basics/partitioned_unordered_map.h @@ -3,7 +3,10 @@ #include #include +#include #include +#include +#include #include #include #include @@ -57,8 +60,8 @@ public: { using iterator_category = std::forward_iterator_tag; partition_map_type* map{nullptr}; - typename partition_map_type::iterator ait{}; - typename map_type::iterator mit; + partition_map_type::iterator ait{}; + map_type::iterator mit; Iterator() = default; @@ -126,8 +129,8 @@ public: using iterator_category = std::forward_iterator_tag; partition_map_type* map{nullptr}; - typename partition_map_type::iterator ait{}; - typename map_type::iterator mit; + partition_map_type::iterator ait{}; + map_type::iterator mit; ConstIterator() = default; @@ -135,11 +138,8 @@ public: { } - ConstIterator(Iterator const& orig) + ConstIterator(Iterator const& orig) : map(orig.map), ait(orig.ait), mit(orig.mit) { - map = orig.map; - ait = orig.ait; - mit = orig.mit; } const_reference @@ -228,11 +228,11 @@ private: public: PartitionedUnorderedMap(std::optional partitions = std::nullopt) - { // Set partitions to the number of hardware threads if the parameter // is either empty or set to 0. - partitions_ = - partitions && (*partitions != 0u) ? *partitions : std::thread::hardware_concurrency(); + : partitions_( + partitions && (*partitions != 0u) ? *partitions : std::thread::hardware_concurrency()) + { map_.resize(partitions_); XRPL_ASSERT( partitions_, diff --git a/include/xrpl/basics/random.h b/include/xrpl/basics/random.h index 17f4a1c213..c544e7d0c8 100644 --- a/include/xrpl/basics/random.h +++ b/include/xrpl/basics/random.h @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -29,6 +28,7 @@ static_assert( namespace detail { // Determines if a type can be called like an Engine +// NOLINTNEXTLINE(readability-redundant-typename): typename required by MSVC template using is_engine = std::is_invocable_r; } // namespace detail @@ -91,8 +91,9 @@ defaultPrng() */ /** @{ */ template -std::enable_if_t && detail::is_engine::value, Integral> +Integral randInt(Engine& engine, Integral min, Integral max) + requires(std::is_integral_v && detail::is_engine::value) { XRPL_ASSERT(max > min, "xrpl::randInt : max over min inputs"); @@ -103,36 +104,41 @@ randInt(Engine& engine, Integral min, Integral max) } template -std::enable_if_t, Integral> +Integral randInt(Integral min, Integral max) + requires(std::is_integral_v) { return randInt(defaultPrng(), min, max); } template -std::enable_if_t && detail::is_engine::value, Integral> +Integral randInt(Engine& engine, Integral max) + requires(std::is_integral_v && detail::is_engine::value) { return randInt(engine, Integral(0), max); } template -std::enable_if_t, Integral> +Integral randInt(Integral max) + requires(std::is_integral_v) { return randInt(defaultPrng(), max); } template -std::enable_if_t && detail::is_engine::value, Integral> +Integral randInt(Engine& engine) + requires(std::is_integral_v && detail::is_engine::value) { return randInt(engine, std::numeric_limits::max()); } template -std::enable_if_t, Integral> +Integral randInt() + requires(std::is_integral_v) { return randInt(defaultPrng(), std::numeric_limits::max()); } @@ -141,19 +147,20 @@ randInt() /** Return a random byte */ /** @{ */ template -std::enable_if_t< - (std::is_same_v || std::is_same_v) && - detail::is_engine::value, - Byte> +Byte randByte(Engine& engine) + requires( + (std::is_same_v || std::is_same_v) && + detail::is_engine::value) { return static_cast(randInt( engine, std::numeric_limits::min(), std::numeric_limits::max())); } template -std::enable_if_t<(std::is_same_v || std::is_same_v), Byte> +Byte randByte() + requires(std::is_same_v || std::is_same_v) { return randByte(defaultPrng()); } diff --git a/include/xrpl/basics/rocksdb.h b/include/xrpl/basics/rocksdb.h deleted file mode 100644 index 3d468b0f1b..0000000000 --- a/include/xrpl/basics/rocksdb.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#if XRPL_ROCKSDB_AVAILABLE -// #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#endif diff --git a/include/xrpl/basics/safe_cast.h b/include/xrpl/basics/safe_cast.h index f71edc47ad..7f2b93a7eb 100644 --- a/include/xrpl/basics/safe_cast.h +++ b/include/xrpl/basics/safe_cast.h @@ -1,6 +1,6 @@ #pragma once -#include +#include // IWYU pragma: keep #include @@ -17,8 +17,9 @@ concept SafeToCast = (std::is_integral_v && std::is_integral_v) && : sizeof(Dest) >= sizeof(Src)); template -constexpr std::enable_if_t && std::is_integral_v, Dest> +constexpr Dest safeCast(Src s) noexcept + requires(std::is_integral_v && std::is_integral_v) { static_assert( std::is_signed_v || std::is_unsigned_v, "Cannot cast signed to unsigned"); @@ -30,15 +31,17 @@ safeCast(Src s) noexcept } template -constexpr std::enable_if_t && std::is_integral_v, Dest> +constexpr Dest safeCast(Src s) noexcept + requires(std::is_enum_v && std::is_integral_v) { return static_cast(safeCast>(s)); } template -constexpr std::enable_if_t && std::is_enum_v, Dest> +constexpr Dest safeCast(Src s) noexcept + requires(std::is_integral_v && std::is_enum_v) { return safeCast(static_cast>(s)); } @@ -48,8 +51,9 @@ safeCast(Src s) noexcept // underlying types become safe, it can be converted to a safe_cast. template -constexpr std::enable_if_t && std::is_integral_v, Dest> +constexpr Dest unsafeCast(Src s) noexcept + requires(std::is_integral_v && std::is_integral_v) { static_assert( !SafeToCast, @@ -59,15 +63,17 @@ unsafeCast(Src s) noexcept } template -constexpr std::enable_if_t && std::is_integral_v, Dest> +constexpr Dest unsafeCast(Src s) noexcept + requires(std::is_enum_v && std::is_integral_v) { return static_cast(unsafeCast>(s)); } template -constexpr std::enable_if_t && std::is_enum_v, Dest> +constexpr Dest unsafeCast(Src s) noexcept + requires(std::is_integral_v && std::is_enum_v) { return unsafeCast(static_cast>(s)); } diff --git a/include/xrpl/basics/sanitizers.h b/include/xrpl/basics/sanitizers.h index b954952848..7937344b53 100644 --- a/include/xrpl/basics/sanitizers.h +++ b/include/xrpl/basics/sanitizers.h @@ -4,7 +4,7 @@ /* ASAN flags some false positives with sudden jumps in control flow, like exceptions, or when encountering coroutine stack switches. This macro can be used to disable ASAN - intrumentation for specific functions. + instrumentation for specific functions. */ #if defined(__GNUC__) || defined(__clang__) #define XRPL_NO_SANITIZE_ADDRESS __attribute__((no_sanitize("address", "hwaddress"))) diff --git a/include/xrpl/basics/scope.h b/include/xrpl/basics/scope.h index cd2a5299b2..e63bb69eb5 100644 --- a/include/xrpl/basics/scope.h +++ b/include/xrpl/basics/scope.h @@ -24,20 +24,20 @@ namespace xrpl { template class ScopeExit { - EF exit_function_; - bool execute_on_destruction_{true}; + EF exitFunction_; + bool executeOnDestruction_{true}; public: ~ScopeExit() { - if (execute_on_destruction_) - exit_function_(); + if (executeOnDestruction_) + exitFunction_(); } ScopeExit(ScopeExit&& rhs) noexcept( std::is_nothrow_move_constructible_v || std::is_nothrow_copy_constructible_v) - : exit_function_{std::forward(rhs.exit_function_)} - , execute_on_destruction_{rhs.execute_on_destruction_} + : exitFunction_{std::forward(rhs.exitFunction_)} + , executeOnDestruction_{rhs.executeOnDestruction_} { rhs.release(); } @@ -46,12 +46,10 @@ public: operator=(ScopeExit&&) = delete; template - explicit ScopeExit( - EFP&& f, - std::enable_if_t< - !std::is_same_v, ScopeExit> && - std::is_constructible_v>* = 0) noexcept - : exit_function_{std::forward(f)} + explicit ScopeExit(EFP&& f) noexcept + requires( + !std::is_same_v, ScopeExit> && std::is_constructible_v) + : exitFunction_{std::forward(f)} { static_assert(std::is_nothrow_constructible_v(f))>); } @@ -59,7 +57,7 @@ public: void release() noexcept { - execute_on_destruction_ = false; + executeOnDestruction_ = false; } }; @@ -69,22 +67,22 @@ ScopeExit(EF) -> ScopeExit; template class ScopeFail { - EF exit_function_; - bool execute_on_destruction_{true}; - int uncaught_on_creation_{std::uncaught_exceptions()}; + EF exitFunction_; + bool executeOnDestruction_{true}; + int uncaughtOnCreation_{std::uncaught_exceptions()}; public: ~ScopeFail() { - if (execute_on_destruction_ && std::uncaught_exceptions() > uncaught_on_creation_) - exit_function_(); + if (executeOnDestruction_ && std::uncaught_exceptions() > uncaughtOnCreation_) + exitFunction_(); } ScopeFail(ScopeFail&& rhs) noexcept( std::is_nothrow_move_constructible_v || std::is_nothrow_copy_constructible_v) - : exit_function_{std::forward(rhs.exit_function_)} - , execute_on_destruction_{rhs.execute_on_destruction_} - , uncaught_on_creation_{rhs.uncaught_on_creation_} + : exitFunction_{std::forward(rhs.exitFunction_)} + , executeOnDestruction_{rhs.executeOnDestruction_} + , uncaughtOnCreation_{rhs.uncaughtOnCreation_} { rhs.release(); } @@ -93,12 +91,10 @@ public: operator=(ScopeFail&&) = delete; template - explicit ScopeFail( - EFP&& f, - std::enable_if_t< - !std::is_same_v, ScopeFail> && - std::is_constructible_v>* = 0) noexcept - : exit_function_{std::forward(f)} + explicit ScopeFail(EFP&& f) noexcept + requires( + !std::is_same_v, ScopeFail> && std::is_constructible_v) + : exitFunction_{std::forward(f)} { static_assert(std::is_nothrow_constructible_v(f))>); } @@ -106,7 +102,7 @@ public: void release() noexcept { - execute_on_destruction_ = false; + executeOnDestruction_ = false; } }; @@ -116,22 +112,22 @@ ScopeFail(EF) -> ScopeFail; template class ScopeSuccess { - EF exit_function_; - bool execute_on_destruction_{true}; - int uncaught_on_creation_{std::uncaught_exceptions()}; + EF exitFunction_; + bool executeOnDestruction_{true}; + int uncaughtOnCreation_{std::uncaught_exceptions()}; public: - ~ScopeSuccess() noexcept(noexcept(exit_function_())) + ~ScopeSuccess() noexcept(noexcept(exitFunction_())) { - if (execute_on_destruction_ && std::uncaught_exceptions() <= uncaught_on_creation_) - exit_function_(); + if (executeOnDestruction_ && std::uncaught_exceptions() <= uncaughtOnCreation_) + exitFunction_(); } ScopeSuccess(ScopeSuccess&& rhs) noexcept( std::is_nothrow_move_constructible_v || std::is_nothrow_copy_constructible_v) - : exit_function_{std::forward(rhs.exit_function_)} - , execute_on_destruction_{rhs.execute_on_destruction_} - , uncaught_on_creation_{rhs.uncaught_on_creation_} + : exitFunction_{std::forward(rhs.exitFunction_)} + , executeOnDestruction_{rhs.executeOnDestruction_} + , uncaughtOnCreation_{rhs.uncaughtOnCreation_} { rhs.release(); } @@ -140,20 +136,19 @@ public: operator=(ScopeSuccess&&) = delete; template - explicit ScopeSuccess( - EFP&& f, - std::enable_if_t< + explicit ScopeSuccess(EFP&& f) noexcept( + std::is_nothrow_constructible_v || std::is_nothrow_constructible_v) + requires( !std::is_same_v, ScopeSuccess> && - std::is_constructible_v>* = - 0) noexcept(std::is_nothrow_constructible_v || std::is_nothrow_constructible_v) - : exit_function_{std::forward(f)} + std::is_constructible_v) + : exitFunction_{std::forward(f)} { } void release() noexcept { - execute_on_destruction_ = false; + executeOnDestruction_ = false; } }; diff --git a/include/xrpl/basics/strHex.h b/include/xrpl/basics/strHex.h index 9cae234f06..1366515bd3 100644 --- a/include/xrpl/basics/strHex.h +++ b/include/xrpl/basics/strHex.h @@ -3,6 +3,9 @@ #include #include +#include +#include + namespace xrpl { template diff --git a/include/xrpl/basics/tagged_integer.h b/include/xrpl/basics/tagged_integer.h index ddcde479f3..5a088db863 100644 --- a/include/xrpl/basics/tagged_integer.h +++ b/include/xrpl/basics/tagged_integer.h @@ -7,6 +7,7 @@ #include #include +#include #include namespace xrpl { @@ -42,10 +43,10 @@ public: TaggedInteger() = default; - template < - class OtherInt, - class = std::enable_if_t && sizeof(OtherInt) <= sizeof(Int)>> - explicit constexpr TaggedInteger(OtherInt value) noexcept : value_(value) + template + explicit constexpr TaggedInteger(OtherInt value) noexcept + requires(std::is_integral_v && sizeof(OtherInt) <= sizeof(Int)) + : value_(value) { static_assert(sizeof(TaggedInteger) == sizeof(Int), "tagged_integer is adding padding"); } diff --git a/include/xrpl/beast/asio/io_latency_probe.h b/include/xrpl/beast/asio/io_latency_probe.h index ce3929a394..f67ff4a692 100644 --- a/include/xrpl/beast/asio/io_latency_probe.h +++ b/include/xrpl/beast/asio/io_latency_probe.h @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -18,8 +19,8 @@ template class IOLatencyProbe { private: - using duration = typename Clock::duration; - using time_point = typename Clock::time_point; + using duration = Clock::duration; + using time_point = Clock::time_point; std::recursive_mutex mutex_; std::condition_variable_any cond_; diff --git a/include/xrpl/beast/clock/abstract_clock.h b/include/xrpl/beast/clock/abstract_clock.h index 33d4096d0e..15d785d138 100644 --- a/include/xrpl/beast/clock/abstract_clock.h +++ b/include/xrpl/beast/clock/abstract_clock.h @@ -34,10 +34,10 @@ template class AbstractClock { public: - using rep = typename Clock::rep; - using period = typename Clock::period; - using duration = typename Clock::duration; - using time_point = typename Clock::time_point; + using rep = Clock::rep; + using period = Clock::period; + using duration = Clock::duration; + using time_point = Clock::time_point; using clock_type = Clock; static bool const is_steady = Clock::is_steady; // NOLINT(readability-identifier-naming) diff --git a/include/xrpl/beast/clock/basic_seconds_clock.h b/include/xrpl/beast/clock/basic_seconds_clock.h index 8460dbe26b..5a267e9458 100644 --- a/include/xrpl/beast/clock/basic_seconds_clock.h +++ b/include/xrpl/beast/clock/basic_seconds_clock.h @@ -20,10 +20,10 @@ public: explicit BasicSecondsClock() = default; - using rep = typename Clock::rep; - using period = typename Clock::period; - using duration = typename Clock::duration; - using time_point = typename Clock::time_point; + using rep = Clock::rep; + using period = Clock::period; + using duration = Clock::duration; + using time_point = Clock::time_point; static bool const is_steady = // NOLINT(readability-identifier-naming) Clock::is_steady; diff --git a/include/xrpl/beast/container/aged_container_utility.h b/include/xrpl/beast/container/aged_container_utility.h index 879672e9cf..f43e59b0f7 100644 --- a/include/xrpl/beast/container/aged_container_utility.h +++ b/include/xrpl/beast/container/aged_container_utility.h @@ -3,14 +3,15 @@ #include #include -#include +#include namespace beast { /** Expire aged container items past the specified age. */ template -std::enable_if_t::value, std::size_t> +std::size_t expire(AgedContainer& c, std::chrono::duration const& age) + requires(IsAgedContainer::value) { std::size_t n(0); auto const expired(c.clock().now() - age); diff --git a/include/xrpl/beast/container/aged_map.h b/include/xrpl/beast/container/aged_map.h index c1f6943451..20daab70a4 100644 --- a/include/xrpl/beast/container/aged_map.h +++ b/include/xrpl/beast/container/aged_map.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace beast { diff --git a/include/xrpl/beast/container/aged_multimap.h b/include/xrpl/beast/container/aged_multimap.h index 65efd1bbf9..f6133ced1c 100644 --- a/include/xrpl/beast/container/aged_multimap.h +++ b/include/xrpl/beast/container/aged_multimap.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace beast { diff --git a/include/xrpl/beast/container/aged_unordered_map.h b/include/xrpl/beast/container/aged_unordered_map.h index a2189e2409..d6ea6e97bd 100644 --- a/include/xrpl/beast/container/aged_unordered_map.h +++ b/include/xrpl/beast/container/aged_unordered_map.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace beast { diff --git a/include/xrpl/beast/container/aged_unordered_multimap.h b/include/xrpl/beast/container/aged_unordered_multimap.h index f1348ed39f..3b72be98b7 100644 --- a/include/xrpl/beast/container/aged_unordered_multimap.h +++ b/include/xrpl/beast/container/aged_unordered_multimap.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace beast { diff --git a/include/xrpl/beast/container/detail/aged_container_iterator.h b/include/xrpl/beast/container/detail/aged_container_iterator.h index 8d44646cd4..d6c061bb86 100644 --- a/include/xrpl/beast/container/detail/aged_container_iterator.h +++ b/include/xrpl/beast/container/detail/aged_container_iterator.h @@ -16,34 +16,33 @@ template class AgedContainerIterator { public: - using iterator_category = typename std::iterator_traits::iterator_category; + using iterator_category = std::iterator_traits::iterator_category; using value_type = std::conditional_t< IsConst, typename Iterator::value_type::Stashed::value_type const, typename Iterator::value_type::Stashed::value_type>; - using difference_type = typename std::iterator_traits::difference_type; + using difference_type = std::iterator_traits::difference_type; using pointer = value_type*; using reference = value_type&; - using time_point = typename Iterator::value_type::Stashed::time_point; + using time_point = Iterator::value_type::Stashed::time_point; AgedContainerIterator() = default; // Disable constructing a const_iterator from a non-const_iterator. // Converting between reverse and non-reverse iterators should be explicit. - template < - bool OtherIsConst, - class OtherIterator, - class = std::enable_if_t< - (!OtherIsConst || IsConst) && - !static_cast(std::is_same_v)>> + template explicit AgedContainerIterator(AgedContainerIterator const& other) + requires( + (!OtherIsConst || IsConst) && + !static_cast(std::is_same_v)) : iter_(other.iter_) { } // Disable constructing a const_iterator from a non-const_iterator. - template > + template AgedContainerIterator(AgedContainerIterator const& other) + requires(!OtherIsConst || IsConst) : iter_(other.iter_) { } @@ -52,7 +51,8 @@ public: template auto operator=(AgedContainerIterator const& other) - -> std::enable_if_t + -> AgedContainerIterator& + requires(!OtherIsConst || IsConst) { iter_ = other.iter_; return *this; diff --git a/include/xrpl/beast/container/detail/aged_ordered_container.h b/include/xrpl/beast/container/detail/aged_ordered_container.h index ad368f2754..c98f16022f 100644 --- a/include/xrpl/beast/container/detail/aged_ordered_container.h +++ b/include/xrpl/beast/container/detail/aged_ordered_container.h @@ -11,9 +11,14 @@ #include #include +#include +#include +#include #include #include #include +#include +#include #include #include @@ -62,8 +67,8 @@ class AgedOrderedContainer { public: using clock_type = AbstractClock; - using time_point = typename clock_type::time_point; - using duration = typename clock_type::duration; + using time_point = clock_type::time_point; + using duration = clock_type::duration; using key_type = Key; using mapped_type = T; using value_type = std::conditional_t, Key>; @@ -94,8 +99,8 @@ private: { explicit Stashed() = default; - using value_type = typename AgedOrderedContainer::value_type; - using time_point = typename AgedOrderedContainer::time_point; + using value_type = AgedOrderedContainer::value_type; + using time_point = AgedOrderedContainer::time_point; }; Element(time_point const& when, value_type const& value) : value(value), when(when) @@ -106,10 +111,9 @@ private: { } - template < - class... Args, - class = std::enable_if_t>> + template Element(time_point const& when, Args&&... args) + requires(std::is_constructible_v) : value(std::forward(args)...), when(when) { } @@ -192,8 +196,8 @@ private: } }; - using list_type = typename boost::intrusive:: - make_list>::type; + using list_type = + boost::intrusive::make_list>::type; using cont_type = std::conditional_t< IsMulti, @@ -206,8 +210,7 @@ private: boost::intrusive::constant_time_size, boost::intrusive::compare>::type>; - using ElementAllocator = - typename std::allocator_traits::template rebind_alloc; + using ElementAllocator = std::allocator_traits::template rebind_alloc; using ElementAllocatorTraits = std::allocator_traits; @@ -356,6 +359,7 @@ private: deleteElement(Element const* p) { ElementAllocatorTraits::destroy(config_.alloc(), p); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) ElementAllocatorTraits::deallocate(config_.alloc(), const_cast(p), 1); } @@ -373,8 +377,8 @@ public: using allocator_type = Allocator; using reference = value_type&; using const_reference = value_type const&; - using pointer = typename std::allocator_traits::pointer; - using const_pointer = typename std::allocator_traits::const_pointer; + using pointer = std::allocator_traits::pointer; + using const_pointer = std::allocator_traits::const_pointer; // A set iterator (IsMap==false) is always const // because the elements of a set are immutable. @@ -604,35 +608,25 @@ public: // //-------------------------------------------------------------------------- - template < - class K, - bool MaybeMulti = IsMulti, - bool MaybeMap = IsMap, - class = std::enable_if_t> + template std::conditional_t& - at(K const& k); + at(K const& k) + requires(MaybeMap && !MaybeMulti); - template < - class K, - bool MaybeMulti = IsMulti, - bool MaybeMap = IsMap, - class = std::enable_if_t> - typename std::conditional::type const& - at(K const& k) const; + template + std::conditional::type const& + at(K const& k) const + requires(MaybeMap && !MaybeMulti); - template < - bool MaybeMulti = IsMulti, - bool MaybeMap = IsMap, - class = std::enable_if_t> + template std::conditional_t& - operator[](Key const& key); + operator[](Key const& key) + requires(MaybeMap && !MaybeMulti); - template < - bool MaybeMulti = IsMulti, - bool MaybeMap = IsMap, - class = std::enable_if_t> + template std::conditional_t& - operator[](Key&& key); + operator[](Key&& key) + requires(MaybeMap && !MaybeMulti); //-------------------------------------------------------------------------- // @@ -766,35 +760,40 @@ public: // map, set template auto - insert(value_type const& value) -> std::enable_if_t>; + insert(value_type const& value) -> std::pair + requires(!MaybeMulti); // multimap, multiset template auto - insert(value_type const& value) -> std::enable_if_t; + insert(value_type const& value) -> iterator + requires MaybeMulti; // set template auto - insert(value_type&& value) - -> std::enable_if_t>; + insert(value_type&& value) -> std::pair + requires(!MaybeMulti && !MaybeMap); // multiset template auto - insert(value_type&& value) -> std::enable_if_t; + insert(value_type&& value) -> iterator + requires(MaybeMulti && !MaybeMap); //--- // map, set template auto - insert(const_iterator hint, value_type const& value) -> std::enable_if_t; + insert(const_iterator hint, value_type const& value) -> iterator + requires(!MaybeMulti); // multimap, multiset template - std::enable_if_t + iterator insert(const_iterator /*hint*/, value_type const& value) + requires MaybeMulti { // VFALCO TODO Figure out how to utilize 'hint' return insert(value); @@ -803,12 +802,14 @@ public: // map, set template auto - insert(const_iterator hint, value_type&& value) -> std::enable_if_t; + insert(const_iterator hint, value_type&& value) -> iterator + requires(!MaybeMulti); // multimap, multiset template - std::enable_if_t + iterator insert(const_iterator /*hint*/, value_type&& value) + requires MaybeMulti { // VFALCO TODO Figure out how to utilize 'hint' return insert(std::move(value)); @@ -816,20 +817,18 @@ public: // map, multimap template - std::enable_if_t< - MaybeMap && std::is_constructible_v, - std::conditional_t>> + std::conditional_t> insert(P&& value) + requires(MaybeMap && std::is_constructible_v) { return emplace(std::forward

(value)); } // map, multimap template - std::enable_if_t< - MaybeMap && std::is_constructible_v, - std::conditional_t>> + std::conditional_t> insert(const_iterator hint, P&& value) + requires(MaybeMap && std::is_constructible_v) { return emplaceHint(hint, std::forward

(value)); } @@ -851,46 +850,45 @@ public: // map, set template auto - emplace(Args&&... args) -> std::enable_if_t>; + emplace(Args&&... args) -> std::pair + requires(!MaybeMulti); // multiset, multimap template auto - emplace(Args&&... args) -> std::enable_if_t; + emplace(Args&&... args) -> iterator + requires MaybeMulti; // map, set template auto - emplaceHint(const_iterator hint, Args&&... args) - -> std::enable_if_t>; + emplaceHint(const_iterator hint, Args&&... args) -> std::pair + requires(!MaybeMulti); // multiset, multimap template - std::enable_if_t + iterator emplaceHint(const_iterator /*hint*/, Args&&... args) + requires MaybeMulti { // VFALCO TODO Figure out how to utilize 'hint' return emplace(std::forward(args)...); } - // enable_if prevents erase (reverse_iterator pos) from compiling - template < - bool IsConst, - class Iterator, - class = std::enable_if_t::value>> + // The constraint prevents erase (reverse_iterator pos) from compiling + template beast::detail::AgedContainerIterator - erase(beast::detail::AgedContainerIterator pos); + erase(beast::detail::AgedContainerIterator pos) + requires(!IsBoostReverseIterator::value); - // enable_if prevents erase (reverse_iterator first, reverse_iterator last) + // The constraint prevents erase (reverse_iterator first, reverse_iterator last) // from compiling - template < - bool IsConst, - class Iterator, - class = std::enable_if_t::value>> + template beast::detail::AgedContainerIterator erase( beast::detail::AgedContainerIterator first, - beast::detail::AgedContainerIterator last); + beast::detail::AgedContainerIterator last) + requires(!IsBoostReverseIterator::value); template auto @@ -901,13 +899,11 @@ public: //-------------------------------------------------------------------------- - // enable_if prevents touch (reverse_iterator pos) from compiling - template < - bool IsConst, - class Iterator, - class = std::enable_if_t::value>> + // The constraint prevents touch (reverse_iterator pos) from compiling + template void touch(beast::detail::AgedContainerIterator pos) + requires(!IsBoostReverseIterator::value) { touch(pos, clock().now()); } @@ -1138,25 +1134,25 @@ public: } private: - // enable_if prevents erase (reverse_iterator pos, now) from compiling - template < - bool IsConst, - class Iterator, - class = std::enable_if_t::value>> + // The constraint prevents erase (reverse_iterator pos, now) from compiling + template void touch( beast::detail::AgedContainerIterator pos, - typename clock_type::time_point const& now); + clock_type::time_point const& now) + requires(!IsBoostReverseIterator::value); template < bool MaybePropagate = std::allocator_traits::propagate_on_container_swap::value> - std::enable_if_t - swapData(AgedOrderedContainer& other) noexcept; + void + swapData(AgedOrderedContainer& other) noexcept + requires MaybePropagate; template < bool MaybePropagate = std::allocator_traits::propagate_on_container_swap::value> - std::enable_if_t - swapData(AgedOrderedContainer& other) noexcept; + void + swapData(AgedOrderedContainer& other) noexcept + requires(!MaybePropagate); private: ConfigT config_; @@ -1248,12 +1244,7 @@ AgedOrderedContainer::AgedOrd template AgedOrderedContainer::AgedOrderedContainer( AgedOrderedContainer const& other) - : config_(other.config_) -#if BOOST_VERSION >= 108000 - , cont_(other.cont_.get_comp()) -#else - , cont_(other.cont_.comp()) -#endif + : config_(other.config_), cont_(other.cont_.get_comp()) { insert(other.cbegin(), other.cend()); } @@ -1262,12 +1253,7 @@ template ::AgedOrderedContainer( AgedOrderedContainer const& other, Allocator const& alloc) - : config_(other.config_, alloc) -#if BOOST_VERSION >= 108000 - , cont_(other.cont_.get_comp()) -#else - , cont_(other.cont_.comp()) -#endif + : config_(other.config_, alloc), cont_(other.cont_.get_comp()) { insert(other.cbegin(), other.cend()); } @@ -1284,13 +1270,7 @@ template ::AgedOrderedContainer( AgedOrderedContainer&& other, // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) Allocator const& alloc) - : config_(std::move(other.config_), alloc) -#if BOOST_VERSION >= 108000 - , cont_(std::move(other.cont_.get_comp())) -#else - , cont_(std::move(other.cont_.comp())) -#endif - + : config_(std::move(other.config_), alloc), cont_(std::move(other.cont_.get_comp())) { insert(other.cbegin(), other.cend()); other.clear(); @@ -1381,9 +1361,10 @@ AgedOrderedContainer::operato //------------------------------------------------------------------------------ template -template +template std::conditional_t& AgedOrderedContainer::at(K const& k) + requires(MaybeMap && !MaybeMulti) { auto const iter(cont_.find(k, std::cref(config_.keyCompare()))); if (iter == cont_.end()) @@ -1392,9 +1373,10 @@ AgedOrderedContainer::at(K co } template -template -typename std::conditional::type const& +template +std::conditional::type const& AgedOrderedContainer::at(K const& k) const + requires(MaybeMap && !MaybeMulti) { auto const iter(cont_.find(k, std::cref(config_.keyCompare()))); if (iter == cont_.end()) @@ -1403,9 +1385,10 @@ AgedOrderedContainer::at(K co } template -template +template std::conditional_t& AgedOrderedContainer::operator[](Key const& key) + requires(MaybeMap && !MaybeMulti) { typename cont_type::insert_commit_data d; auto const result(cont_.insert_check(key, std::cref(config_.keyCompare()), d)); @@ -1421,9 +1404,10 @@ AgedOrderedContainer::operato } template -template +template std::conditional_t& AgedOrderedContainer::operator[](Key&& key) + requires(MaybeMap && !MaybeMulti) { typename cont_type::insert_commit_data d; auto const result(cont_.insert_check(key, std::cref(config_.keyCompare()), d)); @@ -1457,7 +1441,8 @@ template auto AgedOrderedContainer::insert( - value_type const& value) -> std::enable_if_t> + value_type const& value) -> std::pair + requires(!MaybeMulti) { typename cont_type::insert_commit_data d; auto const result(cont_.insert_check(extract(value), std::cref(config_.keyCompare()), d)); @@ -1476,7 +1461,8 @@ template auto AgedOrderedContainer::insert( - value_type const& value) -> std::enable_if_t + value_type const& value) -> iterator + requires MaybeMulti { auto const before(cont_.upper_bound(extract(value), std::cref(config_.keyCompare()))); Element* const p(newElement(value)); @@ -1490,7 +1476,8 @@ template auto AgedOrderedContainer::insert(value_type&& value) - -> std::enable_if_t> + -> std::pair + requires(!MaybeMulti && !MaybeMap) { typename cont_type::insert_commit_data d; auto const result(cont_.insert_check(extract(value), std::cref(config_.keyCompare()), d)); @@ -1509,7 +1496,8 @@ template auto AgedOrderedContainer::insert(value_type&& value) - -> std::enable_if_t + -> iterator + requires(MaybeMulti && !MaybeMap) { auto const before(cont_.upper_bound(extract(value), std::cref(config_.keyCompare()))); Element* const p(newElement(std::move(value))); @@ -1526,7 +1514,8 @@ template auto AgedOrderedContainer::insert( const_iterator hint, - value_type const& value) -> std::enable_if_t + value_type const& value) -> iterator + requires(!MaybeMulti) { typename cont_type::insert_commit_data d; auto const result( @@ -1547,7 +1536,8 @@ template auto AgedOrderedContainer::insert( const_iterator hint, - value_type&& value) -> std::enable_if_t + value_type&& value) -> iterator + requires(!MaybeMulti) { typename cont_type::insert_commit_data d; auto const result( @@ -1567,7 +1557,8 @@ template auto AgedOrderedContainer::emplace(Args&&... args) - -> std::enable_if_t> + -> std::pair + requires(!MaybeMulti) { // VFALCO NOTE Its unfortunate that we need to // construct element here @@ -1589,7 +1580,8 @@ template auto AgedOrderedContainer::emplace(Args&&... args) - -> std::enable_if_t + -> iterator + requires MaybeMulti { Element* const p(newElement(std::forward(args)...)); auto const before(cont_.upper_bound(extract(p->value), std::cref(config_.keyCompare()))); @@ -1604,7 +1596,8 @@ template auto AgedOrderedContainer::emplaceHint( const_iterator hint, - Args&&... args) -> std::enable_if_t> + Args&&... args) -> std::pair + requires(!MaybeMulti) { // VFALCO NOTE Its unfortunate that we need to // construct element here @@ -1623,21 +1616,23 @@ AgedOrderedContainer::emplace } template -template +template beast::detail::AgedContainerIterator AgedOrderedContainer::erase( beast::detail::AgedContainerIterator pos) + requires(!IsBoostReverseIterator::value) { unlinkAndDeleteElement(&*((pos++).iterator())); return beast::detail::AgedContainerIterator(pos.iterator()); } template -template +template beast::detail::AgedContainerIterator AgedOrderedContainer::erase( beast::detail::AgedContainerIterator first, beast::detail::AgedContainerIterator last) + requires(!IsBoostReverseIterator::value) { for (; first != last;) unlinkAndDeleteElement(&*((first++).iterator())); @@ -1732,7 +1727,7 @@ AgedOrderedContainer::operato cend(), other.cbegin(), other.cend(), - [&eq, &other](value_type const& lhs, typename Other::value_type const& rhs) { + [&eq, &other](value_type const& lhs, Other::value_type const& rhs) { return eq(extract(lhs), other.extract(rhs)); }); } @@ -1740,11 +1735,12 @@ AgedOrderedContainer::operato //------------------------------------------------------------------------------ template -template +template void AgedOrderedContainer::touch( beast::detail::AgedContainerIterator pos, - typename clock_type::time_point const& now) + clock_type::time_point const& now) + requires(!IsBoostReverseIterator::value) { auto& e(*pos.iterator()); e.when = now; @@ -1754,9 +1750,10 @@ AgedOrderedContainer::touch( template template -std::enable_if_t +void AgedOrderedContainer::swapData( AgedOrderedContainer& other) noexcept + requires MaybePropagate { std::swap(config_.keyCompare(), other.config_.keyCompare()); std::swap(config_.alloc(), other.config_.alloc()); @@ -1765,9 +1762,10 @@ AgedOrderedContainer::swapDat template template -std::enable_if_t +void AgedOrderedContainer::swapData( AgedOrderedContainer& other) noexcept + requires(!MaybePropagate) { std::swap(config_.keyCompare(), other.config_.keyCompare()); std::swap(config_.clock, other.config_.clock); diff --git a/include/xrpl/beast/container/detail/aged_unordered_container.h b/include/xrpl/beast/container/detail/aged_unordered_container.h index 7162c237d6..9be9e96ba2 100644 --- a/include/xrpl/beast/container/detail/aged_unordered_container.h +++ b/include/xrpl/beast/container/detail/aged_unordered_container.h @@ -10,13 +10,19 @@ #include #include +#include #include +#include +#include #include #include #include #include +#include +#include #include #include +#include /* @@ -67,8 +73,8 @@ class AgedUnorderedContainer { public: using clock_type = AbstractClock; - using time_point = typename clock_type::time_point; - using duration = typename clock_type::duration; + using time_point = clock_type::time_point; + using duration = clock_type::duration; using key_type = Key; using mapped_type = T; using value_type = std::conditional_t, Key>; @@ -99,8 +105,8 @@ private: { explicit Stashed() = default; - using value_type = typename AgedUnorderedContainer::value_type; - using time_point = typename AgedUnorderedContainer::time_point; + using value_type = AgedUnorderedContainer::value_type; + using time_point = AgedUnorderedContainer::time_point; }; Element(time_point const& when, value_type const& value) : value(value), when(when) @@ -111,10 +117,9 @@ private: { } - template < - class... Args, - class = std::enable_if_t>> + template Element(time_point const& when, Args&&... args) + requires(std::is_constructible_v) : value(std::forward(args)...), when(when) { } @@ -201,8 +206,8 @@ private: } }; - using list_type = typename boost::intrusive:: - make_list>::type; + using list_type = + boost::intrusive::make_list>::type; using cont_type = std::conditional_t< IsMulti, @@ -219,16 +224,14 @@ private: boost::intrusive::equal, boost::intrusive::cache_begin>::type>; - using bucket_type = typename cont_type::bucket_type; - using bucket_traits = typename cont_type::bucket_traits; + using bucket_type = cont_type::bucket_type; + using bucket_traits = cont_type::bucket_traits; - using ElementAllocator = - typename std::allocator_traits::template rebind_alloc; + using ElementAllocator = std::allocator_traits::template rebind_alloc; using ElementAllocatorTraits = std::allocator_traits; - using BucketAllocator = - typename std::allocator_traits::template rebind_alloc; + using BucketAllocator = std::allocator_traits::template rebind_alloc; using BucketAllocatorTraits = std::allocator_traits; @@ -525,6 +528,7 @@ private: deleteElement(Element const* p) { ElementAllocatorTraits::destroy(config_.alloc(), p); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) ElementAllocatorTraits::deallocate(config_.alloc(), const_cast(p), 1); } @@ -542,8 +546,8 @@ public: using allocator_type = Allocator; using reference = value_type&; using const_reference = value_type const&; - using pointer = typename std::allocator_traits::pointer; - using const_pointer = typename std::allocator_traits::const_pointer; + using pointer = std::allocator_traits::pointer; + using const_pointer = std::allocator_traits::const_pointer; // A set iterator (IsMap==false) is always const // because the elements of a set are immutable. @@ -837,35 +841,25 @@ public: // //-------------------------------------------------------------------------- - template < - class K, - bool MaybeMulti = IsMulti, - bool MaybeMap = IsMap, - class = std::enable_if_t> + template std::conditional_t& - at(K const& k); + at(K const& k) + requires(MaybeMap && !MaybeMulti); - template < - class K, - bool MaybeMulti = IsMulti, - bool MaybeMap = IsMap, - class = std::enable_if_t> - typename std::conditional::type const& - at(K const& k) const; + template + std::conditional::type const& + at(K const& k) const + requires(MaybeMap && !MaybeMulti); - template < - bool MaybeMulti = IsMulti, - bool MaybeMap = IsMap, - class = std::enable_if_t> + template std::conditional_t& - operator[](Key const& key); + operator[](Key const& key) + requires(MaybeMap && !MaybeMulti); - template < - bool MaybeMulti = IsMulti, - bool MaybeMap = IsMap, - class = std::enable_if_t> + template std::conditional_t& - operator[](Key&& key); + operator[](Key&& key) + requires(MaybeMap && !MaybeMulti); //-------------------------------------------------------------------------- // @@ -963,28 +957,32 @@ public: // map, set template auto - insert(value_type const& value) -> std::enable_if_t>; + insert(value_type const& value) -> std::pair + requires(!MaybeMulti); // multimap, multiset template auto - insert(value_type const& value) -> std::enable_if_t; + insert(value_type const& value) -> iterator + requires MaybeMulti; // map, set template auto - insert(value_type&& value) - -> std::enable_if_t>; + insert(value_type&& value) -> std::pair + requires(!MaybeMulti && !MaybeMap); // multimap, multiset template auto - insert(value_type&& value) -> std::enable_if_t; + insert(value_type&& value) -> iterator + requires(MaybeMulti && !MaybeMap); // map, set template - std::enable_if_t + iterator insert(const_iterator /*hint*/, value_type const& value) + requires(!MaybeMulti) { // Hint is ignored but we provide the interface so // callers may use ordered and unordered interchangeably. @@ -993,8 +991,9 @@ public: // multimap, multiset template - std::enable_if_t + iterator insert(const_iterator /*hint*/, value_type const& value) + requires MaybeMulti { // VFALCO TODO The hint could be used to let // the client order equal ranges @@ -1003,8 +1002,9 @@ public: // map, set template - std::enable_if_t + iterator insert(const_iterator /*hint*/, value_type&& value) + requires(!MaybeMulti) { // Hint is ignored but we provide the interface so // callers may use ordered and unordered interchangeably. @@ -1013,8 +1013,9 @@ public: // multimap, multiset template - std::enable_if_t + iterator insert(const_iterator /*hint*/, value_type&& value) + requires MaybeMulti { // VFALCO TODO The hint could be used to let // the client order equal ranges @@ -1023,20 +1024,18 @@ public: // map, multimap template - std::enable_if_t< - MaybeMap && std::is_constructible_v, - std::conditional_t>> + std::conditional_t> insert(P&& value) + requires(MaybeMap && std::is_constructible_v) { return emplace(std::forward

(value)); } // map, multimap template - std::enable_if_t< - MaybeMap && std::is_constructible_v, - std::conditional_t>> + std::conditional_t> insert(const_iterator hint, P&& value) + requires(MaybeMap && std::is_constructible_v) { return emplaceHint(hint, std::forward

(value)); } @@ -1057,23 +1056,26 @@ public: // set, map template auto - emplace(Args&&... args) -> std::enable_if_t>; + emplace(Args&&... args) -> std::pair + requires(!MaybeMulti); // multiset, multimap template auto - emplace(Args&&... args) -> std::enable_if_t; + emplace(Args&&... args) -> iterator + requires MaybeMulti; // set, map template auto - emplaceHint(const_iterator /*hint*/, Args&&... args) - -> std::enable_if_t>; + emplaceHint(const_iterator /*hint*/, Args&&... args) -> std::pair + requires(!MaybeMulti); // multiset, multimap template - std::enable_if_t + iterator emplaceHint(const_iterator /*hint*/, Args&&... args) + requires MaybeMulti { // VFALCO TODO The hint could be used for multi, to let // the client order equal ranges @@ -1304,7 +1306,7 @@ public: class OtherHash, class OtherAllocator, bool MaybeMulti = IsMulti> - std::enable_if_t + bool operator==(AgedUnorderedContainer< false, OtherIsMap, @@ -1313,7 +1315,8 @@ public: OtherDuration, OtherHash, KeyEqual, - OtherAllocator> const& other) const; + OtherAllocator> const& other) const + requires(!MaybeMulti); template < bool OtherIsMap, @@ -1323,7 +1326,7 @@ public: class OtherHash, class OtherAllocator, bool MaybeMulti = IsMulti> - std::enable_if_t + bool operator==(AgedUnorderedContainer< true, OtherIsMap, @@ -1332,7 +1335,8 @@ public: OtherDuration, OtherHash, KeyEqual, - OtherAllocator> const& other) const; + OtherAllocator> const& other) const + requires MaybeMulti; template < bool OtherIsMulti, @@ -1377,13 +1381,14 @@ private: // map, set template auto - insertUnchecked(value_type const& value) - -> std::enable_if_t>; + insertUnchecked(value_type const& value) -> std::pair + requires(!MaybeMulti); // multimap, multiset template auto - insertUnchecked(value_type const& value) -> std::enable_if_t; + insertUnchecked(value_type const& value) -> iterator + requires MaybeMulti; template void @@ -1414,7 +1419,7 @@ private: void touch( beast::detail::AgedContainerIterator pos, - typename clock_type::time_point const& now) + clock_type::time_point const& now) { auto& e(*pos.iterator()); e.when = now; @@ -1424,8 +1429,9 @@ private: template < bool MaybePropagate = std::allocator_traits::propagate_on_container_swap::value> - std::enable_if_t + void swapData(AgedUnorderedContainer& other) noexcept + requires MaybePropagate { std::swap(config_.hashFunction(), other.config_.hashFunction()); std::swap(config_.keyEq(), other.config_.keyEq()); @@ -1435,8 +1441,9 @@ private: template < bool MaybePropagate = std::allocator_traits::propagate_on_container_swap::value> - std::enable_if_t + void swapData(AgedUnorderedContainer& other) noexcept + requires(!MaybePropagate) { std::swap(config_.hashFunction(), other.config_.hashFunction()); std::swap(config_.keyEq(), other.config_.keyEq()); @@ -2090,9 +2097,10 @@ template < class Hash, class KeyEqual, class Allocator> -template +template std::conditional_t& AgedUnorderedContainer::at(K const& k) + requires(MaybeMap && !MaybeMulti) { auto const iter( cont_.find(k, std::cref(config_.hashFunction()), std::cref(config_.keyValueEqual()))); @@ -2110,10 +2118,11 @@ template < class Hash, class KeyEqual, class Allocator> -template -typename std::conditional::type const& +template +std::conditional::type const& AgedUnorderedContainer::at( K const& k) const + requires(MaybeMap && !MaybeMulti) { auto const iter( cont_.find(k, std::cref(config_.hashFunction()), std::cref(config_.keyValueEqual()))); @@ -2131,10 +2140,11 @@ template < class Hash, class KeyEqual, class Allocator> -template +template std::conditional_t& AgedUnorderedContainer::operator[]( Key const& key) + requires(MaybeMap && !MaybeMulti) { maybeRehash(1); typename cont_type::insert_commit_data d; @@ -2160,10 +2170,11 @@ template < class Hash, class KeyEqual, class Allocator> -template +template std::conditional_t& AgedUnorderedContainer::operator[]( Key&& key) + requires(MaybeMap && !MaybeMulti) { maybeRehash(1); typename cont_type::insert_commit_data d; @@ -2216,7 +2227,8 @@ template < template auto AgedUnorderedContainer::insert( - value_type const& value) -> std::enable_if_t> + value_type const& value) -> std::pair + requires(!MaybeMulti) { maybeRehash(1); typename cont_type::insert_commit_data d; @@ -2245,7 +2257,8 @@ template < template auto AgedUnorderedContainer::insert( - value_type const& value) -> std::enable_if_t + value_type const& value) -> iterator + requires MaybeMulti { maybeRehash(1); Element* const p(newElement(value)); @@ -2267,7 +2280,8 @@ template < template auto AgedUnorderedContainer::insert( - value_type&& value) -> std::enable_if_t> + value_type&& value) -> std::pair + requires(!MaybeMulti && !MaybeMap) { maybeRehash(1); typename cont_type::insert_commit_data d; @@ -2296,7 +2310,8 @@ template < template auto AgedUnorderedContainer::insert( - value_type&& value) -> std::enable_if_t + value_type&& value) -> iterator + requires(MaybeMulti && !MaybeMap) { maybeRehash(1); Element* const p(newElement(std::move(value))); @@ -2305,7 +2320,6 @@ AgedUnorderedContainer return iterator(iter); } -#if 1 // Use insert() instead of insert_check() insert_commit() // set, map template < bool IsMulti, @@ -2319,7 +2333,8 @@ template < template auto AgedUnorderedContainer::emplace( - Args&&... args) -> std::enable_if_t> + Args&&... args) -> std::pair + requires(!MaybeMulti) { maybeRehash(1); // VFALCO NOTE Its unfortunate that we need to @@ -2334,42 +2349,6 @@ AgedUnorderedContainer deleteElement(p); return std::make_pair(iterator(result.first), false); } -#else // As original, use insert_check() / insert_commit () pair. -// set, map -template < - bool IsMulti, - bool IsMap, - class Key, - class T, - class Clock, - class Hash, - class KeyEqual, - class Allocator> -template -auto -AgedUnorderedContainer::emplace( - Args&&... args) -> typename std::enable_if>::type -{ - maybe_rehash(1); - // VFALCO NOTE Its unfortunate that we need to - // construct element here - element* const p(new_element(std::forward(args)...)); - typename cont_type::insert_commit_data d; - auto const result(m_cont.insert_check( - extract(p->value), - std::cref(m_config.hashFunction()), - std::cref(m_config.keyValueEqual()), - d)); - if (result.second) - { - auto const iter(m_cont.insert_commit(*p, d)); - chronological.list.push_back(*p); - return std::make_pair(iterator(iter), true); - } - delete_element(p); - return std::make_pair(iterator(result.first), false); -} -#endif // 0 // multiset, multimap template < @@ -2384,7 +2363,8 @@ template < template auto AgedUnorderedContainer::emplace( - Args&&... args) -> std::enable_if_t + Args&&... args) -> iterator + requires MaybeMulti { maybeRehash(1); Element* const p(newElement(std::forward(args)...)); @@ -2407,7 +2387,8 @@ template auto AgedUnorderedContainer::emplaceHint( const_iterator /*hint*/, - Args&&... args) -> std::enable_if_t> + Args&&... args) -> std::pair + requires(!MaybeMulti) { maybeRehash(1); // VFALCO NOTE Its unfortunate that we need to @@ -2558,7 +2539,7 @@ template < class OtherHash, class OtherAllocator, bool MaybeMulti> -std::enable_if_t +bool AgedUnorderedContainer::operator==( AgedUnorderedContainer< false, @@ -2569,6 +2550,7 @@ AgedUnorderedContainer OtherHash, KeyEqual, OtherAllocator> const& other) const + requires(!MaybeMulti) { if (size() != other.size()) return false; @@ -2598,7 +2580,7 @@ template < class OtherHash, class OtherAllocator, bool MaybeMulti> -std::enable_if_t +bool AgedUnorderedContainer::operator==( AgedUnorderedContainer< true, @@ -2609,6 +2591,7 @@ AgedUnorderedContainer OtherHash, KeyEqual, OtherAllocator> const& other) const + requires MaybeMulti { if (size() != other.size()) return false; @@ -2645,7 +2628,8 @@ template < template auto AgedUnorderedContainer::insertUnchecked( - value_type const& value) -> std::enable_if_t> + value_type const& value) -> std::pair + requires(!MaybeMulti) { typename cont_type::insert_commit_data d; auto const result(cont_.insert_check( @@ -2673,7 +2657,8 @@ template < template auto AgedUnorderedContainer::insertUnchecked( - value_type const& value) -> std::enable_if_t + value_type const& value) -> iterator + requires MaybeMulti { Element* const p(newElement(value)); chronological.list_.push_back(*p); diff --git a/include/xrpl/beast/core/CurrentThreadName.h b/include/xrpl/beast/core/CurrentThreadName.h index 6175d99b16..3cdfe4c678 100644 --- a/include/xrpl/beast/core/CurrentThreadName.h +++ b/include/xrpl/beast/core/CurrentThreadName.h @@ -6,6 +6,7 @@ #include +#include #include #include diff --git a/include/xrpl/beast/core/LexicalCast.h b/include/xrpl/beast/core/LexicalCast.h index 18e63c9c10..1162d83078 100644 --- a/include/xrpl/beast/core/LexicalCast.h +++ b/include/xrpl/beast/core/LexicalCast.h @@ -5,11 +5,12 @@ #include #include -#include +#include #include -#include #include #include +#include +#include #include #include @@ -28,16 +29,18 @@ struct LexicalCast explicit LexicalCast() = default; template - std::enable_if_t, bool> + bool operator()(std::string& out, Arithmetic in) + requires(std::is_arithmetic_v) { out = std::to_string(in); return true; } template - std::enable_if_t, bool> + bool operator()(std::string& out, Enumeration in) + requires(std::is_enum_v) { out = std::to_string(static_cast>(in)); return true; @@ -55,8 +58,9 @@ struct LexicalCast "beast::LexicalCast can only be used with integral types"); template - std::enable_if_t && !std::is_same_v, bool> + bool operator()(Integral& out, std::string_view in) const + requires(std::is_integral_v && !std::is_same_v) { auto first = in.data(); auto last = in.data() + in.size(); diff --git a/include/xrpl/beast/core/List.h b/include/xrpl/beast/core/List.h index 946126a004..1eeeaa87d1 100644 --- a/include/xrpl/beast/core/List.h +++ b/include/xrpl/beast/core/List.h @@ -1,6 +1,8 @@ #pragma once +#include #include +#include namespace beast { @@ -24,7 +26,7 @@ struct CopyConst { explicit CopyConst() = default; - using type = typename std::remove_const::type const; + using type = std::remove_const::type const; }; /** @} */ @@ -56,7 +58,7 @@ class ListIterator { public: using iterator_category = std::bidirectional_iterator_tag; - using value_type = typename beast::detail::CopyConst::type; + using value_type = beast::detail::CopyConst::type; using difference_type = std::ptrdiff_t; using pointer = value_type*; using reference = value_type&; @@ -259,7 +261,7 @@ template class List { public: - using Node = typename detail::ListNode; + using Node = detail::ListNode; using value_type = T; using pointer = value_type*; diff --git a/include/xrpl/beast/core/LockFreeStack.h b/include/xrpl/beast/core/LockFreeStack.h index 6b8f686246..dd135d2d98 100644 --- a/include/xrpl/beast/core/LockFreeStack.h +++ b/include/xrpl/beast/core/LockFreeStack.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -12,13 +13,13 @@ template class LockFreeStackIterator { protected: - using Node = typename Container::Node; + using Node = Container::Node; using NodePtr = std::conditional_t; public: using iterator_category = std::forward_iterator_tag; - using value_type = typename Container::value_type; - using difference_type = typename Container::difference_type; + using value_type = Container::value_type; + using difference_type = Container::difference_type; using pointer = std::conditional_t; using reference = std:: @@ -40,7 +41,7 @@ public: operator=(NodePtr node) { node_ = node; - return static_cast(*this); + return *this; } LockFreeStackIterator& @@ -58,7 +59,7 @@ public: return result; } - NodePtr + [[nodiscard]] NodePtr node() const { return node_; diff --git a/include/xrpl/beast/hash/hash_append.h b/include/xrpl/beast/hash/hash_append.h index 83cff4bdea..3592ffbfe8 100644 --- a/include/xrpl/beast/hash/hash_append.h +++ b/include/xrpl/beast/hash/hash_append.h @@ -5,8 +5,8 @@ #include #include +#include #include -#include #include #include #include @@ -26,7 +26,7 @@ template inline void reverseBytes(T& t) { - unsigned char* bytes = + auto* bytes = static_cast(std::memmove(std::addressof(t), std::addressof(t), sizeof(T))); for (unsigned i = 0; i < sizeof(T) / 2; ++i) std::swap(bytes[i], bytes[sizeof(T) - 1 - i]); @@ -200,26 +200,29 @@ struct IsContiguouslyHashable // scalars template -inline std::enable_if_t::value> +inline void hash_append(Hasher& h, T const& t) noexcept + requires(IsContiguouslyHashable::value) { // NOLINTNEXTLINE(bugprone-sizeof-expression) h(static_cast(std::addressof(t)), sizeof(t)); } template -inline std::enable_if_t< - !IsContiguouslyHashable::value && - (std::is_integral_v || std::is_pointer_v || std::is_enum_v)> +inline void hash_append(Hasher& h, T t) noexcept + requires( + !IsContiguouslyHashable::value && + (std::is_integral_v || std::is_pointer_v || std::is_enum_v)) { detail::reverseBytes(t); h(std::addressof(t), sizeof(t)); } template -inline std::enable_if_t> +inline void hash_append(Hasher& h, T t) noexcept + requires(std::is_floating_point_v) { if (t == 0) t = 0; @@ -239,36 +242,44 @@ hash_append(Hasher& h, std::nullptr_t) noexcept // Forward declarations for ADL purposes template -std::enable_if_t::value> -hash_append(Hasher& h, T (&a)[N]) noexcept; +void +hash_append(Hasher& h, T (&a)[N]) noexcept + requires(!IsContiguouslyHashable::value); template -std::enable_if_t::value> -hash_append(Hasher& h, std::basic_string const& s) noexcept; +void +hash_append(Hasher& h, std::basic_string const& s) noexcept + requires(!IsContiguouslyHashable::value); template -std::enable_if_t::value> -hash_append(Hasher& h, std::basic_string const& s) noexcept; +void +hash_append(Hasher& h, std::basic_string const& s) noexcept + requires(IsContiguouslyHashable::value); template -std::enable_if_t, Hasher>::value> -hash_append(Hasher& h, std::pair const& p) noexcept; +void +hash_append(Hasher& h, std::pair const& p) noexcept + requires(!IsContiguouslyHashable, Hasher>::value); template -std::enable_if_t::value> -hash_append(Hasher& h, std::vector const& v) noexcept; +void +hash_append(Hasher& h, std::vector const& v) noexcept + requires(!IsContiguouslyHashable::value); template -std::enable_if_t::value> -hash_append(Hasher& h, std::vector const& v) noexcept; +void +hash_append(Hasher& h, std::vector const& v) noexcept + requires(IsContiguouslyHashable::value); template -std::enable_if_t, Hasher>::value> -hash_append(Hasher& h, std::array const& a) noexcept; +void +hash_append(Hasher& h, std::array const& a) noexcept + requires(!IsContiguouslyHashable, Hasher>::value); template -std::enable_if_t, Hasher>::value> -hash_append(Hasher& h, std::tuple const& t) noexcept; +void +hash_append(Hasher& h, std::tuple const& t) noexcept + requires(!IsContiguouslyHashable, Hasher>::value); template void @@ -279,11 +290,13 @@ void hash_append(Hasher& h, std::unordered_set const& s); template -std::enable_if_t::value> -hash_append(Hasher& h, boost::container::flat_set const& v) noexcept; +void +hash_append(Hasher& h, boost::container::flat_set const& v) noexcept + requires(!IsContiguouslyHashable::value); template -std::enable_if_t::value> -hash_append(Hasher& h, boost::container::flat_set const& v) noexcept; +void +hash_append(Hasher& h, boost::container::flat_set const& v) noexcept + requires(IsContiguouslyHashable::value); template void hash_append(Hasher& h, T0 const& t0, T1 const& t1, T const&... t) noexcept; @@ -291,8 +304,9 @@ hash_append(Hasher& h, T0 const& t0, T1 const& t1, T const&... t) noexcept; // c-array template -std::enable_if_t::value> +void hash_append(Hasher& h, T (&a)[N]) noexcept + requires(!IsContiguouslyHashable::value) { for (auto const& t : a) hash_append(h, t); @@ -301,8 +315,9 @@ hash_append(Hasher& h, T (&a)[N]) noexcept // basic_string template -inline std::enable_if_t::value> +inline void hash_append(Hasher& h, std::basic_string const& s) noexcept + requires(!IsContiguouslyHashable::value) { for (auto c : s) hash_append(h, c); @@ -310,8 +325,9 @@ hash_append(Hasher& h, std::basic_string const& s) noexcep } template -inline std::enable_if_t::value> +inline void hash_append(Hasher& h, std::basic_string const& s) noexcept + requires(IsContiguouslyHashable::value) { h(s.data(), s.size() * sizeof(CharT)); hash_append(h, s.size()); @@ -320,8 +336,9 @@ hash_append(Hasher& h, std::basic_string const& s) noexcep // pair template -inline std::enable_if_t, Hasher>::value> +inline void hash_append(Hasher& h, std::pair const& p) noexcept + requires(!IsContiguouslyHashable, Hasher>::value) { hash_append(h, p.first, p.second); } @@ -329,8 +346,9 @@ hash_append(Hasher& h, std::pair const& p) noexcept // vector template -inline std::enable_if_t::value> +inline void hash_append(Hasher& h, std::vector const& v) noexcept + requires(!IsContiguouslyHashable::value) { for (auto const& t : v) hash_append(h, t); @@ -338,8 +356,9 @@ hash_append(Hasher& h, std::vector const& v) noexcept } template -inline std::enable_if_t::value> +inline void hash_append(Hasher& h, std::vector const& v) noexcept + requires(IsContiguouslyHashable::value) { h(v.data(), v.size() * sizeof(T)); hash_append(h, v.size()); @@ -348,57 +367,37 @@ hash_append(Hasher& h, std::vector const& v) noexcept // array template -std::enable_if_t, Hasher>::value> +void hash_append(Hasher& h, std::array const& a) noexcept + requires(!IsContiguouslyHashable, Hasher>::value) { for (auto const& t : a) hash_append(h, t); } template -std::enable_if_t::value> +void hash_append(Hasher& h, boost::container::flat_set const& v) noexcept + requires(!IsContiguouslyHashable::value) { for (auto const& t : v) hash_append(h, t); } template -std::enable_if_t::value> +void hash_append(Hasher& h, boost::container::flat_set const& v) noexcept + requires(IsContiguouslyHashable::value) { h(&(v.begin()), v.size() * sizeof(Key)); } // tuple -namespace detail { - -inline void -forEachItem(...) noexcept -{ -} - -template -inline int -hashOne(Hasher& h, T const& t) noexcept -{ - hash_append(h, t); - return 0; -} - -template -inline void -tuple_hash(Hasher& h, std::tuple const& t, std::index_sequence) noexcept -{ - for_each_item(hash_one(h, std::get(t))...); -} - -} // namespace detail - template -inline std::enable_if_t, Hasher>::value> +inline void hash_append(Hasher& h, std::tuple const& t) noexcept + requires(!IsContiguouslyHashable, Hasher>::value) { - detail::tuple_hash(h, t, std::index_sequence_for{}); + std::apply([&h](auto const&... item) { (hash_append(h, item), ...); }, t); } // shared_ptr diff --git a/include/xrpl/beast/hash/uhash.h b/include/xrpl/beast/hash/uhash.h index 97461f67c7..9ffd5924f7 100644 --- a/include/xrpl/beast/hash/uhash.h +++ b/include/xrpl/beast/hash/uhash.h @@ -11,7 +11,7 @@ struct Uhash { Uhash() = default; - using result_type = typename Hasher::result_type; + using result_type = Hasher::result_type; template result_type diff --git a/include/xrpl/beast/hash/xxhasher.h b/include/xrpl/beast/hash/xxhasher.h index 95a67dede0..73dbb8e8ab 100644 --- a/include/xrpl/beast/hash/xxhasher.h +++ b/include/xrpl/beast/hash/xxhasher.h @@ -7,8 +7,11 @@ #include #include #include +#include +#include #include #include +#include namespace beast { @@ -121,14 +124,18 @@ public: } } - template >* = nullptr> - explicit Xxhasher(Seed seed) : seed_(seed) + template + explicit Xxhasher(Seed seed) + requires(std::is_unsigned_v) + : seed_(seed) { resetBuffers(); } - template >* = nullptr> - Xxhasher(Seed seed, Seed) : seed_(seed) + template + Xxhasher(Seed seed, Seed) + requires(std::is_unsigned_v) + : seed_(seed) { resetBuffers(); } diff --git a/include/xrpl/beast/insight/Collector.h b/include/xrpl/beast/insight/Collector.h index 2e73d60400..3f83e329d4 100644 --- a/include/xrpl/beast/insight/Collector.h +++ b/include/xrpl/beast/insight/Collector.h @@ -4,8 +4,10 @@ #include #include #include +#include #include +#include #include namespace beast::insight { diff --git a/include/xrpl/beast/insight/Counter.h b/include/xrpl/beast/insight/Counter.h index 71ace4bb4e..482808b2c7 100644 --- a/include/xrpl/beast/insight/Counter.h +++ b/include/xrpl/beast/insight/Counter.h @@ -3,6 +3,7 @@ #include #include +#include namespace beast::insight { @@ -29,7 +30,7 @@ public: factory function in the Collector interface. @see Collector. */ - explicit Counter(std::shared_ptr const& impl) : impl_(impl) + explicit Counter(std::shared_ptr impl) : impl_(std::move(impl)) { } diff --git a/include/xrpl/beast/insight/Event.h b/include/xrpl/beast/insight/Event.h index 5e424a0f9b..afccf9baba 100644 --- a/include/xrpl/beast/insight/Event.h +++ b/include/xrpl/beast/insight/Event.h @@ -4,6 +4,7 @@ #include #include +#include namespace beast::insight { @@ -31,7 +32,7 @@ public: factory function in the Collector interface. @see Collector. */ - explicit Event(std::shared_ptr const& impl) : impl_(impl) + explicit Event(std::shared_ptr impl) : impl_(std::move(impl)) { } diff --git a/include/xrpl/beast/insight/Gauge.h b/include/xrpl/beast/insight/Gauge.h index dd2c4bc6b6..9a23ea6665 100644 --- a/include/xrpl/beast/insight/Gauge.h +++ b/include/xrpl/beast/insight/Gauge.h @@ -3,6 +3,7 @@ #include #include +#include namespace beast::insight { @@ -31,7 +32,7 @@ public: factory function in the Collector interface. @see Collector. */ - explicit Gauge(std::shared_ptr const& impl) : impl_(impl) + explicit Gauge(std::shared_ptr impl) : impl_(std::move(impl)) { } @@ -48,6 +49,11 @@ public: impl_->set(value); } + // This is a write-through handle: assignment sets the value of the + // referenced metric. It is const-qualified and returns Gauge const& + // (a non-const Gauge& would require a const_cast), so it does not follow + // the conventional assignment-operator signature. + // NOLINTNEXTLINE(misc-unconventional-assign-operator) Gauge const& operator=(value_type value) const { diff --git a/include/xrpl/beast/insight/Hook.h b/include/xrpl/beast/insight/Hook.h index 1cb6cae5d9..8dbe5a4be0 100644 --- a/include/xrpl/beast/insight/Hook.h +++ b/include/xrpl/beast/insight/Hook.h @@ -3,6 +3,7 @@ #include #include +#include namespace beast::insight { @@ -20,7 +21,7 @@ public: factory function in the Collector interface. @see Collector. */ - explicit Hook(std::shared_ptr const& impl) : impl_(impl) + explicit Hook(std::shared_ptr impl) : impl_(std::move(impl)) { } diff --git a/include/xrpl/beast/insight/Insight.h b/include/xrpl/beast/insight/Insight.h deleted file mode 100644 index bf3743cfd8..0000000000 --- a/include/xrpl/beast/insight/Insight.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/include/xrpl/beast/insight/Meter.h b/include/xrpl/beast/insight/Meter.h index 03aa17c313..25ffabd928 100644 --- a/include/xrpl/beast/insight/Meter.h +++ b/include/xrpl/beast/insight/Meter.h @@ -3,6 +3,7 @@ #include #include +#include namespace beast::insight { @@ -28,7 +29,7 @@ public: factory function in the Collector interface. @see Collector. */ - explicit Meter(std::shared_ptr const& impl) : impl_(impl) + explicit Meter(std::shared_ptr impl) : impl_(std::move(impl)) { } diff --git a/include/xrpl/beast/insight/NullCollector.h b/include/xrpl/beast/insight/NullCollector.h index b865526ade..67903420fa 100644 --- a/include/xrpl/beast/insight/NullCollector.h +++ b/include/xrpl/beast/insight/NullCollector.h @@ -2,6 +2,8 @@ #include +#include + namespace beast::insight { /** A Collector which does not collect metrics. */ diff --git a/include/xrpl/beast/insight/StatsDCollector.h b/include/xrpl/beast/insight/StatsDCollector.h index ad436dc626..9a438c48f1 100644 --- a/include/xrpl/beast/insight/StatsDCollector.h +++ b/include/xrpl/beast/insight/StatsDCollector.h @@ -4,6 +4,9 @@ #include #include +#include +#include + namespace beast::insight { /** A Collector that reports metrics to a StatsD server. diff --git a/include/xrpl/beast/net/IPAddress.h b/include/xrpl/beast/net/IPAddress.h index 67deaaa787..f4327b7b8a 100644 --- a/include/xrpl/beast/net/IPAddress.h +++ b/include/xrpl/beast/net/IPAddress.h @@ -9,6 +9,7 @@ #include #include +#include #include //------------------------------------------------------------------------------ diff --git a/include/xrpl/beast/net/IPAddressV4.h b/include/xrpl/beast/net/IPAddressV4.h index dbe5a6095f..9367fbe1eb 100644 --- a/include/xrpl/beast/net/IPAddressV4.h +++ b/include/xrpl/beast/net/IPAddressV4.h @@ -1,7 +1,5 @@ #pragma once -#include - #include namespace beast::IP { diff --git a/include/xrpl/beast/net/IPAddressV6.h b/include/xrpl/beast/net/IPAddressV6.h index 10f806417d..1bfa079990 100644 --- a/include/xrpl/beast/net/IPAddressV6.h +++ b/include/xrpl/beast/net/IPAddressV6.h @@ -1,7 +1,5 @@ #pragma once -#include - #include namespace beast::IP { diff --git a/include/xrpl/beast/net/IPEndpoint.h b/include/xrpl/beast/net/IPEndpoint.h index fec6e1556f..0b661108f2 100644 --- a/include/xrpl/beast/net/IPEndpoint.h +++ b/include/xrpl/beast/net/IPEndpoint.h @@ -3,8 +3,13 @@ #include #include #include +#include +#include +#include #include +#include +#include #include #include diff --git a/include/xrpl/beast/rfc2616.h b/include/xrpl/beast/rfc2616.h index cf80bd26c0..7c681ab140 100644 --- a/include/xrpl/beast/rfc2616.h +++ b/include/xrpl/beast/rfc2616.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -102,7 +103,7 @@ Result split(FwdIt first, FwdIt last, Char delim) { using namespace detail; - using string = typename Result::value_type; + using string = Result::value_type; Result result; diff --git a/include/xrpl/beast/test/yield_to.h b/include/xrpl/beast/test/yield_to.h index 84d7d8846d..1a34ec436e 100644 --- a/include/xrpl/beast/test/yield_to.h +++ b/include/xrpl/beast/test/yield_to.h @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include #include diff --git a/include/xrpl/beast/type_name.h b/include/xrpl/beast/type_name.h index ae7b681af9..85fd9ae6a2 100644 --- a/include/xrpl/beast/type_name.h +++ b/include/xrpl/beast/type_name.h @@ -23,6 +23,7 @@ typeName() if (auto s = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, nullptr)) { name = s; + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) std::free(s); } #endif diff --git a/include/xrpl/beast/unit_test.h b/include/xrpl/beast/unit_test.h index 51ac96cacb..b4d53b2b1c 100644 --- a/include/xrpl/beast/unit_test.h +++ b/include/xrpl/beast/unit_test.h @@ -1,15 +1,6 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include #include -#include -#include #ifndef BEAST_EXPECT #define BEAST_EXPECT_S1(x) #x diff --git a/include/xrpl/beast/unit_test/detail/const_container.h b/include/xrpl/beast/unit_test/detail/const_container.h index c171771e45..6826bf4258 100644 --- a/include/xrpl/beast/unit_test/detail/const_container.h +++ b/include/xrpl/beast/unit_test/detail/const_container.h @@ -32,11 +32,11 @@ protected: } public: - using value_type = typename cont_type::value_type; - using size_type = typename cont_type::size_type; - using difference_type = typename cont_type::difference_type; - using iterator = typename cont_type::const_iterator; - using const_iterator = typename cont_type::const_iterator; + using value_type = cont_type::value_type; + using size_type = cont_type::size_type; + using difference_type = cont_type::difference_type; + using iterator = cont_type::const_iterator; + using const_iterator = cont_type::const_iterator; /** Returns `true` if the container is empty. */ [[nodiscard]] bool diff --git a/include/xrpl/beast/unit_test/match.h b/include/xrpl/beast/unit_test/match.h index 222c4ea656..5faeaa1100 100644 --- a/include/xrpl/beast/unit_test/match.h +++ b/include/xrpl/beast/unit_test/match.h @@ -7,6 +7,7 @@ #include #include +#include namespace beast::unit_test { @@ -41,7 +42,7 @@ private: public: template - explicit Selector(ModeT mode, std::string const& pattern = ""); + explicit Selector(ModeT mode, std::string pattern = ""); template bool @@ -51,9 +52,9 @@ public: //------------------------------------------------------------------------------ template -Selector::Selector(ModeT mode, std::string const& pattern) : mode_(mode), pat_(pattern) +Selector::Selector(ModeT mode, std::string pattern) : mode_(mode), pat_(std::move(pattern)) { - if (mode_ == ModeT::Automatch && pattern.empty()) + if (mode_ == ModeT::Automatch && pat_.empty()) mode_ = ModeT::All; } diff --git a/include/xrpl/beast/unit_test/recorder.h b/include/xrpl/beast/unit_test/recorder.h index 2ed88d4a46..1b7347dc2e 100644 --- a/include/xrpl/beast/unit_test/recorder.h +++ b/include/xrpl/beast/unit_test/recorder.h @@ -6,6 +6,10 @@ #include #include +#include + +#include +#include namespace beast::unit_test { diff --git a/include/xrpl/beast/unit_test/reporter.h b/include/xrpl/beast/unit_test/reporter.h index b4b6c69fa4..a903d9f8c2 100644 --- a/include/xrpl/beast/unit_test/reporter.h +++ b/include/xrpl/beast/unit_test/reporter.h @@ -5,18 +5,21 @@ #pragma once #include -#include +#include +#include #include #include #include #include +#include #include #include #include #include #include +#include namespace beast::unit_test { @@ -48,7 +51,7 @@ private: std::size_t cases = 0; std::size_t total = 0; std::size_t failed = 0; - typename clock_type::time_point start = clock_type::now(); + clock_type::time_point start = clock_type::now(); explicit SuiteResults(std::string name = "") : name(std::move(name)) { @@ -60,7 +63,7 @@ private: struct Results { - using run_time = std::pair; + using run_time = std::pair; static constexpr auto kMaxTop = 10; @@ -69,7 +72,7 @@ private: std::size_t total = 0; std::size_t failed = 0; std::vector top; - typename clock_type::time_point start = clock_type::now(); + clock_type::time_point start = clock_type::now(); void add(SuiteResults const& r); @@ -77,8 +80,8 @@ private: std::ostream& os_; Results results_; - SuiteResults suite_results_; - CaseResults case_results_; + SuiteResults suiteResults_; + CaseResults caseResults_; public: Reporter(Reporter const&) = delete; @@ -91,7 +94,7 @@ public: private: static std::string - fmtdur(typename clock_type::duration const& d); + fmtdur(clock_type::duration const& d); void onSuiteBegin(SuiteInfo const& info) override; @@ -141,9 +144,7 @@ Reporter::Results::add(SuiteResults const& r) top.begin(), top.end(), elapsed, - [](run_time const& t1, typename clock_type::duration const& t2) { - return t1.second > t2; - }); + [](run_time const& t1, clock_type::duration const& t2) { return t1.second > t2; }); if (iter != top.end()) { if (top.size() == kMaxTop) @@ -181,7 +182,7 @@ Reporter::~Reporter() template std::string -Reporter::fmtdur(typename clock_type::duration const& d) +Reporter::fmtdur(clock_type::duration const& d) { using namespace std::chrono; auto const ms = duration_cast(d); @@ -196,22 +197,22 @@ template void Reporter::onSuiteBegin(SuiteInfo const& info) { - suite_results_ = SuiteResults{info.fullName()}; + suiteResults_ = SuiteResults{info.fullName()}; } template void Reporter::onSuiteEnd() { - results_.add(suite_results_); + results_.add(suiteResults_); } template void Reporter::onCaseBegin(std::string const& name) { - case_results_ = CaseResults(name); - os_ << suite_results_.name << (case_results_.name.empty() ? "" : (" " + case_results_.name)) + caseResults_ = CaseResults(name); + os_ << suiteResults_.name << (caseResults_.name.empty() ? "" : (" " + caseResults_.name)) << std::endl; } @@ -219,23 +220,23 @@ template void Reporter::onCaseEnd() { - suite_results_.add(case_results_); + suiteResults_.add(caseResults_); } template void Reporter::onPass() { - ++case_results_.total; + ++caseResults_.total; } template void Reporter::onFail(std::string const& reason) { - ++case_results_.failed; - ++case_results_.total; - os_ << "#" << case_results_.total << " failed" << (reason.empty() ? "" : ": ") << reason + ++caseResults_.failed; + ++caseResults_.total; + os_ << "#" << caseResults_.total << " failed" << (reason.empty() ? "" : ": ") << reason << std::endl; } diff --git a/include/xrpl/beast/unit_test/results.h b/include/xrpl/beast/unit_test/results.h index 02aa9730d1..718d764c9f 100644 --- a/include/xrpl/beast/unit_test/results.h +++ b/include/xrpl/beast/unit_test/results.h @@ -6,6 +6,7 @@ #include +#include #include #include #include diff --git a/include/xrpl/beast/unit_test/suite.h b/include/xrpl/beast/unit_test/suite.h index fded866da0..487663fcc5 100644 --- a/include/xrpl/beast/unit_test/suite.h +++ b/include/xrpl/beast/unit_test/suite.h @@ -10,6 +10,8 @@ #include #include +#include +#include #include #include #include @@ -634,7 +636,7 @@ Suite::run(Runner& r) #define BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(Class, Module, Library, Priority) #else -#include +#include // IWYU pragma: keep #define BEAST_DEFINE_TESTSUITE(Class, Module, Library) \ BEAST_DEFINE_TESTSUITE_INSERT(Class, Module, Library, false, 0) #define BEAST_DEFINE_TESTSUITE_MANUAL(Class, Module, Library) \ diff --git a/include/xrpl/beast/unit_test/suite_info.h b/include/xrpl/beast/unit_test/suite_info.h index c09a0c2257..bda10ae7e3 100644 --- a/include/xrpl/beast/unit_test/suite_info.h +++ b/include/xrpl/beast/unit_test/suite_info.h @@ -4,9 +4,9 @@ #pragma once -#include #include #include +#include #include namespace beast::unit_test { diff --git a/include/xrpl/beast/unit_test/suite_list.h b/include/xrpl/beast/unit_test/suite_list.h index 748f994602..7dd0dd80f0 100644 --- a/include/xrpl/beast/unit_test/suite_list.h +++ b/include/xrpl/beast/unit_test/suite_list.h @@ -10,8 +10,9 @@ #include #include -#include -#include +#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include // IWYU pragma: keep namespace beast::unit_test { diff --git a/include/xrpl/beast/unit_test/thread.h b/include/xrpl/beast/unit_test/thread.h index 7ae093eb85..91d8cf3cab 100644 --- a/include/xrpl/beast/unit_test/thread.h +++ b/include/xrpl/beast/unit_test/thread.h @@ -6,7 +6,9 @@ #include +#include #include +#include #include #include @@ -43,7 +45,10 @@ public: template explicit Thread(Suite& s, F&& f, Args&&... args) : s_(&s) { - std::function b = std::bind(std::forward(f), std::forward(args)...); + std::function b = [f = std::forward(f), + ... args = std::forward(args)]() mutable { + std::invoke(f, args...); + }; t_ = std::thread(&Thread::run, this, std::move(b)); } diff --git a/include/xrpl/beast/utility/Journal.h b/include/xrpl/beast/utility/Journal.h index 1a0c148d1f..3de3cfb0e0 100644 --- a/include/xrpl/beast/utility/Journal.h +++ b/include/xrpl/beast/utility/Journal.h @@ -3,7 +3,10 @@ #include #include +#include #include +#include +#include namespace beast { @@ -105,12 +108,12 @@ public: }; #ifndef __INTELLISENSE__ - static_assert(!std::is_default_constructible_v, ""); - static_assert(!std::is_copy_constructible_v, ""); - static_assert(!std::is_move_constructible_v, ""); - static_assert(!std::is_copy_assignable_v, ""); - static_assert(!std::is_move_assignable_v, ""); - static_assert(std::is_nothrow_destructible_v, ""); + static_assert(!std::is_default_constructible_v); + static_assert(!std::is_copy_constructible_v); + static_assert(!std::is_move_constructible_v); + static_assert(!std::is_copy_assignable_v); + static_assert(!std::is_move_assignable_v); + static_assert(std::is_nothrow_destructible_v); #endif /** Returns a Sink which does nothing. */ @@ -161,12 +164,12 @@ public: }; #ifndef __INTELLISENSE__ - static_assert(!std::is_default_constructible_v, ""); - static_assert(std::is_copy_constructible_v, ""); - static_assert(std::is_move_constructible_v, ""); - static_assert(!std::is_copy_assignable_v, ""); - static_assert(!std::is_move_assignable_v, ""); - static_assert(std::is_nothrow_destructible_v, ""); + static_assert(!std::is_default_constructible_v); + static_assert(std::is_copy_constructible_v); + static_assert(std::is_move_constructible_v); + static_assert(!std::is_copy_assignable_v); + static_assert(!std::is_move_assignable_v); + static_assert(std::is_nothrow_destructible_v); #endif //-------------------------------------------------------------------------- @@ -243,12 +246,12 @@ public: }; #ifndef __INTELLISENSE__ - static_assert(std::is_default_constructible_v, ""); - static_assert(std::is_copy_constructible_v, ""); - static_assert(std::is_move_constructible_v, ""); - static_assert(!std::is_copy_assignable_v, ""); - static_assert(!std::is_move_assignable_v, ""); - static_assert(std::is_nothrow_destructible_v, ""); + static_assert(std::is_default_constructible_v); + static_assert(std::is_copy_constructible_v); + static_assert(std::is_move_constructible_v); + static_assert(!std::is_copy_assignable_v); + static_assert(!std::is_move_assignable_v); + static_assert(std::is_nothrow_destructible_v); #endif //-------------------------------------------------------------------------- @@ -326,12 +329,12 @@ public: }; #ifndef __INTELLISENSE__ -static_assert(!std::is_default_constructible_v, ""); -static_assert(std::is_copy_constructible_v, ""); -static_assert(std::is_move_constructible_v, ""); -static_assert(std::is_copy_assignable_v, ""); -static_assert(std::is_move_assignable_v, ""); -static_assert(std::is_nothrow_destructible_v, ""); +static_assert(!std::is_default_constructible_v); +static_assert(std::is_copy_constructible_v); +static_assert(std::is_move_constructible_v); +static_assert(std::is_copy_assignable_v); +static_assert(std::is_move_assignable_v); +static_assert(std::is_nothrow_destructible_v); #endif //------------------------------------------------------------------------------ @@ -411,9 +414,9 @@ class BasicLogstream : public std::basic_ostream { using char_type = CharT; using traits_type = Traits; - using int_type = typename traits_type::int_type; - using pos_type = typename traits_type::pos_type; - using off_type = typename traits_type::off_type; + using int_type = traits_type::int_type; + using pos_type = traits_type::pos_type; + using off_type = traits_type::off_type; detail::LogStreamBuf buf_; diff --git a/include/xrpl/beast/utility/PropertyStream.h b/include/xrpl/beast/utility/PropertyStream.h index 62de019edd..3fb6df53d9 100644 --- a/include/xrpl/beast/utility/PropertyStream.h +++ b/include/xrpl/beast/utility/PropertyStream.h @@ -3,8 +3,10 @@ #include #include +#include #include #include +#include namespace beast { diff --git a/include/xrpl/beast/utility/WrappedSink.h b/include/xrpl/beast/utility/WrappedSink.h index 22d75927fe..a24ad595db 100644 --- a/include/xrpl/beast/utility/WrappedSink.h +++ b/include/xrpl/beast/utility/WrappedSink.h @@ -2,6 +2,7 @@ #include +#include #include namespace beast { diff --git a/include/xrpl/beast/utility/Zero.h b/include/xrpl/beast/utility/Zero.h index f54345d437..e28589760b 100644 --- a/include/xrpl/beast/utility/Zero.h +++ b/include/xrpl/beast/utility/Zero.h @@ -26,9 +26,7 @@ struct Zero explicit Zero() = default; }; -namespace { -constexpr Zero kZero{}; -} // namespace +inline constexpr Zero kZero{}; /** Default implementation of signum calls the method on the class. */ template diff --git a/include/xrpl/beast/utility/instrumentation.h b/include/xrpl/beast/utility/instrumentation.h index 39b80bc438..c20c42156a 100644 --- a/include/xrpl/beast/utility/instrumentation.h +++ b/include/xrpl/beast/utility/instrumentation.h @@ -11,6 +11,8 @@ // Macros below are copied from antithesis_sdk.h and slightly simplified // The duplication is because Visual Studio 2019 cannot compile that header // even with the option -Zc:__cplusplus added. +// NOTE: cond must not contain bare commas outside () or []. Commas inside {} +// are not protected by the preprocessor and would be parsed as extra arguments. #define ALWAYS(cond, message, ...) assert((message) && (cond)) #define ALWAYS_OR_UNREACHABLE(cond, message) assert((message) && (cond)) #define SOMETIMES(cond, message, ...) @@ -22,6 +24,8 @@ #define XRPL_ASSERT_PARTS(cond, function, description, ...) \ XRPL_ASSERT(cond, function " : " description) +#define XRPL_ASSERT_IF(guard, cond, message) XRPL_ASSERT(!(guard) || (cond), message) + // How to use the instrumentation macros: // // * XRPL_ASSERT if cond must be true but the line might not be reached during @@ -29,6 +33,14 @@ // * XRPL_ASSERT_PARTS is for convenience, and works like XRPL_ASSERT, but // splits the message param into "function" and "description", then joins // them with " : " before passing to XRPL_ASSERT. +// * XRPL_ASSERT_IF(guard, cond, message) asserts the implication +// `guard => cond`: it can only fail when guard is true (e.g. an amendment +// is enabled) and cond is false. Unlike `if (guard) XRPL_ASSERT(...)`, the +// assertion site is always evaluated, so the fuzzer registers it +// unconditionally; cond itself is short-circuited and only evaluated when +// guard is true. NOTE: do not rely on side effects in guard — in release +// builds the assertion body is stripped, and the compiler may optimize away +// a side-effect-free guard entirely. // * ALWAYS if cond must be true _and_ the line must be reached during fuzzing. // Same like `assert` in normal use. // * REACHABLE if the line must be reached during fuzzing diff --git a/include/xrpl/beast/utility/maybe_const.h b/include/xrpl/beast/utility/maybe_const.h index 40904471be..10b2eaf7f6 100644 --- a/include/xrpl/beast/utility/maybe_const.h +++ b/include/xrpl/beast/utility/maybe_const.h @@ -15,6 +15,6 @@ struct MaybeConst /** Alias for omitting `typename`. */ template -using maybe_const_t = typename MaybeConst::type; +using maybe_const_t = MaybeConst::type; } // namespace beast diff --git a/include/xrpl/beast/utility/rngfill.h b/include/xrpl/beast/utility/rngfill.h index 1614a594c5..5bd9d8bc5c 100644 --- a/include/xrpl/beast/utility/rngfill.h +++ b/include/xrpl/beast/utility/rngfill.h @@ -1,11 +1,8 @@ #pragma once -#include - #include #include #include -#include namespace beast { @@ -13,10 +10,10 @@ template void rngfill(void* const buffer, std::size_t const bytes, Generator& g) { - using result_type = typename Generator::result_type; + using result_type = Generator::result_type; constexpr std::size_t kResultSize = sizeof(result_type); - std::uint8_t* const bufferStart = static_cast(buffer); + auto* const bufferStart = static_cast(buffer); std::size_t const completeIterations = bytes / kResultSize; std::size_t const bytesRemaining = bytes % kResultSize; @@ -35,16 +32,14 @@ rngfill(void* const buffer, std::size_t const bytes, Generator& g) } } -template < - class Generator, - std::size_t N, - class = std::enable_if_t> +template void rngfill(std::array& a, Generator& g) + requires(N % sizeof(typename Generator::result_type) == 0) { - using result_type = typename Generator::result_type; + using result_type = Generator::result_type; auto i = N / sizeof(result_type); - result_type* p = reinterpret_cast(a.data()); + auto* p = reinterpret_cast(a.data()); while (i--) *p++ = g(); } diff --git a/include/xrpl/conditions/Condition.h b/include/xrpl/conditions/Condition.h index 66d1d24736..3c798663d7 100644 --- a/include/xrpl/conditions/Condition.h +++ b/include/xrpl/conditions/Condition.h @@ -4,8 +4,12 @@ #include #include +#include #include +#include #include +#include +#include namespace xrpl::cryptoconditions { diff --git a/include/xrpl/conditions/Fulfillment.h b/include/xrpl/conditions/Fulfillment.h index fd8cd7d31e..a3001b2620 100644 --- a/include/xrpl/conditions/Fulfillment.h +++ b/include/xrpl/conditions/Fulfillment.h @@ -4,6 +4,11 @@ #include #include +#include +#include +#include +#include + namespace xrpl::cryptoconditions { struct Fulfillment diff --git a/include/xrpl/conditions/detail/PreimageSha256.h b/include/xrpl/conditions/detail/PreimageSha256.h index c592ea37ee..0973a52e4a 100644 --- a/include/xrpl/conditions/detail/PreimageSha256.h +++ b/include/xrpl/conditions/detail/PreimageSha256.h @@ -7,7 +7,11 @@ #include #include +#include +#include #include +#include +#include namespace xrpl::cryptoconditions { diff --git a/include/xrpl/conditions/detail/utils.h b/include/xrpl/conditions/detail/utils.h index 87f2265034..bf16bfb42b 100644 --- a/include/xrpl/conditions/detail/utils.h +++ b/include/xrpl/conditions/detail/utils.h @@ -6,7 +6,10 @@ #include +#include +#include #include +#include // A collection of functions to decode binary blobs // encoded with X.690 Distinguished Encoding Rules. diff --git a/include/xrpl/basics/BasicConfig.h b/include/xrpl/config/BasicConfig.h similarity index 98% rename from include/xrpl/basics/BasicConfig.h rename to include/xrpl/config/BasicConfig.h index 5a82f7d081..5680b51fe7 100644 --- a/include/xrpl/basics/BasicConfig.h +++ b/include/xrpl/config/BasicConfig.h @@ -6,9 +6,13 @@ #include #include +#include #include +#include +#include #include #include +#include #include namespace xrpl { @@ -298,7 +302,8 @@ set(T& target, std::string const& name, Section const& section) try { auto const val = section.get(name); - if ((foundAndValid = val.has_value())) + foundAndValid = val.has_value(); + if (foundAndValid) target = *val; } catch (boost::bad_lexical_cast const&) // NOLINT(bugprone-empty-catch) diff --git a/include/xrpl/config/Constants.h b/include/xrpl/config/Constants.h new file mode 100644 index 0000000000..5514e0e77b --- /dev/null +++ b/include/xrpl/config/Constants.h @@ -0,0 +1,180 @@ +#pragma once + +namespace xrpl { + +struct Sections +{ + static constexpr auto kAmendments = "amendments"; + static constexpr auto kAmendmentMajorityTime = "amendment_majority_time"; + static constexpr auto kBetaRpcApi = "beta_rpc_api"; + static constexpr auto kClusterNodes = "cluster_nodes"; + static constexpr auto kCompression = "compression"; + static constexpr auto kCrawl = "crawl"; + static constexpr auto kDatabasePath = "database_path"; + static constexpr auto kDebugLogfile = "debug_logfile"; + static constexpr auto kElbSupport = "elb_support"; + static constexpr auto kFeatures = "features"; + static constexpr auto kFeeDefault = "fee_default"; + static constexpr auto kFetchDepth = "fetch_depth"; + static constexpr auto kHashrouter = "hashrouter"; + static constexpr auto kImportNodeDatabase = "import_db"; + static constexpr auto kInsight = "insight"; + static constexpr auto kIoWorkers = "io_workers"; + static constexpr auto kIps = "ips"; + static constexpr auto kIpsFixed = "ips_fixed"; + static constexpr auto kLedgerHistory = "ledger_history"; + static constexpr auto kLedgerReplay = "ledger_replay"; + static constexpr auto kLedgerTxTables = "ledger_tx_tables"; + static constexpr auto kMaxTransactions = "max_transactions"; + static constexpr auto kNetworkId = "network_id"; + static constexpr auto kNetworkQuorum = "network_quorum"; + static constexpr auto kNodeDatabase = "node_db"; + static constexpr auto kNodeSeed = "node_seed"; + static constexpr auto kNodeSize = "node_size"; + static constexpr auto kOverlay = "overlay"; + static constexpr auto kPathSearch = "path_search"; + static constexpr auto kPathSearchFast = "path_search_fast"; + static constexpr auto kPathSearchMax = "path_search_max"; + static constexpr auto kPathSearchOld = "path_search_old"; + static constexpr auto kPeerPrivate = "peer_private"; + static constexpr auto kPeersInMax = "peers_in_max"; + static constexpr auto kPeersMax = "peers_max"; + static constexpr auto kPeersOutMax = "peers_out_max"; + static constexpr auto kPerf = "perf"; + static constexpr auto kPortGrpc = "port_grpc"; + static constexpr auto kPortPeer = "port_peer"; + static constexpr auto kPortRpc = "port_rpc"; + static constexpr auto kPortWs = "port_ws"; + static constexpr auto kPortWssAdmin = "port_wss_admin"; + static constexpr auto kPrefetchWorkers = "prefetch_workers"; + static constexpr auto kReduceRelay = "reduce_relay"; + static constexpr auto kRelationalDb = "relational_db"; + static constexpr auto kRelayProposals = "relay_proposals"; + static constexpr auto kRelayValidations = "relay_validations"; + static constexpr auto kRpcStartup = "rpc_startup"; + static constexpr auto kServer = "server"; + static constexpr auto kServerDomain = "server_domain"; + static constexpr auto kSigningSupport = "signing_support"; + static constexpr auto kSntp = "sntp_servers"; + static constexpr auto kSqdb = "sqdb"; + static constexpr auto kSqlite = "sqlite"; + static constexpr auto kSslVerify = "ssl_verify"; + static constexpr auto kSslVerifyDir = "ssl_verify_dir"; + static constexpr auto kSslVerifyFile = "ssl_verify_file"; + static constexpr auto kSweepInterval = "sweep_interval"; + static constexpr auto kTransactionQueue = "transaction_queue"; + static constexpr auto kValidationSeed = "validation_seed"; + static constexpr auto kValidatorKeys = "validator_keys"; + static constexpr auto kValidatorKeyRevocation = "validator_key_revocation"; + static constexpr auto kValidatorListKeys = "validator_list_keys"; + static constexpr auto kValidatorListSites = "validator_list_sites"; + static constexpr auto kValidatorListThreshold = "validator_list_threshold"; + static constexpr auto kValidatorToken = "validator_token"; + static constexpr auto kValidators = "validators"; + static constexpr auto kValidatorsFile = "validators_file"; + static constexpr auto kVetoAmendments = "veto_amendments"; + static constexpr auto kVl = "vl"; + static constexpr auto kVoting = "voting"; + static constexpr auto kWorkers = "workers"; +}; + +struct Keys +{ + static constexpr auto kAccountReserve = "account_reserve"; + static constexpr auto kAddress = "address"; + static constexpr auto kAdmin = "admin"; + static constexpr auto kAdminPassword = "admin_password"; + static constexpr auto kAdminUser = "admin_user"; + static constexpr auto kAdvisoryDelete = "advisory_delete"; + static constexpr auto kAgeThresholdSeconds = "age_threshold_seconds"; + static constexpr auto kBackOff = "backOff"; + static constexpr auto kBackOffMilliseconds = "back_off_milliseconds"; + static constexpr auto kBackend = "backend"; + static constexpr auto kBbtOptions = "bbt_options"; + static constexpr auto kBgThreads = "bg_threads"; + static constexpr auto kBlockSize = "block_size"; + static constexpr auto kCacheAge = "cache_age"; + static constexpr auto kCacheMb = "cache_mb"; + static constexpr auto kCacheSize = "cache_size"; + static constexpr auto kClientMaxWindowBits = "client_max_window_bits"; + static constexpr auto kClientNoContextTakeover = "client_no_context_takeover"; + static constexpr auto kCompressLevel = "compress_level"; + static constexpr auto kCounts = "counts"; + static constexpr auto kDeleteBatch = "delete_batch"; + static constexpr auto kEarliestSeq = "earliest_seq"; + static constexpr auto kFastLoad = "fast_load"; + static constexpr auto kFileSizeMb = "file_size_mb"; + static constexpr auto kFileSizeMult = "file_size_mult"; + static constexpr auto kFilterBits = "filter_bits"; + static constexpr auto kFilterFull = "filter_full"; + static constexpr auto kHardSet = "hard_set"; + static constexpr auto kHighThreads = "high_threads"; + static constexpr auto kHoldTime = "hold_time"; + static constexpr auto kIp = "ip"; + static constexpr auto kJournalMode = "journal_mode"; + static constexpr auto kJournalSizeLimit = "journal_size_limit"; + static constexpr auto kLedgersInQueue = "ledgers_in_queue"; + static constexpr auto kLimit = "limit"; + static constexpr auto kLogInterval = "log_interval"; + static constexpr auto kMaxDivergedTime = "max_diverged_time"; + static constexpr auto kMaxLedgerCountsToStore = "max_ledger_counts_to_store"; + static constexpr auto kMaxUnknownTime = "max_unknown_time"; + static constexpr auto kMaximumTxnInLedger = "maximum_txn_in_ledger"; + static constexpr auto kMaximumTxnPerAccount = "maximum_txn_per_account"; + static constexpr auto kMemoryLevel = "memory_level"; + static constexpr auto kMinLedgersToComputeSizeLimit = "min_ledgers_to_compute_size_limit"; + static constexpr auto kMinimumEscalationMultiplier = "minimum_escalation_multiplier"; + static constexpr auto kMinimumLastLedgerBuffer = "minimum_last_ledger_buffer"; + static constexpr auto kMinimumQueueSize = "minimum_queue_size"; + static constexpr auto kMinimumTxnInLedger = "minimum_txn_in_ledger"; + static constexpr auto kMinimumTxnInLedgerStandalone = "minimum_txn_in_ledger_standalone"; + static constexpr auto kNormalConsensusIncreasePercent = "normal_consensus_increase_percent"; + static constexpr auto kNudbBlockSize = "nudb_block_size"; + static constexpr auto kOnlineDelete = "online_delete"; + static constexpr auto kOpenFiles = "open_files"; + static constexpr auto kOptions = "options"; + static constexpr auto kOverlay = "overlay"; + static constexpr auto kOwnerReserve = "owner_reserve"; + static constexpr auto kPageSize = "page_size"; + static constexpr auto kPassword = "password"; + static constexpr auto kPath = "path"; + static constexpr auto kPermessageDeflate = "permessage_deflate"; + static constexpr auto kPort = "port"; + static constexpr auto kPrefix = "prefix"; + static constexpr auto kProtocol = "protocol"; + static constexpr auto kRecoveryWaitSeconds = "recovery_wait_seconds"; + static constexpr auto kReferenceFee = "reference_fee"; + static constexpr auto kRelayTime = "relay_time"; + static constexpr auto kRetrySequencePercent = "retry_sequence_percent"; + static constexpr auto kRqBundle = "rq_bundle"; + static constexpr auto kSafetyLevel = "safety_level"; + static constexpr auto kSecureGateway = "secure_gateway"; + static constexpr auto kSendQueueLimit = "send_queue_limit"; + static constexpr auto kServer = "server"; + static constexpr auto kServerMaxWindowBits = "server_max_window_bits"; + static constexpr auto kServerNoContextTakeover = "server_no_context_takeover"; + static constexpr auto kSlowConsensusDecreasePercent = "slow_consensus_decrease_percent"; + static constexpr auto kSslCert = "ssl_cert"; + static constexpr auto kSslCertChain = "ssl_cert_chain"; + static constexpr auto kSslChain = "ssl_chain"; + static constexpr auto kSslCiphers = "ssl_ciphers"; + static constexpr auto kSslClientCa = "ssl_client_ca"; + static constexpr auto kSslKey = "ssl_key"; + static constexpr auto kSynchronous = "synchronous"; + static constexpr auto kTargetTxnInLedger = "target_txn_in_ledger"; + static constexpr auto kTempStore = "temp_store"; + static constexpr auto kTxEnable = "tx_enable"; + static constexpr auto kTxMetrics = "tx_metrics"; + static constexpr auto kTxMinPeers = "tx_min_peers"; + static constexpr auto kTxRelayPercentage = "tx_relay_percentage"; + static constexpr auto kType = "type"; + static constexpr auto kUniversalCompaction = "universal_compaction"; + static constexpr auto kUnl = "unl"; + static constexpr auto kUseTxTables = "use_tx_tables"; + static constexpr auto kUser = "user"; + static constexpr auto kVpBaseSquelchEnable = "vp_base_squelch_enable"; + static constexpr auto kVpBaseSquelchMaxSelectedPeers = "vp_base_squelch_max_selected_peers"; + static constexpr auto kVpEnable = "vp_enable"; +}; + +} // namespace xrpl diff --git a/include/xrpl/core/ClosureCounter.h b/include/xrpl/core/ClosureCounter.h index fb13047f40..ed15db032e 100644 --- a/include/xrpl/core/ClosureCounter.h +++ b/include/xrpl/core/ClosureCounter.h @@ -1,11 +1,14 @@ #pragma once #include +#include #include +#include #include #include #include +#include namespace xrpl { diff --git a/include/xrpl/core/Coro.ipp b/include/xrpl/core/Coro.ipp index 90fa965038..133caf37a9 100644 --- a/include/xrpl/core/Coro.ipp +++ b/include/xrpl/core/Coro.ipp @@ -47,7 +47,7 @@ inline bool JobQueue::Coro::post() { { - std::scoped_lock const lk(mutex_run_); + std::scoped_lock const lk(mutexRun_); running_ = true; } @@ -58,7 +58,7 @@ JobQueue::Coro::post() } // The coroutine will not run. Clean up running_. - std::scoped_lock const lk(mutex_run_); + std::scoped_lock const lk(mutexRun_); running_ = false; cv_.notify_all(); return false; @@ -68,7 +68,7 @@ inline void JobQueue::Coro::resume() { { - std::scoped_lock const lk(mutex_run_); + std::scoped_lock const lk(mutexRun_); running_ = true; } { @@ -92,7 +92,7 @@ JobQueue::Coro::resume() } detail::getLocalValues().release(); detail::getLocalValues().reset(saved); - std::scoped_lock const lk(mutex_run_); + std::scoped_lock const lk(mutexRun_); running_ = false; cv_.notify_all(); } @@ -127,7 +127,7 @@ JobQueue::Coro::expectEarlyExit() inline void JobQueue::Coro::join() { - std::unique_lock lk(mutex_run_); + std::unique_lock lk(mutexRun_); cv_.wait(lk, [this]() { return !running_; }); } diff --git a/include/xrpl/core/HashRouter.h b/include/xrpl/core/HashRouter.h index d36b8aee6e..dad1afb405 100644 --- a/include/xrpl/core/HashRouter.h +++ b/include/xrpl/core/HashRouter.h @@ -4,10 +4,16 @@ #include #include #include +#include #include +#include +#include +#include #include #include +#include +#include namespace xrpl { @@ -19,12 +25,14 @@ enum class HashRouterFlags : std::uint16_t { HELD = 0x08, // Held by LedgerMaster after potential processing failure TRUSTED = 0x10, // Comes from a trusted source - // Private flags (used internally in apply.cpp) - // Do not attempt to read, set, or reuse. + // Private flags. Each group is owned by one file; do not read, set, or + // reuse a flag outside the file noted. + // Used in apply.cpp PRIVATE1 = 0x0100, PRIVATE2 = 0x0200, PRIVATE3 = 0x0400, PRIVATE4 = 0x0800, + // Used in EscrowFinish.cpp PRIVATE5 = 0x1000, PRIVATE6 = 0x2000 }; @@ -119,7 +127,7 @@ private: } [[nodiscard]] HashRouterFlags - getFlags(void) const + getFlags() const { return flags_; } diff --git a/include/xrpl/core/Job.h b/include/xrpl/core/Job.h index b62488dec6..e16d7412bf 100644 --- a/include/xrpl/core/Job.h +++ b/include/xrpl/core/Job.h @@ -2,9 +2,14 @@ #include #include +#include #include +#include +#include #include +#include +#include namespace xrpl { @@ -127,7 +132,7 @@ private: std::function job_; std::shared_ptr loadEvent_; std::string name_; - clock_type::time_point queue_time_; + clock_type::time_point queueTime_; }; using JobCounter = ClosureCounter; diff --git a/include/xrpl/core/JobQueue.h b/include/xrpl/core/JobQueue.h index 2a96ea45fa..e4b64546f3 100644 --- a/include/xrpl/core/JobQueue.h +++ b/include/xrpl/core/JobQueue.h @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -12,10 +11,27 @@ // `boost/context/pooled_fixedsize_stack.hpp`, whose `.malloc()` / `.free()` // member calls on `boost::pool` collide with MSVC's `_CRTDBG_MAP_ALLOC` macros // in Debug builds (see cmake/XrplCompiler.cmake). +#include +#include +#include +#include +#include +#include + #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include namespace xrpl { @@ -52,7 +68,7 @@ public: std::string name_; bool running_{false}; std::mutex mutex_; - std::mutex mutex_run_; + std::mutex mutexRun_; std::condition_variable cv_; boost::coroutines2::coroutine::push_type* yield_{}; boost::coroutines2::coroutine::pull_type coro_; @@ -112,7 +128,7 @@ public: resume(); /** Returns true if the Coro is still runnable (has not returned). */ - bool + [[nodiscard]] bool runnable() const; /** Once called, the Coro allows early exit without an assert. */ @@ -138,16 +154,14 @@ public: @param type The type of job. @param name Name of the job. - @param jobHandler Lambda with signature void (Job&). Called when the - job is executed. + @param jobHandler Callable with signature void(). Called when the job is executed. @return true if jobHandler added to queue. */ - template < - typename JobHandler, - typename = std::enable_if_t()()), void>>> + template bool addJob(JobType type, std::string const& name, JobHandler&& jobHandler) + requires(std::is_void_v>) { if (auto optionalCountedJob = jobCounter_.wrap(std::forward(jobHandler))) { @@ -246,7 +260,7 @@ private: // Statistics tracking perf::PerfLog& perfLog_; beast::insight::Collector::ptr collector_; - beast::insight::Gauge job_count_; + beast::insight::Gauge jobCount_; beast::insight::Hook hook_; std::condition_variable cv_; @@ -384,7 +398,7 @@ private: } // namespace xrpl -#include +#include // IWYU pragma: keep namespace xrpl { diff --git a/include/xrpl/core/JobTypeData.h b/include/xrpl/core/JobTypeData.h index 4e9f95dc04..d53440e1ca 100644 --- a/include/xrpl/core/JobTypeData.h +++ b/include/xrpl/core/JobTypeData.h @@ -2,7 +2,10 @@ #include #include +#include +#include #include +#include #include diff --git a/include/xrpl/core/JobTypeInfo.h b/include/xrpl/core/JobTypeInfo.h index 430e80b388..b5db0dbaab 100644 --- a/include/xrpl/core/JobTypeInfo.h +++ b/include/xrpl/core/JobTypeInfo.h @@ -2,6 +2,10 @@ #include +#include +#include +#include + namespace xrpl { /** Holds all the 'static' information about a job, which does not change */ diff --git a/include/xrpl/core/JobTypes.h b/include/xrpl/core/JobTypes.h index fb5c7988cb..cc2f3ecbf5 100644 --- a/include/xrpl/core/JobTypes.h +++ b/include/xrpl/core/JobTypes.h @@ -1,10 +1,14 @@ #pragma once +#include #include #include +#include #include #include +#include +#include namespace xrpl { @@ -114,7 +118,7 @@ public: [[nodiscard]] JobTypeInfo const& get(JobType jt) const { - Map::const_iterator const iter(map.find(jt)); + auto const iter = map.find(jt); XRPL_ASSERT(iter != map.end(), "xrpl::JobTypes::get : valid input"); if (iter != map.end()) diff --git a/include/xrpl/core/LoadMonitor.h b/include/xrpl/core/LoadMonitor.h index 32a813baa7..f1a8eb6c56 100644 --- a/include/xrpl/core/LoadMonitor.h +++ b/include/xrpl/core/LoadMonitor.h @@ -5,6 +5,7 @@ #include #include +#include #include namespace xrpl { diff --git a/include/xrpl/core/PeerReservationTable.h b/include/xrpl/core/PeerReservationTable.h index a9ab894124..e6f6dd622e 100644 --- a/include/xrpl/core/PeerReservationTable.h +++ b/include/xrpl/core/PeerReservationTable.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include diff --git a/include/xrpl/core/PerfLog.h b/include/xrpl/core/PerfLog.h index 38318c745d..f09665e291 100644 --- a/include/xrpl/core/PerfLog.h +++ b/include/xrpl/core/PerfLog.h @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include #include #include @@ -18,6 +18,7 @@ class Journal; namespace xrpl { class Application; +class Section; namespace perf { /** diff --git a/include/xrpl/core/ServiceRegistry.h b/include/xrpl/core/ServiceRegistry.h index 1d0c9e38f4..50bf2d7c10 100644 --- a/include/xrpl/core/ServiceRegistry.h +++ b/include/xrpl/core/ServiceRegistry.h @@ -1,11 +1,17 @@ #pragma once #include +#include #include #include +#include +#include #include +#include +#include + namespace xrpl { // Forward declarations diff --git a/include/xrpl/core/StartUpType.h b/include/xrpl/core/StartUpType.h index 46359ad7b6..6d05149618 100644 --- a/include/xrpl/core/StartUpType.h +++ b/include/xrpl/core/StartUpType.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include namespace xrpl { diff --git a/include/xrpl/core/detail/semaphore.h b/include/xrpl/core/detail/semaphore.h index 7bc83f86f5..e40463e322 100644 --- a/include/xrpl/core/detail/semaphore.h +++ b/include/xrpl/core/detail/semaphore.h @@ -29,6 +29,7 @@ #pragma once #include +#include #include namespace xrpl { diff --git a/include/xrpl/crypto/RFC1751.h b/include/xrpl/crypto/RFC1751.h index c99c691ba0..278f3c207b 100644 --- a/include/xrpl/crypto/RFC1751.h +++ b/include/xrpl/crypto/RFC1751.h @@ -1,6 +1,8 @@ #pragma once +#include #include +#include #include namespace xrpl { @@ -34,7 +36,7 @@ private: static void standard(std::string& strWord); static int - wsrch(std::string const& strWord, int iMin, int iMax); + wsrch(std::string_view strWord, int iMin, int iMax); static int etob(std::string& strData, std::vector vsHuman); diff --git a/include/xrpl/crypto/csprng.h b/include/xrpl/crypto/csprng.h index e386d9d11e..cdc6a723c8 100644 --- a/include/xrpl/crypto/csprng.h +++ b/include/xrpl/crypto/csprng.h @@ -1,5 +1,8 @@ #pragma once +#include +#include +#include #include namespace xrpl { diff --git a/include/xrpl/json/JsonPropertyStream.h b/include/xrpl/json/JsonPropertyStream.h index 47317b9ddb..405a61cd34 100644 --- a/include/xrpl/json/JsonPropertyStream.h +++ b/include/xrpl/json/JsonPropertyStream.h @@ -3,6 +3,9 @@ #include #include +#include +#include + namespace xrpl { /** A PropertyStream::Sink which produces a json::Value of type ValueType::Object. */ diff --git a/include/xrpl/json/Writer.h b/include/xrpl/json/Writer.h index 664a2a75c1..024876a43c 100644 --- a/include/xrpl/json/Writer.h +++ b/include/xrpl/json/Writer.h @@ -1,11 +1,13 @@ #pragma once -#include #include #include #include +#include #include +#include +#include namespace json { @@ -161,7 +163,7 @@ public: * While the JSON spec doesn't explicitly disallow this, you should avoid * calling this method twice with the same tag for the same object. * - * If CHECK_JSON_WRITER is defined, this function throws an exception if if + * If CHECK_JSON_WRITER is defined, this function throws an exception if * the tag you use has already been used in this object. */ template diff --git a/include/xrpl/json/detail/json_assert.h b/include/xrpl/json/detail/json_assert.h index 8e33f45b65..f501e42aa4 100644 --- a/include/xrpl/json/detail/json_assert.h +++ b/include/xrpl/json/detail/json_assert.h @@ -1,8 +1,5 @@ #pragma once -#include -#include - #define JSON_ASSERT_MESSAGE(condition, message) \ if (!(condition)) \ xrpl::Throw(message); diff --git a/include/xrpl/json/json_reader.h b/include/xrpl/json/json_reader.h index 9251183281..d1e4ada579 100644 --- a/include/xrpl/json/json_reader.h +++ b/include/xrpl/json/json_reader.h @@ -5,7 +5,10 @@ #include +#include +#include #include +#include namespace json { @@ -151,7 +154,7 @@ private: Location end, unsigned int& unicode); bool - addError(std::string const& message, Token& token, Location extra = 0); + addError(std::string const& message, Token& token, Location extra = nullptr); bool recoverFromError(TokenType skipUntilToken); bool diff --git a/include/xrpl/json/json_value.h b/include/xrpl/json/json_value.h index e9dcb8bcbe..f786c6a9dc 100644 --- a/include/xrpl/json/json_value.h +++ b/include/xrpl/json/json_value.h @@ -566,6 +566,7 @@ public: using SelfType = ValueConstIterator; ValueConstIterator() = default; + ValueConstIterator(ValueConstIterator const& other) = default; private: /*! \internal Use by Value to create an iterator. @@ -574,12 +575,12 @@ private: public: SelfType& - operator=(ValueIteratorBase const& other); + operator=(SelfType const& other); SelfType operator++(int) { - SelfType temp(*this); + SelfType const temp(*this); ++*this; return temp; } @@ -587,7 +588,7 @@ public: SelfType operator--(int) { - SelfType temp(*this); + SelfType const temp(*this); --*this; return temp; } diff --git a/include/xrpl/json/json_writer.h b/include/xrpl/json/json_writer.h index afc99fe8c9..4bc15b71da 100644 --- a/include/xrpl/json/json_writer.h +++ b/include/xrpl/json/json_writer.h @@ -3,7 +3,10 @@ #include #include +#include #include +#include +#include #include namespace json { diff --git a/include/xrpl/ledger/AcceptedLedgerTx.h b/include/xrpl/ledger/AcceptedLedgerTx.h index 0a1592f6e1..f59b8a074d 100644 --- a/include/xrpl/ledger/AcceptedLedgerTx.h +++ b/include/xrpl/ledger/AcceptedLedgerTx.h @@ -1,13 +1,23 @@ #pragma once +#include #include +#include #include #include +#include +#include #include +#include +#include #include #include +#include +#include +#include + namespace xrpl { /** diff --git a/include/xrpl/ledger/AmendmentTable.h b/include/xrpl/ledger/AmendmentTable.h index 8ed3cb81ff..6598be5a5c 100644 --- a/include/xrpl/ledger/AmendmentTable.h +++ b/include/xrpl/ledger/AmendmentTable.h @@ -1,14 +1,37 @@ #pragma once +#include +#include +#include +#include +#include +#include +#include #include #include +#include #include #include +#include +#include +#include +#include #include +#include +#include #include +#include +#include +#include +#include +#include +#include #include +#include +#include #include +#include namespace xrpl { diff --git a/include/xrpl/ledger/ApplyView.h b/include/xrpl/ledger/ApplyView.h index f825311e1d..e519013d9a 100644 --- a/include/xrpl/ledger/ApplyView.h +++ b/include/xrpl/ledger/ApplyView.h @@ -1,9 +1,22 @@ #pragma once +#include #include #include -#include #include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include namespace xrpl { @@ -123,7 +136,7 @@ private: bool preserveOrder, Keylet const& directory, uint256 const& key, - std::function const&)> const& describe); + std::function const& describe); public: ApplyView() = default; @@ -153,7 +166,7 @@ public: @return `nullptr` if the key is not present */ - virtual std::shared_ptr + virtual SLE::pointer peek(Keylet const& k) = 0; /** Remove a peeked SLE. @@ -168,7 +181,7 @@ public: The key is no longer associated with the SLE. */ virtual void - erase(std::shared_ptr const& sle) = 0; + erase(SLE::ref sle) = 0; /** Insert a new state SLE @@ -189,7 +202,7 @@ public: @note The key is taken from the SLE */ virtual void - insert(std::shared_ptr const& sle) = 0; + insert(SLE::ref sle) = 0; /** Indicate changes to a peeked SLE @@ -208,7 +221,7 @@ public: */ /** @{ */ virtual void - update(std::shared_ptr const& sle) = 0; + update(SLE::ref sle) = 0; //-------------------------------------------------------------------------- @@ -301,7 +314,7 @@ public: dirAppend( Keylet const& directory, Keylet const& key, - std::function const&)> const& describe) + std::function const& describe) { if (key.type != ltOFFER) { @@ -340,7 +353,7 @@ public: dirInsert( Keylet const& directory, uint256 const& key, - std::function const&)> const& describe) + std::function const& describe) { return dirAdd(false, directory, key, describe); } @@ -349,7 +362,7 @@ public: dirInsert( Keylet const& directory, Keylet const& key, - std::function const&)> const& describe) + std::function const& describe) { return dirAdd(false, directory, key.key, describe); } @@ -411,7 +424,7 @@ createRoot( ApplyView& view, Keylet const& directory, uint256 const& key, - std::function const&)> const& describe); + std::function const& describe); auto findPreviousPage(ApplyView& view, Keylet const& directory, SLE::ref start); @@ -434,7 +447,7 @@ insertPage( SLE::ref next, uint256 const& key, Keylet const& directory, - std::function const&)> const& describe); + std::function const& describe); } // namespace directory } // namespace xrpl diff --git a/include/xrpl/ledger/ApplyViewImpl.h b/include/xrpl/ledger/ApplyViewImpl.h index 7f790f2be5..9a3734a8ca 100644 --- a/include/xrpl/ledger/ApplyViewImpl.h +++ b/include/xrpl/ledger/ApplyViewImpl.h @@ -1,9 +1,20 @@ #pragma once +#include +#include +#include #include +#include #include #include +#include +#include #include +#include + +#include +#include +#include namespace xrpl { @@ -67,8 +78,8 @@ public: std::function const& before, - std::shared_ptr const& after)> const& func); + SLE::const_ref before, + SLE::const_ref after)> const& func); private: std::optional deliver_; diff --git a/include/xrpl/ledger/BookDirs.h b/include/xrpl/ledger/BookDirs.h index afde6ee7b3..dc4361136d 100644 --- a/include/xrpl/ledger/BookDirs.h +++ b/include/xrpl/ledger/BookDirs.h @@ -1,6 +1,13 @@ #pragma once +#include #include +#include +#include + +#include +#include +#include namespace xrpl { @@ -9,15 +16,15 @@ class BookDirs private: ReadView const* view_ = nullptr; uint256 const root_; - uint256 const next_quality_; + uint256 const nextQuality_; uint256 const key_; - std::shared_ptr sle_ = nullptr; + SLE::const_pointer sle_ = nullptr; unsigned int entry_ = 0; uint256 index_; public: class const_iterator; // NOLINT(readability-identifier-naming) - using value_type = std::shared_ptr; + using value_type = SLE::const_pointer; BookDirs(ReadView const&, Book const&); @@ -67,16 +74,16 @@ private: friend class BookDirs; const_iterator(ReadView const& view, uint256 const& root, uint256 const& dirKey) - : view_(&view), root_(root), key_(dirKey), cur_key_(dirKey) + : view_(&view), root_(root), key_(dirKey), curKey_(dirKey) { } ReadView const* view_ = nullptr; uint256 root_; - uint256 next_quality_; + uint256 nextQuality_; uint256 key_; - uint256 cur_key_; - std::shared_ptr sle_; + uint256 curKey_; + SLE::const_pointer sle_; unsigned int entry_ = 0; uint256 index_; std::optional mutable cache_; diff --git a/include/xrpl/ledger/BookListeners.h b/include/xrpl/ledger/BookListeners.h deleted file mode 100644 index 3b96aca680..0000000000 --- a/include/xrpl/ledger/BookListeners.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -namespace xrpl { - -/** Listen to public/subscribe messages from a book. */ -class BookListeners -{ -public: - using pointer = std::shared_ptr; - - BookListeners() = default; - - /** Add a new subscription for this book - */ - void - addSubscriber(InfoSub::ref sub); - - /** Stop publishing to a subscriber - */ - void - removeSubscriber(std::uint64_t sub); - - /** Publish a transaction to subscribers - - Publish a transaction to clients subscribed to changes on this book. - Uses havePublished to prevent sending duplicate transactions to clients - that have subscribed to multiple books. - - @param jvObj JSON transaction data to publish - @param havePublished InfoSub sequence numbers that have already - published this transaction. - - */ - void - publish(MultiApiJson const& jvObj, hash_set& havePublished); - -private: - std::recursive_mutex lock_; - - hash_map listeners_; -}; - -} // namespace xrpl diff --git a/include/xrpl/ledger/CachedView.h b/include/xrpl/ledger/CachedView.h index 34a75e4c07..1da3a67563 100644 --- a/include/xrpl/ledger/CachedView.h +++ b/include/xrpl/ledger/CachedView.h @@ -1,11 +1,20 @@ #pragma once +#include #include #include #include +#include +#include +#include +#include +#include +#include #include +#include #include +#include namespace xrpl { @@ -36,7 +45,7 @@ public: bool exists(Keylet const& k) const override; - std::shared_ptr + SLE::const_pointer read(Keylet const& k) const override; bool @@ -132,7 +141,7 @@ template class CachedView : public detail::CachedViewImpl { private: - static_assert(std::is_base_of_v, ""); + static_assert(std::is_base_of_v); std::shared_ptr sp_; diff --git a/include/xrpl/ledger/CanonicalTXSet.h b/include/xrpl/ledger/CanonicalTXSet.h index 8653816eee..f8349dfab6 100644 --- a/include/xrpl/ledger/CanonicalTXSet.h +++ b/include/xrpl/ledger/CanonicalTXSet.h @@ -1,10 +1,16 @@ #pragma once #include +#include +#include #include #include #include +#include +#include +#include + namespace xrpl { /** Holds transactions which were deferred to the next pass of consensus. @@ -93,7 +99,7 @@ public: } void - insert(std::shared_ptr const& txn); + insert(std::shared_ptr txn); // Pops the next transaction on account that follows seqProx in the // sort order. Normally called when a transaction is successfully diff --git a/include/xrpl/ledger/Dir.h b/include/xrpl/ledger/Dir.h index cfbef357b1..05df887d8b 100644 --- a/include/xrpl/ledger/Dir.h +++ b/include/xrpl/ledger/Dir.h @@ -1,7 +1,15 @@ #pragma once +#include #include -#include +#include +#include +#include + +#include +#include +#include +#include namespace xrpl { @@ -22,12 +30,12 @@ class Dir private: ReadView const* view_ = nullptr; Keylet root_; - std::shared_ptr sle_; + SLE::const_pointer sle_; STVector256 const* indexes_ = nullptr; public: class ConstIterator; - using value_type = std::shared_ptr; + using value_type = SLE::const_pointer; Dir(ReadView const&, Keylet const&); @@ -102,7 +110,7 @@ private: Keylet page_; uint256 index_; std::optional mutable cache_; - std::shared_ptr sle_; + SLE::const_pointer sle_; STVector256 const* indexes_ = nullptr; std::vector::const_iterator it_; }; diff --git a/include/xrpl/ledger/Ledger.h b/include/xrpl/ledger/Ledger.h index 351f7d80e5..3453389a5e 100644 --- a/include/xrpl/ledger/Ledger.h +++ b/include/xrpl/ledger/Ledger.h @@ -1,16 +1,32 @@ #pragma once #include +#include +#include +#include #include #include -#include +#include #include -#include +#include +#include +#include #include #include +#include +#include #include -#include +#include +#include #include +#include + +#include +#include +#include +#include +#include +#include namespace xrpl { @@ -166,7 +182,7 @@ public: std::optional succ(uint256 const& key, std::optional const& last = std::nullopt) const override; - std::shared_ptr + SLE::const_pointer read(Keylet const& k) const override; std::unique_ptr @@ -202,16 +218,16 @@ public: // void - rawErase(std::shared_ptr const& sle) override; + rawErase(SLE::ref sle) override; void - rawInsert(std::shared_ptr const& sle) override; + rawInsert(SLE::ref sle) override; void rawErase(uint256 const& key); void - rawReplace(std::shared_ptr const& sle) override; + rawReplace(SLE::ref sle) override; void rawDestroyXRP(XRPAmount const& fee) override @@ -361,7 +377,7 @@ public: bool isVotingLedger() const; - std::shared_ptr + SLE::pointer peek(Keylet const& k) const; private: diff --git a/include/xrpl/ledger/LedgerTiming.h b/include/xrpl/ledger/LedgerTiming.h index 508403d760..a97e229046 100644 --- a/include/xrpl/ledger/LedgerTiming.h +++ b/include/xrpl/ledger/LedgerTiming.h @@ -1,9 +1,10 @@ #pragma once -#include -#include +#include +#include #include +#include namespace xrpl { diff --git a/include/xrpl/ledger/OpenView.h b/include/xrpl/ledger/OpenView.h index c7f80ee321..875909715c 100644 --- a/include/xrpl/ledger/OpenView.h +++ b/include/xrpl/ledger/OpenView.h @@ -1,15 +1,26 @@ #pragma once +#include +#include #include #include #include -#include +#include +#include +#include +#include +#include +#include #include #include #include +#include #include +#include +#include +#include #include namespace xrpl { @@ -28,7 +39,7 @@ inline constexpr struct OpenLedgerT /** Batch view construction tag. Views constructed with this tag are part of a stack of views - used during batch transaction applied. + used during batch transaction application. */ inline constexpr struct BatchViewT { @@ -71,12 +82,12 @@ private: using txs_map = std::map< key_type, TxData, - std::less, + std::less<>, boost::container::pmr::polymorphic_allocator>>; // monotonic_resource_ must outlive `items_`. Make a pointer so it may be // easily moved. - std::unique_ptr monotonic_resource_; + std::unique_ptr monotonicResource_; txs_map txs_; Rules rules_; LedgerHeader header_; @@ -197,7 +208,7 @@ public: std::optional succ(key_type const& key, std::optional const& last = std::nullopt) const override; - std::shared_ptr + SLE::const_pointer read(Keylet const& k) const override; std::unique_ptr @@ -224,13 +235,13 @@ public: // RawView void - rawErase(std::shared_ptr const& sle) override; + rawErase(SLE::ref sle) override; void - rawInsert(std::shared_ptr const& sle) override; + rawInsert(SLE::ref sle) override; void - rawReplace(std::shared_ptr const& sle) override; + rawReplace(SLE::ref sle) override; void rawDestroyXRP(XRPAmount const& fee) override; diff --git a/include/xrpl/ledger/OrderBookDB.h b/include/xrpl/ledger/OrderBookDB.h index a0aee58e2a..a44183900c 100644 --- a/include/xrpl/ledger/OrderBookDB.h +++ b/include/xrpl/ledger/OrderBookDB.h @@ -1,11 +1,11 @@ #pragma once +#include +#include #include -#include #include #include #include -#include #include #include @@ -77,34 +77,24 @@ public: */ virtual bool isBookToXRP(Asset const& asset, std::optional const& domain = std::nullopt) = 0; - - /** - * Process a transaction for order book tracking. - * @param ledger The ledger the transaction was applied to - * @param alTx The transaction to process - * @param jvObj The JSON object of the transaction - */ - virtual void - processTxn( - std::shared_ptr const& ledger, - AcceptedLedgerTx const& alTx, - MultiApiJson const& jvObj) = 0; - - /** - * Get the book listeners for a book. - * @param book The book to get the listeners for - * @return The book listeners for the book - */ - virtual BookListeners::pointer - getBookListeners(Book const&) = 0; - - /** - * Create a new book listeners for a book. - * @param book The book to create the listeners for - * @return The new book listeners for the book - */ - virtual BookListeners::pointer - makeBookListeners(Book const&) = 0; }; +/** Extract the set of books affected by a transaction. + * + * Walks the transaction's metadata nodes and collects every order book + * whose offers were created, modified, or deleted. Used by NetworkOPs to + * fan transaction notifications out to book subscribers. + * + * @param alTx The accepted ledger transaction to inspect. + * @param j Journal used to log per-node parsing failures. Inspecting an + * offer node can throw if a required field is missing; in that + * case the bad node is skipped and a warn-level message is + * emitted via @p j. Other affected books in the same transaction + * are still returned. + * @return The set of books whose offers were created, modified, or + * deleted. May be empty for non-offer transactions. + */ +hash_set +affectedBooks(AcceptedLedgerTx const& alTx, beast::Journal const& j); + } // namespace xrpl diff --git a/include/xrpl/ledger/PaymentSandbox.h b/include/xrpl/ledger/PaymentSandbox.h index 1cd89d9388..0117a962ff 100644 --- a/include/xrpl/ledger/PaymentSandbox.h +++ b/include/xrpl/ledger/PaymentSandbox.h @@ -1,11 +1,19 @@ #pragma once +#include #include -#include +#include #include #include +#include +#include +#include +#include +#include #include +#include +#include #include namespace xrpl { diff --git a/include/xrpl/ledger/RawView.h b/include/xrpl/ledger/RawView.h index cfcf807e13..ac2674226f 100644 --- a/include/xrpl/ledger/RawView.h +++ b/include/xrpl/ledger/RawView.h @@ -3,6 +3,9 @@ #include #include #include +#include + +#include namespace xrpl { @@ -25,7 +28,7 @@ public: can calculate metadata. */ virtual void - rawErase(std::shared_ptr const& sle) = 0; + rawErase(SLE::ref sle) = 0; /** Unconditionally insert a state item. @@ -39,7 +42,7 @@ public: @note The key is taken from the SLE */ virtual void - rawInsert(std::shared_ptr const& sle) = 0; + rawInsert(SLE::ref sle) = 0; /** Unconditionally replace a state item. @@ -54,7 +57,7 @@ public: @note The key is taken from the SLE */ virtual void - rawReplace(std::shared_ptr const& sle) = 0; + rawReplace(SLE::ref sle) = 0; /** Destroy XRP. diff --git a/include/xrpl/ledger/ReadView.h b/include/xrpl/ledger/ReadView.h index 4f9bf9c31d..724533039b 100644 --- a/include/xrpl/ledger/ReadView.h +++ b/include/xrpl/ledger/ReadView.h @@ -1,21 +1,28 @@ #pragma once +#include #include #include +#include #include +#include #include -#include -#include +#include // IWYU pragma: keep +#include #include +#include #include #include #include #include +#include #include #include +#include #include #include +#include namespace xrpl { @@ -34,9 +41,9 @@ public: using key_type = uint256; - using mapped_type = std::shared_ptr; + using mapped_type = SLE::const_pointer; - struct SlesType : detail::ReadViewFwdRange> + struct SlesType : detail::ReadViewFwdRange { explicit SlesType(ReadView const& view); [[nodiscard]] Iterator @@ -143,7 +150,7 @@ public: @return `nullptr` if the key is not present or if the type does not match. */ - [[nodiscard]] virtual std::shared_ptr + [[nodiscard]] virtual SLE::const_pointer read(Keylet const& k) const = 0; // Accounts in a payment are not allowed to use assets acquired during that diff --git a/include/xrpl/ledger/Sandbox.h b/include/xrpl/ledger/Sandbox.h index dc80df5ba2..ca8838631f 100644 --- a/include/xrpl/ledger/Sandbox.h +++ b/include/xrpl/ledger/Sandbox.h @@ -1,6 +1,8 @@ #pragma once +#include #include +#include #include namespace xrpl { diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 4958a89d8c..c89764df1d 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -1,18 +1,26 @@ #pragma once +#include +#include #include #include #include +#include +#include +#include +#include #include #include +#include +#include #include #include #include +#include #include #include #include -#include #include #include #include @@ -57,7 +65,7 @@ isVaultPseudoAccountFrozen( ReadView const& view, AccountID const& account, MPTIssue const& mptShare, - int depth); + std::uint8_t depth); [[nodiscard]] bool isLPTokenFrozen( @@ -135,7 +143,7 @@ areCompatible( dirLink( ApplyView& view, AccountID const& owner, - std::shared_ptr& object, + SLE::pointer& object, SF_UINT64 const& node = sfOwnerNode); /** Checks that can withdraw funds from an object to itself or a destination. @@ -215,8 +223,8 @@ doWithdraw( * (if should not be skipped) and if the entry should be skipped. The status * is always tesSUCCESS if the entry should be skipped. */ -using EntryDeleter = std::function< - std::pair(LedgerEntryType, uint256 const&, std::shared_ptr&)>; +using EntryDeleter = + std::function(LedgerEntryType, uint256 const&, SLE::pointer&)>; /** Cleanup owner directory entries on account delete. * Used for a regular and AMM accounts deletion. The caller * has to provide the deleter function, which handles details of diff --git a/include/xrpl/ledger/detail/ApplyStateTable.h b/include/xrpl/ledger/detail/ApplyStateTable.h index 7b18f742b4..752c87d588 100644 --- a/include/xrpl/ledger/detail/ApplyStateTable.h +++ b/include/xrpl/ledger/detail/ApplyStateTable.h @@ -1,14 +1,25 @@ #pragma once +#include +#include #include #include #include #include +#include +#include +#include +#include +#include #include #include #include -#include +#include +#include +#include +#include +#include namespace xrpl::detail { @@ -26,7 +37,7 @@ private: Modify, }; - using items_t = std::map>>; + using items_t = std::map>; items_t items_; XRPAmount dropsDestroyed_{0}; @@ -60,10 +71,10 @@ public: [[nodiscard]] std::optional succ(ReadView const& base, key_type const& key, std::optional const& last) const; - [[nodiscard]] std::shared_ptr + [[nodiscard]] SLE::const_pointer read(ReadView const& base, Keylet const& k) const; - std::shared_ptr + SLE::pointer peek(ReadView const& base, Keylet const& k); [[nodiscard]] std::size_t @@ -75,23 +86,23 @@ public: std::function const& before, - std::shared_ptr const& after)> const& func) const; + SLE::const_ref before, + SLE::const_ref after)> const& func) const; void - erase(ReadView const& base, std::shared_ptr const& sle); + erase(ReadView const& base, SLE::ref sle); void - rawErase(ReadView const& base, std::shared_ptr const& sle); + rawErase(ReadView const& base, SLE::ref sle); void - insert(ReadView const& base, std::shared_ptr const& sle); + insert(ReadView const& base, SLE::ref sle); void - update(ReadView const& base, std::shared_ptr const& sle); + update(ReadView const& base, SLE::ref sle); void - replace(ReadView const& base, std::shared_ptr const& sle); + replace(ReadView const& base, SLE::ref sle); void destroyXRP(XRPAmount const& fee); @@ -104,12 +115,12 @@ public: } private: - using Mods = hash_map>; + using Mods = hash_map; static void - threadItem(TxMeta& meta, std::shared_ptr const& to); + threadItem(TxMeta& meta, SLE::ref to); - std::shared_ptr + SLE::pointer getForMod(ReadView const& base, key_type const& key, Mods& mods, beast::Journal j); void @@ -119,7 +130,7 @@ private: threadOwners( ReadView const& base, TxMeta& meta, - std::shared_ptr const& sle, + SLE::const_ref sle, Mods& mods, beast::Journal j); }; diff --git a/include/xrpl/ledger/detail/ApplyViewBase.h b/include/xrpl/ledger/detail/ApplyViewBase.h index 558c9e5d4d..b5b01de277 100644 --- a/include/xrpl/ledger/detail/ApplyViewBase.h +++ b/include/xrpl/ledger/detail/ApplyViewBase.h @@ -1,10 +1,20 @@ #pragma once +#include #include +#include #include #include +#include +#include +#include +#include +#include #include +#include +#include + namespace xrpl::detail { class ApplyViewBase : public ApplyView, public RawView @@ -40,7 +50,7 @@ public: [[nodiscard]] std::optional succ(key_type const& key, std::optional const& last = std::nullopt) const override; - [[nodiscard]] std::shared_ptr + [[nodiscard]] SLE::const_pointer read(Keylet const& k) const override; [[nodiscard]] std::unique_ptr @@ -69,28 +79,28 @@ public: [[nodiscard]] ApplyFlags flags() const override; - std::shared_ptr + SLE::pointer peek(Keylet const& k) override; void - erase(std::shared_ptr const& sle) override; + erase(SLE::ref sle) override; void - insert(std::shared_ptr const& sle) override; + insert(SLE::ref sle) override; void - update(std::shared_ptr const& sle) override; + update(SLE::ref sle) override; // RawView void - rawErase(std::shared_ptr const& sle) override; + rawErase(SLE::ref sle) override; void - rawInsert(std::shared_ptr const& sle) override; + rawInsert(SLE::ref sle) override; void - rawReplace(std::shared_ptr const& sle) override; + rawReplace(SLE::ref sle) override; void rawDestroyXRP(XRPAmount const& feeDrops) override; diff --git a/include/xrpl/ledger/detail/RawStateTable.h b/include/xrpl/ledger/detail/RawStateTable.h index 53532ee5ec..2e36a42eaf 100644 --- a/include/xrpl/ledger/detail/RawStateTable.h +++ b/include/xrpl/ledger/detail/RawStateTable.h @@ -1,12 +1,20 @@ #pragma once +#include +#include #include #include +#include +#include #include #include +#include +#include #include +#include +#include #include namespace xrpl::detail { @@ -22,14 +30,14 @@ public: static constexpr size_t kInitialBufferSize = kilobytes(256); RawStateTable() - : monotonic_resource_{std::make_unique( + : monotonicResource_{std::make_unique( kInitialBufferSize)} - , items_{monotonic_resource_.get()} {}; + , items_{monotonicResource_.get()} {}; RawStateTable(RawStateTable const& rhs) - : monotonic_resource_{std::make_unique( + : monotonicResource_{std::make_unique( kInitialBufferSize)} - , items_{rhs.items_, monotonic_resource_.get()} + , items_{rhs.items_, monotonicResource_.get()} , dropsDestroyed_{rhs.dropsDestroyed_} {}; RawStateTable(RawStateTable&&) = default; @@ -49,15 +57,15 @@ public: succ(ReadView const& base, key_type const& key, std::optional const& last) const; void - erase(std::shared_ptr const& sle); + erase(SLE::ref sle); void - insert(std::shared_ptr const& sle); + insert(SLE::ref sle); void - replace(std::shared_ptr const& sle); + replace(SLE::ref sle); - [[nodiscard]] std::shared_ptr + [[nodiscard]] SLE::const_pointer read(ReadView const& base, Keylet const& k) const; void @@ -84,10 +92,10 @@ private: struct SleAction { Action action; - std::shared_ptr sle; + SLE::pointer sle; // Constructor needed for emplacement in std::map - SleAction(Action action, std::shared_ptr const& sle) : action(action), sle(sle) + SleAction(Action action, SLE::pointer sle) : action(action), sle(std::move(sle)) { } }; @@ -97,11 +105,11 @@ private: using items_t = std::map< key_type, SleAction, - std::less, + std::less<>, boost::container::pmr::polymorphic_allocator>>; // monotonic_resource_ must outlive `items_`. Make a pointer so it may be // easily moved. - std::unique_ptr monotonic_resource_; + std::unique_ptr monotonicResource_; items_t items_; XRPAmount dropsDestroyed_{0}; diff --git a/include/xrpl/ledger/detail/ReadViewFwdRange.h b/include/xrpl/ledger/detail/ReadViewFwdRange.h index c548ccb101..19ac0698c2 100644 --- a/include/xrpl/ledger/detail/ReadViewFwdRange.h +++ b/include/xrpl/ledger/detail/ReadViewFwdRange.h @@ -1,8 +1,10 @@ #pragma once #include +#include #include #include +#include namespace xrpl { @@ -106,8 +108,8 @@ public: std::optional mutable cache_; }; - static_assert(std::is_nothrow_move_constructible{}, ""); - static_assert(std::is_nothrow_move_assignable{}, ""); + static_assert(std::is_nothrow_move_constructible{}); + static_assert(std::is_nothrow_move_assignable{}); using const_iterator = Iterator; diff --git a/include/xrpl/ledger/helpers/AMMHelpers.h b/include/xrpl/ledger/helpers/AMMHelpers.h index a146ef753b..c6a2053010 100644 --- a/include/xrpl/ledger/helpers/AMMHelpers.h +++ b/include/xrpl/ledger/helpers/AMMHelpers.h @@ -1,22 +1,37 @@ #pragma once -#include #include #include +#include #include +#include +#include #include #include -#include #include #include +#include #include +#include #include #include #include +#include #include #include #include #include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include namespace xrpl { @@ -36,6 +51,8 @@ reduceOffer(auto const& amount) enum class IsDeposit : bool { No = false, Yes = true }; +inline Number const kAMMInvariantRelativeTolerance{1, -11}; + /** Calculate LP Tokens given AMM pool reserves. * @param asset1 AMM one side of the pool reserve * @param asset2 AMM another side of the pool reserve @@ -737,11 +754,35 @@ ammPoolHolds( AuthHandling authHandling, beast::Journal const j); +/** Check AMM pool product invariant after an AMM operation that changes LP tokens + * (deposit/withdraw/clawback) from an already calculated pool product mean. + * Returns tecPRECISION_LOSS if poolProductMean < newLPTokenBalance beyond the + * invariant tolerance, + * tesSUCCESS otherwise. Skips check when newLPTokenBalance is zero (last withdrawal). + */ +TER +checkAMMPrecisionLoss(Number const& poolProductMean, STAmount const& newLPTokenBalance); + +/** Check AMM pool product invariant after an AMM operation that changes LP tokens + * (deposit/withdraw/clawback). + * Returns tecPRECISION_LOSS if sqrt(asset1 * asset2) < newLPTokenBalance beyond + * the invariant tolerance, + * tesSUCCESS otherwise. Skips check when newLPTokenBalance is zero (last withdrawal). + */ +TER +checkAMMPrecisionLoss( + ReadView const& view, + AccountID const& ammAccountID, + Asset const& asset1, + Asset const& asset2, + STAmount const& newLPTokenBalance, + beast::Journal const j); + /** Get AMM pool and LP token balances. If both optIssue are * provided then they are used as the AMM token pair issues. * Otherwise the missing issues are fetched from ammSle. */ -Expected, TER> +std::expected, TER> ammHolds( ReadView const& view, SLE const& ammSle, @@ -792,7 +833,7 @@ deleteAMMAccount(Sandbox& view, Asset const& asset, Asset const& asset2, beast:: void initializeFeeAuctionVote( ApplyView& view, - std::shared_ptr& ammSle, + SLE::pointer& ammSle, AccountID const& account, Asset const& lptAsset, std::uint16_t tfee); @@ -801,18 +842,18 @@ initializeFeeAuctionVote( * otherwise. Return tecINTERNAL if encountered an unexpected condition, * for instance Liquidity Provider has more than one LPToken trustline. */ -Expected +std::expected isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID const& lpAccount); /** Due to rounding, the LPTokenBalance of the last LP might * not match the LP's trustline balance. If it's within the tolerance, * update LPTokenBalance to match the LP's trustline balance. */ -Expected +std::expected verifyAndAdjustLPTokenBalance( Sandbox& sb, STAmount const& lpTokens, - std::shared_ptr& ammSle, + SLE::pointer& ammSle, AccountID const& account); } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index 353c27fe41..d0cfc175a3 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -1,15 +1,19 @@ #pragma once -#include +#include #include #include #include +#include #include #include +#include #include #include +#include -#include +#include +#include #include #include @@ -36,11 +40,7 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, /** Adjust the owner count up or down. */ void -adjustOwnerCount( - ApplyView& view, - std::shared_ptr const& sle, - std::int32_t amount, - beast::Journal j); +adjustOwnerCount(ApplyView& view, SLE::ref sle, std::int32_t amount, beast::Journal j); /** Returns IOU issuer transfer fee as Rate. Rate specifies * the fee as fractions of 1 billion. For example, 1% transfer rate @@ -76,9 +76,7 @@ getPseudoAccountFields(); - null pointer */ [[nodiscard]] bool -isPseudoAccount( - std::shared_ptr sleAcct, - std::set const& pseudoFieldFilter = {}); +isPseudoAccount(SLE::const_pointer sleAcct, std::set const& pseudoFieldFilter = {}); /** Convenience overload that reads the account from the view. */ [[nodiscard]] inline bool @@ -98,7 +96,7 @@ isPseudoAccount( * before using a field. The amendment check is **not** performed in * createPseudoAccount. */ -[[nodiscard]] Expected, TER> +[[nodiscard]] std::expected createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField); /** Checks the destination and tag. diff --git a/include/xrpl/ledger/helpers/CredentialHelpers.h b/include/xrpl/ledger/helpers/CredentialHelpers.h index e06d225934..8e78a00923 100644 --- a/include/xrpl/ledger/helpers/CredentialHelpers.h +++ b/include/xrpl/ledger/helpers/CredentialHelpers.h @@ -1,15 +1,22 @@ #pragma once -#include +#include #include +#include #include #include #include #include #include +#include #include +#include #include +#include +#include +#include + namespace xrpl { namespace credentials { @@ -23,7 +30,7 @@ checkExpired(SLE const& sleCredential, NetClock::time_point const& closed); // Actually remove a credentials object from the ledger [[nodiscard]] TER -deleteSLE(ApplyView& view, std::shared_ptr const& sleCredential, beast::Journal j); +deleteSLE(ApplyView& view, SLE::ref sleCredential, beast::Journal j); // Amendment and parameters checks for sfCredentialIDs field NotTEC @@ -36,13 +43,13 @@ checkFields(STTx const& tx, beast::Journal j); TER valid(STTx const& tx, ReadView const& view, AccountID const& src, beast::Journal j); -// Check if subject has any credential maching the given domain. If you call it +// Check if subject has any credential matching the given domain. If you call it // in preclaim and it returns tecEXPIRED, you should call verifyValidDomain in // doApply. This will ensure that expired credentials are deleted. TER validDomain(ReadView const& view, uint256 domainID, AccountID const& subject); -// This function is only called when we about to return tecNO_PERMISSION +// This function is only called when we are about to return tecNO_PERMISSION // because all the checks for the DepositPreauth authorization failed. TER authorizedDepositPreauth(ReadView const& view, STVector256 const& ctx, AccountID const& dst); @@ -58,11 +65,44 @@ checkArray(STArray const& credentials, unsigned maxSize, beast::Journal j); } // namespace credentials -// Check expired credentials and for credentials maching DomainID of the ledger +// Check expired credentials and for credentials matching DomainID of the ledger // object TER verifyValidDomain(ApplyView& view, AccountID const& account, uint256 domainID, beast::Journal j); +/** + * @brief Check whether src is authorized to deposit to dst. + * + * @param tx Transaction containing optional credential IDs. + * @param view Read-only ledger view. + * @param src Source account. + * @param dst Destination account. + * @param sleDst Destination AccountRoot, if it exists. + * @param j Journal for diagnostics. + * @return tesSUCCESS if the deposit is allowed, otherwise an authorization + * error. + */ +TER +checkDepositPreauth( + STTx const& tx, + ReadView const& view, + AccountID const& src, + AccountID const& dst, + std::shared_ptr const& sleDst, + beast::Journal j); + +/** + * @brief Remove expired credentials referenced by the transaction. + * + * @param tx Transaction containing optional sfCredentialIDs. + * @param view Mutable ledger view. + * @param j Journal for diagnostics. + * @return tesSUCCESS if no referenced credentials expired, tecEXPIRED if any + * were removed, or an error from credential deletion. + */ +TER +cleanupExpiredCredentials(STTx const& tx, ApplyView& view, beast::Journal j); + // Check expired credentials and for existing DepositPreauth ledger object TER verifyDepositPreauth( @@ -70,7 +110,7 @@ verifyDepositPreauth( ApplyView& view, AccountID const& src, AccountID const& dst, - std::shared_ptr const& sleDst, + SLE::const_ref sleDst, beast::Journal j); } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/DelegateHelpers.h b/include/xrpl/ledger/helpers/DelegateHelpers.h index 78ccc46d0b..3c277cb4f7 100644 --- a/include/xrpl/ledger/helpers/DelegateHelpers.h +++ b/include/xrpl/ledger/helpers/DelegateHelpers.h @@ -4,6 +4,9 @@ #include #include #include +#include + +#include namespace xrpl { @@ -15,7 +18,7 @@ namespace xrpl { * if not. */ NotTEC -checkTxPermission(std::shared_ptr const& delegate, STTx const& tx); +checkTxPermission(SLE::const_ref delegate, STTx const& tx); /** * Load the granular permissions granted to the delegate account for the @@ -23,13 +26,9 @@ checkTxPermission(std::shared_ptr const& delegate, STTx const& tx); * @param delegate The delegate account. * @param type Used to determine which granted granular permissions to load, * based on the transaction type. - * @param granularPermissions Granted granular permissions tied to the - * transaction type. + * @return the granted granular permissions tied to the transaction type. */ -void -loadGranularPermission( - std::shared_ptr const& delegate, - TxType const& type, - std::unordered_set& granularPermissions); +std::unordered_set +getGranularPermission(SLE::const_ref delegate, TxType const& type); } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/DirectoryHelpers.h b/include/xrpl/ledger/helpers/DirectoryHelpers.h index 2ae188182d..a95b9bc95a 100644 --- a/include/xrpl/ledger/helpers/DirectoryHelpers.h +++ b/include/xrpl/ledger/helpers/DirectoryHelpers.h @@ -1,12 +1,16 @@ #pragma once +#include #include #include #include +#include #include +#include +#include #include -#include +#include #include #include #include @@ -15,11 +19,7 @@ namespace xrpl { namespace detail { -template < - class V, - class N, - class = std::enable_if_t< - std::is_same_v, SLE> && std::is_base_of_v>> +template bool internalDirNext( V& view, @@ -27,6 +27,7 @@ internalDirNext( std::shared_ptr& page, unsigned int& index, uint256& entry) + requires(std::is_same_v, SLE> && std::is_base_of_v) { auto const& svIndexes = page->getFieldV256(sfIndexes); XRPL_ASSERT(index <= svIndexes.size(), "xrpl::detail::internalDirNext : index inside range"); @@ -64,11 +65,7 @@ internalDirNext( return true; } -template < - class V, - class N, - class = std::enable_if_t< - std::is_same_v, SLE> && std::is_base_of_v>> +template bool internalDirFirst( V& view, @@ -76,6 +73,7 @@ internalDirFirst( std::shared_ptr& page, unsigned int& index, uint256& entry) + requires(std::is_same_v, SLE> && std::is_base_of_v) { if constexpr (std::is_const_v) { @@ -115,7 +113,7 @@ bool cdirFirst( ReadView const& view, uint256 const& root, - std::shared_ptr& page, + SLE::const_pointer& page, unsigned int& index, uint256& entry); @@ -123,7 +121,7 @@ bool dirFirst( ApplyView& view, uint256 const& root, - std::shared_ptr& page, + SLE::pointer& page, unsigned int& index, uint256& entry); /** @} */ @@ -147,7 +145,7 @@ bool cdirNext( ReadView const& view, uint256 const& root, - std::shared_ptr& page, + SLE::const_pointer& page, unsigned int& index, uint256& entry); @@ -155,17 +153,14 @@ bool dirNext( ApplyView& view, uint256 const& root, - std::shared_ptr& page, + SLE::pointer& page, unsigned int& index, uint256& entry); /** @} */ /** Iterate all items in the given directory. */ void -forEachItem( - ReadView const& view, - Keylet const& root, - std::function const&)> const& f); +forEachItem(ReadView const& view, Keylet const& root, std::function const& f); /** Iterate all items after an item in the given directory. @param after The key of the item to start after @@ -180,14 +175,11 @@ forEachItemAfter( uint256 const& after, std::uint64_t const hint, unsigned int limit, - std::function const&)> const& f); + std::function const& f); /** Iterate all items in an account's owner directory. */ inline void -forEachItem( - ReadView const& view, - AccountID const& id, - std::function const&)> const& f) +forEachItem(ReadView const& view, AccountID const& id, std::function const& f) { forEachItem(view, keylet::ownerDir(id), f); } @@ -205,7 +197,7 @@ forEachItemAfter( uint256 const& after, std::uint64_t const hint, unsigned int limit, - std::function const&)> const& f) + std::function const& f) { return forEachItemAfter(view, keylet::ownerDir(id), after, hint, limit, f); } diff --git a/include/xrpl/ledger/helpers/EscrowHelpers.h b/include/xrpl/ledger/helpers/EscrowHelpers.h index 859981cf05..d173c33c56 100644 --- a/include/xrpl/ledger/helpers/EscrowHelpers.h +++ b/include/xrpl/ledger/helpers/EscrowHelpers.h @@ -1,15 +1,28 @@ #pragma once #include +#include #include -#include #include #include #include +#include +#include +#include #include #include -#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include + +#include namespace xrpl { @@ -18,7 +31,7 @@ TER escrowUnlockApplyHelper( ApplyView& view, Rate lockedRate, - std::shared_ptr const& sleDest, + SLE::ref sleDest, STAmount const& xrpBalance, STAmount const& amount, AccountID const& issuer, @@ -32,7 +45,7 @@ inline TER escrowUnlockApplyHelper( ApplyView& view, Rate lockedRate, - std::shared_ptr const& sleDest, + SLE::ref sleDest, STAmount const& xrpBalance, STAmount const& amount, AccountID const& issuer, @@ -41,8 +54,8 @@ escrowUnlockApplyHelper( bool createAsset, beast::Journal journal) { - Issue const& issue = amount.get(); - Keylet const trustLineKey = keylet::line(receiver, issue); + auto const& issue = amount.get(); + Keylet const trustLineKey = keylet::trustLine(receiver, issue); bool const recvLow = issuer > receiver; bool const senderIssuer = issuer == sender; bool const receiverIssuer = issuer == receiver; @@ -162,7 +175,7 @@ inline TER escrowUnlockApplyHelper( ApplyView& view, Rate lockedRate, - std::shared_ptr const& sleDest, + SLE::ref sleDest, STAmount const& xrpBalance, STAmount const& amount, AccountID const& issuer, @@ -175,7 +188,7 @@ escrowUnlockApplyHelper( bool const receiverIssuer = issuer == receiver; auto const mptID = amount.get().getMptID(); - auto const issuanceKey = keylet::mptIssuance(mptID); + auto const issuanceKey = keylet::mptokenIssuance(mptID); if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && createAsset && !receiverIssuer) { if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)}; diff --git a/include/xrpl/ledger/helpers/LendingHelpers.h b/include/xrpl/ledger/helpers/LendingHelpers.h index a6ab42254b..873abae272 100644 --- a/include/xrpl/ledger/helpers/LendingHelpers.h +++ b/include/xrpl/ledger/helpers/LendingHelpers.h @@ -1,11 +1,59 @@ #pragma once -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: keep +#include #include -#include +#include +#include +#include +#include // IWYU pragma: keep +#include +#include +#include + +#include +#include +#include +#include namespace xrpl { +/** + * Broker cover preclaim precision guard (fixCleanup3_2_0). + * + * Prevents a "silent sub-ULP no-op" where a deposit, withdrawal, or clawback + * amount is so small that it rounds to zero at `sfCoverAvailable`'s scale. + * Without this guard, both the pseudo trust-line and `sfCoverAvailable` would + * identically absorb the rounded zero, resulting in a successful transaction + * (tesSUCCESS) where no funds actually moved. + * + * @param view Read view (rules used for amendment gating). + * @param sleBroker The loan broker SLE (read-only). + * @param vaultAsset The underlying vault asset (the broker's cover asset). + * @param amount The effective subtraction/addition amount. + * @param j Journal for logging. + * @param logPrefix Transactor name for log diagnostics. + * + * @return `tecPRECISION_LOSS` if the request rounds to zero at cover scale. + * `tesSUCCESS` if the amendment is disabled or the request is safely supra-ULP. + */ +[[nodiscard]] TER +canApplyToBrokerCover( + ReadView const& view, + SLE::const_ref sleBroker, + Asset const& vaultAsset, + STAmount const& amount, + beast::Journal j, + std::string_view logPrefix); + // Lending protocol has dependencies, so capture them here. bool checkLendingProtocolDependencies(Rules const& rules, STTx const& tx); @@ -173,6 +221,21 @@ getAssetsTotalScale(SLE::const_ref vaultSle) return scale(vaultSle->at(sfAssetsTotal), vaultSle->at(sfAsset)); } +// Compute the minimum required broker cover, rounded consistently. +// DebtTotal is a broker-level aggregate maintained at vault scale, so the +// rounding must also use vault scale — never an individual loan's scale. +inline Number +minimumBrokerCover(Number const& debtTotal, TenthBips32 coverRateMinimum, SLE::const_ref vaultSle) +{ + XRPL_ASSERT( + vaultSle && vaultSle->getType() == ltVAULT, "xrpl::minimumBrokerCover : valid Vault sle"); + NumberRoundModeGuard const mg(Number::RoundingMode::Upward); + return roundToAsset( + vaultSle->at(sfAsset), + tenthBipsOfValue(debtTotal, coverRateMinimum), + getAssetsTotalScale(vaultSle)); +} + TER checkLoanGuards( Asset const& vaultAsset, @@ -352,7 +415,7 @@ struct LoanStateDeltas nonNegative(); }; -Expected, TER> +std::expected, TER> tryOverpayment( Rules const& rules, Asset const& asset, @@ -416,6 +479,7 @@ loanAccruedInterest( ExtendedPaymentComponents computeOverpaymentComponents( + Rules const& rules, Asset const& asset, int32_t const loanScale, Number const& overpayment, @@ -477,7 +541,7 @@ isRounded(Asset const& asset, Number const& value, std::int32_t scale); // potential extra work at the end. enum class LoanPaymentType { Regular = 0, Late, Full, Overpayment }; -Expected +std::expected loanMakePayment( Asset const& asset, ApplyView& view, diff --git a/include/xrpl/ledger/helpers/MPTokenHelpers.h b/include/xrpl/ledger/helpers/MPTokenHelpers.h index 6544b18dd1..a725871231 100644 --- a/include/xrpl/ledger/helpers/MPTokenHelpers.h +++ b/include/xrpl/ledger/helpers/MPTokenHelpers.h @@ -4,11 +4,17 @@ #include #include #include +#include +#include #include #include #include +#include #include +#include +#include +#include #include #include @@ -23,18 +29,31 @@ namespace xrpl { [[nodiscard]] bool isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue); +/** Returns true if @p account's MPToken for @p mptIssue carries the + * individual-lock flag (lsfMPTLocked). + * + * @warning This checks only the raw per-holder lock bit. It does **not** + * perform the transitive vault pseudo-account check: if @p mptIssue is a + * vault share whose underlying asset is frozen, this function returns false. + * Call @ref isFrozen instead when determining whether an account may send or + * receive tokens — it combines isIndividualFrozen, isGlobalFrozen, and + * isVaultPseudoAccountFrozen into a single complete check. */ [[nodiscard]] bool isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue); [[nodiscard]] bool -isFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth = 0); +isFrozen( + ReadView const& view, + AccountID const& account, + MPTIssue const& mptIssue, + std::uint8_t depth = 0); [[nodiscard]] bool isAnyFrozen( ReadView const& view, std::initializer_list const& accounts, MPTIssue const& mptIssue, - int depth = 0); + std::uint8_t depth = 0); //------------------------------------------------------------------------------ // @@ -88,7 +107,7 @@ requireAuth( MPTIssue const& mptIssue, AccountID const& account, AuthType authType = AuthType::Legacy, - int depth = 0); + std::uint8_t depth = 0); /** Enforce account has MPToken to match its authorization. * @@ -104,22 +123,77 @@ enforceMPTokenAuthorization( XRPAmount const& priorBalance, beast::Journal j); -/** Check if the destination account is allowed - * to receive MPT. Return tecNO_AUTH if it doesn't - * and tesSUCCESS otherwise. +/** Resolve the underlying asset of a vault share. + * + * Reads sfReferenceHolding from @p sleShareIssuance to determine which + * asset the vault wraps. @p sleHolding must be the SLE that + * sfReferenceHolding points to — either an ltMPTOKEN (returns its + * MPTIssue) or an ltRIPPLE_STATE (returns its low/high Issue). + * + * @pre Both SLEs must exist and @p sleHolding must be of type ltMPTOKEN + * or ltRIPPLE_STATE. Passing any other type is undefined behaviour. + * @param sleShareIssuance MPTokenIssuance SLE for the vault share token. + * @param sleHolding SLE referenced by sfReferenceHolding. + * @return The underlying Asset (MPTIssue or Issue). + */ +[[nodiscard]] Asset +assetOfHolding(SLE const& sleShareIssuance, SLE const& sleHolding); + +/** Check whether @p to may receive the given MPT from @p from. + * + * The check passes when any of the following is true: + * - @p waive is WaiveMPTCanTransfer::Yes (recovery-path exemption), or + * - @p from or @p to is the issuer, or + * - lsfMPTCanTransfer is set on the MPTokenIssuance. + * + * For vault shares (MPTokenIssuances that carry sfReferenceHolding) the + * check recurses into the underlying asset's transferability. This + * recursion is defensive; vault-of-vault-shares is rejected at vault + * creation, so in practice depth never exceeds 1. + * + * @param view Ledger state to read from. + * @param mptIssue The MPT issuance being transferred. + * @param from Sending account. + * @param to Receiving account. + * @param waive WaiveMPTCanTransfer::Yes skips the lsfMPTCanTransfer + * check. Use for recovery paths (e.g. unwinding SAV or + * Lending Protocol positions after an issuer revokes + * transferability). + * @param depth Recursion depth; bounded at kMaxAssetCheckDepth. + * @return tesSUCCESS if the transfer is allowed, tecNO_AUTH otherwise. */ [[nodiscard]] TER canTransfer( ReadView const& view, MPTIssue const& mptIssue, AccountID const& from, - AccountID const& to); + AccountID const& to, + WaiveMPTCanTransfer waive = WaiveMPTCanTransfer::No, + std::uint8_t depth = 0); -/** Check if Asset can be traded on DEX. return tecNO_PERMISSION - * if it doesn't and tesSUCCESS otherwise. +/** Check whether @p asset may be traded on the DEX. + * + * For IOU assets the check delegates to the existing offer/AMM freeze + * logic. For MPT assets it checks lsfMPTCanTrade on the MPTokenIssuance. + * Vault shares recurse into the underlying asset's tradability via + * sfReferenceHolding; depth is bounded at kMaxAssetCheckDepth. + * + * @param view Ledger state to read from. + * @param asset The asset to check. + * @param depth Recursion depth; bounded at kMaxAssetCheckDepth. + * @return tesSUCCESS if trading is allowed, tecNO_PERMISSION otherwise. */ [[nodiscard]] TER -canTrade(ReadView const& view, Asset const& asset); +canTrade(ReadView const& view, Asset const& asset, std::uint8_t depth = 0); + +/** Convenience to combine canTrade/Transfer. Returns tesSUCCESS if Asset is Issue. + */ +[[nodiscard]] TER +canMPTTradeAndTransfer( + ReadView const& v, + Asset const& asset, + AccountID const& from, + AccountID const& to); //------------------------------------------------------------------------------ // @@ -227,17 +301,4 @@ issuerFundsToSelfIssue(ReadView const& view, MPTIssue const& issue); void issuerSelfDebitHookMPT(ApplyView& view, MPTIssue const& issue, std::uint64_t amount); -//------------------------------------------------------------------------------ -// -// MPT DEX -// -//------------------------------------------------------------------------------ - -/* Return true if a transaction is allowed for the specified MPT/account. The - * function checks MPTokenIssuance and MPToken objects flags to determine if the - * transaction is allowed. - */ -TER -checkMPTTxAllowed(ReadView const& v, TxType tx, Asset const& asset, AccountID const& accountID); - } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/NFTokenHelpers.h b/include/xrpl/ledger/helpers/NFTokenHelpers.h index 4294e1ca13..1c4d395fbe 100644 --- a/include/xrpl/ledger/helpers/NFTokenHelpers.h +++ b/include/xrpl/ledger/helpers/NFTokenHelpers.h @@ -1,13 +1,25 @@ #pragma once -#include +#include #include +#include #include +#include #include +#include +#include +#include +#include +#include +#include +#include #include #include -#include +#include +#include +#include +#include #include namespace xrpl::nft { @@ -28,10 +40,9 @@ findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID struct TokenAndPage { STObject token; - std::shared_ptr page; + SLE::pointer page; - TokenAndPage(STObject token, std::shared_ptr page) - : token(std::move(token)), page(std::move(page)) + TokenAndPage(STObject token, SLE::pointer page) : token(std::move(token)), page(std::move(page)) { } }; @@ -47,11 +58,7 @@ TER removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID); TER -removeToken( - ApplyView& view, - AccountID const& owner, - uint256 const& nftokenID, - std::shared_ptr const& page); +removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, SLE::ref page); /** Deletes the given token offer. @@ -63,7 +70,7 @@ removeToken( The offer also consumes one incremental reserve. */ bool -deleteTokenOffer(ApplyView& view, std::shared_ptr const& offer); +deleteTokenOffer(ApplyView& view, SLE::ref offer); /** Repairs the links in an NFTokenPage directory. diff --git a/include/xrpl/ledger/helpers/OfferHelpers.h b/include/xrpl/ledger/helpers/OfferHelpers.h index 9096071811..fc863dff0a 100644 --- a/include/xrpl/ledger/helpers/OfferHelpers.h +++ b/include/xrpl/ledger/helpers/OfferHelpers.h @@ -5,8 +5,6 @@ #include #include -#include - namespace xrpl { /** Delete an offer. @@ -23,6 +21,6 @@ namespace xrpl { */ // [[nodiscard]] // nodiscard commented out so Flow, BookTip and others compile. TER -offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j); +offerDelete(ApplyView& view, SLE::ref sle, beast::Journal j); } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/PaymentChannelHelpers.h b/include/xrpl/ledger/helpers/PaymentChannelHelpers.h index 24838f1331..6e8cd17f7f 100644 --- a/include/xrpl/ledger/helpers/PaymentChannelHelpers.h +++ b/include/xrpl/ledger/helpers/PaymentChannelHelpers.h @@ -1,17 +1,50 @@ #pragma once +#include #include #include +#include +#include #include -#include + +#include +#include namespace xrpl { +/** Close a payment channel and return its remaining funds to the channel owner. + * + * @param slep The SLE for the PayChannel object to close. + * @param view The apply view in which ledger state modifications are made. + * @param key The ledger key identifying the PayChannel entry. + * @param j Journal used for fatal-level diagnostic messages. + * @return tesSUCCESS on success; tefBAD_LEDGER if a directory removal + * fails; tefINTERNAL if the source account SLE cannot be found. + */ TER -closeChannel( - std::shared_ptr const& slep, - ApplyView& view, - uint256 const& key, - beast::Journal j); +closeChannel(SLE::ref slep, ApplyView& view, uint256 const& key, beast::Journal j); + +/** Add two uint32_t values with saturation at UINT32_MAX. + * + * @param rules The current ledger rules used to check amendment status. + * @param lhs Left-hand operand. + * @param rhs Right-hand operand. + * @return @p lhs + @p rhs, saturated at UINT32_MAX when the amendment + * is active. + */ +uint32_t +saturatingAdd(Rules const& rules, uint32_t const lhs, uint32_t const rhs); + +/** Determine whether a payment channel time field represents an expired time. + * + * @param view The apply view providing the parent close time and rules. + * @param timeField The optional expiry timestamp (seconds since the XRP + * Ledger epoch). If empty, the function returns false. + * @return @c true if @p timeField is set and the indicated time is + * in the past relative to the view's parent close time; + * @c false otherwise. + */ +bool +isChannelExpired(ApplyView const& view, std::optional timeField); } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/PermissionedDEXHelpers.h b/include/xrpl/ledger/helpers/PermissionedDEXHelpers.h index 04b12f2fc5..12681257aa 100644 --- a/include/xrpl/ledger/helpers/PermissionedDEXHelpers.h +++ b/include/xrpl/ledger/helpers/PermissionedDEXHelpers.h @@ -1,5 +1,10 @@ #pragma once -#include + +#include +#include +#include +#include +#include namespace xrpl::permissioned_dex { diff --git a/include/xrpl/ledger/helpers/RippleStateHelpers.h b/include/xrpl/ledger/helpers/RippleStateHelpers.h index 17b0f7673e..ab09b931cc 100644 --- a/include/xrpl/ledger/helpers/RippleStateHelpers.h +++ b/include/xrpl/ledger/helpers/RippleStateHelpers.h @@ -1,14 +1,21 @@ #pragma once +#include #include #include #include #include +#include #include #include #include #include #include +#include +#include + +#include +#include //------------------------------------------------------------------------------ // @@ -93,7 +100,7 @@ isFrozen(ReadView const& view, AccountID const& account, Issue const& issue) // Overload with depth parameter for uniformity with MPTIssue version. // The depth parameter is ignored for IOUs since they don't have vault recursion. [[nodiscard]] inline bool -isFrozen(ReadView const& view, AccountID const& account, Issue const& issue, int /*depth*/) +isFrozen(ReadView const& view, AccountID const& account, Issue const& issue, std::uint8_t /*depth*/) { return isFrozen(view, account, issue); } @@ -110,7 +117,7 @@ isDeepFrozen( ReadView const& view, AccountID const& account, Issue const& issue, - int = 0 /*ignored*/) + std::uint8_t = 0 /*ignored*/) { return isDeepFrozen(view, account, issue.currency, issue.account); } @@ -154,7 +161,7 @@ trustCreate( [[nodiscard]] TER trustDelete( ApplyView& view, - std::shared_ptr const& sleRippleState, + SLE::ref sleRippleState, AccountID const& uLowAccountID, AccountID const& uHighAccountID, beast::Journal j); @@ -248,7 +255,7 @@ removeEmptyHolding( [[nodiscard]] TER deleteAMMTrustLine( ApplyView& view, - std::shared_ptr sleState, + SLE::pointer sleState, std::optional const& ammAccountID, beast::Journal j); @@ -258,7 +265,7 @@ deleteAMMTrustLine( [[nodiscard]] TER deleteAMMMPToken( ApplyView& view, - std::shared_ptr sleMPT, + SLE::pointer sleMPT, AccountID const& ammAccountID, beast::Journal j); diff --git a/include/xrpl/ledger/helpers/TokenHelpers.h b/include/xrpl/ledger/helpers/TokenHelpers.h index 3d41ac47cd..32f785a0d6 100644 --- a/include/xrpl/ledger/helpers/TokenHelpers.h +++ b/include/xrpl/ledger/helpers/TokenHelpers.h @@ -1,15 +1,22 @@ #pragma once +#include #include #include #include +#include #include +#include #include #include #include #include +#include +#include +#include #include +#include #include namespace xrpl { @@ -34,6 +41,15 @@ enum class WaiveTransferFee : bool { No = false, Yes }; /** Controls whether accountSend is allowed to overflow OutstandingAmount **/ enum class AllowMPTOverflow : bool { No = false, Yes }; +/** Controls whether canTransfer enforces lsfMPTCanTransfer on MPTs. + * + * Default is No (enforce). Use Yes at call sites that must remain available + * even when an MPT issuer has cleared lsfMPTCanTransfer - for example, + * unwinding existing positions in SAV or the Lending Protocol. Has no + * effect on the IOU branch of canTransfer. + */ +enum class WaiveMPTCanTransfer : bool { No = false, Yes }; + /* Check if MPToken (for MPT) or trust line (for IOU) exists: * - StrongAuth - before checking if authorization is required * - WeakAuth @@ -54,16 +70,26 @@ enum class AuthType { StrongAuth, WeakAuth, Legacy }; [[nodiscard]] bool isGlobalFrozen(ReadView const& view, Asset const& asset); +[[nodiscard]] TER +checkGlobalFrozen(ReadView const& view, Asset const& asset); + [[nodiscard]] bool isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset); +[[nodiscard]] TER +checkIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset); + /** * isFrozen check is recursive for MPT shares in a vault, descending to * assets in the vault, up to maxAssetCheckDepth recursion depth. This is * purely defensive, as we currently do not allow such vaults to be created. */ [[nodiscard]] bool -isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0); +isFrozen( + ReadView const& view, + AccountID const& account, + Asset const& asset, + std::uint8_t depth = 0); [[nodiscard]] TER checkFrozen(ReadView const& view, AccountID const& account, Issue const& issue); @@ -85,14 +111,14 @@ isAnyFrozen( ReadView const& view, std::initializer_list const& accounts, Asset const& asset, - int depth = 0); + std::uint8_t depth = 0); [[nodiscard]] bool isDeepFrozen( ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, - int depth = 0); + std::uint8_t depth = 0); /** * isFrozen check is recursive for MPT shares in a vault, descending to @@ -100,7 +126,11 @@ isDeepFrozen( * purely defensive, as we currently do not allow such vaults to be created. */ [[nodiscard]] bool -isDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0); +isDeepFrozen( + ReadView const& view, + AccountID const& account, + Asset const& asset, + std::uint8_t depth = 0); [[nodiscard]] TER checkDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue); @@ -108,6 +138,71 @@ checkDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& [[nodiscard]] TER checkDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset); +/** + * Checks freeze compliance for withdrawing an asset from a pseudo-account (e.g. Vault, AMM, + * LoanBroker) to a destination account. + * + * Asserts that sourceAcct is a pseudo-account and that submitterAcct and dstAcct are not. + * + * Issuer exemption: returns tesSUCCESS immediately when dstAcct is the asset issuer — the issuer + * can always receive their own token, even when the pool is frozen. Callers that need to block + * withdrawals from a frozen pool even for the issuer (e.g. because the pool math cannot handle it) + * must check checkFrozen(sourceAcct, asset) separately before calling this function. + * + * Otherwise checks, in order: + * 1. If the asset is globally frozen the remaining checks are redundant. + * 2. The pseudo-account's trustline / MPToken must not be individually frozen for sending. + * 3. The submitter's trustline / MPToken must not be individually frozen. Skipped when + * submitter == dst (self-withdrawal) so a regular freeze does not prevent recovering one's own + * funds. (Enforced as defensive code; no current caller exercises a frozen submitter ≠ dst.) + * 4. The destination must not be deep-frozen. + * + * For IOUs a regular individual freeze on the submitter does NOT block self-withdrawal; only deep + * freeze does. For MPTs "locked" is equivalent to deep-frozen, so locked MPT holders are always + * blocked. + * + * @param view Ledger view to read freeze state from. + * @param pseudoAcct Pseudo-account the funds are withdrawn from (sender). + * @param submitterAcct Account that submitted the withdrawal transaction. + * @param dstAcct Account receiving the withdrawn funds. + * @param asset Asset being withdrawn. + * @return tesSUCCESS if the withdrawal is permitted, otherwise a freeze + * result (tecFROZEN for IOUs, tecLOCKED for MPTs). + */ +[[nodiscard]] TER +checkWithdrawFreeze( + ReadView const& view, + AccountID const& pseudoAcct, + AccountID const& submitterAcct, + AccountID const& dstAcct, + Asset const& asset); + +/** + * Checks freeze compliance for depositing an asset into a pseudo-account (e.g. Vault, AMM, + * LoanBroker). + * + * Checks, in order: + * 1. If the asset is globally frozen the remaining checks are redundant. + * 2. The depositor must not be individually frozen for the asset. Skipped when srcAcct is the + * asset issuer, since the issuer can always send its own asset. + * 3. The pseudo-account must not be individually frozen for the asset. Unlike regular accounts, + * pseudo-accounts cannot receive deposits under a regular freeze because the deposited funds + * could not later be withdrawn. + * + * @param view Ledger view to read freeze state from. + * @param srcAcct Depositor sending the funds. + * @param pseudoAcct Pseudo-account receiving the deposit. + * @param asset Asset being deposited. + * @return tesSUCCESS if the deposit is permitted, otherwise a freeze result + * (tecFROZEN for IOUs, tecLOCKED for MPTs). + */ +[[nodiscard]] TER +checkDepositFreeze( + ReadView const& view, + AccountID const& srcAcct, + AccountID const& pseudoAcct, + Asset const& asset); + //------------------------------------------------------------------------------ // // Account balance functions (Asset-based dispatchers) @@ -234,7 +329,13 @@ requireAuth( AuthType authType = AuthType::Legacy); [[nodiscard]] TER -canTransfer(ReadView const& view, Asset const& asset, AccountID const& from, AccountID const& to); +canTransfer( + ReadView const& view, + Asset const& asset, + AccountID const& from, + AccountID const& to, + WaiveMPTCanTransfer waive = WaiveMPTCanTransfer::No, + std::uint8_t depth = 0); //------------------------------------------------------------------------------ // diff --git a/include/xrpl/ledger/helpers/VaultHelpers.h b/include/xrpl/ledger/helpers/VaultHelpers.h index 14b0c004cb..2344b4de77 100644 --- a/include/xrpl/ledger/helpers/VaultHelpers.h +++ b/include/xrpl/ledger/helpers/VaultHelpers.h @@ -1,9 +1,10 @@ #pragma once +#include +#include #include #include -#include #include namespace xrpl { @@ -19,10 +20,7 @@ namespace xrpl { @return The number of shares, or nullopt on error. */ [[nodiscard]] std::optional -assetsToSharesDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& assets); +assetsToSharesDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& assets); /** From the perspective of a vault, return the number of assets to take from depositor when they receive a fixed amount of shares. Note, since shares are @@ -35,14 +33,19 @@ assetsToSharesDeposit( @return The number of assets, or nullopt on error. */ [[nodiscard]] std::optional -sharesToAssetsDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& shares); +sharesToAssetsDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& shares); /** Controls whether to truncate shares instead of rounding. */ enum class TruncateShares : bool { No = false, Yes = true }; +/** Controls whether the withdraw conversion helpers + (assetsToSharesWithdraw and sharesToAssetsWithdraw) subtract + sfLossUnrealized from sfAssetsTotal before computing the exchange rate. + The default (No) applies the standard discounted rate; Yes is used when + the redeemer is the sole remaining shareholder. +*/ +enum class WaiveUnrealizedLoss : bool { No = false, Yes = true }; + /** From the perspective of a vault, return the number of shares to demand from the depositor when they ask to withdraw a fixed amount of assets. Since shares are MPT this number is integral, and it will be rounded to nearest @@ -52,15 +55,18 @@ enum class TruncateShares : bool { No = false, Yes = true }; @param issuance The MPTokenIssuance SLE for the vault's shares. @param assets The amount of assets to convert. @param truncate Whether to truncate instead of rounding. + @param waive Whether to waive the unrealized-loss discount when computing + the exchange rate. @return The number of shares, or nullopt on error. */ [[nodiscard]] std::optional assetsToSharesWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, + SLE::const_ref vault, + SLE::const_ref issuance, STAmount const& assets, - TruncateShares truncate = TruncateShares::No); + TruncateShares truncate = TruncateShares::No, + WaiveUnrealizedLoss waive = WaiveUnrealizedLoss::No); /** From the perspective of a vault, return the number of assets to give the depositor when they redeem a fixed amount of shares. Note, since shares are @@ -69,13 +75,28 @@ assetsToSharesWithdraw( @param vault The vault SLE. @param issuance The MPTokenIssuance SLE for the vault's shares. @param shares The amount of shares to convert. + @param waive Whether to waive (i.e. not subtract) the vault's unrealized + loss when computing the exchange rate. @return The number of assets, or nullopt on error. */ [[nodiscard]] std::optional sharesToAssetsWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& shares); + SLE::const_ref vault, + SLE::const_ref issuance, + STAmount const& shares, + WaiveUnrealizedLoss waive = WaiveUnrealizedLoss::No); + +/** Returns true iff `account` holds all of the vault's outstanding shares — + i.e. is the sole remaining shareholder. Returns false if the account + holds no shares or fewer than the total outstanding. + + @param view The ledger view. + @param account The candidate sole shareholder. + @param issuance The MPTokenIssuance SLE for the vault's shares; provides + both the share MPTID and the outstanding-amount total. +*/ +[[nodiscard]] bool +isSoleShareholder(ReadView const& view, AccountID const& account, SLE::const_ref issuance); } // namespace xrpl diff --git a/include/xrpl/net/AutoSocket.h b/include/xrpl/net/AutoSocket.h index 16ed0d6ca9..b98885959d 100644 --- a/include/xrpl/net/AutoSocket.h +++ b/include/xrpl/net/AutoSocket.h @@ -2,12 +2,20 @@ #include #include +#include #include #include #include #include +#include +#include +#include +#include +#include +#include + // Socket wrapper that supports both SSL and non-SSL connections. // Generally, handle it as you would an SSL connection. // To force a non-SSL connection, just don't call async_handshake. @@ -112,12 +120,9 @@ public: socket_->next_layer().async_receive( boost::asio::buffer(buffer_), boost::asio::socket_base::message_peek, - std::bind( - &AutoSocket::handleAutodetect, - this, - cbFunc, - std::placeholders::_1, - std::placeholders::_2)); + [this, cbFunc](error_code const& ec, size_t bytesTransferred) { + handleAutodetect(cbFunc, ec, bytesTransferred); + }); } } diff --git a/include/xrpl/net/HTTPClient.h b/include/xrpl/net/HTTPClient.h index f059b19047..7ed9b35b9b 100644 --- a/include/xrpl/net/HTTPClient.h +++ b/include/xrpl/net/HTTPClient.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -53,7 +54,7 @@ public: boost::system::error_code const& ecResult, int iStatus, std::string const& strData)> complete, - beast::Journal& j); + beast::Journal const& j); static void get(bool bSSL, @@ -67,7 +68,7 @@ public: boost::system::error_code const& ecResult, int iStatus, std::string const& strData)> complete, - beast::Journal& j); + beast::Journal const& j); static void request( @@ -82,7 +83,7 @@ public: boost::system::error_code const& ecResult, int iStatus, std::string const& strData)> complete, - beast::Journal& j); + beast::Journal const& j); }; } // namespace xrpl diff --git a/include/xrpl/net/HTTPClientSSLContext.h b/include/xrpl/net/HTTPClientSSLContext.h index d1744bbce4..51b50a084c 100644 --- a/include/xrpl/net/HTTPClientSSLContext.h +++ b/include/xrpl/net/HTTPClientSSLContext.h @@ -10,6 +10,13 @@ #include #include +#include +#include + +#include +#include +#include + namespace xrpl { class HTTPClientSSLContext @@ -21,13 +28,13 @@ public: bool sslVerify, beast::Journal j, boost::asio::ssl::context_base::method method = boost::asio::ssl::context::sslv23) - : ssl_context_{method}, j_(j), verify_{sslVerify} + : sslContext_{method}, j_(j), verify_{sslVerify} { boost::system::error_code ec; if (sslVerifyFile.empty()) { - registerSSLCerts(ssl_context_, ec, j_); + registerSSLCerts(sslContext_, ec, j_); if (ec && sslVerifyDir.empty()) { @@ -37,12 +44,12 @@ public: } else { - ssl_context_.load_verify_file(sslVerifyFile); + sslContext_.load_verify_file(sslVerifyFile); } if (!sslVerifyDir.empty()) { - ssl_context_.add_verify_path(sslVerifyDir, ec); + sslContext_.add_verify_path(sslVerifyDir, ec); if (ec) { @@ -55,7 +62,7 @@ public: boost::asio::ssl::context& context() { - return ssl_context_; + return sslContext_; } [[nodiscard]] bool @@ -76,13 +83,12 @@ public: * * @return error_code indicating failures, if any */ - template < - class T, - class = std::enable_if_t< - std::is_same_v> || - std::is_same_v>>> + template boost::system::error_code preConnectVerify(T& strm, std::string const& host) + requires( + std::is_same_v> || + std::is_same_v>) { boost::system::error_code ec; if (!SSL_set_tlsext_host_name(strm.native_handle(), host.c_str())) @@ -96,11 +102,7 @@ public: return ec; } - template < - class T, - class = std::enable_if_t< - std::is_same_v> || - std::is_same_v>>> + template /** * @brief invoked after connect/async_connect but before sending data * on an ssl stream - to setup name verification. @@ -110,6 +112,9 @@ public: */ boost::system::error_code postConnectVerify(T& strm, std::string const& host) + requires( + std::is_same_v> || + std::is_same_v>) { boost::system::error_code ec; @@ -119,8 +124,9 @@ public: if (!ec) { strm.set_verify_callback( - std::bind( - &rfc6125Verify, host, std::placeholders::_1, std::placeholders::_2, j_), + [host, j = j_](bool preverified, boost::asio::ssl::verify_context& ctx) { + return rfc6125Verify(host, preverified, ctx, j); + }, ec); } } @@ -153,7 +159,7 @@ public: } private: - boost::asio::ssl::context ssl_context_; + boost::asio::ssl::context sslContext_; beast::Journal const j_; bool const verify_; }; diff --git a/include/xrpl/net/RegisterSSLCerts.h b/include/xrpl/net/RegisterSSLCerts.h index e313b1cb06..5cc9934638 100644 --- a/include/xrpl/net/RegisterSSLCerts.h +++ b/include/xrpl/net/RegisterSSLCerts.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include diff --git a/include/xrpl/nodestore/Backend.h b/include/xrpl/nodestore/Backend.h index 124cd99db5..29c4a8b526 100644 --- a/include/xrpl/nodestore/Backend.h +++ b/include/xrpl/nodestore/Backend.h @@ -1,8 +1,17 @@ #pragma once +#include +#include +#include #include +#include #include +#include +#include +#include +#include +#include namespace xrpl::NodeStore { @@ -83,10 +92,6 @@ public: virtual Status fetch(uint256 const& hash, std::shared_ptr* pObject) = 0; - /** Fetch a batch synchronously. */ - virtual std::pair>, Status> - fetchBatch(std::vector const& hashes) = 0; - /** Store a single object. Depending on the implementation this may happen immediately or deferred using a scheduled task. diff --git a/include/xrpl/nodestore/Database.h b/include/xrpl/nodestore/Database.h index ca2dde560c..49002ee301 100644 --- a/include/xrpl/nodestore/Database.h +++ b/include/xrpl/nodestore/Database.h @@ -1,14 +1,29 @@ #pragma once -#include -#include -#include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include #include #include #include -#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { +class Section; +} // namespace xrpl namespace xrpl::NodeStore { @@ -131,6 +146,10 @@ public: std::uint32_t ledgerSeq, std::function const&)>&& callback); + /** Remove expired entries from the positive and negative caches. */ + virtual void + sweep() = 0; + /** Gather statistics pertaining to read and write activities. * * @param obj Json object reference into which to place counters. diff --git a/include/xrpl/nodestore/DatabaseRotating.h b/include/xrpl/nodestore/DatabaseRotating.h index a7deed294a..69eb31261d 100644 --- a/include/xrpl/nodestore/DatabaseRotating.h +++ b/include/xrpl/nodestore/DatabaseRotating.h @@ -1,6 +1,13 @@ #pragma once +#include +#include #include +#include + +#include +#include +#include namespace xrpl::NodeStore { diff --git a/include/xrpl/nodestore/DummyScheduler.h b/include/xrpl/nodestore/DummyScheduler.h index 472684ff13..f626115786 100644 --- a/include/xrpl/nodestore/DummyScheduler.h +++ b/include/xrpl/nodestore/DummyScheduler.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace xrpl::NodeStore { diff --git a/include/xrpl/nodestore/Factory.h b/include/xrpl/nodestore/Factory.h index c40be62d21..e79ae3e05d 100644 --- a/include/xrpl/nodestore/Factory.h +++ b/include/xrpl/nodestore/Factory.h @@ -1,11 +1,18 @@ #pragma once -#include #include #include #include -#include +#include + +#include +#include +#include + +namespace xrpl { +class Section; +} // namespace xrpl namespace xrpl::NodeStore { diff --git a/include/xrpl/nodestore/Manager.h b/include/xrpl/nodestore/Manager.h index 1c4e5b63cf..f813412846 100644 --- a/include/xrpl/nodestore/Manager.h +++ b/include/xrpl/nodestore/Manager.h @@ -1,7 +1,14 @@ #pragma once -#include +#include +#include +#include #include +#include + +#include +#include +#include namespace xrpl::NodeStore { diff --git a/include/xrpl/nodestore/NodeObject.h b/include/xrpl/nodestore/NodeObject.h index 04ba391b2b..3f3b75d5f8 100644 --- a/include/xrpl/nodestore/NodeObject.h +++ b/include/xrpl/nodestore/NodeObject.h @@ -4,6 +4,10 @@ #include #include +#include +#include +#include + // VFALCO NOTE Intentionally not in the NodeStore namespace namespace xrpl { diff --git a/include/xrpl/nodestore/Types.h b/include/xrpl/nodestore/Types.h index 21c01e9111..eaee82c99e 100644 --- a/include/xrpl/nodestore/Types.h +++ b/include/xrpl/nodestore/Types.h @@ -2,6 +2,7 @@ #include +#include #include namespace xrpl::NodeStore { diff --git a/include/xrpl/nodestore/detail/BatchWriter.h b/include/xrpl/nodestore/detail/BatchWriter.h index b0383838dc..7fa23bcb3e 100644 --- a/include/xrpl/nodestore/detail/BatchWriter.h +++ b/include/xrpl/nodestore/detail/BatchWriter.h @@ -1,10 +1,12 @@ #pragma once +#include #include #include #include #include +#include #include namespace xrpl::NodeStore { diff --git a/include/xrpl/nodestore/detail/DatabaseNodeImp.h b/include/xrpl/nodestore/detail/DatabaseNodeImp.h index add66c51a7..6f2fca682f 100644 --- a/include/xrpl/nodestore/detail/DatabaseNodeImp.h +++ b/include/xrpl/nodestore/detail/DatabaseNodeImp.h @@ -1,8 +1,26 @@ #pragma once +#include #include +#include #include +#include +#include +#include +#include +#include +#include #include +#include +#include + +#include +#include +#include +#include +#include +#include +#include namespace xrpl::NodeStore { @@ -22,6 +40,32 @@ public: beast::Journal j) : Database(scheduler, readThreads, config, j), backend_(std::move(backend)) { + std::optional cacheSize, cacheAge; + + if (config.exists(Keys::kCacheSize)) + { + cacheSize = get(config, Keys::kCacheSize); + if (cacheSize.value() < 0) + Throw("Specified negative value for cache_size"); + } + + if (config.exists(Keys::kCacheAge)) + { + cacheAge = get(config, Keys::kCacheAge); + if (cacheAge.value() < 0) + Throw("Specified negative value for cache_age"); + } + + if (cacheSize.has_value() || cacheAge.has_value()) + { + cache_ = std::make_shared>( + "DatabaseNodeImp", + cacheSize.value_or(0), + std::chrono::minutes(cacheAge.value_or(0)), + stopwatch(), + j); + } + XRPL_ASSERT( backend_, "xrpl::NodeStore::DatabaseNodeImp::DatabaseNodeImp : non-null " @@ -67,16 +111,19 @@ public: backend_->sync(); } - std::vector> - fetchBatch(std::vector const& hashes); - void asyncFetch( uint256 const& hash, std::uint32_t ledgerSeq, std::function const&)>&& callback) override; + void + sweep() override; + private: + // Cache for database objects. This cache is not always initialized. Check + // for null before using. + std::shared_ptr> cache_; // Persistent key/value storage std::shared_ptr backend_; diff --git a/include/xrpl/nodestore/detail/DatabaseRotatingImp.h b/include/xrpl/nodestore/detail/DatabaseRotatingImp.h index 39441ef4d8..6343275c76 100644 --- a/include/xrpl/nodestore/detail/DatabaseRotatingImp.h +++ b/include/xrpl/nodestore/detail/DatabaseRotatingImp.h @@ -1,8 +1,19 @@ #pragma once +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include #include +#include namespace xrpl::NodeStore { @@ -55,6 +66,9 @@ public: void sync() override; + void + sweep() override; + private: std::shared_ptr writableBackend_; std::shared_ptr archiveBackend_; diff --git a/include/xrpl/nodestore/detail/DecodedBlob.h b/include/xrpl/nodestore/detail/DecodedBlob.h index 90a7b6c9cb..02ccd5787a 100644 --- a/include/xrpl/nodestore/detail/DecodedBlob.h +++ b/include/xrpl/nodestore/detail/DecodedBlob.h @@ -2,6 +2,8 @@ #include +#include + namespace xrpl::NodeStore { /** Parsed key/value blob into NodeObject components. @@ -32,11 +34,11 @@ public: createObject(); private: - bool success_; + bool success_{false}; void const* key_; - NodeObjectType objectType_; - unsigned char const* objectData_; + NodeObjectType objectType_{NodeObjectType::Unknown}; + unsigned char const* objectData_{nullptr}; int dataBytes_; }; diff --git a/include/xrpl/nodestore/detail/EncodedBlob.h b/include/xrpl/nodestore/detail/EncodedBlob.h index 343e1720a0..3982ab1b95 100644 --- a/include/xrpl/nodestore/detail/EncodedBlob.h +++ b/include/xrpl/nodestore/detail/EncodedBlob.h @@ -7,7 +7,10 @@ #include #include +#include #include +#include +#include namespace xrpl::NodeStore { diff --git a/include/xrpl/nodestore/detail/ManagerImp.h b/include/xrpl/nodestore/detail/ManagerImp.h index 98aec6459b..fc84b0aa57 100644 --- a/include/xrpl/nodestore/detail/ManagerImp.h +++ b/include/xrpl/nodestore/detail/ManagerImp.h @@ -1,6 +1,17 @@ #pragma once +#include +#include +#include +#include #include +#include + +#include +#include +#include +#include +#include namespace xrpl::NodeStore { diff --git a/include/xrpl/nodestore/detail/codec.h b/include/xrpl/nodestore/detail/codec.h index 49238fa34a..2f69d532be 100644 --- a/include/xrpl/nodestore/detail/codec.h +++ b/include/xrpl/nodestore/detail/codec.h @@ -1,6 +1,10 @@ #pragma once // Disable lz4 deprecation warning due to incompatibility with clang attributes +#include +#include +#include +#include #define LZ4_DISABLE_DEPRECATE_WARNINGS #include @@ -58,7 +62,7 @@ lz4Compress(void const* in, std::size_t inSize, BufferFactory&& bf) std::array::kMax> vi{}; auto const n = writeVarint(vi.data(), inSize); auto const outMax = LZ4_compressBound(inSize); - std::uint8_t* out = reinterpret_cast(bf(n + outMax)); + auto* out = reinterpret_cast(bf(n + outMax)); result.first = out; std::memcpy(out, vi.data(), n); auto const outSize = LZ4_compress_default( @@ -86,7 +90,7 @@ nodeobjectDecompress(void const* in, std::size_t inSize, BufferFactory&& bf) { using namespace nudb::detail; - std::uint8_t const* p = reinterpret_cast(in); + auto const* p = reinterpret_cast(in); std::size_t type = 0; auto const vn = readVarint(p, inSize, type); if (vn == 0) @@ -233,7 +237,7 @@ nodeobjectCompress(void const* in, std::size_t inSize, BufferFactory&& bf) auto const vs = sizeVarint(type); result.second = vs + field::size + // mask (n * 32); // hashes - std::uint8_t* out = reinterpret_cast(bf(result.second)); + auto* out = reinterpret_cast(bf(result.second)); result.first = out; ostream os(out, result.second); write(os, type); @@ -245,7 +249,7 @@ nodeobjectCompress(void const* in, std::size_t inSize, BufferFactory&& bf) auto const type = 3U; auto const vs = sizeVarint(type); result.second = vs + (n * 32); // hashes - std::uint8_t* out = reinterpret_cast(bf(result.second)); + auto* out = reinterpret_cast(bf(result.second)); result.first = out; ostream os(out, result.second); write(os, type); diff --git a/include/xrpl/nodestore/detail/varint.h b/include/xrpl/nodestore/detail/varint.h index e6b78fcf08..5a65545d3a 100644 --- a/include/xrpl/nodestore/detail/varint.h +++ b/include/xrpl/nodestore/detail/varint.h @@ -2,6 +2,7 @@ #include +#include #include #include @@ -25,7 +26,7 @@ struct varint_traits { explicit varint_traits() = default; - static constexpr std::size_t kMax = (8 * sizeof(T) + 6) / 7; + static constexpr std::size_t kMax = ((8 * sizeof(T)) + 6) / 7; }; // Returns: Number of bytes consumed or 0 on error, @@ -38,7 +39,7 @@ readVarint(void const* buf, std::size_t buflen, std::size_t& t) if (buflen == 0) return 0; t = 0; - std::uint8_t const* p = reinterpret_cast(buf); + auto const* p = reinterpret_cast(buf); std::size_t n = 0; while (p[n] & 0x80) { @@ -67,9 +68,10 @@ readVarint(void const* buf, std::size_t buflen, std::size_t& t) return used; } -template >* = nullptr> +template std::size_t sizeVarint(T v) + requires(std::is_unsigned_v) { std::size_t n = 0; do @@ -85,7 +87,7 @@ std::size_t writeVarint(void* p0, std::size_t v) { // NOLINTNEXTLINE(misc-const-correctness) - std::uint8_t* p = reinterpret_cast(p0); + auto* p = reinterpret_cast(p0); do { std::uint8_t d = v % 127; @@ -99,9 +101,10 @@ writeVarint(void* p0, std::size_t v) // input stream -template >* = nullptr> +template void read(nudb::detail::istream& is, std::size_t& u) + requires(std::is_same_v) { auto p0 = is(1); auto p1 = p0; @@ -112,9 +115,10 @@ read(nudb::detail::istream& is, std::size_t& u) // output stream -template >* = nullptr> +template void write(nudb::detail::ostream& os, std::size_t t) + requires(std::is_same_v) { writeVarint(os.data(sizeVarint(t)), t); } diff --git a/include/xrpl/proto/org/xrpl/rpc/v1/README.md b/include/xrpl/proto/org/xrpl/rpc/v1/README.md index e8566ec179..d0ff14cd13 100644 --- a/include/xrpl/proto/org/xrpl/rpc/v1/README.md +++ b/include/xrpl/proto/org/xrpl/rpc/v1/README.md @@ -70,9 +70,9 @@ into helper functions (see Tx.cpp or AccountTx.cpp for an example). #### Testing When modifying an existing gRPC method, be sure to test that modification in the -corresponding, existing unit test. When creating a new gRPC method, implement a class that -derives from GRPCTestClientBase, and use the newly created class to call the new -method. See the class `GrpcTxClient` in the file Tx_test.cpp for an example. +corresponding, existing unit test. When creating a new gRPC method, create a +client stub with `XRPLedgerAPIService::NewStub` and `grpc::CreateChannel`, and +use it to call the new method. See `GRPCServerTLS_test.cpp` for an example. The gRPC tests are paired with their JSON counterpart, and the tests should mirror the JSON test as much as possible. diff --git a/include/xrpl/protocol/AMMCore.h b/include/xrpl/protocol/AMMCore.h index ced84c4c87..c4fccd029a 100644 --- a/include/xrpl/protocol/AMMCore.h +++ b/include/xrpl/protocol/AMMCore.h @@ -3,9 +3,14 @@ #include #include #include +#include #include #include +#include +#include +#include + namespace xrpl { constexpr std::uint16_t kTradingFeeThreshold = 1000; // 1% @@ -65,7 +70,7 @@ invalidAMMAssetPair( std::optional ammAuctionTimeSlot(std::uint64_t current, STObject const& auctionSlot); -/** Return true if required AMM amendments are enabled +/** Return true if required AMM amendment is enabled */ bool ammEnabled(Rules const&); diff --git a/include/xrpl/protocol/AccountID.h b/include/xrpl/protocol/AccountID.h index 4938812ffa..a7d49246ca 100644 --- a/include/xrpl/protocol/AccountID.h +++ b/include/xrpl/protocol/AccountID.h @@ -3,13 +3,17 @@ #include // VFALCO Uncomment when the header issues are resolved // #include -#include #include +#include +#include #include +#include #include #include +#include #include +#include #include namespace xrpl { diff --git a/include/xrpl/protocol/AmountConversions.h b/include/xrpl/protocol/AmountConversions.h index a5f7ec310f..3bcd80e827 100644 --- a/include/xrpl/protocol/AmountConversions.h +++ b/include/xrpl/protocol/AmountConversions.h @@ -1,10 +1,20 @@ #pragma once +#include +#include +#include +#include #include +#include +#include +#include #include #include #include +#include +#include // IWYU pragma: keep +#include #include namespace xrpl { diff --git a/include/xrpl/protocol/ApiVersion.h b/include/xrpl/protocol/ApiVersion.h index 345049b377..c3292e6074 100644 --- a/include/xrpl/protocol/ApiVersion.h +++ b/include/xrpl/protocol/ApiVersion.h @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -102,25 +103,32 @@ getAPIVersionNumber(json::Value const& jv, bool betaEnabled) json::Value const maxVersion( betaEnabled ? RPC::kApiBetaVersion : RPC::kApiMaximumSupportedVersion); - if (jv.isObject()) + if (!jv.isObject() || !jv.isMember(jss::api_version)) + return RPC::kApiVersionIfUnspecified; + + try { - if (jv.isMember(jss::api_version)) + auto const& rawVersion = jv[jss::api_version]; + switch (rawVersion.type()) { - auto const specifiedVersion = jv[jss::api_version]; - if (!specifiedVersion.isInt() && !specifiedVersion.isUInt()) - { - return RPC::kApiInvalidVersion; + case json::ValueType::Int: + if (rawVersion.asInt() < 0) + return RPC::kApiInvalidVersion; + [[fallthrough]]; + case json::ValueType::UInt: { + auto const apiVersion = rawVersion.asUInt(); + if (apiVersion < kMinVersion || apiVersion > maxVersion) + return RPC::kApiInvalidVersion; + return apiVersion; } - auto const specifiedVersionInt = specifiedVersion.asInt(); - if (specifiedVersionInt < kMinVersion || specifiedVersionInt > maxVersion) - { + default: return RPC::kApiInvalidVersion; - } - return specifiedVersionInt; } } - - return RPC::kApiVersionIfUnspecified; + catch (...) + { + return RPC::kApiInvalidVersion; + } } } // namespace RPC diff --git a/include/xrpl/protocol/Asset.h b/include/xrpl/protocol/Asset.h index ec9d8db02f..2bf24b19fe 100644 --- a/include/xrpl/protocol/Asset.h +++ b/include/xrpl/protocol/Asset.h @@ -2,10 +2,19 @@ #include #include +#include +#include +#include #include #include #include -#include +#include + +#include +#include +#include +#include +#include namespace xrpl { diff --git a/include/xrpl/protocol/Batch.h b/include/xrpl/protocol/Batch.h index fa7641af70..1e4f811fa9 100644 --- a/include/xrpl/protocol/Batch.h +++ b/include/xrpl/protocol/Batch.h @@ -1,13 +1,26 @@ +#pragma once + +#include +#include #include -#include #include +#include +#include + namespace xrpl { inline void -serializeBatch(Serializer& msg, std::uint32_t const& flags, std::vector const& txids) +serializeBatch( + Serializer& msg, + AccountID const& outerAccount, + std::uint32_t outerSeqValue, + std::uint32_t const& flags, + std::vector const& txids) { msg.add32(HashPrefix::Batch); + msg.addBitString(outerAccount); + msg.add32(outerSeqValue); msg.add32(flags); msg.add32(std::uint32_t(txids.size())); for (auto const& txid : txids) diff --git a/include/xrpl/protocol/Book.h b/include/xrpl/protocol/Book.h index fc36abddc4..476bdba35a 100644 --- a/include/xrpl/protocol/Book.h +++ b/include/xrpl/protocol/Book.h @@ -2,10 +2,21 @@ #include #include +#include #include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include + namespace xrpl { /** Specifies an order book. @@ -140,8 +151,8 @@ private: using issue_hasher = std::hash; using mptissue_hasher = std::hash; - issue_hasher m_issue_hasher_; - mptissue_hasher m_mptissue_hasher_; + issue_hasher mIssueHasher_; + mptissue_hasher mMptissueHasher_; public: explicit hash() = default; @@ -151,11 +162,11 @@ public: { return asset.visit( [&](xrpl::Issue const& issue) { - value_type const result(m_issue_hasher_(issue)); + value_type const result(mIssueHasher_(issue)); return result; }, [&](xrpl::MPTIssue const& issue) { - value_type const result(m_mptissue_hasher_(issue)); + value_type const result(mMptissueHasher_(issue)); return result; }); } @@ -170,8 +181,8 @@ private: using asset_hasher = std::hash; using uint256_hasher = xrpl::uint256::hasher; - asset_hasher issue_hasher_; - uint256_hasher uint256_hasher_; + asset_hasher issueHasher_; + uint256_hasher uint256Hasher_; public: hash() = default; @@ -182,11 +193,11 @@ public: value_type operator()(argument_type const& value) const { - value_type result(issue_hasher_(value.in)); - boost::hash_combine(result, issue_hasher_(value.out)); + value_type result(issueHasher_(value.in)); + boost::hash_combine(result, issueHasher_(value.out)); if (value.domain) - boost::hash_combine(result, uint256_hasher_(*value.domain)); + boost::hash_combine(result, uint256Hasher_(*value.domain)); return result; } diff --git a/include/xrpl/protocol/BuildInfo.h b/include/xrpl/protocol/BuildInfo.h index 47a27339a8..a60c37e714 100644 --- a/include/xrpl/protocol/BuildInfo.h +++ b/include/xrpl/protocol/BuildInfo.h @@ -2,6 +2,7 @@ #include #include +#include /** Versioning information for this build. */ // VFALCO The namespace is deprecated diff --git a/include/xrpl/protocol/ConfidentialTransfer.h b/include/xrpl/protocol/ConfidentialTransfer.h new file mode 100644 index 0000000000..325117eed4 --- /dev/null +++ b/include/xrpl/protocol/ConfidentialTransfer.h @@ -0,0 +1,430 @@ +#pragma once + +#include +#include +#include +#include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace xrpl { + +/** + * @brief Bundles an ElGamal public key with its associated encrypted amount. + * + * Used to represent a recipient in confidential transfers, containing both + * the recipient's ElGamal public key and the ciphertext encrypting the + * transfer amount under that key. + */ +struct ConfidentialRecipient +{ + /** @brief The recipient's ElGamal public key (size=xrpl::kEcPubKeyLength). */ + Slice publicKey; + + /** + * @brief The encrypted amount ciphertext + * (size=xrpl::kEcGamalEncryptedTotalLength). + */ + Slice encryptedAmount; +}; + +/** + * @brief Holds two secp256k1 public key components representing an ElGamal + * ciphertext (C1, C2). + */ +struct EcPair +{ + /** @brief First ElGamal ciphertext component. */ + secp256k1_pubkey c1; + + /** @brief Second ElGamal ciphertext component. */ + secp256k1_pubkey c2; +}; + +/** + * @brief Increments the confidential balance version counter on an MPToken. + * + * The version counter is used to prevent replay attacks by binding proofs + * to a specific state of the account's confidential balance. Wraps to 0 + * on overflow (defined behavior for unsigned integers). + * + * @param mptoken The MPToken ledger entry to update. + */ +inline void +incrementConfidentialVersion(STObject& mptoken) +{ + // Retrieve current version and increment, wrapping back to 0 at UINT32_MAX. + // The wrap is computed explicitly rather than relying on unsigned overflow + // of `+ 1u`, as it trips the unsigned-integer-overflow sanitizer in the UBSan CI build. + auto const current = mptoken[~sfConfidentialBalanceVersion].valueOr(0u); + mptoken[sfConfidentialBalanceVersion] = + current == std::numeric_limits::max() ? 0u : current + 1u; +} + +/** + * @brief Generates the context hash for ConfidentialMPTSend transactions. + * + * Creates a unique 256-bit hash that binds the zero-knowledge proofs to + * this specific send transaction, preventing proof reuse across transactions. + * + * @param account The sender's account ID. + * @param issuanceID The MPToken Issuance ID. + * @param sequence The transaction sequence number or ticket number. + * @param destination The destination account ID. + * @param version The sender's confidential balance version. + * @return A 256-bit context hash unique to this transaction. + */ +uint256 +getSendContextHash( + AccountID const& account, + uint192 const& issuanceID, + std::uint32_t sequence, + AccountID const& destination, + std::uint32_t version); + +/** + * @brief Generates the context hash for ConfidentialMPTClawback transactions. + * + * Creates a unique 256-bit hash that binds the equality proof to this + * specific clawback transaction. + * + * @param account The issuer's account ID. + * @param issuanceID The MPToken Issuance ID. + * @param sequence The transaction sequence number or ticket number. + * @param holder The holder's account ID being clawed back from. + * @return A 256-bit context hash unique to this transaction. + */ +uint256 +getClawbackContextHash( + AccountID const& account, + uint192 const& issuanceID, + std::uint32_t sequence, + AccountID const& holder); + +/** + * @brief Generates the context hash for ConfidentialMPTConvert transactions. + * + * Creates a unique 256-bit hash that binds the Schnorr proof (for key + * registration) to this specific convert transaction. + * + * @param account The holder's account ID. + * @param issuanceID The MPToken Issuance ID. + * @param sequence The transaction sequence number or a ticket number. + * @return A 256-bit context hash unique to this transaction. + */ +uint256 +getConvertContextHash(AccountID const& account, uint192 const& issuanceID, std::uint32_t sequence); + +/** + * @brief Generates the context hash for ConfidentialMPTConvertBack transactions. + * + * Creates a unique 256-bit hash that binds the zero-knowledge proofs to + * this specific convert-back transaction. + * + * @param account The holder's account ID. + * @param issuanceID The MPToken Issuance ID. + * @param sequence The transaction sequence number or a ticket number. + * @param version The holder's confidential balance version. + * @return A 256-bit context hash unique to this transaction. + */ +uint256 +getConvertBackContextHash( + AccountID const& account, + uint192 const& issuanceID, + std::uint32_t sequence, + std::uint32_t version); + +/** + * @brief Parses an ElGamal ciphertext into two secp256k1 public key components. + * + * Breaks an encrypted amount (size=xrpl::kEcGamalEncryptedTotalLength, two + * compressed EC points of size=xrpl::kEcCiphertextComponentLength) into + * a pair containing (C1, C2) for use in cryptographic operations. + * + * @param buffer The buffer containing the compressed ciphertext + * (size=xrpl::kEcGamalEncryptedTotalLength). + * @return The parsed pair (c1, c2) if successful, std::nullopt if the buffer is invalid. + */ +std::optional +makeEcPair(Slice const& buffer); + +/** + * @brief Serializes an EcPair into compressed form. + * + * Converts an EcPair (C1, C2) back into a buffer + * (size=xrpl::kEcGamalEncryptedTotalLength) containing two compressed EC + * points (size=xrpl::kEcCiphertextComponentLength each). + * + * @param pair The EcPair to serialize. + * @return The buffer (size=xrpl::kEcGamalEncryptedTotalLength), or std::nullopt + * if serialization fails. + */ +std::optional +serializeEcPair(EcPair const& pair); + +/** + * @brief Verifies that a buffer contains two valid, parsable EC public keys. + * + * @param buffer The input buffer containing two concatenated components. + * @return true if both components can be parsed successfully, false otherwise. + */ +bool +isValidCiphertext(Slice const& buffer); + +/** + * @brief Verifies that a buffer contains a valid, parsable compressed EC point. + * + * Can be used to validate both compressed public keys and Pedersen commitments. + * Fails early if the prefix byte is not 0x02 or 0x03. + * + * @param buffer The input buffer containing a compressed EC point + * (size=xrpl::kCompressedEcPointLength). + * @return true if the point can be parsed successfully, false otherwise. + */ +bool +isValidCompressedECPoint(Slice const& buffer); + +/** + * @brief Homomorphically adds two ElGamal ciphertexts. + * + * Uses the additive homomorphic property of ElGamal encryption to compute + * Enc(a + b) from Enc(a) and Enc(b) without decryption. + * + * @param a The first ciphertext (size=xrpl::kEcGamalEncryptedTotalLength). + * @param b The second ciphertext (size=xrpl::kEcGamalEncryptedTotalLength). + * @return The resulting ciphertext Enc(a + b), or std::nullopt on failure. + */ +std::optional +homomorphicAdd(Slice const& a, Slice const& b); + +/** + * @brief Homomorphically subtracts two ElGamal ciphertexts. + * + * Uses the additive homomorphic property of ElGamal encryption to compute + * Enc(a - b) from Enc(a) and Enc(b) without decryption. + * + * @param a The minuend ciphertext (size=xrpl::kEcGamalEncryptedTotalLength). + * @param b The subtrahend ciphertext (size=xrpl::kEcGamalEncryptedTotalLength). + * @return The resulting ciphertext Enc(a - b), or std::nullopt on failure. + */ +std::optional +homomorphicSubtract(Slice const& a, Slice const& b); + +/** + * @brief Re-randomizes an ElGamal ciphertext without changing its plaintext. + * + * Adds Enc(0; randomness) under the supplied public key to the ciphertext. + * This is used when a public, deterministic scalar must perturb ciphertext + * randomness while preserving ledger reproducibility. + * + * @param ciphertext The ciphertext to re-randomize + * (size=xrpl::kEcGamalEncryptedTotalLength). + * @param pubKeySlice The ElGamal public key matching the ciphertext recipient. + * @param randomness The scalar used as zero-encryption randomness + * (size=xrpl::kEcScalarLength). + * @return The re-randomized ciphertext, or std::nullopt on failure. + */ +std::optional +rerandomizeCiphertext(Slice const& ciphertext, Slice const& pubKeySlice, Slice const& randomness); + +/** + * @brief Encrypts an amount using ElGamal encryption. + * + * Produces a ciphertext C = (C1, C2) where C1 = r*G and C2 = m*G + r*Pk, + * using the provided blinding factor r. + * + * @param amt The plaintext amount to encrypt. + * @param pubKeySlice The recipient's ElGamal public key (size=xrpl::kEcPubKeyLength). + * @param blindingFactor The randomness used as blinding factor r + * (size=xrpl::ecBlindingFactorLength). + * @return The ciphertext (size=xrpl::kEcGamalEncryptedTotalLength), or std::nullopt on failure. + */ +std::optional +encryptAmount(uint64_t const amt, Slice const& pubKeySlice, Slice const& blindingFactor); + +/** + * @brief Generates the canonical zero encryption for a specific MPToken. + * + * Creates a deterministic encryption of zero that is unique to the account + * and MPT issuance. Used to initialize confidential balance fields. + * + * @param pubKeySlice The holder's ElGamal public key (size=xrpl::kEcPubKeyLength). + * @param account The account ID of the token holder. + * @param mptId The MPToken Issuance ID. + * @return The canonical zero ciphertext (size=xrpl::kEcGamalEncryptedTotalLength), or std::nullopt + * on failure. + */ +std::optional +encryptCanonicalZeroAmount(Slice const& pubKeySlice, AccountID const& account, MPTID const& mptId); + +/** + * @brief Verifies a Schnorr proof of knowledge of an ElGamal private key. + * + * Proves that the submitter knows the secret key corresponding to the + * provided public key, without revealing the secret key itself. + * + * @param pubKeySlice The ElGamal public key (size=xrpl::kEcPubKeyLength). + * @param proofSlice The Schnorr proof (size=xrpl::ecSchnorrProofLength). + * @param contextHash The 256-bit context hash binding the proof. + * @return tesSUCCESS if valid, or an error code otherwise. + */ +TER +verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 const& contextHash); + +/** + * @brief Validates the format of encrypted amount fields in a transaction. + * + * Checks that all ciphertext fields in the transaction object have the + * correct length and contain valid EC points. This function is only used + * by ConfidentialMPTConvert and ConfidentialMPTConvertBack transactions. + * + * @param object The transaction object containing encrypted amount fields. + * @return tesSUCCESS if all formats are valid, temMALFORMED if required fields + * are missing, or temBAD_CIPHERTEXT if format validation fails. + */ +NotTEC +checkEncryptedAmountFormat(STObject const& object); + +/** + * @brief Verifies revealed amount encryptions for all recipients. + * + * Validates that the same amount was correctly encrypted for the holder, + * issuer, and optionally the auditor using their respective public keys. + * + * @param amount The revealed plaintext amount. + * @param blindingFactor The blinding factor used in all encryptions + * (size=xrpl::ecBlindingFactorLength). + * @param holder The holder's public key and encrypted amount. + * @param issuer The issuer's public key and encrypted amount. + * @param auditor Optional auditor's public key and encrypted amount. + * @return tesSUCCESS if all encryptions are valid, or an error code otherwise. + */ +TER +verifyRevealedAmount( + uint64_t const amount, + Slice const& blindingFactor, + ConfidentialRecipient const& holder, + ConfidentialRecipient const& issuer, + std::optional const& auditor); + +/** + * @brief Returns the number of recipients in a confidential transfer. + * + * Returns 4 if an auditor is present (sender, destination, issuer, auditor), + * or 3 if no auditor (sender, destination, issuer). + * + * @param hasAuditor Whether the issuance has an auditor configured. + * @return The number of recipients (3 or 4). + */ +constexpr uint8_t +getConfidentialRecipientCount(bool hasAuditor) +{ + return hasAuditor ? 4 : 3; +} + +/** + * @brief Verifies a compact sigma clawback proof. + * + * Proves that the issuer knows the exact amount encrypted in the holder's + * balance ciphertext. Used in ConfidentialMPTClawback to verify the issuer + * can decrypt the balance using their private key. + * + * @param amount The revealed plaintext amount. + * @param proof The zero-knowledge proof bytes (ecClawbackProofLength). + * @param pubKeySlice The issuer's ElGamal public key (kEcPubKeyLength bytes). + * @param ciphertext The issuer's encrypted balance on the holder's account + * (kEcGamalEncryptedTotalLength bytes). + * @param contextHash The 256-bit context hash binding the proof. + * @return tesSUCCESS if the proof is valid, or an error code otherwise. + */ +TER +verifyClawbackProof( + uint64_t const amount, + Slice const& proof, + Slice const& pubKeySlice, + Slice const& ciphertext, + uint256 const& contextHash); + +/** + * @brief Generates a cryptographically secure blinding factor + * (size=xrpl::kEcBlindingFactorLength). + * + * Produces random bytes suitable for use as an ElGamal blinding factor + * or Pedersen commitment randomness. + * + * @return A buffer containing the random blinding factor + * (size=xrpl::kEcBlindingFactorLength). + */ +Buffer +generateBlindingFactor(); + +/** + * @brief Verifies all zero-knowledge proofs for a ConfidentialMPTSend transaction. + * + * This function calls mpt_verify_send_proof API in the mpt-crypto utility lib, which verifies the + * equality proof, amount linkage, balance linkage, and range proof. + * Equality proof: Proves the same value is encrypted for the sender, receiver, issuer, and auditor. + * Amount linkage: Proves the send amount matches the amount Pedersen commitment. + * Balance linkage: Proves the sender's balance matches the balance Pedersen + * commitment. + * Range proof: Proves the amount and the remaining balance are within range [0, 2^64-1]. + * + * @param proof The full proof blob. + * @param sender The sender's public key and encrypted amount. + * @param destination The destination's public key and encrypted amount. + * @param issuer The issuer's public key and encrypted amount. + * @param auditor The auditor's public key and encrypted amount if present. + * @param spendingBalance The sender's current spending balance ciphertext. + * @param amountCommitment The Pedersen commitment to the send amount. + * @param balanceCommitment The Pedersen commitment to the sender's balance. + * @param contextHash The context hash binding the proof. + * @return tesSUCCESS if all proofs are valid, or an error code otherwise. + */ +TER +verifySendProof( + Slice const& proof, + ConfidentialRecipient const& sender, + ConfidentialRecipient const& destination, + ConfidentialRecipient const& issuer, + std::optional const& auditor, + Slice const& spendingBalance, + Slice const& amountCommitment, + Slice const& balanceCommitment, + uint256 const& contextHash); + +/** + * @brief Verifies all zero-knowledge proofs for a ConfidentialMPTConvertBack transaction. + * + * This function calls mpt_verify_convert_back_proof API in the mpt-crypto utility lib, which + * verifies the balance linkage proof and range proof. Balance linkage proof: proves the balance + * commitment matches the spending ciphertext. Range proof: proves the remaining balance after + * convert back is within range [0, 2^64-1]. + * + * @param proof The full proof blob. + * @param pubKeySlice The holder's public key. + * @param spendingBalance The holder's spending balance ciphertext. + * @param balanceCommitment The Pedersen commitment to the balance. + * @param amount The amount being converted back to public. + * @param contextHash The context hash binding the proof. + * @return tesSUCCESS if all proofs are valid, or an error code otherwise. + */ +TER +verifyConvertBackProof( + Slice const& proof, + Slice const& pubKeySlice, + Slice const& spendingBalance, + Slice const& balanceCommitment, + uint64_t amount, + uint256 const& contextHash); + +} // namespace xrpl diff --git a/include/xrpl/protocol/ErrorCodes.h b/include/xrpl/protocol/ErrorCodes.h index a91adfc55a..38b8bc6d76 100644 --- a/include/xrpl/protocol/ErrorCodes.h +++ b/include/xrpl/protocol/ErrorCodes.h @@ -1,7 +1,8 @@ #pragma once #include -#include + +#include namespace xrpl { @@ -172,24 +173,24 @@ struct ErrorInfo { // Default ctor needed to produce an empty std::array during constexpr eval. constexpr ErrorInfo() - : code(RpcUnknown), token("unknown"), message("An unknown error code."), http_status(200) + : code(RpcUnknown), token("unknown"), message("An unknown error code."), httpStatus(200) { } constexpr ErrorInfo(ErrorCodeI code, char const* token, char const* message) - : code(code), token(token), message(message), http_status(200) + : code(code), token(token), message(message), httpStatus(200) { } constexpr ErrorInfo(ErrorCodeI code, char const* token, char const* message, int httpStatus) - : code(code), token(token), message(message), http_status(httpStatus) + : code(code), token(token), message(message), httpStatus(httpStatus) { } ErrorCodeI code; json::StaticString token; json::StaticString message; - int http_status; + int httpStatus; }; /** Returns an ErrorInfo that reflects the error code. */ diff --git a/include/xrpl/protocol/Feature.h b/include/xrpl/protocol/Feature.h index 5de8ca64a9..927fde542a 100644 --- a/include/xrpl/protocol/Feature.h +++ b/include/xrpl/protocol/Feature.h @@ -1,10 +1,12 @@ #pragma once #include +#include #include #include +#include #include #include #include diff --git a/include/xrpl/protocol/Fees.h b/include/xrpl/protocol/Fees.h index 14bcc068bf..4e79204f4c 100644 --- a/include/xrpl/protocol/Fees.h +++ b/include/xrpl/protocol/Fees.h @@ -2,6 +2,9 @@ #include +#include +#include + namespace xrpl { // Deprecated constant for backwards compatibility with pre-XRPFees amendment. diff --git a/include/xrpl/protocol/IOUAmount.h b/include/xrpl/protocol/IOUAmount.h index 9e0fbe38eb..186ce054f1 100644 --- a/include/xrpl/protocol/IOUAmount.h +++ b/include/xrpl/protocol/IOUAmount.h @@ -1,12 +1,12 @@ #pragma once -#include #include #include #include #include +#include #include namespace xrpl { @@ -179,36 +179,4 @@ to_string(IOUAmount const& amount); IOUAmount mulRatio(IOUAmount const& amt, std::uint32_t num, std::uint32_t den, bool roundUp); -// Since many uses of the number class do not have access to a ledger, -// getSTNumberSwitchover needs to be globally accessible. - -bool -getSTNumberSwitchover(); - -void -setSTNumberSwitchover(bool v); - -/** RAII class to set and restore the Number switchover. - */ - -class NumberSO -{ - bool saved_; - -public: - ~NumberSO() - { - setSTNumberSwitchover(saved_); - } - - NumberSO(NumberSO const&) = delete; - NumberSO& - operator=(NumberSO const&) = delete; - - explicit NumberSO(bool v) : saved_(getSTNumberSwitchover()) - { - setSTNumberSwitchover(v); - } -}; - } // namespace xrpl diff --git a/include/xrpl/protocol/Indexes.h b/include/xrpl/protocol/Indexes.h index 887a208ec6..053a66787f 100644 --- a/include/xrpl/protocol/Indexes.h +++ b/include/xrpl/protocol/Indexes.h @@ -1,18 +1,25 @@ #pragma once +#include #include +#include +#include +#include +#include #include +#include #include #include #include -#include #include -#include #include #include +#include #include +#include #include +#include namespace xrpl { @@ -68,21 +75,15 @@ skip(LedgerIndex ledger) noexcept; /** The (fixed) index of the object containing the ledger fees. */ Keylet const& -fees() noexcept; +feeSettings() noexcept; /** The (fixed) index of the object containing the ledger negativeUNL. */ Keylet const& negativeUNL() noexcept; /** The beginning of an order book */ -struct BookT -{ - explicit BookT() = default; - - Keylet - operator()(Book const& b) const; -}; -static BookT const kBook{}; +Keylet +book(Book const& b); /** The index of a trust line for a given currency @@ -93,12 +94,12 @@ static BookT const kBook{}; */ /** @{ */ Keylet -line(AccountID const& id0, AccountID const& id1, Currency const& currency) noexcept; +trustLine(AccountID const& id0, AccountID const& id1, Currency const& currency) noexcept; inline Keylet -line(AccountID const& id, Issue const& issue) noexcept +trustLine(AccountID const& id, Issue const& issue) noexcept { - return line(id, issue.account, issue.currency); + return trustLine(id, issue.account, issue.currency); } /** @} */ @@ -119,37 +120,27 @@ Keylet quality(Keylet const& k, std::uint64_t q) noexcept; /** The directory for the next lower quality */ -struct NextT -{ - explicit NextT() = default; - - Keylet - operator()(Keylet const& k) const; -}; -static NextT const kNext{}; +Keylet +next(Keylet const& k); /** A ticket belonging to an account */ -struct TicketT +/** @{ */ +Keylet +ticket(AccountID const& id, std::uint32_t ticketSeq); + +Keylet +ticket(AccountID const& id, SeqProxy ticketSeq); + +inline Keylet +ticket(uint256 const& key) { - explicit TicketT() = default; - - Keylet - operator()(AccountID const& id, std::uint32_t ticketSeq) const; - - Keylet - operator()(AccountID const& id, SeqProxy ticketSeq) const; - - Keylet - operator()(uint256 const& key) const - { - return {ltTICKET, key}; - } -}; -static TicketT const kTicket{}; + return {ltTICKET, key}; +} +/** @} */ /** A SignerList */ Keylet -signers(AccountID const& account) noexcept; +signerList(AccountID const& account) noexcept; /** A Check */ /** @{ */ @@ -209,7 +200,7 @@ escrow(AccountID const& src, std::uint32_t seq) noexcept; /** A PaymentChannel */ Keylet -payChan(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept; +payChannel(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept; /** NFT page keylets @@ -221,22 +212,22 @@ payChan(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept; /** @{ */ /** A keylet for the owner's first possible NFT page. */ Keylet -nftpageMin(AccountID const& owner); +nftokenPageMin(AccountID const& owner); /** A keylet for the owner's last possible NFT page. */ Keylet -nftpageMax(AccountID const& owner); +nftokenPageMax(AccountID const& owner); Keylet -nftpage(Keylet const& k, uint256 const& token); +nftokenPage(Keylet const& k, uint256 const& token); /** @} */ /** An offer from an account to buy or sell an NFT */ Keylet -nftoffer(AccountID const& owner, std::uint32_t seq); +nftokenOffer(AccountID const& owner, std::uint32_t seq); inline Keylet -nftoffer(uint256 const& offer) +nftokenOffer(uint256 const& offer) { return {ltNFTOKEN_OFFER, offer}; } @@ -287,13 +278,13 @@ credential(uint256 const& key) noexcept } Keylet -mptIssuance(std::uint32_t seq, AccountID const& issuer) noexcept; +mptokenIssuance(std::uint32_t seq, AccountID const& issuer) noexcept; Keylet -mptIssuance(MPTID const& issuanceID) noexcept; +mptokenIssuance(MPTID const& issuanceID) noexcept; inline Keylet -mptIssuance(uint256 const& issuanceKey) +mptokenIssuance(uint256 const& issuanceKey) { return {ltMPTOKEN_ISSUANCE, issuanceKey}; } @@ -320,10 +311,10 @@ vault(uint256 const& vaultKey) } Keylet -loanbroker(AccountID const& owner, std::uint32_t seq) noexcept; +loanBroker(AccountID const& owner, std::uint32_t seq) noexcept; inline Keylet -loanbroker(uint256 const& key) +loanBroker(uint256 const& key) { return {ltLOAN_BROKER, key}; } @@ -376,11 +367,15 @@ struct KeyletDesc std::array, 6> const kDirectAccountKeylets{ {{.function = &keylet::account, .expectedLEName = jss::AccountRoot, .includeInTests = false}, {.function = &keylet::ownerDir, .expectedLEName = jss::DirectoryNode, .includeInTests = true}, - {.function = &keylet::signers, .expectedLEName = jss::SignerList, .includeInTests = true}, + {.function = &keylet::signerList, .expectedLEName = jss::SignerList, .includeInTests = true}, // It's normally impossible to create an item at nftpage_min, but // test it anyway, since the invariant checks for it. - {.function = &keylet::nftpageMin, .expectedLEName = jss::NFTokenPage, .includeInTests = true}, - {.function = &keylet::nftpageMax, .expectedLEName = jss::NFTokenPage, .includeInTests = true}, + {.function = &keylet::nftokenPageMin, + .expectedLEName = jss::NFTokenPage, + .includeInTests = true}, + {.function = &keylet::nftokenPageMax, + .expectedLEName = jss::NFTokenPage, + .includeInTests = true}, {.function = &keylet::did, .expectedLEName = jss::DID, .includeInTests = true}}}; MPTID diff --git a/include/xrpl/protocol/InnerObjectFormats.h b/include/xrpl/protocol/InnerObjectFormats.h index 9d07a21d1c..c8312c3701 100644 --- a/include/xrpl/protocol/InnerObjectFormats.h +++ b/include/xrpl/protocol/InnerObjectFormats.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include namespace xrpl { diff --git a/include/xrpl/protocol/Issue.h b/include/xrpl/protocol/Issue.h index c8022698d3..3d556e83eb 100644 --- a/include/xrpl/protocol/Issue.h +++ b/include/xrpl/protocol/Issue.h @@ -1,9 +1,13 @@ #pragma once -#include #include +#include #include +#include +#include +#include + namespace xrpl { /** A currency issued by an account. diff --git a/include/xrpl/protocol/KnownFormats.h b/include/xrpl/protocol/KnownFormats.h index 9aa914ba97..c31e28c37d 100644 --- a/include/xrpl/protocol/KnownFormats.h +++ b/include/xrpl/protocol/KnownFormats.h @@ -7,7 +7,11 @@ #include #include +#include #include +#include +#include +#include namespace xrpl { @@ -118,13 +122,13 @@ public: } // begin() and end() are provided for testing purposes. - [[nodiscard]] typename std::forward_list::const_iterator + [[nodiscard]] std::forward_list::const_iterator begin() const { return formats_.begin(); } - [[nodiscard]] typename std::forward_list::const_iterator + [[nodiscard]] std::forward_list::const_iterator end() const { return formats_.end(); diff --git a/include/xrpl/protocol/LedgerFormats.h b/include/xrpl/protocol/LedgerFormats.h index 99d5d818f1..5b8a8cc2c5 100644 --- a/include/xrpl/protocol/LedgerFormats.h +++ b/include/xrpl/protocol/LedgerFormats.h @@ -3,9 +3,12 @@ // NOLINTBEGIN(readability-identifier-naming) #include +#include +#include #include #include +#include #include namespace xrpl { @@ -177,17 +180,19 @@ enum LedgerEntryType : std::uint16_t { LSF_FLAG(lsfMPTCanEscrow, 0x00000008) \ LSF_FLAG(lsfMPTCanTrade, 0x00000010) \ LSF_FLAG(lsfMPTCanTransfer, 0x00000020) \ - LSF_FLAG(lsfMPTCanClawback, 0x00000040)) \ + LSF_FLAG(lsfMPTCanClawback, 0x00000040) \ + LSF_FLAG(lsfMPTCanHoldConfidentialBalance, 0x00000080)) \ \ LEDGER_OBJECT(MPTokenIssuanceMutable, \ - LSF_FLAG(lsmfMPTCanMutateCanLock, 0x00000002) \ - LSF_FLAG(lsmfMPTCanMutateRequireAuth, 0x00000004) \ - LSF_FLAG(lsmfMPTCanMutateCanEscrow, 0x00000008) \ - LSF_FLAG(lsmfMPTCanMutateCanTrade, 0x00000010) \ - LSF_FLAG(lsmfMPTCanMutateCanTransfer, 0x00000020) \ - LSF_FLAG(lsmfMPTCanMutateCanClawback, 0x00000040) \ + LSF_FLAG(lsmfMPTCanEnableCanLock, 0x00000002) \ + LSF_FLAG(lsmfMPTCanEnableRequireAuth, 0x00000004) \ + LSF_FLAG(lsmfMPTCanEnableCanEscrow, 0x00000008) \ + LSF_FLAG(lsmfMPTCanEnableCanTrade, 0x00000010) \ + LSF_FLAG(lsmfMPTCanEnableCanTransfer, 0x00000020) \ + LSF_FLAG(lsmfMPTCanEnableCanClawback, 0x00000040) \ + LSF_FLAG(lsmfMPTCannotEnableCanHoldConfidentialBalance, 0x00000080) \ LSF_FLAG(lsmfMPTCanMutateMetadata, 0x00010000) \ - LSF_FLAG(lsmfMPTCanMutateTransferFee, 0x00020000)) \ + LSF_FLAG(lsmfMPTCanMutateTransferFee, 0x00020000)) \ \ LEDGER_OBJECT(MPToken, \ LSF_FLAG2(lsfMPTLocked, 0x00000001) \ diff --git a/include/xrpl/protocol/LedgerHeader.h b/include/xrpl/protocol/LedgerHeader.h index f05b11d1eb..df8f314c5f 100644 --- a/include/xrpl/protocol/LedgerHeader.h +++ b/include/xrpl/protocol/LedgerHeader.h @@ -3,10 +3,13 @@ #include #include #include +#include #include #include #include +#include + namespace xrpl { /** Information about the notional ledger backing the view. */ diff --git a/include/xrpl/protocol/MPTAmount.h b/include/xrpl/protocol/MPTAmount.h index 6ea36fc294..329d83610e 100644 --- a/include/xrpl/protocol/MPTAmount.h +++ b/include/xrpl/protocol/MPTAmount.h @@ -2,13 +2,15 @@ #include #include -#include #include #include #include #include +#include +#include +#include #include namespace xrpl { diff --git a/include/xrpl/protocol/MPTIssue.h b/include/xrpl/protocol/MPTIssue.h index f55029f50d..0c495aa57f 100644 --- a/include/xrpl/protocol/MPTIssue.h +++ b/include/xrpl/protocol/MPTIssue.h @@ -1,8 +1,18 @@ #pragma once +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include + namespace xrpl { /* Adapt MPTID to provide the same interface as Issue. Enables using static diff --git a/include/xrpl/protocol/PathAsset.h b/include/xrpl/protocol/PathAsset.h index b51dc52b47..ebf6fb68a4 100644 --- a/include/xrpl/protocol/PathAsset.h +++ b/include/xrpl/protocol/PathAsset.h @@ -1,7 +1,14 @@ #pragma once +#include #include #include +#include + +#include +#include +#include +#include namespace xrpl { diff --git a/include/xrpl/protocol/Permissions.h b/include/xrpl/protocol/Permissions.h index 5d56fa4461..703a0939c9 100644 --- a/include/xrpl/protocol/Permissions.h +++ b/include/xrpl/protocol/Permissions.h @@ -1,14 +1,22 @@ #pragma once +#include #include -#include +#include #include +#include +#include #include #include #include +#include +#include namespace xrpl { + +class STTx; + /** * We have both transaction type permissions and granular type permissions. * Since we will reuse the TransactionFormats to parse the Transaction @@ -19,15 +27,15 @@ namespace xrpl { // Macro-generated, complex // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) enum GranularPermissionType : std::uint32_t { -#pragma push_macro("PERMISSION") -#undef PERMISSION +#pragma push_macro("GRANULAR_PERMISSION") +#undef GRANULAR_PERMISSION -#define PERMISSION(type, txType, value) type = (value), +#define GRANULAR_PERMISSION(name, txType, value, ...) name = (value), #include -#undef PERMISSION -#pragma pop_macro("PERMISSION") +#undef GRANULAR_PERMISSION +#pragma pop_macro("GRANULAR_PERMISSION") }; // Injected bare enumerators (xrpl::delegable / xrpl::notDelegable) are required by preprocessor @@ -40,15 +48,30 @@ class Permission private: Permission(); - std::unordered_map txFeatureMap_; + struct GranularPermissionEntry + { + std::string name; + TxType txType; + std::uint32_t permittedFlags; + SOTemplate permittedFields; - std::unordered_map delegableTx_; + GranularPermissionEntry( + std::string name, + TxType txType, + std::uint32_t permittedFlags, + std::vector fields); + }; - std::unordered_map granularPermissionMap_; + struct TxDelegationEntry + { + uint256 amendment; + Delegation delegable{NotDelegable}; + }; - std::unordered_map granularNameMap_; - - std::unordered_map granularTxTypeMap_; + std::unordered_set granularTxTypes_; + std::unordered_map txDelegationMap_; + std::unordered_map granularPermissionsByName_; + std::unordered_map granularPermissions_; public: static Permission const& @@ -59,30 +82,52 @@ public: operator=(Permission const&) = delete; [[nodiscard]] std::optional - getPermissionName(std::uint32_t const value) const; + getPermissionName(std::uint32_t value) const; [[nodiscard]] std::optional getGranularValue(std::string const& name) const; [[nodiscard]] std::optional - getGranularName(GranularPermissionType const& value) const; + getGranularName(GranularPermissionType value) const; [[nodiscard]] std::optional - getGranularTxType(GranularPermissionType const& gpType) const; + getGranularTxType(GranularPermissionType gpType) const; + // Returns a reference to avoid copying uint256 - 32 bytes. std::optional + // cannot hold references directly, so std::reference_wrapper is used. [[nodiscard]] std::optional> getTxFeature(TxType txType) const; [[nodiscard]] bool - isDelegable(std::uint32_t const& permissionValue, Rules const& rules) const; + isDelegable(std::uint32_t permissionValue, Rules const& rules) const; + + [[nodiscard]] bool + hasGranularPermissions(TxType txType) const; // for tx level permission, permission value is equal to tx type plus one - static uint32_t - txToPermissionType(TxType const& type); + [[nodiscard]] static uint32_t + txToPermissionType(TxType type); // tx type value is permission value minus one - static TxType - permissionToTxType(uint32_t const& value); + [[nodiscard]] static std::optional + permissionToTxType(std::uint32_t value); + + /** + * @brief Verifies a delegated transaction against its granular permission template. + * + * @note WARNING: Do not move this check before standard transaction-level + * format checks, which is in preclaim. This function assumes the transaction's + * base structural integrity (fees, sequence, signatures) has already been + * validated. + * + * @param tx The transaction to verify. + * @param heldPermissions The granular permissions that the sender hold. + * @return true if the transaction fields and flags comply with the granular template. + */ + [[nodiscard]] bool + checkGranularSandbox( + STTx const& tx, + std::unordered_set const& heldPermissions) const; }; } // namespace xrpl diff --git a/include/xrpl/protocol/Protocol.h b/include/xrpl/protocol/Protocol.h index 6a96b2ccbe..f802cfe058 100644 --- a/include/xrpl/protocol/Protocol.h +++ b/include/xrpl/protocol/Protocol.h @@ -1,9 +1,15 @@ #pragma once #include +#include #include +#include #include +#include +#include + +#include #include namespace xrpl { @@ -307,4 +313,65 @@ constexpr std::size_t kPermissionMaxSize = 10; /** The maximum number of transactions that can be in a batch. */ constexpr std::size_t kMaxBatchTxCount = 8; +/** The maximum number of batch signers. */ +constexpr std::size_t kMaxBatchSigners = kMaxBatchTxCount * 3; + +/** Length of a secp256k1 scalar in bytes. */ +constexpr std::size_t kEcScalarLength = kMPT_SCALAR_SIZE; + +/** Length of EC point (compressed) */ +constexpr std::size_t kCompressedEcPointLength = 33; + +/** Length of one compressed EC point component in an EC ElGamal ciphertext. */ +constexpr std::size_t kEcCiphertextComponentLength = kMPT_ELGAMAL_CIPHER_SIZE; + +/** EC ElGamal ciphertext length: two compressed EC points concatenated. */ +constexpr std::size_t kEcGamalEncryptedTotalLength = kMPT_ELGAMAL_TOTAL_SIZE; + +/** Length of EC public key (compressed) */ +constexpr std::size_t kEcPubKeyLength = kMPT_PUBKEY_SIZE; + +/** Length of EC private key in bytes */ +constexpr std::size_t kEcPrivKeyLength = kMPT_PRIVKEY_SIZE; + +/** Length of the EC blinding factor in bytes */ +constexpr std::size_t kEcBlindingFactorLength = kMPT_BLINDING_FACTOR_SIZE; + +/** Length of Schnorr ZKProof for public key registration (compact form) in bytes */ +constexpr std::size_t kEcSchnorrProofLength = kMPT_SCHNORR_PROOF_SIZE; + +/** Length of Pedersen Commitment (compressed) */ +constexpr std::size_t kEcPedersenCommitmentLength = kMPT_PEDERSEN_COMMIT_SIZE; + +/** Length of single bulletproof (range proof for 1 commitment) in bytes */ +constexpr std::size_t kEcSingleBulletproofLength = kMPT_SINGLE_BULLETPROOF_SIZE; + +/** Length of double bulletproof (range proof for 2 commitments) in bytes */ +constexpr std::size_t kEcDoubleBulletproofLength = kMPT_DOUBLE_BULLETPROOF_SIZE; + +/** Length of the compact sigma proof component for ConfidentialMPTSend. */ +constexpr std::size_t kEcSendSigmaProofLength = SECP256K1_COMPACT_STANDARD_PROOF_SIZE; + +/** 192 bytes compact sigma proof + 754 bytes double bulletproof. */ +constexpr std::size_t kEcSendProofLength = kEcSendSigmaProofLength + kEcDoubleBulletproofLength; + +/** Length of the compact sigma proof component for ConfidentialMPTConvertBack. */ +constexpr std::size_t kEcConvertBackSigmaProofLength = SECP256K1_COMPACT_CONVERTBACK_PROOF_SIZE; + +/** 128 bytes compact sigma proof + 688 bytes single bulletproof. */ +constexpr std::size_t kEcConvertBackProofLength = + kEcConvertBackSigmaProofLength + kEcSingleBulletproofLength; + +/** Length of the ZKProof for ConfidentialMPTClawback. */ +constexpr std::size_t kEcClawbackProofLength = SECP256K1_COMPACT_CLAWBACK_PROOF_SIZE; + +/** Extra base fee multiplier charged to confidential MPT transactions. */ +constexpr std::uint32_t kConfidentialFeeMultiplier = 9; + +/** Compressed EC point prefix for even y-coordinate */ +constexpr std::uint8_t kEcCompressedPrefixEvenY = 0x02; + +/** Compressed EC point prefix for odd y-coordinate */ +constexpr std::uint8_t kEcCompressedPrefixOddY = 0x03; + } // namespace xrpl diff --git a/include/xrpl/protocol/PublicKey.h b/include/xrpl/protocol/PublicKey.h index 20693160d3..13db17fc6e 100644 --- a/include/xrpl/protocol/PublicKey.h +++ b/include/xrpl/protocol/PublicKey.h @@ -1,8 +1,15 @@ #pragma once #include +#include +#include +#include #include +#include +#include #include +#include +#include #include #include #include @@ -11,8 +18,11 @@ #include #include #include +#include #include #include +#include +#include namespace xrpl { diff --git a/include/xrpl/protocol/Quality.h b/include/xrpl/protocol/Quality.h index e261025cb8..de61d79ca5 100644 --- a/include/xrpl/protocol/Quality.h +++ b/include/xrpl/protocol/Quality.h @@ -1,11 +1,13 @@ #pragma once +#include +#include #include -#include #include -#include #include +#include +#include #include #include #include @@ -280,7 +282,7 @@ public: auto const maxVMantissa = mantissa(maxV); auto const expDiff = exponent(maxV) - exponent(minV); - double const minVD = static_cast(minVMantissa); + auto const minVD = static_cast(minVMantissa); double const maxVD = (expDiff != 0) ? maxVMantissa * pow(10, expDiff) : static_cast(maxVMantissa); diff --git a/include/xrpl/protocol/QualityFunction.h b/include/xrpl/protocol/QualityFunction.h index 96d30735b8..7830519deb 100644 --- a/include/xrpl/protocol/QualityFunction.h +++ b/include/xrpl/protocol/QualityFunction.h @@ -1,9 +1,15 @@ #pragma once #include +#include +#include #include #include +#include +#include +#include + namespace xrpl { /** Average quality of a path as a function of `out`: q(out) = m * out + b, diff --git a/include/xrpl/protocol/Rate.h b/include/xrpl/protocol/Rate.h index 504b17ed80..b8b04c8fb9 100644 --- a/include/xrpl/protocol/Rate.h +++ b/include/xrpl/protocol/Rate.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/include/xrpl/protocol/Rules.h b/include/xrpl/protocol/Rules.h index fbbd3d8805..da2031650f 100644 --- a/include/xrpl/protocol/Rules.h +++ b/include/xrpl/protocol/Rules.h @@ -4,7 +4,10 @@ #include #include +#include +#include #include +#include namespace xrpl { @@ -122,4 +125,15 @@ private: std::optional saved_; }; +class NumberMantissaScaleGuard; + +bool +useRulesGuards(Rules const& rules); + +void +createGuards( + Rules const& rules, + std::optional& rulesGuard, + std::optional& mantissaScaleGuard); + } // namespace xrpl diff --git a/include/xrpl/protocol/SField.h b/include/xrpl/protocol/SField.h index 1d4e4e69a0..d97bcb0a1d 100644 --- a/include/xrpl/protocol/SField.h +++ b/include/xrpl/protocol/SField.h @@ -2,10 +2,11 @@ #include #include -#include #include #include +#include +#include namespace xrpl { @@ -365,8 +366,8 @@ using SF_XCHAIN_BRIDGE = TypedField; #define UNTYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) extern SField const sfName; #define TYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) extern SF_##stiSuffix const sfName; -extern SField const kSfInvalid; -extern SField const kSfGeneric; +extern SField const sfInvalid; // NOLINT(readability-identifier-naming) +extern SField const sfGeneric; // NOLINT(readability-identifier-naming) #include diff --git a/include/xrpl/protocol/SOTemplate.h b/include/xrpl/protocol/SOTemplate.h index 72e0573d29..682a7c655e 100644 --- a/include/xrpl/protocol/SOTemplate.h +++ b/include/xrpl/protocol/SOTemplate.h @@ -3,9 +3,11 @@ #include #include +#include #include #include #include +#include #include namespace xrpl { diff --git a/include/xrpl/protocol/STAccount.h b/include/xrpl/protocol/STAccount.h index 65f404d58d..17d3affc57 100644 --- a/include/xrpl/protocol/STAccount.h +++ b/include/xrpl/protocol/STAccount.h @@ -1,9 +1,13 @@ #pragma once +#include #include #include +#include #include +#include +#include #include namespace xrpl { diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index 01511c23ea..5e53a85129 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -1,19 +1,35 @@ #pragma once #include -#include #include +#include +#include +#include +#include #include +#include +#include #include +#include #include #include #include +#include +#include #include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include + namespace xrpl { // Internal form: @@ -184,6 +200,23 @@ public: [[nodiscard]] STAmount const& value() const noexcept; + /** + * Checks if this amount evaluates to zero when constrained to a specific + * accounting scale. + * For XRP and MPT `roundToScale` is a no-op, returns true only when the amount itself is zero. + * The `scale` argument is ignored in that case. + * For IOU, the amount is rounded to the given scale using Number::RoundingMode::ToNearest mode + * and the result is checked for zero; if `scale <= exponent()`, `roundToScale` short-circuits + * and returns the value unchanged, so this returns false for any non-zero amount. + * + * @param scale The target accounting scale to evaluate against. + * @return `true` if this amount rounds to zero at the given scale, `false` otherwise. + * + * @see roundToScale + */ + [[nodiscard]] bool + isZeroAtScale(int scale) const; + //-------------------------------------------------------------------------- // // Operators @@ -540,7 +573,7 @@ STAmount::fromNumber(A const& a, Number const& number) return STAmount{asset, intValue, 0, negative}; } - auto const [mantissa, exponent] = working.normalizeToRange(kMinValue, kMaxValue); + auto const [mantissa, exponent] = working.normalizeToRange(); return STAmount{asset, mantissa, exponent, negative}; } @@ -575,12 +608,25 @@ STAmount::value() const noexcept return *this; } -inline bool +[[nodiscard]] inline bool isLegalNet(STAmount const& value) { return !value.native() || (value.mantissa() <= STAmount::kMaxNativeN); } +[[nodiscard]] inline bool +isLegalMPT(STAmount const& value) +{ + return !value.holds() || + (!value.negative() && value.exponent() == 0 && value.mantissa() <= kMaxMpTokenAmount); +} + +/* Check recursively if an object has invalid MPTAmount or XRPAmount in STAmount field. + * Calls isLegalNet() and isLegalMPT(). + */ +[[nodiscard]] bool +hasInvalidAmount(STBase const& field, beast::Journal j); + //------------------------------------------------------------------------------ // // Operators diff --git a/include/xrpl/protocol/STArray.h b/include/xrpl/protocol/STArray.h index 61753c52dc..573bb6dad8 100644 --- a/include/xrpl/protocol/STArray.h +++ b/include/xrpl/protocol/STArray.h @@ -1,7 +1,18 @@ #pragma once #include +#include +#include +#include #include +#include + +#include +#include +#include +#include +#include +#include namespace xrpl { @@ -21,17 +32,13 @@ public: STArray() = default; STArray(STArray const&) = default; - template < - class Iter, - class = std::enable_if_t< - std::is_convertible_v::reference, STObject>>> - explicit STArray(Iter first, Iter last); + template + explicit STArray(Iter first, Iter last) + requires(std::is_convertible_v::reference, STObject>); - template < - class Iter, - class = std::enable_if_t< - std::is_convertible_v::reference, STObject>>> - STArray(SField const& f, Iter first, Iter last); + template + STArray(SField const& f, Iter first, Iter last) + requires(std::is_convertible_v::reference, STObject>); STArray& operator=(STArray const&) = default; @@ -159,13 +166,17 @@ private: friend class detail::STVar; }; -template -STArray::STArray(Iter first, Iter last) : v_(first, last) +template +STArray::STArray(Iter first, Iter last) + requires(std::is_convertible_v::reference, STObject>) + : v_(first, last) { } -template -STArray::STArray(SField const& f, Iter first, Iter last) : STBase(f), v_(first, last) +template +STArray::STArray(SField const& f, Iter first, Iter last) + requires(std::is_convertible_v::reference, STObject>) + : STBase(f), v_(first, last) { } diff --git a/include/xrpl/protocol/STBase.h b/include/xrpl/protocol/STBase.h index 6633253d3b..341c80edd7 100644 --- a/include/xrpl/protocol/STBase.h +++ b/include/xrpl/protocol/STBase.h @@ -1,9 +1,12 @@ #pragma once #include +#include #include #include +#include +#include #include #include #include diff --git a/include/xrpl/protocol/STBitString.h b/include/xrpl/protocol/STBitString.h index 87c8cd4f45..6f71f48f25 100644 --- a/include/xrpl/protocol/STBitString.h +++ b/include/xrpl/protocol/STBitString.h @@ -1,8 +1,15 @@ #pragma once #include +#include #include +#include +#include #include +#include + +#include +#include namespace xrpl { @@ -141,7 +148,7 @@ template bool STBitString::isEquivalent(STBase const& t) const { - STBitString const* v = dynamic_cast(&t); + auto const* v = dynamic_cast(&t); return v && (value_ == v->value_); } @@ -163,7 +170,7 @@ STBitString::setValue(BaseUInt const& v) } template -typename STBitString::value_type const& +STBitString::value_type const& STBitString::value() const { return value_; diff --git a/include/xrpl/protocol/STBlob.h b/include/xrpl/protocol/STBlob.h index c9110a367e..ab6175f5e3 100644 --- a/include/xrpl/protocol/STBlob.h +++ b/include/xrpl/protocol/STBlob.h @@ -3,10 +3,14 @@ #include #include #include -#include +#include #include +#include +#include #include +#include +#include namespace xrpl { @@ -24,7 +28,7 @@ public: STBlob(SField const& f, void const* data, std::size_t size); STBlob(SField const& f, Buffer&& b); STBlob(SField const& n); - STBlob(SerialIter&, SField const& name = kSfGeneric); + STBlob(SerialIter&, SField const& name = sfGeneric); [[nodiscard]] std::size_t size() const; diff --git a/include/xrpl/protocol/STCurrency.h b/include/xrpl/protocol/STCurrency.h index 55d1ab1e74..18642b20cf 100644 --- a/include/xrpl/protocol/STCurrency.h +++ b/include/xrpl/protocol/STCurrency.h @@ -1,11 +1,15 @@ #pragma once -#include +#include #include #include #include #include +#include +#include +#include + namespace xrpl { class STCurrency final : public STBase diff --git a/include/xrpl/protocol/STExchange.h b/include/xrpl/protocol/STExchange.h index c733df37cf..a9c1f57bd8 100644 --- a/include/xrpl/protocol/STExchange.h +++ b/include/xrpl/protocol/STExchange.h @@ -1,14 +1,15 @@ #pragma once -#include #include #include #include #include +#include #include #include #include +#include #include #include #include diff --git a/include/xrpl/protocol/STInteger.h b/include/xrpl/protocol/STInteger.h index 4e3c9a8923..951c4fc52f 100644 --- a/include/xrpl/protocol/STInteger.h +++ b/include/xrpl/protocol/STInteger.h @@ -1,7 +1,15 @@ #pragma once #include +#include +#include +#include #include +#include + +#include +#include +#include namespace xrpl { @@ -107,7 +115,7 @@ template inline bool STInteger::isEquivalent(STBase const& t) const { - STInteger const* v = dynamic_cast(&t); + auto const* v = dynamic_cast(&t); return v && (value_ == v->value_); } @@ -120,7 +128,7 @@ STInteger::operator=(value_type const& v) } template -inline typename STInteger::value_type +inline STInteger::value_type STInteger::value() const noexcept { return value_; diff --git a/include/xrpl/protocol/STIssue.h b/include/xrpl/protocol/STIssue.h index f5e1f61168..8ff579553f 100644 --- a/include/xrpl/protocol/STIssue.h +++ b/include/xrpl/protocol/STIssue.h @@ -1,11 +1,20 @@ #pragma once #include +#include +#include #include +#include +#include #include #include #include +#include +#include +#include +#include + namespace xrpl { class STIssue final : public STBase, CountedObject diff --git a/include/xrpl/protocol/STLedgerEntry.h b/include/xrpl/protocol/STLedgerEntry.h index aa87411ae6..a5f449f99c 100644 --- a/include/xrpl/protocol/STLedgerEntry.h +++ b/include/xrpl/protocol/STLedgerEntry.h @@ -1,7 +1,19 @@ #pragma once -#include +#include +#include +#include +#include +#include +#include +#include #include +#include + +#include +#include +#include +#include namespace xrpl { diff --git a/include/xrpl/protocol/STNumber.h b/include/xrpl/protocol/STNumber.h index 8594a292f4..7efb63ac5e 100644 --- a/include/xrpl/protocol/STNumber.h +++ b/include/xrpl/protocol/STNumber.h @@ -2,10 +2,17 @@ #include #include +#include +#include +#include #include #include +#include +#include +#include #include +#include namespace xrpl { diff --git a/include/xrpl/protocol/STObject.h b/include/xrpl/protocol/STObject.h index c635e8ce22..fe5c611648 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -1,27 +1,39 @@ #pragma once +#include #include +#include #include -#include +#include #include #include +#include +#include #include +#include #include #include #include +#include #include #include #include #include +#include #include #include #include +#include +#include +#include #include #include +#include #include #include +#include namespace xrpl { @@ -217,6 +229,11 @@ public: [[nodiscard]] AccountID getAccountID(SField const& field) const; + /** The account responsible for the fee and authorization: the delegate when + sfDelegate is present, otherwise the account. */ + [[nodiscard]] AccountID + getFeePayer() const; + [[nodiscard]] Blob getFieldVL(SField const& field) const; [[nodiscard]] STAmount const& @@ -243,7 +260,7 @@ public: @throws STObject::FieldErr if the field is not present. */ template - typename T::value_type + T::value_type operator[](TypedField const& f) const; /** Get the value of a field as a std::optional @@ -290,7 +307,7 @@ public: @throws STObject::FieldErr if the field is not present. */ template - [[nodiscard]] typename T::value_type + [[nodiscard]] T::value_type at(TypedField const& f) const; /** Get the value of a field as std::optional @@ -478,7 +495,7 @@ template class STObject::Proxy { public: - using value_type = typename T::value_type; + using value_type = T::value_type; [[nodiscard]] value_type value() const; @@ -513,13 +530,10 @@ protected: template concept IsArithmeticNumber = std::is_arithmetic_v || std::is_same_v || std::is_same_v; -template < - typename U, - typename Value = typename U::value_type, - typename Unit = typename U::unit_type> +template concept IsArithmeticValueUnit = std::is_same_v> && IsArithmeticNumber && std::is_class_v; -template +template concept IsArithmeticST = !IsArithmeticValueUnit && IsArithmeticNumber; template concept IsArithmetic = IsArithmeticNumber || IsArithmeticST || IsArithmeticValueUnit; @@ -534,16 +548,21 @@ template class STObject::ValueProxy : public Proxy { private: - using value_type = typename T::value_type; + using value_type = T::value_type; public: ValueProxy(ValueProxy const&) = default; ValueProxy& operator=(ValueProxy const&) = delete; + // Write-through proxy: assignment sets the referenced field to the given + // value, so it intentionally takes the assigned value rather than a + // ValueProxy. template - std::enable_if_t, ValueProxy&> - operator=(U&& u); + // NOLINTNEXTLINE(misc-unconventional-assign-operator) + ValueProxy& + operator=(U&& u) + requires(std::is_assignable_v); // Convenience operators for value types supporting // arithmetic operations @@ -576,7 +595,7 @@ template class STObject::OptionalProxy : public Proxy { private: - using value_type = typename T::value_type; + using value_type = T::value_type; using optional_type = std::optional>; @@ -677,8 +696,9 @@ public: operator=(optional_type const& v); template - std::enable_if_t, OptionalProxy&> - operator=(U&& u); + OptionalProxy& + operator=(U&& u) + requires(std::is_assignable_v); private: friend class STObject; @@ -784,8 +804,10 @@ STObject::Proxy::assign(U&& u) template template -std::enable_if_t, STObject::ValueProxy&> +// NOLINTNEXTLINE(misc-unconventional-assign-operator) +STObject::ValueProxy& STObject::ValueProxy::operator=(U&& u) + requires(std::is_assignable_v) { this->assign(std::forward(u)); return *this; @@ -840,7 +862,7 @@ operator typename STObject::OptionalProxy::optional_type() const } template -typename STObject::OptionalProxy::optional_type +STObject::OptionalProxy::optional_type STObject::OptionalProxy::operator~() const { return optionalValue(); @@ -888,8 +910,9 @@ STObject::OptionalProxy::operator=(optional_type const& v) -> OptionalProxy& template template -std::enable_if_t, STObject::OptionalProxy&> +STObject::OptionalProxy& STObject::OptionalProxy::operator=(U&& u) + requires(std::is_assignable_v) { this->assign(std::forward(u)); return *this; @@ -933,7 +956,7 @@ STObject::OptionalProxy::optionalValue() const -> optional_type } template -typename STObject::OptionalProxy::value_type +STObject::OptionalProxy::value_type STObject::OptionalProxy::valueOr(value_type val) const { return engaged() ? this->value() : val; @@ -1040,7 +1063,7 @@ STObject::getPIndex(int offset) } template -typename T::value_type +T::value_type STObject::operator[](TypedField const& f) const { return at(f); @@ -1068,7 +1091,7 @@ STObject::operator[](OptionaledField const& of) -> OptionalProxy } template -[[nodiscard]] typename T::value_type +[[nodiscard]] T::value_type STObject::at(TypedField const& f) const { auto const b = peekAtPField(f); @@ -1227,7 +1250,7 @@ template void STObject::setFieldUsingSetValue(SField const& field, V value) { - static_assert(!std::is_lvalue_reference_v, ""); + static_assert(!std::is_lvalue_reference_v); STBase* rf = getPField(field, true); diff --git a/include/xrpl/protocol/STParsedJSON.h b/include/xrpl/protocol/STParsedJSON.h index 2557ab055b..1eeecc8b9e 100644 --- a/include/xrpl/protocol/STParsedJSON.h +++ b/include/xrpl/protocol/STParsedJSON.h @@ -1,8 +1,11 @@ #pragma once -#include +#include +#include +#include #include +#include namespace xrpl { diff --git a/include/xrpl/protocol/STPathSet.h b/include/xrpl/protocol/STPathSet.h index a1891164f6..23f4e653c4 100644 --- a/include/xrpl/protocol/STPathSet.h +++ b/include/xrpl/protocol/STPathSet.h @@ -3,14 +3,17 @@ #include #include #include -#include +#include #include #include #include +#include #include #include #include +#include +#include namespace xrpl { @@ -21,8 +24,8 @@ class STPathElement final : public CountedObject PathAsset assetID_; AccountID issuerID_; - bool is_offer_; - std::size_t hash_value_; + bool isOffer_; + std::size_t hashValue_; public: // Bitwise values (typeCurrency | typeMPT) @@ -235,9 +238,12 @@ private: // ------------ STPathElement ------------ -inline STPathElement::STPathElement() : type_(TypeNone), is_offer_(true) +inline STPathElement::STPathElement() : type_(TypeNone), isOffer_(true) { - hash_value_ = getHash(*this); + // hashValue_ is derived from the whole object, so it is computed in the body + // once every other member is initialized (as in the other constructors). + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + hashValue_ = getHash(*this); } inline STPathElement::STPathElement( @@ -248,11 +254,11 @@ inline STPathElement::STPathElement( { if (!account) { - is_offer_ = true; + isOffer_ = true; } else { - is_offer_ = false; + isOffer_ = false; accountID_ = *account; type_ |= TypeAccount; XRPL_ASSERT( @@ -272,7 +278,7 @@ inline STPathElement::STPathElement( XRPL_ASSERT(issuerID_ != noAccount(), "xrpl::STPathElement::STPathElement : issuer is set"); } - hash_value_ = getHash(*this); + hashValue_ = getHash(*this); } inline STPathElement::STPathElement( @@ -284,9 +290,9 @@ inline STPathElement::STPathElement( , accountID_(account) , assetID_(asset) , issuerID_(issuer) - , is_offer_(isXRP(accountID_)) + , isOffer_(isXRP(accountID_)) { - if (!is_offer_) + if (!isOffer_) type_ |= TypeAccount; if (forceAsset || !isXRP(assetID_)) @@ -295,7 +301,7 @@ inline STPathElement::STPathElement( if (!isXRP(issuer)) type_ |= TypeIssuer; - hash_value_ = getHash(*this); + hashValue_ = getHash(*this); } inline STPathElement::STPathElement( @@ -307,12 +313,15 @@ inline STPathElement::STPathElement( , accountID_(account) , assetID_(asset) , issuerID_(issuer) - , is_offer_(isXRP(accountID_)) + , isOffer_(isXRP(accountID_)) { assetID_.visit( [&](Currency const&) { type_ = type_ & (~Type::TypeMpt); }, [&](MPTID const&) { type_ = type_ & (~Type::TypeCurrency); }); - hash_value_ = getHash(*this); + // hashValue_ must be computed after type_ is adjusted above, so this cannot + // be a member initializer. + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + hashValue_ = getHash(*this); } inline auto @@ -324,7 +333,7 @@ STPathElement::getNodeType() const inline bool STPathElement::isOffer() const { - return is_offer_; + return isOffer_; } inline bool @@ -404,7 +413,7 @@ STPathElement::getIssuerID() const inline bool STPathElement::operator==(STPathElement const& t) const { - return (type_ & TypeAccount) == (t.type_ & TypeAccount) && hash_value_ == t.hash_value_ && + return (type_ & TypeAccount) == (t.type_ & TypeAccount) && hashValue_ == t.hashValue_ && accountID_ == t.accountID_ && assetID_ == t.assetID_ && issuerID_ == t.issuerID_; } diff --git a/include/xrpl/protocol/STTakesAsset.h b/include/xrpl/protocol/STTakesAsset.h index bf75ffccf7..70bafd0e91 100644 --- a/include/xrpl/protocol/STTakesAsset.h +++ b/include/xrpl/protocol/STTakesAsset.h @@ -3,6 +3,8 @@ #include #include +#include + namespace xrpl { /** Intermediate class for any STBase-derived class to store an Asset. diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index 5aa70d1299..b36207bf61 100644 --- a/include/xrpl/protocol/STTx.h +++ b/include/xrpl/protocol/STTx.h @@ -1,17 +1,30 @@ #pragma once -#include -#include +#include +#include +#include +#include +#include #include #include +#include +#include #include #include #include +#include #include #include +#include +#include +#include #include +#include +#include +#include +#include namespace xrpl { @@ -27,7 +40,7 @@ enum class TxnSql : char { class STTx final : public STObject, public CountedObject { uint256 tid_; - TxType tx_type_; + TxType txType_; public: static constexpr std::size_t kMinMultiSigners = 1; @@ -51,51 +64,48 @@ public: STTx(TxType type, std::function assembler); // STObject functions. - SerializedTypeID + [[nodiscard]] SerializedTypeID getSType() const override; - std::string + [[nodiscard]] std::string getFullText() const override; // Outer transaction functions / signature functions. static Blob getSignature(STObject const& sigObject); - Blob + [[nodiscard]] Blob getSignature() const { return getSignature(*this); } - uint256 + [[nodiscard]] uint256 getSigningHash() const; - TxType + [[nodiscard]] TxType getTxnType() const; - Blob + [[nodiscard]] Blob getSigningPubKey() const; - SeqProxy + [[nodiscard]] SeqProxy getSeqProxy() const; /** Returns the first non-zero value of (Sequence, TicketSequence). */ - std::uint32_t + [[nodiscard]] std::uint32_t getSeqValue() const; - AccountID - getFeePayer() const; - - boost::container::flat_set + [[nodiscard]] boost::container::flat_set getMentionedAccounts() const; - uint256 + [[nodiscard]] uint256 getTransactionID() const; - json::Value + [[nodiscard]] json::Value getJson(JsonOptions options) const override; - json::Value + [[nodiscard]] json::Value getJson(JsonOptions options, bool binary) const; void @@ -108,27 +118,27 @@ public: @param rules The current ledger rules. @return `true` if valid signature. If invalid, the error message string. */ - Expected + [[nodiscard]] std::expected checkSign(Rules const& rules) const; - Expected + [[nodiscard]] std::expected checkBatchSign(Rules const& rules) const; // SQL Functions with metadata. static std::string const& getMetaSQLInsertReplaceHeader(); - std::string + [[nodiscard]] std::string getMetaSQL(std::uint32_t inLedger, std::string const& escapedMetaData) const; - std::string + [[nodiscard]] std::string getMetaSQL( Serializer rawTxn, std::uint32_t inLedger, TxnSql status, std::string const& escapedMetaData) const; - std::vector const& + [[nodiscard]] std::vector const& getBatchTransactionIDs() const; private: @@ -138,28 +148,31 @@ private: Will be *this more often than not. @return `true` if valid signature. If invalid, the error message string. */ - Expected + [[nodiscard]] std::expected checkSign(Rules const& rules, STObject const& sigObject) const; - Expected + [[nodiscard]] std::expected checkSingleSign(STObject const& sigObject) const; - Expected + [[nodiscard]] std::expected checkMultiSign(Rules const& rules, STObject const& sigObject) const; - Expected + [[nodiscard]] std::expected checkBatchSingleSign(STObject const& batchSigner) const; - Expected + [[nodiscard]] std::expected checkBatchMultiSign(STObject const& batchSigner, Rules const& rules) const; + void + buildBatchTxnIds(); + STBase* copy(std::size_t n, void* buf) const override; STBase* move(std::size_t n, void* buf) override; friend class detail::STVar; - mutable std::vector batchTxnIds_; + std::optional> batchTxnIds_; }; bool @@ -187,7 +200,7 @@ inline STTx::STTx(SerialIter&& sit) // NOLINT(cppcoreguidelines-rvalue-referenc inline TxType STTx::getTxnType() const { - return tx_type_; + return txType_; } inline Blob diff --git a/include/xrpl/protocol/STValidation.h b/include/xrpl/protocol/STValidation.h index 91ce88b441..67a6594419 100644 --- a/include/xrpl/protocol/STValidation.h +++ b/include/xrpl/protocol/STValidation.h @@ -1,15 +1,30 @@ #pragma once +#include +#include #include +#include +#include +#include +#include #include +#include #include +#include +#include +#include #include #include -#include +#include +#include +#include +#include #include #include #include +#include +#include namespace xrpl { @@ -72,35 +87,35 @@ public: F&& f); // Hash of the validated ledger - uint256 + [[nodiscard]] uint256 getLedgerHash() const; // Hash of consensus transaction set used to generate ledger - uint256 + [[nodiscard]] uint256 getConsensusHash() const; - NetClock::time_point + [[nodiscard]] NetClock::time_point getSignTime() const; - NetClock::time_point + [[nodiscard]] NetClock::time_point getSeenTime() const noexcept; - PublicKey const& + [[nodiscard]] PublicKey const& getSignerPublic() const noexcept; - NodeID const& + [[nodiscard]] NodeID const& getNodeID() const noexcept; - bool + [[nodiscard]] bool isValid() const noexcept; - bool + [[nodiscard]] bool isFull() const noexcept; - bool + [[nodiscard]] bool isTrusted() const noexcept; - uint256 + [[nodiscard]] uint256 getSigningHash() const; void @@ -112,13 +127,13 @@ public: void setSeen(NetClock::time_point s); - Blob + [[nodiscard]] Blob getSerialized() const; - Blob + [[nodiscard]] Blob getSignature() const; - std::string + [[nodiscard]] std::string render() const { std::stringstream ss; diff --git a/include/xrpl/protocol/STVector256.h b/include/xrpl/protocol/STVector256.h index ab3a2f99e3..46a1abc713 100644 --- a/include/xrpl/protocol/STVector256.h +++ b/include/xrpl/protocol/STVector256.h @@ -1,9 +1,15 @@ #pragma once #include +#include +#include +#include #include -#include -#include +#include + +#include +#include +#include namespace xrpl { @@ -17,8 +23,8 @@ public: STVector256() = default; explicit STVector256(SField const& n); - explicit STVector256(std::vector const& vector); - STVector256(SField const& n, std::vector const& vector); + explicit STVector256(std::vector vector); + STVector256(SField const& n, std::vector vector); STVector256(SerialIter& sit, SField const& name); [[nodiscard]] SerializedTypeID @@ -103,12 +109,12 @@ inline STVector256::STVector256(SField const& n) : STBase(n) { } -inline STVector256::STVector256(std::vector const& vector) : value_(vector) +inline STVector256::STVector256(std::vector vector) : value_(std::move(vector)) { } -inline STVector256::STVector256(SField const& n, std::vector const& vector) - : STBase(n), value_(vector) +inline STVector256::STVector256(SField const& n, std::vector vector) + : STBase(n), value_(std::move(vector)) { } diff --git a/include/xrpl/protocol/STXChainBridge.h b/include/xrpl/protocol/STXChainBridge.h index 292ffe2767..24d64ef02b 100644 --- a/include/xrpl/protocol/STXChainBridge.h +++ b/include/xrpl/protocol/STXChainBridge.h @@ -1,9 +1,19 @@ #pragma once #include +#include +#include +#include +#include #include #include #include +#include + +#include +#include +#include +#include namespace xrpl { diff --git a/include/xrpl/protocol/SecretKey.h b/include/xrpl/protocol/SecretKey.h index 712b095f81..8a0d917ab4 100644 --- a/include/xrpl/protocol/SecretKey.h +++ b/include/xrpl/protocol/SecretKey.h @@ -2,14 +2,18 @@ #include #include +#include #include #include #include #include #include +#include #include +#include #include +#include namespace xrpl { diff --git a/include/xrpl/protocol/Seed.h b/include/xrpl/protocol/Seed.h index 0b93b84516..a669f52079 100644 --- a/include/xrpl/protocol/Seed.h +++ b/include/xrpl/protocol/Seed.h @@ -5,7 +5,10 @@ #include #include +#include +#include #include +#include namespace xrpl { diff --git a/include/xrpl/protocol/Serializer.h b/include/xrpl/protocol/Serializer.h index ffe9afabe8..73bd9c8289 100644 --- a/include/xrpl/protocol/Serializer.h +++ b/include/xrpl/protocol/Serializer.h @@ -6,13 +6,14 @@ #include #include #include -#include #include #include #include #include #include +#include +#include #include namespace xrpl { @@ -333,7 +334,7 @@ public: template explicit SerialIter(std::uint8_t const (&data)[N]) : SerialIter(&data[0], N) { - static_assert(N > 0, ""); + static_assert(N > 0); } [[nodiscard]] bool diff --git a/include/xrpl/protocol/Sign.h b/include/xrpl/protocol/Sign.h index 0b5b5d7239..18f085352d 100644 --- a/include/xrpl/protocol/Sign.h +++ b/include/xrpl/protocol/Sign.h @@ -1,9 +1,13 @@ #pragma once +#include #include +#include #include +#include #include #include +#include namespace xrpl { diff --git a/include/xrpl/protocol/SystemParameters.h b/include/xrpl/protocol/SystemParameters.h index 1cc35a0f31..b31dd0cd42 100644 --- a/include/xrpl/protocol/SystemParameters.h +++ b/include/xrpl/protocol/SystemParameters.h @@ -1,9 +1,12 @@ #pragma once +#include #include #include +#include #include +#include #include namespace xrpl { diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index c89610f354..54b081f358 100644 --- a/include/xrpl/protocol/TER.h +++ b/include/xrpl/protocol/TER.h @@ -8,7 +8,9 @@ #include #include #include +#include #include +#include namespace xrpl { @@ -128,6 +130,7 @@ enum TEMcodes : TERUnderlyingType { temBAD_TRANSFER_FEE, temINVALID_INNER_BATCH, temBAD_MPT, + temBAD_CIPHERTEXT, }; //------------------------------------------------------------------------------ @@ -174,6 +177,8 @@ enum TEFcodes : TERUnderlyingType { tefNO_TICKET, tefNFTOKEN_IS_NOT_TRANSFERABLE, tefINVALID_LEDGER_FIX_TYPE, + tefNO_DST_PARTIAL, + tefBAD_PATH_COUNT, }; //------------------------------------------------------------------------------ @@ -358,6 +363,11 @@ enum TECcodes : TERUnderlyingType { tecLIMIT_EXCEEDED = 195, tecPSEUDO_ACCOUNT = 196, tecPRECISION_LOSS = 197, + // DEPRECATED: This error code tecNO_DELEGATE_PERMISSION is reserved for + // backward compatibility with historical data on non-prod networks, can be + // reclaimed after those networks reset. + tecNO_DELEGATE_PERMISSION = 198, + tecBAD_PROOF = 199, }; //------------------------------------------------------------------------------ @@ -401,7 +411,7 @@ TERtoInt(TECcodes v) //------------------------------------------------------------------------------ // Template class that is specific to selected ranges of error codes. The -// Trait tells std::enable_if which ranges are allowed. +// Trait tells the requires-clause which ranges are allowed. template