diff --git a/.clang-tidy b/.clang-tidy index e09d326916..68fc9e75fc 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,158 +1,76 @@ --- 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-container-data-pointer, + -readability-function-cognitive-complexity, + -readability-identifier-length, + -readability-inconsistent-declaration-parameter-name, + -readability-isolate-declaration, + -readability-magic-numbers, + -readability-named-parameter, + -readability-qualified-auto, + -readability-redundant-access-specifiers, + -readability-static-accessed-through-instance, + -readability-uppercase-literal-suffix " # --- +# 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 # --- @@ -161,7 +79,7 @@ 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 diff --git a/cspell.config.yaml b/.cspell.config.yaml similarity index 94% rename from cspell.config.yaml rename to .cspell.config.yaml index 77f0e9df7a..13da132b90 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: @@ -60,12 +58,14 @@ words: - autobridging - bimap - bindir + - blindings - bookdir - Bougalis - Britto - Btrfs - Buildx - canonicality + - canonicalised - changespq - checkme - choco @@ -73,6 +73,7 @@ words: - citardauq - clawback - clawbacks + - clippy - cmaketoolchain - coeffs - coldwallet @@ -95,6 +96,7 @@ words: - daria - dcmake - dearmor + - decryptor - dedented - deleteme - demultiplexer @@ -106,9 +108,11 @@ words: - distro - doxyfile - dxrpl + - elgamal - enabled - enablerepo - endmacro + - envrc - exceptioned - EXPECT_STREQ - Falco @@ -118,6 +122,7 @@ words: - fmtdur - fsanitize - funclets + - Gamal - gcov - gcovr - ghead @@ -215,6 +220,7 @@ words: - partitioner - paychan - paychans + - Pedersen - permdex - perminute - permissioned @@ -238,6 +244,10 @@ words: - Raphson - rcflags - replayer + - rerandomize + - rerandomization + - rerandomized + - rerandomizes - rerere - retriable - RIPD @@ -251,9 +261,11 @@ words: - rocksdb - Rohrs - roundings + - rustc - sahyadri - Satoshi - scons + - Schnorr - secp - sendq - seqit @@ -270,6 +282,8 @@ words: - sles - soci - socidb + - sponsee + - sponsees - SRPMS - sslws - statsd @@ -284,12 +298,14 @@ words: - stvar - stvector - stxchainattestations + - summands - superpeer - superpeers - takergets - takerpays - ters - TMEndpointv2 + - tparam - trixie - tx - txid @@ -300,6 +316,7 @@ words: - txs - ubsan - UBSAN + - ufdio - umant - unacquired - unambiguity @@ -316,6 +333,7 @@ words: - unserviced - unshareable - unshares + - unsponsored - unsquelch - unsquelched - unsquelching 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 245f827f90..aa63076c8b 100644 --- a/.gersemi/definitions.cmake +++ b/.gersemi/definitions.cmake @@ -48,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() @@ -96,3 +102,6 @@ function(verbose_find_path variable name) ${ARGN} ) endfunction() + +function(patch_nix_binary target) +endfunction() diff --git a/.github/actions/setup-conan/action.yml b/.github/actions/setup-conan/action.yml index 0dd22f0d92..e8a548cfce 100644 --- a/.github/actions/setup-conan/action.yml +++ b/.github/actions/setup-conan/action.yml @@ -9,7 +9,7 @@ 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 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 12176ec0d4..b31e6ae961 100644 --- a/.github/scripts/levelization/results/ordering.txt +++ b/.github/scripts/levelization/results/ordering.txt @@ -14,7 +14,6 @@ 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 @@ -73,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 @@ -84,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 @@ -107,9 +104,9 @@ 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 @@ -162,11 +159,9 @@ test.peerfinder > xrpl.protocol test.protocol > test.jtx test.protocol > test.unit_test test.protocol > xrpl.basics +test.protocol > xrpld.core 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 @@ -190,14 +185,6 @@ 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.config -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 @@ -209,6 +196,7 @@ 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 @@ -220,12 +208,14 @@ 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 @@ -243,7 +233,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 @@ -298,8 +287,10 @@ 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 @@ -317,5 +308,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/strategy-matrix/generate.py b/.github/scripts/strategy-matrix/generate.py index 6353567f27..c783f32fb7 100755 --- a/.github/scripts/strategy-matrix/generate.py +++ b/.github/scripts/strategy-matrix/generate.py @@ -20,31 +20,21 @@ _SANITIZER_SUFFIX: dict[str, str] = { 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 build_type == "Release": - args.append("-Dassert=ON") if extra_args: args.extend(extra_args.split()) return " ".join(args) -def runs_on_event(exclude_event_types: list[str], event: str | None) -> bool: - """Whether a config should run for the current event. - - '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. - """ - if event is None: - return True - return event not in exclude_event_types - - # --------------------------------------------------------------------------- # Input types — shapes of the JSON config files # --------------------------------------------------------------------------- +# Every config must declare 'minimal'. Minimal configs form the reduced matrix +# built for pull requests by default; the full matrix adds the rest. Packaging +# configs declare it too, but packaging is gated in the workflow, not by it. + + @dataclasses.dataclass class LinuxConfig: """One entry in linux.json's 'configs' or 'package_configs' arrays.""" @@ -52,13 +42,11 @@ class LinuxConfig: compiler: list[str] build_type: list[str] arch: list[str] + minimal: bool 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) @dataclasses.dataclass @@ -91,11 +79,9 @@ class PlatformConfig: """One entry in macos.json's or windows.json's 'configs' array.""" build_type: list[str] + minimal: bool 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): @@ -170,20 +156,18 @@ _ARCHS: dict[str, Architecture] = { } -def expand_linux_matrix( - linux: LinuxFile, event: str | None = None -) -> list[MatrixEntry]: +def expand_linux_matrix(linux: LinuxFile, minimal: bool) -> 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. + compiler, build_type, sanitizers, and architecture lists. When 'minimal' is + true, only configs flagged as minimal are included. """ entries: list[MatrixEntry] = [] for distro, configs in linux.configs.items(): for cfg in configs: - if not runs_on_event(cfg.exclude_event_types, event): + if minimal and not cfg.minimal: continue # An empty sanitizers list means "one entry with no sanitizer". effective_sanitizers = cfg.sanitizers or [""] @@ -242,19 +226,17 @@ def expand_linux_packaging(linux: LinuxFile) -> list[PackagingEntry]: return entries -def expand_platform_matrix( - pf: PlatformFile, event: str | None = None -) -> list[MatrixEntry]: +def expand_platform_matrix(pf: PlatformFile, minimal: bool) -> list[MatrixEntry]: """Expand a PlatformFile (macOS or Windows) into matrix entries. - Configs that exclude the current event are skipped. + When 'minimal' is true, only configs flagged as minimal are included. """ 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): + if minimal and not cfg.minimal: continue for build_type in cfg.build_type: entries.append( @@ -294,12 +276,12 @@ if __name__ == "__main__": 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, + "-m", + "--minimal", + help="Emit only the minimal matrix (the configs flagged 'minimal'), " + "used for pull requests by default. If omitted, the full matrix is " + "emitted.", + action="store_true", ) args = parser.parse_args() @@ -310,15 +292,15 @@ if __name__ == "__main__": else: if args.config in ("linux", None): matrix += expand_linux_matrix( - LinuxFile.load(THIS_DIR / "linux.json"), args.event + LinuxFile.load(THIS_DIR / "linux.json"), args.minimal ) if args.config in ("macos", None): matrix += expand_platform_matrix( - PlatformFile.load(THIS_DIR / "macos.json"), args.event + PlatformFile.load(THIS_DIR / "macos.json"), args.minimal ) if args.config in ("windows", None): matrix += expand_platform_matrix( - PlatformFile.load(THIS_DIR / "windows.json"), args.event + PlatformFile.load(THIS_DIR / "windows.json"), args.minimal ) 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 4f45216cda..03ac1c6334 100644 --- a/.github/scripts/strategy-matrix/linux.json +++ b/.github/scripts/strategy-matrix/linux.json @@ -1,17 +1,31 @@ { - "image_tag": "sha-63ffdc3", + "image_tag": "sha-e29b523", "configs": { "ubuntu": [ + { + "compiler": ["clang"], + "build_type": ["Release"], + "arch": ["amd64"], + "minimal": true + }, + { + "compiler": ["gcc"], + "build_type": ["Release"], + "arch": ["amd64"], + "minimal": false + }, { "compiler": ["gcc", "clang"], "build_type": ["Debug", "Release"], - "arch": ["amd64", "arm64"] + "arch": ["arm64"], + "minimal": false }, { "compiler": ["gcc", "clang"], "build_type": ["Debug", "Release"], "arch": ["amd64"], + "minimal": false, "sanitizers": ["address", "undefinedbehavior"] }, @@ -19,6 +33,7 @@ "compiler": ["gcc"], "build_type": ["Debug"], "arch": ["amd64"], + "minimal": true, "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" }, @@ -26,6 +41,7 @@ "compiler": ["clang"], "build_type": ["Debug"], "arch": ["amd64"], + "minimal": false, "suffix": "voidstar", "extra_cmake_args": "-Dvoidstar=ON" }, @@ -33,6 +49,7 @@ "compiler": ["clang"], "build_type": ["Release"], "arch": ["amd64"], + "minimal": false, "suffix": "reffee", "extra_cmake_args": "-DUNIT_TEST_REFERENCE_FEE=1000" }, @@ -40,9 +57,9 @@ "compiler": ["gcc"], "build_type": ["Debug"], "arch": ["amd64"], + "minimal": false, "suffix": "unity", - "extra_cmake_args": "-Dunity=ON", - "exclude_event_types": ["pull_request"] + "extra_cmake_args": "-Dunity=ON" } ], @@ -50,7 +67,8 @@ { "compiler": ["gcc"], "build_type": ["Release"], - "arch": ["amd64"] + "arch": ["amd64"], + "minimal": false } ], @@ -58,7 +76,8 @@ { "compiler": ["gcc"], "build_type": ["Release"], - "arch": ["amd64"] + "arch": ["amd64"], + "minimal": false } ] }, @@ -68,7 +87,8 @@ "compiler": ["gcc"], "build_type": ["Release"], "arch": ["amd64"], - "image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-63ffdc3" + "minimal": false, + "image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-577d745" } ], @@ -77,7 +97,8 @@ "compiler": ["gcc"], "build_type": ["Release"], "arch": ["amd64"], - "image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-63ffdc3" + "minimal": false, + "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 66d7a55a43..98e0f13141 100644 --- a/.github/scripts/strategy-matrix/macos.json +++ b/.github/scripts/strategy-matrix/macos.json @@ -1,16 +1,17 @@ { "platform": "macos/arm64", - "runner": ["self-hosted", "macOS", "ARM64", "mac-runner-m1"], + "runner": ["self-hosted", "macOS", "ARM64", "macos-26-apple-clang-21"], "configs": [ { "build_type": "Release", - "extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5" + "extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5", + "minimal": true }, { "build_type": "Debug", "extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5", "build_only": true, - "exclude_event_types": ["pull_request"] + "minimal": false } ] } diff --git a/.github/scripts/strategy-matrix/windows.json b/.github/scripts/strategy-matrix/windows.json index e25f9ad131..6b926e85f5 100644 --- a/.github/scripts/strategy-matrix/windows.json +++ b/.github/scripts/strategy-matrix/windows.json @@ -1,12 +1,12 @@ { "platform": "windows/amd64", - "runner": ["self-hosted", "Windows", "devbox"], + "runner": ["self-hosted", "Windows", "dev-box-windows-2026"], "configs": [ - { "build_type": "Release" }, + { "build_type": "Release", "minimal": true }, { "build_type": "Debug", "build_only": true, - "exclude_event_types": ["pull_request"] + "minimal": false } ] } diff --git a/.github/workflows/build-nix-images.yml b/.github/workflows/build-nix-images.yml index 24f069902d..54911ef6e0 100644 --- a/.github/workflows/build-nix-images.yml +++ b/.github/workflows/build-nix-images.yml @@ -9,12 +9,20 @@ on: - "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: @@ -46,9 +54,9 @@ jobs: base_image: debian:bookworm - name: rhel base_image: registry.access.redhat.com/ubi9/ubi:latest - uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@c1b480188519e0cad040e6aa70db1cbc5a797e07 + uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@ee03d31bcc4501d7599dc1b1ecd7a34af582ad1c with: - image_name: ghcr.io/xrplf/xrpld/nix-${{ matrix.distro.name }} + image_name: xrpld/nix-${{ matrix.distro.name }} dockerfile: nix/docker/Dockerfile base_image: ${{ matrix.distro.base_image }} - push: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} + push: ${{ github.event_name == 'push' }} diff --git a/.github/workflows/build-packaging-images.yml b/.github/workflows/build-packaging-images.yml index d6dabb0f95..3633847ef3 100644 --- a/.github/workflows/build-packaging-images.yml +++ b/.github/workflows/build-packaging-images.yml @@ -38,9 +38,9 @@ jobs: base_image: debian:bookworm - name: rhel base_image: registry.access.redhat.com/ubi9/ubi:latest - uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@c1b480188519e0cad040e6aa70db1cbc5a797e07 + uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@ee03d31bcc4501d7599dc1b1ecd7a34af582ad1c with: - image_name: ghcr.io/xrplf/xrpld/packaging-${{ matrix.distro.name }} + image_name: xrpld/packaging-${{ matrix.distro.name }} dockerfile: package/Dockerfile base_image: ${{ matrix.distro.base_image }} - push: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} + push: ${{ github.event_name == 'push' }} diff --git a/.github/workflows/check-pr-description.yml b/.github/workflows/check-pr-description.yml index a60b83738a..744449f216 100644 --- a/.github/workflows/check-pr-description.yml +++ b/.github/workflows/check-pr-description.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Write PR body to file env: diff --git a/.github/workflows/conflicting-pr.yml b/.github/workflows/conflicting-pr.yml index 772d46fd7d..cf65640954 100644 --- a/.github/workflows/conflicting-pr.yml +++ b/.github/workflows/conflicting-pr.yml @@ -14,6 +14,7 @@ permissions: jobs: main: + if: ${{ !contains(github.event.pull_request.labels.*.name, 'IgnoreConflicts') }} runs-on: ubuntu-latest steps: - name: Check if PRs are dirty diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index 4b2edeb93d..bbf6f8c39e 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -1,7 +1,11 @@ -# This workflow runs all workflows to check, build and test the project on -# various Linux flavors, as well as on MacOS and Windows, on every push to a -# user branch. However, it will not run if the pull request is a draft unless it -# has the 'DraftRunCI' label. For commits to PRs that target a release branch, +# This workflow runs workflows to check, build and test the project +# on every meaningful change on pull_request. +# However, it will not run if the PR is a draft +# unless it has the 'DraftRunCI' or 'Full CI build' label. +# +# By default a PR builds only a minimal matrix. +# The full matrix runs once the PR is labeled "Ready to merge" or "Full CI build". +# For commits to PRs that target a release branch, # it also uploads the libxrpl recipe to the Conan remote. name: PR @@ -15,9 +19,24 @@ on: - reopened - synchronize - ready_for_review + # Trigger on label changes so toggling "Ready to merge" or "Full CI build" + # switches between the minimal and full matrix without needing a new push. + - labeled + - unlabeled concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + # Use a per-ref group so a newer run (a push, or a change to a label below) + # supersedes the in-progress one for that ref. Label events we don't act on get + # their own unique group (per run id) instead, keeping them out of the shared + # group so real builds keep running. Keep this list in sync with `should-run`. + group: >- + ${{ github.workflow }}-${{ github.ref }}${{ + ((github.event.action == 'labeled' || github.event.action == 'unlabeled') + && github.event.label.name != 'Ready to merge' + && github.event.label.name != 'DraftRunCI' + && github.event.label.name != 'Full CI build') + && format('-{0}', github.run_id) || '' + }} cancel-in-progress: true defaults: @@ -26,14 +45,25 @@ defaults: jobs: # This job determines whether the rest of the workflow should run. It runs - # when the PR is not a draft (which should also cover merge-group) or - # has the 'DraftRunCI' label. + # when the PR is not a draft (which should also cover merge-group) or has the + # 'DraftRunCI' or 'Full CI build' label. For label events it only runs when the + # label added or removed is one we act on ('Ready to merge', 'DraftRunCI' or + # 'Full CI build'), so unrelated label changes do not trigger a redundant run. should-run: - if: ${{ !github.event.pull_request.draft || contains(github.event.pull_request.labels.*.name, 'DraftRunCI') }} + if: >- + ${{ + ((github.event.action != 'labeled' && github.event.action != 'unlabeled') + || github.event.label.name == 'Ready to merge' + || github.event.label.name == 'DraftRunCI' + || github.event.label.name == 'Full CI build') + && (!github.event.pull_request.draft + || contains(github.event.pull_request.labels.*.name, 'DraftRunCI') + || contains(github.event.pull_request.labels.*.name, 'Full CI build')) + }} runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + 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 +100,7 @@ jobs: .github/workflows/reusable-upload-recipe.yml .clang-tidy .codecov.yml + bin/check-tools.sh cfg/** cmake/** conan/** @@ -90,15 +121,17 @@ jobs: # least one of: # * Any of the files checked in the `changes` step were modified # * The PR is NOT a draft and is labeled "Ready to merge" + # * The PR is labeled "Full CI build" (draft or not) # * The workflow is running from the merge queue id: go env: FILES: ${{ steps.changes.outputs.any_changed }} DRAFT: ${{ github.event.pull_request.draft }} READY: ${{ contains(github.event.pull_request.labels.*.name, 'Ready to merge') }} + FULL: ${{ contains(github.event.pull_request.labels.*.name, 'Full CI build') }} 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.FULL == 'true' || env.FILES == 'true' || env.MERGE == 'true' }}" >>"${GITHUB_OUTPUT}" cat "${GITHUB_OUTPUT}" outputs: go: ${{ steps.go.outputs.go == 'true' }} @@ -121,7 +154,6 @@ jobs: issues: write contents: read with: - check_only_changed: true create_issue_on_failure: false build-test: @@ -142,7 +174,10 @@ jobs: package: needs: [should-run, build-test] - if: ${{ needs.should-run.outputs.go == 'true' }} + # Packaging consumes the debian/rhel release binaries, which are only built + # by the full matrix. Skip it for pull requests that ran only the minimal + # matrix (i.e. not yet labeled "Ready to merge" or "Full CI build"). + if: ${{ needs.should-run.outputs.go == 'true' && (github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'Ready to merge') || contains(github.event.pull_request.labels.*.name, 'Full CI build')) }} uses: ./.github/workflows/reusable-package.yml upload-recipe: @@ -153,8 +188,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 diff --git a/.github/workflows/on-tag.yml b/.github/workflows/on-tag.yml index 42d5827cab..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' }} diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index 74bca82019..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: @@ -97,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 aecf0c2a8b..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@312aaab296060ff89d7f798dcab59f019bea6e02 + 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 bcf5968384..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/xrpld/nix-ubuntu:sha-63ffdc3 + container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-e29b523 steps: - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Prepare runner - uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8 + uses: XRPLF/actions/prepare-runner@64ec3cf3b152b4444638f470bbd6df7a7a30c81c with: enable_ccache: false diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index 8d52bfd2a6..15ce51b5bf 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -88,7 +88,7 @@ jobs: name: ${{ inputs.config_name }} runs-on: ${{ fromJSON(inputs.runs_on) }} container: ${{ inputs.image != '' && inputs.image || null }} - timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 90 }} + timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 180 }} env: # Use a namespace to keep the objects separate for each configuration. CCACHE_NAMESPACE: ${{ inputs.config_name }} @@ -116,10 +116,10 @@ jobs: uses: XRPLF/actions/cleanup-workspace@c7d9ce5ebb03c752a354889ecd870cadfc2b1cd4 - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Prepare runner - uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8 + uses: XRPLF/actions/prepare-runner@64ec3cf3b152b4444638f470bbd6df7a7a30c81c with: enable_ccache: ${{ inputs.ccache_enabled }} @@ -127,6 +127,11 @@ jobs: if: ${{ inputs.ccache_enabled && runner.debug == '1' }} run: echo "CCACHE_LOGFILE=${{ runner.temp }}/ccache.log" >>"${GITHUB_ENV}" + - name: Check tools + env: + CHECK_TOOLS_SKIP_CLONE: "1" + run: ./bin/check-tools.sh || true + - name: Print build environment uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574 @@ -164,12 +169,33 @@ jobs: CMAKE_ARGS: ${{ inputs.cmake_args }} run: | cmake \ - -G '${{ runner.os == 'Windows' && 'Visual Studio 17 2022' || 'Ninja' }}' \ + -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 }} env: @@ -209,21 +235,6 @@ jobs: --parallel "${BUILD_NPROC}" \ --target "${CMAKE_TARGET}" - # This step is needed to allow running in non-Nix environments - - name: Patch binary to use default loader and remove rpath (Linux) - if: ${{ runner.os == 'Linux' && env.SANITIZERS_ENABLED == 'false' }} - run: | - loader="$(/tmp/loader-path.sh)" - patchelf --set-interpreter "${loader}" --remove-rpath "${{ env.BUILD_DIR }}/xrpld" - - # We're only running aarch64 Linux builds in Ubuntu-based images, so this is kept simple - - name: Install libatomic (Linux aarch64) - if: ${{ runner.os == 'Linux' && runner.arch == 'ARM64' }} - run: | - apt update --yes - apt install -y --no-install-recommends \ - libatomic1 - - name: Show ccache statistics if: ${{ inputs.ccache_enabled }} run: | @@ -285,20 +296,6 @@ 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: ${{ runner.os == 'Windows' && format('{0}/{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }} diff --git a/.github/workflows/reusable-check-levelization.yml b/.github/workflows/reusable-check-levelization.yml index 813c0e1e36..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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Check levelization run: python .github/scripts/levelization/generate.py - name: Check for differences diff --git a/.github/workflows/reusable-check-rename.yml b/.github/workflows/reusable-check-rename.yml index 5002cc7f40..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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Check definitions run: .github/scripts/rename/definitions.sh . - name: Check copyright notices diff --git a/.github/workflows/reusable-clang-tidy.yml b/.github/workflows/reusable-clang-tidy.yml index 9f10711b6f..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@312aaab296060ff89d7f798dcab59f019bea6e02 + 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/xrpld/nix-debian:sha-63ffdc3" + container: "ghcr.io/xrplf/xrpld/nix-debian:sha-e29b523" permissions: contents: read issues: write steps: - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Prepare runner - uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8 + uses: XRPLF/actions/prepare-runner@64ec3cf3b152b4444638f470bbd6df7a7a30c81c with: enable_ccache: false @@ -59,7 +57,7 @@ jobs: - name: Set compiler environment uses: ./.github/actions/set-compiler-env with: - compiler: clang + compiler: ${{ env.COMPILER }} - name: Setup Conan uses: ./.github/actions/setup-conan @@ -81,6 +79,7 @@ jobs: -Dtests=ON \ -Dwerr=ON \ -Dxrpld=ON \ + -Dverify_headers=ON \ .. # clang-tidy needs headers generated from proto files @@ -93,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' }} @@ -145,26 +144,26 @@ jobs: \`\`\` EOF - - name: Append clang-tidy output to issue body (filter for errors and warnings) + - name: Append filtered clang-tidy output to issue body if: ${{ steps.run_clang_tidy.outcome != 'success' }} run: | if [ -f "${OUTPUT_FILE}" ]; then - # Extract lines containing 'error:', 'warning:', or 'note:' - grep -E '(error:|warning:|note:)' "${OUTPUT_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 + 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 >"${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}" fi diff --git a/.github/workflows/reusable-package.yml b/.github/workflows/reusable-package.yml index 0e3f657006..6feecbfb75 100644 --- a/.github/workflows/reusable-package.yml +++ b/.github/workflows/reusable-package.yml @@ -27,10 +27,10 @@ jobs: matrix: ${{ steps.generate.outputs.matrix }} steps: - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + 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" @@ -39,23 +39,8 @@ jobs: working-directory: .github/scripts/strategy-matrix run: ./generate.py --packaging >>"${GITHUB_OUTPUT}" - generate-version: - runs-on: ubuntu-latest - outputs: - version: ${{ steps.version.outputs.version }} - steps: - - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - with: - sparse-checkout: | - .github/actions/generate-version - src/libxrpl/protocol/BuildInfo.cpp - - name: Generate version - id: version - uses: ./.github/actions/generate-version - package: - needs: [generate-matrix, generate-version] + needs: [generate-matrix] if: ${{ github.event.repository.visibility == 'public' }} strategy: fail-fast: false @@ -69,7 +54,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Download pre-built binary uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -82,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 4518a8ffef..b6091b99d9 100644 --- a/.github/workflows/reusable-strategy-matrix.yml +++ b/.github/workflows/reusable-strategy-matrix.yml @@ -23,10 +23,10 @@ jobs: matrix: ${{ steps.generate.outputs.matrix }} steps: - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + 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" @@ -35,5 +35,8 @@ jobs: id: generate env: GENERATE_CONFIG: ${{ inputs.os != '' && format('--config={0}', inputs.os) || '' }} - GENERATE_EVENT: ${{ github.event_name }} - run: ./generate.py ${GENERATE_CONFIG} --event="${GENERATE_EVENT}" >>"${GITHUB_OUTPUT}" + # Run only the minimal matrix for pull requests that are not yet + # labeled "Ready to merge" or "Full CI build". Any other event (merge + # queue, push, schedule, manual dispatch) runs the full matrix. + GENERATE_MINIMAL: ${{ (github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'Ready to merge') && !contains(github.event.pull_request.labels.*.name, 'Full CI build')) && '--minimal' || '' }} + run: ./generate.py ${GENERATE_CONFIG} ${GENERATE_MINIMAL} >>"${GITHUB_OUTPUT}" diff --git a/.github/workflows/reusable-upload-recipe.yml b/.github/workflows/reusable-upload-recipe.yml index 1c90fb0e72..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/xrpld/nix-ubuntu:sha-63ffdc3 + 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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + 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 7ca9d13007..22ad36d98f 100644 --- a/.github/workflows/upload-conan-deps.yml +++ b/.github/workflows/upload-conan-deps.yml @@ -34,7 +34,7 @@ on: 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: @@ -65,10 +65,10 @@ jobs: uses: XRPLF/actions/cleanup-workspace@c7d9ce5ebb03c752a354889ecd870cadfc2b1cd4 - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Prepare runner - uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8 + uses: XRPLF/actions/prepare-runner@64ec3cf3b152b4444638f470bbd6df7a7a30c81c with: enable_ccache: false @@ -108,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 c9dec89435..8a689c0f8b 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,30 +28,47 @@ 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$' + # run-clang-tidy --fix may edit headers included by files it is not run on, + # so pre-commit must not split the files across parallel hook invocations. + # The script determines the staged files itself and lets run-clang-tidy + # handle parallelism internally. + pass_filenames: false - 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)$ + - id: check-doxygen-style + name: check Doxygen comment style + entry: ./bin/pre-commit/check_doxygen_style.py + language: python + types_or: [c++, c] - repo: https://github.com/pre-commit/mirrors-clang-format 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-pre-commit - rev: faadd6a9d852369ca94f4d15b2404c967ba8cb01 # frozen: 0.27.6 + rev: e98930bdc210d3387007f9252d8c1694ea7e410f # frozen: 0.27.7 hooks: - id: gersemi - repo: https://github.com/rbubley/mirrors-prettier - rev: 515f543f5718ebfd6ce22e16708bb32c68ff96e1 # frozen: v3.8.3 + rev: 39e2973981e6d2f9b6c543b0086a2d2393abdc89 # frozen: v3.9.4 hooks: - id: prettier args: [--end-of-line=auto] @@ -80,22 +98,21 @@ repos: files: \.md$ - repo: https://github.com/streetsidesoftware/cspell-cli - rev: 4643f154907327ee0a2c7038f0296e0dd77d9776 # frozen: v10.0.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/API-CHANGELOG.md b/API-CHANGELOG.md index 56a45c132a..a04f265328 100644 --- a/API-CHANGELOG.md +++ b/API-CHANGELOG.md @@ -28,6 +28,9 @@ This section contains changes targeting a future version. ### Additions +- `account_tx`: Added an optional `delegate` request object to filter delegated transactions. The object requires `delegate_filter`, which must be either `actor` for transactions owned by the requested account but signed by another account, or `authorizer` for transactions signed by the requested account on behalf of another account. The optional `counter_party` account narrows the results to a specific signer/delegate for `actor` or a specific owner/delegator for `authorizer`. Malformed `delegate`, `delegate_filter`, and `counter_party` values return standard invalid field errors, and invalid account IDs return `actMalformed`. + When paginating delegate-filtered queries, a marker from a delegate-filtered query includes a `delegate` flag and is only valid for follow-up requests that also supply `delegate` (mixing marker conventions returns `invalidParams`). Because filtering is applied after the ledger scan, a page may contain fewer results than `limit` (possibly zero) while still returning a marker, so callers must continue until no marker is present. + - `ledger_entry`, `account_objects`: The `Delegate` ledger entry now includes an optional `DestinationNode` field, which stores the index into the authorized account's owner directory. This field is present on entries created after bidirectional directory tracking was introduced and may appear in RPC responses for those entries. ([#6681](https://github.com/XRPLF/rippled/pull/6681)) - `server_definitions`: Added the following new sections to the response ([#6321](https://github.com/XRPLF/rippled/pull/6321)): diff --git a/BUILD.md b/BUILD.md index 662ba0d33d..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++23 dialect and includes the `` header. -The [tested compiler versions][2] are: - -| Compiler | Version | -| ----------- | --------- | -| GCC | 15 | -| Clang | 22 | -| Apple Clang | 17 | -| 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`: - -```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 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++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 -``` - -#### 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 @@ -547,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 @@ -583,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 @@ -610,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 3dbe60a220..1e8befcc8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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..9929b2eb39 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 @@ -84,7 +84,9 @@ If you create new source files, they must be organized as follows: - All other non-test files must go under `src/xrpld`. - All test source files must go under `src/test`. -The source must be formatted according to the style guide below. +The source must be formatted according to the style guide below. The easiest +way to satisfy this is to install the [`pre-commit`](#pre-commit-hooks) hooks, +which format and lint your changes automatically on every commit. Header includes must be [levelized](.github/scripts/levelization). @@ -130,11 +132,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. @@ -214,13 +214,61 @@ This is a non-exhaustive list of recommended style guidelines. These are not always strictly enforced and serve as a way to keep the codebase coherent rather than a set of _thou shalt not_ commandments. +## Pre-commit hooks + +We use the [`pre-commit`](https://pre-commit.com/) framework to run the +formatting and linting tools that keep the codebase consistent. `pre-commit` +runs each tool configured in +[`.pre-commit-config.yaml`](./.pre-commit-config.yaml) in its own isolated +environment, so you don't need to install most of the individual tools +yourself. The version of each hook sourced from an external repository +(`clang-format`, `gersemi`, etc.) is pinned in that file, so running the hooks +locally uses exactly the same versions as CI. A few `local` hooks — most notably +`clang-tidy` — run tools from your own environment; see +[Installing clang-tidy](#installing-clang-tidy) for how to get those. + +To get started, install `pre-commit` and enable the git hook scripts: + +```bash +pip install pre-commit +pre-commit install +``` + +Once installed, the hooks run automatically on your staged files every time you +`git commit`. You can also run them on demand: + +```bash +# Run all hooks against only the staged files +pre-commit run + +# Run all hooks against every file in the repository +pre-commit run --all-files + +# Run a single hook (e.g. clang-format) against all files +pre-commit run clang-format --all-files +``` + +The hooks configured in this repository include, among others: + +- `clang-format` — C++/proto formatting (see [Formatting](#formatting)) +- `clang-tidy` — C++ static analysis (see [Clang-tidy](#clang-tidy)); opt in with `TIDY=1` +- `fix-include-style`, `fix-pragma-once`, `check-doxygen-style` — C++ hygiene +- `gersemi` — CMake formatting +- `prettier`, `black`, `shfmt` — formatting for JavaScript/JSON/Markdown, Python, and shell +- `cspell` — spell checking + +The same hooks run in CI on every pull request, so running them locally before +you push helps you avoid CI failures. + ## Formatting -All code must conform to `clang-format` version 21, -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 -this: +All code must conform to `clang-format`, according to the settings in +[`.clang-format`](./.clang-format), unless the result would be unreasonably +difficult to read or maintain. The `clang-format` version is pinned in +[`.pre-commit-config.yaml`](./.pre-commit-config.yaml), so the +[`pre-commit`](#pre-commit-hooks) hook always formats with the same version as +CI. To demarcate lines that should be left as-is, surround them with comments +like this: ``` // clang-format off @@ -228,9 +276,21 @@ this: // clang-format on ``` -You can format individual files in place by running `clang-format -i ...` +The easiest way to format your changes is to let the `pre-commit` hook run +automatically on commit, or to run it manually: + +```bash +pre-commit run clang-format --all-files +``` + +You can also format individual files in place by running `clang-format -i ...` from any directory within this project. +> [!NOTE] +> This uses whatever `clang-format` version is installed locally, which may +> differ from the pinned version used by `pre-commit` and CI, so the results +> can vary. + There is a Continuous Integration job that runs clang-format on pull requests. If the code doesn't comply, a patch file that corrects auto-fixable formatting issues is generated. To download the patch file: @@ -241,13 +301,6 @@ To download the patch file: 4. Download the zip file and extract it to your local git repository. Run `git apply [patch-file-name]`. 5. Commit and push. -You can install a pre-commit hook to automatically run `clang-format` before every commit: - -``` -pip3 install pre-commit -pre-commit install -``` - ## Clang-tidy All code must pass `clang-tidy` checks according to the settings in [`.clang-tidy`](./.clang-tidy). @@ -261,7 +314,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 @@ -269,7 +322,7 @@ Before running clang-tidy, you must build the project to generate required files #### Via pre-commit (recommended) -If you have already installed the pre-commit hooks (see above), you can run clang-tidy on your staged files using: +If you have already installed the [`pre-commit`](#pre-commit-hooks) hooks, you can run clang-tidy on your staged files using: ``` TIDY=1 pre-commit run clang-tidy diff --git a/bin/check-tools.sh b/bin/check-tools.sh new file mode 100755 index 0000000000..7886bcf8b0 --- /dev/null +++ b/bin/check-tools.sh @@ -0,0 +1,178 @@ +#!/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 + +# Rust toolchain. Part of the Nix commonPackages, so available on both Linux +# and macOS. The cargo plugins are invoked through cargo (`cargo `), which +# resolves the matching `cargo-` binary on PATH; `--version` is offline and +# does not need a Cargo project. +if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then + echo + echo "Rust toolchain:" + check cargo + check cargo-audit cargo audit --version + check cargo-llvm-cov cargo llvm-cov --version + check cargo-nextest cargo nextest --version + check clippy clippy-driver --version + check rust-analyzer + check rustc + check rustfmt +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/nix/docker/install-sanitizer-libs.sh b/bin/install-sanitizer-libs.sh similarity index 100% rename from nix/docker/install-sanitizer-libs.sh rename to bin/install-sanitizer-libs.sh diff --git a/bin/pre-commit/check_doxygen_style.py b/bin/pre-commit/check_doxygen_style.py new file mode 100755 index 0000000000..6a9af9399f --- /dev/null +++ b/bin/pre-commit/check_doxygen_style.py @@ -0,0 +1,440 @@ +#!/usr/bin/env python3 +""" +Check C++ Doxygen comment style. + +Enforces the house convention for documentation comments: + + * Use ``/** ... */`` blocks, not ``///``, ``//!`` or ``/*! ... */``; a plain + ``/* ... */`` that contains Doxygen commands is a doc comment missing its + second star. Trailing member-after comments use ``///<`` (not ``//!<``, + ``/*!< ... */`` or ``/**< ... */`` -- the block forms get reflowed and + mis-attached by clang-format on packed enum values, the line form does not). + * ``/**`` sits alone on its line; the closing ``*/`` sits alone on its line. + * Every content line is prefixed with `` * `` (no bare-indented continuation). + * The first content line is flush (not over-indented). + * Doxygen commands use the ``@cmd`` form, not ``\\cmd``. + * Use ``@return`` / ``@throws`` rather than prose ``Returns:`` / ``Throws:``. + * A plain ``//`` comment carrying a block-level ``@command`` (``@param``, + ``@return``, ``@see``, ...) is documentation and must be a ``/** ... */`` + block (Doxygen ignores ``//``). + * Use canonical command spellings: ``@return`` (not ``@returns``), + ``@throws`` (not ``@throw``), ``@see`` (not ``@sa``). + * Order block tags ``@tparam`` -> ``@param`` -> ``@return``. (Whether + ``@param`` order matches the signature is not checked here -- too fragile to + parse; Doxygen's WARN_IF_DOC_ERROR covers name mismatches.) + * One-liners are expanded to three lines, EXCEPT bare markers ``@{`` / ``@}`` + / ``@cond [label]`` / ``@endcond`` / ``@file [name]`` which stay on one line. + +Left intentionally alone (recognized, valid Doxygen that is not this style's +concern): + + * ``///<`` trailing "member-after" comments (the house form). + * Divider lines made only of slashes (``//////////``). + * Plain ``/* ... */`` (non-Doxygen) comments. + +Usage: + check_doxygen_style.py [FILE ...] # explicit files + check_doxygen_style.py # default: src/ and include/ trees + +Exit status is non-zero if any violation is found. +""" + +import argparse +import re +import sys +from collections.abc import Iterable, Iterator +from dataclasses import dataclass +from enum import Enum +from pathlib import Path + + +class Category(Enum): + """A kind of style violation: a printed ``label`` and its ``description``. + + The description is the default message; a few categories whose wording + depends on the offending text (see ``Finding.detail``) override it. + """ + + def __init__(self, label: str, description: str) -> None: + self.label = label + self.description = description + + BACKSLASH_COMMAND = ("backslash-command", "use the @cmd form, not \\cmd") + WRONG_COMMAND = ("wrong-command", "use the canonical command spelling") + TRIPLE_SLASH = ("triple-slash", "use a /** ... */ block instead of ///") + QT_MEMBER = ("qt-member", "use ///< instead of //!<") + QT_LINE = ("qt-line", "use a /** ... */ block instead of //!") + BLOCK_MEMBER = ("block-member", "use ///< instead of /**<") + QT_BLOCK_MEMBER = ("qt-block-member", "use ///< instead of /*!<") + DOC_IN_LINE_COMMENT = ( + "doc-in-line-comment", + "use a /** ... */ block for documentation, not //", + ) + QT_COMMENT = ("qt-comment", "use /** instead of /*!") + SINGLE_LINE_BLOCK = ( + "single-line-block", + "expand one-line /** ... */ to a multi-line block " + "(markers @{ @} @cond @endcond @file may stay)", + ) + TEXT_ON_OPENER = ("text-on-opener", "move text off the /** opener line") + BARE_CONTINUATION = ("bare-continuation", 'prefix continuation lines with " * "') + OVER_INDENTED = ("over-indented", "first content line is over-indented") + OVER_INDENTED_TAG = ( + "over-indented-tag", + 'Doxygen tag over-indented; use a single space after "*"', + ) + COMBINED_MARKER = ( + "combined-marker", + "scope marker @{ / @} should be its own single-line /** @{ */ block", + ) + PROSE_LABEL = ("prose-label", "use a Doxygen tag instead of a prose label") + CONTENT_ON_CLOSER = ("content-on-closer", "move content off the closing */ line") + PLAIN_BLOCK_DOC = ( + "plain-block-doc", + "documentation comment must open with /** not /*", + ) + TAG_ORDER = ( + "tag-order", + "block tags out of order; expected @tparam, then @param, then @return", + ) + + +@dataclass(frozen=True) +class Finding: + """A single style violation at a 1-based line number. + + ``detail`` overrides the category's default description when the message + depends on the offending text (e.g. which command was misspelled). + """ + + line: int + category: Category + detail: str | None = None + + @property + def message(self) -> str: + return self.detail if self.detail is not None else self.category.description + + +DEFAULT_ROOTS = ("src", "include") +EXTS = {".h", ".hpp", ".cpp", ".ipp", ".cxx", ".cc"} + +# Every Doxygen command we recognize when written with a backslash (\cmd). +_ALL_COMMANDS = ( + "brief|param|tparam|return|returns|retval|note|warning|pre|post|see|sa|ref|" + "throw|throws|exception|deprecated|details|code|endcode|verbatim|endverbatim|" + "li|arg|c|internal|since|todo|attention|remark|remarks|ingroup|defgroup" +) +# Block-level tags whose over-indentation we flag inside a block body. +_BLOCK_TAGS = ( + "param|tparam|returns?|retval|brief|throws?|note|warning|" + "pre|post|see|sa|details|deprecated" +) +# Tags that, appearing anywhere in a comment, mark it as documentation. +_ANY_DOC_TAGS = ( + "param|tparam|returns?|retval|brief|throws?|note|warning|pre|post|see|sa" +) +# Tags that make a plain // comment a mis-styled doc comment. +_LINE_DOC_TAGS = "brief|param|tparam|returns?|retval|throws?|note|see|pre|post" + +# \cmd that should be @cmd. +RE_BACKSLASH_CMD = re.compile(r"\\(" + _ALL_COMMANDS + r")\b") +# Bare markers that may legitimately stay on a single line. +RE_MARKER = re.compile(r"^@(\{|\}|cond(\s.*)?|endcond|file(\s.*)?)$") +# Prose section labels that should be Doxygen tags. +RE_PROSE_LABEL = re.compile(r"^\*\s(Returns|Throws|Exceptions):\s*$") +# An over-indented block tag: "*" followed by 2+ spaces then the tag. +RE_OVERINDENTED_TAG = re.compile(r"^\*\s{2,}@(" + _BLOCK_TAGS + r")\b") +# Any documentation tag (used to spot a doc comment hiding in a plain /* */). +RE_ANY_DOC_TAG = re.compile(r"@(" + _ANY_DOC_TAGS + r")\b") +# A documentation tag inside a // comment. +RE_LINE_DOC_TAG = re.compile(r"@(" + _LINE_DOC_TAGS + r")\b") +# Order-relevant tags, for the @tparam -> @param -> @return ordering check. +RE_ORDER_TAG = re.compile(r"^\*\s*@(param|tparam|returns?|retval)\b") +# First content line indented by 2+ spaces after the "*". +RE_FIRST_OVERINDENT = re.compile(r"^\s*\*\s{2,}\S") +# A scope marker @{ / @} sharing a comment with other text. +RE_COMBINED_MARKER = re.compile(r"^\*\s*@[{}]\s*$") + +# Non-canonical command spellings -> the house spelling (bare command names). +# Used both to flag a wrong @form and to suggest the right @form for a \wrong. +CANONICAL_COMMAND = {"returns": "return", "throw": "throws", "sa": "see"} +WRONG_SPELLINGS = [ + (re.compile(rf"@{wrong}\b"), f"@{right}") + for wrong, right in CANONICAL_COMMAND.items() +] + +# Order block tags should appear in; a body out of this order is a violation. +EXPECTED_TAG_ORDER = ("tparam", "param", "return") + + +def is_doxy_open(stripped: str) -> bool: + """True for a line-start Doxygen block opener we should normalize.""" + if stripped.startswith("/*!"): # Qt-style Doxygen + return not stripped.startswith("/*!<") # member-after, leave inline + return ( + stripped.startswith("/**") + and not stripped.startswith("/***") + and not stripped.startswith("/**/") + and not stripped.startswith("/**<") + ) + + +def _flag_commands(raw_line: str, stripped: str, index: int) -> list[Finding]: + """Flag \\cmd and misspelled @cmd on a comment line (opener, body, or closer).""" + if not stripped.startswith(("*", "//", "/*")): + return [] + findings: list[Finding] = [] + backslash = RE_BACKSLASH_CMD.search(raw_line) + if backslash: + command = backslash.group(1) + canonical = CANONICAL_COMMAND.get(command, command) + findings.append( + Finding( + index + 1, + Category.BACKSLASH_COMMAND, + f"use @{canonical} instead of \\{command}", + ) + ) + for pattern, replacement in WRONG_SPELLINGS: + wrong = pattern.search(raw_line) + if wrong: + findings.append( + Finding( + index + 1, + Category.WRONG_COMMAND, + f"use {replacement} instead of {wrong.group(0)}", + ) + ) + return findings + + +def _flag_line_comment(raw_line: str, stripped: str, index: int) -> Finding | None: + """Return the finding for a single-line comment form (///, //!, /**<, ...), else None.""" + if stripped.startswith("///") and not stripped.startswith(("////", "///<")): + return Finding(index + 1, Category.TRIPLE_SLASH) + if "//!<" in raw_line: + return Finding(index + 1, Category.QT_MEMBER) + if stripped.startswith("//!"): + return Finding(index + 1, Category.QT_LINE) + if "/**<" in raw_line: + return Finding(index + 1, Category.BLOCK_MEMBER) + if "/*!<" in raw_line: + return Finding(index + 1, Category.QT_BLOCK_MEMBER) + if stripped.startswith("//") and RE_LINE_DOC_TAG.search(stripped): + return Finding(index + 1, Category.DOC_IN_LINE_COMMENT) + return None + + +def _flag_single_line_block(stripped: str, line_no: int, is_qt: bool) -> list[Finding]: + """Findings for a whole /** ... */ or /*! ... */ block on one line.""" + inner = re.sub(r"^/\*[*!]", "", stripped) + inner = re.sub(r"\*/\s*$", "", inner).strip() + findings: list[Finding] = [] + if is_qt: + findings.append(Finding(line_no, Category.QT_COMMENT)) + if inner and not RE_MARKER.match(inner): + findings.append(Finding(line_no, Category.SINGLE_LINE_BLOCK)) + return findings + + +def _canonical_order_tag(body: str) -> str | None: + """The order-relevant tag (tparam/param/return) a body line opens with, if any.""" + match = RE_ORDER_TAG.match(body) + if match is None: + return None + command = match.group(1) + return "return" if command in ("return", "returns", "retval") else command + + +def _flag_body_line( + body_line: str, line_no: int, is_first_content: bool +) -> list[Finding]: + """Findings for one interior line of a multi-line block.""" + body = body_line.strip() + findings: list[Finding] = [] + if body and not body.startswith("*"): + findings.append(Finding(line_no, Category.BARE_CONTINUATION)) + if body.startswith("*"): + if is_first_content and RE_FIRST_OVERINDENT.match(body_line): + findings.append(Finding(line_no, Category.OVER_INDENTED)) + if RE_OVERINDENTED_TAG.match(body): + findings.append(Finding(line_no, Category.OVER_INDENTED_TAG)) + if RE_COMBINED_MARKER.match(body): + findings.append(Finding(line_no, Category.COMBINED_MARKER)) + label = RE_PROSE_LABEL.match(body) + if label: + suggested_tag = "@return" if label.group(1) == "Returns" else "@throws" + findings.append( + Finding( + line_no, + Category.PROSE_LABEL, + f'use {suggested_tag} instead of prose "{label.group(1)}:"', + ) + ) + return findings + + +def _flag_closer(closer_line: str, line_no: int) -> list[Finding]: + """Findings for content sharing the closing */ line.""" + before = closer_line[: closer_line.index("*/")].strip() + if before and before != "*": + return [Finding(line_no, Category.CONTENT_ON_CLOSER)] + return [] + + +def _flag_tag_order(first_tag_line: dict[str, int]) -> list[Finding]: + """One finding if the present block tags are not in EXPECTED_TAG_ORDER.""" + tag_lines = [ + first_tag_line[tag] for tag in EXPECTED_TAG_ORDER if tag in first_tag_line + ] + if tag_lines != sorted(tag_lines): + return [Finding(min(tag_lines), Category.TAG_ORDER)] + return [] + + +def _flag_doxy_block(lines: list[str], start: int) -> tuple[int, list[Finding]]: + """Handle a /** or /*! block opening at ``start``; return (next index, findings).""" + raw_line = lines[start] + stripped = raw_line.lstrip() + open_pos = raw_line.index("/*") + is_qt = stripped.startswith("/*!") + + # A whole block on one line: /** ... */. + if "*/" in raw_line[open_pos + 2 :]: + return start + 1, _flag_single_line_block(stripped, start + 1, is_qt) + + # Multi-line block: opener, then scan the body to the closer. + findings: list[Finding] = [] + if is_qt: + findings.append(Finding(start + 1, Category.QT_COMMENT)) + if raw_line[open_pos + 3 :].strip(): + findings.append(Finding(start + 1, Category.TEXT_ON_OPENER)) + + line_count = len(lines) + cursor = start + 1 + is_first_content = True + first_tag_line: dict[str, int] = {} # canonical tag -> 1-based first line + while cursor < line_count and "*/" not in lines[cursor]: + body_line = lines[cursor] + body = body_line.strip() + findings.extend(_flag_commands(body_line, body, cursor)) + tag = _canonical_order_tag(body) + if tag is not None: + first_tag_line.setdefault(tag, cursor + 1) + findings.extend(_flag_body_line(body_line, cursor + 1, is_first_content)) + if body.startswith("*"): + is_first_content = False + cursor += 1 + + if cursor < line_count: + closer_line = lines[cursor] + findings.extend(_flag_commands(closer_line, closer_line.strip(), cursor)) + findings.extend(_flag_closer(closer_line, cursor + 1)) + findings.extend(_flag_tag_order(first_tag_line)) + + return cursor + 1, findings + + +def _flag_plain_block(lines: list[str], start: int) -> tuple[int, list[Finding]]: + """Handle a line-start plain /* ... */ block; return (next index, findings). + + Only flagged when it hides a documentation command (a missing second star). + """ + line_count = len(lines) + cursor = start + while cursor < line_count and "*/" not in lines[cursor]: + cursor += 1 + findings: list[Finding] = [] + # The opener (start) is command-checked by check_file; check the rest here. + for i in range(start + 1, min(cursor + 1, line_count)): + findings.extend(_flag_commands(lines[i], lines[i].strip(), i)) + block_text = "\n".join( + lines[start : cursor + 1] if cursor < line_count else lines[start:] + ) + if RE_ANY_DOC_TAG.search(block_text): + findings.append(Finding(start + 1, Category.PLAIN_BLOCK_DOC)) + next_index = cursor + 1 if cursor < line_count else line_count + return next_index, findings + + +def check_source(text: str) -> list[Finding]: + """Return all style violations found in the given source text.""" + lines = text.split("\n") + findings: list[Finding] = [] + line_count = len(lines) + index = 0 + in_plain_block = False # inside a mid-line, non-Doxygen /* ... */ + while index < line_count: + raw_line = lines[index] + stripped = raw_line.lstrip() + + # Skip the interior of a plain block opened on an earlier line. + if in_plain_block: + in_plain_block = "*/" not in raw_line + index += 1 + continue + + findings.extend(_flag_commands(raw_line, stripped, index)) + + line_finding = _flag_line_comment(raw_line, stripped, index) + if line_finding is not None: + findings.append(line_finding) + index += 1 + elif is_doxy_open(stripped): + index, block_findings = _flag_doxy_block(lines, index) + findings.extend(block_findings) + elif stripped.startswith("/*"): + index, block_findings = _flag_plain_block(lines, index) + findings.extend(block_findings) + else: + # A /* that opens mid-line without closing starts a plain block. + if "/*" in raw_line and not stripped.startswith("//"): + if "*/" not in raw_line[raw_line.index("/*") + 2 :]: + in_plain_block = True + index += 1 + return findings + + +def check_file(path: Path) -> list[Finding]: + """Return all style violations found in one file.""" + return check_source(path.read_text(encoding="utf-8")) + + +def iter_files(paths: Iterable[str]) -> Iterator[Path]: + """Yield every C++ source file among the given files and directories.""" + for raw_path in paths: + path = Path(raw_path) + if path.is_dir(): + for candidate in path.rglob("*"): + if candidate.is_file() and candidate.suffix in EXTS: + yield candidate + elif path.suffix in EXTS: + yield path + + +def main() -> int: + parser = argparse.ArgumentParser(description="Check Doxygen comment style.") + parser.add_argument( + "files", nargs="*", help="files or directories (default: src/ include/)" + ) + parser.add_argument( + "-q", "--quiet", action="store_true", help="only print the summary count" + ) + args = parser.parse_args() + roots = args.files or [root for root in DEFAULT_ROOTS if Path(root).is_dir()] + + total = 0 + for path in sorted(set(iter_files(roots)), key=str): + for finding in check_file(path): + total += 1 + if not args.quiet: + print( + f"{path}:{finding.line}: {finding.category.label}: {finding.message}" + ) + print(f"\n{total} doxygen-style violation(s)", file=sys.stderr) + return 1 if total else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bin/pre-commit/clang_tidy_check.py b/bin/pre-commit/clang_tidy_check.py index f134660671..cf4808d2ea 100755 --- a/bin/pre-commit/clang_tidy_check.py +++ b/bin/pre-commit/clang_tidy_check.py @@ -1,24 +1,46 @@ #!/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 staged files using run-clang-tidy. + +The script determines the staged files itself (see `pass_filenames: false` in +.pre-commit-config.yaml) so run-clang-tidy is run once and handles parallelism +internally: pre-commit would otherwise split the files across parallel hook +invocations that race when fixes edit a shared header. + +Fixes are collected with `-export-fixes` and applied by clang-apply-replacements +in a separate step rather than with run-clang-tidy's `-fix`. The `add_module` +build isolates each module's headers behind a per-module symlink directory +(build/modules//...), so a header reachable from several translation +units is referenced through different paths that all resolve to the same source +file. clang-apply-replacements deduplicates identical replacements by their +literal path, so those paths must be canonicalised to the real source path +first; otherwise the same fix is applied once per path and corrupts the header. +""" from __future__ import annotations -import json import os import re import shutil import subprocess import sys -from collections import defaultdict +import tempfile 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 + +# Extensions run-clang-tidy can analyse: `.cpp` translation units and, thanks to +# the `verify_headers` build option, `.h`/`.hpp` headers (each has its own +# compile_commands.json entry). `.ipp` fragments have no entry and are skipped. +TIDY_EXTENSIONS = {".cpp", ".h", ".hpp"} + +# A single-quoted `FilePath:` entry in an -export-fixes YAML file, allowing the +# `- ` marker that precedes it inside a `Replacements:` sequence. clang-tidy +# emits paths single-quoted and doubles any embedded quote per YAML rules. +FILEPATH_RE = re.compile(r"^(\s*(?:-\s+)?FilePath:\s*)'((?:[^']|'')*)'\s*$") -def find_run_clang_tidy() -> str | None: - for candidate in ("run-clang-tidy-21", "run-clang-tidy"): +def find_tool(name: str) -> str | None: + for candidate in (f"{name}-{CLANG_TIDY_VERSION}", name): if path := shutil.which(candidate): return path return None @@ -32,136 +54,37 @@ def find_build_dir(repo_root: Path) -> Path | None: return None -def build_include_graph(build_dir: Path, repo_root: Path) -> tuple[dict, set]: +def staged_files(repo_root: Path) -> list[Path]: + """Return absolute paths of staged, lint-able C/C++ files. + + `--diff-filter=d` excludes deletions so we never lint a removed file. """ - 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, + output = subprocess.check_output( + ["git", "diff", "--staged", "--name-only", "--diff-filter=d", "--"] + + [f"*{ext}" for ext in TIDY_EXTENSIONS], 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] + return [repo_root / rel for rel in output.splitlines() if rel] + + +def canonicalize_fix_paths(fixes_dir: Path) -> None: + """Rewrite every `FilePath` in the exported fixes to its real source path. + + A header included through a module's isolation symlink is recorded under that + symlink's path; collapsing all paths to the same real file lets + clang-apply-replacements recognise the per-translation-unit duplicates and + apply each fix once. + """ + for yaml in fixes_dir.glob("*.yaml"): + lines = [] + for line in yaml.read_text().splitlines(): + if m := FILEPATH_RE.match(line): + path = m.group(2).replace("''", "'") + real = os.path.realpath(path).replace("'", "''") + line = f"{m.group(1)}'{real}'" + lines.append(line) + yaml.write_text("\n".join(lines) + "\n") def main(): @@ -175,15 +98,25 @@ def main(): text=True, ).strip() ) + files = staged_files(repo_root) if not files: return 0 - run_clang_tidy = find_run_clang_tidy() - if not run_clang_tidy: + run_clang_tidy = find_tool("run-clang-tidy") + clang_apply_replacements = find_tool("clang-apply-replacements") + missing = [ + name + for name, path in ( + ("run-clang-tidy", run_clang_tidy), + ("clang-apply-replacements", clang_apply_replacements), + ) + if not path + ] + if missing: 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 {' and '.join(missing)} " + f"was not found in PATH (tried the '-{CLANG_TIDY_VERSION}' suffix too).", file=sys.stderr, ) return 1 @@ -197,15 +130,23 @@ def main(): ) return 1 - tidy_files = resolve_files(files, build_dir, repo_root) - if not tidy_files: - return 0 + with tempfile.TemporaryDirectory() as fixes_dir: + result = subprocess.run( + [ + run_clang_tidy, + "-quiet", + "-p", + build_dir, + "-export-fixes", + fixes_dir, + "-allow-no-checks", + ] + + files + ) + canonicalize_fix_paths(Path(fixes_dir)) + applied = subprocess.run([clang_apply_replacements, fixes_dir]) - result = subprocess.run( - [run_clang_tidy, "-quiet", "-p", str(build_dir), "-fix", "-allow-no-checks"] - + tidy_files - ) - return result.returncode + return result.returncode or applied.returncode if __name__ == "__main__": 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/bin/pre-commit/test_check_doxygen_style.py b/bin/pre-commit/test_check_doxygen_style.py new file mode 100755 index 0000000000..861414f46d --- /dev/null +++ b/bin/pre-commit/test_check_doxygen_style.py @@ -0,0 +1,406 @@ +#!/usr/bin/env python3 +""" +Tests for check_doxygen_style.py. + +Run directly (no test framework needed): + ./bin/pre-commit/test_check_doxygen_style.py +or under pytest: + pytest bin/pre-commit/test_check_doxygen_style.py +""" + +import sys +import textwrap + +from check_doxygen_style import Finding, check_source + + +def findings_for(text: str) -> list[Finding]: + """Return the style violations for the given source text. + + The text is dedented and its leading newline stripped, so fixtures can be + written as indented triple-quoted here-docs while keeping honest 1-based + line numbers. + """ + text = textwrap.dedent(text).lstrip("\n") + return check_source(text) + + +def labels_for(text: str) -> list[str]: + return [f.category.label for f in findings_for(text)] + + +def messages_for(text: str) -> list[str]: + return [f.message for f in findings_for(text)] + + +# --- well-formed input produces nothing ------------------------------------- + + +def test_clean_block_ok() -> None: + code = """ + /** + * Brief. + * + * @tparam T a type + * @param x the x + * @return the result + */ + """ + assert findings_for(code) == [] + + +def test_blank_lines_inside_block_ok() -> None: + code = """ + /** + * a + * + * b + */ + """ + assert findings_for(code) == [] + + +def test_member_and_divider_allowed() -> None: + assert findings_for("int x; ///< ok member\n") == [] + assert findings_for("//////////\n") == [] + assert findings_for("//// text\n") == [] + + +# --- line-comment forms ------------------------------------------------------ + + +def test_triple_slash() -> None: + code = "/// doc\n" + assert labels_for(code) == ["triple-slash"] + + +def test_qt_line() -> None: + code = "//! doc\n" + assert labels_for(code) == ["qt-line"] + + +def test_qt_member() -> None: + code = "int x; //!< doc\n" + assert labels_for(code) == ["qt-member"] + + +def test_block_member() -> None: + code = "int x; /**< doc */\n" + assert labels_for(code) == ["block-member"] + + +def test_qt_block_member() -> None: + code = "int x; /*!< doc */\n" + assert labels_for(code) == ["qt-block-member"] + + +def test_doc_in_line_comment() -> None: + code = "// @param x\n" + assert labels_for(code) == ["doc-in-line-comment"] + + +# --- block forms ------------------------------------------------------------- + + +def test_qt_comment() -> None: + code = """ + /*! + * brief + */ + """ + assert labels_for(code) == ["qt-comment"] + + +def test_qt_comment_single_line() -> None: + # /*! ... */ on one line -> qt-comment (plus single-line-block) + code = "/*! brief */\n" + assert labels_for(code) == ["qt-comment", "single-line-block"] + + +def test_single_line_block() -> None: + code = "/** brief */\n" + assert labels_for(code) == ["single-line-block"] + + +def test_single_line_markers_allowed() -> None: + for marker in ("@{", "@}", "@cond LABEL", "@endcond", "@file foo.h"): + code = f"/** {marker} */\n" + assert findings_for(code) == [], marker + + +def test_text_on_opener() -> None: + code = """ + /** text here + * more + */ + """ + assert labels_for(code) == ["text-on-opener"] + + +def test_bare_continuation() -> None: + code = """ + /** + * a + bare line + */ + """ + assert labels_for(code) == ["bare-continuation"] + + +def test_over_indented_first_line() -> None: + code = """ + /** + * over + */ + """ + assert labels_for(code) == ["over-indented"] + + +def test_over_indented_tag() -> None: + # a flush first line consumes "first content", isolating the tag check + code = """ + /** + * brief + * @param x + */ + """ + assert labels_for(code) == ["over-indented-tag"] + + +def test_combined_marker() -> None: + code = """ + /** + * @{ + */ + """ + assert labels_for(code) == ["combined-marker"] + + +def test_prose_label() -> None: + for word in ("Returns", "Throws", "Exceptions"): + code = f""" + /** + * {word}: + */ + """ + assert labels_for(code) == ["prose-label"], word + + +def test_content_on_closer() -> None: + code = """ + /** + * a + * b */ + """ + assert labels_for(code) == ["content-on-closer"] + + +def test_plain_block_doc() -> None: + assert labels_for("/* @param x */\n") == ["plain-block-doc"] + assert findings_for("/* just an ordinary note */\n") == [] + + +def test_tag_order() -> None: + out_of_order = """ + /** + * @param x + * @tparam T + */ + """ + assert labels_for(out_of_order) == ["tag-order"] + + correct = """ + /** + * @tparam T + * @param x + * @return r + */ + """ + assert findings_for(correct) == [] + + single = """ + /** + * @param x + */ + """ + assert findings_for(single) == [] # single tag: never out of order + + +# --- command spelling (must work on body/closer lines, not just the opener) -- + + +def test_backslash_command_on_body_line() -> None: + code = r""" + /** + * \brief x + */ + """ + assert labels_for(code) == ["backslash-command"] + + +def test_backslash_command_suggests_canonical_spelling() -> None: + # a backslash + non-canonical spelling is fixed in one pass, not two: + # \sa -> @see (not @sa), \returns -> @return (not @returns) + sa = r""" + /** + * \sa other + */ + """ + assert messages_for(sa) == [r"use @see instead of \sa"] + + returns = r""" + /** + * \returns x + */ + """ + assert messages_for(returns) == [r"use @return instead of \returns"] + + +def test_wrong_command_on_body_line() -> None: + code = """ + /** + * @returns x + */ + """ + assert labels_for(code) == ["wrong-command"] + + +def test_body_line_commands_regression() -> None: + # regression: these live on body lines of a multi-line block + code = r""" + /** + * @returns bad + * @throw ex + * @sa other + * \param y + */ + """ + assert labels_for(code) == [ + "wrong-command", + "wrong-command", + "wrong-command", + "backslash-command", + ] + + +def test_command_on_closer_line() -> None: + code = """ + /** + * a + * @sa b */ + """ + assert labels_for(code) == ["wrong-command", "content-on-closer"] + + +def test_no_double_count_across_opener_body_closer() -> None: + code = """ + /** @returns opener + * @throw body + * @sa closer */ + """ + assert labels_for(code).count("wrong-command") == 3 + + +def test_code_with_word_allowed() -> None: + # @code{.cpp} is valid Doxygen and must not be flagged + code = """ + /** + * @code{.cpp} + * int x; + * @endcode + */ + """ + assert findings_for(code) == [] + + +# --- rendered message text --------------------------------------------------- + + +def test_message_uses_category_description() -> None: + # a static category renders its default description + code = "/// doc\n" + assert messages_for(code) == ["use a /** ... */ block instead of ///"] + + +def test_message_detail_overrides() -> None: + # dynamic categories render the offending text via Finding.detail + backslash = r""" + /** + * \param y + */ + """ + assert messages_for(backslash) == [r"use @param instead of \param"] + + wrong = """ + /** + * @returns x + */ + """ + assert messages_for(wrong) == ["use @return instead of @returns"] + + prose = """ + /** + * Throws: + */ + """ + assert messages_for(prose) == ['use @throws instead of prose "Throws:"'] + + +# --- robustness -------------------------------------------------------------- + + +def test_empty_file_no_crash() -> None: + assert findings_for("") == [] + + +def test_mid_line_plain_block_skipped() -> None: + # a /* opened mid-line (after code) and spanning lines is skipped, so its + # comment-like contents are not analyzed + code = """ + int x = 0; /* note: @returns is not a real tag here + * @param also not real + */ + int y = 0; + """ + assert findings_for(code) == [] + + +def test_unclosed_block_scanned_to_eof() -> None: + # an unterminated /** block is still scanned to EOF (no crash, body checked) + code = """ + /** + * @returns x + """ + assert labels_for(code) == ["wrong-command"] + + +def test_banner_and_empty_comment_not_flagged() -> None: + code = """ + /*** + * banner + ***/ + """ + assert findings_for(code) == [] + assert findings_for("/**/\n") == [] + + +def main() -> int: + tests = sorted( + (name, fn) + for name, fn in globals().items() + if name.startswith("test_") and callable(fn) + ) + failed = 0 + for name, fn in tests: + try: + fn() + print(f"PASS {name}") + except AssertionError as exc: + failed += 1 + print(f"FAIL {name}: {exc!r}") + print(f"\n{len(tests) - failed}/{len(tests)} passed") + return 1 if failed else 0 + + +if __name__ == "__main__": + sys.exit(main()) 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/XrplCompiler.cmake b/cmake/XrplCompiler.cmake index 9af8e962d0..cb4e797137 100644 --- a/cmake/XrplCompiler.cmake +++ b/cmake/XrplCompiler.cmake @@ -154,6 +154,15 @@ else() > ) + # 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 diff --git a/cmake/XrplCore.cmake b/cmake/XrplCore.cmake index 9d13d62536..31a86a7d25 100644 --- a/cmake/XrplCore.cmake +++ b/cmake/XrplCore.cmake @@ -247,6 +247,7 @@ target_link_modules( if(xrpld) add_executable(xrpld) + patch_nix_binary(xrpld) if(tests) target_compile_definitions(xrpld PUBLIC ENABLE_TESTS) endif() @@ -288,4 +289,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/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 5b7e3c115b..03ca71f327 100644 --- a/cmake/XrplSettings.cmake +++ b/cmake/XrplSettings.cmake @@ -24,6 +24,23 @@ option(xrpld "Build xrpld" ON) option(tests "Build tests" ON) +# 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/templates/LedgerEntry.h.mako b/cmake/scripts/codegen/templates/LedgerEntry.h.mako index 63f5f39ef9..c799903b21 100644 --- a/cmake/scripts/codegen/templates/LedgerEntry.h.mako +++ b/cmake/scripts/codegen/templates/LedgerEntry.h.mako @@ -177,7 +177,9 @@ ${field['typeData']['setter_type']} ${field['paramName']}${',' if i < len(requir object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ % for field in fields: /** diff --git a/cmake/scripts/codegen/templates/Transaction.h.mako b/cmake/scripts/codegen/templates/Transaction.h.mako index d3b303d9d6..49e2e4a5cd 100644 --- a/cmake/scripts/codegen/templates/Transaction.h.mako +++ b/cmake/scripts/codegen/templates/Transaction.h.mako @@ -185,7 +185,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ % for field in fields: /** 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/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/profiles/default b/conan/profiles/default index e0a88ebca1..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=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 083807ea9e..09e6aef02b 100644 --- a/conan/profiles/sanitizers +++ b/conan/profiles/sanitizers @@ -87,15 +87,15 @@ include(default) {% 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 | join(' ')}}", "SANITIZERS_LINKER_FLAGS": "{{sanitizer_linker_flags | join(' ')}}"} +&: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/docs/0001-negative-unl/README.md b/docs/0001-negative-unl/README.md index dd5f9af2ae..0bc65cd860 100644 --- a/docs/0001-negative-unl/README.md +++ b/docs/0001-negative-unl/README.md @@ -288,7 +288,7 @@ components with non-trivial changes are colored green. validated. ![Sequence diagram](./negativeUNL_highLevel_sequence.png?raw=true "Negative UNL - Changes") +Changes") ## Roads Not Taken diff --git a/docs/build/advanced_conan.md b/docs/build/advanced_conan.md new file mode 100644 index 0000000000..26b88ef186 --- /dev/null +++ b/docs/build/advanced_conan.md @@ -0,0 +1,193 @@ +# Advanced Conan configuration + +This document provides advanced instructions for setting up and configuring Conan for `xrpld` development: custom profiles, the lockfile, patched recipes, and profile tweaks. + +## Custom profile + +If the default profile does not work for you and you do not yet have a Conan +profile, you can create one by running: + +```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. + +## Conan lockfile + +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). + +## 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 --force xrplf https://conan.xrplf.org/repository/conan/ +``` + +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`: + +```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 f8553af703..80243ccf15 100644 --- a/flake.lock +++ b/flake.lock @@ -2,17 +2,18 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1780749050, - "narHash": "sha256-3av0pIjlOWQ6rDbNOmpUSvbNnJkGORQKKjb4LtCZsIY=", + "lastModified": 1781173989, + "narHash": "sha256-fnzKKPvS+oieI/pTzotA5tkoM47EB1NpaBcgk4R97hE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "a799d3e3886da994fa307f817a6bc705ae538eeb", + "rev": "8c91a71d13451abc40eb9dae8910f972f979852f", "type": "github" }, "original": { - "id": "nixpkgs", - "ref": "nixos-unstable", - "type": "indirect" + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" } }, "nixpkgs-custom-glibc": { diff --git a/flake.nix b/flake.nix index 3b3ec7ea08..c52f4d050e 100644 --- a/flake.nix +++ b/flake.nix @@ -1,7 +1,7 @@ { 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' diff --git a/include/xrpl/basics/Archive.h b/include/xrpl/basics/Archive.h index 58e12bbb71..66d6a019af 100644 --- a/include/xrpl/basics/Archive.h +++ b/include/xrpl/basics/Archive.h @@ -4,13 +4,14 @@ namespace xrpl { -/** Extract a tar archive compressed with lz4 - - @param src the path of the archive to be extracted - @param dst the directory to extract to - - @throws runtime_error -*/ +/** + * Extract a tar archive compressed with lz4 + * + * @param src the path of the archive to be extracted + * @param dst the directory to extract to + * + * @throws runtime_error + */ void extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const& dst); diff --git a/include/xrpl/basics/Blob.h b/include/xrpl/basics/Blob.h index ee0d6cf3b5..bfb8e5a697 100644 --- a/include/xrpl/basics/Blob.h +++ b/include/xrpl/basics/Blob.h @@ -4,9 +4,10 @@ namespace xrpl { -/** Storage for linear binary data. - Blocks of binary data appear often in various idioms and structures. -*/ +/** + * Storage for linear binary data. + * Blocks of binary data appear often in various idioms and structures. + */ using Blob = std::vector; } // namespace xrpl diff --git a/include/xrpl/basics/Buffer.h b/include/xrpl/basics/Buffer.h index 59968a4fa4..05af6c409a 100644 --- a/include/xrpl/basics/Buffer.h +++ b/include/xrpl/basics/Buffer.h @@ -6,12 +6,14 @@ #include #include #include +#include namespace xrpl { -/** Like std::vector but better. - Meets the requirements of BufferFactory. -*/ +/** + * Like std::vector but better. + * Meets the requirements of BufferFactory. + */ class Buffer { private: @@ -23,30 +25,37 @@ public: Buffer() = default; - /** Create an uninitialized buffer with the given size. */ + /** + * Create an uninitialized buffer with the given size. + */ explicit Buffer(std::size_t size) : p_((size != 0u) ? new std::uint8_t[size] : nullptr), size_(size) { } - /** Create a buffer as a copy of existing memory. - - @param data a pointer to the existing memory. If - size is non-zero, it must not be null. - @param size size of the existing memory block. - */ + /** + * Create a buffer as a copy of existing memory. + * + * @param data a pointer to the existing memory. If + * size is non-zero, it must not be null. + * @param size size of the existing memory block. + */ Buffer(void const* data, std::size_t size) : Buffer(size) { if (size != 0u) std::memcpy(p_.get(), data, size); } - /** Copy-construct */ + /** + * Copy-construct + */ Buffer(Buffer const& other) : Buffer(other.p_.get(), other.size_) { } - /** Copy assign */ + /** + * Copy assign + */ Buffer& operator=(Buffer const& other) { @@ -58,17 +67,19 @@ public: return *this; } - /** Move-construct. - The other buffer is reset. - */ + /** + * Move-construct. + * The other buffer is reset. + */ Buffer(Buffer&& other) noexcept : p_(std::move(other.p_)), size_(other.size_) { other.size_ = 0; } - /** Move-assign. - The other buffer is reset. - */ + /** + * Move-assign. + * The other buffer is reset. + */ Buffer& operator=(Buffer&& other) noexcept { @@ -81,12 +92,16 @@ public: return *this; } - /** Construct from a slice */ + /** + * Construct from a slice + */ explicit Buffer(Slice s) : Buffer(s.data(), s.size()) { } - /** Assign from slice */ + /** + * Assign from slice + */ Buffer& operator=(Slice s) { @@ -100,7 +115,9 @@ public: return *this; } - /** Returns the number of bytes in the buffer. */ + /** + * Returns the number of bytes in the buffer. + */ [[nodiscard]] std::size_t size() const noexcept { @@ -120,10 +137,11 @@ public: return Slice{p_.get(), size_}; } - /** Return a pointer to beginning of the storage. - @note The return type is guaranteed to be a pointer - to a single byte, to facilitate pointer arithmetic. - */ + /** + * Return a pointer to beginning of the storage. + * @note The return type is guaranteed to be a pointer + * to a single byte, to facilitate pointer arithmetic. + */ /** @{ */ [[nodiscard]] std::uint8_t const* data() const noexcept @@ -138,9 +156,10 @@ public: } /** @} */ - /** Reset the buffer. - All memory is deallocated. The resulting size is 0. - */ + /** + * Reset the buffer. + * All memory is deallocated. The resulting size is 0. + */ void clear() noexcept { @@ -148,9 +167,10 @@ public: size_ = 0; } - /** Reallocate the storage. - Existing data, if any, is discarded. - */ + /** + * Reallocate the storage. + * Existing data, if any, is discarded. + */ std::uint8_t* alloc(std::size_t n) { diff --git a/include/xrpl/basics/CompressionAlgorithms.h b/include/xrpl/basics/CompressionAlgorithms.h index e24c490337..316acb14ac 100644 --- a/include/xrpl/basics/CompressionAlgorithms.h +++ b/include/xrpl/basics/CompressionAlgorithms.h @@ -5,13 +5,15 @@ #include #include +#include #include #include #include namespace xrpl::compression_algorithms { -/** LZ4 block compression. +/** + * LZ4 block compression. * @tparam BufferFactory Callable object or lambda. * Takes the requested buffer size and returns allocated buffer pointer. * @param in Data to compress @@ -79,7 +81,8 @@ lz4Decompress( return decompressedSize; } -/** LZ4 block decompression. +/** + * LZ4 block decompression. * @tparam InputStream ZeroCopyInputStream * @param in Input source stream * @param inSize Size of compressed data diff --git a/include/xrpl/basics/CountedObject.h b/include/xrpl/basics/CountedObject.h index 275894673e..bb7b0d8877 100644 --- a/include/xrpl/basics/CountedObject.h +++ b/include/xrpl/basics/CountedObject.h @@ -9,7 +9,9 @@ namespace xrpl { -/** Manages all counted object types. */ +/** + * Manages all counted object types. + */ class CountedObjects { public: @@ -23,10 +25,11 @@ public: getCounts(int minimumThreshold) const; public: - /** Implementation for @ref CountedObject. - - @internal - */ + /** + * Implementation for @ref CountedObject. + * + * @internal + */ class Counter { public: @@ -94,13 +97,14 @@ private: //------------------------------------------------------------------------------ -/** Tracks the number of instances of an object. - - Derived classes have their instances counted automatically. This is used - for reporting purposes. - - @ingroup basics -*/ +/** + * Tracks the number of instances of an object. + * + * Derived classes have their instances counted automatically. This is used + * for reporting purposes. + * + * @ingroup basics + */ template class CountedObject { diff --git a/include/xrpl/basics/DecayingSample.h b/include/xrpl/basics/DecayingSample.h index d1861ebc4a..1b05770734 100644 --- a/include/xrpl/basics/DecayingSample.h +++ b/include/xrpl/basics/DecayingSample.h @@ -2,31 +2,34 @@ #include #include +#include namespace xrpl { -/** Sampling function using exponential decay to provide a continuous value. - @tparam The number of seconds in the decay window. -*/ +/** + * Sampling function using exponential decay to provide a continuous value. + * @tparam The number of seconds in the decay window. + */ 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; /** - @param now Start time of DecayingSample. - */ + * @param now Start time of DecayingSample. + */ explicit DecayingSample(time_point now) : value_(value_type()), when_(now) { } - /** Add a new sample. - The value is first aged according to the specified time. - */ + /** + * Add a new sample. + * The value is first aged according to the specified time. + */ value_type add(value_type value, time_point now) { @@ -35,9 +38,10 @@ public: return value_ / Window; } - /** Retrieve the current value in normalized units. - The samples are first aged according to the specified time. - */ + /** + * Retrieve the current value in normalized units. + * The samples are first aged according to the specified time. + */ value_type value(time_point now) { @@ -86,14 +90,15 @@ private: //------------------------------------------------------------------------------ -/** Sampling function using exponential decay to provide a continuous value. - @tparam HalfLife The half life of a sample, in seconds. -*/ +/** + * Sampling function using exponential decay to provide a continuous value. + * @tparam HalfLife The half life of a sample, in seconds. + */ 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/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..59853ad4d0 100644 --- a/include/xrpl/basics/IntrusivePointer.h +++ b/include/xrpl/basics/IntrusivePointer.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -9,33 +10,37 @@ namespace xrpl { //------------------------------------------------------------------------------ -/** Tag to create an intrusive pointer from another intrusive pointer by using a - static cast. This is useful to create an intrusive pointer to a derived - class from an intrusive pointer to a base class. -*/ +/** + * Tag to create an intrusive pointer from another intrusive pointer by using a + * static cast. This is useful to create an intrusive pointer to a derived + * class from an intrusive pointer to a base class. + */ struct StaticCastTagSharedIntrusive { }; -/** Tag to create an intrusive pointer from another intrusive pointer by using a - dynamic cast. This is useful to create an intrusive pointer to a derived - class from an intrusive pointer to a base class. If the cast fails an empty - (null) intrusive pointer is created. -*/ +/** + * Tag to create an intrusive pointer from another intrusive pointer by using a + * dynamic cast. This is useful to create an intrusive pointer to a derived + * class from an intrusive pointer to a base class. If the cast fails an empty + * (null) intrusive pointer is created. + */ struct DynamicCastTagSharedIntrusive { }; -/** When creating or adopting a raw pointer, controls whether the strong count - is incremented or not. Use this tag to increment the strong count. -*/ +/** + * When creating or adopting a raw pointer, controls whether the strong count + * is incremented or not. Use this tag to increment the strong count. + */ struct SharedIntrusiveAdoptIncrementStrongTag { }; -/** When creating or adopting a raw pointer, controls whether the strong count - is incremented or not. Use this tag to leave the strong count unchanged. -*/ +/** + * When creating or adopting a raw pointer, controls whether the strong count + * is incremented or not. Use this tag to leave the strong count unchanged. + */ struct SharedIntrusiveAdoptNoIncrementTag { }; @@ -49,20 +54,21 @@ concept CAdoptTag = std::is_same_v || //------------------------------------------------------------------------------ -/** A shared intrusive pointer class that supports weak pointers. - - This is meant to be used for SHAMapInnerNodes, but may be useful for other - cases. Since the reference counts are stored on the pointee, the pointee is - not destroyed until both the strong _and_ weak pointer counts go to zero. - When the strong pointer count goes to zero, the "partialDestructor" is - called. This can be used to destroy as much of the object as possible while - still retaining the reference counts. For example, for SHAMapInnerNodes the - children may be reset in that function. Note that std::shared_pointer WILL - run the destructor when the strong count reaches zero, but may not free the - memory used by the object until the weak count reaches zero. In xrpld, we - typically allocate shared pointers with the `make_shared` function. When - that is used, the memory is not reclaimed until the weak count reaches zero. -*/ +/** + * A shared intrusive pointer class that supports weak pointers. + * + * This is meant to be used for SHAMapInnerNodes, but may be useful for other + * cases. Since the reference counts are stored on the pointee, the pointee is + * not destroyed until both the strong _and_ weak pointer counts go to zero. + * When the strong pointer count goes to zero, the "partialDestructor" is + * called. This can be used to destroy as much of the object as possible while + * still retaining the reference counts. For example, for SHAMapInnerNodes the + * children may be reset in that function. Note that std::shared_pointer WILL + * run the destructor when the strong count reaches zero, but may not free the + * memory used by the object until the weak count reaches zero. In xrpld, we + * typically allocate shared pointers with the `make_shared` function. When + * that is used, the memory is not reclaimed until the weak count reaches zero. + */ template class SharedIntrusive { @@ -110,8 +116,9 @@ public: operator=( SharedIntrusive&& rhs); // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) - /** Adopt the raw pointer. The strong reference may or may not be - incremented, depending on the TAdoptTag + /** + * Adopt the raw pointer. The strong reference may or may not be + * incremented, depending on the TAdoptTag */ template void @@ -119,27 +126,31 @@ public: ~SharedIntrusive(); - /** Create a new SharedIntrusive by statically casting the pointer - controlled by the rhs param. - */ + /** + * Create a new SharedIntrusive by statically casting the pointer + * controlled by the rhs param. + */ template SharedIntrusive(StaticCastTagSharedIntrusive, SharedIntrusive const& rhs); - /** Create a new SharedIntrusive by statically casting the pointer - controlled by the rhs param. - */ + /** + * Create a new SharedIntrusive by statically casting the pointer + * controlled by the rhs param. + */ template SharedIntrusive(StaticCastTagSharedIntrusive, SharedIntrusive&& rhs); - /** Create a new SharedIntrusive by dynamically casting the pointer - controlled by the rhs param. - */ + /** + * Create a new SharedIntrusive by dynamically casting the pointer + * controlled by the rhs param. + */ template SharedIntrusive(DynamicCastTagSharedIntrusive, SharedIntrusive const& rhs); - /** Create a new SharedIntrusive by dynamically casting the pointer - controlled by the rhs param. - */ + /** + * Create a new SharedIntrusive by dynamically casting the pointer + * controlled by the rhs param. + */ template SharedIntrusive(DynamicCastTagSharedIntrusive, SharedIntrusive&& rhs); @@ -152,17 +163,22 @@ public: explicit operator bool() const noexcept; - /** Set the pointer to null, decrement the strong count, and run the - appropriate release action. - */ + /** + * Set the pointer to null, decrement the strong count, and run the + * appropriate release action. + */ void reset(); - /** Get the raw pointer */ + /** + * Get the raw pointer + */ [[nodiscard]] T* get() const; - /** Return the strong count */ + /** + * Return the strong count + */ [[nodiscard]] std::size_t useCount() const; @@ -180,43 +196,51 @@ public: friend class WeakIntrusive; private: - /** Return the raw pointer held by this object. */ + /** + * Return the raw pointer held by this object. + */ [[nodiscard]] T* unsafeGetRawPtr() const; - /** Exchange the current raw pointer held by this object with the given - pointer. Decrement the strong count of the raw pointer previously held - by this object and run the appropriate release action. + /** + * Exchange the current raw pointer held by this object with the given + * pointer. Decrement the strong count of the raw pointer previously held + * by this object and run the appropriate release action. */ void unsafeReleaseAndStore(T* next); - /** Set the raw pointer directly. This is wrapped in a function so the class - can support both atomic and non-atomic pointers in a future patch. + /** + * Set the raw pointer directly. This is wrapped in a function so the class + * can support both atomic and non-atomic pointers in a future patch. */ void unsafeSetRawPtr(T* p); - /** Exchange the raw pointer directly. - This sets the raw pointer to the given value and returns the previous - value. This is wrapped in a function so the class can support both - atomic and non-atomic pointers in a future patch. + /** + * Exchange the raw pointer directly. + * This sets the raw pointer to the given value and returns the previous + * value. This is wrapped in a function so the class can support both + * atomic and non-atomic pointers in a future patch. */ T* unsafeExchange(T* p); - /** pointer to the type with an intrusive count */ + /** + * pointer to the type with an intrusive count + */ T* ptr_{nullptr}; }; //------------------------------------------------------------------------------ -/** A weak intrusive pointer class for the SharedIntrusive pointer class. - -Note that this weak pointer class asks differently from normal weak pointer -classes. When the strong pointer count goes to zero, the "partialDestructor" -is called. See the comment on SharedIntrusive for a fuller explanation. -*/ +/** + * A weak intrusive pointer class for the SharedIntrusive pointer class. + * + * Note that this weak pointer class asks differently from normal weak pointer + * classes. When the strong pointer count goes to zero, the "partialDestructor" + * is called. See the comment on SharedIntrusive for a fuller explanation. + */ template class WeakIntrusive { @@ -246,54 +270,62 @@ public: WeakIntrusive& operator=(SharedIntrusive const& rhs); - /** Adopt the raw pointer and increment the weak count. */ + /** + * Adopt the raw pointer and increment the weak count. + */ void adopt(T* ptr); ~WeakIntrusive(); - /** Get a strong pointer from the weak pointer, if possible. This will - only return a seated pointer if the strong count on the raw pointer - is non-zero before locking. + /** + * Get a strong pointer from the weak pointer, if possible. This will + * only return a seated pointer if the strong count on the raw pointer + * is non-zero before locking. */ SharedIntrusive lock() const; - /** Return true if the strong count is zero. */ + /** + * Return true if the strong count is zero. + */ [[nodiscard]] bool expired() const; - /** Set the pointer to null and decrement the weak count. - - Note: This may run the destructor if the strong count is zero. - */ + /** + * Set the pointer to null and decrement the weak count. + * + * Note: This may run the destructor if the strong count is zero. + */ void reset(); private: T* ptr_ = nullptr; - /** Decrement the weak count. This does _not_ set the raw pointer to - null. - - Note: This may run the destructor if the strong count is zero. - */ + /** + * Decrement the weak count. This does _not_ set the raw pointer to + * null. + * + * Note: This may run the destructor if the strong count is zero. + */ void unsafeReleaseNoStore(); }; //------------------------------------------------------------------------------ -/** A combination of a strong and a weak intrusive pointer stored in the - space of a single pointer. - - This class is similar to a `std::variant` - with some optimizations. In particular, it uses a low-order bit to - determine if the raw pointer represents a strong pointer or a weak - pointer. It can also be quickly switched between its strong pointer and - weak pointer representations. This class is useful for storing intrusive - pointers in tagged caches. - */ +/** + * A combination of a strong and a weak intrusive pointer stored in the + * space of a single pointer. + * + * This class is similar to a `std::variant` + * with some optimizations. In particular, it uses a low-order bit to + * determine if the raw pointer represents a strong pointer or a weak + * pointer. It can also be quickly switched between its strong pointer and + * weak pointer representations. This class is useful for storing intrusive + * pointers in tagged caches. + */ template class SharedWeakUnion @@ -335,69 +367,83 @@ public: ~SharedWeakUnion(); - /** Return a strong pointer if this is already a strong pointer (i.e. - don't lock the weak pointer. Use the `lock` method if that's what's - needed) + /** + * Return a strong pointer if this is already a strong pointer (i.e. + * don't lock the weak pointer. Use the `lock` method if that's what's + * needed) */ [[nodiscard]] SharedIntrusive getStrong() const; - /** Return true if this is a strong pointer and the strong pointer is - seated. + /** + * Return true if this is a strong pointer and the strong pointer is + * seated. */ explicit operator bool() const noexcept; - /** Set the pointer to null, decrement the appropriate ref count, and - run the appropriate release action. + /** + * Set the pointer to null, decrement the appropriate ref count, and + * run the appropriate release action. */ void reset(); - /** If this is a strong pointer, return the raw pointer. Otherwise - return null. + /** + * If this is a strong pointer, return the raw pointer. Otherwise + * return null. */ [[nodiscard]] T* get() const; - /** If this is a strong pointer, return the strong count. Otherwise + /** + * If this is a strong pointer, return the strong count. Otherwise * return 0 */ [[nodiscard]] std::size_t useCount() const; - /** Return true if there is a non-zero strong count. */ + /** + * Return true if there is a non-zero strong count. + */ [[nodiscard]] bool expired() const; - /** If this is a strong pointer, return the strong pointer. Otherwise - attempt to lock the weak pointer. + /** + * If this is a strong pointer, return the strong pointer. Otherwise + * attempt to lock the weak pointer. */ [[nodiscard]] SharedIntrusive lock() const; - /** Return true is this represents a strong pointer. */ + /** + * Return true is this represents a strong pointer. + */ [[nodiscard]] bool isStrong() const; - /** Return true is this represents a weak pointer. */ + /** + * Return true is this represents a weak pointer. + */ [[nodiscard]] bool isWeak() const; - /** If this is a weak pointer, attempt to convert it to a strong - pointer. - - @return true if successfully converted to a strong pointer (or was - already a strong pointer). Otherwise false. - */ + /** + * If this is a weak pointer, attempt to convert it to a strong + * pointer. + * + * @return true if successfully converted to a strong pointer (or was + * already a strong pointer). Otherwise false. + */ bool convertToStrong(); - /** If this is a strong pointer, attempt to convert it to a weak - pointer. - - @return false if the pointer is null. Otherwise return true. - */ + /** + * If this is a strong pointer, attempt to convert it to a weak + * pointer. + * + * @return false if the pointer is null. Otherwise return true. + */ bool convertToWeak(); @@ -410,23 +456,27 @@ private: static constexpr std::uintptr_t kPtrMask = ~kTagMask; private: - /** Return the raw pointer held by this object. + /** + * Return the raw pointer held by this object. */ [[nodiscard]] T* unsafeGetRawPtr() const; enum class RefStrength { Strong, Weak }; - /** Set the raw pointer and tag bit directly. + /** + * Set the raw pointer and tag bit directly. */ void unsafeSetRawPtr(T* p, RefStrength rs); - /** Set the raw pointer and tag bit to all zeros (strong null pointer). + /** + * Set the raw pointer and tag bit to all zeros (strong null pointer). */ void unsafeSetRawPtr(std::nullptr_t); - /** Decrement the appropriate ref count, and run the appropriate release - action. Note: this does _not_ set the raw pointer to null. + /** + * Decrement the appropriate ref count, and run the appropriate release + * action. Note: this does _not_ set the raw pointer to null. */ void unsafeReleaseNoStore(); @@ -434,12 +484,13 @@ private: //------------------------------------------------------------------------------ -/** Create a shared intrusive pointer. - - Note: unlike std::shared_ptr, where there is an advantage of allocating - the pointer and control block together, there is no benefit for intrusive - pointers. -*/ +/** + * Create a shared intrusive pointer. + * + * Note: unlike std::shared_ptr, where there is an advantage of allocating + * the pointer and control block together, there is no benefit for intrusive + * pointers. + */ template SharedIntrusive makeSharedIntrusive(Args&&... args) 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..caa06ed786 100644 --- a/include/xrpl/basics/IntrusiveRefCounts.h +++ b/include/xrpl/basics/IntrusiveRefCounts.h @@ -3,39 +3,43 @@ #include #include +#include #include namespace xrpl { -/** Action to perform when releasing a strong pointer. - - noop: Do nothing. For example, a `noop` action will occur when a count is - decremented to a non-zero value. - - partialDestroy: Run the `partialDestructor`. This action will happen when a - strong count is decremented to zero and the weak count is non-zero. - - destroy: Run the destructor. This action will occur when either the strong - count or weak count is decremented and the other count is also zero. +/** + * Action to perform when releasing a strong pointer. + * + * noop: Do nothing. For example, a `noop` action will occur when a count is + * decremented to a non-zero value. + * + * partialDestroy: Run the `partialDestructor`. This action will happen when a + * strong count is decremented to zero and the weak count is non-zero. + * + * destroy: Run the destructor. This action will occur when either the strong + * count or weak count is decremented and the other count is also zero. */ enum class ReleaseStrongRefAction { NoOp, PartialDestroy, Destroy }; -/** Action to perform when releasing a weak pointer. - - noop: Do nothing. For example, a `noop` action will occur when a count is - decremented to a non-zero value. - - destroy: Run the destructor. This action will occur when either the strong - count or weak count is decremented and the other count is also zero. +/** + * Action to perform when releasing a weak pointer. + * + * noop: Do nothing. For example, a `noop` action will occur when a count is + * decremented to a non-zero value. + * + * destroy: Run the destructor. This action will occur when either the strong + * count or weak count is decremented and the other count is also zero. */ enum class ReleaseWeakRefAction { NoOp, Destroy }; -/** Implement the strong count, weak count, and bit flags for an intrusive - pointer. - - A class can satisfy the requirements of an xrpl::IntrusivePointer by - inheriting from this class. - */ +/** + * Implement the strong count, weak count, and bit flags for an intrusive + * pointer. + * + * A class can satisfy the requirements of an xrpl::IntrusivePointer by + * inheriting from this class. + */ struct IntrusiveRefCounts { virtual ~IntrusiveRefCounts() noexcept; @@ -104,109 +108,123 @@ private: static constexpr size_t kFieldTypeBits = sizeof(FieldType) * 8; static constexpr FieldType kOne = 1; - /** `refCounts` consists of four fields that are treated atomically: - - 1. Strong count. This is a count of the number of shared pointers that - hold a reference to this object. When the strong counts goes to zero, - if the weak count is zero, the destructor is run. If the weak count is - non-zero when the strong count goes to zero then the partialDestructor - is run. - - 2. Weak count. This is a count of the number of weak pointer that hold - a reference to this object. When the weak count goes to zero and the - strong count is also zero, then the destructor is run. - - 3. Partial destroy started bit. This bit is set if the - `partialDestructor` function has been started (or is about to be - started). This is used to prevent the destructor from running - concurrently with the partial destructor. This can easily happen when - the last strong pointer release its reference in one thread and starts - the partialDestructor, while in another thread the last weak pointer - goes out of scope and starts the destructor while the partialDestructor - is still running. Both a start and finished bit is needed to handle a - corner-case where the last strong pointer goes out of scope, then then - last `weakPointer` goes out of scope, but this happens before the - `partialDestructor` bit is set. It would be possible to use a single - bit if it could also be set atomically when the strong count goes to - zero and the weak count is non-zero, but that would add complexity (and - likely slow down common cases as well). - - 4. Partial destroy finished bit. This bit is set when the - `partialDestructor` has finished running. See (3) above for more - information. - - */ + /** + * `refCounts` consists of four fields that are treated atomically: + * + * 1. Strong count. This is a count of the number of shared pointers that + * hold a reference to this object. When the strong counts goes to zero, + * if the weak count is zero, the destructor is run. If the weak count is + * non-zero when the strong count goes to zero then the partialDestructor + * is run. + * + * 2. Weak count. This is a count of the number of weak pointer that hold + * a reference to this object. When the weak count goes to zero and the + * strong count is also zero, then the destructor is run. + * + * 3. Partial destroy started bit. This bit is set if the + * `partialDestructor` function has been started (or is about to be + * started). This is used to prevent the destructor from running + * concurrently with the partial destructor. This can easily happen when + * the last strong pointer release its reference in one thread and starts + * the partialDestructor, while in another thread the last weak pointer + * goes out of scope and starts the destructor while the partialDestructor + * is still running. Both a start and finished bit is needed to handle a + * corner-case where the last strong pointer goes out of scope, then then + * last `weakPointer` goes out of scope, but this happens before the + * `partialDestructor` bit is set. It would be possible to use a single + * bit if it could also be set atomically when the strong count goes to + * zero and the weak count is non-zero, but that would add complexity (and + * likely slow down common cases as well). + * + * 4. Partial destroy finished bit. This bit is set when the + * `partialDestructor` has finished running. See (3) above for more + * information. + */ mutable std::atomic refCounts_{kStrongDelta}; - /** Amount to change the strong count when adding or releasing a reference - - Note: The strong count is stored in the low `StrongCountNumBits` bits - of refCounts - */ + /** + * Amount to change the strong count when adding or releasing a reference + * + * Note: The strong count is stored in the low `StrongCountNumBits` bits + * of refCounts + */ static constexpr FieldType kStrongDelta = 1; - /** Amount to change the weak count when adding or releasing a reference - - Note: The weak count is stored in the high `WeakCountNumBits` bits of - refCounts - */ + /** + * Amount to change the weak count when adding or releasing a reference + * + * Note: The weak count is stored in the high `WeakCountNumBits` bits of + * refCounts + */ static constexpr FieldType kWeakDelta = (kOne << kStrongCountNumBits); - /** Flag that is set when the partialDestroy function has started running - (or is about to start running). - - See description of the `refCounts` field for a fuller description of - this field. - */ + /** + * Flag that is set when the partialDestroy function has started running + * (or is about to start running). + * + * See description of the `refCounts` field for a fuller description of + * this field. + */ static constexpr FieldType kPartialDestroyStartedMask = (kOne << (kFieldTypeBits - 1)); - /** Flag that is set when the partialDestroy function has finished running - - See description of the `refCounts` field for a fuller description of - this field. - */ + /** + * Flag that is set when the partialDestroy function has finished running + * + * See description of the `refCounts` field for a fuller description of + * this field. + */ static constexpr FieldType kPartialDestroyFinishedMask = (kOne << (kFieldTypeBits - 2)); - /** Mask that will zero out all the `count` bits and leave the tag bits - unchanged. - */ + /** + * Mask that will zero out all the `count` bits and leave the tag bits + * unchanged. + */ static constexpr FieldType kTagMask = kPartialDestroyStartedMask | kPartialDestroyFinishedMask; - /** Mask that will zero out the `tag` bits and leave the count bits - unchanged. - */ + /** + * Mask that will zero out the `tag` bits and leave the count bits + * unchanged. + */ static constexpr FieldType kValueMask = ~kTagMask; - /** Mask that will zero out everything except the strong count. + /** + * Mask that will zero out everything except the strong count. */ static constexpr FieldType kStrongMask = ((kOne << kStrongCountNumBits) - 1) & kValueMask; - /** Mask that will zero out everything except the weak count. + /** + * Mask that will zero out everything except the weak count. */ static constexpr FieldType kWeakMask = (((kOne << kWeakCountNumBits) - 1) << kStrongCountNumBits) & kValueMask; - /** Unpack the count and tag fields from the packed atomic integer form. */ + /** + * Unpack the count and tag fields from the packed atomic integer form. + */ struct RefCountPair { CountType strong; CountType weak; - /** The `partialDestroyStartedBit` is set to on when the partial - destroy function is started. It is not a boolean; it is a uint32 - with all bits zero with the possible exception of the - `partialDestroyStartedMask` bit. This is done so it can be directly - masked into the `combinedValue`. + /** + * The `partialDestroyStartedBit` is set to on when the partial + * destroy function is started. It is not a boolean; it is a uint32 + * with all bits zero with the possible exception of the + * `partialDestroyStartedMask` bit. This is done so it can be directly + * masked into the `combinedValue`. */ FieldType partialDestroyStartedBit{0}; - /** The `partialDestroyFinishedBit` is set to on when the partial - destroy function has finished. + /** + * The `partialDestroyFinishedBit` is set to on when the partial + * destroy function has finished. */ FieldType partialDestroyFinishedBit{0}; RefCountPair(FieldType v) noexcept; RefCountPair(CountType s, CountType w) noexcept; - /** Convert back to the packed integer form. */ + /** + * Convert back to the packed integer form. + */ [[nodiscard]] FieldType combinedValue() const noexcept; @@ -214,9 +232,10 @@ private: static_cast((kOne << kStrongCountNumBits) - 1); static constexpr CountType kMaxWeakValue = static_cast((kOne << kWeakCountNumBits) - 1); - /** Put an extra margin to detect when running up against limits. - This is only used in debug code, and is useful if we reduce the - number of bits in the strong and weak counts (to 16 and 14 bits). + /** + * Put an extra margin to detect when running up against limits. + * This is only used in debug code, and is useful if we reduce the + * number of bits in the strong and weak counts (to 16 and 14 bits). */ static constexpr CountType kCheckStrongMaxValue = kMaxStrongValue - 32; static constexpr CountType kCheckWeakMaxValue = kMaxWeakValue - 32; diff --git a/include/xrpl/basics/LocalValue.h b/include/xrpl/basics/LocalValue.h index 1c2a657a18..c5e544a343 100644 --- a/include/xrpl/basics/LocalValue.h +++ b/include/xrpl/basics/LocalValue.h @@ -70,11 +70,15 @@ public: { } - /** Stores instance of T specific to the calling coroutine or thread. */ + /** + * Stores instance of T specific to the calling coroutine or thread. + */ T& operator*(); - /** Stores instance of T specific to the calling coroutine or thread. */ + /** + * Stores instance of T specific to the calling coroutine or thread. + */ T* operator->() { diff --git a/include/xrpl/basics/Log.h b/include/xrpl/basics/Log.h index 6bafbc7c54..945dc1b4ec 100644 --- a/include/xrpl/basics/Log.h +++ b/include/xrpl/basics/Log.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -11,11 +10,15 @@ #include #include #include +#include #include +#include namespace xrpl { -/** Manages partitions for logging. */ +/** + * Manages partitions for logging. + */ class Logs { private: @@ -39,69 +42,81 @@ private: writeAlways(beast::Severity level, std::string const& text) override; }; - /** Manages a system file containing logged output. - The system file remains open during program execution. Interfaces - are provided for interoperating with standard log management - tools like logrotate(8): - http://linuxcommand.org/man_pages/logrotate8.html - @note None of the listed interfaces are thread-safe. - */ + /** + * Manages a system file containing logged output. + * The system file remains open during program execution. Interfaces + * are provided for interoperating with standard log management + * tools like logrotate(8): + * http://linuxcommand.org/man_pages/logrotate8.html + * @note None of the listed interfaces are thread-safe. + */ class File { public: - /** Construct with no associated system file. - A system file may be associated later with @ref open. - @see open - */ + /** + * Construct with no associated system file. + * A system file may be associated later with @ref open. + * @see open + */ File(); - /** Destroy the object. - If a system file is associated, it will be flushed and closed. - */ + /** + * Destroy the object. + * If a system file is associated, it will be flushed and closed. + */ ~File() = default; - /** Determine if a system file is associated with the log. - @return `true` if a system file is associated and opened for - writing. - */ + /** + * Determine if a system file is associated with the log. + * @return `true` if a system file is associated and opened for + * writing. + */ [[nodiscard]] bool isOpen() const noexcept; - /** Associate a system file with the log. - If the file does not exist an attempt is made to create it - and open it for writing. If the file already exists an attempt is - made to open it for appending. - If a system file is already associated with the log, it is closed - first. - @return `true` if the file was opened. - */ + /** + * Associate a system file with the log. + * If the file does not exist an attempt is made to create it + * and open it for writing. If the file already exists an attempt is + * made to open it for appending. + * If a system file is already associated with the log, it is closed + * first. + * @return `true` if the file was opened. + */ bool open(boost::filesystem::path const& path); - /** Close and re-open the system file associated with the log - This assists in interoperating with external log management tools. - @return `true` if the file was opened. - */ + /** + * Close and re-open the system file associated with the log + * This assists in interoperating with external log management tools. + * @return `true` if the file was opened. + */ bool closeAndReopen(); - /** Close the system file if it is open. */ + /** + * Close the system file if it is open. + */ void close(); - /** write to the log file. - Does nothing if there is no associated system file. - */ + /** + * write to the log file. + * Does nothing if there is no associated system file. + */ void write(char const* text); - /** write to the log file and append an end of line marker. - Does nothing if there is no associated system file. - */ + /** + * write to the log file and append an end of line marker. + * Does nothing if there is no associated system file. + */ void writeln(char const* text); - /** Write to the log file using std::string. */ + /** + * Write to the log file using std::string. + */ /** @{ */ void write(std::string const& str) @@ -206,8 +221,7 @@ private: #ifndef JLOG #define JLOG(x) \ if (!(x)) \ - { \ - } \ + ; \ else \ x #endif @@ -223,19 +237,21 @@ private: //------------------------------------------------------------------------------ // Debug logging: -/** Set the sink for the debug journal. - - @param sink unique_ptr to new debug Sink. - @return unique_ptr to the previous Sink. nullptr if there was no Sink. -*/ +/** + * Set the sink for the debug journal. + * + * @param sink unique_ptr to new debug Sink. + * @return unique_ptr to the previous Sink. nullptr if there was no Sink. + */ std::unique_ptr setDebugLogSink(std::unique_ptr sink); -/** Returns a debug journal. - The journal may drain to a null sink, so its output - may never be seen. Never use it for critical - information. -*/ +/** + * Returns a debug journal. + * The journal may drain to a null sink, so its output + * may never be seen. Never use it for critical + * information. + */ beast::Journal debugLog(); diff --git a/include/xrpl/basics/MathUtilities.h b/include/xrpl/basics/MathUtilities.h index 4552b335e1..78f5c76988 100644 --- a/include/xrpl/basics/MathUtilities.h +++ b/include/xrpl/basics/MathUtilities.h @@ -6,7 +6,8 @@ namespace xrpl { -/** Calculate one number divided by another number in percentage. +/** + * Calculate one number divided by another number in percentage. * The result is rounded up to the next integer, and capped in the range [0,100] * E.g. calculatePercent(1, 100) = 1 because 1/100 = 0.010000 * calculatePercent(1, 99) = 2 because 1/99 = 0.010101 @@ -19,7 +20,7 @@ namespace xrpl { * @return the percentage, in [0, 100] * * @note total cannot be zero. - * */ + */ constexpr std::size_t calculatePercent(std::size_t count, std::size_t total) { diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index cee0c45355..f90800c715 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -2,7 +2,9 @@ #include +#include #include +#include #include #include #include @@ -11,7 +13,8 @@ #include #include #include -#include +#include +#include namespace xrpl { @@ -44,46 +47,54 @@ isPowerOfTen(T value) namespace detail { -/** Builds a table of the powers of 10 +/** + * 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 +constexpr std::size_t kUint64Digits = 20; +[[maybe_unused]] constexpr std::size_t kUint128Digits = 39; + +template +consteval std::array buildPowersOfTen() { - std::array result{}; + std::array result{}; - std::uint64_t power = 1; + 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) + 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"); + if (power < std::numeric_limits::max() / 10) + throw std::logic_error("Power of 10 table is not big enough for the given type"); return result; } } // namespace detail -constexpr std::array kPowerOfTen = detail::buildPowersOfTen(); +template +constexpr std::array kPowerOfTenImpl = detail::buildPowersOfTen(); + +constexpr auto kPowerOfTen = kPowerOfTenImpl; 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); + isPowerOfTen(kPowerOfTen.back()) && *logTen(kPowerOfTen.back()) == detail::kUint64Digits - 1); -/** MantissaRange defines a range for the mantissa of a normalized Number. +/** + * 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 @@ -120,17 +131,37 @@ struct MantissaRange final { using rep = std::uint64_t; + // NOLINTBEGIN(readability-enum-initial-value) + // The values don't matter, except for Large enum class MantissaScale { + // Small can be removed when either featureSingleAssetVault or featureLendingProtocol are + // retired Small, // LargeLegacy can be removed when fixCleanup3_2_0 is retired LargeLegacy, - Large, + // Large320 can be removed when fixCleanup3_3_0 is retired + Large320, + // If Large330 is ever the only remaining "Large*" entry, it can be renamed to just "Large". + Large330, + // Large is a de-facto alias for "the latest", and is only here for backward compatibility + // in the extremely unlikely case that a downstream project made use of it. Note that + // because the behavior changed, this may still be a breaking change. + Large = Large330, }; + // NOLINTEND(readability-enum-initial-value) - // This entire enum can be removed when fixCleanup3_2_0 is retired - enum class CuspRoundingFix : bool { - Disabled = false, - Enabled = true, + // This entire enum can be removed when the last relevant amendment is retired + enum class CuspRoundingFix : std::uint8_t { + // Disabled can be removed when fixCleanup3_2_0 is retired + Disabled = 0, + // Enabled320 can be removed when fixCleanup3_3_0 is retired + Enabled320 = 1, + // If we ever get to the point that there's only one entry, remove the entire enum + Enabled330 = 2, + // Enabled is a de-facto alias for "the latest", and is only here for backward compatibility + // in the extremely unlikely case that a downstream project made use of it. Note that + // because the behavior changed, this may still be a breaking change. + Enabled = Enabled330, }; explicit constexpr MantissaRange(MantissaScale sc) : scale(sc) @@ -141,13 +172,27 @@ struct MantissaRange final 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); + CuspRoundingFix const cuspRoundingFix{isCuspFixEnabled(scale)}; static std::set const& - getAllScales(); + getAllScales() + { + static std::set const kScales = { + MantissaRange::MantissaScale::Small, + MantissaRange::MantissaScale::LargeLegacy, + MantissaRange::MantissaScale::Large320, + MantissaRange::MantissaScale::Large330, + }; + return kScales; + } + + class Access + { + static constexpr MantissaRange const& + mantissaRange(MantissaScale scale); + + friend Number; + }; private: static constexpr int @@ -158,7 +203,8 @@ private: case MantissaScale::Small: return 15; case MantissaScale::LargeLegacy: - case MantissaScale::Large: + case MantissaScale::Large320: + case MantissaScale::Large330: return 18; // LCOV_EXCL_START default: @@ -187,24 +233,24 @@ private: case MantissaScale::Small: case MantissaScale::LargeLegacy: return CuspRoundingFix::Disabled; - case MantissaScale::Large: - return CuspRoundingFix::Enabled; + case MantissaScale::Large320: + return CuspRoundingFix::Enabled320; + case MantissaScale::Large330: + return CuspRoundingFix::Enabled330; 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. template concept Integral64 = std::is_same_v || std::is_same_v; -/** Number is a floating point type that can represent a wide range of values. +/** + * Number is a floating point type that can represent a wide range of values. * * It can represent all values that can be represented by an STAmount - * regardless of asset type - XRPAmount, MPTAmount, and IOUAmount, with at least @@ -300,7 +346,6 @@ concept Integral64 = std::is_same_v || std::is_same_v::max(); static_assert(kMaxRep == 9'223'372'036'854'775'807); static_assert(-kMaxRep == std::numeric_limits::min() + 1); + static constexpr internalrep kMaxRepUp = ((kMaxRep / 10) + 1) * 10; + static_assert(kMaxRepUp == 9'223'372'036'854'775'810ULL); // May need to make unchecked private struct Unchecked @@ -386,10 +433,11 @@ public: static Number lowest() noexcept; - /** Conversions to Number are implicit and conversions away from Number - * are explicit. This design encourages and facilitates the use of Number - * as the preferred type for floating point arithmetic as it makes - * "mixed mode" more convenient, e.g. MPTAmount + Number. + /** + * Conversions to Number are implicit and conversions away from Number + * are explicit. This design encourages and facilitates the use of Number + * as the preferred type for floating point arithmetic as it makes + * "mixed mode" more convenient, e.g. MPTAmount + Number. */ explicit operator rep() const; // round to nearest, even on tie @@ -444,7 +492,9 @@ public: return l.mantissa_ < r.mantissa_; } - /** Return the sign of the amount */ + /** + * Return the sign of the amount + */ [[nodiscard]] constexpr int signum() const noexcept { @@ -498,14 +548,16 @@ public: static RoundingMode setround(RoundingMode inMode); - /** Returns which mantissa scale is currently in use for normalization. + /** + * Returns which mantissa scale is currently in use for normalization. * * If you think you need to call this outside of unit tests, no you don't. */ static MantissaRange::MantissaScale getMantissaScale(); - /** Changes which mantissa scale is used for normalization. + /** + * Changes which mantissa scale is used for normalization. * * If you think you need to call this outside of unit tests, no you don't. */ @@ -541,6 +593,13 @@ public: std::pair normalizeToRange() const; + // Safely convert rep (int64) mantissa to internalrep (uint64). If the rep + // is negative, returns the positive value. This takes a little extra work + // because converting std::numeric_limits::min() flirts with + // UB, and can vary across compilers. + static internalrep + externalToInternal(rep mantissa); + private: static thread_local RoundingMode mode; // The available ranges for mantissa @@ -550,10 +609,17 @@ private: // changing the values inside the range. static thread_local std::reference_wrapper kRange; + class Guard; + void normalize(MantissaRange const& range); - /** Normalize Number components to an arbitrary range. + // Guard has the fields that we need, as well as MantissaRange, so if we have a guard, use that + void + normalize(Guard const& guard); + + /** + * Normalize Number components to an arbitrary range. * * min/maxMantissa are parameters because this function is used by both * normalize(), which reads from kRange, and by normalizeToRange, @@ -567,7 +633,7 @@ private: int& exponent, internalrep const& minMantissa, internalrep const& maxMantissa, - MantissaRange::CuspRoundingFix cuspRoundingFixEnabled); + MantissaRange::CuspRoundingFix cuspRoundingFix); template friend void @@ -577,7 +643,7 @@ private: int& exponent, MantissaRange::rep const& minMantissa, MantissaRange::rep const& maxMantissa, - MantissaRange::CuspRoundingFix cuspRoundingFixEnabled, + MantissaRange::CuspRoundingFix cuspRoundingFix, bool dropped); [[nodiscard]] bool @@ -588,15 +654,6 @@ private: // exponent could go out of range, so it will be checked. [[nodiscard]] Number shiftExponent(int exponentDelta) const; - - // Safely convert rep (int64) mantissa to internalrep (uint64). If the rep - // is negative, returns the positive value. This takes a little extra work - // because converting std::numeric_limits::min() flirts with - // UB, and can vary across compilers. - static internalrep - externalToInternal(rep mantissa); - - class Guard; }; constexpr Number::Number(bool negative, internalrep mantissa, int exponent, Unchecked) noexcept @@ -631,7 +688,8 @@ inline Number::Number(rep mantissa) : Number{mantissa, 0} { } -/** Returns the mantissa of the external view of the Number. +/** + * Returns the mantissa of the external view of the Number. * * Please see the "---- External Interface ----" section of the class * documentation for an explanation of why the internal value may be modified. @@ -652,7 +710,8 @@ Number::mantissa() const noexcept return sign * static_cast(m); } -/** Returns the exponent of the external view of the Number. +/** + * Returns the exponent of the external view of the Number. * * Please see the "---- External Interface ----" section of the class * documentation for an explanation of why the internal value may be modified. @@ -858,21 +917,11 @@ squelch(Number const& x, Number const& limit) noexcept return x; } -inline std::string -to_string(MantissaRange::MantissaScale const& scale) -{ - switch (scale) - { - case MantissaRange::MantissaScale::Small: - return "small"; - case MantissaRange::MantissaScale::LargeLegacy: - return "largeLegacy"; - case MantissaRange::MantissaScale::Large: - return "large"; - default: - throw std::runtime_error("Bad scale"); - } -} +std::string +to_string(MantissaRange::MantissaScale const& scale); + +std::string +to_string(Number::RoundingMode const& round); class SaveNumberRoundMode { @@ -911,10 +960,10 @@ public: operator=(NumberRoundModeGuard const&) = delete; }; -/** Sets the new scale and restores the old scale when it leaves scope. +/** + * Sets the new scale and restores the old scale when it leaves scope. * * If you think you need to use this class outside of unit tests, no you don't. - * */ class NumberMantissaScaleGuard { diff --git a/include/xrpl/basics/RangeSet.h b/include/xrpl/basics/RangeSet.h index e1cee8b6c4..3de882979e 100644 --- a/include/xrpl/basics/RangeSet.h +++ b/include/xrpl/basics/RangeSet.h @@ -6,29 +6,32 @@ #include #include +#include #include #include #include namespace xrpl { -/** A closed interval over the domain T. - - For an instance ClosedInterval c, this represents the closed interval - (c.first(), c.last()). A single element interval has c.first() == c.last(). - - This is simply a type-alias for boost interval container library interval - set, so users should consult that documentation for available supporting - member and free functions. -*/ +/** + * A closed interval over the domain T. + * + * For an instance ClosedInterval c, this represents the closed interval + * (c.first(), c.last()). A single element interval has c.first() == c.last(). + * + * This is simply a type-alias for boost interval container library interval + * set, so users should consult that documentation for available supporting + * member and free functions. + */ template using ClosedInterval = boost::icl::closed_interval; -/** Create a closed range interval - - Helper function to create a closed range interval without having to qualify - the template argument. -*/ +/** + * Create a closed range interval + * + * Helper function to create a closed range interval without having to qualify + * the template argument. + */ template ClosedInterval range(T low, T high) @@ -36,28 +39,30 @@ range(T low, T high) return ClosedInterval(low, high); } -/** A set of closed intervals over the domain T. - - Represents a set of values of the domain T using the minimum number - of disjoint ClosedInterval. This is useful to represent ranges of - T where a few instances are missing, e.g. the set 1-5,8-9,11-14. - - This is simply a type-alias for boost interval container library interval - set, so users should consult that documentation for available supporting - member and free functions. -*/ +/** + * A set of closed intervals over the domain T. + * + * Represents a set of values of the domain T using the minimum number + * of disjoint ClosedInterval. This is useful to represent ranges of + * T where a few instances are missing, e.g. the set 1-5,8-9,11-14. + * + * This is simply a type-alias for boost interval container library interval + * set, so users should consult that documentation for available supporting + * member and free functions. + */ template using RangeSet = boost::icl::interval_set>; -/** Convert a ClosedInterval to a styled string - - The styled string is - "c.first()-c.last()" if c.first() != c.last() - "c.first()" if c.first() == c.last() - - @param ci The closed interval to convert - @return The style string -*/ +/** + * Convert a ClosedInterval to a styled string + * + * The styled string is + * "c.first()-c.last()" if c.first() != c.last() + * "c.first()" if c.first() == c.last() + * + * @param ci The closed interval to convert + * @return The style string + */ template std::string to_string(ClosedInterval const& ci) @@ -67,14 +72,15 @@ to_string(ClosedInterval const& ci) return std::to_string(ci.first()) + "-" + std::to_string(ci.last()); } -/** Convert the given RangeSet to a styled string. - - The styled string representation is the set of disjoint intervals joined - by commas. The string "empty" is returned if the set is empty. - - @param rs The rangeset to convert - @return The styled string -*/ +/** + * Convert the given RangeSet to a styled string. + * + * The styled string representation is the set of disjoint intervals joined + * by commas. The string "empty" is returned if the set is empty. + * + * @param rs The rangeset to convert + * @return The styled string + */ template std::string to_string(RangeSet const& rs) @@ -90,15 +96,16 @@ to_string(RangeSet const& rs) return s; } -/** Convert the given styled string to a RangeSet. - - The styled string representation is the set - of disjoint intervals joined by commas. - - @param rs The set to be populated - @param s The styled string to convert - @return True on successfully converting styled string -*/ +/** + * Convert the given styled string to a RangeSet. + * + * The styled string representation is the set + * of disjoint intervals joined by commas. + * + * @param rs The set to be populated + * @param s The styled string to convert + * @return True on successfully converting styled string + */ template [[nodiscard]] bool fromString(RangeSet& rs, std::string const& s) @@ -160,14 +167,15 @@ fromString(RangeSet& rs, std::string const& s) return result; } -/** Find the largest value not in the set that is less than a given value. - - @param rs The set of interest - @param t The value that must be larger than the result - @param minVal (Default is 0) The smallest allowed value - @return The largest v such that minV <= v < t and !contains(rs, v) or - std::nullopt if no such v exists. -*/ +/** + * Find the largest value not in the set that is less than a given value. + * + * @param rs The set of interest + * @param t The value that must be larger than the result + * @param minVal (Default is 0) The smallest allowed value + * @return The largest v such that minV <= v < t and !contains(rs, v) or + * std::nullopt if no such v exists. + */ template std::optional prevMissing(RangeSet const& rs, T t, T minVal = 0) diff --git a/include/xrpl/basics/Resolver.h b/include/xrpl/basics/Resolver.h index 3b6a950247..239eb9630e 100644 --- a/include/xrpl/basics/Resolver.h +++ b/include/xrpl/basics/Resolver.h @@ -3,6 +3,7 @@ #include #include +#include #include namespace xrpl { @@ -14,22 +15,29 @@ public: virtual ~Resolver() = 0; - /** Issue an asynchronous stop request. */ + /** + * Issue an asynchronous stop request. + */ virtual void stopAsync() = 0; - /** Issue a synchronous stop request. */ + /** + * Issue a synchronous stop request. + */ virtual void stop() = 0; - /** Issue a synchronous start request. */ + /** + * Issue a synchronous start request. + */ virtual void start() = 0; - /** resolve all hostnames on the list - @param names the names to be resolved - @param handler the handler to call - */ + /** + * resolve all hostnames on the list + * @param names the names to be resolved + * @param handler the handler to call + */ /** @{ */ template void 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..1b78af2fae 100644 --- a/include/xrpl/basics/SharedWeakCachePointer.h +++ b/include/xrpl/basics/SharedWeakCachePointer.h @@ -1,17 +1,20 @@ #pragma once +#include +#include #include #include namespace xrpl { -/** A combination of a std::shared_ptr and a std::weak_pointer. - - -This class is a wrapper to a `std::variant` -This class is useful for storing intrusive pointers in tagged caches using less -memory than storing both pointers directly. -*/ +/** + * A combination of a std::shared_ptr and a std::weak_pointer. + * + * + * This class is a wrapper to a `std::variant` + * This class is useful for storing intrusive pointers in tagged caches using less + * memory than storing both pointers directly. + */ template class SharedWeakCachePointer @@ -46,65 +49,79 @@ public: ~SharedWeakCachePointer(); - /** Return a strong pointer if this is already a strong pointer (i.e. don't - lock the weak pointer. Use the `lock` method if that's what's needed) + /** + * Return a strong pointer if this is already a strong pointer (i.e. don't + * lock the weak pointer. Use the `lock` method if that's what's needed) */ [[nodiscard]] std::shared_ptr const& getStrong() const; - /** Return true if this is a strong pointer and the strong pointer is - seated. + /** + * Return true if this is a strong pointer and the strong pointer is + * seated. */ explicit operator bool() const noexcept; - /** Set the pointer to null, decrement the appropriate ref count, and run - the appropriate release action. + /** + * Set the pointer to null, decrement the appropriate ref count, and run + * the appropriate release action. */ void reset(); - /** If this is a strong pointer, return the raw pointer. Otherwise return - null. + /** + * If this is a strong pointer, return the raw pointer. Otherwise return + * null. */ [[nodiscard]] T* get() const; - /** If this is a strong pointer, return the strong count. Otherwise return 0 + /** + * If this is a strong pointer, return the strong count. Otherwise return 0 */ [[nodiscard]] std::size_t useCount() const; - /** Return true if there is a non-zero strong count. */ + /** + * Return true if there is a non-zero strong count. + */ [[nodiscard]] bool expired() const; - /** If this is a strong pointer, return the strong pointer. Otherwise - attempt to lock the weak pointer. + /** + * If this is a strong pointer, return the strong pointer. Otherwise + * attempt to lock the weak pointer. */ [[nodiscard]] std::shared_ptr lock() const; - /** Return true is this represents a strong pointer. */ + /** + * Return true is this represents a strong pointer. + */ [[nodiscard]] bool isStrong() const; - /** Return true is this represents a weak pointer. */ + /** + * Return true is this represents a weak pointer. + */ [[nodiscard]] bool isWeak() const; - /** If this is a weak pointer, attempt to convert it to a strong pointer. - - @return true if successfully converted to a strong pointer (or was - already a strong pointer). Otherwise false. - */ + /** + * If this is a weak pointer, attempt to convert it to a strong pointer. + * + * @return true if successfully converted to a strong pointer (or was + * already a strong pointer). Otherwise false. + */ bool convertToStrong(); - /** If this is a strong pointer, attempt to convert it to a weak pointer. - - @return false if the pointer is null. Otherwise return true. - */ + /** + * If this is a strong pointer, attempt to convert it to a weak pointer. + * + * @return false if the pointer is null. Otherwise return true. + */ bool convertToWeak(); diff --git a/include/xrpl/basics/SlabAllocator.h b/include/xrpl/basics/SlabAllocator.h index 0172b1ade2..7b6e88e8bc 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 @@ -32,7 +33,9 @@ class SlabAllocator static_assert(alignof(Type) == 8 || alignof(Type) == 4); - /** A block of memory that is owned by a slab allocator */ + /** + * A block of memory that is owned by a slab allocator + */ struct SlabBlock { // A mutex to protect the freelist for this block: @@ -79,7 +82,9 @@ class SlabAllocator SlabBlock& operator=(SlabBlock&& other) = delete; - /** Determines whether the given pointer belongs to this allocator */ + /** + * Determines whether the given pointer belongs to this allocator + */ bool own(std::uint8_t const* pIn) const noexcept { @@ -106,14 +111,15 @@ class SlabAllocator return ret; } - /** Return an item to this allocator's freelist. - - @param ptr The pointer to the chunk of memory being deallocated. - - @note This is a dangerous, private interface; the item being - returned should belong to this allocator. Debug builds - will check and assert if this is not the case. Release - builds will not. + /** + * Return an item to this allocator's freelist. + * + * @param ptr The pointer to the chunk of memory being deallocated. + * + * @note This is a dangerous, private interface; the item being + * returned should belong to this allocator. Debug builds + * will check and assert if this is not the case. Release + * builds will not. */ void deallocate(std::uint8_t* ptr) noexcept @@ -144,13 +150,14 @@ private: std::size_t const slabSize_; public: - /** Constructs a slab allocator able to allocate objects of a fixed size - - @param count the number of items the slab allocator can allocate; note - that a count of 0 is valid and means that the allocator - is, effectively, disabled. This can be very useful in some - contexts (e.g. when minimal memory usage is needed) and - allows for graceful failure. + /** + * Constructs a slab allocator able to allocate objects of a fixed size + * + * @param count the number of items the slab allocator can allocate; note + * that a count of 0 is valid and means that the allocator + * is, effectively, disabled. This can be very useful in some + * contexts (e.g. when minimal memory usage is needed) and + * allows for graceful failure. */ constexpr explicit SlabAllocator( std::size_t extra, @@ -178,17 +185,20 @@ public: // shutdown process up could make this possible. ~SlabAllocator() = default; - /** Returns the size of the memory block this allocator returns. */ + /** + * Returns the size of the memory block this allocator returns. + */ [[nodiscard]] constexpr std::size_t size() const noexcept { return itemSize_; } - /** Returns a suitably aligned pointer, if one is available. - - @return a pointer to a block of memory from the allocator, or - nullptr if the allocator can't satisfy this request. + /** + * Returns a suitably aligned pointer, if one is available. + * + * @return a pointer to a block of memory from the allocator, or + * nullptr if the allocator can't satisfy this request. */ std::uint8_t* allocate() noexcept @@ -249,12 +259,13 @@ public: return slab->allocate(); } - /** Returns the memory block to the allocator. - - @param ptr A pointer to a memory block. - @param size If non-zero, a hint as to the size of the block. - @return true if this memory block belonged to the allocator and has - been released; false otherwise. + /** + * Returns the memory block to the allocator. + * + * @param ptr A pointer to a memory block. + * @param size If non-zero, a hint as to the size of the block. + * @return true if this memory block belonged to the allocator and has + * been released; false otherwise. */ bool deallocate(std::uint8_t* ptr) noexcept @@ -277,7 +288,9 @@ public: } }; -/** A collection of slab allocators of various sizes for a given type. */ +/** + * A collection of slab allocators of various sizes for a given type. + */ template class SlabAllocatorSet { @@ -344,13 +357,14 @@ public: ~SlabAllocatorSet() = default; - /** Returns a suitably aligned pointer, if one is available. - - @param extra The number of extra bytes, above and beyond the size of - the object, that should be returned by the allocator. - - @return a pointer to a block of memory, or nullptr if the allocator - can't satisfy this request. + /** + * Returns a suitably aligned pointer, if one is available. + * + * @param extra The number of extra bytes, above and beyond the size of + * the object, that should be returned by the allocator. + * + * @return a pointer to a block of memory, or nullptr if the allocator + * can't satisfy this request. */ std::uint8_t* allocate(std::size_t extra) noexcept @@ -367,12 +381,13 @@ public: return nullptr; } - /** Returns the memory block to the allocator. - - @param ptr A pointer to a memory block. - - @return true if this memory block belonged to one of the allocators - in this set and has been released; false otherwise. + /** + * Returns the memory block to the allocator. + * + * @param ptr A pointer to a memory block. + * + * @return true if this memory block belonged to one of the allocators + * in this set and has been released; false otherwise. */ bool deallocate(std::uint8_t* ptr) noexcept diff --git a/include/xrpl/basics/Slice.h b/include/xrpl/basics/Slice.h index 948d012958..36e7615c3a 100644 --- a/include/xrpl/basics/Slice.h +++ b/include/xrpl/basics/Slice.h @@ -16,12 +16,13 @@ namespace xrpl { -/** An immutable linear range of bytes. - - A fully constructed Slice is guaranteed to be in a valid state. - A Slice is lightweight and copyable, it retains no ownership - of the underlying memory. -*/ +/** + * An immutable linear range of bytes. + * + * A fully constructed Slice is guaranteed to be in a valid state. + * A Slice is lightweight and copyable, it retains no ownership + * of the underlying memory. + */ class Slice { private: @@ -32,30 +33,37 @@ public: using value_type = std::uint8_t; using const_iterator = value_type const*; - /** Default constructed Slice has length 0. */ + /** + * Default constructed Slice has length 0. + */ Slice() noexcept = default; Slice(Slice const&) noexcept = default; Slice& operator=(Slice const&) noexcept = default; - /** Create a slice pointing to existing memory. */ + /** + * Create a slice pointing to existing memory. + */ Slice(void const* data, std::size_t size) noexcept : data_(reinterpret_cast(data)), size_(size) { } - /** Return `true` if the byte range is empty. */ + /** + * Return `true` if the byte range is empty. + */ [[nodiscard]] bool empty() const noexcept { return size_ == 0; } - /** Returns the number of bytes in the storage. - - This may be zero for an empty range. - */ + /** + * Returns the number of bytes in the storage. + * + * This may be zero for an empty range. + */ /** @{ */ [[nodiscard]] std::size_t size() const noexcept @@ -70,17 +78,20 @@ public: } /** @} */ - /** Return a pointer to beginning of the storage. - @note The return type is guaranteed to be a pointer - to a single byte, to facilitate pointer arithmetic. - */ + /** + * Return a pointer to beginning of the storage. + * @note The return type is guaranteed to be a pointer + * to a single byte, to facilitate pointer arithmetic. + */ [[nodiscard]] std::uint8_t const* data() const noexcept { return data_; } - /** Access raw bytes. */ + /** + * Access raw bytes. + */ std::uint8_t operator[](std::size_t i) const noexcept { @@ -88,7 +99,9 @@ public: return data_[i]; } - /** Advance the buffer. */ + /** + * Advance the buffer. + */ /** @{ */ Slice& operator+=(std::size_t n) @@ -108,7 +121,9 @@ public: } /** @} */ - /** Shrinks the slice by moving its start forward by n characters. */ + /** + * Shrinks the slice by moving its start forward by n characters. + */ void removePrefix(std::size_t n) { @@ -116,7 +131,9 @@ public: size_ -= n; } - /** Shrinks the slice by moving its end backward by n characters. */ + /** + * Shrinks the slice by moving its end backward by n characters. + */ void removeSuffix(std::size_t n) { @@ -147,16 +164,17 @@ public: return data_ + size_; } - /** Return a "sub slice" of given length starting at the given position - - Note that the subslice encompasses the range [pos, pos + rCount), - where rCount is the smaller of count and size() - pos. - - @param pos position of the first character - @count requested length - - @returns The requested subslice, if the request is valid. - @throws std::out_of_range if pos > size() + /** + * Return a "sub slice" of given length starting at the given position + * + * Note that the subslice encompasses the range [pos, pos + rCount), + * where rCount is the smaller of count and size() - pos. + * + * @param pos position of the first character + * @count requested length + * + * @return The requested subslice, if the request is valid. + * @throws std::out_of_range if pos > size() */ [[nodiscard]] Slice substr(std::size_t pos, std::size_t count = std::numeric_limits::max()) const @@ -211,15 +229,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 1d3434b7ed..2b360d2fda 100644 --- a/include/xrpl/basics/StringUtilities.h +++ b/include/xrpl/basics/StringUtilities.h @@ -1,30 +1,32 @@ #pragma once #include -#include #include #include #include #include +#include #include #include #include #include #include +#include namespace xrpl { -/** Format arbitrary binary data as an SQLite "blob literal". - - In SQLite, blob literals must be encoded when used in a query. Per - https://sqlite.org/lang_expr.html#literal_values_constants_ they are - encoded as string literals containing hexadecimal data and preceded - by a single 'X' character. - - @param blob An arbitrary blob of binary data - @return The input, encoded as a blob literal. +/** + * Format arbitrary binary data as an SQLite "blob literal". + * + * In SQLite, blob literals must be encoded when used in a query. Per + * https://sqlite.org/lang_expr.html#literal_values_constants_ they are + * encoded as string literals containing hexadecimal data and preceded + * by a single 'X' character. + * + * @param blob An arbitrary blob of binary data + * @return The input, encoded as a blob literal. */ std::string sqlBlobLiteral(Blob const& blob); @@ -129,11 +131,12 @@ trimWhitespace(std::string str); std::optional toUInt64(std::string const& s); -/** Determines if the given string looks like a TOML-file hosting domain. - - Do not use this function to determine if a particular string is a valid - domain, as this function may reject domains that are otherwise valid and - doesn't check whether the TLD is valid. +/** + * Determines if the given string looks like a TOML-file hosting domain. + * + * Do not use this function to determine if a particular string is a valid + * domain, as this function may reject domains that are otherwise valid and + * doesn't check whether the TLD is valid. */ bool isProperlyFormedTomlDomain(std::string_view domain); diff --git a/include/xrpl/basics/TaggedCache.h b/include/xrpl/basics/TaggedCache.h index 380b7c687f..7bb2cb552b 100644 --- a/include/xrpl/basics/TaggedCache.h +++ b/include/xrpl/basics/TaggedCache.h @@ -1,34 +1,59 @@ #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 { -/** 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 - with the same tag to get the same actual object. +namespace detail { - So long as data is in the cache, it will stay in memory. - If it stays in memory even after it is ejected from the cache, - the map will track it. +// 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; - @note Callers must not modify data objects that are stored in the cache - unless they hold their own lock over all cache operations. -*/ +} // 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 + * with the same tag to get the same actual object. + * + * So long as data is in the cache, it will stay in memory. + * If it stays in memory even after it is ejected from the cache, + * the map will track it. + * + * @note Callers must not modify data objects that are stored in the cache + * unless they hold their own lock over all cache operations. + */ template < class Key, class T, @@ -58,11 +83,15 @@ public: beast::insight::Collector::ptr const& collector = beast::insight::NullCollector::make()); public: - /** Return the clock associated with the cache. */ + /** + * Return the clock associated with the cache. + */ clock_type& clock(); - /** Returns the number of items in the container. */ + /** + * Returns the number of items in the container. + */ std::size_t size() const; @@ -81,9 +110,10 @@ public: void reset(); - /** Refresh the last access time on a key if present. - @return `true` If the key was found. - */ + /** + * Refresh the last access time on a key if present. + * @return `true` If the key was found. + */ template bool touchIfExists(KeyComparable const& key); @@ -96,44 +126,110 @@ public: bool del(key_type const& key, bool valid); -public: - /** Replace aliased objects with originals. +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&>; - Due to concurrency it is possible for two separate objects with - the same content and referring to the same unique "thing" to exist. - This routine eliminates the duplicate and performs a replacement - on the callers shared pointer if needed. - - @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 - - @return `true` If the key already existed. - */ - template + /** + * 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 - canonicalize(key_type const& key, SharedPointerType& data, R&& replaceCallback); + canonicalizeImpl( + key_type const& key, + CanonicalizeClientPointerType data, + Policy policy, + Callback&& replaceCallback = nullptr); +public: + /** + * Replace aliased objects with originals. + * + * Due to concurrency it is possible for two separate objects with + * the same content and referring to the same unique "thing" to exist. + * 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 replaceCallback A callable (existing strong pointer -> bool). + * + * @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, 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); SharedPointerType fetch(key_type const& key); - /** Insert the element into the container. - If the key already exists, nothing happens. - @return `true` If the element was inserted - */ + /** + * Insert the element into the container. + * If the key already exists, nothing happens. + * @return `true` If the element was inserted + */ 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. @@ -150,15 +246,18 @@ public: getKeys() const; // CachedSLEs functions. - /** Returns the fraction of cache hits. */ + /** + * Returns the fraction of cache hits. + */ double rate() const; - /** Fetch an item from the cache. - If the digest was not found, Handler - will be called with this signature: - SLE::const_pointer(void) - */ + /** + * Fetch an item from the cache. + * If the digest was not found, Handler + * will be called with this signature: + * SLE::const_pointer(void) + */ template SharedPointerType fetch(key_type const& digest, Handler const& h); @@ -262,7 +361,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 +370,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&); diff --git a/include/xrpl/basics/TaggedCache.ipp b/include/xrpl/basics/TaggedCache.ipp index cee02749c6..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,7 +57,10 @@ 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) , targetSize_(size) , targetAge_(expiration) @@ -300,13 +328,29 @@ template < class Hash, class KeyEqual, class Mutex> -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); @@ -324,13 +368,14 @@ 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,7 +534,8 @@ 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()); @@ -676,7 +741,7 @@ TaggedCache& allRemovals, std::scoped_lock const&) @@ -756,7 +821,7 @@ TaggedCache& allRemovals, std::scoped_lock const&) diff --git a/include/xrpl/basics/ToString.h b/include/xrpl/basics/ToString.h index 7764c1e3e3..a54db8a8ce 100644 --- a/include/xrpl/basics/ToString.h +++ b/include/xrpl/basics/ToString.h @@ -5,15 +5,17 @@ namespace xrpl { -/** to_string() generalizes std::to_string to handle bools, chars, and strings. - - It's also possible to provide implementation of to_string for a class - which needs a string implementation. +/** + * to_string() generalizes std::to_string to handle bools, chars, and strings. + * + * It's also possible to provide implementation of to_string for a class + * which needs a string implementation. */ 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/UptimeClock.h b/include/xrpl/basics/UptimeClock.h index 502aae7c25..b375de4497 100644 --- a/include/xrpl/basics/UptimeClock.h +++ b/include/xrpl/basics/UptimeClock.h @@ -7,12 +7,13 @@ namespace xrpl { -/** Tracks program uptime to seconds precision. - - The timer caches the current time as a performance optimization. - This allows clients to query the current time thousands of times - per second. -*/ +/** + * Tracks program uptime to seconds precision. + * + * The timer caches the current time as a performance optimization. + * This allows clients to query the current time thousands of times + * per second. + */ class UptimeClock { diff --git a/include/xrpl/basics/base64.h b/include/xrpl/basics/base64.h index 660958ce14..24fd660e65 100644 --- a/include/xrpl/basics/base64.h +++ b/include/xrpl/basics/base64.h @@ -34,6 +34,7 @@ #pragma once +#include #include #include #include diff --git a/include/xrpl/basics/base_uint.h b/include/xrpl/basics/base_uint.h index 93520ff699..bee8b8b945 100644 --- a/include/xrpl/basics/base_uint.h +++ b/include/xrpl/basics/base_uint.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -18,8 +19,17 @@ #include #include +#include +#include +#include #include #include +#include +#include +#include +#include +#include +#include #include namespace xrpl { @@ -53,18 +63,19 @@ struct AlwaysFalseT : std::bool_constant } // namespace detail -/** Integers of any length that is a multiple of 32-bits - - @note This class stores its values internally in big-endian - form and that internal representation is part of the - binary protocol of the XRP Ledger and cannot be changed - arbitrarily without causing breakage. - - @tparam Bits The number of bits this integer should have; must - be at least 64 and a multiple of 32. - @tparam Tag An arbitrary type that functions as a tag and allows - the instantiation of "distinct" types that the same - number of bits. +/** + * Integers of any length that is a multiple of 32-bits + * + * @note This class stores its values internally in big-endian + * form and that internal representation is part of the + * binary protocol of the XRP Ledger and cannot be changed + * arbitrarily without causing breakage. + * + * @tparam Bits The number of bits this integer should have; must + * be at least 64 and a multiple of 32. + * @tparam Tag An arbitrary type that functions as a tag and allows + * the instantiation of "distinct" types that the same + * number of bits. */ template class BaseUInt @@ -87,7 +98,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; @@ -144,21 +155,23 @@ public: return data() + kBytes; } - /** Value hashing function. - The seed prevents crafted inputs from causing degenerate parent - containers. - */ + /** + * Value hashing function. + * The seed prevents crafted inputs from causing degenerate parent + * containers. + */ using hasher = HardenedHash<>; //-------------------------------------------------------------------------- private: - /** Construct from a raw pointer. - The buffer pointed to by `data` must be at least Bits/8 bytes. - - @note the structure is used to disambiguate this from the std::uint64_t - constructor: something like base_uint(0) is ambiguous. - */ + /** + * Construct from a raw pointer. + * The buffer pointed to by `data` must be at least Bits/8 bytes. + * + * @note the structure is used to disambiguate this from the std::uint64_t + * constructor: something like base_uint(0) is ambiguous. + */ // NIKB TODO Remove the need for this constructor. struct VoidHelper { @@ -270,12 +283,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,33 +297,38 @@ 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( c.size() * sizeof(typename Container::value_type) == size(), "xrpl::BaseUInt::fromRaw(Container auto) : input size match"); - std::memcpy(result.data_.data(), c.data(), size()); + std::size_t const canCopy = + std::min(size(), c.size() * sizeof(typename Container::value_type)); + std::memcpy(result.data_.data(), c.data(), canCopy); return result; } 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(), "xrpl::BaseUInt::operator=(Container auto) : input size match"); - std::memcpy(data_.data(), c.data(), size()); + std::size_t const canCopy = + std::min(size(), c.size() * sizeof(typename Container::value_type)); + if (canCopy < size()) + *this = beast::kZero; + std::memcpy(data_.data(), c.data(), canCopy); return *this; } @@ -495,13 +512,14 @@ public: h(a.data_.data(), sizeof(a.data_)); } - /** Parse a hex string into a base_uint - - The input must be precisely `2 * bytes` hexadecimal characters - long, with one exception: the value '0'. - - @param sv A null-terminated string of hexadecimal characters - @return true if the input was parsed properly; false otherwise. + /** + * Parse a hex string into a base_uint + * + * The input must be precisely `2 * bytes` hexadecimal characters + * long, with one exception: the value '0'. + * + * @param sv A null-terminated string of hexadecimal characters + * @return true if the input was parsed properly; false otherwise. */ [[nodiscard]] constexpr bool parseHex(std::string_view sv) @@ -587,7 +605,7 @@ template [[nodiscard]] constexpr bool operator==(BaseUInt const& lhs, BaseUInt const& rhs) { - return (lhs <=> rhs) == 0; + return (lhs <=> rhs) == 0; // NOLINT(modernize-use-nullptr) } //------------------------------------------------------------------------------ diff --git a/include/xrpl/basics/chrono.h b/include/xrpl/basics/chrono.h index 5d6de06248..b855318524 100644 --- a/include/xrpl/basics/chrono.h +++ b/include/xrpl/basics/chrono.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace xrpl { @@ -20,15 +21,16 @@ using days = using weeks = std::chrono::duration>>; -/** Clock for measuring the network time. - - The epoch is January 1, 2000 - - epoch_offset - = date(2000-01-01) - date(1970-0-01) - = days(10957) - = seconds(946684800) -*/ +/** + * Clock for measuring the network time. + * + * The epoch is January 1, 2000 + * + * epoch_offset + * = date(2000-01-01) - date(1970-0-01) + * = days(10957) + * = seconds(946684800) + */ static constexpr std::chrono::seconds kEpochOffset = date::sys_days{date::year{2000} / 1 / 1} - date::sys_days{date::year{1970} / 1 / 1}; @@ -80,16 +82,21 @@ toStringIso(NetClock::time_point tp) return toStringIso(date::sys_time{tp.time_since_epoch() + kEpochOffset}); } -/** A clock for measuring elapsed time. - - The epoch is unspecified. -*/ +/** + * A clock for measuring elapsed time. + * + * The epoch is unspecified. + */ using Stopwatch = beast::AbstractClock; -/** A manual Stopwatch for unit tests. */ +/** + * A manual Stopwatch for unit tests. + */ using TestStopwatch = beast::ManualClock; -/** Returns an instance of a wall clock. */ +/** + * Returns an instance of a wall clock. + */ inline Stopwatch& stopwatch() { 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/contract.h b/include/xrpl/basics/contract.h index 0e90687de3..6588cb5d1a 100644 --- a/include/xrpl/basics/contract.h +++ b/include/xrpl/basics/contract.h @@ -15,20 +15,23 @@ namespace xrpl { preconditions, postconditions, and invariants. */ -/** Generates and logs a call stack */ +/** + * Generates and logs a call stack + */ void logThrow(std::string const& title); -/** Rethrow the exception currently being handled. - - When called from within a catch block, it will pass - control to the next matching exception handler, if any. - Otherwise, std::terminate will be called. - - ASAN can't handle sudden jumps in control flow very well. This - function is marked as XRPL_NO_SANITIZE_ADDRESS to prevent it from - triggering false positives, since it throws. -*/ +/** + * Rethrow the exception currently being handled. + * + * When called from within a catch block, it will pass + * control to the next matching exception handler, if any. + * Otherwise, std::terminate will be called. + * + * ASAN can't handle sudden jumps in control flow very well. This + * function is marked as XRPL_NO_SANITIZE_ADDRESS to prevent it from + * triggering false positives, since it throws. + */ [[noreturn]] XRPL_NO_SANITIZE_ADDRESS inline void rethrow() { @@ -56,7 +59,9 @@ Throw(Args&&... args) throw std::move(e); } -/** Called when faulty logic causes a broken invariant. */ +/** + * Called when faulty logic causes a broken invariant. + */ [[noreturn]] void logicError(std::string const& how) noexcept; diff --git a/include/xrpl/basics/hardened_hash.h b/include/xrpl/basics/hardened_hash.h index efc77e058b..6b8277a560 100644 --- a/include/xrpl/basics/hardened_hash.h +++ b/include/xrpl/basics/hardened_hash.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -40,33 +39,33 @@ makeSeedPair() noexcept /** * Seed functor once per construction - - A std compatible hash adapter that resists adversarial inputs. - For this to work, T must implement in its own namespace: - - @code - - template - void - hash_append (Hasher& h, T const& t) noexcept - { - // hash_append each base and member that should - // participate in forming the hash - using beast::hash_append; - hash_append (h, static_cast(t)); - hash_append (h, static_cast(t)); - // ... - hash_append (h, t.member1); - hash_append (h, t.member2); - // ... - } - - @endcode - - Do not use any version of Murmur or CityHash for the Hasher - template parameter (the hashing algorithm). For details - see https://131002.net/siphash/#at -*/ + * + * A std compatible hash adapter that resists adversarial inputs. + * For this to work, T must implement in its own namespace: + * + * @code + * + * template + * void + * hash_append (Hasher& h, T const& t) noexcept + * { + * // hash_append each base and member that should + * // participate in forming the hash + * using beast::hash_append; + * hash_append (h, static_cast(t)); + * hash_append (h, static_cast(t)); + * // ... + * hash_append (h, t.member1); + * hash_append (h, t.member2); + * // ... + * } + * + * @endcode + * + * Do not use any version of Murmur or CityHash for the Hasher + * template parameter (the hashing algorithm). For details + * see https://131002.net/siphash/#at + */ template class HardenedHash @@ -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 c214212473..492e4f3122 100644 --- a/include/xrpl/basics/join.h +++ b/include/xrpl/basics/join.h @@ -1,7 +1,9 @@ #pragma once +#include #include #include +#include namespace xrpl { diff --git a/include/xrpl/basics/make_SSLContext.h b/include/xrpl/basics/make_SSLContext.h index 46f6a15e84..c8ada176f9 100644 --- a/include/xrpl/basics/make_SSLContext.h +++ b/include/xrpl/basics/make_SSLContext.h @@ -2,15 +2,20 @@ #include +#include #include namespace xrpl { -/** Create a self-signed SSL context that allows anonymous Diffie Hellman. */ +/** + * Create a self-signed SSL context that allows anonymous Diffie Hellman. + */ std::shared_ptr makeSslContext(std::string const& cipherList); -/** Create an authenticated SSL context using the specified files. */ +/** + * Create an authenticated SSL context using the specified files. + */ std::shared_ptr makeSslContextAuthed( std::string const& keyFile, diff --git a/include/xrpl/basics/mulDiv.h b/include/xrpl/basics/mulDiv.h index 9076da62f2..38fa57294b 100644 --- a/include/xrpl/basics/mulDiv.h +++ b/include/xrpl/basics/mulDiv.h @@ -7,16 +7,16 @@ namespace xrpl { constexpr auto kMuldivMax = std::numeric_limits::max(); -/** Return value*mul/div accurately. - Computes the result of the multiplication and division in - a single step, avoiding overflow and retaining precision. - Throws: - None - Returns: - `std::optional`: - `std::nullopt` if the calculation overflows. Otherwise, `value * mul - / div`. -*/ +/** + * Return value*mul/div accurately. + * + * Computes the result of the multiplication and division in + * a single step, avoiding overflow and retaining precision. + * + * @throws None + * @return `std::nullopt` if the calculation overflows. Otherwise, + * `value * mul / div`. + */ std::optional mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div); 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..7aeb7d6145 100644 --- a/include/xrpl/basics/random.h +++ b/include/xrpl/basics/random.h @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -29,20 +28,22 @@ 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 -/** Return the default random engine. - - This engine is guaranteed to be deterministic, but by - default will be randomly seeded. It is NOT cryptographically - secure and MUST NOT be used to generate randomness that - will be used for keys, secure cookies, IVs, padding, etc. - - Each thread gets its own instance of the engine which - will be randomly seeded. -*/ +/** + * Return the default random engine. + * + * This engine is guaranteed to be deterministic, but by + * default will be randomly seeded. It is NOT cryptographically + * secure and MUST NOT be used to generate randomness that + * will be used for keys, secure cookies, IVs, padding, etc. + * + * Each thread gets its own instance of the engine which + * will be randomly seeded. + */ inline beast::xor_shift_engine& defaultPrng() { @@ -70,29 +71,31 @@ defaultPrng() return kEngine; } -/** Return a uniformly distributed random integer. - - @param min The smallest value to return. If not specified - the value defaults to 0. - @param max The largest value to return. If not specified - the value defaults to the largest value that - can be represented. - - The randomness is generated by the specified engine (or - the default engine if one is not specified). The result - is cryptographically secure only when the engine passed - into the function is cryptographically secure. - - @note The range is always a closed interval, so calling - rand_int(-5, 15) can return any integer in the - closed interval [-5, 15]; similarly, calling - rand_int(7) can return any integer in the closed - interval [0, 7]. -*/ +/** + * Return a uniformly distributed random integer. + * + * @param min The smallest value to return. If not specified + * the value defaults to 0. + * @param max The largest value to return. If not specified + * the value defaults to the largest value that + * can be represented. + * + * The randomness is generated by the specified engine (or + * the default engine if one is not specified). The result + * is cryptographically secure only when the engine passed + * into the function is cryptographically secure. + * + * @note The range is always a closed interval, so calling + * rand_int(-5, 15) can return any integer in the + * closed interval [-5, 15]; similarly, calling + * rand_int(7) can return any integer in the closed + * interval [0, 7]. + */ /** @{ */ 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,63 +106,73 @@ 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()); } /** @} */ -/** Return a random byte */ +/** + * 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()); } /** @} */ -/** Return a random boolean value */ +/** + * Return a random boolean value + */ /** @{ */ template inline bool 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 cfd21e6e30..5821e1dacc 100644 --- a/include/xrpl/basics/scope.h +++ b/include/xrpl/basics/scope.h @@ -46,11 +46,9 @@ public: operator=(ScopeExit&&) = delete; template - explicit ScopeExit( - EFP&& f, - std::enable_if_t< - !std::is_same_v, ScopeExit> && - std::is_constructible_v>* = 0) noexcept + 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))>); @@ -93,11 +91,9 @@ public: operator=(ScopeFail&&) = delete; template - explicit ScopeFail( - EFP&& f, - std::enable_if_t< - !std::is_same_v, ScopeFail> && - std::is_constructible_v>* = 0) noexcept + 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))>); @@ -140,12 +136,11 @@ 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) + std::is_constructible_v) : exitFunction_{std::forward(f)} { } @@ -161,41 +156,41 @@ template ScopeSuccess(EF) -> ScopeSuccess; /** - Automatically unlocks and re-locks a unique_lock object. - - This is the reverse of a std::unique_lock object - instead of locking the - mutex for the lifetime of this object, it unlocks it. - - Make sure you don't try to unlock mutexes that aren't actually locked! - - This is essentially a less-versatile boost::reverse_lock. - - e.g. @code - - std::mutex mut; - - for (;;) - { - std::unique_lock myScopedLock{mut}; - // mut is now locked - - ... do some stuff with it locked .. - - while (xyz) - { - ... do some stuff with it locked .. - - scope_unlock unlocker{myScopedLock}; - - // mut is now unlocked for the remainder of this block, - // and re-locked at the end. - - ...do some stuff with it unlocked ... - } // mut gets locked here. - - } // mut gets unlocked here - @endcode -*/ + * Automatically unlocks and re-locks a unique_lock object. + * + * This is the reverse of a std::unique_lock object - instead of locking the + * mutex for the lifetime of this object, it unlocks it. + * + * Make sure you don't try to unlock mutexes that aren't actually locked! + * + * This is essentially a less-versatile boost::reverse_lock. + * + * e.g. @code + * + * std::mutex mut; + * + * for (;;) + * { + * std::unique_lock myScopedLock{mut}; + * // mut is now locked + * + * ... do some stuff with it locked .. + * + * while (xyz) + * { + * ... do some stuff with it locked .. + * + * scope_unlock unlocker{myScopedLock}; + * + * // mut is now unlocked for the remainder of this block, + * // and re-locked at the end. + * + * ...do some stuff with it unlocked ... + * } // mut gets locked here. + * + * } // mut gets unlocked here + * @endcode + */ template class ScopeUnlock diff --git a/include/xrpl/basics/spinlock.h b/include/xrpl/basics/spinlock.h index 2cc00efdef..87611f20ba 100644 --- a/include/xrpl/basics/spinlock.h +++ b/include/xrpl/basics/spinlock.h @@ -15,15 +15,16 @@ namespace xrpl { namespace detail { -/** Inform the processor that we are in a tight spin-wait loop. - - Spinlocks caught in tight loops can result in the processor's pipeline - filling up with comparison operations, resulting in a misprediction at - the time the lock is finally acquired, necessitating pipeline flushing - which is ridiculously expensive and results in very high latency. - - This function instructs the processor to "pause" for some architecture - specific amount of time, to prevent this. +/** + * Inform the processor that we are in a tight spin-wait loop. + * + * Spinlocks caught in tight loops can result in the processor's pipeline + * filling up with comparison operations, resulting in a misprediction at + * the time the lock is finally acquired, necessitating pipeline flushing + * which is ridiculously expensive and results in very high latency. + * + * This function instructs the processor to "pause" for some architecture + * specific amount of time, to prevent this. */ inline void spinPause() noexcept @@ -38,37 +39,39 @@ spinPause() noexcept } // namespace detail /** @{ */ -/** Classes to handle arrays of spinlocks packed into a single atomic integer: - - Packed spinlocks allow for tremendously space-efficient lock-sharding - but they come at a cost. - - First, the implementation is necessarily low-level and uses advanced - features like memory ordering and highly platform-specific tricks to - maximize performance. This imposes a significant and ongoing cost to - developers. - - Second, and perhaps most important, is that the packing of multiple - locks into a single integer which, albeit space-efficient, also has - performance implications stemming from data dependencies, increased - cache-coherency traffic between processors and heavier loads on the - processor's load/store units. - - To be sure, these locks can have advantages but they are definitely - not general purpose locks and should not be thought of or used that - way. The use cases for them are likely few and far between; without - a compelling reason to use them, backed by profiling data, it might - be best to use one of the standard locking primitives instead. Note - that in most common platforms, `std::mutex` is so heavily optimized - that it can, usually, outperform spinlocks. - - @tparam T An unsigned integral type (e.g. std::uint16_t) +/** + * Classes to handle arrays of spinlocks packed into a single atomic integer: + * + * Packed spinlocks allow for tremendously space-efficient lock-sharding + * but they come at a cost. + * + * First, the implementation is necessarily low-level and uses advanced + * features like memory ordering and highly platform-specific tricks to + * maximize performance. This imposes a significant and ongoing cost to + * developers. + * + * Second, and perhaps most important, is that the packing of multiple + * locks into a single integer which, albeit space-efficient, also has + * performance implications stemming from data dependencies, increased + * cache-coherency traffic between processors and heavier loads on the + * processor's load/store units. + * + * To be sure, these locks can have advantages but they are definitely + * not general purpose locks and should not be thought of or used that + * way. The use cases for them are likely few and far between; without + * a compelling reason to use them, backed by profiling data, it might + * be best to use one of the standard locking primitives instead. Note + * that in most common platforms, `std::mutex` is so heavily optimized + * that it can, usually, outperform spinlocks. + * + * @tparam T An unsigned integral type (e.g. std::uint16_t) */ -/** A class that grabs a single packed spinlock from an atomic integer. - - This class meets the requirements of Lockable: - https://en.cppreference.com/w/cpp/named_req/Lockable +/** + * A class that grabs a single packed spinlock from an atomic integer. + * + * This class meets the requirements of Lockable: + * https://en.cppreference.com/w/cpp/named_req/Lockable */ template class PackedSpinlock @@ -91,13 +94,14 @@ public: PackedSpinlock& operator=(PackedSpinlock const&) = delete; - /** A single spinlock packed inside the specified atomic - - @param lock The atomic integer inside which the spinlock is packed. - @param index The index of the spinlock this object acquires. - - @note For performance reasons, you should strive to have `lock` be - on a cacheline by itself. + /** + * A single spinlock packed inside the specified atomic + * + * @param lock The atomic integer inside which the spinlock is packed. + * @param index The index of the spinlock this object acquires. + * + * @note For performance reasons, you should strive to have `lock` be + * on a cacheline by itself. */ PackedSpinlock(std::atomic& lock, int index) : bits_(lock), mask_(static_cast(1) << index) { @@ -133,17 +137,18 @@ public: } }; -/** A spinlock implemented on top of an atomic integer. - - @note Using `packed_spinlock` and `spinlock` against the same underlying - atomic integer can result in `spinlock` not being able to actually - acquire the lock during periods of high contention, because of how - the two locks operate: `spinlock` will spin trying to grab all the - bits at once, whereas any given `packed_spinlock` will only try to - grab one bit at a time. Caveat emptor. - - This class meets the requirements of Lockable: - https://en.cppreference.com/w/cpp/named_req/Lockable +/** + * A spinlock implemented on top of an atomic integer. + * + * @note Using `packed_spinlock` and `spinlock` against the same underlying + * atomic integer can result in `spinlock` not being able to actually + * acquire the lock during periods of high contention, because of how + * the two locks operate: `spinlock` will spin trying to grab all the + * bits at once, whereas any given `packed_spinlock` will only try to + * grab one bit at a time. Caveat emptor. + * + * This class meets the requirements of Lockable: + * https://en.cppreference.com/w/cpp/named_req/Lockable */ template class Spinlock @@ -159,12 +164,13 @@ public: Spinlock& operator=(Spinlock const&) = delete; - /** Grabs the - - @param lock The atomic integer to spin against. - - @note For performance reasons, you should strive to have `lock` be - on a cacheline by itself. + /** + * Grabs the + * + * @param lock The atomic integer to spin against. + * + * @note For performance reasons, you should strive to have `lock` be + * on a cacheline by itself. */ Spinlock(std::atomic& lock) : lock_(lock) { 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..2edb314a16 100644 --- a/include/xrpl/basics/tagged_integer.h +++ b/include/xrpl/basics/tagged_integer.h @@ -7,21 +7,23 @@ #include #include +#include #include namespace xrpl { -/** A type-safe wrap around standard integral types - - The tag is used to implement type safety, catching mismatched types at - compile time. Multiple instantiations wrapping the same underlying integral - type are distinct types (distinguished by tag) and will not interoperate. A - tagged_integer supports all the usual assignment, arithmetic, comparison and - shifting operations defined for the underlying type - - The tag is not meant as a unit, which would require restricting the set of - allowed arithmetic operations. -*/ +/** + * A type-safe wrap around standard integral types + * + * The tag is used to implement type safety, catching mismatched types at + * compile time. Multiple instantiations wrapping the same underlying integral + * type are distinct types (distinguished by tag) and will not interoperate. A + * tagged_integer supports all the usual assignment, arithmetic, comparison and + * shifting operations defined for the underlying type + * + * The tag is not meant as a unit, which would require restricting the set of + * allowed arithmetic operations. + */ template class TaggedInteger : boost::totally_ordered< TaggedInteger, @@ -42,10 +44,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..d87bdafe45 100644 --- a/include/xrpl/beast/asio/io_latency_probe.h +++ b/include/xrpl/beast/asio/io_latency_probe.h @@ -8,18 +8,21 @@ #include #include +#include #include #include namespace beast { -/** Measures handler latency on an io_context queue. */ +/** + * Measures handler latency on an io_context queue. + */ 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_; @@ -41,7 +44,9 @@ public: cancel(lock, true); } - /** Return the io_context associated with the latency probe. */ + /** + * Return the io_context associated with the latency probe. + */ /** @{ */ boost::asio::io_context& getIoContext() @@ -56,9 +61,10 @@ public: } /** @} */ - /** Cancel all pending i/o. - Any handlers which have already been queued will still be called. - */ + /** + * Cancel all pending i/o. + * Any handlers which have already been queued will still be called. + */ /** @{ */ void cancel() @@ -75,10 +81,11 @@ public: } /** @} */ - /** Measure one sample of i/o latency. - Handler will be called with this signature: - void Handler (Duration d); - */ + /** + * Measure one sample of i/o latency. + * Handler will be called with this signature: + * void Handler (Duration d); + */ template void sampleOne(Handler&& handler) @@ -90,10 +97,11 @@ public: ios_, SampleOp(std::forward(handler), Clock::now(), false, this)); } - /** Initiate continuous i/o latency sampling. - Handler will be called with this signature: - void Handler (std::chrono::milliseconds); - */ + /** + * Initiate continuous i/o latency sampling. + * Handler will be called with this signature: + * void Handler (std::chrono::milliseconds); + */ template void sample(Handler&& handler) diff --git a/include/xrpl/beast/clock/abstract_clock.h b/include/xrpl/beast/clock/abstract_clock.h index 33d4096d0e..6e23700730 100644 --- a/include/xrpl/beast/clock/abstract_clock.h +++ b/include/xrpl/beast/clock/abstract_clock.h @@ -2,42 +2,43 @@ namespace beast { -/** Abstract interface to a clock. - - This makes now() a member function instead of a static member, so - an instance of the class can be dependency injected, facilitating - unit tests where time may be controlled. - - An abstract_clock inherits all the nested types of the Clock - template parameter. - - Example: - - @code - - struct Implementation - { - using clock_type = abstract_clock ; - clock_type& clock_; - explicit Implementation (clock_type& clock) - : clock_(clock) - { - } - }; - - @endcode - - @tparam Clock A type meeting these requirements: - http://en.cppreference.com/w/cpp/concept/Clock -*/ +/** + * Abstract interface to a clock. + * + * This makes now() a member function instead of a static member, so + * an instance of the class can be dependency injected, facilitating + * unit tests where time may be controlled. + * + * An abstract_clock inherits all the nested types of the Clock + * template parameter. + * + * Example: + * + * @code + * + * struct Implementation + * { + * using clock_type = abstract_clock ; + * clock_type& clock_; + * explicit Implementation (clock_type& clock) + * : clock_(clock) + * { + * } + * }; + * + * @endcode + * + * @tparam Clock A type meeting these requirements: + * http://en.cppreference.com/w/cpp/concept/Clock + */ 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) @@ -46,7 +47,9 @@ public: AbstractClock() = default; AbstractClock(AbstractClock const&) = default; - /** Returns the current time. */ + /** + * Returns the current time. + */ [[nodiscard]] virtual time_point now() const = 0; }; @@ -74,11 +77,12 @@ struct AbstractClockWrapper : public AbstractClock //------------------------------------------------------------------------------ -/** Returns a global instance of an abstract clock. - @tparam Facade A type meeting these requirements: - http://en.cppreference.com/w/cpp/concept/Clock - @tparam Clock The actual concrete clock to use. -*/ +/** + * Returns a global instance of an abstract clock. + * @tparam Facade A type meeting these requirements: + * http://en.cppreference.com/w/cpp/concept/Clock + * @tparam Clock The actual concrete clock to use. + */ template AbstractClock& getAbstractClock() diff --git a/include/xrpl/beast/clock/basic_seconds_clock.h b/include/xrpl/beast/clock/basic_seconds_clock.h index 8460dbe26b..dce521d0b8 100644 --- a/include/xrpl/beast/clock/basic_seconds_clock.h +++ b/include/xrpl/beast/clock/basic_seconds_clock.h @@ -4,15 +4,16 @@ namespace beast { -/** A clock whose minimum resolution is one second. - - The purpose of this class is to optimize the performance of the now() - member function call. It uses a dedicated thread that wakes up at least - once per second to sample the requested trivial clock. - - @tparam Clock A type meeting these requirements: - http://en.cppreference.com/w/cpp/concept/Clock -*/ +/** + * A clock whose minimum resolution is one second. + * + * The purpose of this class is to optimize the performance of the now() + * member function call. It uses a dedicated thread that wakes up at least + * once per second to sample the requested trivial clock. + * + * @tparam Clock A type meeting these requirements: + * http://en.cppreference.com/w/cpp/concept/Clock + */ class BasicSecondsClock { public: @@ -20,10 +21,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/clock/manual_clock.h b/include/xrpl/beast/clock/manual_clock.h index 8b3e4e63c6..4dc9553644 100644 --- a/include/xrpl/beast/clock/manual_clock.h +++ b/include/xrpl/beast/clock/manual_clock.h @@ -7,15 +7,16 @@ namespace beast { -/** Manual clock implementation. - - This concrete class implements the @ref abstract_clock interface and - allows the time to be advanced manually, mainly for the purpose of - providing a clock in unit tests. - - @tparam Clock A type meeting these requirements: - http://en.cppreference.com/w/cpp/concept/Clock -*/ +/** + * Manual clock implementation. + * + * This concrete class implements the @ref abstract_clock interface and + * allows the time to be advanced manually, mainly for the purpose of + * providing a clock in unit tests. + * + * @tparam Clock A type meeting these requirements: + * http://en.cppreference.com/w/cpp/concept/Clock + */ template class ManualClock : public AbstractClock { @@ -38,7 +39,9 @@ public: return now_; } - /** Set the current time of the manual clock. */ + /** + * Set the current time of the manual clock. + */ void set(time_point const& when) { @@ -48,7 +51,9 @@ public: now_ = when; } - /** Convenience for setting the time in seconds from epoch. */ + /** + * Convenience for setting the time in seconds from epoch. + */ template void set(Integer secondsFromEpoch) @@ -56,7 +61,9 @@ public: set(time_point(duration(std::chrono::seconds(secondsFromEpoch)))); } - /** Advance the clock by a duration. */ + /** + * Advance the clock by a duration. + */ template void advance(std::chrono::duration const& elapsed) @@ -67,7 +74,9 @@ public: now_ += elapsed; } - /** Convenience for advancing the clock by one second. */ + /** + * Convenience for advancing the clock by one second. + */ ManualClock& operator++() { diff --git a/include/xrpl/beast/container/aged_container_utility.h b/include/xrpl/beast/container/aged_container_utility.h index 879672e9cf..da3e4e0500 100644 --- a/include/xrpl/beast/container/aged_container_utility.h +++ b/include/xrpl/beast/container/aged_container_utility.h @@ -3,14 +3,17 @@ #include #include -#include +#include namespace beast { -/** Expire aged container items past the specified age. */ +/** + * 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..5b60ef7e6d 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 @@ -34,22 +39,23 @@ struct IsBoostReverseIterator> : std::tru explicit IsBoostReverseIterator() = default; }; -/** Associative container where each element is also indexed by time. - - This container mirrors the interface of the standard library ordered - associative containers, with the addition that each element is associated - with a `when` `time_point` which is obtained from the value of the clock's - `now`. The function `touch` updates the time for an element to the current - time as reported by the clock. - - An extra set of iterator types and member functions are provided in the - `chronological` memberspace that allow traversal in temporal or reverse - temporal order. This container is useful as a building block for caches - whose items expire after a certain amount of time. The chronological - iterators allow for fully customizable expiration strategies. - - @see aged_set, aged_multiset, aged_map, aged_multimap -*/ +/** + * Associative container where each element is also indexed by time. + * + * This container mirrors the interface of the standard library ordered + * associative containers, with the addition that each element is associated + * with a `when` `time_point` which is obtained from the value of the clock's + * `now`. The function `touch` updates the time for an element to the current + * time as reported by the clock. + * + * An extra set of iterator types and member functions are provided in the + * `chronological` memberspace that allow traversal in temporal or reverse + * temporal order. This container is useful as a building block for caches + * whose items expire after a certain amount of time. The chronological + * iterators allow for fully customizable expiration strategies. + * + * @see aged_set, aged_multiset, aged_map, aged_multimap + */ template < bool IsMulti, bool IsMap, @@ -62,8 +68,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 +100,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 +112,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 +197,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 +211,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 +360,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 +378,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 +609,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 +761,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 +803,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 +818,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 +851,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 +900,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 +1135,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 +1245,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 +1254,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 +1271,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 +1362,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 +1374,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 +1386,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 +1405,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 +1442,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 +1462,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 +1477,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 +1497,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 +1515,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 +1537,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 +1558,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 +1581,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 +1597,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 +1617,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 +1728,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 +1736,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 +1751,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 +1763,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); @@ -1797,7 +1796,9 @@ swap( lhs.swap(rhs); } -/** Expire aged container items past the specified age. */ +/** + * Expire aged container items past the specified age. + */ template < bool IsMulti, bool IsMap, diff --git a/include/xrpl/beast/container/detail/aged_unordered_container.h b/include/xrpl/beast/container/detail/aged_unordered_container.h index 7162c237d6..db10e8cc23 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 /* @@ -37,23 +43,24 @@ TODO namespace beast { namespace detail { -/** Associative container where each element is also indexed by time. - - This container mirrors the interface of the standard library unordered - associative containers, with the addition that each element is associated - with a `when` `time_point` which is obtained from the value of the clock's - `now`. The function `touch` updates the time for an element to the current - time as reported by the clock. - - An extra set of iterator types and member functions are provided in the - `chronological` memberspace that allow traversal in temporal or reverse - temporal order. This container is useful as a building block for caches - whose items expire after a certain amount of time. The chronological - iterators allow for fully customizable expiration strategies. - - @see aged_unordered_set, aged_unordered_multiset - @see aged_unordered_map, aged_unordered_multimap -*/ +/** + * Associative container where each element is also indexed by time. + * + * This container mirrors the interface of the standard library unordered + * associative containers, with the addition that each element is associated + * with a `when` `time_point` which is obtained from the value of the clock's + * `now`. The function `touch` updates the time for an element to the current + * time as reported by the clock. + * + * An extra set of iterator types and member functions are provided in the + * `chronological` memberspace that allow traversal in temporal or reverse + * temporal order. This container is useful as a building block for caches + * whose items expire after a certain amount of time. The chronological + * iterators allow for fully customizable expiration strategies. + * + * @see aged_unordered_set, aged_unordered_multiset + * @see aged_unordered_map, aged_unordered_multimap + */ template < bool IsMulti, bool IsMap, @@ -67,8 +74,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 +106,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 +118,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 +207,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 +225,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 +529,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 +547,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 +842,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 +958,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 +992,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 +1003,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 +1014,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 +1025,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 +1057,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 +1307,7 @@ public: class OtherHash, class OtherAllocator, bool MaybeMulti = IsMulti> - std::enable_if_t + bool operator==(AgedUnorderedContainer< false, OtherIsMap, @@ -1313,7 +1316,8 @@ public: OtherDuration, OtherHash, KeyEqual, - OtherAllocator> const& other) const; + OtherAllocator> const& other) const + requires(!MaybeMulti); template < bool OtherIsMap, @@ -1323,7 +1327,7 @@ public: class OtherHash, class OtherAllocator, bool MaybeMulti = IsMulti> - std::enable_if_t + bool operator==(AgedUnorderedContainer< true, OtherIsMap, @@ -1332,7 +1336,8 @@ public: OtherDuration, OtherHash, KeyEqual, - OtherAllocator> const& other) const; + OtherAllocator> const& other) const + requires MaybeMulti; template < bool OtherIsMulti, @@ -1377,13 +1382,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 +1420,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 +1430,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 +1442,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 +2098,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 +2119,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 +2141,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 +2171,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 +2228,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 +2258,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 +2281,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 +2311,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 +2321,6 @@ AgedUnorderedContainer return iterator(iter); } -#if 1 // Use insert() instead of insert_check() insert_commit() // set, map template < bool IsMulti, @@ -2319,7 +2334,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 +2350,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 +2364,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 +2388,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 +2540,7 @@ template < class OtherHash, class OtherAllocator, bool MaybeMulti> -std::enable_if_t +bool AgedUnorderedContainer::operator==( AgedUnorderedContainer< false, @@ -2569,6 +2551,7 @@ AgedUnorderedContainer OtherHash, KeyEqual, OtherAllocator> const& other) const + requires(!MaybeMulti) { if (size() != other.size()) return false; @@ -2598,7 +2581,7 @@ template < class OtherHash, class OtherAllocator, bool MaybeMulti> -std::enable_if_t +bool AgedUnorderedContainer::operator==( AgedUnorderedContainer< true, @@ -2609,6 +2592,7 @@ AgedUnorderedContainer OtherHash, KeyEqual, OtherAllocator> const& other) const + requires MaybeMulti { if (size() != other.size()) return false; @@ -2645,7 +2629,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 +2658,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); @@ -2724,7 +2710,9 @@ swap( lhs.swap(rhs); } -/** Expire aged container items past the specified age. */ +/** + * Expire aged container items past the specified age. + */ template < bool IsMulti, bool IsMap, diff --git a/include/xrpl/beast/core/CurrentThreadName.h b/include/xrpl/beast/core/CurrentThreadName.h index 6175d99b16..d1f14a6f80 100644 --- a/include/xrpl/beast/core/CurrentThreadName.h +++ b/include/xrpl/beast/core/CurrentThreadName.h @@ -6,14 +6,16 @@ #include +#include #include #include namespace beast { -/** Changes the name of the caller thread. - Different OSes may place different length or content limits on this name. -*/ +/** + * Changes the name of the caller thread. + * Different OSes may place different length or content limits on this name. + */ void setCurrentThreadName(std::string_view newThreadName); @@ -23,13 +25,14 @@ setCurrentThreadName(std::string_view newThreadName); // Maximum number of characters is therefore 15. constexpr std::size_t kMaxThreadNameLength = 15; -/** Sets the name of the caller thread with compile-time size checking. - @tparam N The size of the string literal including null terminator - @param newThreadName A string literal to set as the thread name - - This template overload enforces that thread names are at most 16 characters - (including null terminator) at compile time, matching Linux's limit. -*/ +/** + * Sets the name of the caller thread with compile-time size checking. + * @tparam N The size of the string literal including null terminator + * @param newThreadName A string literal to set as the thread name + * + * This template overload enforces that thread names are at most 16 characters + * (including null terminator) at compile time, matching Linux's limit. + */ template void setCurrentThreadName(char const (&newThreadName)[N]) @@ -40,14 +43,15 @@ setCurrentThreadName(char const (&newThreadName)[N]) } #endif -/** Returns the name of the caller thread. - - The name returned is the name as set by a call to setCurrentThreadName(). - If the thread name is set by an external force, then that name change - will not be reported. - - If no name has ever been set, then the empty string is returned. -*/ +/** + * Returns the name of the caller thread. + * + * The name returned is the name as set by a call to setCurrentThreadName(). + * If the thread name is set by an external force, then that name change + * will not be reported. + * + * If no name has ever been set, then the empty string is returned. + */ std::string getCurrentThreadName(); diff --git a/include/xrpl/beast/core/LexicalCast.h b/include/xrpl/beast/core/LexicalCast.h index 18e63c9c10..7cf21892bd 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(); @@ -159,17 +163,19 @@ struct LexicalCast //------------------------------------------------------------------------------ -/** Thrown when a conversion is not possible with LexicalCast. - Only used in the throw variants of lexicalCast. -*/ +/** + * Thrown when a conversion is not possible with LexicalCast. + * Only used in the throw variants of lexicalCast. + */ struct BadLexicalCast : public std::bad_cast { explicit BadLexicalCast() = default; }; -/** Intelligently convert from one type to another. - @return `false` if there was a parsing or range error -*/ +/** + * Intelligently convert from one type to another. + * @return `false` if there was a parsing or range error + */ template bool lexicalCastChecked(Out& out, In in) @@ -177,12 +183,13 @@ lexicalCastChecked(Out& out, In in) return detail::LexicalCast()(out, in); } -/** Convert from one type to another, throw on error - - An exception of type BadLexicalCast is thrown if the conversion fails. - - @return The new type. -*/ +/** + * Convert from one type to another, throw on error + * + * An exception of type BadLexicalCast is thrown if the conversion fails. + * + * @return The new type. + */ template Out lexicalCastThrow(In in) @@ -193,11 +200,12 @@ lexicalCastThrow(In in) throw BadLexicalCast(); } -/** Convert from one type to another. - - @param defaultValue The value returned if parsing fails - @return The new type. -*/ +/** + * Convert from one type to another. + * + * @param defaultValue The value returned if parsing fails + * @return The new type. + */ template Out lexicalCast(In in, Out defaultValue = Out()) diff --git a/include/xrpl/beast/core/List.h b/include/xrpl/beast/core/List.h index 946126a004..b9b6829d31 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 { @@ -9,7 +11,9 @@ class List; namespace detail { -/** Copy `const` attribute from T to U if present. */ +/** + * Copy `const` attribute from T to U if present. + */ /** @{ */ template struct CopyConst @@ -24,7 +28,7 @@ struct CopyConst { explicit CopyConst() = default; - using type = typename std::remove_const::type const; + using type = std::remove_const::type const; }; /** @} */ @@ -56,7 +60,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&; @@ -151,115 +155,116 @@ private: } // namespace detail -/** Intrusive doubly linked list. - - This intrusive List is a container similar in operation to std::list in the - Standard Template Library (STL). Like all @ref intrusive containers, List - requires you to first derive your class from List<>::Node: - - @code - - struct Object : List ::Node - { - explicit Object (int value) : value_ (value) - { - } - - int value_; - }; - - @endcode - - Now we define the list, and add a couple of items. - - @code - - List list; - - list.push_back (* (new Object (1))); - list.push_back (* (new Object (2))); - - @endcode - - For compatibility with the standard containers, push_back() expects a - reference to the object. Unlike the standard container, however, push_back() - places the actual object in the list and not a copy-constructed duplicate. - - Iterating over the list follows the same idiom as the STL: - - @code - - for (List ::iterator iter = list.begin(); iter != list.end; ++iter) - std::cout << iter->value_; - - @endcode - - You can even use BOOST_FOREACH, or range based for loops: - - @code - - BOOST_FOREACH (Object& object, list) // boost only - std::cout << object.value_; - - for (Object& object : list) // C++11 only - std::cout << object.value_; - - @endcode - - Because List is mostly STL compliant, it can be passed into STL algorithms: - e.g. `std::for_each()` or `std::find_first_of()`. - - In general, objects placed into a List should be dynamically allocated - although this cannot be enforced at compile time. Since the caller provides - the storage for the object, the caller is also responsible for deleting the - object. An object still exists after being removed from a List, until the - caller deletes it. This means an element can be moved from one List to - another with practically no overhead. - - Unlike the standard containers, an object may only exist in one list at a - time, unless special preparations are made. The Tag template parameter is - used to distinguish between different list types for the same object, - allowing the object to exist in more than one list simultaneously. - - For example, consider an actor system where a global list of actors is - maintained, so that they can each be periodically receive processing - time. We wish to also maintain a list of the subset of actors that require - a domain-dependent update. To achieve this, we declare two tags, the - associated list types, and the list element thusly: - - @code - - struct Actor; // Forward declaration required - - struct ProcessTag { }; - struct UpdateTag { }; - - using ProcessList = List ; - using UpdateList = List ; - - // Derive from both node types so we can be in each list at once. - // - struct Actor : ProcessList::Node, UpdateList::Node - { - bool process (); // returns true if we need an update - void update (); - }; - - @endcode - - @tparam T The base type of element which the list will store - pointers to. - - @tparam Tag An optional unique type name used to distinguish lists and - nodes, when the object can exist in multiple lists simultaneously. - - @ingroup beast_core intrusive -*/ +/** + * Intrusive doubly linked list. + * + * This intrusive List is a container similar in operation to std::list in the + * Standard Template Library (STL). Like all @ref intrusive containers, List + * requires you to first derive your class from List<>::Node: + * + * @code + * + * struct Object : List ::Node + * { + * explicit Object (int value) : value_ (value) + * { + * } + * + * int value_; + * }; + * + * @endcode + * + * Now we define the list, and add a couple of items. + * + * @code + * + * List list; + * + * list.push_back (* (new Object (1))); + * list.push_back (* (new Object (2))); + * + * @endcode + * + * For compatibility with the standard containers, push_back() expects a + * reference to the object. Unlike the standard container, however, push_back() + * places the actual object in the list and not a copy-constructed duplicate. + * + * Iterating over the list follows the same idiom as the STL: + * + * @code + * + * for (List ::iterator iter = list.begin(); iter != list.end; ++iter) + * std::cout << iter->value_; + * + * @endcode + * + * You can even use BOOST_FOREACH, or range based for loops: + * + * @code + * + * BOOST_FOREACH (Object& object, list) // boost only + * std::cout << object.value_; + * + * for (Object& object : list) // C++11 only + * std::cout << object.value_; + * + * @endcode + * + * Because List is mostly STL compliant, it can be passed into STL algorithms: + * e.g. `std::for_each()` or `std::find_first_of()`. + * + * In general, objects placed into a List should be dynamically allocated + * although this cannot be enforced at compile time. Since the caller provides + * the storage for the object, the caller is also responsible for deleting the + * object. An object still exists after being removed from a List, until the + * caller deletes it. This means an element can be moved from one List to + * another with practically no overhead. + * + * Unlike the standard containers, an object may only exist in one list at a + * time, unless special preparations are made. The Tag template parameter is + * used to distinguish between different list types for the same object, + * allowing the object to exist in more than one list simultaneously. + * + * For example, consider an actor system where a global list of actors is + * maintained, so that they can each be periodically receive processing + * time. We wish to also maintain a list of the subset of actors that require + * a domain-dependent update. To achieve this, we declare two tags, the + * associated list types, and the list element thusly: + * + * @code + * + * struct Actor; // Forward declaration required + * + * struct ProcessTag { }; + * struct UpdateTag { }; + * + * using ProcessList = List ; + * using UpdateList = List ; + * + * // Derive from both node types so we can be in each list at once. + * // + * struct Actor : ProcessList::Node, UpdateList::Node + * { + * bool process (); // returns true if we need an update + * void update (); + * }; + * + * @endcode + * + * @tparam T The base type of element which the list will store + * pointers to. + * + * @tparam Tag An optional unique type name used to distinguish lists and + * nodes, when the object can exist in multiple lists simultaneously. + * + * @ingroup beast_core intrusive + */ template class List { public: - using Node = typename detail::ListNode; + using Node = detail::ListNode; using value_type = T; using pointer = value_type*; @@ -272,7 +277,9 @@ public: using iterator = detail::ListIterator; using const_iterator = detail::ListIterator; - /** Create an empty list. */ + /** + * Create an empty list. + */ List() { head_.prev_ = nullptr; // identifies the head @@ -284,119 +291,133 @@ public: List& operator=(List const&) = delete; - /** Determine if the list is empty. - @return `true` if the list is empty. - */ + /** + * Determine if the list is empty. + * @return `true` if the list is empty. + */ [[nodiscard]] bool empty() const noexcept { return size() == 0; } - /** Returns the number of elements in the list. */ + /** + * Returns the number of elements in the list. + */ [[nodiscard]] size_type size() const noexcept { return size_; } - /** Obtain a reference to the first element. - @invariant The list may not be empty. - @return A reference to the first element. - */ + /** + * Obtain a reference to the first element. + * @invariant The list may not be empty. + * @return A reference to the first element. + */ reference front() noexcept { return element_from(head_.next_); } - /** Obtain a const reference to the first element. - @invariant The list may not be empty. - @return A const reference to the first element. - */ + /** + * Obtain a const reference to the first element. + * @invariant The list may not be empty. + * @return A const reference to the first element. + */ [[nodiscard]] const_reference front() const noexcept { return element_from(head_.next_); } - /** Obtain a reference to the last element. - @invariant The list may not be empty. - @return A reference to the last element. - */ + /** + * Obtain a reference to the last element. + * @invariant The list may not be empty. + * @return A reference to the last element. + */ reference back() noexcept { return element_from(tail_.prev_); } - /** Obtain a const reference to the last element. - @invariant The list may not be empty. - @return A const reference to the last element. - */ + /** + * Obtain a const reference to the last element. + * @invariant The list may not be empty. + * @return A const reference to the last element. + */ [[nodiscard]] const_reference back() const noexcept { return element_from(tail_.prev_); } - /** Obtain an iterator to the beginning of the list. - @return An iterator pointing to the beginning of the list. - */ + /** + * Obtain an iterator to the beginning of the list. + * @return An iterator pointing to the beginning of the list. + */ iterator begin() noexcept { return iterator(head_.next_); } - /** Obtain a const iterator to the beginning of the list. - @return A const iterator pointing to the beginning of the list. - */ + /** + * Obtain a const iterator to the beginning of the list. + * @return A const iterator pointing to the beginning of the list. + */ [[nodiscard]] const_iterator begin() const noexcept { return const_iterator(head_.next_); } - /** Obtain a const iterator to the beginning of the list. - @return A const iterator pointing to the beginning of the list. - */ + /** + * Obtain a const iterator to the beginning of the list. + * @return A const iterator pointing to the beginning of the list. + */ [[nodiscard]] const_iterator cbegin() const noexcept { return const_iterator(head_.next_); } - /** Obtain a iterator to the end of the list. - @return An iterator pointing to the end of the list. - */ + /** + * Obtain a iterator to the end of the list. + * @return An iterator pointing to the end of the list. + */ iterator end() noexcept { return iterator(&tail_); } - /** Obtain a const iterator to the end of the list. - @return A constiterator pointing to the end of the list. - */ + /** + * Obtain a const iterator to the end of the list. + * @return A constiterator pointing to the end of the list. + */ [[nodiscard]] const_iterator end() const noexcept { return const_iterator(&tail_); } - /** Obtain a const iterator to the end of the list - @return A constiterator pointing to the end of the list. - */ + /** + * Obtain a const iterator to the end of the list + * @return A constiterator pointing to the end of the list. + */ [[nodiscard]] const_iterator cend() const noexcept { return const_iterator(&tail_); } - /** Clear the list. - @note This does not free the elements. - */ + /** + * Clear the list. + * @note This does not free the elements. + */ void clear() noexcept { @@ -405,12 +426,13 @@ public: size_ = 0; } - /** Insert an element. - @invariant The element must not already be in the list. - @param pos The location to insert after. - @param element The element to insert. - @return An iterator pointing to the newly inserted element. - */ + /** + * Insert an element. + * @invariant The element must not already be in the list. + * @param pos The location to insert after. + * @param element The element to insert. + * @return An iterator pointing to the newly inserted element. + */ iterator insert(iterator pos, T& element) noexcept { @@ -423,11 +445,12 @@ public: return iterator(node); } - /** Insert another list into this one. - The other list is cleared. - @param pos The location to insert after. - @param other The list to insert. - */ + /** + * Insert another list into this one. + * The other list is cleared. + * @param pos The location to insert after. + * @param other The list to insert. + */ void insert(iterator pos, List& other) noexcept { @@ -443,11 +466,12 @@ public: } } - /** Remove an element. - @invariant The element must exist in the list. - @param pos An iterator pointing to the element to remove. - @return An iterator pointing to the next element after the one removed. - */ + /** + * Remove an element. + * @invariant The element must exist in the list. + * @param pos An iterator pointing to the element to remove. + * @return An iterator pointing to the next element after the one removed. + */ iterator erase(iterator pos) noexcept { @@ -459,20 +483,22 @@ public: return pos; } - /** Insert an element at the beginning of the list. - @invariant The element must not exist in the list. - @param element The element to insert. - */ + /** + * Insert an element at the beginning of the list. + * @invariant The element must not exist in the list. + * @param element The element to insert. + */ iterator pushFront(T& element) noexcept { return insert(begin(), element); } - /** Remove the element at the beginning of the list. - @invariant The list must not be empty. - @return A reference to the popped element. - */ + /** + * Remove the element at the beginning of the list. + * @invariant The list must not be empty. + * @return A reference to the popped element. + */ T& popFront() noexcept { @@ -481,20 +507,22 @@ public: return element; } - /** Append an element at the end of the list. - @invariant The element must not exist in the list. - @param element The element to append. - */ + /** + * Append an element at the end of the list. + * @invariant The element must not exist in the list. + * @param element The element to append. + */ iterator pushBack(T& element) noexcept { return insert(end(), element); } - /** Remove the element at the end of the list. - @invariant The list must not be empty. - @return A reference to the popped element. - */ + /** + * Remove the element at the end of the list. + * @invariant The list must not be empty. + * @return A reference to the popped element. + */ T& popBack() noexcept { @@ -503,7 +531,9 @@ public: return element; } - /** Swap contents with another list. */ + /** + * Swap contents with another list. + */ void swap(List& other) noexcept { @@ -513,42 +543,46 @@ public: append(temp); } - /** Insert another list at the beginning of this list. - The other list is cleared. - @param list The other list to insert. - */ + /** + * Insert another list at the beginning of this list. + * The other list is cleared. + * @param list The other list to insert. + */ iterator prepend(List& list) noexcept { return insert(begin(), list); } - /** Append another list at the end of this list. - The other list is cleared. - @param list the other list to append. - */ + /** + * Append another list at the end of this list. + * The other list is cleared. + * @param list the other list to append. + */ iterator append(List& list) noexcept { return insert(end(), list); } - /** Obtain an iterator from an element. - @invariant The element must exist in the list. - @param element The element to obtain an iterator for. - @return An iterator to the element. - */ + /** + * Obtain an iterator from an element. + * @invariant The element must exist in the list. + * @param element The element to obtain an iterator for. + * @return An iterator to the element. + */ iterator iteratorTo(T& element) const noexcept { return iterator(static_cast(&element)); } - /** Obtain a const iterator from an element. - @invariant The element must exist in the list. - @param element The element to obtain an iterator for. - @return A const iterator to the element. - */ + /** + * Obtain a const iterator from an element. + * @invariant The element must exist in the list. + * @param element The element to obtain an iterator for. + * @return A const iterator to the element. + */ [[nodiscard]] const_iterator constIteratorTo(T const& element) const noexcept { diff --git a/include/xrpl/beast/core/LockFreeStack.h b/include/xrpl/beast/core/LockFreeStack.h index 6b8f686246..849edc8fce 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_; @@ -102,18 +103,19 @@ operator!=( //------------------------------------------------------------------------------ -/** Multiple Producer, Multiple Consumer (MPMC) intrusive stack. - - This stack is implemented using the same intrusive interface as List. - All mutations are lock-free. - - The caller is responsible for preventing the "ABA" problem: - http://en.wikipedia.org/wiki/ABA_problem - - @param Tag A type name used to distinguish lists and nodes, for - putting objects in multiple lists. If this parameter is - omitted, the default tag is used. -*/ +/** + * Multiple Producer, Multiple Consumer (MPMC) intrusive stack. + * + * This stack is implemented using the same intrusive interface as List. + * All mutations are lock-free. + * + * The caller is responsible for preventing the "ABA" problem: + * http://en.wikipedia.org/wiki/ABA_problem + * + * @param Tag A type name used to distinguish lists and nodes, for + * putting objects in multiple lists. If this parameter is + * omitted, the default tag is used. + */ template class LockFreeStack { @@ -161,24 +163,27 @@ public: LockFreeStack& operator=(LockFreeStack const&) = delete; - /** Returns true if the stack is empty. */ + /** + * Returns true if the stack is empty. + */ [[nodiscard]] bool empty() const { return head_.load() == &end_; } - /** Push a node onto the stack. - The caller is responsible for preventing the ABA problem. - This operation is lock-free. - Thread safety: - Safe to call from any thread. - - @param node The node to push. - - @return `true` if the stack was previously empty. If multiple threads - are attempting to push, only one will receive `true`. - */ + /** + * Push a node onto the stack. + * The caller is responsible for preventing the ABA problem. + * This operation is lock-free. + * Thread safety: + * Safe to call from any thread. + * + * @param node The node to push. + * + * @return `true` if the stack was previously empty. If multiple threads + * are attempting to push, only one will receive `true`. + */ // VFALCO NOTE Fix this, shouldn't it be a reference like intrusive list? bool pushFront(Node* node) @@ -194,15 +199,16 @@ public: return first; } - /** Pop an element off the stack. - The caller is responsible for preventing the ABA problem. - This operation is lock-free. - Thread safety: - Safe to call from any thread. - - @return The element that was popped, or `nullptr` if the stack - was empty. - */ + /** + * Pop an element off the stack. + * The caller is responsible for preventing the ABA problem. + * This operation is lock-free. + * Thread safety: + * Safe to call from any thread. + * + * @return The element that was popped, or `nullptr` if the stack + * was empty. + */ Element* popFront() { @@ -218,12 +224,13 @@ public: return static_cast(node); } - /** Return a forward iterator to the beginning or end of the stack. - Undefined behavior results if push_front or pop_front is called - while an iteration is in progress. - Thread safety: - Caller is responsible for synchronization. - */ + /** + * Return a forward iterator to the beginning or end of the stack. + * Undefined behavior results if push_front or pop_front is called + * while an iteration is in progress. + * Thread safety: + * Caller is responsible for synchronization. + */ /** @{ */ iterator begin() diff --git a/include/xrpl/beast/core/SemanticVersion.h b/include/xrpl/beast/core/SemanticVersion.h index 826a43d3f8..338942c252 100644 --- a/include/xrpl/beast/core/SemanticVersion.h +++ b/include/xrpl/beast/core/SemanticVersion.h @@ -6,13 +6,14 @@ namespace beast { -/** A Semantic Version number. - - Identifies the build of a particular version of software using - the Semantic Versioning Specification described here: - - http://semver.org/ -*/ +/** + * A Semantic Version number. + * + * Identifies the build of a particular version of software using + * the Semantic Versioning Specification described here: + * + * http://semver.org/ + */ class SemanticVersion { public: @@ -29,14 +30,17 @@ public: SemanticVersion(std::string_view version); - /** Parse a semantic version string. - The parsing is as strict as possible. - @return `true` if the string was parsed. - */ + /** + * Parse a semantic version string. + * The parsing is as strict as possible. + * @return `true` if the string was parsed. + */ bool parse(std::string_view input); - /** Produce a string from semantic version components. */ + /** + * Produce a string from semantic version components. + */ [[nodiscard]] std::string print() const; @@ -52,9 +56,10 @@ public: } }; -/** Compare two SemanticVersions against each other. - The comparison follows the rules as per the specification. -*/ +/** + * Compare two SemanticVersions against each other. + * The comparison follows the rules as per the specification. + */ int compare(SemanticVersion const& lhs, SemanticVersion const& rhs); diff --git a/include/xrpl/beast/hash/hash_append.h b/include/xrpl/beast/hash/hash_append.h index 83cff4bdea..c5374f95e5 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]); @@ -135,19 +135,20 @@ struct IsUniquelyRepresented> explicit IsUniquelyRepresented() = default; }; -/** Metafunction returning `true` if the type can be hashed in one call. - - For `IsContiguouslyHashable::value` to be true, then for every - combination of possible values of `T` held in `x` and `y`, - if `x == y`, then it must be true that `memcmp(&x, &y, sizeof(T))` - return 0; i.e. that `x` and `y` are represented by the same bit pattern. - - For example: A two's complement `int` should be contiguously hashable. - Every bit pattern produces a unique value that does not compare equal to - any other bit pattern's value. A IEEE floating point should not be - contiguously hashable because -0. and 0. have different bit patterns, - though they compare equal. -*/ +/** + * Metafunction returning `true` if the type can be hashed in one call. + * + * For `IsContiguouslyHashable::value` to be true, then for every + * combination of possible values of `T` held in `x` and `y`, + * if `x == y`, then it must be true that `memcmp(&x, &y, sizeof(T))` + * return 0; i.e. that `x` and `y` are represented by the same bit pattern. + * + * For example: A two's complement `int` should be contiguously hashable. + * Every bit pattern produces a unique value that does not compare equal to + * any other bit pattern's value. A IEEE floating point should not be + * contiguously hashable because -0. and 0. have different bit patterns, + * though they compare equal. + */ /** @{ */ template struct IsContiguouslyHashable @@ -172,54 +173,58 @@ struct IsContiguouslyHashable //------------------------------------------------------------------------------ -/** Logically concatenate input data to a `Hasher`. - - Hasher requirements: - - `X` is the type `Hasher` - `h` is a value of type `x` - `p` is a value convertible to `void const*` - `n` is a value of type `std::size_t`, greater than zero - - Expression: - `h.append (p, n);` - Throws: - Never - Effect: - Adds the input data to the hasher state. - - Expression: - `static_cast(j)` - Throws: - Never - Effect: - Returns the resulting hash of all the input data. -*/ +/** + * Logically concatenate input data to a `Hasher`. + * + * Hasher requirements: + * + * `X` is the type `Hasher` + * `h` is a value of type `x` + * `p` is a value convertible to `void const*` + * `n` is a value of type `std::size_t`, greater than zero + * + * Expression: + * `h.append (p, n);` + * Throws: + * Never + * Effect: + * Adds the input data to the hasher state. + * + * Expression: + * `static_cast(j)` + * Throws: + * Never + * Effect: + * Returns the resulting hash of all the input data. + */ /** @{ */ // 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 +244,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 +292,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 +306,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 +317,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 +327,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 +338,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 +348,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 +358,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 +369,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 978bbc6917..73dbb8e8ab 100644 --- a/include/xrpl/beast/hash/xxhasher.h +++ b/include/xrpl/beast/hash/xxhasher.h @@ -124,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..9da2a8bb74 100644 --- a/include/xrpl/beast/insight/Collector.h +++ b/include/xrpl/beast/insight/Collector.h @@ -4,22 +4,25 @@ #include #include #include +#include #include +#include #include namespace beast::insight { -/** Interface for a manager that allows collection of metrics. - - To export metrics from a class, pass and save a shared_ptr to this - interface in the class constructor. Create the metric objects - as desired (counters, events, gauges, meters, and an optional hook) - using the interface. - - @see Counter, Event, Gauge, Hook, Meter - @see NullCollector, StatsDCollector -*/ +/** + * Interface for a manager that allows collection of metrics. + * + * To export metrics from a class, pass and save a shared_ptr to this + * interface in the class constructor. Create the metric objects + * as desired (counters, events, gauges, meters, and an optional hook) + * using the interface. + * + * @see Counter, Event, Gauge, Hook, Meter + * @see NullCollector, StatsDCollector + */ class Collector { public: @@ -27,18 +30,19 @@ public: virtual ~Collector() = 0; - /** Create a hook. - - A hook is called at each collection interval, on an implementation - defined thread. This is a convenience facility for gathering metrics - in the polling style. The typical usage is to update all the metrics - of interest in the handler. - - Handler will be called with this signature: - void handler (void) - - @see Hook - */ + /** + * Create a hook. + * + * A hook is called at each collection interval, on an implementation + * defined thread. This is a convenience facility for gathering metrics + * in the polling style. The typical usage is to update all the metrics + * of interest in the handler. + * + * Handler will be called with this signature: + * void handler (void) + * + * @see Hook + */ /** @{ */ template Hook @@ -51,9 +55,10 @@ public: makeHook(HookImpl::HandlerType const& handler) = 0; /** @} */ - /** Create a counter with the specified name. - @see Counter - */ + /** + * Create a counter with the specified name. + * @see Counter + */ /** @{ */ virtual Counter makeCounter(std::string const& name) = 0; @@ -67,9 +72,10 @@ public: } /** @} */ - /** Create an event with the specified name. - @see Event - */ + /** + * Create an event with the specified name. + * @see Event + */ /** @{ */ virtual Event makeEvent(std::string const& name) = 0; @@ -83,9 +89,10 @@ public: } /** @} */ - /** Create a gauge with the specified name. - @see Gauge - */ + /** + * Create a gauge with the specified name. + * @see Gauge + */ /** @{ */ virtual Gauge makeGauge(std::string const& name) = 0; @@ -99,9 +106,10 @@ public: } /** @} */ - /** Create a meter with the specified name. - @see Meter - */ + /** + * Create a meter with the specified name. + * @see Meter + */ /** @{ */ virtual Meter makeMeter(std::string const& name) = 0; diff --git a/include/xrpl/beast/insight/Counter.h b/include/xrpl/beast/insight/Counter.h index 482808b2c7..875fadf33a 100644 --- a/include/xrpl/beast/insight/Counter.h +++ b/include/xrpl/beast/insight/Counter.h @@ -7,34 +7,39 @@ namespace beast::insight { -/** A metric for measuring an integral value. - - A counter is a gauge calculated at the server. The owner of the counter - may increment and decrement the value by an amount. - - This is a lightweight reference wrapper which is cheap to copy and assign. - When the last reference goes away, the metric is no longer collected. -*/ +/** + * A metric for measuring an integral value. + * + * A counter is a gauge calculated at the server. The owner of the counter + * may increment and decrement the value by an amount. + * + * This is a lightweight reference wrapper which is cheap to copy and assign. + * When the last reference goes away, the metric is no longer collected. + */ class Counter final { public: using value_type = CounterImpl::value_type; - /** Create a null metric. - A null metric reports no information. - */ + /** + * Create a null metric. + * A null metric reports no information. + */ Counter() = default; - /** Create the metric reference the specified implementation. - Normally this won't be called directly. Instead, call the appropriate - factory function in the Collector interface. - @see Collector. - */ + /** + * Create the metric reference the specified implementation. + * Normally this won't be called directly. Instead, call the appropriate + * factory function in the Collector interface. + * @see Collector. + */ explicit Counter(std::shared_ptr impl) : impl_(std::move(impl)) { } - /** Increment the counter. */ + /** + * Increment the counter. + */ /** @{ */ void increment(value_type amount) const diff --git a/include/xrpl/beast/insight/Event.h b/include/xrpl/beast/insight/Event.h index afccf9baba..c3ff1a8877 100644 --- a/include/xrpl/beast/insight/Event.h +++ b/include/xrpl/beast/insight/Event.h @@ -8,35 +8,40 @@ namespace beast::insight { -/** A metric for reporting event timing. - - An event is an operation that has an associated millisecond time, or - other integral value. Because events happen at a specific moment, the - metric only supports a push-style interface. - - This is a lightweight reference wrapper which is cheap to copy and assign. - When the last reference goes away, the metric is no longer collected. -*/ +/** + * A metric for reporting event timing. + * + * An event is an operation that has an associated millisecond time, or + * other integral value. Because events happen at a specific moment, the + * metric only supports a push-style interface. + * + * This is a lightweight reference wrapper which is cheap to copy and assign. + * When the last reference goes away, the metric is no longer collected. + */ class Event final { public: using value_type = EventImpl::value_type; - /** Create a null metric. - A null metric reports no information. - */ + /** + * Create a null metric. + * A null metric reports no information. + */ Event() = default; - /** Create the metric reference the specified implementation. - Normally this won't be called directly. Instead, call the appropriate - factory function in the Collector interface. - @see Collector. - */ + /** + * Create the metric reference the specified implementation. + * Normally this won't be called directly. Instead, call the appropriate + * factory function in the Collector interface. + * @see Collector. + */ explicit Event(std::shared_ptr impl) : impl_(std::move(impl)) { } - /** Push an event notification. */ + /** + * Push an event notification. + */ template void notify(std::chrono::duration const& value) const diff --git a/include/xrpl/beast/insight/Gauge.h b/include/xrpl/beast/insight/Gauge.h index b24c4366c3..ef62e252b3 100644 --- a/include/xrpl/beast/insight/Gauge.h +++ b/include/xrpl/beast/insight/Gauge.h @@ -7,40 +7,44 @@ namespace beast::insight { -/** A metric for measuring an integral value. - - A gauge is an instantaneous measurement of a value, like the gas gauge - in a car. The caller directly sets the value, or adjusts it by a - specified amount. The value is kept in the client rather than the collector. - - This is a lightweight reference wrapper which is cheap to copy and assign. - When the last reference goes away, the metric is no longer collected. -*/ +/** + * A metric for measuring an integral value. + * + * A gauge is an instantaneous measurement of a value, like the gas gauge + * in a car. The caller directly sets the value, or adjusts it by a + * specified amount. The value is kept in the client rather than the collector. + * + * This is a lightweight reference wrapper which is cheap to copy and assign. + * When the last reference goes away, the metric is no longer collected. + */ class Gauge final { public: using value_type = GaugeImpl::value_type; using difference_type = GaugeImpl::difference_type; - /** Create a null metric. - A null metric reports no information. - */ + /** + * Create a null metric. + * A null metric reports no information. + */ Gauge() = default; - /** Create the metric reference the specified implementation. - Normally this won't be called directly. Instead, call the appropriate - factory function in the Collector interface. - @see Collector. - */ + /** + * Create the metric reference the specified implementation. + * Normally this won't be called directly. Instead, call the appropriate + * factory function in the Collector interface. + * @see Collector. + */ explicit Gauge(std::shared_ptr impl) : impl_(std::move(impl)) { } - /** Set the value on the gauge. - A Collector implementation should combine multiple calls to value - changes into a single change if the calls occur within a single - collection interval. - */ + /** + * Set the value on the gauge. + * A Collector implementation should combine multiple calls to value + * changes into a single change if the calls occur within a single + * collection interval. + */ /** @{ */ void set(value_type value) const @@ -49,6 +53,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 { @@ -57,7 +66,9 @@ public: } /** @} */ - /** Adjust the value of the gauge. */ + /** + * Adjust the value of the gauge. + */ /** @{ */ void increment(difference_type amount) const diff --git a/include/xrpl/beast/insight/Group.h b/include/xrpl/beast/insight/Group.h index 3e0eb93452..ecf7709546 100644 --- a/include/xrpl/beast/insight/Group.h +++ b/include/xrpl/beast/insight/Group.h @@ -7,13 +7,17 @@ namespace beast::insight { -/** A collector front-end that manages a group of metrics. */ +/** + * A collector front-end that manages a group of metrics. + */ class Group : public Collector { public: using ptr = std::shared_ptr; - /** Returns the name of this group, for diagnostics. */ + /** + * Returns the name of this group, for diagnostics. + */ [[nodiscard]] virtual std::string const& name() const = 0; }; diff --git a/include/xrpl/beast/insight/Groups.h b/include/xrpl/beast/insight/Groups.h index cfe4d99bdc..77fc2d3336 100644 --- a/include/xrpl/beast/insight/Groups.h +++ b/include/xrpl/beast/insight/Groups.h @@ -8,13 +8,17 @@ namespace beast::insight { -/** A container for managing a set of metric groups. */ +/** + * A container for managing a set of metric groups. + */ class Groups { public: virtual ~Groups() = 0; - /** Find or create a new collector with a given name. */ + /** + * Find or create a new collector with a given name. + */ /** @{ */ virtual Group::ptr const& get(std::string const& name) = 0; @@ -27,7 +31,9 @@ public: /** @} */ }; -/** Create a group container that uses the specified collector. */ +/** + * Create a group container that uses the specified collector. + */ std::unique_ptr makeGroups(Collector::ptr const& collector); diff --git a/include/xrpl/beast/insight/Hook.h b/include/xrpl/beast/insight/Hook.h index 8dbe5a4be0..572a9ffcb4 100644 --- a/include/xrpl/beast/insight/Hook.h +++ b/include/xrpl/beast/insight/Hook.h @@ -7,20 +7,24 @@ namespace beast::insight { -/** A reference to a handler for performing polled collection. */ +/** + * A reference to a handler for performing polled collection. + */ class Hook final { public: - /** Create a null hook. - A null hook has no associated handler. - */ + /** + * Create a null hook. + * A null hook has no associated handler. + */ Hook() = default; - /** Create a hook referencing the specified implementation. - Normally this won't be called directly. Instead, call the appropriate - factory function in the Collector interface. - @see Collector. - */ + /** + * Create a hook referencing the specified implementation. + * Normally this won't be called directly. Instead, call the appropriate + * factory function in the Collector interface. + * @see Collector. + */ 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 25ffabd928..ac2f3a352c 100644 --- a/include/xrpl/beast/insight/Meter.h +++ b/include/xrpl/beast/insight/Meter.h @@ -7,33 +7,38 @@ namespace beast::insight { -/** A metric for measuring an integral value. - - A meter may be thought of as an increment-only counter. - - This is a lightweight reference wrapper which is cheap to copy and assign. - When the last reference goes away, the metric is no longer collected. -*/ +/** + * A metric for measuring an integral value. + * + * A meter may be thought of as an increment-only counter. + * + * This is a lightweight reference wrapper which is cheap to copy and assign. + * When the last reference goes away, the metric is no longer collected. + */ class Meter final { public: using value_type = MeterImpl::value_type; - /** Create a null metric. - A null metric reports no information. - */ + /** + * Create a null metric. + * A null metric reports no information. + */ Meter() = default; - /** Create the metric reference the specified implementation. - Normally this won't be called directly. Instead, call the appropriate - factory function in the Collector interface. - @see Collector. - */ + /** + * Create the metric reference the specified implementation. + * Normally this won't be called directly. Instead, call the appropriate + * factory function in the Collector interface. + * @see Collector. + */ explicit Meter(std::shared_ptr impl) : impl_(std::move(impl)) { } - /** Increment the meter. */ + /** + * Increment the meter. + */ /** @{ */ void increment(value_type amount) const diff --git a/include/xrpl/beast/insight/NullCollector.h b/include/xrpl/beast/insight/NullCollector.h index b865526ade..ffafe6d6d5 100644 --- a/include/xrpl/beast/insight/NullCollector.h +++ b/include/xrpl/beast/insight/NullCollector.h @@ -2,9 +2,13 @@ #include +#include + namespace beast::insight { -/** A Collector which does not collect metrics. */ +/** + * A Collector which does not collect metrics. + */ class NullCollector : public Collector { public: diff --git a/include/xrpl/beast/insight/StatsDCollector.h b/include/xrpl/beast/insight/StatsDCollector.h index ad436dc626..e14d3a27ff 100644 --- a/include/xrpl/beast/insight/StatsDCollector.h +++ b/include/xrpl/beast/insight/StatsDCollector.h @@ -4,22 +4,27 @@ #include #include +#include +#include + namespace beast::insight { -/** A Collector that reports metrics to a StatsD server. - Reference: - https://github.com/b/statsd_spec -*/ +/** + * A Collector that reports metrics to a StatsD server. + * Reference: + * https://github.com/b/statsd_spec + */ class StatsDCollector : public Collector { public: explicit StatsDCollector() = default; - /** Create a StatsD collector. - @param address The IP address and port of the StatsD server. - @param prefix A string pre-pended before each metric name. - @param journal Destination for logging output. - */ + /** + * Create a StatsD collector. + * @param address The IP address and port of the StatsD server. + * @param prefix A string pre-pended before each metric name. + * @param journal Destination for logging output. + */ static std::shared_ptr make(IP::Endpoint const& address, std::string const& prefix, Journal journal); }; diff --git a/include/xrpl/beast/net/IPAddress.h b/include/xrpl/beast/net/IPAddress.h index 67deaaa787..4f4fb189a6 100644 --- a/include/xrpl/beast/net/IPAddress.h +++ b/include/xrpl/beast/net/IPAddress.h @@ -9,6 +9,7 @@ #include #include +#include #include //------------------------------------------------------------------------------ @@ -18,42 +19,54 @@ namespace IP { using Address = boost::asio::ip::address; -/** Returns the address represented as a string. */ +/** + * Returns the address represented as a string. + */ inline std::string to_string(Address const& addr) { return addr.to_string(); } -/** Returns `true` if this is a loopback address. */ +/** + * Returns `true` if this is a loopback address. + */ inline bool isLoopback(Address const& addr) { return addr.is_loopback(); } -/** Returns `true` if the address is unspecified. */ +/** + * Returns `true` if the address is unspecified. + */ inline bool isUnspecified(Address const& addr) { return addr.is_unspecified(); } -/** Returns `true` if the address is a multicast address. */ +/** + * Returns `true` if the address is a multicast address. + */ inline bool isMulticast(Address const& addr) { return addr.is_multicast(); } -/** Returns `true` if the address is a private unroutable address. */ +/** + * Returns `true` if the address is a private unroutable address. + */ inline bool isPrivate(Address const& addr) { return (addr.is_v4()) ? isPrivate(addr.to_v4()) : isPrivate(addr.to_v6()); } -/** Returns `true` if the address is a public routable address. */ +/** + * Returns `true` if the address is a public routable address. + */ inline bool isPublic(Address const& addr) { diff --git a/include/xrpl/beast/net/IPAddressConversion.h b/include/xrpl/beast/net/IPAddressConversion.h index b5fb697233..73777cf841 100644 --- a/include/xrpl/beast/net/IPAddressConversion.h +++ b/include/xrpl/beast/net/IPAddressConversion.h @@ -6,23 +6,29 @@ namespace beast::IP { -/** Convert to Endpoint. - The port is set to zero. -*/ +/** + * Convert to Endpoint. + * The port is set to zero. + */ Endpoint fromAsio(boost::asio::ip::address const& address); -/** Convert to Endpoint. */ +/** + * Convert to Endpoint. + */ Endpoint fromAsio(boost::asio::ip::tcp::endpoint const& endpoint); -/** Convert to asio::ip::address. - The port is ignored. -*/ +/** + * Convert to asio::ip::address. + * The port is ignored. + */ boost::asio::ip::address toAsioAddress(Endpoint const& endpoint); -/** Convert to asio::ip::tcp::endpoint. */ +/** + * Convert to asio::ip::tcp::endpoint. + */ boost::asio::ip::tcp::endpoint toAsioEndpoint(Endpoint const& endpoint); diff --git a/include/xrpl/beast/net/IPAddressV4.h b/include/xrpl/beast/net/IPAddressV4.h index dbe5a6095f..94943af3ea 100644 --- a/include/xrpl/beast/net/IPAddressV4.h +++ b/include/xrpl/beast/net/IPAddressV4.h @@ -1,24 +1,27 @@ #pragma once -#include - #include namespace beast::IP { using AddressV4 = boost::asio::ip::address_v4; -/** Returns `true` if the address is a private unroutable address. */ +/** + * Returns `true` if the address is a private unroutable address. + */ bool isPrivate(AddressV4 const& addr); -/** Returns `true` if the address is a public routable address. */ +/** + * Returns `true` if the address is a public routable address. + */ bool isPublic(AddressV4 const& addr); -/** Returns the address class for the given address. - @note Class 'D' represents multicast addresses (224.*.*.*). -*/ +/** + * Returns the address class for the given address. + * @note Class 'D' represents multicast addresses (224.*.*.*). + */ char getClass(AddressV4 const& address); diff --git a/include/xrpl/beast/net/IPAddressV6.h b/include/xrpl/beast/net/IPAddressV6.h index 10f806417d..b51cb62532 100644 --- a/include/xrpl/beast/net/IPAddressV6.h +++ b/include/xrpl/beast/net/IPAddressV6.h @@ -1,18 +1,20 @@ #pragma once -#include - #include namespace beast::IP { using AddressV6 = boost::asio::ip::address_v6; -/** Returns `true` if the address is a private unroutable address. */ +/** + * Returns `true` if the address is a private unroutable address. + */ bool isPrivate(AddressV6 const& addr); -/** Returns `true` if the address is a public routable address. */ +/** + * Returns `true` if the address is a public routable address. + */ bool isPublic(AddressV6 const& addr); diff --git a/include/xrpl/beast/net/IPEndpoint.h b/include/xrpl/beast/net/IPEndpoint.h index fec6e1556f..c4b269e9c3 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 @@ -12,51 +17,68 @@ namespace beast::IP { using Port = std::uint16_t; -/** A version-independent IP address and port combination. */ +/** + * A version-independent IP address and port combination. + */ class Endpoint { public: - /** Create an unspecified endpoint. */ + /** + * Create an unspecified endpoint. + */ Endpoint(); - /** Create an endpoint from the address and optional port. */ + /** + * Create an endpoint from the address and optional port. + */ explicit Endpoint(Address addr, Port port = 0); - /** Create an Endpoint from a string. - If the port is omitted, the endpoint will have a zero port. - @return An optional endpoint; will be `std::nullopt` on failure - */ + /** + * Create an Endpoint from a string. + * If the port is omitted, the endpoint will have a zero port. + * @return An optional endpoint; will be `std::nullopt` on failure + */ static std::optional fromStringChecked(std::string const& s); static Endpoint fromString(std::string const& s); - /** Returns a string representing the endpoint. */ + /** + * Returns a string representing the endpoint. + */ [[nodiscard]] std::string toString() const; - /** Returns the port number on the endpoint. */ + /** + * Returns the port number on the endpoint. + */ [[nodiscard]] Port port() const { return port_; } - /** Returns a new Endpoint with a different port. */ + /** + * Returns a new Endpoint with a different port. + */ [[nodiscard]] Endpoint atPort(Port port) const { return Endpoint(addr_, port); } - /** Returns the address portion of this endpoint. */ + /** + * Returns the address portion of this endpoint. + */ [[nodiscard]] Address const& address() const { return addr_; } - /** Convenience accessors for the address part. */ + /** + * Convenience accessors for the address part. + */ /** @{ */ [[nodiscard]] bool isV4() const @@ -80,7 +102,9 @@ public: } /** @} */ - /** Arithmetic comparison. */ + /** + * Arithmetic comparison. + */ /** @{ */ friend bool operator==(Endpoint const& lhs, Endpoint const& rhs); @@ -126,35 +150,45 @@ private: // Properties -/** Returns `true` if the endpoint is a loopback address. */ +/** + * Returns `true` if the endpoint is a loopback address. + */ inline bool isLoopback(Endpoint const& endpoint) { return isLoopback(endpoint.address()); } -/** Returns `true` if the endpoint is unspecified. */ +/** + * Returns `true` if the endpoint is unspecified. + */ inline bool isUnspecified(Endpoint const& endpoint) { return isUnspecified(endpoint.address()); } -/** Returns `true` if the endpoint is a multicast address. */ +/** + * Returns `true` if the endpoint is a multicast address. + */ inline bool isMulticast(Endpoint const& endpoint) { return isMulticast(endpoint.address()); } -/** Returns `true` if the endpoint is a private unroutable address. */ +/** + * Returns `true` if the endpoint is a private unroutable address. + */ inline bool isPrivate(Endpoint const& endpoint) { return isPrivate(endpoint.address()); } -/** Returns `true` if the endpoint is a public routable address. */ +/** + * Returns `true` if the endpoint is a public routable address. + */ inline bool isPublic(Endpoint const& endpoint) { @@ -163,14 +197,18 @@ isPublic(Endpoint const& endpoint) //------------------------------------------------------------------------------ -/** Returns the endpoint represented as a string. */ +/** + * Returns the endpoint represented as a string. + */ inline std::string to_string(Endpoint const& endpoint) { return endpoint.toString(); } -/** Output stream conversion. */ +/** + * Output stream conversion. + */ template OutputStream& operator<<(OutputStream& os, Endpoint const& endpoint) @@ -179,7 +217,9 @@ operator<<(OutputStream& os, Endpoint const& endpoint) return os; } -/** Input stream conversion. */ +/** + * Input stream conversion. + */ std::istream& operator>>(std::istream& is, Endpoint& endpoint); @@ -188,7 +228,9 @@ operator>>(std::istream& is, Endpoint& endpoint); //------------------------------------------------------------------------------ namespace std { -/** std::hash support. */ +/** + * std::hash support. + */ template <> struct hash<::beast::IP::Endpoint> { @@ -203,7 +245,9 @@ struct hash<::beast::IP::Endpoint> } // namespace std namespace boost { -/** boost::hash support. */ +/** + * boost::hash support. + */ template <> struct hash<::beast::IP::Endpoint> { diff --git a/include/xrpl/beast/rfc2616.h b/include/xrpl/beast/rfc2616.h index cf80bd26c0..1986568553 100644 --- a/include/xrpl/beast/rfc2616.h +++ b/include/xrpl/beast/rfc2616.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -29,17 +30,20 @@ struct CiEqualPred } }; -/** Returns `true` if `c` is linear white space. - - This excludes the CRLF sequence allowed for line continuations. -*/ +/** + * Returns `true` if `c` is linear white space. + * + * This excludes the CRLF sequence allowed for line continuations. + */ inline bool isLws(char c) { return c == ' ' || c == '\t'; } -/** Returns `true` if `c` is any whitespace character. */ +/** + * Returns `true` if `c` is any whitespace character. + */ inline bool isWhite(char c) { @@ -86,14 +90,15 @@ trimRight(String const& s) } // namespace detail -/** Parse a character sequence of values separated by commas. - Double quotes and escape sequences will be converted. Excess white - space, commas, double quotes, and empty elements are not copied. - Format: - #(token|quoted-string) - Reference: - http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2 -*/ +/** + * Parse a character sequence of values separated by commas. + * Double quotes and escape sequences will be converted. Excess white + * space, commas, double quotes, and empty elements are not copied. + * Format: + * #(token|quoted-string) + * Reference: + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2 + */ template < class FwdIt, class Result = std::vector::value_type>>, @@ -102,7 +107,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; @@ -188,14 +193,15 @@ splitCommas(boost::beast::string_view const& s) //------------------------------------------------------------------------------ -/** Iterates through a comma separated list. - - Meets the requirements of ForwardIterator. - - List defined in rfc2616 2.1. - - @note Values returned may contain backslash escapes. -*/ +/** + * Iterates through a comma separated list. + * + * Meets the requirements of ForwardIterator. + * + * List defined in rfc2616 2.1. + * + * @note Values returned may contain backslash escapes. + */ class ListIterator { using iter_type = boost::string_ref::const_iterator; @@ -322,17 +328,20 @@ ListIterator::increment() } } } -/** Returns true if two strings are equal. - - A case-insensitive comparison is used. -*/ +/** + * Returns true if two strings are equal. + * + * A case-insensitive comparison is used. + */ inline bool ciEqual(boost::string_ref s1, boost::string_ref s2) { return boost::range::equal(s1, s2, detail::CiEqualPred{}); } -/** Returns a range representing the list. */ +/** + * Returns a range representing the list. + */ inline boost::iterator_range makeList(boost::string_ref const& field) { @@ -340,20 +349,22 @@ makeList(boost::string_ref const& field) ListIterator{field.begin(), field.end()}, ListIterator{field.end(), field.end()}}; } -/** Returns true if the specified token exists in the list. - - A case-insensitive comparison is used. -*/ +/** + * Returns true if the specified token exists in the list. + * + * A case-insensitive comparison is used. + */ template bool tokenInList(boost::string_ref const& value, boost::string_ref const& token) { - for (auto const& item : makeList(value)) - { - if (ciEqual(item, token)) - return true; - } - return false; + auto const list = makeList(value); + // ListIterator is not default-constructible, so it does not model a std::ranges + // sentinel/range; the classic std::any_of (which only needs an input iterator) + // is used instead. + // NOLINTNEXTLINE(modernize-use-ranges) + return std::any_of( + list.begin(), list.end(), [&token](auto const& item) { return ciEqual(item, token); }); } template diff --git a/include/xrpl/beast/test/yield_to.h b/include/xrpl/beast/test/yield_to.h index 84d7d8846d..b3aa482dd5 100644 --- a/include/xrpl/beast/test/yield_to.h +++ b/include/xrpl/beast/test/yield_to.h @@ -11,18 +11,21 @@ #include #include +#include +#include #include #include #include namespace beast::test { -/** Mix-in to support tests using asio coroutines. - - Derive from this class and use yield_to to launch test - functions inside coroutines. This is handy for testing - asynchronous asio code. -*/ +/** + * Mix-in to support tests using asio coroutines. + * + * Derive from this class and use yield_to to launch test + * functions inside coroutines. This is handy for testing + * asynchronous asio code. + */ class EnableYieldTo { protected: @@ -36,7 +39,9 @@ private: std::size_t running_ = 0; public: - /// The type of yield context passed to functions. + /** + * The type of yield context passed to functions. + */ using yield_context = boost::asio::yield_context; explicit EnableYieldTo(std::size_t concurrency = 1) : work_(boost::asio::make_work_guard(ios_)) @@ -55,24 +60,27 @@ public: t.join(); } - /// Return the `io_context` associated with the object + /** + * Return the `io_context` associated with the object + */ boost::asio::io_context& getIoContext() { return ios_; } - /** Run one or more functions, each in a coroutine. - - This call will block until all coroutines terminate. - - Each functions should have this signature: - @code - void f(yield_context); - @endcode - - @param fn... One or more functions to invoke. - */ + /** + * Run one or more functions, each in a coroutine. + * + * This call will block until all coroutines terminate. + * + * Each functions should have this signature: + * @code + * void f(yield_context); + * @endcode + * + * @param fn... One or more functions to invoke. + */ #if BEAST_DOXYGEN template void 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/amount.h b/include/xrpl/beast/unit_test/amount.h index 3a392f393f..c1e4357753 100644 --- a/include/xrpl/beast/unit_test/amount.h +++ b/include/xrpl/beast/unit_test/amount.h @@ -10,7 +10,9 @@ namespace beast::unit_test { -/** Utility for producing nicely composed output of amounts with units. */ +/** + * Utility for producing nicely composed output of amounts with units. + */ class Amount { private: diff --git a/include/xrpl/beast/unit_test/detail/const_container.h b/include/xrpl/beast/unit_test/detail/const_container.h index c171771e45..9f4646cbdb 100644 --- a/include/xrpl/beast/unit_test/detail/const_container.h +++ b/include/xrpl/beast/unit_test/detail/const_container.h @@ -6,10 +6,11 @@ namespace beast::unit_test::detail { -/** Adapter to constrain a container interface. - The interface allows for limited read only operations. Derived classes - provide additional behavior. -*/ +/** + * Adapter to constrain a container interface. + * The interface allows for limited read only operations. Derived classes + * provide additional behavior. + */ template class ConstContainer { @@ -32,27 +33,33 @@ 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. */ + /** + * Returns `true` if the container is empty. + */ [[nodiscard]] bool empty() const { return cont_.empty(); } - /** Returns the number of items in the container. */ + /** + * Returns the number of items in the container. + */ [[nodiscard]] size_type size() const { return cont_.size(); } - /** Returns forward iterators for traversal. */ + /** + * Returns forward iterators for traversal. + */ /** @{ */ [[nodiscard]] const_iterator begin() const diff --git a/include/xrpl/beast/unit_test/global_suites.h b/include/xrpl/beast/unit_test/global_suites.h index 72ed738bdb..18e5bc3a6b 100644 --- a/include/xrpl/beast/unit_test/global_suites.h +++ b/include/xrpl/beast/unit_test/global_suites.h @@ -10,7 +10,9 @@ namespace beast::unit_test { namespace detail { -/// Holds test suites registered during static initialization. +/** + * Holds test suites registered during static initialization. + */ inline SuiteList& globalSuites() { @@ -34,7 +36,9 @@ struct InsertSuite } // namespace detail -/// Holds test suites registered during static initialization. +/** + * Holds test suites registered during static initialization. + */ inline SuiteList const& globalSuites() { diff --git a/include/xrpl/beast/unit_test/match.h b/include/xrpl/beast/unit_test/match.h index da466ab228..966574b833 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 { @@ -120,42 +121,48 @@ Selector::operator()(SuiteInfo const& s) // Utility functions for producing predicates to select suites. -/** Returns a predicate that implements a smart matching rule. - The predicate checks the suite, module, and library fields of the - SuiteInfo in that order. When it finds a match, it changes modes - depending on what was found: - - If a suite is matched first, then only the suite is selected. The - suite may be marked manual. - - If a module is matched first, then only suites from that module - and library not marked manual are selected from then on. - - If a library is matched first, then only suites from that library - not marked manual are selected from then on. - -*/ +/** + * Returns a predicate that implements a smart matching rule. + * The predicate checks the suite, module, and library fields of the + * SuiteInfo in that order. When it finds a match, it changes modes + * depending on what was found: + * + * If a suite is matched first, then only the suite is selected. The + * suite may be marked manual. + * + * If a module is matched first, then only suites from that module + * and library not marked manual are selected from then on. + * + * If a library is matched first, then only suites from that library + * not marked manual are selected from then on. + */ inline Selector matchAuto(std::string const& name) { return Selector(Selector::ModeT::Automatch, name); } -/** Return a predicate that matches all suites not marked manual. */ +/** + * Return a predicate that matches all suites not marked manual. + */ inline Selector matchAll() { return Selector(Selector::ModeT::All); } -/** Returns a predicate that matches a specific suite. */ +/** + * Returns a predicate that matches a specific suite. + */ inline Selector matchSuite(std::string const& name) { return Selector(Selector::ModeT::Suite, name); } -/** Returns a predicate that matches all suites in a library. */ +/** + * Returns a predicate that matches all suites in a library. + */ inline Selector matchLibrary(std::string const& name) { diff --git a/include/xrpl/beast/unit_test/recorder.h b/include/xrpl/beast/unit_test/recorder.h index 2ed88d4a46..fcadb63fc9 100644 --- a/include/xrpl/beast/unit_test/recorder.h +++ b/include/xrpl/beast/unit_test/recorder.h @@ -6,10 +6,16 @@ #include #include +#include + +#include +#include namespace beast::unit_test { -/** A test runner that stores the results. */ +/** + * A test runner that stores the results. + */ class Recorder : public Runner { private: @@ -20,7 +26,9 @@ private: public: Recorder() = default; - /** Returns a report with the results of all completed suites. */ + /** + * Returns a report with the results of all completed suites. + */ [[nodiscard]] Results const& report() const { diff --git a/include/xrpl/beast/unit_test/reporter.h b/include/xrpl/beast/unit_test/reporter.h index 1fdbb451a6..0fe77a7862 100644 --- a/include/xrpl/beast/unit_test/reporter.h +++ b/include/xrpl/beast/unit_test/reporter.h @@ -5,26 +5,30 @@ #pragma once #include -#include +#include +#include #include #include #include #include +#include #include #include #include #include #include +#include namespace beast::unit_test { namespace detail { -/** A simple test runner that writes everything to a stream in real time. - The totals are output when the object is destroyed. -*/ +/** + * A simple test runner that writes everything to a stream in real time. + * The totals are output when the object is destroyed. + */ template class Reporter : public Runner { @@ -48,7 +52,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 +64,7 @@ private: struct Results { - using run_time = std::pair; + using run_time = std::pair; static constexpr auto kMaxTop = 10; @@ -69,7 +73,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); @@ -91,7 +95,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 +145,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 +183,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); diff --git a/include/xrpl/beast/unit_test/results.h b/include/xrpl/beast/unit_test/results.h index 02aa9730d1..273ad5b129 100644 --- a/include/xrpl/beast/unit_test/results.h +++ b/include/xrpl/beast/unit_test/results.h @@ -6,17 +6,22 @@ #include +#include #include #include #include namespace beast::unit_test { -/** Holds a set of test condition outcomes in a testcase. */ +/** + * Holds a set of test condition outcomes in a testcase. + */ class CaseResults { public: - /** Holds the result of evaluating one test condition. */ + /** + * Holds the result of evaluating one test condition. + */ struct Test { explicit Test(bool pass) : pass(pass) @@ -40,28 +45,36 @@ private: public: TestsT() = default; - /** Returns the total number of test conditions. */ + /** + * Returns the total number of test conditions. + */ [[nodiscard]] std::size_t total() const { return cont().size(); } - /** Returns the number of failed test conditions. */ + /** + * Returns the number of failed test conditions. + */ [[nodiscard]] std::size_t failed() const { return failed_; } - /** Register a successful test condition. */ + /** + * Register a successful test condition. + */ void pass() { cont().emplace_back(true); } - /** Register a failed test condition. */ + /** + * Register a failed test condition. + */ void fail(std::string const& reason = "") { @@ -73,7 +86,9 @@ private: class LogT : public detail::ConstContainer> { public: - /** Insert a string into the log. */ + /** + * Insert a string into the log. + */ void insert(std::string const& s) { @@ -88,23 +103,31 @@ public: { } - /** Returns the name of this testcase. */ + /** + * Returns the name of this testcase. + */ [[nodiscard]] std::string const& name() const { return name_; } - /** Memberspace for a container of test condition outcomes. */ + /** + * Memberspace for a container of test condition outcomes. + */ TestsT tests; - /** Memberspace for a container of testcase log messages. */ + /** + * Memberspace for a container of testcase log messages. + */ LogT log; }; //-------------------------------------------------------------------------- -/** Holds the set of testcase results in a suite. */ +/** + * Holds the set of testcase results in a suite. + */ class SuiteResults : public detail::ConstContainer> { private: @@ -117,28 +140,36 @@ public: { } - /** Returns the name of this suite. */ + /** + * Returns the name of this suite. + */ [[nodiscard]] std::string const& name() const { return name_; } - /** Returns the total number of test conditions. */ + /** + * Returns the total number of test conditions. + */ [[nodiscard]] std::size_t total() const { return total_; } - /** Returns the number of failures. */ + /** + * Returns the number of failures. + */ [[nodiscard]] std::size_t failed() const { return failed_; } - /** Insert a set of testcase results. */ + /** + * Insert a set of testcase results. + */ /** @{ */ void insert(CaseResults&& r) @@ -161,7 +192,9 @@ public: //------------------------------------------------------------------------------ // VFALCO TODO Make this a template class using scoped allocators -/** Holds the results of running a set of testsuites. */ +/** + * Holds the results of running a set of testsuites. + */ class Results : public detail::ConstContainer> { private: @@ -172,28 +205,36 @@ private: public: Results() = default; - /** Returns the total number of test cases. */ + /** + * Returns the total number of test cases. + */ [[nodiscard]] std::size_t cases() const { return cases_; } - /** Returns the total number of test conditions. */ + /** + * Returns the total number of test conditions. + */ [[nodiscard]] std::size_t total() const { return total_; } - /** Returns the number of failures. */ + /** + * Returns the number of failures. + */ [[nodiscard]] std::size_t failed() const { return failed_; } - /** Insert a set of suite results. */ + /** + * Insert a set of suite results. + */ /** @{ */ void insert(SuiteResults&& r) diff --git a/include/xrpl/beast/unit_test/runner.h b/include/xrpl/beast/unit_test/runner.h index f0aea84280..b0f729009e 100644 --- a/include/xrpl/beast/unit_test/runner.h +++ b/include/xrpl/beast/unit_test/runner.h @@ -14,11 +14,12 @@ namespace beast::unit_test { -/** Unit test runner interface. - - Derived classes can customize the reporting behavior. This interface is - injected into the unit_test class to receive the results of the tests. -*/ +/** + * Unit test runner interface. + * + * Derived classes can customize the reporting behavior. This interface is + * injected into the unit_test class to receive the results of the tests. + */ class Runner { std::string arg_; @@ -35,128 +36,152 @@ public: Runner& operator=(Runner const&) = delete; - /** Set the argument string. - - The argument string is available to suites and - allows for customization of the test. Each suite - defines its own syntax for the argument string. - The same argument is passed to all suites. - */ + /** + * Set the argument string. + * + * The argument string is available to suites and + * allows for customization of the test. Each suite + * defines its own syntax for the argument string. + * The same argument is passed to all suites. + */ void arg(std::string const& s) { arg_ = s; } - /** Returns the argument string. */ + /** + * Returns the argument string. + */ [[nodiscard]] std::string const& arg() const { return arg_; } - /** Set the reference fee (in drops) for tests. - - If provided, this value is used in every suite that - does not override it. - */ + /** + * Set the reference fee (in drops) for tests. + * + * If provided, this value is used in every suite that does not override it. + */ void referenceFee(std::int64_t fee) { referenceFee_ = fee; } - /** Returns the reference fee, if any. */ + /** + * Returns the reference fee, if any. + */ [[nodiscard]] std::optional const& referenceFee() const { return referenceFee_; } - /** Run the specified suite. - @return `true` if any conditions failed. - */ + /** + * Run the specified suite. + * @return `true` if any conditions failed. + */ template bool run(SuiteInfo const& s); - /** Run a sequence of suites. - The expression - `FwdIter::value_type` - must be convertible to `SuiteInfo`. - @return `true` if any conditions failed. - */ + /** + * Run a sequence of suites. + * The expression + * `FwdIter::value_type` + * must be convertible to `SuiteInfo`. + * @return `true` if any conditions failed. + */ template bool run(FwdIter first, FwdIter last); - /** Conditionally run a sequence of suites. - pred will be called as: - @code - bool pred(SuiteInfo const&); - @endcode - @return `true` if any conditions failed. - */ + /** + * Conditionally run a sequence of suites. + * pred will be called as: + * @code + * bool pred(SuiteInfo const&); + * @endcode + * @return `true` if any conditions failed. + */ template bool runIf(FwdIter first, FwdIter last, Pred pred = Pred{}); - /** Run all suites in a container. - @return `true` if any conditions failed. - */ + /** + * Run all suites in a container. + * @return `true` if any conditions failed. + */ template bool runEach(SequenceContainer const& c); - /** Conditionally run suites in a container. - pred will be called as: - @code - bool pred(SuiteInfo const&); - @endcode - @return `true` if any conditions failed. - */ + /** + * Conditionally run suites in a container. + * pred will be called as: + * @code + * bool pred(SuiteInfo const&); + * @endcode + * @return `true` if any conditions failed. + */ template bool runEachIf(SequenceContainer const& c, Pred pred = Pred{}); protected: - /// Called when a new suite starts. + /** + * Called when a new suite starts. + */ virtual void onSuiteBegin(SuiteInfo const&) { } - /// Called when a suite ends. + /** + * Called when a suite ends. + */ virtual void onSuiteEnd() { } - /// Called when a new case starts. + /** + * Called when a new case starts. + */ virtual void onCaseBegin(std::string const&) { } - /// Called when a new case ends. + /** + * Called when a new case ends. + */ virtual void onCaseEnd() { } - /// Called for each passing condition. + /** + * Called for each passing condition. + */ virtual void onPass() { } - /// Called for each failing condition. + /** + * Called for each failing condition. + */ virtual void onFail(std::string const&) { } - /// Called when a test logs output. + /** + * Called when a test logs output. + */ virtual void onLog(std::string const&) { diff --git a/include/xrpl/beast/unit_test/suite.h b/include/xrpl/beast/unit_test/suite.h index 10a70b6db2..3c716e5859 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 @@ -39,13 +41,14 @@ class Thread; enum class AbortT { NoAbortOnFail, AbortOnFail }; -/** A testsuite class. - - Derived classes execute a series of testcases, where each testcase is - a series of pass/fail tests. To provide a unit test using this class, - derive from it and use the BEAST_DEFINE_UNIT_TEST macro in a - translation unit. -*/ +/** + * A testsuite class. + * + * Derived classes execute a series of testcases, where each testcase is + * a series of pass/fail tests. To provide a unit test using this class, + * derive from it and use the BEAST_DEFINE_UNIT_TEST macro in a + * translation unit. + */ class Suite { private: @@ -116,16 +119,17 @@ private: { } - /** Open a new testcase. - - A testcase is a series of evaluated test conditions. A test - suite may have multiple test cases. A test is associated with - the last opened testcase. When the test first runs, a default - unnamed case is opened. Tests with only one case may omit the - call to testcase. - - @param abort Determines if suite continues running after a failure. - */ + /** + * Open a new testcase. + * + * A testcase is a series of evaluated test conditions. A test + * suite may have multiple test cases. A test is associated with + * the last opened testcase. When the test first runs, a default + * unnamed case is opened. Tests with only one case may omit the + * call to testcase. + * + * @param abort Determines if suite continues running after a failure. + */ void operator()(std::string const& name, AbortT abort = AbortT::NoAbortOnFail); @@ -138,19 +142,23 @@ private: }; public: - /** Logging output stream. - - Text sent to the log output stream will be forwarded to - the output stream associated with the runner. - */ + /** + * Logging output stream. + * + * Text sent to the log output stream will be forwarded to + * the output stream associated with the runner. + */ LogOs log; - /** Memberspace for declaring test cases. */ + /** + * Memberspace for declaring test cases. + */ TestcaseT testcase; - /** Returns the "current" running suite. - If no suite is running, nullptr is returned. - */ + /** + * Returns the "current" running suite. + * If no suite is running, nullptr is returned. + */ static Suite* thisSuite() { @@ -166,30 +174,34 @@ public: Suite& operator=(Suite const&) = delete; - /** Invokes the test using the specified runner. - - Data members are set up here instead of the constructor as a - convenience to writing the derived class to avoid repetition of - forwarded constructor arguments to the base. - Normally this is called by the framework for you. - */ + /** + * Invokes the test using the specified runner. + * + * Data members are set up here instead of the constructor as a + * convenience to writing the derived class to avoid repetition of + * forwarded constructor arguments to the base. + * Normally this is called by the framework for you. + */ template void operator()(Runner& r); - /** Record a successful test condition. */ + /** + * Record a successful test condition. + */ template void pass(); - /** Record a failure. - - @param reason Optional text added to the output on a failure. - - @param file The source code file where the test failed. - - @param line The source code line number where the test failed. - */ + /** + * Record a failure. + * + * @param reason Optional text added to the output on a failure. + * + * @param file The source code file where the test failed. + * + * @param line The source code line number where the test failed. + */ /** @{ */ template void @@ -200,23 +212,24 @@ public: fail(std::string const& reason = ""); /** @} */ - /** Evaluate a test condition. - - This function provides improved logging by incorporating the - file name and line number into the reported output on failure, - as well as additional text specified by the caller. - - @param shouldBeTrue The condition to test. The condition - is evaluated in a boolean context. - - @param reason Optional added text to output on a failure. - - @param file The source code file where the test failed. - - @param line The source code line number where the test failed. - - @return `true` if the test condition indicates success. - */ + /** + * Evaluate a test condition. + * + * This function provides improved logging by incorporating the + * file name and line number into the reported output on failure, + * as well as additional text specified by the caller. + * + * @param shouldBeTrue The condition to test. The condition + * is evaluated in a boolean context. + * + * @param reason Optional added text to output on a failure. + * + * @param file The source code file where the test failed. + * + * @param line The source code line number where the test failed. + * + * @return `true` if the test condition indicates success. + */ /** @{ */ template bool @@ -273,14 +286,18 @@ public: return unexcept(f, ""); } - /** Return the argument associated with the runner. */ + /** + * Return the argument associated with the runner. + */ std::string const& arg() const { return runner_->arg(); } - /** Return the reference fee associated with the runner. */ + /** + * Return the reference fee associated with the runner. + */ std::optional referenceFee() const { @@ -290,8 +307,10 @@ public: return runner_->referenceFee(); } - // DEPRECATED - // @return `true` if the test condition indicates success(a false value) + /** + * DEPRECATED + * @return `true` if the test condition indicates success(a false value) + */ template bool unexpected(Condition shouldBeFalse, String const& reason); @@ -313,7 +332,9 @@ private: return &kPTs; } - /** Runs the suite. */ + /** + * Runs the suite. + */ virtual void run() = 0; @@ -566,18 +587,20 @@ Suite::run(Runner& r) } #ifndef BEAST_EXPECT -/** Check a precondition. - - If the condition is false, the file and line number are reported. -*/ +/** + * Check a precondition. + * + * If the condition is false, the file and line number are reported. + */ #define BEAST_EXPECT(cond) expect(cond, __FILE__, __LINE__) #endif #ifndef BEAST_EXPECTS -/** Check a precondition. - - If the condition is false, the file and line number are reported. -*/ +/** + * Check a precondition. + * + * If the condition is false, the file and line number are reported. + */ #define BEAST_EXPECTS(cond, reason) \ ((cond) ? (pass(), true) : (fail((reason), __FILE__, __LINE__), false)) #endif @@ -601,41 +624,43 @@ Suite::run(Runner& r) // #ifndef BEAST_DEFINE_TESTSUITE -/** Enables insertion of test suites into the global container. - The default is to insert all test suite definitions into the global - container. If BEAST_DEFINE_TESTSUITE is user defined, this macro - has no effect. -*/ +/** + * Enables insertion of test suites into the global container. + * The default is to insert all test suite definitions into the global + * container. If BEAST_DEFINE_TESTSUITE is user defined, this macro + * has no effect. + */ #ifndef BEAST_NO_UNIT_TEST_INLINE #define BEAST_NO_UNIT_TEST_INLINE 0 #endif -/** Define a unit test suite. - - Class The type representing the class being tested. - Module Identifies the module. - Library Identifies the library. - - The declaration for the class implementing the test should be the same - as Class ## _test. For example, if Class is aged_ordered_container, the - test class must be declared as: - - @code - - struct aged_ordered_container_test : beast::unit_test::suite - { - //... - }; - - @endcode - - The macro invocation must appear in the same namespace as the test class. - - Unit test priorities were introduced so parallel unit_test::suites would - execute faster. Suites with longer running times have higher priorities - than unit tests with shorter running times. Suites with no priorities - are assumed to run most quickly, so they run last. -*/ +/** + * Define a unit test suite. + * + * Class The type representing the class being tested. + * Module Identifies the module. + * Library Identifies the library. + * + * The declaration for the class implementing the test should be the same + * as Class ## _test. For example, if Class is aged_ordered_container, the + * test class must be declared as: + * + * @code + * + * struct aged_ordered_container_test : beast::unit_test::suite + * { + * //... + * }; + * + * @endcode + * + * The macro invocation must appear in the same namespace as the test class. + * + * Unit test priorities were introduced so parallel unit_test::suites would + * execute faster. Suites with longer running times have higher priorities + * than unit tests with shorter running times. Suites with no priorities + * are assumed to run most quickly, so they run last. + */ #if BEAST_NO_UNIT_TEST_INLINE #define BEAST_DEFINE_TESTSUITE(Class, Module, Library) @@ -644,7 +669,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..c4e3496f13 100644 --- a/include/xrpl/beast/unit_test/suite_info.h +++ b/include/xrpl/beast/unit_test/suite_info.h @@ -4,16 +4,18 @@ #pragma once -#include #include #include +#include #include namespace beast::unit_test { class Runner; -/** Associates a unit test type with metadata. */ +/** + * Associates a unit test type with metadata. + */ class SuiteInfo { using run_type = std::function; @@ -60,21 +62,27 @@ public: return library_; } - /// Returns `true` if this suite only runs manually. + /** + * Returns `true` if this suite only runs manually. + */ [[nodiscard]] bool manual() const { return manual_; } - /// Return the canonical suite name as a string. + /** + * Return the canonical suite name as a string. + */ [[nodiscard]] std::string fullName() const { return library_ + "." + module_ + "." + name_; } - /// Run a new instance of the associated test suite. + /** + * Run a new instance of the associated test suite. + */ void run(Runner& r) const { @@ -93,7 +101,9 @@ public: //------------------------------------------------------------------------------ -/// Convenience for producing SuiteInfo for a given test type. +/** + * Convenience for producing SuiteInfo for a given test type. + */ template SuiteInfo makeSuiteInfo(std::string name, std::string module, std::string library, bool manual, int priority) diff --git a/include/xrpl/beast/unit_test/suite_list.h b/include/xrpl/beast/unit_test/suite_list.h index 748f994602..057a362859 100644 --- a/include/xrpl/beast/unit_test/suite_list.h +++ b/include/xrpl/beast/unit_test/suite_list.h @@ -10,12 +10,15 @@ #include #include -#include -#include +#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include // IWYU pragma: keep namespace beast::unit_test { -/// A container of test suites. +/** + * A container of test suites. + */ class SuiteList : public detail::ConstContainer> { private: @@ -25,10 +28,11 @@ private: #endif public: - /** Insert a suite into the set. - - The suite must not already exist. - */ + /** + * Insert a suite into the set. + * + * The suite must not already exist. + */ template void insert(char const* name, char const* module, char const* library, bool manual, int priority); diff --git a/include/xrpl/beast/unit_test/thread.h b/include/xrpl/beast/unit_test/thread.h index 7ae093eb85..5a5a99d149 100644 --- a/include/xrpl/beast/unit_test/thread.h +++ b/include/xrpl/beast/unit_test/thread.h @@ -6,13 +6,17 @@ #include +#include #include +#include #include #include namespace beast::unit_test { -/** Replacement for std::thread that handles exceptions in unit tests. */ +/** + * Replacement for std::thread that handles exceptions in unit tests. + */ class Thread { private: @@ -43,7 +47,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..9f0a1ead66 100644 --- a/include/xrpl/beast/utility/Journal.h +++ b/include/xrpl/beast/utility/Journal.h @@ -3,11 +3,16 @@ #include #include +#include #include +#include +#include namespace beast { -/** Severity level / threshold of a Journal message. */ +/** + * Severity level / threshold of a Journal message. + */ enum class Severity : std::uint8_t { All = 0, @@ -22,18 +27,19 @@ enum class Severity : std::uint8_t { None = Disabled }; -/** A generic endpoint for log messages. - - The Journal has a few simple goals: - - * To be light-weight and copied by value. - * To allow logging statements to be left in source code. - * The logging is controlled at run-time based on a logging threshold. - - It is advisable to check Journal::active(level) prior to formatting log - text. Doing so sidesteps expensive text formatting when the results - will not be sent to the log. -*/ +/** + * A generic endpoint for log messages. + * + * The Journal has a few simple goals: + * + * * To be light-weight and copied by value. + * * To allow logging statements to be left in source code. + * * The logging is controlled at run-time based on a logging threshold. + * + * It is advisable to check Journal::active(level) prior to formatting log + * text. Doing so sidesteps expensive text formatting when the results + * will not be sent to the log. + */ class Journal { public: @@ -46,7 +52,9 @@ private: public: //-------------------------------------------------------------------------- - /** Abstraction for the underlying message destination. */ + /** + * Abstraction for the underlying message destination. + */ class Sink { protected: @@ -60,36 +68,47 @@ public: Sink& operator=(Sink const& lhs) = delete; - /** Returns `true` if text at the passed severity produces output. */ + /** + * Returns `true` if text at the passed severity produces output. + */ [[nodiscard]] virtual bool active(Severity level) const; - /** Returns `true` if a message is also written to the Output Window - * (MSVC). */ + /** + * Returns `true` if a message is also written to the Output Window + * (MSVC). + */ [[nodiscard]] virtual bool console() const; - /** Set whether messages are also written to the Output Window (MSVC). + /** + * Set whether messages are also written to the Output Window (MSVC). */ virtual void console(bool output); - /** Returns the minimum severity level this sink will report. */ + /** + * Returns the minimum severity level this sink will report. + */ [[nodiscard]] virtual Severity threshold() const; - /** Set the minimum severity this sink will report. */ + /** + * Set the minimum severity this sink will report. + */ virtual void threshold(Severity thresh); - /** Write text to the sink at the specified severity. - A conforming implementation will not write the text if the passed - level is below the current threshold(). - */ + /** + * Write text to the sink at the specified severity. + * A conforming implementation will not write the text if the passed + * level is below the current threshold(). + */ virtual void write(Severity level, std::string const& text) = 0; - /** Bypass filter and write text to the sink at the specified severity. + /** + * Bypass filter and write text to the sink at the specified severity. * Always write the message, but maintain the same formatting as if * it passed through a level filter. * @@ -105,15 +124,17 @@ 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. */ + /** + * Returns a Sink which does nothing. + */ static Sink& getNullSink(); @@ -161,36 +182,43 @@ 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 //-------------------------------------------------------------------------- public: - /** Provide a light-weight way to check active() before string formatting */ + /** + * Provide a light-weight way to check active() before string formatting + */ class Stream { public: - /** Create a stream which produces no output. */ + /** + * Create a stream which produces no output. + */ explicit Stream() : sink_(getNullSink()), level_(Severity::Disabled) { } - /** Create a stream that writes at the given level. - - Constructor is inlined so checking active() very inexpensive. - */ + /** + * Create a stream that writes at the given level. + * + * Constructor is inlined so checking active() very inexpensive. + */ Stream(Sink& sink, Severity level) : sink_(sink), level_(level) { XRPL_ASSERT( level_ < Severity::Disabled, "beast::Journal::Stream::Stream : maximum level"); } - /** Construct or copy another Stream. */ + /** + * Construct or copy another Stream. + */ Stream(Stream const& other) : Stream(other.sink_, other.level_) { } @@ -198,21 +226,27 @@ public: Stream& operator=(Stream const& other) = delete; - /** Returns the Sink that this Stream writes to. */ + /** + * Returns the Sink that this Stream writes to. + */ [[nodiscard]] Sink& sink() const { return sink_; } - /** Returns the Severity level of messages this Stream reports. */ + /** + * Returns the Severity level of messages this Stream reports. + */ [[nodiscard]] Severity level() const { return level_; } - /** Returns `true` if sink logs anything at this stream's level. */ + /** + * Returns `true` if sink logs anything at this stream's level. + */ /** @{ */ [[nodiscard]] bool active() const @@ -227,7 +261,9 @@ public: } /** @} */ - /** Output stream support. */ + /** + * Output stream support. + */ /** @{ */ ScopedStream operator<<(std::ostream& manip(std::ostream&)) const; @@ -243,49 +279,60 @@ 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 //-------------------------------------------------------------------------- - /** Journal has no default constructor. */ + /** + * Journal has no default constructor. + */ Journal() = delete; - /** Create a journal that writes to the specified sink. */ + /** + * Create a journal that writes to the specified sink. + */ explicit Journal(Sink& sink) : sink_(&sink) { } - /** Returns the Sink associated with this Journal. */ + /** + * Returns the Sink associated with this Journal. + */ [[nodiscard]] Sink& sink() const { return *sink_; } - /** Returns a stream for this sink, with the specified severity level. */ + /** + * Returns a stream for this sink, with the specified severity level. + */ [[nodiscard]] Stream stream(Severity level) const { return Stream(*sink_, level); } - /** Returns `true` if any message would be logged at this severity level. - For a message to be logged, the severity must be at or above the - sink's severity threshold. - */ + /** + * Returns `true` if any message would be logged at this severity level. + * For a message to be logged, the severity must be at or above the + * sink's severity threshold. + */ [[nodiscard]] bool active(Severity level) const { return sink_->active(level); } - /** Severity stream access functions. */ + /** + * Severity stream access functions. + */ /** @{ */ [[nodiscard]] Stream trace() const @@ -326,12 +373,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 +458,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..f32f5b7fef 100644 --- a/include/xrpl/beast/utility/PropertyStream.h +++ b/include/xrpl/beast/utility/PropertyStream.h @@ -3,14 +3,18 @@ #include #include +#include #include #include +#include namespace beast { //------------------------------------------------------------------------------ -/** Abstract stream with RAII containers that produce a property tree. */ +/** + * Abstract stream with RAII containers that produce a property tree. + */ class PropertyStream { public: @@ -304,7 +308,9 @@ public: // //------------------------------------------------------------------------------ -/** Subclasses can be called to write to a stream and have children. */ +/** + * Subclasses can be called to write to a stream and have children. + */ class PropertyStream::Source { private: @@ -322,17 +328,22 @@ public: Source& operator=(Source const&) = delete; - /** Returns the name of this source. */ + /** + * Returns the name of this source. + */ [[nodiscard]] std::string const& name() const; - /** Add a child source. */ + /** + * Add a child source. + */ void add(Source& source); - /** Add a child source by pointer. - The source pointer is returned so it can be used in ctor-initializers. - */ + /** + * Add a child source by pointer. + * The source pointer is returned so it can be used in ctor-initializers. + */ template Derived* add(Derived* child) @@ -341,45 +352,55 @@ public: return child; } - /** Remove a child source from this Source. */ + /** + * Remove a child source from this Source. + */ void remove(Source& child); - /** Remove all child sources from this Source. */ + /** + * Remove all child sources from this Source. + */ void removeAll(); - /** Write only this Source to the stream. */ + /** + * Write only this Source to the stream. + */ void writeOne(PropertyStream& stream); - /** write this source and all its children recursively to the stream. */ + /** + * write this source and all its children recursively to the stream. + */ void write(PropertyStream& stream); - /** Parse the path and write the corresponding Source and optional children. - If the source is found, it is written. If the wildcard character '*' - exists as the last character in the path, then all the children are - written recursively. - */ + /** + * Parse the path and write the corresponding Source and optional children. + * If the source is found, it is written. If the wildcard character '*' + * exists as the last character in the path, then all the children are + * written recursively. + */ void write(PropertyStream& stream, std::string const& path); - /** Parse the dot-delimited Source path and return the result. - The first value will be a pointer to the Source object corresponding - to the given path. If no Source object exists, then the first value - will be nullptr and the second value will be undefined. - The second value is a boolean indicating whether or not the path string - specifies the wildcard character '*' as the last character. - - print statement examples - "parent.child" prints child and all of its children - "parent.child." start at the parent and print down to child - "parent.grandchild" prints nothing- grandchild not direct descendent - "parent.grandchild." starts at the parent and prints down to grandchild - "parent.grandchild.*" starts at parent, print through grandchild - children - */ + /** + * Parse the dot-delimited Source path and return the result. + * The first value will be a pointer to the Source object corresponding + * to the given path. If no Source object exists, then the first value + * will be nullptr and the second value will be undefined. + * The second value is a boolean indicating whether or not the path string + * specifies the wildcard character '*' as the last character. + * + * print statement examples + * "parent.child" prints child and all of its children + * "parent.child." start at the parent and print down to child + * "parent.grandchild" prints nothing- grandchild not direct descendent + * "parent.grandchild." starts at the parent and prints down to grandchild + * "parent.grandchild.*" starts at parent, print through grandchild + * children + */ std::pair find(std::string path); @@ -399,9 +420,10 @@ public: //-------------------------------------------------------------------------- - /** Subclass override. - The default version does nothing. - */ + /** + * Subclass override. + * The default version does nothing. + */ virtual void onWrite(Map&); }; diff --git a/include/xrpl/beast/utility/WrappedSink.h b/include/xrpl/beast/utility/WrappedSink.h index 22d75927fe..3ab48e1939 100644 --- a/include/xrpl/beast/utility/WrappedSink.h +++ b/include/xrpl/beast/utility/WrappedSink.h @@ -2,11 +2,14 @@ #include +#include #include namespace beast { -/** Wraps a Journal::Sink to prefix its output with a string. */ +/** + * Wraps a Journal::Sink to prefix its output with a string. + */ // A WrappedSink both is a Sink and has a Sink: // o It inherits from Sink so it has the correct interface. diff --git a/include/xrpl/beast/utility/Zero.h b/include/xrpl/beast/utility/Zero.h index f54345d437..406921c500 100644 --- a/include/xrpl/beast/utility/Zero.h +++ b/include/xrpl/beast/utility/Zero.h @@ -4,33 +4,34 @@ namespace beast { -/** Zero allows classes to offer efficient comparisons to zero. - - Zero is a struct to allow classes to efficiently compare with zero without - requiring an rvalue construction. - - It's often the case that we have classes which combine a number and a unit. - In such cases, comparisons like t > 0 or t != 0 make sense, but comparisons - like t > 1 or t != 1 do not. - - The class Zero allows such comparisons to be easily made. - - The comparing class T either needs to have a method called signum() which - returns a positive number, 0, or a negative; or there needs to be a signum - function which resolves in the namespace which takes an instance of T and - returns a positive, zero or negative number. -*/ +/** + * Zero allows classes to offer efficient comparisons to zero. + * + * Zero is a struct to allow classes to efficiently compare with zero without + * requiring an rvalue construction. + * + * It's often the case that we have classes which combine a number and a unit. + * In such cases, comparisons like t > 0 or t != 0 make sense, but comparisons + * like t > 1 or t != 1 do not. + * + * The class Zero allows such comparisons to be easily made. + * + * The comparing class T either needs to have a method called signum() which + * returns a positive number, 0, or a negative; or there needs to be a signum + * function which resolves in the namespace which takes an instance of T and + * returns a positive, zero or negative number. + */ struct Zero { explicit Zero() = default; }; -namespace { -constexpr Zero kZero{}; -} // namespace +inline constexpr Zero kZero{}; -/** Default implementation of signum calls the method on the class. */ +/** + * Default implementation of signum calls the method on the class. + */ template auto signum(T const& t) diff --git a/include/xrpl/beast/utility/maybe_const.h b/include/xrpl/beast/utility/maybe_const.h index 40904471be..848ea86cb2 100644 --- a/include/xrpl/beast/utility/maybe_const.h +++ b/include/xrpl/beast/utility/maybe_const.h @@ -4,7 +4,9 @@ namespace beast { -/** Makes T const or non const depending on a bool. */ +/** + * Makes T const or non const depending on a bool. + */ template struct MaybeConst { @@ -13,8 +15,10 @@ struct MaybeConst conditional_t::type const, std::remove_const_t>; }; -/** Alias for omitting `typename`. */ +/** + * 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/beast/utility/temp_dir.h b/include/xrpl/beast/utility/temp_dir.h index ec661b51c4..a0ff1e6940 100644 --- a/include/xrpl/beast/utility/temp_dir.h +++ b/include/xrpl/beast/utility/temp_dir.h @@ -6,11 +6,12 @@ namespace beast { -/** RAII temporary directory. - - The directory and all its contents are deleted when - the instance of `temp_dir` is destroyed. -*/ +/** + * RAII temporary directory. + * + * The directory and all its contents are deleted when + * the instance of `temp_dir` is destroyed. + */ class TempDir { boost::filesystem::path path_; @@ -22,7 +23,9 @@ public: operator=(TempDir const&) = delete; #endif - /// Construct a temporary directory. + /** + * Construct a temporary directory. + */ TempDir() { auto const dir = boost::filesystem::temp_directory_path(); @@ -33,7 +36,9 @@ public: boost::filesystem::create_directory(path_); } - /// Destroy a temporary directory. + /** + * Destroy a temporary directory. + */ ~TempDir() { // use non-throwing calls in the destructor @@ -42,17 +47,20 @@ public: // TODO: warn/notify if ec set ? } - /// Get the native path for the temporary directory + /** + * Get the native path for the temporary directory + */ [[nodiscard]] std::string path() const { return path_.string(); } - /** Get the native path for the a file. - - The file does not need to exist. - */ + /** + * Get the native path for the a file. + * + * The file does not need to exist. + */ [[nodiscard]] std::string file(std::string const& name) const { diff --git a/include/xrpl/beast/xor_shift_engine.h b/include/xrpl/beast/xor_shift_engine.h index 45baecf101..6a7272c195 100644 --- a/include/xrpl/beast/xor_shift_engine.h +++ b/include/xrpl/beast/xor_shift_engine.h @@ -85,14 +85,15 @@ XorShiftEngine::murmurhash3(result_type x) -> result_type } // namespace detail -/** XOR-shift Generator. - - Meets the requirements of UniformRandomNumberGenerator. - - Simple and fast RNG based on: - http://xorshift.di.unimi.it/xorshift128plus.c - does not accept seed==0 -*/ +/** + * XOR-shift Generator. + * + * Meets the requirements of UniformRandomNumberGenerator. + * + * Simple and fast RNG based on: + * http://xorshift.di.unimi.it/xorshift128plus.c + * does not accept seed==0 + */ using xor_shift_engine = detail::XorShiftEngine<>; } // namespace beast diff --git a/include/xrpl/conditions/Condition.h b/include/xrpl/conditions/Condition.h index 66d1d24736..365a41a087 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 { @@ -20,42 +24,49 @@ enum class Type : std::uint8_t { class Condition { public: - /** The largest binary condition we support. - - @note This value will be increased in the future, but it - must never decrease, as that could cause conditions - that were previously considered valid to no longer - be allowed. - */ + /** + * The largest binary condition we support. + * + * @note This value will be increased in the future, but it + * must never decrease, as that could cause conditions + * that were previously considered valid to no longer + * be allowed. + */ static constexpr std::size_t kMaxSerializedCondition = 128; - /** Load a condition from its binary form - - @param s The buffer containing the fulfillment to load. - @param ec Set to the error, if any occurred. - - The binary format for a condition is specified in the - cryptoconditions RFC. See: - - https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.2 - */ + /** + * Load a condition from its binary form + * + * @param s The buffer containing the fulfillment to load. + * @param ec Set to the error, if any occurred. + * + * The binary format for a condition is specified in the + * cryptoconditions RFC. See: + * + * https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.2 + */ static std::unique_ptr deserialize(Slice s, std::error_code& ec); public: Type type; - /** An identifier for this condition. - - This fingerprint is meant to be unique only with - respect to other conditions of the same type. - */ + /** + * An identifier for this condition. + * + * This fingerprint is meant to be unique only with + * respect to other conditions of the same type. + */ Buffer fingerprint; - /** The cost associated with this condition. */ + /** + * The cost associated with this condition. + */ std::uint32_t cost; - /** For compound conditions, set of conditions includes */ + /** + * For compound conditions, set of conditions includes + */ std::set subtypes; Condition(Type t, std::uint32_t c, Slice fp) : type(t), fingerprint(fp), cost(c) diff --git a/include/xrpl/conditions/Fulfillment.h b/include/xrpl/conditions/Fulfillment.h index fd8cd7d31e..11f3165a58 100644 --- a/include/xrpl/conditions/Fulfillment.h +++ b/include/xrpl/conditions/Fulfillment.h @@ -4,69 +4,83 @@ #include #include +#include +#include +#include +#include + namespace xrpl::cryptoconditions { struct Fulfillment { public: - /** The largest binary fulfillment we support. - - @note This value will be increased in the future, but it - must never decrease, as that could cause fulfillments - that were previously considered valid to no longer - be allowed. - */ + /** + * The largest binary fulfillment we support. + * + * @note This value will be increased in the future, but it + * must never decrease, as that could cause fulfillments + * that were previously considered valid to no longer + * be allowed. + */ static constexpr std::size_t kMaxSerializedFulfillment = 256; - /** Load a fulfillment from its binary form - - @param s The buffer containing the fulfillment to load. - @param ec Set to the error, if any occurred. - - The binary format for a fulfillment is specified in the - cryptoconditions RFC. See: - - https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.3 - */ + /** + * Load a fulfillment from its binary form + * + * @param s The buffer containing the fulfillment to load. + * @param ec Set to the error, if any occurred. + * + * The binary format for a fulfillment is specified in the + * cryptoconditions RFC. See: + * + * https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.3 + */ static std::unique_ptr deserialize(Slice s, std::error_code& ec); public: virtual ~Fulfillment() = default; - /** Returns the fulfillment's fingerprint: - - The fingerprint is an octet string uniquely - representing this fulfillment's condition - with respect to other conditions of the - same type. - */ + /** + * Returns the fulfillment's fingerprint: + * + * The fingerprint is an octet string uniquely + * representing this fulfillment's condition + * with respect to other conditions of the + * same type. + */ [[nodiscard]] virtual Buffer fingerprint() const = 0; - /** Returns the type of this condition. */ + /** + * Returns the type of this condition. + */ [[nodiscard]] virtual Type type() const = 0; - /** Validates a fulfillment. */ + /** + * Validates a fulfillment. + */ [[nodiscard]] virtual bool validate(Slice data) const = 0; - /** Calculates the cost associated with this fulfillment. * - - The cost function is deterministic and depends on the - type and properties of the condition and the fulfillment - that the condition is generated from. - */ + /** + * Calculates the cost associated with this fulfillment. * + * + * The cost function is deterministic and depends on the + * type and properties of the condition and the fulfillment + * that the condition is generated from. + */ [[nodiscard]] virtual std::uint32_t cost() const = 0; - /** Returns the condition associated with the given fulfillment. - - This process is completely deterministic. All implementations - will, if compliant, produce the identical condition for the - same fulfillment. - */ + /** + * Returns the condition associated with the given fulfillment. + * + * This process is completely deterministic. All implementations + * will, if compliant, produce the identical condition for the + * same fulfillment. + */ [[nodiscard]] virtual Condition condition() const = 0; }; @@ -85,36 +99,40 @@ operator!=(Fulfillment const& lhs, Fulfillment const& rhs) return !(lhs == rhs); } -/** Determine whether the given fulfillment and condition match */ +/** + * Determine whether the given fulfillment and condition match + */ bool match(Fulfillment const& f, Condition const& c); -/** Verify if the given message satisfies the fulfillment. - - @param f The fulfillment - @param c The condition - @param m The message - - @note the message is not relevant for some conditions - and a fulfillment will successfully satisfy its - condition for any given message. -*/ +/** + * Verify if the given message satisfies the fulfillment. + * + * @param f The fulfillment + * @param c The condition + * @param m The message + * + * @note the message is not relevant for some conditions + * and a fulfillment will successfully satisfy its + * condition for any given message. + */ bool validate(Fulfillment const& f, Condition const& c, Slice m); -/** Verify a cryptoconditional trigger. - - A cryptoconditional trigger is a cryptocondition with - an empty message. - - When using such triggers, it is recommended that the - trigger be of type preimage, prefix or threshold. If - a signature type is used (i.e. Ed25519 or RSA-SHA256) - then the Ed25519 or RSA keys should be single-use keys. - - @param f The fulfillment - @param c The condition -*/ +/** + * Verify a cryptoconditional trigger. + * + * A cryptoconditional trigger is a cryptocondition with + * an empty message. + * + * When using such triggers, it is recommended that the + * trigger be of type preimage, prefix or threshold. If + * a signature type is used (i.e. Ed25519 or RSA-SHA256) + * then the Ed25519 or RSA keys should be single-use keys. + * + * @param f The fulfillment + * @param c The condition + */ bool validate(Fulfillment const& f, Condition const& c); diff --git a/include/xrpl/conditions/detail/PreimageSha256.h b/include/xrpl/conditions/detail/PreimageSha256.h index c592ea37ee..007588a0b5 100644 --- a/include/xrpl/conditions/detail/PreimageSha256.h +++ b/include/xrpl/conditions/detail/PreimageSha256.h @@ -7,30 +7,36 @@ #include #include +#include +#include #include +#include +#include namespace xrpl::cryptoconditions { class PreimageSha256 final : public Fulfillment { public: - /** The maximum allowed length of a preimage. - - The specification does not specify a minimum supported - length, nor does it require all conditions to support - the same minimum length. - - While future versions of this code will never lower - this limit, they may opt to raise it. - */ + /** + * The maximum allowed length of a preimage. + * + * The specification does not specify a minimum supported + * length, nor does it require all conditions to support + * the same minimum length. + * + * While future versions of this code will never lower + * this limit, they may opt to raise it. + */ static constexpr std::size_t kMaxPreimageLength = 128; - /** Parse the payload for a PreimageSha256 condition - - @param s A slice containing the DER encoded payload - @param ec indicates success or failure of the operation - @return the preimage, if successful; empty pointer otherwise. - */ + /** + * Parse the payload for a PreimageSha256 condition + * + * @param s A slice containing the DER encoded payload + * @param ec indicates success or failure of the operation + * @return the preimage, if successful; empty pointer otherwise. + */ static std::unique_ptr deserialize(Slice s, std::error_code& ec) { 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/config/BasicConfig.h b/include/xrpl/config/BasicConfig.h index 5a82f7d081..607a0c3e5f 100644 --- a/include/xrpl/config/BasicConfig.h +++ b/include/xrpl/config/BasicConfig.h @@ -6,9 +6,13 @@ #include #include +#include #include +#include +#include #include #include +#include #include namespace xrpl { @@ -17,9 +21,10 @@ using IniFileSections = std::unordered_map //------------------------------------------------------------------------------ -/** Holds a collection of configuration values. - A configuration file contains zero or more sections. -*/ +/** + * Holds a collection of configuration values. + * A configuration file contains zero or more sections. + */ class Section { private: @@ -32,28 +37,34 @@ private: using const_iterator = decltype(lookup_)::const_iterator; public: - /** Create an empty section. */ + /** + * Create an empty section. + */ explicit Section(std::string name = ""); - /** Returns the name of this section. */ + /** + * Returns the name of this section. + */ [[nodiscard]] std::string const& name() const { return name_; } - /** Returns all the lines in the section. - This includes everything. - */ + /** + * Returns all the lines in the section. + * This includes everything. + */ [[nodiscard]] std::vector const& lines() const { return lines_; } - /** Returns all the values in the section. - Values are non-empty lines which are not key/value pairs. - */ + /** + * Returns all the values in the section. + * Values are non-empty lines which are not key/value pairs. + */ [[nodiscard]] std::vector const& values() const { @@ -80,7 +91,7 @@ public: * Get the legacy value for this section. * * @return The retrieved value. A section with an empty legacy value returns - an empty string. + * an empty string. */ [[nodiscard]] std::string legacy() const @@ -95,28 +106,34 @@ public: return lines_[0]; } - /** Set a key/value pair. - The previous value is discarded. - */ + /** + * Set a key/value pair. + * The previous value is discarded. + */ void set(std::string const& key, std::string const& value); - /** Append a set of lines to this section. - Lines containing key/value pairs are added to the map, - else they are added to the values list. Everything is - added to the lines list. - */ + /** + * Append a set of lines to this section. + * Lines containing key/value pairs are added to the map, + * else they are added to the values list. Everything is + * added to the lines list. + */ void append(std::vector const& lines); - /** Append a line to this section. */ + /** + * Append a line to this section. + */ void append(std::string const& line) { append(std::vector{line}); } - /** Returns `true` if a key with the given name exists. */ + /** + * Returns `true` if a key with the given name exists. + */ [[nodiscard]] bool exists(std::string const& name) const; @@ -130,7 +147,9 @@ public: return boost::lexical_cast(iter->second); } - /// Returns a value if present, else another value. + /** + * Returns a value if present, else another value. + */ template [[nodiscard]] T valueOr(std::string const& name, T const& other) const @@ -195,23 +214,27 @@ public: //------------------------------------------------------------------------------ -/** Holds unparsed configuration information. - The raw data sections are processed with intermediate parsers specific - to each module instead of being all parsed in a central location. -*/ +/** + * Holds unparsed configuration information. + * The raw data sections are processed with intermediate parsers specific + * to each module instead of being all parsed in a central location. + */ class BasicConfig { private: std::unordered_map map_; public: - /** Returns `true` if a section with the given name exists. */ + /** + * Returns `true` if a section with the given name exists. + */ [[nodiscard]] bool exists(std::string const& name) const; - /** Returns the section with the given name. - If the section does not exist, an empty section is returned. - */ + /** + * Returns the section with the given name. + * If the section does not exist, an empty section is returned. + */ /** @{ */ Section& section(std::string const& name); @@ -232,37 +255,39 @@ public: } /** @} */ - /** Overwrite a key/value pair with a command line argument - If the section does not exist it is created. - The previous value, if any, is overwritten. - */ + /** + * Overwrite a key/value pair with a command line argument + * If the section does not exist it is created. + * The previous value, if any, is overwritten. + */ void overwrite(std::string const& section, std::string const& key, std::string const& value); - /** Remove all the key/value pairs from the section. + /** + * Remove all the key/value pairs from the section. */ void deprecatedClearSection(std::string const& section); /** - * Set a value that is not a key/value pair. + * Set a value that is not a key/value pair. * - * The value is stored as the section's first value and may be retrieved - * through section::legacy. + * The value is stored as the section's first value and may be retrieved + * through section::legacy. * - * @param section Name of the section to modify. - * @param value Contents of the legacy value. + * @param section Name of the section to modify. + * @param value Contents of the legacy value. */ void legacy(std::string const& section, std::string value); /** - * Get the legacy value of a section. A section with a - * single-line value may be retrieved as a legacy value. + * Get the legacy value of a section. A section with a + * single-line value may be retrieved as a legacy value. * - * @param sectionName Retrieve the contents of this section's - * legacy value. - * @return Contents of the legacy value. + * @param sectionName Retrieve the contents of this section's + * legacy value. + * @return Contents of the legacy value. */ [[nodiscard]] std::string legacy(std::string const& sectionName) const; @@ -285,11 +310,12 @@ protected: //------------------------------------------------------------------------------ -/** Set a value from a configuration Section - If the named value is not found or doesn't parse as a T, - the variable is unchanged. - @return `true` if value was set. -*/ +/** + * Set a value from a configuration Section + * If the named value is not found or doesn't parse as a T, + * the variable is unchanged. + * @return `true` if value was set. + */ template bool set(T& target, std::string const& name, Section const& section) @@ -298,7 +324,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) @@ -307,11 +334,12 @@ set(T& target, std::string const& name, Section const& section) return foundAndValid; } -/** Set a value from a configuration Section - If the named value is not found or doesn't cast to T, - the variable is assigned the default. - @return `true` if the named value was found and is valid. -*/ +/** + * Set a value from a configuration Section + * If the named value is not found or doesn't cast to T, + * the variable is assigned the default. + * @return `true` if the named value was found and is valid. + */ template bool set(T& target, T const& defaultValue, std::string const& name, Section const& section) @@ -322,10 +350,11 @@ set(T& target, T const& defaultValue, std::string const& name, Section const& se return foundAndValid; } -/** Retrieve a key/value pair from a section. - @return The value string converted to T if it exists - and can be parsed, or else defaultValue. -*/ +/** + * Retrieve a key/value pair from a section. + * @return The value string converted to T if it exists + * and can be parsed, or else defaultValue. + */ // NOTE This routine might be more clumsy than the previous two template T diff --git a/include/xrpl/core/ClosureCounter.h b/include/xrpl/core/ClosureCounter.h index fb13047f40..33899d671b 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 { @@ -27,8 +30,8 @@ namespace xrpl { * the caller that they should drop the closure and cancel their operation. * `join` blocks until all existing closure substitutes are destroyed. * - * \tparam Ret The return type of the closure. - * \tparam Args The argument types of the closure. + * @tparam Ret The return type of the closure. + * @tparam Args The argument types of the closure. */ template class ClosureCounter @@ -128,18 +131,21 @@ public: ClosureCounter& operator=(ClosureCounter const&) = delete; - /** Destructor verifies all in-flight closures are complete. */ + /** + * Destructor verifies all in-flight closures are complete. + */ ~ClosureCounter() { using namespace std::chrono_literals; join("ClosureCounter", 1s, debugLog()); } - /** Returns once all counted in-flight closures are destroyed. - - @param name Name reported if join time exceeds wait. - @param wait If join() exceeds this duration report to Journal. - @param j Journal written to if wait is exceeded. + /** + * Returns once all counted in-flight closures are destroyed. + * + * @param name Name reported if join time exceeds wait. + * @param wait If join() exceeds this duration report to Journal. + * @param j Journal written to if wait is exceeded. */ void join(char const* name, std::chrono::milliseconds wait, beast::Journal j) @@ -157,13 +163,14 @@ public: } } - /** Wrap the passed closure with a reference counter. - - @param closure Closure that accepts Args parameters and returns Ret. - @return If join() has been called returns std::nullopt. Otherwise - returns a std::optional that wraps closure with a - reference counter. - */ + /** + * Wrap the passed closure with a reference counter. + * + * @param closure Closure that accepts Args parameters and returns Ret. + * @return If join() has been called returns std::nullopt. Otherwise + * returns a std::optional that wraps closure with a + * reference counter. + */ template std::optional> wrap(Closure&& closure) @@ -177,19 +184,22 @@ public: return ret; } - /** Current number of Closures outstanding. Only useful for testing. */ + /** + * Current number of Closures outstanding. Only useful for testing. + */ int count() const { return closureCount_; } - /** Returns true if this has been joined. - - Even if true is returned, counted closures may still be in flight. - However if (joined() && (count() == 0)) there should be no more - counted closures in flight. - */ + /** + * Returns true if this has been joined. + * + * Even if true is returned, counted closures may still be in flight. + * However if (joined() && (count() == 0)) there should be no more + * counted closures in flight. + */ bool joined() const { diff --git a/include/xrpl/core/Coro.ipp b/include/xrpl/core/Coro.ipp index 133caf37a9..9a45dac504 100644 --- a/include/xrpl/core/Coro.ipp +++ b/include/xrpl/core/Coro.ipp @@ -4,8 +4,10 @@ namespace xrpl { -/// Coroutine stack size (1.5 MB). Increased from 1 MB because -/// ASAN-instrumented deep call stacks exceeded the original limit. +/** + * Coroutine stack size (1.5 MB). Increased from 1 MB because + * ASAN-instrumented deep call stacks exceeded the original limit. + */ constexpr std::size_t kCoroStackSize = 1536 * 1024; template diff --git a/include/xrpl/core/HashRouter.h b/include/xrpl/core/HashRouter.h index d36b8aee6e..20aafecc5f 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 }; @@ -67,19 +75,21 @@ any(HashRouterFlags flags) class Config; -/** Routing table for objects identified by hash. - - This table keeps track of which hashes have been received by which peers. - It is used to manage the routing and broadcasting of messages in the peer - to peer overlay. -*/ +/** + * Routing table for objects identified by hash. + * + * This table keeps track of which hashes have been received by which peers. + * It is used to manage the routing and broadcasting of messages in the peer + * to peer overlay. + */ class HashRouter { public: // The type here *MUST* match the type of Peer::id_t using PeerShortID = std::uint32_t; - /** Structure used to customize @ref HashRouter behavior. + /** + * Structure used to customize @ref HashRouter behavior. * * Even though these items are configurable, they are undocumented. Don't * change them unless there is a good reason, and network-wide coordination @@ -89,22 +99,27 @@ public: */ struct Setup { - /// Default constructor + /** + * Default constructor + */ explicit Setup() = default; using seconds = std::chrono::seconds; - /** Expiration time for a hash entry + /** + * Expiration time for a hash entry */ seconds holdTime{300}; - /** Amount of time required before a relayed item will be relayed again. + /** + * Amount of time required before a relayed item will be relayed again. */ seconds relayTime{30}; }; private: - /** An entry in the routing table. + /** + * An entry in the routing table. */ class Entry : public CountedObject { @@ -119,7 +134,7 @@ private: } [[nodiscard]] HashRouterFlags - getFlags(void) const + getFlags() const { return flags_; } @@ -130,26 +145,31 @@ private: flags_ |= flagsToSet; } - /** Return set of peers we've relayed to and reset tracking */ + /** + * Return set of peers we've relayed to and reset tracking + */ std::set releasePeerSet() { return std::move(peers_); } - /** Return seated relay time point if the message has been relayed */ + /** + * Return seated relay time point if the message has been relayed + */ [[nodiscard]] std::optional relayed() const { return relayed_; } - /** Determines if this item should be relayed. - - Checks whether the item has been recently relayed. - If it has, return false. If it has not, update the - last relay timestamp and return true. - */ + /** + * Determines if this item should be relayed. + * + * Checks whether the item has been recently relayed. + * If it has, return false. If it has not, update the + * last relay timestamp and return true. + */ bool shouldRelay(Stopwatch::time_point const& now, std::chrono::seconds relayTime) { @@ -195,11 +215,13 @@ public: bool addSuppressionPeer(uint256 const& key, PeerShortID peer); - /** Add a suppression peer and get message's relay status. + /** + * Add a suppression peer and get message's relay status. * Return pair: * element 1: true if the peer is added. * element 2: optional is seated to the relay time point or - * is unseated if has not relayed yet. */ + * is unseated if has not relayed yet. + */ std::pair> addSuppressionPeerWithStatus(uint256 const& key, PeerShortID peer); @@ -214,28 +236,30 @@ public: HashRouterFlags& flags, std::chrono::seconds txInterval); - /** Set the flags on a hash. - - @return `true` if the flags were changed. `false` if unchanged. - */ + /** + * Set the flags on a hash. + * + * @return `true` if the flags were changed. `false` if unchanged. + */ bool setFlags(uint256 const& key, HashRouterFlags flags); HashRouterFlags getFlags(uint256 const& key); - /** Determines whether the hashed item should be relayed. - - Effects: - - If the item should be relayed, this function will not - return a seated optional again until the relay time has expired. - The internal set of peers will also be reset. - - @return A `std::optional` set of peers which do not need to be - relayed to. If the result is unseated, the item should - _not_ be relayed. - */ + /** + * Determines whether the hashed item should be relayed. + * + * Effects: + * + * If the item should be relayed, this function will not + * return a seated optional again until the relay time has expired. + * The internal set of peers will also be reset. + * + * @return A `std::optional` set of peers which do not need to be + * relayed to. If the result is unseated, the item should + * _not_ be relayed. + */ std::optional> shouldRelay(uint256 const& key); diff --git a/include/xrpl/core/Job.h b/include/xrpl/core/Job.h index 6af32eb2d8..93b39701be 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 { @@ -78,12 +83,13 @@ class Job : public CountedObject public: using clock_type = std::chrono::steady_clock; - /** Default constructor. - - Allows Job to be used as a container type. - - This is used to allow things like jobMap [key] = value. - */ + /** + * Default constructor. + * + * Allows Job to be used as a container type. + * + * This is used to allow things like jobMap [key] = value. + */ // VFALCO NOTE I'd prefer not to have a default constructed object. // What is the semantic meaning of a Job with no associated // function? Having the invariant "all Job objects refer to @@ -103,7 +109,9 @@ public: [[nodiscard]] JobType getType() const; - /** Returns the time when the job was queued. */ + /** + * Returns the time when the job was queued. + */ [[nodiscard]] clock_type::time_point const& queueTime() const; diff --git a/include/xrpl/core/JobQueue.h b/include/xrpl/core/JobQueue.h index fc15e9a064..0c9fc76357 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 { @@ -29,20 +45,23 @@ struct CoroCreateT explicit CoroCreateT() = default; }; -/** A pool of threads to perform work. - - A job posted will always run to completion. - - Coroutines that are suspended must be resumed, - and run to completion. - - When the JobQueue stops, it waits for all jobs - and coroutines to finish. -*/ +/** + * A pool of threads to perform work. + * + * A job posted will always run to completion. + * + * Coroutines that are suspended must be resumed, + * and run to completion. + * + * When the JobQueue stops, it waits for all jobs + * and coroutines to finish. + */ class JobQueue : private Workers::Callback { public: - /** Coroutines must run to completion. */ + /** + * Coroutines must run to completion. + */ class Coro : public std::enable_shared_from_this { private: @@ -71,55 +90,64 @@ public: ~Coro(); - /** Suspend coroutine execution. - Effects: - The coroutine's stack is saved. - The associated Job thread is released. - Note: - The associated Job function returns. - Undefined behavior if called consecutively without a corresponding - post. - */ + /** + * Suspend coroutine execution. + * Effects: + * The coroutine's stack is saved. + * The associated Job thread is released. + * Note: + * The associated Job function returns. + * Undefined behavior if called consecutively without a corresponding + * post. + */ void yield() const; - /** Schedule coroutine execution. - Effects: - Returns immediately. - A new job is scheduled to resume the execution of the coroutine. - When the job runs, the coroutine's stack is restored and execution - continues at the beginning of coroutine function or the - statement after the previous call to yield. Undefined behavior if - called after the coroutine has completed with a return (as opposed to - a yield()). Undefined behavior if post() or resume() called - consecutively without a corresponding yield. - - @return true if the Coro's job is added to the JobQueue. - */ + /** + * Schedule coroutine execution. + * Effects: + * Returns immediately. + * A new job is scheduled to resume the execution of the coroutine. + * When the job runs, the coroutine's stack is restored and execution + * continues at the beginning of coroutine function or the + * statement after the previous call to yield. Undefined behavior if + * called after the coroutine has completed with a return (as opposed to + * a yield()). Undefined behavior if post() or resume() called + * consecutively without a corresponding yield. + * + * @return true if the Coro's job is added to the JobQueue. + */ bool post(); - /** Resume coroutine execution. - Effects: - The coroutine continues execution from where it last left off - using this same thread. - If the coroutine has already completed, returns immediately - (handles the documented post-before-yield race condition). - Undefined behavior if resume() or post() called consecutively - without a corresponding yield. - */ + /** + * Resume coroutine execution. + * Effects: + * The coroutine continues execution from where it last left off + * using this same thread. + * If the coroutine has already completed, returns immediately + * (handles the documented post-before-yield race condition). + * Undefined behavior if resume() or post() called consecutively + * without a corresponding yield. + */ void resume(); - /** Returns true if the Coro is still runnable (has not returned). */ - bool + /** + * Returns true if the Coro is still runnable (has not returned). + */ + [[nodiscard]] bool runnable() const; - /** Once called, the Coro allows early exit without an assert. */ + /** + * Once called, the Coro allows early exit without an assert. + */ void expectEarlyExit(); - /** Waits until coroutine returns from the user function. */ + /** + * Waits until coroutine returns from the user function. + */ void join(); }; @@ -134,20 +162,19 @@ public: perf::PerfLog& perfLog); ~JobQueue() override; - /** Adds a job to the JobQueue. - - @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. - - @return true if jobHandler added to queue. - */ - template < - typename JobHandler, - typename = std::enable_if_t()()), void>>> + /** + * Adds a job to the JobQueue. + * + * @param type The type of job. + * @param name Name of the job. + * @param jobHandler Callable with signature void(). Called when the job is executed. + * + * @return true if jobHandler added to queue. + */ + template bool addJob(JobType type, std::string const& name, JobHandler&& jobHandler) + requires(std::is_void_v>) { if (auto optionalCountedJob = jobCounter_.wrap(std::forward(jobHandler))) { @@ -156,40 +183,46 @@ public: return false; } - /** Creates a coroutine and adds a job to the queue which will run it. - - @param t The type of job. - @param name Name of the job. - @param f Has a signature of void(std::shared_ptr). Called when the - job executes. - - @return shared_ptr to posted Coro. nullptr if post was not successful. - */ + /** + * Creates a coroutine and adds a job to the queue which will run it. + * + * @param t The type of job. + * @param name Name of the job. + * @param f Has a signature of void(std::shared_ptr). Called when the + * job executes. + * + * @return shared_ptr to posted Coro. nullptr if post was not successful. + */ template std::shared_ptr postCoro(JobType t, std::string const& name, F&& f); - /** Jobs waiting at this priority. + /** + * Jobs waiting at this priority. */ int getJobCount(JobType t) const; - /** Jobs waiting plus running at this priority. + /** + * Jobs waiting plus running at this priority. */ int getJobCountTotal(JobType t) const; - /** All waiting jobs at or greater than this priority. + /** + * All waiting jobs at or greater than this priority. */ int getJobCountGE(JobType t) const; - /** Return a scoped LoadEvent. + /** + * Return a scoped LoadEvent. */ std::unique_ptr makeLoadEvent(JobType t, std::string const& name); - /** Add multiple load events. + /** + * Add multiple load events. */ void addLoadEvents(JobType t, int count, std::chrono::milliseconds elapsed); @@ -202,7 +235,9 @@ public: json::Value getJson(int c = 0); - /** Block until no jobs running. */ + /** + * Block until no jobs running. + */ void rendezvous(); @@ -384,7 +419,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..302a462ac6 100644 --- a/include/xrpl/core/JobTypeInfo.h +++ b/include/xrpl/core/JobTypeInfo.h @@ -2,23 +2,32 @@ #include +#include +#include +#include + namespace xrpl { -/** Holds all the 'static' information about a job, which does not change */ +/** + * Holds all the 'static' information about a job, which does not change + */ class JobTypeInfo { private: JobType const type_; std::string const name_; - /** The limit on the number of running jobs for this job type. - - A limit of 0 marks this as a "special job" which is not - dispatched via the job queue. + /** + * The limit on the number of running jobs for this job type. + * + * A limit of 0 marks this as a "special job" which is not + * dispatched via the job queue. */ int const limit_; - /** Average and peak latencies for this job type. 0 is none specified */ + /** + * Average and peak latencies for this job type. 0 is none specified + */ std::chrono::milliseconds const avgLatency_; std::chrono::milliseconds const peakLatency_; 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/NetworkIDService.h b/include/xrpl/core/NetworkIDService.h index 009f9ba6f8..8e2b3fcfe2 100644 --- a/include/xrpl/core/NetworkIDService.h +++ b/include/xrpl/core/NetworkIDService.h @@ -4,25 +4,27 @@ namespace xrpl { -/** Service that provides access to the network ID. - - This service provides read-only access to the network ID configured - for this server. The network ID identifies which network (mainnet, - testnet, devnet, or custom network) this server is configured to - connect to. - - Well-known network IDs: - - 0: Mainnet - - 1: Testnet - - 2: Devnet - - 1025+: Custom networks (require NetworkID field in transactions) -*/ +/** + * Service that provides access to the network ID. + * + * This service provides read-only access to the network ID configured + * for this server. The network ID identifies which network (mainnet, + * testnet, devnet, or custom network) this server is configured to + * connect to. + * + * Well-known network IDs: + * - 0: Mainnet + * - 1: Testnet + * - 2: Devnet + * - 1025+: Custom networks (require NetworkID field in transactions) + */ class NetworkIDService { public: virtual ~NetworkIDService() = default; - /** Get the configured network ID + /** + * Get the configured network ID * * @return The network ID this server is configured for */ diff --git a/include/xrpl/core/PeerReservationTable.h b/include/xrpl/core/PeerReservationTable.h index a9ab894124..c95c88b967 100644 --- a/include/xrpl/core/PeerReservationTable.h +++ b/include/xrpl/core/PeerReservationTable.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -80,7 +81,7 @@ public: /** * @return the replaced reservation if it existed - * @throw soci::soci_error + * @throws soci::soci_error */ std::optional insertOrAssign(PeerReservation const& reservation); diff --git a/include/xrpl/core/PerfLog.h b/include/xrpl/core/PerfLog.h index ca0d9333a4..f09665e291 100644 --- a/include/xrpl/core/PerfLog.h +++ b/include/xrpl/core/PerfLog.h @@ -1,6 +1,7 @@ #pragma once -#include +#include +#include #include #include diff --git a/include/xrpl/core/ServiceRegistry.h b/include/xrpl/core/ServiceRegistry.h index 1d0c9e38f4..592964134b 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 @@ -77,17 +83,17 @@ using RCLValidations = Validations; using NodeCache = TaggedCache; -/** Service registry for dependency injection. - - This abstract interface provides access to various services and components - used throughout the application. It separates the service locator pattern - from the Application lifecycle management. - - Components that need access to services can hold a reference to - ServiceRegistry rather than Application when they only need service - access and not lifecycle management. - -*/ +/** + * Service registry for dependency injection. + * + * This abstract interface provides access to various services and components + * used throughout the application. It separates the service locator pattern + * from the Application lifecycle management. + * + * Components that need access to services can hold a reference to + * ServiceRegistry rather than Application when they only need service + * access and not lifecycle management. + */ class ServiceRegistry { public: @@ -234,7 +240,9 @@ public: [[nodiscard]] virtual std::optional const& getTrapTxID() const = 0; - /** Retrieve the "wallet database" */ + /** + * Retrieve the "wallet database" + */ virtual DatabaseCon& getWalletDB() = 0; 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/Workers.h b/include/xrpl/core/detail/Workers.h index d20ebf7a64..6829d5a14b 100644 --- a/include/xrpl/core/detail/Workers.h +++ b/include/xrpl/core/detail/Workers.h @@ -60,7 +60,9 @@ class PerfLog; class Workers { public: - /** Called to perform tasks as needed. */ + /** + * Called to perform tasks as needed. + */ struct Callback { virtual ~Callback() = default; @@ -69,27 +71,29 @@ public: Callback& operator=(Callback const&) = delete; - /** Perform a task. - - The call is made on a thread owned by Workers. It is important - that you only process one task from inside your callback. Each - call to addTask will result in exactly one call to processTask. - - @param instance The worker thread instance. - - @see Workers::addTask - */ + /** + * Perform a task. + * + * The call is made on a thread owned by Workers. It is important + * that you only process one task from inside your callback. Each + * call to addTask will result in exactly one call to processTask. + * + * @param instance The worker thread instance. + * + * @see Workers::addTask + */ virtual void processTask(int instance) = 0; }; - /** Create the object. - - A number of initial threads may be optionally specified. The - default is to create one thread per CPU. - - @param threadNames The name given to each created worker thread. - */ + /** + * Create the object. + * + * A number of initial threads may be optionally specified. The + * default is to create one thread per CPU. + * + * @param threadNames The name given to each created worker thread. + */ explicit Workers( Callback& callback, perf::PerfLog* perfLog, @@ -98,49 +102,54 @@ public: ~Workers(); - /** Retrieve the desired number of threads. - - This just returns the number of active threads that were requested. If - there was a recent call to setNumberOfThreads, the actual number of - active threads may be temporarily different from what was last requested. - - @note This function is not thread-safe. - */ + /** + * Retrieve the desired number of threads. + * + * This just returns the number of active threads that were requested. If + * there was a recent call to setNumberOfThreads, the actual number of + * active threads may be temporarily different from what was last requested. + * + * @note This function is not thread-safe. + */ [[nodiscard]] int getNumberOfThreads() const noexcept; - /** Set the desired number of threads. - @note This function is not thread-safe. - */ + /** + * Set the desired number of threads. + * @note This function is not thread-safe. + */ void setNumberOfThreads(int numberOfThreads); - /** Pause all threads and wait until they are paused. - - If a thread is processing a task it will pause as soon as the task - completes. There may still be tasks signaled even after all threads - have paused. - - @note This function is not thread-safe. - */ + /** + * Pause all threads and wait until they are paused. + * + * If a thread is processing a task it will pause as soon as the task + * completes. There may still be tasks signaled even after all threads + * have paused. + * + * @note This function is not thread-safe. + */ void stop(); - /** Add a task to be performed. - - Every call to addTask will eventually result in a call to - Callback::processTask unless the Workers object is destroyed or - the number of threads is never set above zero. - - @note This function is thread-safe. - */ + /** + * Add a task to be performed. + * + * Every call to addTask will eventually result in a call to + * Callback::processTask unless the Workers object is destroyed or + * the number of threads is never set above zero. + * + * @note This function is thread-safe. + */ void addTask(); - /** Get the number of currently executing calls of Callback::processTask. - While this function is thread-safe, the value may not stay - accurate for very long. It's mainly for diagnostic purposes. - */ + /** + * Get the number of currently executing calls of Callback::processTask. + * While this function is thread-safe, the value may not stay + * accurate for very long. It's mainly for diagnostic purposes. + */ [[nodiscard]] int numberOfCurrentlyRunningTasks() const noexcept; diff --git a/include/xrpl/core/detail/semaphore.h b/include/xrpl/core/detail/semaphore.h index 7bc83f86f5..abf6705097 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 { @@ -44,14 +45,17 @@ private: public: using size_type = std::size_t; - /** Create the semaphore, with an optional initial count. - If unspecified, the initial count is zero. - */ + /** + * Create the semaphore, with an optional initial count. + * If unspecified, the initial count is zero. + */ explicit BasicSemaphore(size_type count = 0) : count_(count) { } - /** Increment the count and unblock one waiting thread. */ + /** + * Increment the count and unblock one waiting thread. + */ void notify() { @@ -60,7 +64,9 @@ public: cond_.notify_one(); } - /** Block until notify is called. */ + /** + * Block until notify is called. + */ void wait() { @@ -70,9 +76,10 @@ public: --count_; } - /** Perform a non-blocking wait. - @return `true` If the wait would be satisfied. - */ + /** + * Perform a non-blocking wait. + * @return `true` If the wait would be satisfied. + */ bool tryWait() { diff --git a/include/xrpl/crypto/RFC1751.h b/include/xrpl/crypto/RFC1751.h index 19b636b9dc..3de65c3028 100644 --- a/include/xrpl/crypto/RFC1751.h +++ b/include/xrpl/crypto/RFC1751.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -15,13 +16,14 @@ public: static void getEnglishFromKey(std::string& strHuman, std::string const& strKey); - /** Chooses a single dictionary word from the data. - - This is not particularly secure but it can be useful to provide - a unique name for something given a GUID or fixed data. We use - it to turn the pubkey_node into an easily remembered and identified - 4 character string. - */ + /** + * Chooses a single dictionary word from the data. + * + * This is not particularly secure but it can be useful to provide + * a unique name for something given a GUID or fixed data. We use + * it to turn the pubkey_node into an easily remembered and identified + * 4 character string. + */ static std::string getWordFromBlob(void const* blob, size_t bytes); diff --git a/include/xrpl/crypto/csprng.h b/include/xrpl/crypto/csprng.h index e386d9d11e..e19d33a464 100644 --- a/include/xrpl/crypto/csprng.h +++ b/include/xrpl/crypto/csprng.h @@ -1,17 +1,21 @@ #pragma once +#include +#include +#include #include namespace xrpl { -/** A cryptographically secure random number engine - - The engine is thread-safe (it uses a lock to serialize - access) and will, automatically, mix in some randomness - from std::random_device. - - Meets the requirements of UniformRandomNumberEngine -*/ +/** + * A cryptographically secure random number engine + * + * The engine is thread-safe (it uses a lock to serialize + * access) and will, automatically, mix in some randomness + * from std::random_device. + * + * Meets the requirements of UniformRandomNumberEngine + */ class CsprngEngine { private: @@ -31,15 +35,21 @@ public: CsprngEngine(); ~CsprngEngine(); - /** Mix entropy into the pool */ + /** + * Mix entropy into the pool + */ void mixEntropy(void* buffer = nullptr, std::size_t count = 0); - /** Generate a random integer */ + /** + * Generate a random integer + */ result_type operator()(); - /** Fill a buffer with the requested amount of random data */ + /** + * Fill a buffer with the requested amount of random data + */ void operator()(void* ptr, std::size_t count); @@ -58,14 +68,15 @@ public: } }; -/** The default cryptographically secure PRNG - - Use this when you need to generate random numbers or - data that will be used for encryption or passed into - cryptographic routines. - - This meets the requirements of UniformRandomNumberEngine -*/ +/** + * The default cryptographically secure PRNG + * + * Use this when you need to generate random numbers or + * data that will be used for encryption or passed into + * cryptographic routines. + * + * This meets the requirements of UniformRandomNumberEngine + */ CsprngEngine& cryptoPrng(); diff --git a/include/xrpl/crypto/secure_erase.h b/include/xrpl/crypto/secure_erase.h index 74284b03f7..38531afc1d 100644 --- a/include/xrpl/crypto/secure_erase.h +++ b/include/xrpl/crypto/secure_erase.h @@ -4,20 +4,21 @@ namespace xrpl { -/** Attempts to clear the given blob of memory. - - The underlying implementation of this function takes pains to - attempt to outsmart the compiler from optimizing the clearing - away. Please note that, despite that, remnants of content may - remain floating around in memory as well as registers, caches - and more. - - For a more in-depth discussion of the subject please see the - below posts by Colin Percival: - - http://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html - http://www.daemonology.net/blog/2014-09-06-zeroing-buffers-is-insufficient.html -*/ +/** + * Attempts to clear the given blob of memory. + * + * The underlying implementation of this function takes pains to + * attempt to outsmart the compiler from optimizing the clearing + * away. Please note that, despite that, remnants of content may + * remain floating around in memory as well as registers, caches + * and more. + * + * For a more in-depth discussion of the subject please see the + * below posts by Colin Percival: + * + * http://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html + * http://www.daemonology.net/blog/2014-09-06-zeroing-buffers-is-insufficient.html + */ void secureErase(void* dest, std::size_t bytes); diff --git a/include/xrpl/json/JsonPropertyStream.h b/include/xrpl/json/JsonPropertyStream.h index 47317b9ddb..498283c16b 100644 --- a/include/xrpl/json/JsonPropertyStream.h +++ b/include/xrpl/json/JsonPropertyStream.h @@ -3,9 +3,14 @@ #include #include +#include +#include + namespace xrpl { -/** A PropertyStream::Sink which produces a json::Value of type ValueType::Object. */ +/** + * A PropertyStream::Sink which produces a json::Value of type ValueType::Object. + */ class JsonPropertyStream : public beast::PropertyStream { public: diff --git a/include/xrpl/json/Output.h b/include/xrpl/json/Output.h index c01253f713..53d453c277 100644 --- a/include/xrpl/json/Output.h +++ b/include/xrpl/json/Output.h @@ -17,18 +17,20 @@ stringOutput(std::string& s) return [&](boost::beast::string_view const& b) { s.append(b.data(), b.size()); }; } -/** Writes a minimal representation of a Json value to an Output in O(n) time. - - Data is streamed right to the output, so only a marginal amount of memory is - used. This can be very important for a very large json::Value. +/** + * Writes a minimal representation of a Json value to an Output in O(n) time. + * + * Data is streamed right to the output, so only a marginal amount of memory is + * used. This can be very important for a very large json::Value. */ void outputJson(json::Value const&, Output const&); -/** Return the minimal string representation of a json::Value in O(n) time. - - This requires a memory allocation for the full size of the output. - If possible, use outputJson(). +/** + * Return the minimal string representation of a json::Value in O(n) time. + * + * This requires a memory allocation for the full size of the output. + * If possible, use outputJson(). */ std::string jsonAsString(json::Value const&); diff --git a/include/xrpl/json/Writer.h b/include/xrpl/json/Writer.h index 87e3e99c7e..ec7fd6a0d2 100644 --- a/include/xrpl/json/Writer.h +++ b/include/xrpl/json/Writer.h @@ -1,107 +1,109 @@ #pragma once -#include #include #include #include +#include #include +#include +#include namespace json { /** - * Writer implements an O(1)-space, O(1)-granular output JSON writer. + * Writer implements an O(1)-space, O(1)-granular output JSON writer. * - * O(1)-space means that it uses a fixed amount of memory, and that there are - * no heap allocations at each step of the way. + * O(1)-space means that it uses a fixed amount of memory, and that there are + * no heap allocations at each step of the way. * - * O(1)-granular output means the writer only outputs in small segments of a - * bounded size, using a bounded number of CPU cycles in doing so. This is - * very helpful in scheduling long jobs. + * O(1)-granular output means the writer only outputs in small segments of a + * bounded size, using a bounded number of CPU cycles in doing so. This is + * very helpful in scheduling long jobs. * - * The tradeoff is that you have to fill items in the JSON tree as you go, - * and you can never go backward. + * The tradeoff is that you have to fill items in the JSON tree as you go, + * and you can never go backward. * - * Writer can write single JSON tokens, but the typical use is to write out an - * entire JSON object. For example: + * Writer can write single JSON tokens, but the typical use is to write out an + * entire JSON object. For example: * - * { - * Writer w (out); + * { + * Writer w (out); * - * w.startObject (); // Start the root object. - * w.set ("hello", "world"); - * w.set ("goodbye", 23); - * w.finishObject (); // Finish the root object. - * } + * w.startObject (); // Start the root object. + * w.set ("hello", "world"); + * w.set ("goodbye", 23); + * w.finishObject (); // Finish the root object. + * } * - * which outputs the string + * which outputs the string * - * {"hello":"world","goodbye":23} + * {"hello":"world","goodbye":23} * - * There can be an object inside an object: + * There can be an object inside an object: * - * { - * Writer w (out); + * { + * Writer w (out); * - * w.startObject (); // Start the root object. - * w.set ("hello", "world"); + * w.startObject (); // Start the root object. + * w.set ("hello", "world"); * - * w.startObjectSet ("subobject"); // Start a sub-object. - * w.set ("goodbye", 23); // Add a key, value assignment. - * w.finishObject (); // Finish the sub-object. + * w.startObjectSet ("subobject"); // Start a sub-object. + * w.set ("goodbye", 23); // Add a key, value assignment. + * w.finishObject (); // Finish the sub-object. * - * w.finishObject (); // Finish the root-object. - * } + * w.finishObject (); // Finish the root-object. + * } * - * which outputs the string + * which outputs the string * - * {"hello":"world","subobject":{"goodbye":23}}. + * {"hello":"world","subobject":{"goodbye":23}}. * - * Arrays work similarly + * Arrays work similarly * - * { - * Writer w (out); - * w.startObject (); // Start the root object. + * { + * Writer w (out); + * w.startObject (); // Start the root object. * - * w.startArraySet ("hello"); // Start an array. - * w.append (23) // Append some items. - * w.append ("skidoo") - * w.finishArray (); // Finish the array. + * w.startArraySet ("hello"); // Start an array. + * w.append (23) // Append some items. + * w.append ("skidoo") + * w.finishArray (); // Finish the array. * - * w.finishObject (); // Finish the root object. - * } + * w.finishObject (); // Finish the root object. + * } * - * which outputs the string + * which outputs the string * - * {"hello":[23,"skidoo"]}. + * {"hello":[23,"skidoo"]}. * * - * If you've reached the end of a long object, you can just use finishAll() - * which finishes all arrays and objects that you have started. + * If you've reached the end of a long object, you can just use finishAll() + * which finishes all arrays and objects that you have started. * - * { - * Writer w (out); - * w.startObject (); // Start the root object. + * { + * Writer w (out); + * w.startObject (); // Start the root object. * - * w.startArraySet ("hello"); // Start an array. - * w.append (23) // Append an item. + * w.startArraySet ("hello"); // Start an array. + * w.append (23) // Append an item. * - * w.startArrayAppend () // Start a sub-array. - * w.append ("one"); - * w.append ("two"); + * w.startArrayAppend () // Start a sub-array. + * w.append ("one"); + * w.append ("two"); * - * w.startObjectAppend (); // Append a sub-object. - * w.finishAll (); // Finish everything. - * } + * w.startObjectAppend (); // Append a sub-object. + * w.finishAll (); // Finish everything. + * } * - * which outputs the string + * which outputs the string * - * {"hello":[23,["one","two",{}]]}. + * {"hello":[23,["one","two",{}]]}. * - * For convenience, the destructor of Writer calls w.finishAll() which makes - * sure that all arrays and objects are closed. This means that you can throw - * an exception, or have a coroutine simply clean up the stack, and be sure - * that you do in fact generate a complete JSON object. + * For convenience, the destructor of Writer calls w.finishAll() which makes + * sure that all arrays and objects are closed. This means that you can throw + * an exception, or have a coroutine simply clean up the stack, and be sure + * that you do in fact generate a complete JSON object. */ class Writer @@ -116,26 +118,37 @@ public: ~Writer(); - /** Start a new collection at the root level. */ + /** + * Start a new collection at the root level. + */ void startRoot(CollectionType); - /** Start a new collection inside an array. */ + /** + * Start a new collection inside an array. + */ void startAppend(CollectionType); - /** Start a new collection inside an object. */ + /** + * Start a new collection inside an object. + */ void startSet(CollectionType, std::string const& key); - /** Finish the collection most recently started. */ + /** + * Finish the collection most recently started. + */ void finish(); - /** Finish all objects and arrays. After finishArray() has been called, no - * more operations can be performed. */ + /** + * Finish all objects and arrays. After finishArray() has been called, no + * more operations can be performed. + */ void finishAll(); - /** Append a value to an array. + /** + * Append a value to an array. * * Scalar must be a scalar - that is, a number, boolean, string, string * literal, nullptr or json::Value @@ -148,12 +161,15 @@ public: output(t); } - /** Add a comma before this next item if not the first item in an array. - Useful if you are writing the actual array yourself. */ + /** + * Add a comma before this next item if not the first item in an array. + * Useful if you are writing the actual array yourself. + */ void rawAppend(); - /** Add a key, value assignment to an object. + /** + * Add a key, value assignment to an object. * * Scalar must be a scalar - that is, a number, boolean, string, string * literal, or nullptr. @@ -172,8 +188,10 @@ public: output(t); } - /** Emit just "tag": as part of an object. Useful if you are writing the - actual value data yourself. */ + /** + * Emit just "tag": as part of an object. Useful if you are writing the + * actual value data yourself. + */ void rawSet(std::string const& key); @@ -192,22 +210,32 @@ public: void output(json::Value const&); - /** Output a null. */ + /** + * Output a null. + */ void output(std::nullptr_t); - /** Output a float. */ + /** + * Output a float. + */ void output(float); - /** Output a double. */ + /** + * Output a double. + */ void output(double); - /** Output a bool. */ + /** + * Output a bool. + */ void output(bool); - /** Output numbers or booleans. */ + /** + * Output numbers or booleans. + */ template void output(Type t) 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..ed60f49ce4 100644 --- a/include/xrpl/json/json_reader.h +++ b/include/xrpl/json/json_reader.h @@ -5,13 +5,16 @@ #include +#include +#include #include +#include namespace json { -/** \brief Unserialize a JSON document into a +/** + * @brief Unserialize a JSON document into a * Value. - * */ class Reader { @@ -19,48 +22,55 @@ public: using Char = char; using Location = Char const*; - /** \brief Constructs a Reader allowing all features + /** + * @brief Constructs a Reader allowing all features * for parsing. */ Reader() = default; - /** \brief Read a Value from a JSON - * document. \param document UTF-8 encoded string containing the document to - * read. \param root [out] Contains the root value of the document if it was + /** + * @brief Read a Value from a JSON + * document. @param document UTF-8 encoded string containing the document to + * read. @param root [out] Contains the root value of the document if it was * successfully parsed. - * \return \c true if the document was successfully parsed, \c false if an + * @return @c true if the document was successfully parsed, @c false if an * error occurred. */ bool parse(std::string const& document, Value& root); - /** \brief Read a Value from a JSON - * document. \param document UTF-8 encoded string containing the document to - * read. \param root [out] Contains the root value of the document if it was + /** + * @brief Read a Value from a JSON + * document. @param document UTF-8 encoded string containing the document to + * read. @param root [out] Contains the root value of the document if it was * successfully parsed. - * \return \c true if the document was successfully parsed, \c false if an + * @return @c true if the document was successfully parsed, @c false if an * error occurred. */ bool parse(char const* beginDoc, char const* endDoc, Value& root); - /// \brief Parse from input stream. - /// \see json::operator>>(std::istream&, json::Value&). + /** + * @brief Parse from input stream. + * @see json::operator>>(std::istream&, json::Value&). + */ bool parse(std::istream& is, Value& root); - /** \brief Read a Value from a JSON buffer - * sequence. \param root [out] Contains the root value of the document if it - * was successfully parsed. \param UTF-8 encoded buffer sequence. \return \c - * true if the buffer was successfully parsed, \c false if an error + /** + * @brief Read a Value from a JSON buffer + * sequence. @param root [out] Contains the root value of the document if it + * was successfully parsed. @param UTF-8 encoded buffer sequence. @return @c + * true if the buffer was successfully parsed, @c false if an error * occurred. */ template bool parse(Value& root, BufferSequence const& bs); - /** \brief Returns a user friendly string that list errors in the parsed - * document. \return Formatted error message with the list of errors with + /** + * @brief Returns a user friendly string that list errors in the parsed + * document. @return Formatted error message with the list of errors with * their location in the parsed document. An empty string is returned if no * error occurred during parsing. */ @@ -151,7 +161,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 @@ -192,30 +202,31 @@ Reader::parse(Value& root, BufferSequence const& bs) return parse(s, root); } -/** \brief Read from 'sin' into 'root'. - - Always keep comments from the input JSON. - - This can be used to read a file into a particular sub-object. - For example: - \code - json::Value root; - cin >> root["dir"]["file"]; - cout << root; - \endcode - Result: - \verbatim - { -"dir": { - "file": { - // The input stream JSON would be nested here. - } -} - } - \endverbatim - \throw std::exception on parse error. - \see json::operator<<() -*/ +/** + * @brief Read from 'sin' into 'root'. + * + * Always keep comments from the input JSON. + * + * This can be used to read a file into a particular sub-object. + * For example: + * @code + * json::Value root; + * cin >> root["dir"]["file"]; + * cout << root; + * @endcode + * Result: + * @verbatim + * { + * "dir": { + * "file": { + * // The input stream JSON would be nested here. + * } + * } + * } + * @endverbatim + * @throws std::exception on parse error. + * @see json::operator<<() + */ std::istream& operator>>(std::istream&, Value&); diff --git a/include/xrpl/json/json_value.h b/include/xrpl/json/json_value.h index e9dcb8bcbe..47ad3ac1e0 100644 --- a/include/xrpl/json/json_value.h +++ b/include/xrpl/json/json_value.h @@ -9,11 +9,13 @@ #include #include -/** \brief JSON (JavaScript Object Notation). +/** + * @brief JSON (JavaScript Object Notation). */ namespace json { -/** \brief Type of the value held by a Value object. +/** + * @brief Type of the value held by a Value object. */ enum class ValueType { Null = 0, ///< 'null' value @@ -26,19 +28,20 @@ enum class ValueType { Object ///< object value (collection of name/value pairs). }; -/** \brief Lightweight wrapper to tag static string. +/** + * @brief Lightweight wrapper to tag static string. * * Value constructor and ValueType::Object member assignment takes advantage of the * StaticString and avoid the cost of string duplication when storing the * string or the member name. * * Example of usage: - * \code + * @code * json::Value aValue( StaticString("some text") ); * json::Value object; * static const StaticString code("code"); * object[code] = 1234; - * \endcode + * @endcode */ class StaticString { @@ -99,7 +102,8 @@ operator!=(StaticString x, std::string const& y) return !(y == x); } -/** \brief Represents a JSON value. +/** + * @brief Represents a JSON value. * * This class is a discriminated union wrapper that can represent a: * - signed integer [range: Value::kMinInt - Value::kMaxInt] @@ -175,37 +179,39 @@ public: using ObjectValues = std::map; public: - /** \brief Create a default Value of the given type. - - This is a very useful constructor. - To create an empty array, pass ValueType::Array. - To create an empty object, pass ValueType::Object. - Another Value can then be set to this one by assignment. - This is useful since clear() and resize() will not alter types. - - Examples: - \code - json::Value null_value; // null - json::Value arr_value(json::ValueType::Array); // [] - json::Value obj_value(json::ValueType::Object); // {} - \endcode - */ + /** + * @brief Create a default Value of the given type. + * + * This is a very useful constructor. + * To create an empty array, pass ValueType::Array. + * To create an empty object, pass ValueType::Object. + * Another Value can then be set to this one by assignment. + * This is useful since clear() and resize() will not alter types. + * + * Examples: + * @code + * json::Value null_value; // null + * json::Value arr_value(json::ValueType::Array); // [] + * json::Value obj_value(json::ValueType::Object); // {} + * @endcode + */ Value(ValueType type = ValueType::Null); Value(Int value); Value(UInt value); Value(double value); Value(char const* value); Value(xrpl::Number const& value); - /** \brief Constructs a value from a static string. - + /** + * @brief Constructs a value from a static string. + * * Like other value string constructor but do not duplicate the string for * internal storage. The given string must remain alive after the call to - this + * this * constructor. * Example of usage: - * \code + * @code * json::Value aValue( StaticString("some text") ); - * \endcode + * @endcode */ Value(StaticString const& value); Value(std::string const& value); @@ -220,7 +226,9 @@ public: Value(Value&& other) noexcept; - /// Swap values. + /** + * Swap values. + */ void swap(Value& other) noexcept; @@ -229,7 +237,9 @@ public: [[nodiscard]] char const* asCString() const; - /** Returns the unquoted string value. */ + /** + * Returns the unquoted string value. + */ [[nodiscard]] std::string asString() const; [[nodiscard]] Int @@ -241,13 +251,17 @@ public: [[nodiscard]] bool asBool() const; - /** Correct absolute value from int or unsigned int */ + /** + * Correct absolute value from int or unsigned int + */ [[nodiscard]] UInt asAbsUInt() const; // TODO: What is the "empty()" method this docstring mentions? - /** isNull() tests to see if this field is null. Don't use this method to - test for emptiness: use empty(). */ + /** + * isNull() tests to see if this field is null. Don't use this method to + * test for emptiness: use empty(). + */ [[nodiscard]] bool isNull() const; [[nodiscard]] bool @@ -276,116 +290,157 @@ public: [[nodiscard]] bool isConvertibleTo(ValueType other) const; - /// Number of values in array or object + /** + * Number of values in array or object + */ [[nodiscard]] UInt size() const; - /** Returns false if this is an empty array, empty object, empty string, - or null. */ + /** + * Returns false if this is an empty array, empty object, empty string, + * or null. + */ explicit operator bool() const; - /// Remove all object members and array elements. - /// \pre type() is ValueType::Array, ValueType::Object, or ValueType::Null - /// \post type() is unchanged + /** + * Remove all object members and array elements. + * @pre type() is ValueType::Array, ValueType::Object, or ValueType::Null + * @post type() is unchanged + */ void clear(); - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are - /// inserted in the array so that its size is index+1. (You may need to say - /// 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) + /** + * Access an array element (zero based index ). + * If the array contains less than index element, then null value are + * inserted in the array so that its size is index+1. (You may need to say + * 'value[0u]' to get your compiler to distinguish + * this from the operator[] which takes a string.) + */ Value& operator[](UInt index); - /// Access an array element (zero based index ) - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) + /** + * Access an array element (zero based index ) + * (You may need to say 'value[0u]' to get your compiler to distinguish + * this from the operator[] which takes a string.) + */ Value const& operator[](UInt index) const; - /// If the array contains at least index+1 elements, returns the element - /// value, otherwise returns defaultValue. + /** + * If the array contains at least index+1 elements, returns the element + * value, otherwise returns defaultValue. + */ [[nodiscard]] Value get(UInt index, Value const& defaultValue) const; - /// Return true if index < size(). + /** + * Return true if index < size(). + */ [[nodiscard]] bool isValidIndex(UInt index) const; - /// \brief Append value to array at the end. - /// - /// Equivalent to jsonvalue[jsonvalue.size()] = value; + /** + * @brief Append value to array at the end. + * + * Equivalent to jsonvalue[jsonvalue.size()] = value; + */ Value& append(Value const& value); Value& append(Value&& value); - /// Access an object value by name, create a null member if it does not - /// exist. + /** + * Access an object value by name, create a null member if it does not + * exist. + */ Value& operator[](char const* key); - /// Access an object value by name, returns null if there is no member with - /// that name. + /** + * Access an object value by name, returns null if there is no member with + * that name. + */ Value const& operator[](char const* key) const; - /// Access an object value by name, create a null member if it does not - /// exist. + /** + * Access an object value by name, create a null member if it does not + * exist. + */ Value& operator[](std::string const& key); - /// Access an object value by name, returns null if there is no member with - /// that name. + /** + * Access an object value by name, returns null if there is no member with + * that name. + */ Value const& operator[](std::string const& key) const; - /** \brief Access an object value by name, create a null member if it does - not exist. - + /** + * @brief Access an object value by name, create a null member if it does + * not exist. + * * If the object as no entry for that name, then the member name used to - store + * store * the new entry is not duplicated. * Example of use: - * \code + * @code * json::Value object; * static const StaticString code("code"); * object[code] = 1234; - * \endcode + * @endcode */ Value& operator[](StaticString const& key); Value const& operator[](StaticString const& key) const; - /// Return the member named key if it exist, defaultValue otherwise. + /** + * Return the member named key if it exist, defaultValue otherwise. + */ Value get(char const* key, Value const& defaultValue) const; - /// Return the member named key if it exist, defaultValue otherwise. + /** + * Return the member named key if it exist, defaultValue otherwise. + */ [[nodiscard]] Value get(std::string const& key, Value const& defaultValue) const; - /// \brief Remove and return the named member. - /// - /// Do nothing if it did not exist. - /// \return the removed Value, or null. - /// \pre type() is ValueType::Object or ValueType::Null - /// \post type() is unchanged + /** + * @brief Remove and return the named member. + * + * Do nothing if it did not exist. + * @return the removed Value, or null. + * @pre type() is ValueType::Object or ValueType::Null + * @post type() is unchanged + */ Value removeMember(char const* key); - /// Same as removeMember(const char*) + /** + * Same as removeMember(const char*) + */ Value removeMember(std::string const& key); - /// Return true if the object has a member named key. + /** + * Return true if the object has a member named key. + */ bool isMember(char const* key) const; - /// Return true if the object has a member named key. + /** + * Return true if the object has a member named key. + */ [[nodiscard]] bool isMember(std::string const& key) const; - /// Return true if the object has a member named key. + /** + * Return true if the object has a member named key. + */ [[nodiscard]] bool isMember(StaticString const& key) const; - /// \brief Return a list of the member names. - /// - /// If null, return an empty list. - /// \pre type() is ValueType::Object or ValueType::Null - /// \post if type() was ValueType::Null, it remains ValueType::Null + /** + * @brief Return a list of the member names. + * + * If null, return an empty list. + * @pre type() is ValueType::Object or ValueType::Null + * @post if type() was ValueType::Null, it remains ValueType::Null + */ [[nodiscard]] Members getMemberNames() const; @@ -461,7 +516,8 @@ operator>=(Value const& x, Value const& y) return !(x < y); } -/** \brief Experimental do not use: Allocator to customize member name and +/** + * @brief Experimental do not use: Allocator to customize member name and * string value memory management done by Value. * * - makeMemberName() and releaseMemberName() are called to respectively @@ -486,8 +542,8 @@ public: releaseStringValue(char* value) = 0; }; -/** \brief base class for Value iterators. - * +/** + * @brief base class for Value iterators. */ class ValueIteratorBase { @@ -512,17 +568,23 @@ public: return !isEqual(other); } - /// Return either the index or the member name of the referenced value as a - /// Value. + /** + * Return either the index or the member name of the referenced value as a + * Value. + */ [[nodiscard]] Value key() const; - /// Return the index of the referenced Value. -1 if it is not an ValueType::Array. + /** + * Return the index of the referenced Value. -1 if it is not an ValueType::Array. + */ [[nodiscard]] UInt index() const; - /// Return the member name of the referenced Value. "" if it is not an - /// ValueType::Object. + /** + * Return the member name of the referenced Value. "" if it is not an + * ValueType::Object. + */ [[nodiscard]] char const* memberName() const; @@ -551,8 +613,8 @@ private: bool isNull_; }; -/** \brief const iterator for object and array value. - * +/** + * @brief const iterator for object and array value. */ class ValueConstIterator : public ValueIteratorBase { @@ -566,20 +628,22 @@ public: using SelfType = ValueConstIterator; ValueConstIterator() = default; + ValueConstIterator(ValueConstIterator const& other) = default; private: - /*! \internal Use by Value to create an iterator. + /** + * @internal Use by Value to create an iterator. */ explicit ValueConstIterator(Value::ObjectValues::iterator const& current); 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 +651,7 @@ public: SelfType operator--(int) { - SelfType temp(*this); + SelfType const temp(*this); --*this; return temp; } @@ -613,7 +677,8 @@ public: } }; -/** \brief Iterator for object and array value. +/** + * @brief Iterator for object and array value. */ class ValueIterator : public ValueIteratorBase { @@ -631,7 +696,8 @@ public: ValueIterator(ValueIterator const& other); private: - /*! \internal Use by Value to create an iterator. + /** + * @internal Use by Value to create an iterator. */ explicit ValueIterator(Value::ObjectValues::iterator const& current); diff --git a/include/xrpl/json/json_writer.h b/include/xrpl/json/json_writer.h index afc99fe8c9..65c8b20931 100644 --- a/include/xrpl/json/json_writer.h +++ b/include/xrpl/json/json_writer.h @@ -3,14 +3,18 @@ #include #include +#include #include +#include +#include #include namespace json { class Value; -/** \brief Abstract class for writers. +/** + * @brief Abstract class for writers. */ class WriterBase { @@ -20,12 +24,13 @@ public: write(Value const& root) = 0; }; -/** \brief Outputs a Value in JSON format +/** + * @brief Outputs a Value in JSON format * without formatting (not human friendly). * * The JSON document is written in a single line. It is not intended for 'human' * consumption, but may be useful to support feature such as RPC where bandwidth - * is limited. \sa Reader, Value + * is limited. @see Reader, Value */ class FastWriter : public WriterBase @@ -45,7 +50,8 @@ private: std::string document_; }; -/** \brief Writes a Value in JSON format in a +/** + * @brief Writes a Value in JSON format in a * human friendly way. * * The rules for line break and indent are as follow: @@ -61,7 +67,7 @@ private: * - otherwise, it the values do not fit on one line, or the array contains * object or non empty array, then print one value per line. * - * \sa Reader, Value + * @see Reader, Value */ class StyledWriter : public WriterBase { @@ -70,8 +76,9 @@ public: ~StyledWriter() override = default; public: // overridden from Writer - /** \brief Serialize a Value in JSON - * format. \param root Value to serialize. \return String containing the + /** + * @brief Serialize a Value in JSON + * format. @param root Value to serialize. @return String containing the * JSON document that represents the root value. */ std::string @@ -105,26 +112,27 @@ private: bool addChildValues_{}; }; -/** \brief Writes a Value in JSON format in a - human friendly way, to a stream rather than to a string. +/** + * @brief Writes a Value in JSON format in a + * human friendly way, to a stream rather than to a string. * * The rules for line break and indent are as follow: * - Object value: * - if empty then print {} without indent and line break * - if not empty the print '{', line break & indent, print one value per - line + * line * and then unindent and line break and print '}'. * - Array value: * - if empty then print [] without indent and line break * - if the array contains no object value, empty array or some other value - types, + * types, * and all the values fit on one lines, then print the array on a single - line. + * line. * - otherwise, it the values do not fit on one line, or the array contains * object or non empty array, then print one value per line. * - * \param indentation Each level will be indented by this amount extra. - * \sa Reader, Value + * @param indentation Each level will be indented by this amount extra. + * @see Reader, Value */ class StyledStreamWriter { @@ -133,10 +141,11 @@ public: ~StyledStreamWriter() = default; public: - /** \brief Serialize a Value in JSON - * format. \param out Stream to write to. (Can be ostringstream, e.g.) - * \param root Value to serialize. - * \note There is no point in deriving from Writer, since write() should not + /** + * @brief Serialize a Value in JSON + * format. @param out Stream to write to. (Can be ostringstream, e.g.) + * @param root Value to serialize. + * @note There is no point in deriving from Writer, since write() should not * return a value. */ void @@ -181,8 +190,10 @@ valueToString(bool value); std::string valueToQuotedString(char const* value); -/// \brief Output using the StyledStreamWriter. -/// \see json::operator>>() +/** + * @brief Output using the StyledStreamWriter. + * @see json::operator>>() + */ std::ostream& operator<<(std::ostream&, Value const& root); @@ -262,12 +273,13 @@ writeValue(Write const& write, Value const& value) } // namespace detail -/** Stream compact JSON to the specified function. - - @param jv The json::Value to write - @param write Invocable with signature void(void const*, std::size_t) that - is called when output should be written to the stream. -*/ +/** + * Stream compact JSON to the specified function. + * + * @param jv The json::Value to write + * @param write Invocable with signature void(void const*, std::size_t) that + * is called when output should be written to the stream. + */ template void stream(json::Value const& jv, Write const& write) @@ -276,29 +288,31 @@ stream(json::Value const& jv, Write const& write) write("\n", 1); } -/** Decorator for streaming out compact json - - Use - - json::Value jv; - out << json::Compact{jv} - - to write a single-line, compact version of `jv` to the stream, rather - than the styled format that comes from undecorated streaming. -*/ +/** + * Decorator for streaming out compact json + * + * Use + * + * json::Value jv; + * out << json::Compact{jv} + * + * to write a single-line, compact version of `jv` to the stream, rather + * than the styled format that comes from undecorated streaming. + */ class Compact { json::Value jv_; public: - /** Wrap a json::Value for compact streaming - - @param jv The json::Value to stream - - @note For now, we do not support wrapping lvalues to avoid - potentially costly copies. If we find a need, we can consider - adding support for compact lvalue streaming in the future. - */ + /** + * Wrap a json::Value for compact streaming + * + * @param jv The json::Value to stream + * + * @note For now, we do not support wrapping lvalues to avoid + * potentially costly copies. If we find a need, we can consider + * adding support for compact lvalue streaming in the future. + */ Compact(json::Value&& jv) : jv_{std::move(jv)} { } diff --git a/include/xrpl/json/to_string.h b/include/xrpl/json/to_string.h index 1d7b4c785a..bdd7a51e6a 100644 --- a/include/xrpl/json/to_string.h +++ b/include/xrpl/json/to_string.h @@ -6,11 +6,15 @@ namespace json { -/** Writes a json::Value to an std::string. */ +/** + * Writes a json::Value to an std::string. + */ std::string to_string(Value const&); -/** Writes a json::Value to an std::string. */ +/** + * Writes a json::Value to an std::string. + */ std::string pretty(Value const&); diff --git a/include/xrpl/ledger/AcceptedLedgerTx.h b/include/xrpl/ledger/AcceptedLedgerTx.h index 0a1592f6e1..283dcf6e24 100644 --- a/include/xrpl/ledger/AcceptedLedgerTx.h +++ b/include/xrpl/ledger/AcceptedLedgerTx.h @@ -1,27 +1,37 @@ #pragma once +#include #include +#include #include #include +#include +#include #include +#include +#include #include #include +#include +#include +#include + namespace xrpl { /** - A transaction that is in a closed ledger. - - Description - - An accepted ledger transaction contains additional information that the - server needs to tell clients about the transaction. For example, - - The transaction in JSON form - - Which accounts are affected - * This is used by InfoSub to report to clients - - Cached stuff -*/ + * A transaction that is in a closed ledger. + * + * Description + * + * An accepted ledger transaction contains additional information that the + * server needs to tell clients about the transaction. For example, + * - The transaction in JSON form + * - Which accounts are affected + * * This is used by InfoSub to report to clients + * - Cached stuff + */ class AcceptedLedgerTx : public CountedObject { public: diff --git a/include/xrpl/ledger/AmendmentTable.h b/include/xrpl/ledger/AmendmentTable.h index 8ed3cb81ff..c3ef779eb1 100644 --- a/include/xrpl/ledger/AmendmentTable.h +++ b/include/xrpl/ledger/AmendmentTable.h @@ -1,23 +1,47 @@ #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 { class ServiceRegistry; -/** The amendment table stores the list of enabled and potential amendments. - Individuals amendments are voted on by validators during the consensus - process. -*/ +/** + * The amendment table stores the list of enabled and potential amendments. + * Individuals amendments are voted on by validators during the consensus + * process. + */ class AmendmentTable { public: @@ -67,11 +91,15 @@ public: [[nodiscard]] virtual json::Value getJson(bool isAdmin) const = 0; - /** Returns a json::ValueType::Object. */ + /** + * Returns a json::ValueType::Object. + */ [[nodiscard]] virtual json::Value getJson(uint256 const& amendment, bool isAdmin) const = 0; - /** Called when a new fully-validated ledger is accepted. */ + /** + * Called when a new fully-validated ledger is accepted. + */ void doValidatedLedger(std::shared_ptr const& lastValidatedLedger) { @@ -84,9 +112,10 @@ public: } } - /** Called to determine whether the amendment logic needs to process - a new validated ledger. (If it could have changed things.) - */ + /** + * Called to determine whether the amendment logic needs to process + * a new validated ledger. (If it could have changed things.) + */ [[nodiscard]] virtual bool needValidatedLedger(LedgerIndex seq) const = 0; diff --git a/include/xrpl/ledger/ApplyView.h b/include/xrpl/ledger/ApplyView.h index 362eae0f79..724d89b7c6 100644 --- a/include/xrpl/ledger/ApplyView.h +++ b/include/xrpl/ledger/ApplyView.h @@ -1,9 +1,24 @@ #pragma once +#include #include #include -#include +#include #include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include namespace xrpl { @@ -77,47 +92,50 @@ operator&=(ApplyFlags& lhs, ApplyFlags const& rhs) //------------------------------------------------------------------------------ -/** Writeable view to a ledger, for applying a transaction. - - This refinement of ReadView provides an interface where - the SLE can be "checked out" for modifications and put - back in an updated or removed state. Also added is an - interface to provide contextual information necessary - to calculate the results of transaction processing, - including the metadata if the view is later applied to - the parent (using an interface in the derived class). - The context info also includes values from the base - ledger such as sequence number and the network time. - - This allows implementations to journal changes made to - the state items in a ledger, with the option to apply - those changes to the base or discard the changes without - affecting the base. - - Typical usage is to call read() for non-mutating - operations. - - For mutating operations the sequence is as follows: - - // Add a new value - v.insert(sle); - - // Check out a value for modification - sle = v.peek(k); - - // Indicate that changes were made - v.update(sle) - - // Or, erase the value - v.erase(sle) - - The invariant is that insert, update, and erase may not - be called with any SLE which belongs to different view. -*/ +/** + * Writeable view to a ledger, for applying a transaction. + * + * This refinement of ReadView provides an interface where + * the SLE can be "checked out" for modifications and put + * back in an updated or removed state. Also added is an + * interface to provide contextual information necessary + * to calculate the results of transaction processing, + * including the metadata if the view is later applied to + * the parent (using an interface in the derived class). + * The context info also includes values from the base + * ledger such as sequence number and the network time. + * + * This allows implementations to journal changes made to + * the state items in a ledger, with the option to apply + * those changes to the base or discard the changes without + * affecting the base. + * + * Typical usage is to call read() for non-mutating + * operations. + * + * For mutating operations the sequence is as follows: + * + * // Add a new value + * v.insert(sle); + * + * // Check out a value for modification + * sle = v.peek(k); + * + * // Indicate that changes were made + * v.update(sle) + * + * // Or, erase the value + * v.erase(sle) + * + * The invariant is that insert, update, and erase may not + * be called with any SLE which belongs to different view. + */ class ApplyView : public ReadView { private: - /** Add an entry to a directory using the specified insert strategy */ + /** + * Add an entry to a directory using the specified insert strategy + */ std::optional dirAdd( bool preserveOrder, @@ -128,84 +146,89 @@ private: public: ApplyView() = default; - /** Returns the tx apply flags. - - Flags can affect the outcome of transaction - processing. For example, transactions applied - to an open ledger generate "local" failures, - while transactions applied to the consensus - ledger produce hard failures (and claim a fee). - */ + /** + * Returns the tx apply flags. + * + * Flags can affect the outcome of transaction + * processing. For example, transactions applied + * to an open ledger generate "local" failures, + * while transactions applied to the consensus + * ledger produce hard failures (and claim a fee). + */ [[nodiscard]] virtual ApplyFlags flags() const = 0; - /** Prepare to modify the SLE associated with key. - - Effects: - - Gives the caller ownership of a modifiable - SLE associated with the specified key. - - The returned SLE may be used in a subsequent - call to erase or update. - - The SLE must not be passed to any other ApplyView. - - @return `nullptr` if the key is not present - */ + /** + * Prepare to modify the SLE associated with key. + * + * Effects: + * + * Gives the caller ownership of a modifiable + * SLE associated with the specified key. + * + * The returned SLE may be used in a subsequent + * call to erase or update. + * + * The SLE must not be passed to any other ApplyView. + * + * @return `nullptr` if the key is not present + */ virtual SLE::pointer peek(Keylet const& k) = 0; - /** Remove a peeked SLE. - - Requirements: - - `sle` was obtained from prior call to peek() - on this instance of the RawView. - - Effects: - - The key is no longer associated with the SLE. - */ + /** + * Remove a peeked SLE. + * + * Requirements: + * + * `sle` was obtained from prior call to peek() + * on this instance of the RawView. + * + * Effects: + * + * The key is no longer associated with the SLE. + */ virtual void erase(SLE::ref sle) = 0; - /** Insert a new state SLE - - Requirements: - - `sle` was not obtained from any calls to - peek() on any instances of RawView. - - The SLE's key must not already exist. - - Effects: - - The key in the state map is associated - with the SLE. - - The RawView acquires ownership of the shared_ptr. - - @note The key is taken from the SLE - */ + /** + * Insert a new state SLE + * + * Requirements: + * + * `sle` was not obtained from any calls to + * peek() on any instances of RawView. + * + * The SLE's key must not already exist. + * + * Effects: + * + * The key in the state map is associated + * with the SLE. + * + * The RawView acquires ownership of the shared_ptr. + * + * @note The key is taken from the SLE + */ virtual void insert(SLE::ref sle) = 0; - /** Indicate changes to a peeked SLE - - Requirements: - - The SLE's key must exist. - - `sle` was obtained from prior call to peek() - on this instance of the RawView. - - Effects: - - The SLE is updated - - @note The key is taken from the SLE - */ + /** + * Indicate changes to a peeked SLE + * + * Requirements: + * + * The SLE's key must exist. + * + * `sle` was obtained from prior call to peek() + * on this instance of the RawView. + * + * Effects: + * + * The SLE is updated + * + * @note The key is taken from the SLE + */ /** @{ */ virtual void update(SLE::ref sle) = 0; @@ -235,7 +258,8 @@ public: XRPL_ASSERT(amount.holds(), "creditHookMPT: amount is for MPTIssue"); } - /** Facilitate tracking of MPT sold by an issuer owning MPT sell offer. + /** + * Facilitate tracking of MPT sold by an issuer owning MPT sell offer. * Unlike IOU, MPT doesn't have bi-directional relationship with an issuer, * where a trustline limits an amount that can be issued to a holder. * Consequently, the credit step (last MPTEndpointStep or @@ -275,27 +299,28 @@ public: // Called when the owner count changes // This is required to support PaymentSandbox virtual void - adjustOwnerCountHook(AccountID const& account, std::uint32_t cur, std::uint32_t next) + adjustOwnerCountHook(AccountID const& account, OwnerCounts const& cur, OwnerCounts const& next) { } - /** Append an entry to a directory - - Entries in the directory will be stored in order of insertion, i.e. new - entries will always be added at the tail end of the last page. - - @param directory the base of the directory - @param key the entry to insert - @param describe callback to add required entries to a new page - - @return a \c std::optional which, if insertion was successful, - will contain the page number in which the item was stored. - - @note this function may create a page (including a root page), if no - page with space is available. This function will only fail if the - page counter exceeds the protocol-defined maximum number of - allowable pages. - */ + /** + * Append an entry to a directory + * + * Entries in the directory will be stored in order of insertion, i.e. new + * entries will always be added at the tail end of the last page. + * + * @param directory the base of the directory + * @param key the entry to insert + * @param describe callback to add required entries to a new page + * + * @return a @c std::optional which, if insertion was successful, + * will contain the page number in which the item was stored. + * + * @note this function may create a page (including a root page), if no + * page with space is available. This function will only fail if the + * page counter exceeds the protocol-defined maximum number of + * allowable pages. + */ /** @{ */ std::optional dirAppend( @@ -318,23 +343,24 @@ public: } /** @} */ - /** Insert an entry to a directory - - Entries in the directory will be stored in a semi-random order, but - each page will be maintained in sorted order. - - @param directory the base of the directory - @param key the entry to insert - @param describe callback to add required entries to a new page - - @return a \c std::optional which, if insertion was successful, - will contain the page number in which the item was stored. - - @note this function may create a page (including a root page), if no - page with space is available.this function will only fail if the - page counter exceeds the protocol-defined maximum number of - allowable pages. - */ + /** + * Insert an entry to a directory + * + * Entries in the directory will be stored in a semi-random order, but + * each page will be maintained in sorted order. + * + * @param directory the base of the directory + * @param key the entry to insert + * @param describe callback to add required entries to a new page + * + * @return a @c std::optional which, if insertion was successful, + * will contain the page number in which the item was stored. + * + * @note this function may create a page (including a root page), if no + * page with space is available.this function will only fail if the + * page counter exceeds the protocol-defined maximum number of + * allowable pages. + */ /** @{ */ std::optional dirInsert( @@ -355,21 +381,22 @@ public: } /** @} */ - /** Remove an entry from a directory - - @param directory the base of the directory - @param page the page number for this page - @param key the entry to remove - @param keepRoot if deleting the last entry, don't - delete the root page (i.e. the directory itself). - - @return \c true if the entry was found and deleted and - \c false otherwise. - - @note This function will remove zero or more pages from the directory; - the root page will not be deleted even if it is empty, unless - \p keepRoot is not set and the directory is empty. - */ + /** + * Remove an entry from a directory + * + * @param directory the base of the directory + * @param page the page number for this page + * @param key the entry to remove + * @param keepRoot if deleting the last entry, don't + * delete the root page (i.e. the directory itself). + * + * @return @c true if the entry was found and deleted and + * @c false otherwise. + * + * @note This function will remove zero or more pages from the directory; + * the root page will not be deleted even if it is empty, unless + * \p keepRoot is not set and the directory is empty. + */ /** @{ */ bool dirRemove(Keylet const& directory, std::uint64_t page, uint256 const& key, bool keepRoot); @@ -381,29 +408,51 @@ public: } /** @} */ - /** Remove the specified directory, invoking the callback for every node. */ + /** + * Remove the specified directory, invoking the callback for every node. + */ bool dirDelete(Keylet const& directory, std::function const&); - /** Remove the specified directory, if it is empty. - - @param directory the identifier of the directory node to be deleted - @return \c true if the directory was found and was successfully deleted - \c false otherwise. - - @note The function should only be called with the root entry (i.e. with - the first page) of a directory. - */ + /** + * Remove the specified directory, if it is empty. + * + * @param directory the identifier of the directory node to be deleted + * @return @c true if the directory was found and was successfully deleted + * @c false otherwise. + * + * @note The function should only be called with the root entry (i.e. with + * the first page) of a directory. + */ bool emptyDirDelete(Keylet const& directory); }; -namespace directory { -/** Helper functions for managing low-level directory operations. - These are not part of the ApplyView interface. +/** + * Bundles the mutable ledger view and the transaction being applied. + * + * Passed together to avoid threading two separate parameters through every + * helper that needs both the view (for state reads/writes) and the + * transaction (for field inspection and metadata). + * + * Both members are non-owning references; the caller is responsible for + * ensuring that the referenced objects outlive the ApplyViewContext. + * + * TODO: replace with ApplyContext after it's untangled with xrpl/tx + */ +struct ApplyViewContext +{ + ApplyView& view; + STTx const& tx; +}; - Don't use them unless you really, really know what you're doing. - Instead use dirAdd, dirInsert, etc. +namespace directory { +/** + * Helper functions for managing low-level directory operations. + * These are not part of the ApplyView interface. + * + * Don't use them unless you really, really know what you're doing. + * Instead use dirAdd, dirInsert, etc. */ std::uint64_t diff --git a/include/xrpl/ledger/ApplyViewImpl.h b/include/xrpl/ledger/ApplyViewImpl.h index 1245568630..630153f90a 100644 --- a/include/xrpl/ledger/ApplyViewImpl.h +++ b/include/xrpl/ledger/ApplyViewImpl.h @@ -1,18 +1,30 @@ #pragma once +#include +#include +#include #include +#include #include #include +#include +#include #include +#include + +#include +#include +#include namespace xrpl { -/** Editable, discardable view that can build metadata for one tx. - - Iteration of the tx map is delegated to the base. - - @note Presented as ApplyView to clients. -*/ +/** + * Editable, discardable view that can build metadata for one tx. + * + * Iteration of the tx map is delegated to the base. + * + * @note Presented as ApplyView to clients. + */ class ApplyViewImpl final : public detail::ApplyViewBase { public: @@ -26,12 +38,13 @@ public: ApplyViewImpl(ApplyViewImpl&&) = default; ApplyViewImpl(ReadView const* base, ApplyFlags flags); - /** Apply the transaction. - - After a call to `apply`, the only valid - operation on this object is to call the - destructor. - */ + /** + * Apply the transaction. + * + * After a call to `apply`, the only valid + * operation on this object is to call the + * destructor. + */ std::optional apply( OpenView& to, @@ -41,25 +54,28 @@ public: bool isDryRun, beast::Journal j); - /** Set the amount of currency delivered. - - This value is used when generating metadata - for payments, to set the DeliveredAmount field. - If the amount is not specified, the field is - excluded from the resulting metadata. - */ + /** + * Set the amount of currency delivered. + * + * This value is used when generating metadata + * for payments, to set the DeliveredAmount field. + * If the amount is not specified, the field is + * excluded from the resulting metadata. + */ void deliver(STAmount const& amount) { deliver_ = amount; } - /** Get the number of modified entries + /** + * Get the number of modified entries */ std::size_t size(); - /** Visit modified entries + /** + * Visit modified entries */ void visit( diff --git a/include/xrpl/ledger/BookDirs.h b/include/xrpl/ledger/BookDirs.h index 36798934da..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 { 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 462db48ee3..b9e2cf8d66 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 { @@ -124,15 +133,16 @@ public: } // namespace detail -/** Wraps a DigestAwareReadView to provide caching. - - @tparam Base A subclass of DigestAwareReadView -*/ +/** + * Wraps a DigestAwareReadView to provide caching. + * + * @tparam Base A subclass of DigestAwareReadView + */ 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_; @@ -149,10 +159,11 @@ public: { } - /** Returns the base type. - - @note This breaks encapsulation and bypasses the cache. - */ + /** + * Returns the base type. + * + * @note This breaks encapsulation and bypasses the cache. + */ std::shared_ptr const& base() const { diff --git a/include/xrpl/ledger/CanonicalTXSet.h b/include/xrpl/ledger/CanonicalTXSet.h index 4dffadd52f..11aadf4e92 100644 --- a/include/xrpl/ledger/CanonicalTXSet.h +++ b/include/xrpl/ledger/CanonicalTXSet.h @@ -1,19 +1,25 @@ #pragma once #include +#include +#include #include #include #include +#include +#include +#include + namespace xrpl { -/** Holds transactions which were deferred to the next pass of consensus. - - "Canonical" refers to the order in which transactions are applied. - - - Puts transactions from the same account in SeqProxy order - -*/ +/** + * Holds transactions which were deferred to the next pass of consensus. + * + * "Canonical" refers to the order in which transactions are applied. + * + * - Puts transactions from the same account in SeqProxy order + */ // VFALCO TODO rename to SortedTxSet class CanonicalTXSet : public CountedObject { diff --git a/include/xrpl/ledger/Dir.h b/include/xrpl/ledger/Dir.h index d305e21938..233719cdeb 100644 --- a/include/xrpl/ledger/Dir.h +++ b/include/xrpl/ledger/Dir.h @@ -1,22 +1,31 @@ #pragma once +#include #include -#include +#include +#include +#include + +#include +#include +#include +#include namespace xrpl { -/** A class that simplifies iterating ledger directory pages - - The Dir class provides a forward iterator for walking through - the uint256 values contained in ledger directories. - - The Dir class also allows accelerated directory walking by - stepping directly from one page to the next using the next_page() - member function. - - As of July 2024, the Dir class is only being used with NFTokenOffer - directories and for unit tests. -*/ +/** + * A class that simplifies iterating ledger directory pages + * + * The Dir class provides a forward iterator for walking through + * the uint256 values contained in ledger directories. + * + * The Dir class also allows accelerated directory walking by + * stepping directly from one page to the next using the next_page() + * member function. + * + * As of July 2024, the Dir class is only being used with NFTokenOffer + * directories and for unit tests. + */ class Dir { private: diff --git a/include/xrpl/ledger/Ledger.h b/include/xrpl/ledger/Ledger.h index 5f7d79c61d..e1dd2c422e 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 { @@ -26,32 +42,33 @@ struct CreateGenesisT }; extern CreateGenesisT const kCreateGenesis; -/** Holds a ledger. - - The ledger is composed of two SHAMaps. The state map holds all of the - ledger entries such as account roots and order books. The tx map holds - all of the transactions and associated metadata that made it into that - particular ledger. Most of the operations on a ledger are concerned - with the state map. - - This can hold just the header, a partial set of data, or the entire set - of data. It all depends on what is in the corresponding SHAMap entry. - Various functions are provided to populate or depopulate the caches that - the object holds references to. - - Ledgers are constructed as either mutable or immutable. - - 1) If you are the sole owner of a mutable ledger, you can do whatever you - want with no need for locks. - - 2) If you have an immutable ledger, you cannot ever change it, so no need - for locks. - - 3) Mutable ledgers cannot be shared. - - @note Presented to clients as ReadView - @note Calls virtuals in the constructor, so marked as final -*/ +/** + * Holds a ledger. + * + * The ledger is composed of two SHAMaps. The state map holds all of the + * ledger entries such as account roots and order books. The tx map holds + * all of the transactions and associated metadata that made it into that + * particular ledger. Most of the operations on a ledger are concerned + * with the state map. + * + * This can hold just the header, a partial set of data, or the entire set + * of data. It all depends on what is in the corresponding SHAMap entry. + * Various functions are provided to populate or depopulate the caches that + * the object holds references to. + * + * Ledgers are constructed as either mutable or immutable. + * + * 1) If you are the sole owner of a mutable ledger, you can do whatever you + * want with no need for locks. + * + * 2) If you have an immutable ledger, you cannot ever change it, so no need + * for locks. + * + * 3) Mutable ledgers cannot be shared. + * + * @note Presented to clients as ReadView + * @note Calls virtuals in the constructor, so marked as final + */ class Ledger final : public std::enable_shared_from_this, public DigestAwareReadView, public TxsRawView, @@ -66,20 +83,21 @@ public: Ledger& operator=(Ledger&&) = delete; - /** Create the Genesis ledger. - - The Genesis ledger contains a single account whose - AccountID is generated with a Generator using the seed - computed from the string "masterpassphrase" and ordinal - zero. - - The account has an XRP balance equal to the total amount - of XRP in the system. No more XRP than the amount which - starts in this account can ever exist, with amounts - used to pay fees being destroyed. - - Amendments specified are enabled in the genesis ledger - */ + /** + * Create the Genesis ledger. + * + * The Genesis ledger contains a single account whose + * AccountID is generated with a Generator using the seed + * computed from the string "masterpassphrase" and ordinal + * zero. + * + * The account has an XRP balance equal to the total amount + * of XRP in the system. No more XRP than the amount which + * starts in this account can ever exist, with amounts + * used to pay fees being destroyed. + * + * Amendments specified are enabled in the genesis ledger + */ Ledger( CreateGenesisT, Rules rules, @@ -89,13 +107,14 @@ public: Ledger(LedgerHeader const& info, Rules rules, Family& family); - /** Used for ledgers loaded from JSON files - - @param acquire If true, acquires the ledger if not found locally - - @note The fees parameter provides default values, but setup() may - override them from the ledger state if fee-related SLEs exist. - */ + /** + * Used for ledgers loaded from JSON files + * + * @param acquire If true, acquires the ledger if not found locally + * + * @note The fees parameter provides default values, but setup() may + * override them from the ledger state if fee-related SLEs exist. + */ Ledger( LedgerHeader const& info, bool& loaded, @@ -105,12 +124,13 @@ public: Family& family, beast::Journal j); - /** Create a new ledger following a previous ledger - - The ledger will have the sequence number that - follows previous, and have - parentCloseTime == previous.closeTime. - */ + /** + * Create a new ledger following a previous ledger + * + * The ledger will have the sequence number that + * follows previous, and have + * parentCloseTime == previous.closeTime. + */ Ledger(Ledger const& previous, NetClock::time_point closeTime); // used for database ledgers @@ -353,11 +373,15 @@ public: void updateNegativeUNL(); - /** Returns true if the ledger is a flag ledger */ + /** + * Returns true if the ledger is a flag ledger + */ bool isFlagLedger() const; - /** Returns true if the ledger directly precedes a flag ledger */ + /** + * Returns true if the ledger directly precedes a flag ledger + */ bool isVotingLedger() const; @@ -371,23 +395,25 @@ private: bool setup(); - /** @brief Deserialize a SHAMapItem containing a single STTx. + /** + * @brief Deserialize a SHAMapItem containing a single STTx. * * @param item The SHAMapItem to deserialize. * @return A shared pointer to the deserialized transaction. - * @throw May throw on deserialization error. + * @throws May throw on deserialization error. */ static std::shared_ptr deserializeTx(SHAMapItem const& item); - /** @brief Deserialize a SHAMapItem containing STTx + STObject metadata. + /** + * @brief Deserialize a SHAMapItem containing STTx + STObject metadata. * * The SHAMapItem must contain two variable length serialization objects. * * @param item The SHAMapItem to deserialize. * @return A pair containing shared pointers to the deserialized transaction * and metadata. - * @throw May throw on deserialization error. + * @throws May throw on deserialization error. */ static std::pair, std::shared_ptr> deserializeTxPlusMeta(SHAMapItem const& item); @@ -409,7 +435,9 @@ private: beast::Journal j_; }; -/** A ledger wrapped in a CachedView. */ +/** + * A ledger wrapped in a CachedView. + */ using CachedLedger = CachedView; } // namespace xrpl diff --git a/include/xrpl/ledger/LedgerTiming.h b/include/xrpl/ledger/LedgerTiming.h index 508403d760..77254a434b 100644 --- a/include/xrpl/ledger/LedgerTiming.h +++ b/include/xrpl/ledger/LedgerTiming.h @@ -1,17 +1,19 @@ #pragma once -#include -#include +#include +#include #include +#include namespace xrpl { -/** Possible ledger close time resolutions. - - Values should not be duplicated. - @see getNextLedgerTimeResolution -*/ +/** + * Possible ledger close time resolutions. + * + * Values should not be duplicated. + * @see getNextLedgerTimeResolution + */ constexpr std::chrono::seconds kLedgerPossibleTimeResolutions[] = { std::chrono::seconds{10}, std::chrono::seconds{20}, @@ -20,41 +22,50 @@ constexpr std::chrono::seconds kLedgerPossibleTimeResolutions[] = { std::chrono::seconds{90}, std::chrono::seconds{120}}; -//! Initial resolution of ledger close time. +/** + * Initial resolution of ledger close time. + */ constexpr auto kLedgerDefaultTimeResolution = kLedgerPossibleTimeResolutions[2]; -//! Close time resolution in genesis ledger +/** + * Close time resolution in genesis ledger + */ constexpr auto kLedgerGenesisTimeResolution = kLedgerPossibleTimeResolutions[0]; -//! How often we increase the close time resolution (in numbers of ledgers) +/** + * How often we increase the close time resolution (in numbers of ledgers) + */ constexpr auto kIncreaseLedgerTimeResolutionEvery = 8; -//! How often we decrease the close time resolution (in numbers of ledgers) +/** + * How often we decrease the close time resolution (in numbers of ledgers) + */ constexpr auto kDecreaseLedgerTimeResolutionEvery = 1; -/** Calculates the close time resolution for the specified ledger. - - The XRPL protocol uses binning to represent time intervals using only one - timestamp. This allows servers to derive a common time for the next ledger, - without the need for perfectly synchronized clocks. - The time resolution (i.e. the size of the intervals) is adjusted dynamically - based on what happened in the last ledger, to try to avoid disagreements. - - @param previousResolution the resolution used for the prior ledger - @param previousAgree whether consensus agreed on the close time of the prior - ledger - @param ledgerSeq the sequence number of the new ledger - - @pre previousResolution must be a valid bin - from @ref kLedgerPossibleTimeResolutions - - @tparam Rep Type representing number of ticks in std::chrono::duration - @tparam Period An std::ratio representing tick period in - std::chrono::duration - @tparam Seq Unsigned integer-like type corresponding to the ledger sequence - number. It should be comparable to 0 and support modular - division. Built-in and tagged_integers are supported. -*/ +/** + * Calculates the close time resolution for the specified ledger. + * + * The XRPL protocol uses binning to represent time intervals using only one + * timestamp. This allows servers to derive a common time for the next ledger, + * without the need for perfectly synchronized clocks. + * The time resolution (i.e. the size of the intervals) is adjusted dynamically + * based on what happened in the last ledger, to try to avoid disagreements. + * + * @tparam Rep Type representing number of ticks in std::chrono::duration + * @tparam Period An std::ratio representing tick period in + * std::chrono::duration + * @tparam Seq Unsigned integer-like type corresponding to the ledger sequence + * number. It should be comparable to 0 and support modular + * division. Built-in and tagged_integers are supported. + * + * @param previousResolution the resolution used for the prior ledger + * @param previousAgree whether consensus agreed on the close time of the prior + * ledger + * @param ledgerSeq the sequence number of the new ledger + * + * @pre previousResolution must be a valid bin + * from @ref kLedgerPossibleTimeResolutions + */ template std::chrono::duration getNextLedgerTimeResolution( @@ -97,13 +108,14 @@ getNextLedgerTimeResolution( return previousResolution; } -/** Calculates the close time for a ledger, given a close time resolution. - - @param closeTime The time to be rounded - @param closeResolution The resolution - @return @b closeTime rounded to the nearest multiple of @b closeResolution. - Rounds up if @b closeTime is midway between multiples of @b closeResolution. -*/ +/** + * Calculates the close time for a ledger, given a close time resolution. + * + * @param closeTime The time to be rounded + * @param closeResolution The resolution + * @return @b closeTime rounded to the nearest multiple of @b closeResolution. + * Rounds up if @b closeTime is midway between multiples of @b closeResolution. + */ template std::chrono::time_point roundCloseTime( @@ -118,15 +130,16 @@ roundCloseTime( return closeTime - (closeTime.time_since_epoch() % closeResolution); } -/** Calculate the effective ledger close time - - After adjusting the ledger close time based on the current resolution, also - ensure it is sufficiently separated from the prior close time. - - @param closeTime The raw ledger close time - @param resolution The current close time resolution - @param priorCloseTime The close time of the prior ledger -*/ +/** + * Calculate the effective ledger close time + * + * After adjusting the ledger close time based on the current resolution, also + * ensure it is sufficiently separated from the prior close time. + * + * @param closeTime The raw ledger close time + * @param resolution The current close time resolution + * @param priorCloseTime The close time of the prior ledger + */ template std::chrono::time_point effCloseTime( diff --git a/include/xrpl/ledger/OpenView.h b/include/xrpl/ledger/OpenView.h index 4ba2a7759b..3f8e950b02 100644 --- a/include/xrpl/ledger/OpenView.h +++ b/include/xrpl/ledger/OpenView.h @@ -1,34 +1,47 @@ #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 { -/** Open ledger construction tag. - - Views constructed with this tag will have the - rules of open ledgers applied during transaction - processing. +/** + * Open ledger construction tag. + * + * Views constructed with this tag will have the + * rules of open ledgers applied during transaction + * processing. */ inline constexpr struct OpenLedgerT { explicit constexpr OpenLedgerT() = default; } kOpenLedger{}; -/** Batch view construction tag. - - Views constructed with this tag are part of a stack of views - used during batch transaction applied. +/** + * Batch view construction tag. + * + * Views constructed with this tag are part of a stack of views + * used during batch transaction application. */ inline constexpr struct BatchViewT { @@ -37,10 +50,11 @@ inline constexpr struct BatchViewT //------------------------------------------------------------------------------ -/** Writable ledger view that accumulates state and tx changes. - - @note Presented as ReadView to clients. -*/ +/** + * Writable ledger view that accumulates state and tx changes. + * + * @note Presented as ReadView to clients. + */ class OpenView final : public ReadView, public TxsRawView { private: @@ -71,7 +85,7 @@ 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 @@ -84,7 +98,9 @@ private: detail::RawStateTable items_; std::shared_ptr hold_; - /// In batch mode, the number of transactions already executed. + /** + * In batch mode, the number of transactions already executed. + */ std::size_t baseTxCount_ = 0; bool open_ = true; @@ -98,40 +114,42 @@ public: OpenView(OpenView&&) = default; - /** Construct a shallow copy. - - Effects: - - Creates a new object with a copy of - the modification state table. - - The objects managed by shared pointers are - not duplicated but shared between instances. - Since the SLEs are immutable, calls on the - RawView interface cannot break invariants. - */ + /** + * Construct a shallow copy. + * + * Effects: + * + * Creates a new object with a copy of + * the modification state table. + * + * The objects managed by shared pointers are + * not duplicated but shared between instances. + * Since the SLEs are immutable, calls on the + * RawView interface cannot break invariants. + */ OpenView(OpenView const&); - /** Construct an open ledger view. - - Effects: - - The sequence number is set to the - sequence number of parent plus one. - - The parentCloseTime is set to the - closeTime of parent. - - If `hold` is not nullptr, retains - ownership of a copy of `hold` until - the MetaView is destroyed. - - Calls to rules() will return the - rules provided on construction. - - The tx list starts empty and will contain - all newly inserted tx. - */ + /** + * Construct an open ledger view. + * + * Effects: + * + * The sequence number is set to the + * sequence number of parent plus one. + * + * The parentCloseTime is set to the + * closeTime of parent. + * + * If `hold` is not nullptr, retains + * ownership of a copy of `hold` until + * the MetaView is destroyed. + * + * Calls to rules() will return the + * rules provided on construction. + * + * The tx list starts empty and will contain + * all newly inserted tx. + */ OpenView( OpenLedgerT, ReadView const* base, @@ -148,35 +166,41 @@ public: baseTxCount_ = base.txCount(); } - /** Construct a new last closed ledger. - - Effects: - - The LedgerHeader is copied from the base. - - The rules are inherited from the base. - - The tx list starts empty and will contain - all newly inserted tx. - */ + /** + * Construct a new last closed ledger. + * + * Effects: + * + * The LedgerHeader is copied from the base. + * + * The rules are inherited from the base. + * + * The tx list starts empty and will contain + * all newly inserted tx. + */ OpenView(ReadView const* base, std::shared_ptr hold = nullptr); - /** Returns true if this reflects an open ledger. */ + /** + * Returns true if this reflects an open ledger. + */ bool open() const override { return open_; } - /** Return the number of tx inserted since creation. - - This is used to set the "apply ordinal" - when calculating transaction metadata. - */ + /** + * Return the number of tx inserted since creation. + * + * This is used to set the "apply ordinal" + * when calculating transaction metadata. + */ std::size_t txCount() const; - /** Apply changes. */ + /** + * Apply changes. + */ void apply(TxsRawView& to) const; diff --git a/include/xrpl/ledger/OrderBookDB.h b/include/xrpl/ledger/OrderBookDB.h index a0aee58e2a..96dc94b1f4 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 @@ -14,97 +14,94 @@ namespace xrpl { -/** Tracks order books in the ledger. - - This interface provides access to order book information, including: - - Which order books exist in the ledger - - Querying order books by issue - - Managing order book subscriptions - - The order book database is updated as ledgers are accepted and provides - efficient lookup of order book information for pathfinding and client - subscriptions. -*/ +/** + * Tracks order books in the ledger. + * + * This interface provides access to order book information, including: + * - Which order books exist in the ledger + * - Querying order books by issue + * - Managing order book subscriptions + * + * The order book database is updated as ledgers are accepted and provides + * efficient lookup of order book information for pathfinding and client + * subscriptions. + */ class OrderBookDB { public: virtual ~OrderBookDB() = default; - /** Initialize or update the order book database with a new ledger. - - This method should be called when a new ledger is accepted to update - the order book database with the current state of all order books. - - @param ledger The ledger to scan for order books - */ + /** + * Initialize or update the order book database with a new ledger. + * + * This method should be called when a new ledger is accepted to update + * the order book database with the current state of all order books. + * + * @param ledger The ledger to scan for order books + */ virtual void setup(std::shared_ptr const& ledger) = 0; - /** Add an order book to track. - - @param book The order book to add - */ + /** + * Add an order book to track. + * + * @param book The order book to add + */ virtual void addOrderBook(Book const& book) = 0; - /** Get all order books that want a specific issue. - - Returns a list of all order books where the taker pays the specified - issue. This is useful for pathfinding to find all possible next hops - from a given currency. - - @param asset The asset to search for - @param domain Optional domain restriction for the order book - @return Vector of books that want this issue - */ + /** + * Get all order books that want a specific issue. + * + * Returns a list of all order books where the taker pays the specified + * issue. This is useful for pathfinding to find all possible next hops + * from a given currency. + * + * @param asset The asset to search for + * @param domain Optional domain restriction for the order book + * @return Vector of books that want this issue + */ virtual std::vector getBooksByTakerPays(Asset const& asset, std::optional const& domain = std::nullopt) = 0; - /** Get the count of order books that want a specific issue. - - @param asset The asset to search for - @param domain Optional domain restriction for the order book - @return Number of books that want this issue - */ + /** + * Get the count of order books that want a specific issue. + * + * @param asset The asset to search for + * @param domain Optional domain restriction for the order book + * @return Number of books that want this issue + */ virtual int getBookSize(Asset const& asset, std::optional const& domain = std::nullopt) = 0; - /** Check if an order book to XRP exists for the given issue. - - @param asset The asset to check - @param domain Optional domain restriction for the order book - @return true if a book from this issue to XRP exists - */ + /** + * Check if an order book to XRP exists for the given issue. + * + * @param asset The asset to check + * @param domain Optional domain restriction for the order book + * @return true if a book from this issue to XRP exists + */ 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/OwnerCounts.h b/include/xrpl/ledger/OwnerCounts.h new file mode 100644 index 0000000000..74d60014b5 --- /dev/null +++ b/include/xrpl/ledger/OwnerCounts.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include // IWYU pragma: keep +#include + +#include +#include + +namespace xrpl { + +struct OwnerCounts +{ + std::uint32_t owner = 0; + std::uint32_t sponsored = 0; + std::uint32_t sponsoring = 0; + + OwnerCounts() = default; + OwnerCounts(SLE::const_ref sle) + : owner(sle->at(sfOwnerCount)) + , sponsored(sle->at(sfSponsoredOwnerCount)) + , sponsoring(sle->at(sfSponsoringOwnerCount)) + { + XRPL_ASSERT( + owner >= sponsored, + "xrpl::OwnerCounts : OwnerCount must be greater than or equal to " + "SponsoredOwnerCount"); + XRPL_ASSERT(sle->getType() == ltACCOUNT_ROOT, "xrpl::OwnerCounts : sle is AccountRoot"); + } + + [[nodiscard]] std::uint32_t + count() const + { + std::int64_t const x = static_cast(owner) - sponsored + sponsoring; + if (x < 0) + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::OwnerCounts::count : count less than zero"); + return 0; + // LCOV_EXCL_STOP + } + + if (x > std::numeric_limits::max()) + return std::numeric_limits::max(); // LCOV_EXCL_LINE + return static_cast(x); + } + + auto + operator<=>(OwnerCounts const& o) const + { + if (auto cmp = count() <=> o.count(); cmp != 0) // NOLINT(modernize-use-nullptr) + return cmp; + if (auto cmp = owner <=> o.owner; cmp != 0) // NOLINT(modernize-use-nullptr) + return cmp; + if (auto cmp = sponsored <=> o.sponsored; cmp != 0) // NOLINT(modernize-use-nullptr) + return cmp; + return sponsoring <=> o.sponsoring; + } + + bool + operator==(OwnerCounts const& o) const + { + return this == &o || + (owner == o.owner && sponsored == o.sponsored && sponsoring == o.sponsoring); + } +}; + +} // namespace xrpl diff --git a/include/xrpl/ledger/PaymentSandbox.h b/include/xrpl/ledger/PaymentSandbox.h index 1cd89d9388..e725bdd556 100644 --- a/include/xrpl/ledger/PaymentSandbox.h +++ b/include/xrpl/ledger/PaymentSandbox.h @@ -1,11 +1,20 @@ #pragma once +#include +#include #include -#include +#include #include #include +#include +#include +#include +#include +#include #include +#include +#include #include namespace xrpl { @@ -99,12 +108,12 @@ public: issuerSelfDebitMPT(MPTIssue const& issue, std::uint64_t amount, std::int64_t origBalance); void - ownerCount(AccountID const& id, std::uint32_t cur, std::uint32_t next); + ownerCount(AccountID const& id, OwnerCounts const& cur, OwnerCounts const& next); // Get the adjusted owner count. Since DeferredCredits is meant to be used // in payments, and payments only decrease owner counts, return the max // remembered owner count. - [[nodiscard]] std::optional + [[nodiscard]] std::optional ownerCount(AccountID const& id) const; void @@ -116,25 +125,26 @@ private: std::map creditsIOU_; std::map creditsMPT_; - std::map ownerCounts_; + std::map ownerCounts_; }; } // namespace detail //------------------------------------------------------------------------------ -/** A wrapper which makes credits unavailable to balances. - - This is used for payments and pathfinding, so that consuming - liquidity from a path never causes portions of that path or - other paths to gain liquidity. - - The behavior of certain free functions in the ApplyView API - will change via the balanceHook and creditHook overrides - of PaymentSandbox. - - @note Presented as ApplyView to clients -*/ +/** + * A wrapper which makes credits unavailable to balances. + * + * This is used for payments and pathfinding, so that consuming + * liquidity from a path never causes portions of that path or + * other paths to gain liquidity. + * + * The behavior of certain free functions in the ApplyView API + * will change via the balanceHook and creditHook overrides + * of PaymentSandbox. + * + * @note Presented as ApplyView to clients + */ class PaymentSandbox final : public detail::ApplyViewBase { public: @@ -155,16 +165,17 @@ public: { } - /** Construct on top of existing PaymentSandbox. - - The changes are pushed to the parent when - apply() is called. - - @param parent A non-null pointer to the parent. - - @note A pointer is used to prevent confusion - with copy construction. - */ + /** + * Construct on top of existing PaymentSandbox. + * + * The changes are pushed to the parent when + * apply() is called. + * + * @param parent A non-null pointer to the parent. + * + * @note A pointer is used to prevent confusion + * with copy construction. + */ // VFALCO If we are constructing on top of a PaymentSandbox, // or a PaymentSandbox-derived class, we MUST go through // one of these constructors or invariants will be broken. @@ -210,17 +221,19 @@ public: override; void - adjustOwnerCountHook(AccountID const& account, std::uint32_t cur, std::uint32_t next) override; + adjustOwnerCountHook(AccountID const& account, OwnerCounts const& cur, OwnerCounts const& next) + override; - [[nodiscard]] std::uint32_t - ownerCountHook(AccountID const& account, std::uint32_t count) const override; + [[nodiscard]] OwnerCounts + ownerCountHook(AccountID const& account, OwnerCounts const& count) const override; - /** Apply changes to base view. - - `to` must contain contents identical to the parent - view passed upon construction, else undefined - behavior will result. - */ + /** + * Apply changes to base view. + * + * `to` must contain contents identical to the parent + * view passed upon construction, else undefined + * behavior will result. + */ /** @{ */ void apply(RawView& to); diff --git a/include/xrpl/ledger/PendingSaves.h b/include/xrpl/ledger/PendingSaves.h index a18292df68..723ae1aef1 100644 --- a/include/xrpl/ledger/PendingSaves.h +++ b/include/xrpl/ledger/PendingSaves.h @@ -8,12 +8,13 @@ namespace xrpl { -/** Keeps track of which ledgers haven't been fully saved. - - During the ledger building process this collection will keep - track of those ledgers that are being built but have not yet - been completely written. -*/ +/** + * Keeps track of which ledgers haven't been fully saved. + * + * During the ledger building process this collection will keep + * track of those ledgers that are being built but have not yet + * been completely written. + */ class PendingSaves { private: @@ -22,12 +23,13 @@ private: std::condition_variable await_; public: - /** Start working on a ledger - - This is called prior to updating the SQLite indexes. - - @return 'true' if work should be done - */ + /** + * Start working on a ledger + * + * This is called prior to updating the SQLite indexes. + * + * @return 'true' if work should be done + */ bool startWork(LedgerIndex seq) { @@ -45,12 +47,13 @@ public: return true; } - /** Finish working on a ledger - - This is called after updating the SQLite indexes. - The tracking of the work in progress is removed and - threads awaiting completion are notified. - */ + /** + * Finish working on a ledger + * + * This is called after updating the SQLite indexes. + * The tracking of the work in progress is removed and + * threads awaiting completion are notified. + */ void finishWork(LedgerIndex seq) { @@ -60,7 +63,9 @@ public: await_.notify_all(); } - /** Return `true` if a ledger is in the progress of being saved. */ + /** + * Return `true` if a ledger is in the progress of being saved. + */ bool pending(LedgerIndex seq) { @@ -68,14 +73,15 @@ public: return map_.contains(seq); } - /** Check if a ledger should be dispatched - - Called to determine whether work should be done or - dispatched. If work is already in progress and the - call is synchronous, wait for work to be completed. - - @return 'true' if work should be done or dispatched - */ + /** + * Check if a ledger should be dispatched + * + * Called to determine whether work should be done or + * dispatched. If work is already in progress and the + * call is synchronous, wait for work to be completed. + * + * @return 'true' if work should be done or dispatched + */ bool shouldWork(LedgerIndex seq, bool isSynchronous) { @@ -108,12 +114,13 @@ public: } while (true); } - /** Get a snapshot of the pending saves - - Each entry in the returned map corresponds to a ledger - that is in progress or dispatched. The boolean indicates - whether work is currently in progress. - */ + /** + * Get a snapshot of the pending saves + * + * Each entry in the returned map corresponds to a ledger + * that is in progress or dispatched. The boolean indicates + * whether work is currently in progress. + */ std::map getSnapshot() const { diff --git a/include/xrpl/ledger/RawView.h b/include/xrpl/ledger/RawView.h index cf61c3e814..b94a7aab27 100644 --- a/include/xrpl/ledger/RawView.h +++ b/include/xrpl/ledger/RawView.h @@ -3,13 +3,17 @@ #include #include #include +#include + +#include namespace xrpl { -/** Interface for ledger entry changes. - - Subclasses allow raw modification of ledger entries. -*/ +/** + * Interface for ledger entry changes. + * + * Subclasses allow raw modification of ledger entries. + */ class RawView { public: @@ -19,66 +23,72 @@ public: RawView& operator=(RawView const&) = delete; - /** Delete an existing state item. - - The SLE is provided so the implementation - can calculate metadata. - */ + /** + * Delete an existing state item. + * + * The SLE is provided so the implementation + * can calculate metadata. + */ virtual void rawErase(SLE::ref sle) = 0; - /** Unconditionally insert a state item. - - Requirements: - The key must not already exist. - - Effects: - - The key is associated with the SLE. - - @note The key is taken from the SLE - */ + /** + * Unconditionally insert a state item. + * + * Requirements: + * The key must not already exist. + * + * Effects: + * + * The key is associated with the SLE. + * + * @note The key is taken from the SLE + */ virtual void rawInsert(SLE::ref sle) = 0; - /** Unconditionally replace a state item. - - Requirements: - - The key must exist. - - Effects: - - The key is associated with the SLE. - - @note The key is taken from the SLE - */ + /** + * Unconditionally replace a state item. + * + * Requirements: + * + * The key must exist. + * + * Effects: + * + * The key is associated with the SLE. + * + * @note The key is taken from the SLE + */ virtual void rawReplace(SLE::ref sle) = 0; - /** Destroy XRP. - - This is used to pay for transaction fees. - */ + /** + * Destroy XRP. + * + * This is used to pay for transaction fees. + */ virtual void rawDestroyXRP(XRPAmount const& fee) = 0; }; //------------------------------------------------------------------------------ -/** Interface for changing ledger entries with transactions. - - Allows raw modification of ledger entries and insertion - of transactions into the transaction map. -*/ +/** + * Interface for changing ledger entries with transactions. + * + * Allows raw modification of ledger entries and insertion + * of transactions into the transaction map. + */ class TxsRawView : public RawView { public: - /** Add a transaction to the tx map. - - Closed ledgers must have metadata, - while open ledgers omit metadata. - */ + /** + * Add a transaction to the tx map. + * + * Closed ledgers must have metadata, + * while open ledgers omit metadata. + */ virtual void rawTxInsert( ReadView::key_type const& key, diff --git a/include/xrpl/ledger/ReadView.h b/include/xrpl/ledger/ReadView.h index f4ee7e6fd2..d0010b6030 100644 --- a/include/xrpl/ledger/ReadView.h +++ b/include/xrpl/ledger/ReadView.h @@ -1,32 +1,42 @@ #pragma once +#include #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 +#include namespace xrpl { //------------------------------------------------------------------------------ -/** A view into a ledger. - - This interface provides read access to state - and transaction items. There is no checkpointing - or calculation of metadata. -*/ +/** + * A view into a ledger. + * + * This interface provides read access to state + * and transaction items. There is no checkpointing + * or calculation of metadata. + */ class ReadView { public: @@ -77,72 +87,87 @@ public: { } - /** Returns information about the ledger. */ + /** + * Returns information about the ledger. + */ [[nodiscard]] virtual LedgerHeader const& header() const = 0; - /** Returns true if this reflects an open ledger. */ + /** + * Returns true if this reflects an open ledger. + */ [[nodiscard]] virtual bool open() const = 0; - /** Returns the close time of the previous ledger. */ + /** + * Returns the close time of the previous ledger. + */ [[nodiscard]] NetClock::time_point parentCloseTime() const { return header().parentCloseTime; } - /** Returns the sequence number of the base ledger. */ + /** + * Returns the sequence number of the base ledger. + */ [[nodiscard]] LedgerIndex seq() const { return header().seq; } - /** Returns the fees for the base ledger. */ + /** + * Returns the fees for the base ledger. + */ [[nodiscard]] virtual Fees const& fees() const = 0; - /** Returns the tx processing rules. */ + /** + * Returns the tx processing rules. + */ [[nodiscard]] virtual Rules const& rules() const = 0; - /** Determine if a state item exists. - - @note This can be more efficient than calling read. - - @return `true` if a SLE is associated with the - specified key. - */ + /** + * Determine if a state item exists. + * + * @note This can be more efficient than calling read. + * + * @return `true` if a SLE is associated with the + * specified key. + */ [[nodiscard]] virtual bool exists(Keylet const& k) const = 0; - /** Return the key of the next state item. - - This returns the key of the first state item - whose key is greater than the specified key. If - no such key is present, std::nullopt is returned. - - If `last` is engaged, returns std::nullopt when - the key returned would be outside the open - interval (key, last). - */ + /** + * Return the key of the next state item. + * + * This returns the key of the first state item + * whose key is greater than the specified key. If + * no such key is present, std::nullopt is returned. + * + * If `last` is engaged, returns std::nullopt when + * the key returned would be outside the open + * interval (key, last). + */ [[nodiscard]] virtual std::optional succ(key_type const& key, std::optional const& last = std::nullopt) const = 0; - /** Return the state item associated with a key. - - Effects: - If the key exists, gives the caller ownership - of the non-modifiable corresponding SLE. - - @note While the returned SLE is `const` from the - perspective of the caller, it can be changed - by other callers through raw operations. - - @return `nullptr` if the key is not present or - if the type does not match. - */ + /** + * Return the state item associated with a key. + * + * Effects: + * If the key exists, gives the caller ownership + * of the non-modifiable corresponding SLE. + * + * @note While the returned SLE is `const` from the + * perspective of the caller, it can be changed + * by other callers through raw operations. + * + * @return `nullptr` if the key is not present or + * if the type does not match. + */ [[nodiscard]] virtual SLE::const_pointer read(Keylet const& k) const = 0; @@ -182,8 +207,8 @@ public: // changes that accounts make during a payment. `ownerCountHook` adjusts the // ownerCount so it returns the max value of the ownerCount so far. // This is required to support PaymentSandbox. - [[nodiscard]] virtual std::uint32_t - ownerCountHook(AccountID const& account, std::uint32_t count) const + [[nodiscard]] virtual OwnerCounts + ownerCountHook(AccountID const& account, OwnerCounts const& count) const { return count; } @@ -208,22 +233,24 @@ public: [[nodiscard]] virtual std::unique_ptr txsEnd() const = 0; - /** Returns `true` if a tx exists in the tx map. - - A tx exists in the map if it is part of the - base ledger, or if it is a newly inserted tx. - */ + /** + * Returns `true` if a tx exists in the tx map. + * + * A tx exists in the map if it is part of the + * base ledger, or if it is a newly inserted tx. + */ [[nodiscard]] virtual bool txExists(key_type const& key) const = 0; - /** Read a transaction from the tx map. - - If the view represents an open ledger, - the metadata object will be empty. - - @return A pair of nullptr if the - key is not found in the tx map. - */ + /** + * Read a transaction from the tx map. + * + * If the view represents an open ledger, + * the metadata object will be empty. + * + * @return A pair of nullptr if the + * key is not found in the tx map. + */ [[nodiscard]] virtual tx_type txRead(key_type const& key) const = 0; @@ -231,11 +258,12 @@ public: // Memberspaces // - /** Iterable range of ledger state items. - - @note Visiting each state entry in the ledger can - become quite expensive as the ledger grows. - */ + /** + * Iterable range of ledger state items. + * + * @note Visiting each state entry in the ledger can + * become quite expensive as the ledger grows. + */ SlesType sles; // The range of transactions @@ -244,7 +272,9 @@ public: //------------------------------------------------------------------------------ -/** ReadView that associates keys with digests. */ +/** + * ReadView that associates keys with digests. + */ class DigestAwareReadView : public ReadView { public: @@ -253,10 +283,11 @@ public: DigestAwareReadView() = default; DigestAwareReadView(DigestAwareReadView const&) = default; - /** Return the digest associated with the key. - - @return std::nullopt if the item does not exist. - */ + /** + * Return the digest associated with the key. + * + * @return std::nullopt if the item does not exist. + */ [[nodiscard]] virtual std::optional digest(key_type const& key) const = 0; }; diff --git a/include/xrpl/ledger/Sandbox.h b/include/xrpl/ledger/Sandbox.h index dc80df5ba2..fd48e339eb 100644 --- a/include/xrpl/ledger/Sandbox.h +++ b/include/xrpl/ledger/Sandbox.h @@ -1,16 +1,19 @@ #pragma once +#include #include +#include #include namespace xrpl { -/** Discardable, editable view to a ledger. - - The sandbox inherits the flags of the base. - - @note Presented as ApplyView to clients. -*/ +/** + * Discardable, editable view to a ledger. + * + * The sandbox inherits the flags of the base. + * + * @note Presented as ApplyView to clients. + */ class Sandbox : public detail::ApplyViewBase { public: diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 255413e459..768e518008 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -1,13 +1,22 @@ #pragma once +#include +#include #include #include #include +#include +#include +#include +#include #include #include +#include +#include #include #include #include +#include #include #include @@ -26,26 +35,27 @@ enum class SkipEntry : bool { No = false, Yes }; // //------------------------------------------------------------------------------ -/** Determines whether the given expiration time has passed. - - In the XRP Ledger, expiration times are defined as the number of whole - seconds after the "XRPL epoch" which, for historical reasons, is set - to January 1, 2000 (00:00 UTC). - - This is like the way the Unix epoch works, except the XRPL epoch is - precisely 946,684,800 seconds after the Unix Epoch. - - See https://xrpl.org/basic-data-types.html#specifying-time - - Expiration is defined in terms of the close time of the parent ledger, - because we definitively know the time that it closed (since consensus - agrees on time) but we do not know the closing time of the ledger that - is under construction. - - @param view The ledger whose parent time is used as the clock. - @param exp The optional expiration time we want to check. - - @returns `true` if `exp` is in the past; `false` otherwise. +/** + * Determines whether the given expiration time has passed. + * + * In the XRP Ledger, expiration times are defined as the number of whole + * seconds after the "XRPL epoch" which, for historical reasons, is set + * to January 1, 2000 (00:00 UTC). + * + * This is like the way the Unix epoch works, except the XRPL epoch is + * precisely 946,684,800 seconds after the Unix Epoch. + * + * See https://xrpl.org/basic-data-types.html#specifying-time + * + * Expiration is defined in terms of the close time of the parent ledger, + * because we definitively know the time that it closed (since consensus + * agrees on time) but we do not know the closing time of the ledger that + * is under construction. + * + * @param view The ledger whose parent time is used as the clock. + * @param exp The optional expiration time we want to check. + * + * @return `true` if `exp` is in the past; `false` otherwise. */ [[nodiscard]] bool hasExpired(ReadView const& view, std::optional const& exp); @@ -74,41 +84,44 @@ using majorityAmendments_t = std::map; [[nodiscard]] majorityAmendments_t getMajorityAmendments(ReadView const& view); -/** Return the hash of a ledger by sequence. - The hash is retrieved by looking up the "skip list" - in the passed ledger. As the skip list is limited - in size, if the requested ledger sequence number is - out of the range of ledgers represented in the skip - list, then std::nullopt is returned. - @return The hash of the ledger with the - given sequence number or std::nullopt. -*/ +/** + * Return the hash of a ledger by sequence. + * The hash is retrieved by looking up the "skip list" + * in the passed ledger. As the skip list is limited + * in size, if the requested ledger sequence number is + * out of the range of ledgers represented in the skip + * list, then std::nullopt is returned. + * @return The hash of the ledger with the + * given sequence number or std::nullopt. + */ [[nodiscard]] std::optional hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal); -/** Find a ledger index from which we could easily get the requested ledger - - The index that we return should meet two requirements: - 1) It must be the index of a ledger that has the hash of the ledger - we are looking for. This means that its sequence must be equal to - greater than the sequence that we want but not more than 256 greater - since each ledger contains the hashes of the 256 previous ledgers. - - 2) Its hash must be easy for us to find. This means it must be 0 mod 256 - because every such ledger is permanently enshrined in a LedgerHashes - page which we can easily retrieve via the skip list. -*/ +/** + * Find a ledger index from which we could easily get the requested ledger + * + * The index that we return should meet two requirements: + * 1) It must be the index of a ledger that has the hash of the ledger + * we are looking for. This means that its sequence must be equal to + * greater than the sequence that we want but not more than 256 greater + * since each ledger contains the hashes of the 256 previous ledgers. + * + * 2) Its hash must be easy for us to find. This means it must be 0 mod 256 + * because every such ledger is permanently enshrined in a LedgerHashes + * page which we can easily retrieve via the skip list. + */ inline LedgerIndex getCandidateLedger(LedgerIndex requested) { return (requested + 255) & (~255); } -/** Return false if the test ledger is provably incompatible - with the valid ledger, that is, they could not possibly - both be valid. Use the first form if you have both ledgers, - use the second form if you have not acquired the valid ledger yet -*/ +/** + * Return false if the test ledger is provably incompatible + * with the valid ledger, that is, they could not possibly + * both be valid. Use the first form if you have both ledgers, + * use the second form if you have not acquired the valid ledger yet + */ [[nodiscard]] bool areCompatible( ReadView const& validLedger, @@ -137,7 +150,8 @@ dirLink( SLE::pointer& object, SF_UINT64 const& node = sfOwnerNode); -/** Checks that can withdraw funds from an object to itself or a destination. +/** + * Checks that can withdraw funds from an object to itself or a destination. * * The receiver may be either the submitting account (sfAccount) or a different * destination account (sfDestination). @@ -160,7 +174,8 @@ canWithdraw( STAmount const& amount, bool hasDestinationTag); -/** Checks that can withdraw funds from an object to itself or a destination. +/** + * Checks that can withdraw funds from an object to itself or a destination. * * The receiver may be either the submitting account (sfAccount) or a different * destination account (sfDestination). @@ -182,7 +197,8 @@ canWithdraw( STAmount const& amount, bool hasDestinationTag); -/** Checks that can withdraw funds from an object to itself or a destination. +/** + * Checks that can withdraw funds from an object to itself or a destination. * * The receiver may be either the submitting account (sfAccount) or a different * destination account (sfDestination). @@ -201,8 +217,7 @@ canWithdraw(ReadView const& view, STTx const& tx); [[nodiscard]] TER doWithdraw( - ApplyView& view, - STTx const& tx, + ApplyViewContext ctx, AccountID const& senderAcct, AccountID const& dstAcct, AccountID const& sourceAcct, @@ -210,13 +225,15 @@ doWithdraw( STAmount const& amount, beast::Journal j); -/** Deleter function prototype. Returns the status of the entry deletion +/** + * Deleter function prototype. Returns the status of the entry deletion * (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(LedgerEntryType, uint256 const&, SLE::pointer&)>; -/** Cleanup owner directory entries on account delete. +/** + * 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 * specific account-owned object deletion. @@ -231,12 +248,13 @@ cleanupOnAccountDelete( beast::Journal j, std::optional maxNodesToDelete = std::nullopt); -/** Has the specified time passed? - - @param now the current time - @param mark the cutoff point - @return true if \a now refers to a time strictly after \a mark, else false. -*/ +/** + * Has the specified time passed? + * + * @param now the current time + * @param mark the cutoff point + * @return true if \a now refers to a time strictly after \a mark, else false. + */ bool after(NetClock::time_point now, std::uint32_t mark); diff --git a/include/xrpl/ledger/detail/ApplyStateTable.h b/include/xrpl/ledger/detail/ApplyStateTable.h index f40e3d0d1c..752c87d588 100644 --- a/include/xrpl/ledger/detail/ApplyStateTable.h +++ b/include/xrpl/ledger/detail/ApplyStateTable.h @@ -1,13 +1,26 @@ #pragma once +#include +#include #include #include #include #include +#include +#include +#include +#include +#include #include #include #include +#include +#include +#include +#include +#include + namespace xrpl::detail { // Helper class that buffers modifications diff --git a/include/xrpl/ledger/detail/ApplyViewBase.h b/include/xrpl/ledger/detail/ApplyViewBase.h index d6493c46a8..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 diff --git a/include/xrpl/ledger/detail/RawStateTable.h b/include/xrpl/ledger/detail/RawStateTable.h index d2567e34f1..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 { @@ -97,7 +105,7 @@ 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. 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 d21e50e7cb..7d41bfce81 100644 --- a/include/xrpl/ledger/helpers/AMMHelpers.h +++ b/include/xrpl/ledger/helpers/AMMHelpers.h @@ -2,22 +2,36 @@ #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 { @@ -37,7 +51,10 @@ reduceOffer(auto const& amount) enum class IsDeposit : bool { No = false, Yes = true }; -/** Calculate LP Tokens given AMM pool reserves. +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 * @return LP Tokens as IOU @@ -45,7 +62,8 @@ enum class IsDeposit : bool { No = false, Yes = true }; STAmount ammLPTokens(STAmount const& asset1, STAmount const& asset2, Asset const& lptIssue); -/** Calculate LP Tokens given asset's deposit amount. +/** + * Calculate LP Tokens given asset's deposit amount. * @param asset1Balance current AMM asset1 balance * @param asset1Deposit requested asset1 deposit amount * @param lptAMMBalance AMM LPT balance @@ -59,10 +77,11 @@ lpTokensOut( STAmount const& lptAMMBalance, std::uint16_t tfee); -/** Calculate asset deposit given LP Tokens. +/** + * Calculate asset deposit given LP Tokens. * @param asset1Balance current AMM asset1 balance - * @param lpTokens LP Tokens * @param lptAMMBalance AMM LPT balance + * @param lpTokens LP Tokens * @param tfee trading fee in basis points * @return */ @@ -73,7 +92,8 @@ ammAssetIn( STAmount const& lpTokens, std::uint16_t tfee); -/** Calculate LP Tokens given asset's withdraw amount. Return 0 +/** + * Calculate LP Tokens given asset's withdraw amount. Return 0 * if can't calculate. * @param asset1Balance current AMM asset1 balance * @param asset1Withdraw requested asset1 withdraw amount @@ -88,7 +108,8 @@ lpTokensIn( STAmount const& lptAMMBalance, std::uint16_t tfee); -/** Calculate asset withdrawal by tokens +/** + * Calculate asset withdrawal by tokens * @param assetBalance balance of the asset being withdrawn * @param lptAMMBalance total AMM Tokens balance * @param lpTokens LP Tokens balance @@ -102,7 +123,8 @@ ammAssetOut( STAmount const& lpTokens, std::uint16_t tfee); -/** Check if the relative distance between the qualities +/** + * Check if the relative distance between the qualities * is within the requested distance. * @param calcQuality calculated quality * @param reqQuality requested quality @@ -121,7 +143,8 @@ withinRelativeDistance(Quality const& calcQuality, Quality const& reqQuality, Nu return ((min.rate() - max.rate()) / min.rate()) < dist; } -/** Check if the relative distance between the amounts +/** + * Check if the relative distance between the amounts * is within the requested distance. * @param calc calculated amount * @param req requested amount @@ -142,13 +165,15 @@ withinRelativeDistance(Amt const& calc, Amt const& req, Number const& dist) return ((max - min) / max) < dist; } -/** Solve quadratic equation to find takerGets or takerPays. Round +/** + * Solve quadratic equation to find takerGets or takerPays. Round * to minimize the amount in order to maximize the quality. */ std::optional solveQuadraticEqSmallest(Number const& a, Number const& b, Number const& c); -/** Generate AMM offer starting with takerGets when AMM pool +/** + * Generate AMM offer starting with takerGets when AMM pool * from the payment perspective is IOU(in)/XRP(out) * Equations: * Spot Price Quality after the offer is consumed: @@ -215,7 +240,8 @@ getAMMOfferStartWithTakerGets( return amounts; } -/** Generate AMM offer starting with takerPays when AMM pool +/** + * Generate AMM offer starting with takerPays when AMM pool * from the payment perspective is XRP(in)/IOU(out) or IOU(in)/IOU(out). * Equations: * Spot Price Quality after the offer is consumed: @@ -282,7 +308,8 @@ getAMMOfferStartWithTakerPays( return amounts; } -/** Generate AMM offer so that either updated Spot Price Quality (SPQ) +/** + * Generate AMM offer so that either updated Spot Price Quality (SPQ) * is equal to LOB quality (in this case AMM offer quality is * better than LOB quality) or AMM offer is equal to LOB quality * (in this case SPQ is better than LOB quality). @@ -399,7 +426,8 @@ changeSpotPriceQuality( return amounts; } -/** AMM pool invariant - the product (A * B) after swap in/out has to remain +/** + * AMM pool invariant - the product (A * B) after swap in/out has to remain * at least the same: (A + in) * (B - out) >= A * B * XRP round-off may result in a smaller product after swap in/out. * To address this: @@ -411,7 +439,8 @@ changeSpotPriceQuality( * value is increased. */ -/** Swap assetIn into the pool and swap out a proportional amount +/** + * Swap assetIn into the pool and swap out a proportional amount * of the other asset. Implements AMM Swap in. * @see [XLS30d:AMM * Swap](https://github.com/XRPLF/XRPL-Standards/discussions/78) @@ -477,7 +506,8 @@ swapAssetIn(TAmounts const& pool, TIn const& assetIn, std::uint16_t t Number::RoundingMode::Downward); } -/** Swap assetOut out of the pool and swap in a proportional amount +/** + * Swap assetOut out of the pool and swap in a proportional amount * of the other asset. Implements AMM Swap out. * @see [XLS30d:AMM * Swap](https://github.com/XRPLF/XRPL-Standards/discussions/78) @@ -543,12 +573,14 @@ swapAssetOut(TAmounts const& pool, TOut const& assetOut, std::uint16_ Number::RoundingMode::Upward); } -/** Return square of n. +/** + * Return square of n. */ Number square(Number const& n); -/** Adjust LP tokens to deposit/withdraw. +/** + * Adjust LP tokens to deposit/withdraw. * Amount type keeps 16 digits. Maintaining the LP balance by adding * deposited tokens or subtracting withdrawn LP tokens from LP balance * results in losing precision in LP balance. I.e. the resulting LP balance @@ -562,7 +594,8 @@ square(Number const& n); STAmount adjustLPTokens(STAmount const& lptAMMBalance, STAmount const& lpTokens, IsDeposit isDeposit); -/** Calls adjustLPTokens() and adjusts deposit or withdraw amounts if +/** + * Calls adjustLPTokens() and adjusts deposit or withdraw amounts if * the adjusted LP tokens are less than the provided LP tokens. * @param amountBalance asset1 pool balance * @param amount asset1 to deposit or withdraw @@ -583,7 +616,8 @@ adjustAmountsByLPTokens( std::uint16_t tfee, IsDeposit isDeposit); -/** Positive solution for quadratic equation: +/** + * Positive solution for quadratic equation: * x = (-b + sqrt(b**2 + 4*a*c))/(2*a) */ Number @@ -614,7 +648,8 @@ getAssetRounding(IsDeposit isDeposit) } // namespace detail -/** Round AMM equal deposit/withdrawal amount. Deposit/withdrawal formulas +/** + * Round AMM equal deposit/withdrawal amount. Deposit/withdrawal formulas * calculate the amount as a fractional value of the pool balance. The rounding * takes place on the last step of multiplying the balance by the fraction if * AMMv1_3 is enabled. @@ -638,7 +673,8 @@ getRoundedAsset(Rules const& rules, STAmount const& balance, A const& frac, IsDe return multiply(balance, frac, rm); } -/** Round AMM single deposit/withdrawal amount. +/** + * Round AMM single deposit/withdrawal amount. * The lambda's are used to delay evaluation until the function * is executed so that the calculation is not done twice. noRoundCb() is * called if AMMv1_3 is disabled. Otherwise, the rounding is set and @@ -655,7 +691,8 @@ getRoundedAsset( std::function const& productCb, IsDeposit isDeposit); -/** Round AMM deposit/withdrawal LPToken amount. Deposit/withdrawal formulas +/** + * Round AMM deposit/withdrawal LPToken amount. Deposit/withdrawal formulas * calculate the lptokens as a fractional value of the AMM total lptokens. * The rounding takes place on the last step of multiplying the balance by * the fraction if AMMv1_3 is enabled. The tokens are then @@ -669,7 +706,8 @@ getRoundedLPTokens( Number const& frac, IsDeposit isDeposit); -/** Round AMM single deposit/withdrawal LPToken amount. +/** + * Round AMM single deposit/withdrawal LPToken amount. * The lambda's are used to delay evaluation until the function is executed * so that the calculations are not done twice. * noRoundCb() is called if AMMv1_3 is disabled. Otherwise, the rounding is set @@ -716,7 +754,8 @@ adjustAssetOutByTokens( STAmount const& tokens, std::uint16_t tfee); -/** Find a fraction of tokens after the tokens are adjusted. The fraction +/** + * Find a fraction of tokens after the tokens are adjusted. The fraction * is used to adjust equal deposit/withdraw amount. */ Number @@ -726,7 +765,8 @@ adjustFracByTokens( STAmount const& tokens, Number const& frac); -/** Get AMM pool balances. +/** + * Get AMM pool balances. */ std::pair ammPoolHolds( @@ -738,7 +778,34 @@ ammPoolHolds( AuthHandling authHandling, beast::Journal const j); -/** Get AMM pool and LP token balances. If both optIssue are +/** + * 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. */ @@ -752,7 +819,8 @@ ammHolds( AuthHandling authHandling, beast::Journal const j); -/** Get the balance of LP tokens. +/** + * Get the balance of LP tokens. */ STAmount ammLPHolds( @@ -770,25 +838,29 @@ ammLPHolds( AccountID const& lpAccount, beast::Journal const j); -/** Get AMM trading fee for the given account. The fee is discounted +/** + * Get AMM trading fee for the given account. The fee is discounted * if the account is the auction slot owner or one of the slot's authorized * accounts. */ std::uint16_t getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account); -/** Returns total amount held by AMM for the given token. +/** + * Returns total amount held by AMM for the given token. */ STAmount ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Asset const& asset); -/** Delete trustlines to AMM. If all trustlines are deleted then +/** + * Delete trustlines to AMM. If all trustlines are deleted then * AMM object and account are deleted. Otherwise tecINCOMPLETE is returned. */ TER deleteAMMAccount(Sandbox& view, Asset const& asset, Asset const& asset2, beast::Journal j); -/** Initialize Auction and Voting slots and set the trading/discounted fee. +/** + * Initialize Auction and Voting slots and set the trading/discounted fee. */ void initializeFeeAuctionVote( @@ -798,14 +870,16 @@ initializeFeeAuctionVote( Asset const& lptAsset, std::uint16_t tfee); -/** Return true if the Liquidity Provider is the only AMM provider, false +/** + * Return true if the Liquidity Provider is the only AMM provider, false * otherwise. Return tecINTERNAL if encountered an unexpected condition, * for instance Liquidity Provider has more than one LPToken trustline. */ std::expected isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID const& lpAccount); -/** Due to rounding, the LPTokenBalance of the last LP might +/** + * 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. */ diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index cf6082d533..350fc6ca85 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -1,43 +1,337 @@ #pragma once +#include #include #include #include +#include #include #include +#include #include #include +#include +#include #include +#include #include #include namespace xrpl { -/** Check if the issuer has the global freeze flag set. - @param issuer The account to check - @return true if the account has global freeze set -*/ +/** + * Check if the issuer has the global freeze flag set. + * @param issuer The account to check + * @return true if the account has global freeze set + */ [[nodiscard]] bool isGlobalFrozen(ReadView const& view, AccountID const& issuer); -// Calculate liquid XRP balance for an account. -// This function may be used to calculate the amount of XRP that -// the holder is able to freely spend. It subtracts reserve requirements. -// -// ownerCountAdj adjusts the owner count in case the caller calculates -// before ledger entries are added or removed. Positive to add, negative -// to subtract. -// -// @param ownerCountAdj positive to add to count, negative to reduce count. +/** + * Calculate liquid XRP balance for an account. + * + * This function may be used to calculate the amount of XRP that + * the holder is able to freely spend. It subtracts reserve requirements. + * + * ownerCountAdj adjusts the owner count in case the caller calculates + * before ledger entries are added or removed. Positive to add, negative + * to subtract. + * + * @param view The ledger view to read from + * @param id The account ID to check + * @param ownerCountAdj Positive to add to count, negative to reduce count + * @param j Journal for logging + * @return The liquid XRP amount available to the account + */ [[nodiscard]] XRPAmount xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j); -/** Adjust the owner count up or down. */ -void -adjustOwnerCount(ApplyView& view, SLE::ref sle, std::int32_t amount, beast::Journal j); +struct Adjustment +{ + std::int32_t ownerCountDelta = 0; + std::int32_t accountCountDelta = 0; +}; -/** Returns IOU issuer transfer fee as Rate. Rate specifies +/** + * Returns the account reserve, in drops. + * + * Actual owner count can be adjusted by delta in ownerCountAdj + * Actual reserve count can be adjusted by delta in accountCountAdj + * The reserve is calculated as: + * (ownerCount + "sponsoring object count" - "sponsored object count" + additionalOwnerCount) * + * increment + (1 if not sponsored account + sponsoringAccountCount) * "reserve base" + * + * @param view The ledger view to read from + * @param sle The ledger entry for the account + * @param j Journal for logging + * @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to + * subtract. + * @return The account reserve amount in drops + */ +[[nodiscard]] XRPAmount +accountReserve(ReadView const& view, SLE::const_ref sle, beast::Journal j, Adjustment adj = {}); + +/** + * Convenience overload that accepts AccountID instead of SLE. + * + * @param view The ledger view to read from + * @param id The account ID + * @param j Journal for logging + * @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to + * subtract. + * @return The account reserve amount in drops + */ +[[nodiscard]] inline XRPAmount +accountReserve(ReadView const& view, AccountID const& id, beast::Journal j, Adjustment adj = {}) +{ + return accountReserve(view, view.read(keylet::account(id)), j, adj); +} + +/** + * Check if an account has sufficient reserve. + * + * @param view The ledger view to read from + * @param tx The transaction being processed + * @param accSle The account's ledger entry + * @param accBalance The account's balance + * @param sponsorSle The sponsor's ledger entry (if applicable) + * @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to + * subtract. + * @param j Journal for logging (default: null sink) + * @param insufReserveCode The transaction result code to return if the reserve is insufficient + * (default: tecINSUFFICIENT_RESERVE). + * @return Transaction result code + */ +[[nodiscard]] TER +checkReserve( + ApplyViewContext ctx, + SLE::const_ref accSle, + XRPAmount accBalance, + SLE::const_ref sponsorSle, + Adjustment adj, + beast::Journal j, + TER insufReserveCode = tecINSUFFICIENT_RESERVE); + +/** + * Check if an account has sufficient reserve, deriving the sponsor internally. + * + * Equivalent to the overload above, but resolves the sponsor via + * getEffectiveTxReserveSponsor(ctx, accSle) instead of taking it explicitly. Use this + * in the common case where the sponsor is simply the transaction's reserve + * sponsor for accSle. Callers that must force the account's-own-reserve branch + * (passing a null sponsor) or supply a different sponsor should use the + * explicit overload above. + * + * @param ctx The apply-view context (view + tx) + * @param accSle The account's ledger entry + * @param accBalance The account's balance + * @param adj Reserve adjustments (owner/account count deltas) + * @param j Journal for logging (default: null sink) + * @return Transaction result code + */ +[[nodiscard]] TER +checkReserve( + ApplyViewContext ctx, + SLE::const_ref accSle, + XRPAmount accBalance, + Adjustment adj, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}); + +/** + * Return number of the objects which reserve is covered by the account(sle) (so called "owner + * count"). Actual owner count can be adjusted by delta in ownerCountAdj. + * + * @param sle The account's ledger entry + * @param j Journal for logging + * @param ownerCountAdj Adjustment to the owner count (default: 0) + * @return The adjusted owner count + */ +std::uint32_t +ownerCount(SLE::const_ref sle, beast::Journal j, std::int32_t ownerCountAdj = 0); + +/** + * Increase owner-count fields when the caller supplies the sponsor. + * + * This helper does not create a ledger object. It updates reserve accounting + * after the caller has created/updated an object. + * If sponsorSle is provided, this also adjusts the account's sponsored count + * and the sponsor's sponsoring count. + * + * @param view The apply view for making changes + * @param accountSle The account's ledger entry + * @param sponsorSle The sponsor's ledger entry (if applicable) + * @param count Amount to add to the owner count + * @param j Journal for logging + */ +void +increaseOwnerCount( + ApplyView& view, + SLE::ref accountSle, + SLE::ref sponsorSle, + std::uint32_t count, + beast::Journal j); + +/** + * Increase owner-count fields, deriving the tx reserve sponsor internally. + * + * Equivalent to the overload above, but resolves the sponsor via + * getEffectiveTxReserveSponsor(ctx, accountSle) instead of taking it explicitly. Use + * this when the sponsor is the transaction's reserve sponsor for accountSle + * (the common create path). Deletion paths, which derive the sponsor from an + * object's sfSponsor field, should keep using the explicit overload. + * + * @param ctx The apply-view context (view + tx) + * @param accountSle The account's ledger entry + * @param count Amount to add to the owner count + * @param j Journal for logging + */ +void +increaseOwnerCount( + ApplyViewContext ctx, + SLE::ref accountSle, + std::uint32_t count, + beast::Journal j); + +/** + * Convenience overload that accepts AccountID instead of SLE references. + * + * @param view The apply view for making changes + * @param account The account ID + * @param sponsor The optional sponsor account ID + * @param count Amount to add to the owner count + * @param j Journal for logging + */ +inline void +increaseOwnerCount( + ApplyView& view, + AccountID const& account, + std::optional const& sponsor, + std::uint32_t count, + beast::Journal j) +{ + increaseOwnerCount( + view, + view.peek(keylet::account(account)), + sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(), + count, + j); +} + +/** + * Decrease owner-count fields when the caller supplies the sponsor. + * + * This helper does not delete a ledger object. It updates reserve accounting + * after the caller has removed an owner-counted reserve, or for special + * owner-count changes whose sponsor cannot be derived from an object's + * sfSponsor field. + * + * @param view The apply view for making changes + * @param accountSle The account's ledger entry + * @param sponsorSle The sponsor's ledger entry (if applicable) + * @param count Amount to remove from the owner count + * @param j Journal for logging + */ +void +decreaseOwnerCount( + ApplyView& view, + SLE::ref accountSle, + SLE::ref sponsorSle, + std::uint32_t count, + beast::Journal j); + +/** + * Convenience overload that accepts AccountID instead of SLE references. + * + * @param view The apply view for making changes + * @param account The account ID + * @param sponsor The optional sponsor account ID + * @param count Amount to remove from the owner count + * @param j Journal for logging + */ +inline void +decreaseOwnerCount( + ApplyView& view, + AccountID const& account, + std::optional const& sponsor, + std::uint32_t count, + beast::Journal j) +{ + decreaseOwnerCount( + view, + view.peek(keylet::account(account)), + sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(), + count, + j); +} + +/** + * Decrease owner-count fields for an existing ledger object. + * + * This helper derives the reserve sponsor from objectSle's sfSponsor field, + * then updates the same owner-count fields as decreaseOwnerCount. Use this + * when removing an existing object whose reserve sponsor is stored on that + * object. + * + * @param view The apply view for making changes + * @param accountSle The account's ledger entry + * @param objectSle The object's ledger entry + * @param count Amount to remove from the owner count + * @param j Journal for logging + */ +void +decreaseOwnerCountForObject( + ApplyView& view, + SLE::ref accountSle, + SLE::ref objectSle, + std::uint32_t count, + beast::Journal j); + +/** + * Convenience overload that accepts AccountID instead of account SLE reference. + * + * @param view The apply view for making changes + * @param account The account ID + * @param objectSle The object's ledger entry + * @param count Amount to remove from the owner count + * @param j Journal for logging + */ +inline void +decreaseOwnerCountForObject( + ApplyView& view, + AccountID const& account, + SLE::ref objectSle, + std::uint32_t count, + beast::Journal j) +{ + SLE::ref accountSle = view.peek(keylet::account(account)); + decreaseOwnerCountForObject(view, accountSle, objectSle, count, j); +} + +/** + * Adjust a LoanBroker's owner count. + * + * A LoanBroker's sfOwnerCount tracks the number of outstanding loans on + * that broker; it is not a reserve-backed owner count and is distinct + * from the broker's pseudo-account's owner count. Loans can never carry a + * reserve sponsor (LoanSet rejects reserve sponsorship at preflight), so + * this never involves sponsor accounting and never invokes the + * ownerCountHook used for ACCOUNT_ROOT reserve tracking. + * + * @param view The apply view for making changes + * @param brokerSle The LoanBroker's ledger entry + * @param delta Amount to add (positive) or remove (negative) from the count + * @param j Journal for logging + */ +void +adjustLoanBrokerOwnerCount( + ApplyView& view, + SLE::ref brokerSle, + std::int32_t delta, + beast::Journal j); + +/** + * Returns IOU issuer transfer fee as Rate. Rate specifies * the fee as fractions of 1 billion. For example, 1% transfer rate * is represented as 1,010,000,000. * @param issuer The IOU issuer @@ -45,35 +339,40 @@ adjustOwnerCount(ApplyView& view, SLE::ref sle, std::int32_t amount, beast::Jour [[nodiscard]] Rate transferRate(ReadView const& view, AccountID const& issuer); -/** Generate a pseudo-account address from a pseudo owner key. - @param pseudoOwnerKey The key to generate the address from - @return The generated account ID -*/ +/** + * Generate a pseudo-account address from a pseudo owner key. + * @param pseudoOwnerKey The key to generate the address from + * @return The generated account ID + */ AccountID pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey); -/** Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account - if set. - - The list is constructed during initialization and is const after that. - Pseudo-account designator fields MUST be maintained by including the - SField::sMD_PseudoAccount flag in the SField definition. -*/ +/** + * Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account + * if set. + * + * The list is constructed during initialization and is const after that. + * Pseudo-account designator fields MUST be maintained by including the + * SField::sMD_PseudoAccount flag in the SField definition. + */ [[nodiscard]] std::vector const& getPseudoAccountFields(); -/** Returns true if and only if sleAcct is a pseudo-account or specific - pseudo-accounts in pseudoFieldFilter. - - Returns false if sleAcct is: - - NOT a pseudo-account OR - - NOT a ltACCOUNT_ROOT OR - - null pointer -*/ +/** + * Returns true if and only if sleAcct is a pseudo-account or specific + * pseudo-accounts in pseudoFieldFilter. + * + * Returns false if sleAcct is: + * - NOT a pseudo-account OR + * - NOT a ltACCOUNT_ROOT OR + * - null pointer + */ [[nodiscard]] bool isPseudoAccount(SLE::const_pointer sleAcct, std::set const& pseudoFieldFilter = {}); -/** Convenience overload that reads the account from the view. */ +/** + * Convenience overload that reads the account from the view. + */ [[nodiscard]] inline bool isPseudoAccount( ReadView const& view, @@ -94,11 +393,12 @@ isPseudoAccount( [[nodiscard]] std::expected createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField); -/** Checks the destination and tag. - - - Checks that the SLE is not null. - - If the SLE requires a destination tag, checks that there is a tag. -*/ +/** + * Checks the destination and tag. + * + * - Checks that the SLE is not null. + * - If the SLE requires a destination tag, checks that there is a tag. + */ [[nodiscard]] TER checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag); diff --git a/include/xrpl/ledger/helpers/CredentialHelpers.h b/include/xrpl/ledger/helpers/CredentialHelpers.h index 549644764f..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 { @@ -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( diff --git a/include/xrpl/ledger/helpers/DelegateHelpers.h b/include/xrpl/ledger/helpers/DelegateHelpers.h index 9cdad7173d..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 { @@ -23,13 +26,9 @@ checkTxPermission(SLE::const_ref 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( - SLE::const_ref 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 a0be52df99..25085c4252 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) { @@ -97,19 +95,20 @@ internalDirFirst( } // namespace detail /** @{ */ -/** Returns the first entry in the directory, advancing the index - - @deprecated These are legacy function that are considered deprecated - and will soon be replaced with an iterator-based model - that is easier to use. You should not use them in new code. - - @param view The view against which to operate - @param root The root (i.e. first page) of the directory to iterate - @param page The current page - @param index The index inside the current page - @param entry The entry at the current index - - @return true if the directory isn't empty; false otherwise +/** + * Returns the first entry in the directory, advancing the index + * + * @deprecated These are legacy function that are considered deprecated + * and will soon be replaced with an iterator-based model + * that is easier to use. You should not use them in new code. + * + * @param view The view against which to operate + * @param root The root (i.e. first page) of the directory to iterate + * @param page The current page + * @param index The index inside the current page + * @param entry The entry at the current index + * + * @return true if the directory isn't empty; false otherwise */ bool cdirFirst( @@ -129,19 +128,20 @@ dirFirst( /** @} */ /** @{ */ -/** Returns the next entry in the directory, advancing the index - - @deprecated These are legacy function that are considered deprecated - and will soon be replaced with an iterator-based model - that is easier to use. You should not use them in new code. - - @param view The view against which to operate - @param root The root (i.e. first page) of the directory to iterate - @param page The current page - @param index The index inside the current page - @param entry The entry at the current index - - @return true if the directory isn't empty; false otherwise +/** + * Returns the next entry in the directory, advancing the index + * + * @deprecated These are legacy function that are considered deprecated + * and will soon be replaced with an iterator-based model + * that is easier to use. You should not use them in new code. + * + * @param view The view against which to operate + * @param root The root (i.e. first page) of the directory to iterate + * @param page The current page + * @param index The index inside the current page + * @param entry The entry at the current index + * + * @return true if the directory isn't empty; false otherwise */ bool cdirNext( @@ -160,16 +160,19 @@ dirNext( uint256& entry); /** @} */ -/** Iterate all items in the given directory. */ +/** + * Iterate all items in the given directory. + */ void 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 - @param hint The directory page containing `after` - @param limit The maximum number of items to return - @return `false` if the iteration failed -*/ +/** + * Iterate all items after an item in the given directory. + * @param after The key of the item to start after + * @param hint The directory page containing `after` + * @param limit The maximum number of items to return + * @return `false` if the iteration failed + */ bool forEachItemAfter( ReadView const& view, @@ -179,19 +182,22 @@ forEachItemAfter( unsigned int limit, std::function const& f); -/** Iterate all items in an account's owner directory. */ +/** + * Iterate all items in an account's owner directory. + */ inline void forEachItem(ReadView const& view, AccountID const& id, std::function const& f) { forEachItem(view, keylet::ownerDir(id), f); } -/** Iterate all items after an item in an owner directory. - @param after The key of the item to start after - @param hint The directory page containing `after` - @param limit The maximum number of items to return - @return `false` if the iteration failed -*/ +/** + * Iterate all items after an item in an owner directory. + * @param after The key of the item to start after + * @param hint The directory page containing `after` + * @param limit The maximum number of items to return + * @return `false` if the iteration failed + */ inline bool forEachItemAfter( ReadView const& view, @@ -204,13 +210,16 @@ forEachItemAfter( return forEachItemAfter(view, keylet::ownerDir(id), after, hint, limit, f); } -/** Returns `true` if the directory is empty - @param key The key of the directory -*/ +/** + * Returns `true` if the directory is empty + * @param key The key of the directory + */ [[nodiscard]] bool dirIsEmpty(ReadView const& view, Keylet const& k); -/** Returns a function that sets the owner on a directory SLE */ +/** + * Returns a function that sets the owner on a directory SLE + */ [[nodiscard]] std::function describeOwnerDir(AccountID const& account); diff --git a/include/xrpl/ledger/helpers/EscrowHelpers.h b/include/xrpl/ledger/helpers/EscrowHelpers.h index bdb83230eb..9f54e53769 100644 --- a/include/xrpl/ledger/helpers/EscrowHelpers.h +++ b/include/xrpl/ledger/helpers/EscrowHelpers.h @@ -1,25 +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 namespace xrpl { template TER escrowUnlockApplyHelper( - ApplyView& view, + ApplyViewContext ctx, Rate lockedRate, SLE::ref sleDest, - STAmount const& xrpBalance, + XRPAmount xrpBalance, STAmount const& amount, AccountID const& issuer, AccountID const& sender, @@ -30,10 +42,10 @@ escrowUnlockApplyHelper( template <> inline TER escrowUnlockApplyHelper( - ApplyView& view, + ApplyViewContext ctx, Rate lockedRate, SLE::ref sleDest, - STAmount const& xrpBalance, + XRPAmount xrpBalance, STAmount const& amount, AccountID const& issuer, AccountID const& sender, @@ -41,8 +53,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; @@ -53,16 +65,26 @@ escrowUnlockApplyHelper( if (receiverIssuer) return tesSUCCESS; - if (!view.exists(trustLineKey) && createAsset) + if (!ctx.view.exists(trustLineKey) && createAsset) { // Can the account cover the trust line's reserve? - if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)}; - xrpBalance < view.fees().accountReserve(ownerCount + 1)) + auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE + + if (auto const ret = checkReserve( + ctx, + sleDest, + xrpBalance, + *sponsorSle, + {.ownerCountDelta = 1}, + journal, + tecNO_LINE_INSUF_RESERVE); + !isTesSuccess(ret)) { JLOG(journal.trace()) << "Trust line does not exist. " "Insufficient reserve to create line."; - - return tecNO_LINE_INSUF_RESERVE; + return ret; } Currency const currency = issue.currency; @@ -70,7 +92,7 @@ escrowUnlockApplyHelper( initialBalance.get().account = noAccount(); if (TER const ter = trustCreate( - view, // payment sandbox + ctx.view, // payment sandbox recvLow, // is dest low? issuer, // source receiver, // destination @@ -84,19 +106,20 @@ escrowUnlockApplyHelper( Issue(currency, receiver), // limit of zero 0, // quality in 0, // quality out + *sponsorSle, // sponsor journal); // journal !isTesSuccess(ter)) { return ter; // LCOV_EXCL_LINE } - view.update(sleDest); + ctx.view.update(sleDest); } - if (!view.exists(trustLineKey) && !receiverIssuer) + if (!ctx.view.exists(trustLineKey) && !receiverIssuer) return tecNO_LINE; - auto const xferRate = transferRate(view, amount); + auto const xferRate = transferRate(ctx.view, amount); // update if issuer rate is less than locked rate if (xferRate < lockedRate) lockedRate = xferRate; @@ -124,7 +147,7 @@ escrowUnlockApplyHelper( // of the funds if (!createAsset) { - auto const sleRippleState = view.peek(trustLineKey); + auto const sleRippleState = ctx.view.peek(trustLineKey); if (!sleRippleState) return tecINTERNAL; // LCOV_EXCL_LINE @@ -150,7 +173,7 @@ escrowUnlockApplyHelper( // if destination is not the issuer then transfer funds if (!receiverIssuer) { - auto const ter = directSendNoFee(view, issuer, receiver, finalAmt, true, journal); + auto const ter = directSendNoFee(ctx.view, issuer, receiver, finalAmt, true, journal); if (!isTesSuccess(ter)) return ter; // LCOV_EXCL_LINE } @@ -160,10 +183,10 @@ escrowUnlockApplyHelper( template <> inline TER escrowUnlockApplyHelper( - ApplyView& view, + ApplyViewContext ctx, Rate lockedRate, SLE::ref sleDest, - STAmount const& xrpBalance, + XRPAmount xrpBalance, STAmount const& amount, AccountID const& issuer, AccountID const& sender, @@ -175,28 +198,33 @@ escrowUnlockApplyHelper( bool const receiverIssuer = issuer == receiver; auto const mptID = amount.get().getMptID(); - auto const issuanceKey = keylet::mptIssuance(mptID); - if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && createAsset && !receiverIssuer) + auto const issuanceKey = keylet::mptokenIssuance(mptID); + auto const mptKeylet = keylet::mptoken(issuanceKey.key, receiver); + if (!ctx.view.exists(mptKeylet) && createAsset && !receiverIssuer) { - if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)}; - xrpBalance < view.fees().accountReserve(ownerCount + 1)) - { - return tecINSUFFICIENT_RESERVE; - } + auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE - if (auto const ter = createMPToken(view, mptID, receiver, 0); !isTesSuccess(ter)) + if (auto const ret = checkReserve( + ctx, sleDest, xrpBalance, *sponsorSle, {.ownerCountDelta = 1}, journal); + !isTesSuccess(ret)) + return ret; + + if (auto const ter = createMPToken(ctx.view, mptID, receiver, *sponsorSle, 0); + !isTesSuccess(ter)) { return ter; // LCOV_EXCL_LINE } // update owner count. - adjustOwnerCount(view, sleDest, 1, journal); + increaseOwnerCount(ctx.view, sleDest, *sponsorSle, 1, journal); } - if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && !receiverIssuer) + if (!ctx.view.exists(mptKeylet) && !receiverIssuer) return tecNO_PERMISSION; - auto const xferRate = transferRate(view, amount); + auto const xferRate = transferRate(ctx.view, amount); // update if issuer rate is less than locked rate if (xferRate < lockedRate) lockedRate = xferRate; @@ -219,11 +247,11 @@ escrowUnlockApplyHelper( finalAmt = amount.value() - xferFee; } return unlockEscrowMPT( - view, + ctx.view, sender, receiver, finalAmt, - view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt, + ctx.view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt, journal); } diff --git a/include/xrpl/ledger/helpers/LendingHelpers.h b/include/xrpl/ledger/helpers/LendingHelpers.h index 8de945233b..e2605e9ab7 100644 --- a/include/xrpl/ledger/helpers/LendingHelpers.h +++ b/include/xrpl/ledger/helpers/LendingHelpers.h @@ -1,11 +1,28 @@ #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 { @@ -46,7 +63,9 @@ static constexpr std::uint32_t kSecondsInYear = 365 * 24 * 60 * 60; Number loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval); -/// Ensure the periodic payment is always rounded consistently +/** + * Ensure the periodic payment is always rounded consistently + */ inline Number roundPeriodicPayment(Asset const& asset, Number const& periodicPayment, std::int32_t scale) { @@ -110,7 +129,8 @@ struct LoanPaymentParts operator==(LoanPaymentParts const& other) const; }; -/** This structure captures the parts of a loan state. +/** + * This structure captures the parts of a loan state. * * Whether the values are theoretical (unrounded) or rounded will depend on how * it was computed. @@ -307,12 +327,14 @@ struct PaymentComponents // - extra: An additional payment beyond the regular schedule (overpayment) PaymentSpecialCase specialCase = PaymentSpecialCase::None; - // Calculates the tracked interest portion of this payment. - // This is derived from the other components as: - // trackedValueDelta - trackedPrincipalDelta - trackedManagementFeeDelta - // - // @return The amount of tracked interest included in this payment that - // will be paid to the vault. + /** + * Calculates the tracked interest portion of this payment. + * This is derived from the other components as: + * trackedValueDelta - trackedPrincipalDelta - trackedManagementFeeDelta + * + * @return The amount of tracked interest included in this payment that + * will be paid to the vault. + */ [[nodiscard]] Number trackedInterestPart() const; }; @@ -384,7 +406,8 @@ struct LoanStateDeltas // The difference in management fee outstanding between two loan states. Number managementFee; - /* Calculates the total change across all components. + /** + * Calculates the total change across all components. * @return The sum of principal, interest, and management fee deltas. */ [[nodiscard]] Number diff --git a/include/xrpl/ledger/helpers/MPTokenHelpers.h b/include/xrpl/ledger/helpers/MPTokenHelpers.h index c709badab8..5418e5b26a 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,6 +29,17 @@ 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); @@ -46,7 +63,8 @@ isAnyFrozen( // //------------------------------------------------------------------------------ -/** Returns MPT transfer fee as Rate. Rate specifies +/** + * Returns MPT transfer fee as Rate. Rate specifies * the fee as fractions of 1 billion. For example, 1% transfer rate * is represented as 1,010,000,000. * @param issuanceID MPTokenIssuanceID of MPTTokenIssuance object @@ -71,7 +89,7 @@ canAddHolding(ReadView const& view, MPTIssue const& mptIssue); [[nodiscard]] TER authorizeMPToken( - ApplyView& view, + ApplyViewContext ctx, XRPAmount const& priorBalance, MPTID const& mptIssuanceID, AccountID const& account, @@ -79,7 +97,8 @@ authorizeMPToken( std::uint32_t flags = 0, std::optional holderID = std::nullopt); -/** Check if the account lacks required authorization for MPT. +/** + * Check if the account lacks required authorization for MPT. * * requireAuth check is recursive for MPT shares in a vault, descending to * assets in the vault, up to maxAssetCheckDepth recursion depth. This is @@ -94,7 +113,8 @@ requireAuth( AuthType authType = AuthType::Legacy, std::uint8_t depth = 0); -/** Enforce account has MPToken to match its authorization. +/** + * Enforce account has MPToken to match its authorization. * * Called from doApply - it will check for expired (and delete if found any) * credentials matching DomainID set in MPTokenIssuance. Must be called if @@ -102,50 +122,52 @@ requireAuth( */ [[nodiscard]] TER enforceMPTokenAuthorization( - ApplyView& view, + ApplyViewContext ctx, MPTID const& mptIssuanceID, AccountID const& account, XRPAmount const& priorBalance, beast::Journal j); -/** Resolve the underlying asset of a vault share. +/** + * 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). + * 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). + * @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. +/** + * 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. + * 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. + * 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. + * @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( @@ -156,22 +178,24 @@ canTransfer( WaiveMPTCanTransfer waive = WaiveMPTCanTransfer::No, std::uint8_t depth = 0); -/** Check whether @p asset may be traded on the DEX. +/** + * 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. + * 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. + * @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, std::uint8_t depth = 0); -/** Convenience to combine canTrade/Transfer. Returns tesSUCCESS if Asset is Issue. +/** + * Convenience to combine canTrade/Transfer. Returns tesSUCCESS if Asset is Issue. */ [[nodiscard]] TER canMPTTradeAndTransfer( @@ -188,7 +212,7 @@ canMPTTradeAndTransfer( [[nodiscard]] TER addEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, XRPAmount priorBalance, MPTIssue const& mptIssue, @@ -196,7 +220,7 @@ addEmptyHolding( [[nodiscard]] TER removeEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, MPTIssue const& mptIssue, beast::Journal journal); @@ -228,6 +252,7 @@ createMPToken( ApplyView& view, MPTID const& mptIssuanceID, AccountID const& account, + SLE::ref sponsorSle, std::uint32_t const flags); TER @@ -235,6 +260,7 @@ checkCreateMPT( xrpl::ApplyView& view, xrpl::MPTIssue const& mptIssue, xrpl::AccountID const& holder, + SLE::ref sponsorSle, beast::Journal j); //------------------------------------------------------------------------------ @@ -255,7 +281,8 @@ availableMPTAmount(SLE const& sleIssuance); std::int64_t availableMPTAmount(ReadView const& view, MPTID const& mptID); -/** Checks for two types of OutstandingAmount overflow during a send operation. +/** + * Checks for two types of OutstandingAmount overflow during a send operation. * 1. **Direct directSendNoFee (Overflow: No):** A true overflow check when * `OutstandingAmount > MaximumAmount`. This threshold is used for direct * directSendNoFee transactions that bypass the payment engine. @@ -280,7 +307,8 @@ isMPTOverflow( [[nodiscard]] STAmount issuerFundsToSelfIssue(ReadView const& view, MPTIssue const& issue); -/** Facilitate tracking of MPT sold by an issuer owning MPT sell offer. +/** + * Facilitate tracking of MPT sold by an issuer owning MPT sell offer. * See ApplyView::issuerSelfDebitHookMPT(). */ void diff --git a/include/xrpl/ledger/helpers/NFTokenHelpers.h b/include/xrpl/ledger/helpers/NFTokenHelpers.h index 362cfe5a8c..d9d195c559 100644 --- a/include/xrpl/ledger/helpers/NFTokenHelpers.h +++ b/include/xrpl/ledger/helpers/NFTokenHelpers.h @@ -1,30 +1,48 @@ #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 { -/** Delete up to a specified number of offers from the specified token offer - * directory. */ +/** + * Delete up to a specified number of offers from the specified token offer + * directory. + */ std::size_t removeTokenOffersWithLimit( ApplyView& view, Keylet const& directory, std::size_t maxDeletableOffers); -/** Finds the specified token in the owner's token directory. */ +/** + * Finds the specified token in the owner's token directory. + */ std::optional findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID); -/** Finds the token in the owner's token directory. Returns token and page. */ +/** + * Finds the token in the owner's token directory. Returns token and page. + */ struct TokenAndPage { STObject token; @@ -37,33 +55,39 @@ struct TokenAndPage std::optional findTokenAndPage(ApplyView& view, AccountID const& owner, uint256 const& nftokenID); -/** Insert the token in the owner's token directory. */ +/** + * Insert the token in the owner's token directory. + */ TER insertToken(ApplyView& view, AccountID owner, STObject&& nft); -/** Remove the token from the owner's token directory. */ +/** + * Remove the token from the owner's token directory. + */ TER removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID); TER removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, SLE::ref page); -/** Deletes the given token offer. - - An offer is tracked in two separate places: - - The token's 'buy' directory, if it's a buy offer; or - - The token's 'sell' directory, if it's a sell offer; and - - The owner directory of the account that placed the offer. - - The offer also consumes one incremental reserve. +/** + * Deletes the given token offer. + * + * An offer is tracked in two separate places: + * - The token's 'buy' directory, if it's a buy offer; or + * - The token's 'sell' directory, if it's a sell offer; and + * - The owner directory of the account that placed the offer. + * + * The offer also consumes one incremental reserve. */ bool deleteTokenOffer(ApplyView& view, SLE::ref offer); -/** Repairs the links in an NFTokenPage directory. - - Returns true if a repair took place, otherwise false. -*/ +/** + * Repairs the links in an NFTokenPage directory. + * + * Returns true if a repair took place, otherwise false. + */ bool repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner); @@ -77,7 +101,9 @@ changeTokenURI( uint256 const& nftokenID, std::optional const& uri); -/** Preflight checks shared by NFTokenCreateOffer and NFTokenMint */ +/** + * Preflight checks shared by NFTokenCreateOffer and NFTokenMint + */ NotTEC tokenOfferCreatePreflight( AccountID const& acctID, @@ -89,7 +115,9 @@ tokenOfferCreatePreflight( std::optional const& owner = std::nullopt, std::uint32_t txFlags = tfSellNFToken); -/** Preclaim checks shared by NFTokenCreateOffer and NFTokenMint */ +/** + * Preclaim checks shared by NFTokenCreateOffer and NFTokenMint + */ TER tokenOfferCreatePreclaim( ReadView const& view, @@ -103,7 +131,9 @@ tokenOfferCreatePreclaim( std::optional const& owner = std::nullopt, std::uint32_t txFlags = tfSellNFToken); -/** doApply implementation shared by NFTokenCreateOffer and NFTokenMint */ +/** + * doApply implementation shared by NFTokenCreateOffer and NFTokenMint + */ TER tokenOfferCreateApply( ApplyView& view, diff --git a/include/xrpl/ledger/helpers/OfferHelpers.h b/include/xrpl/ledger/helpers/OfferHelpers.h index fc863dff0a..524288ea33 100644 --- a/include/xrpl/ledger/helpers/OfferHelpers.h +++ b/include/xrpl/ledger/helpers/OfferHelpers.h @@ -7,18 +7,19 @@ namespace xrpl { -/** Delete an offer. - - Requirements: - The offer must exist. - The caller must have already checked permissions. - - @param view The ApplyView to modify. - @param sle The offer to delete. - @param j Journal for logging. - - @return tesSUCCESS on success, otherwise an error code. -*/ +/** + * Delete an offer. + * + * Requirements: + * The offer must exist. + * The caller must have already checked permissions. + * + * @param view The ApplyView to modify. + * @param sle The offer to delete. + * @param j Journal for logging. + * + * @return tesSUCCESS on success, otherwise an error code. + */ // [[nodiscard]] // nodiscard commented out so Flow, BookTip and others compile. TER offerDelete(ApplyView& view, SLE::ref sle, beast::Journal j); diff --git a/include/xrpl/ledger/helpers/OracleHelpers.h b/include/xrpl/ledger/helpers/OracleHelpers.h new file mode 100644 index 0000000000..da04618e46 --- /dev/null +++ b/include/xrpl/ledger/helpers/OracleHelpers.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include // IWYU pragma: keep +#include + +#include +#include + +namespace xrpl { + +constexpr std::uint32_t kMinOracleReserveCount = 1; +constexpr std::uint32_t kMaxOracleReserveCount = 2; +constexpr std::size_t kOracleReserveCountThreshold = 5; + +template + requires requires(T const& t) { t.size(); } +inline std::uint32_t +calculateOracleReserve(T const& priceDataSeries) +{ + return priceDataSeries.size() > kOracleReserveCountThreshold ? kMaxOracleReserveCount + : kMinOracleReserveCount; +} + +inline std::uint32_t +calculateOracleReserve(SLE::const_ref oracleSle) +{ + return calculateOracleReserve(oracleSle->getFieldArray(sfPriceDataSeries)); +} + +} // namespace xrpl diff --git a/include/xrpl/ledger/helpers/PaymentChannelHelpers.h b/include/xrpl/ledger/helpers/PaymentChannelHelpers.h index 810907b0af..5e1f590c58 100644 --- a/include/xrpl/ledger/helpers/PaymentChannelHelpers.h +++ b/include/xrpl/ledger/helpers/PaymentChannelHelpers.h @@ -1,13 +1,53 @@ #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(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 3aaaa541fd..a0508d074f 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 //------------------------------------------------------------------------------ // @@ -24,13 +31,14 @@ namespace xrpl { // //------------------------------------------------------------------------------ -/** Calculate the maximum amount of IOUs that an account can hold - @param view the ledger to check against. - @param account the account of interest. - @param issuer the issuer of the IOU. - @param currency the IOU to check. - @return The maximum amount that can be held. -*/ +/** + * Calculate the maximum amount of IOUs that an account can hold + * @param view the ledger to check against. + * @param account the account of interest. + * @param issuer the issuer of the IOU. + * @param currency the IOU to check. + * @return The maximum amount that can be held. + */ /** @{ */ STAmount creditLimit( @@ -43,12 +51,13 @@ IOUAmount creditLimit2(ReadView const& v, AccountID const& acc, AccountID const& iss, Currency const& cur); /** @} */ -/** Returns the amount of IOUs issued by issuer that are held by an account - @param view the ledger to check against. - @param account the account of interest. - @param issuer the issuer of the IOU. - @param currency the IOU to check. -*/ +/** + * Returns the amount of IOUs issued by issuer that are held by an account + * @param view the ledger to check against. + * @param account the account of interest. + * @param issuer the issuer of the IOU. + * @param currency the IOU to check. + */ /** @{ */ STAmount creditBalance( @@ -127,10 +136,11 @@ checkDeepFrozen(ReadView const& view, AccountID const& account, Issue const& iss // //------------------------------------------------------------------------------ -/** Create a trust line - - This can set an initial balance. -*/ +/** + * Create a trust line + * + * This can set an initial balance. + */ [[nodiscard]] TER trustCreate( ApplyView& view, @@ -149,6 +159,7 @@ trustCreate( // Issuer should be the account being set. std::uint32_t uQualityIn, std::uint32_t uQualityOut, + SLE::ref sponsorSle, beast::Journal j); [[nodiscard]] TER @@ -171,6 +182,7 @@ issueIOU( AccountID const& account, STAmount const& amount, Issue const& issue, + SLE::ref sponsorSle, beast::Journal j); [[nodiscard]] TER @@ -187,7 +199,8 @@ redeemIOU( // //------------------------------------------------------------------------------ -/** Check if the account lacks required authorization. +/** + * Check if the account lacks required authorization. * * Return tecNO_AUTH or tecNO_LINE if it does * and tesSUCCESS otherwise. @@ -211,7 +224,8 @@ requireAuth( AccountID const& account, AuthType authType = AuthType::Legacy); -/** Check if the destination account is allowed +/** + * Check if the destination account is allowed * to receive IOU. Return terNO_RIPPLE if rippling is * disabled on both sides and tesSUCCESS otherwise. */ @@ -224,11 +238,13 @@ canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, Acc // //------------------------------------------------------------------------------ -/// Any transactors that call addEmptyHolding() in doApply must call -/// canAddHolding() in preflight with the same View and Asset +/** + * Any transactors that call addEmptyHolding() in doApply must call + * canAddHolding() in preflight with the same View and Asset + */ [[nodiscard]] TER addEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, XRPAmount priorBalance, Issue const& issue, @@ -236,12 +252,13 @@ addEmptyHolding( [[nodiscard]] TER removeEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, Issue const& issue, beast::Journal journal); -/** Delete trustline to AMM. The passed `sle` must be obtained from a prior +/** + * Delete trustline to AMM. The passed `sle` must be obtained from a prior * call to view.peek(). Fail if neither side of the trustline is AMM or * if ammAccountID is seated and is not one of the trustline's side. */ @@ -252,7 +269,8 @@ deleteAMMTrustLine( std::optional const& ammAccountID, beast::Journal j); -/** Delete AMMs MPToken. The passed `sle` must be obtained from a prior +/** + * Delete AMMs MPToken. The passed `sle` must be obtained from a prior * call to view.peek(). */ [[nodiscard]] TER diff --git a/include/xrpl/ledger/helpers/SponsorHelpers.h b/include/xrpl/ledger/helpers/SponsorHelpers.h new file mode 100644 index 0000000000..98bf419140 --- /dev/null +++ b/include/xrpl/ledger/helpers/SponsorHelpers.h @@ -0,0 +1,204 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace xrpl { + +/** + * Whether the given transaction type may use reserve sponsorship (v1). + * + * Reserve sponsorship is restricted to an explicit allow-list of transaction + * types; all others reject spfSponsorReserve at preflight. + */ +bool +isReserveSponsorAllowed(TxType txType); + +/** + * Whether the transaction's fee is sponsored (sfSponsor present + spfSponsorFee set). + */ +inline bool +isFeeSponsored(STTx const& tx) +{ + return tx.isFieldPresent(sfSponsor) && ((tx.getFieldU32(sfSponsorFlags) & spfSponsorFee) != 0u); +} + +/** + * Whether the transaction's reserve is sponsored (sfSponsor present + spfSponsorReserve set). + */ +inline bool +isReserveSponsored(STTx const& tx) +{ + return tx.isFieldPresent(sfSponsor) && + ((tx.getFieldU32(sfSponsorFlags) & spfSponsorReserve) != 0u); +} + +/** + * Return the AccountID of the transaction's reserve sponsor, or nullopt if unsponsored. + */ +std::optional +getTxReserveSponsorID(STTx const& tx); + +/** + * Return a mutable SLE for the transaction's reserve sponsor account. + * + * @param ctx The apply-view context (view + tx) + * @return The sponsor account SLE, a null pointer if the tx is not + * reserve-sponsored, or tecINTERNAL if the sponsor account cannot + * be loaded (an already-checked invariant). + */ +std::expected +getTxReserveSponsor(ApplyViewContext ctx); + +/** + * Return a read-only SLE for the transaction's reserve sponsor account. + * + * @param view The ledger read view + * @param tx The transaction to inspect + * @return The sponsor account SLE, a null pointer if the tx is not + * reserve-sponsored, or tecINTERNAL if the sponsor account cannot + * be loaded (an already-checked invariant). + */ +std::expected +getTxReserveSponsor(ReadView const& view, STTx const& tx); + +/** + * The transaction's reserve sponsor for the given account, if applicable. + * + * A reserve sponsor only covers the transaction submitter's own objects, so + * this returns the tx reserve sponsor SLE only when accountSle is the tx's own + * (non-pseudo) account; otherwise it returns a null sponsor pointer. This is + * the single source of truth for the "sponsor applies to tx.Account only" rule + * that the sponsor-deriving helper overloads in AccountRootHelpers rely on. + * + * @param ctx The apply-view context (view + tx) + * @param accountSle The account whose sponsor is being resolved + * @return The sponsor SLE (nullptr if unsponsored), or tecINTERNAL if the + * sponsor account cannot be loaded (an already-checked invariant) + */ +[[nodiscard]] std::expected +getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::const_ref accountSle); + +/** + * Return the AccountID stored in the given sponsor field of a ledger entry, or nullopt if absent. + */ +std::optional +getLedgerEntryReserveSponsorID(SLE::const_ref sle, SF_ACCOUNT const& field = sfSponsor); + +/** + * Return a mutable SLE for the reserve sponsor recorded on a ledger entry. + * + * Reads the sponsor AccountID from @p field on @p sle and peeks the + * corresponding account root in @p view. + * + * @param view The mutable apply view + * @param sle The ledger entry whose sponsor field is inspected + * @param field The field that holds the sponsor AccountID (defaults to sfSponsor) + * @return The sponsor account SLE, or a null pointer if the entry is unsponsored. + */ +SLE::pointer +getLedgerEntryReserveSponsor( + ApplyView& view, + SLE::const_ref sle, + SF_ACCOUNT const& field = sfSponsor); + +/** + * Stamp a reserve sponsor onto a ledger entry using an explicit sponsor SLE. + * + * Sets @p field on @p sle to the AccountID from @p sponsorSle. A no-op when + * @p sponsorSle is null (unsponsored). For RippleState entries the field must + * be sfHighSponsor or sfLowSponsor; for all other entry types it must be + * sfSponsor. + * + * @param sle The ledger entry to stamp + * @param sponsorSle The sponsor's account root SLE (null → no-op) + * @param field The sponsor field to set (defaults to sfSponsor) + */ +void +addSponsorToLedgerEntry( + SLE::ref sle, + SLE::const_ref sponsorSle, + SF_ACCOUNT const& field = sfSponsor); + +/** + * Stamp the transaction's reserve sponsor onto a newly-created ledger entry. + * + * Equivalent to the overload above, but resolves the sponsor via + * getTxReserveSponsor(ctx) instead of taking it explicitly. A no-op when the + * transaction is not reserve-sponsored. The entry is assumed to be owned by + * the transaction submitter, which is the only account a tx reserve sponsor + * can cover. + */ +void +addSponsorToLedgerEntry(ApplyViewContext ctx, SLE::ref sle, SF_ACCOUNT const& field = sfSponsor); + +/** + * Remove the reserve sponsor field from a ledger entry. + * + * A no-op when @p field is not present on @p sle. For RippleState entries + * the field must be sfHighSponsor or sfLowSponsor; for all other entry types + * it must be sfSponsor. + * + * @param sle The ledger entry to modify + * @param field The sponsor field to clear (defaults to sfSponsor) + */ +void +removeSponsorFromLedgerEntry(SLE::ref sle, SF_ACCOUNT const& field = sfSponsor); + +/** + * Whether @p account is the owner of a ledger entry for sponsorship purposes. + * + * Ownership rules vary by entry type. For RippleState entries the owner is + * whichever side of the trust line holds the reserve. For credentials, the + * owner is the subject once accepted and the issuer before acceptance. + * + * @param view The ledger read view (used for SignerList lookup) + * @param sle The ledger entry whose owner is checked + * @param account The candidate account to match against + * @return true if @p account owns @p sle, false otherwise. + */ +bool +isLedgerEntryOwner(ReadView const& view, SLE const& sle, AccountID const& account); + +/** + * Whether this ledger entry type can have a reserve sponsor attached to it. + */ +bool +isLedgerEntrySupportedBySponsorship(SLE const& sle); + +/** + * Return the number of owner-count units the ledger entry consumes. + * + * Most entries cost 1. Exceptions: Oracles scale with their price-data series + * size, Vaults cost 2 (vault + pseudo-account), and legacy SignerList entries + * (pre-MultiSignReserve) cost 2 + signer count. + */ +std::uint32_t +getLedgerEntryOwnerCount(SLE const& sle); + +/** + * Return the SField used to store the reserve sponsor for @p owner on @p sle. + * + * For most entry types this is sfSponsor. RippleState entries use + * sfHighSponsor or sfLowSponsor depending on which side of the trust line + * @p owner holds. + * + * @param sle The ledger entry + * @param owner The account whose sponsor field is needed + * @return sfHighSponsor, sfLowSponsor, or sfSponsor as appropriate. + */ +SF_ACCOUNT const& +getLedgerEntrySponsorField(SLE const& sle, AccountID const& owner); + +} // namespace xrpl diff --git a/include/xrpl/ledger/helpers/TokenHelpers.h b/include/xrpl/ledger/helpers/TokenHelpers.h index f736e51d28..501101136a 100644 --- a/include/xrpl/ledger/helpers/TokenHelpers.h +++ b/include/xrpl/ledger/helpers/TokenHelpers.h @@ -1,15 +1,23 @@ #pragma once +#include #include #include #include +#include #include +#include #include #include #include +#include #include +#include +#include +#include #include +#include #include namespace xrpl { @@ -20,21 +28,30 @@ namespace xrpl { // //------------------------------------------------------------------------------ -/** Controls the treatment of frozen account balances */ +/** + * Controls the treatment of frozen account balances + */ enum class FreezeHandling { IgnoreFreeze, ZeroIfFrozen }; -/** Controls the treatment of unauthorized MPT balances */ +/** + * Controls the treatment of unauthorized MPT balances + */ enum class AuthHandling { IgnoreAuth, ZeroIfUnauthorized }; -/** Controls whether to include the account's full spendable balance */ +/** + * Controls whether to include the account's full spendable balance + */ enum class SpendableHandling { SimpleBalance, FullBalance }; enum class WaiveTransferFee : bool { No = false, Yes }; -/** Controls whether accountSend is allowed to overflow OutstandingAmount **/ +/** + * Controls whether accountSend is allowed to overflow OutstandingAmount * + */ enum class AllowMPTOverflow : bool { No = false, Yes }; -/** Controls whether canTransfer enforces lsfMPTCanTransfer on MPTs. +/** + * 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, @@ -73,9 +90,9 @@ isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& 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. + * 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( @@ -114,9 +131,9 @@ isDeepFrozen( std::uint8_t depth = 0); /** - * 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. + * 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 isDeepFrozen( @@ -131,6 +148,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) @@ -212,7 +294,8 @@ accountFunds( AuthHandling authHandling, beast::Journal j); -/** Returns the transfer fee as Rate based on the type of token +/** + * Returns the transfer fee as Rate based on the type of token * @param view The ledger view * @param amount The amount to transfer */ @@ -230,7 +313,7 @@ canAddHolding(ReadView const& view, Asset const& asset); [[nodiscard]] TER addEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, XRPAmount priorBalance, Asset const& asset, @@ -238,7 +321,7 @@ addEmptyHolding( [[nodiscard]] TER removeEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, Asset const& asset, beast::Journal journal); @@ -277,7 +360,8 @@ canTransfer( // --> bCheckIssuer : normally require issuer to be involved. // [[nodiscard]] // nodiscard commented out so DirectStep.cpp compiles. -/** Calls static directSendNoFeeIOU if saAmount represents Issue. +/** + * Calls static directSendNoFeeIOU if saAmount represents Issue. * Calls static directSendNoFeeMPT if saAmount represents MPTIssue. */ TER @@ -289,7 +373,8 @@ directSendNoFee( bool bCheckIssuer, beast::Journal j); -/** Calls static accountSendIOU if saAmount represents Issue. +/** + * Calls static accountSendIOU if saAmount represents Issue. * Calls static accountSendMPT if saAmount represents MPTIssue. */ [[nodiscard]] TER @@ -299,11 +384,13 @@ accountSend( AccountID const& to, STAmount const& saAmount, beast::Journal j, + SLE::ref sponsorSle = {}, WaiveTransferFee waiveFee = WaiveTransferFee::No, AllowMPTOverflow allowOverflow = AllowMPTOverflow::No); using MultiplePaymentDestinations = std::vector>; -/** Like accountSend, except one account is sending multiple payments (with the +/** + * Like accountSend, except one account is sending multiple payments (with the * same asset!) simultaneously * * Calls static accountSendMultiIOU if saAmount represents Issue. diff --git a/include/xrpl/ledger/helpers/VaultHelpers.h b/include/xrpl/ledger/helpers/VaultHelpers.h index 2344b4de77..1bd1663314 100644 --- a/include/xrpl/ledger/helpers/VaultHelpers.h +++ b/include/xrpl/ledger/helpers/VaultHelpers.h @@ -9,57 +9,63 @@ namespace xrpl { -/** From the perspective of a vault, return the number of shares to give - depositor when they offer a fixed amount of assets. Note, since shares are - MPT, this number is integral and always truncated in this calculation. - - @param vault The vault SLE. - @param issuance The MPTokenIssuance SLE for the vault's shares. - @param assets The amount of assets to convert. - - @return The number of shares, or nullopt on error. -*/ +/** + * From the perspective of a vault, return the number of shares to give + * depositor when they offer a fixed amount of assets. Note, since shares are + * MPT, this number is integral and always truncated in this calculation. + * + * @param vault The vault SLE. + * @param issuance The MPTokenIssuance SLE for the vault's shares. + * @param assets The amount of assets to convert. + * + * @return The number of shares, or nullopt on error. + */ [[nodiscard]] std::optional 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 - MPT, they are always an integral number. - - @param vault The vault SLE. - @param issuance The MPTokenIssuance SLE for the vault's shares. - @param shares The amount of shares to convert. - - @return The number of assets, or nullopt on error. -*/ +/** + * 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 + * MPT, they are always an integral number. + * + * @param vault The vault SLE. + * @param issuance The MPTokenIssuance SLE for the vault's shares. + * @param shares The amount of shares to convert. + * + * @return The number of assets, or nullopt on error. + */ [[nodiscard]] std::optional sharesToAssetsDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& shares); -/** Controls whether to truncate shares instead of rounding. */ +/** + * 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. -*/ +/** + * 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 - unless explicitly requested to be truncated instead. - - @param vault The vault SLE. - @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. -*/ +/** + * 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 + * unless explicitly requested to be truncated instead. + * + * @param vault The vault SLE. + * @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( SLE::const_ref vault, @@ -68,18 +74,19 @@ assetsToSharesWithdraw( 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 - MPT, they are always an integral number. - - @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. -*/ +/** + * 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 + * MPT, they are always an integral number. + * + * @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( SLE::const_ref vault, @@ -87,15 +94,16 @@ sharesToAssetsWithdraw( 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. -*/ +/** + * 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); 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 456f769922..752afac9c4 100644 --- a/include/xrpl/net/HTTPClient.h +++ b/include/xrpl/net/HTTPClient.h @@ -7,13 +7,15 @@ #include #include +#include #include #include #include namespace xrpl { -/** Provides an asynchronous HTTP client implementation with optional SSL. +/** + * Provides an asynchronous HTTP client implementation with optional SSL. */ class HTTPClient { @@ -29,14 +31,15 @@ public: bool sslVerify, beast::Journal j); - /** Destroys the global SSL context created by initializeSSLContext(). + /** + * Destroys the global SSL context created by initializeSSLContext(). * - * This releases the underlying boost::asio::ssl::context and any - * associated OpenSSL resources. Must not be called while any - * HTTPClient requests are in flight. + * This releases the underlying boost::asio::ssl::context and any + * associated OpenSSL resources. Must not be called while any + * HTTPClient requests are in flight. * - * @note Currently only called from tests during teardown. In production, - * the SSL context lives for the lifetime of the process. + * @note Currently only called from tests during teardown. In production, + * the SSL context lives for the lifetime of the process. */ static void cleanupSSLContext(); diff --git a/include/xrpl/net/HTTPClientSSLContext.h b/include/xrpl/net/HTTPClientSSLContext.h index ca1983f141..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 @@ -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); } } diff --git a/include/xrpl/net/RegisterSSLCerts.h b/include/xrpl/net/RegisterSSLCerts.h index e313b1cb06..004f893515 100644 --- a/include/xrpl/net/RegisterSSLCerts.h +++ b/include/xrpl/net/RegisterSSLCerts.h @@ -1,17 +1,18 @@ #pragma once -#include +#include #include namespace xrpl { -/** Register default SSL certificates. - - Register the system default SSL root certificates. On linux/mac, - this just calls asio's `set_default_verify_paths` to look in standard - operating system locations. On windows, it uses the OS certificate - store accessible via CryptoAPI. -*/ +/** + * Register default SSL certificates. + * + * Register the system default SSL root certificates. On linux/mac, + * this just calls asio's `set_default_verify_paths` to look in standard + * operating system locations. On windows, it uses the OS certificate + * store accessible via CryptoAPI. + */ void registerSSLCerts(boost::asio::ssl::context&, boost::system::error_code&, beast::Journal j); diff --git a/include/xrpl/nodestore/Backend.h b/include/xrpl/nodestore/Backend.h index 0061890237..564a874c5e 100644 --- a/include/xrpl/nodestore/Backend.h +++ b/include/xrpl/nodestore/Backend.h @@ -1,38 +1,51 @@ #pragma once +#include +#include +#include #include +#include #include +#include +#include +#include +#include +#include namespace xrpl::NodeStore { -/** A backend used for the NodeStore. - - The NodeStore uses a swappable backend so that other database systems - can be tried. Different databases may offer various features such - as improved performance, fault tolerant or distributed storage, or - all in-memory operation. - - A given instance of a backend is fixed to a particular key size. -*/ +/** + * A backend used for the NodeStore. + * + * The NodeStore uses a swappable backend so that other database systems + * can be tried. Different databases may offer various features such + * as improved performance, fault tolerant or distributed storage, or + * all in-memory operation. + * + * A given instance of a backend is fixed to a particular key size. + */ class Backend { public: - /** Destroy the backend. - - All open files are closed and flushed. If there are batched writes - or other tasks scheduled, they will be completed before this call - returns. - */ + /** + * Destroy the backend. + * + * All open files are closed and flushed. If there are batched writes + * or other tasks scheduled, they will be completed before this call + * returns. + */ virtual ~Backend() = default; - /** Get the human-readable name of this backend. - This is used for diagnostic output. - */ + /** + * Get the human-readable name of this backend. + * This is used for diagnostic output. + */ virtual std::string getName() = 0; - /** Get the block size for backends that support it + /** + * Get the block size for backends that support it */ [[nodiscard]] virtual std::optional getBlockSize() const @@ -40,25 +53,28 @@ public: return std::nullopt; } - /** Open the backend. - @param createIfMissing Create the database files if necessary. - This allows the caller to catch exceptions. - */ + /** + * Open the backend. + * @param createIfMissing Create the database files if necessary. + * This allows the caller to catch exceptions. + */ virtual void open(bool createIfMissing = true) = 0; - /** Returns true is the database is open. + /** + * Returns true is the database is open. */ virtual bool isOpen() = 0; - /** Open the backend. - @param createIfMissing Create the database files if necessary. - @param appType Deterministic appType used to create a backend. - @param uid Deterministic uid used to create a backend. - @param salt Deterministic salt used to create a backend. - @throws std::runtime_error is function is called not for NuDB backend. - */ + /** + * Open the backend. + * @param createIfMissing Create the database files if necessary. + * @param appType Deterministic appType used to create a backend. + * @param uid Deterministic uid used to create a backend. + * @param salt Deterministic salt used to create a backend. + * @throws std::runtime_error is function is called not for NuDB backend. + */ virtual void open(bool createIfMissing, uint64_t appType, uint64_t uid, uint64_t salt) { @@ -66,60 +82,70 @@ public: "Deterministic appType/uid/salt not supported by backend " + getName()); } - /** Close the backend. - This allows the caller to catch exceptions. - */ + /** + * Close the backend. + * This allows the caller to catch exceptions. + */ virtual void close() = 0; - /** Fetch a single object. - If the object is not found or an error is encountered, the - result will indicate the condition. - @note This will be called concurrently. - @param hash The hash of the object. - @param pObject [out] The created object if successful. - @return The result of the operation. - */ + /** + * Fetch a single object. + * If the object is not found or an error is encountered, the + * result will indicate the condition. + * @note This will be called concurrently. + * @param hash The hash of the object. + * @param pObject [out] The created object if successful. + * @return The result of the operation. + */ virtual Status fetch(uint256 const& hash, std::shared_ptr* pObject) = 0; - /** Store a single object. - Depending on the implementation this may happen immediately - or deferred using a scheduled task. - @note This will be called concurrently. - @param object The object to store. - */ + /** + * Store a single object. + * Depending on the implementation this may happen immediately + * or deferred using a scheduled task. + * @note This will be called concurrently. + * @param object The object to store. + */ virtual void store(std::shared_ptr const& object) = 0; - /** Store a group of objects. - @note This function will not be called concurrently with - itself or @ref store. - */ + /** + * Store a group of objects. + * @note This function will not be called concurrently with + * itself or @ref store. + */ virtual void storeBatch(Batch const& batch) = 0; virtual void sync() = 0; - /** Visit every object in the database - This is usually called during import. - @note This routine will not be called concurrently with itself - or other methods. - @see import - */ + /** + * Visit every object in the database + * This is usually called during import. + * @note This routine will not be called concurrently with itself + * or other methods. + * @see import + */ virtual void forEach(std::function)> f) = 0; - /** Estimate the number of write operations pending. */ + /** + * Estimate the number of write operations pending. + */ virtual int getWriteLoad() = 0; - /** Remove contents on disk upon destruction. */ + /** + * Remove contents on disk upon destruction. + */ virtual void setDeletePath() = 0; - /** Perform consistency checks on database. + /** + * Perform consistency checks on database. * * This method is implemented only by NuDBBackend. It is not yet called * anywhere, but it might be a good idea to one day call it at startup to @@ -130,7 +156,9 @@ public: { } - /** Returns the number of file descriptors the backend expects to need. */ + /** + * Returns the number of file descriptors the backend expects to need. + */ [[nodiscard]] virtual int fdRequired() const = 0; }; diff --git a/include/xrpl/nodestore/Database.h b/include/xrpl/nodestore/Database.h index 68c5dcefb6..96ba91bd76 100644 --- a/include/xrpl/nodestore/Database.h +++ b/include/xrpl/nodestore/Database.h @@ -1,13 +1,25 @@ #pragma once -#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; @@ -15,100 +27,109 @@ class Section; namespace xrpl::NodeStore { -/** Persistency layer for NodeObject - - A Node is a ledger object which is uniquely identified by a key, which is - the 256-bit hash of the body of the node. The payload is a variable length - block of serialized data. - - All ledger data is stored as node objects and as such, needs to be persisted - between launches. Furthermore, since the set of node objects will in - general be larger than the amount of available memory, purged node objects - which are later accessed must be retrieved from the node store. - - @see NodeObject -*/ +/** + * Persistency layer for NodeObject + * + * A Node is a ledger object which is uniquely identified by a key, which is + * the 256-bit hash of the body of the node. The payload is a variable length + * block of serialized data. + * + * All ledger data is stored as node objects and as such, needs to be persisted + * between launches. Furthermore, since the set of node objects will in + * general be larger than the amount of available memory, purged node objects + * which are later accessed must be retrieved from the node store. + * + * @see NodeObject + */ class Database { public: Database() = delete; - /** Construct the node store. - - @param scheduler The scheduler to use for performing asynchronous tasks. - @param readThreads The number of asynchronous read threads to create. - @param config The configuration settings - @param journal Destination for logging output. - */ + /** + * Construct the node store. + * + * @param scheduler The scheduler to use for performing asynchronous tasks. + * @param readThreads The number of asynchronous read threads to create. + * @param config The configuration settings + * @param journal Destination for logging output. + */ Database(Scheduler& scheduler, int readThreads, Section const& config, beast::Journal j); - /** Destroy the node store. - All pending operations are completed, pending writes flushed, - and files closed before this returns. - */ + /** + * Destroy the node store. + * All pending operations are completed, pending writes flushed, + * and files closed before this returns. + */ virtual ~Database(); - /** Retrieve the name associated with this backend. - This is used for diagnostics and may not reflect the actual path - or paths used by the underlying backend. - */ + /** + * Retrieve the name associated with this backend. + * This is used for diagnostics and may not reflect the actual path + * or paths used by the underlying backend. + */ virtual std::string getName() const = 0; - /** Import objects from another database. */ + /** + * Import objects from another database. + */ virtual void importDatabase(Database& source) = 0; - /** Retrieve the estimated number of pending write operations. - This is used for diagnostics. - */ + /** + * Retrieve the estimated number of pending write operations. + * This is used for diagnostics. + */ virtual std::int32_t getWriteLoad() const = 0; - /** Store the object. - - The caller's Blob parameter is overwritten. - - @param type The type of object. - @param data The payload of the object. The caller's - variable is overwritten. - @param hash The 256-bit hash of the payload data. - @param ledgerSeq The sequence of the ledger the object belongs to. - - @return `true` if the object was stored? - */ + /** + * Store the object. + * + * The caller's Blob parameter is overwritten. + * + * @param type The type of object. + * @param data The payload of the object. The caller's + * variable is overwritten. + * @param hash The 256-bit hash of the payload data. + * @param ledgerSeq The sequence of the ledger the object belongs to. + * + * @return `true` if the object was stored? + */ virtual void store(NodeObjectType type, Blob&& data, uint256 const& hash, std::uint32_t ledgerSeq) = 0; - /* Check if two ledgers are in the same database - - If these two sequence numbers map to the same database, - the result of a fetch with either sequence number would - be identical. - - @param s1 The first sequence number - @param s2 The second sequence number - - @return 'true' if both ledgers would be in the same DB - - */ + /** + * Check if two ledgers are in the same database + * + * If these two sequence numbers map to the same database, + * the result of a fetch with either sequence number would + * be identical. + * + * @param s1 The first sequence number + * @param s2 The second sequence number + * + * @return 'true' if both ledgers would be in the same DB + */ virtual bool isSameDB(std::uint32_t s1, std::uint32_t s2) = 0; virtual void sync() = 0; - /** Fetch a node object. - If the object is known to be not in the database, isn't found in the - database during the fetch, or failed to load correctly during the fetch, - `nullptr` is returned. - - @note This can be called concurrently. - @param hash The key of the object to retrieve. - @param ledgerSeq The sequence of the ledger where the object is stored. - @param fetchType the type of fetch, synchronous or asynchronous. - @return The object, or nullptr if it couldn't be retrieved. - */ + /** + * Fetch a node object. + * If the object is known to be not in the database, isn't found in the + * database during the fetch, or failed to load correctly during the fetch, + * `nullptr` is returned. + * + * @note This can be called concurrently. + * @param hash The key of the object to retrieve. + * @param ledgerSeq The sequence of the ledger where the object is stored. + * @param fetchType the type of fetch, synchronous or asynchronous. + * @return The object, or nullptr if it couldn't be retrieved. + */ std::shared_ptr fetchNodeObject( uint256 const& hash, @@ -116,29 +137,33 @@ public: FetchType fetchType = FetchType::Synchronous, bool duplicate = false); - /** Fetch an object without waiting. - If I/O is required to determine whether or not the object is present, - `false` is returned. Otherwise, `true` is returned and `object` is set - to refer to the object, or `nullptr` if the object is not present. - If I/O is required, the I/O is scheduled and `true` is returned - - @note This can be called concurrently. - @param hash The key of the object to retrieve - @param ledgerSeq The sequence of the ledger where the - object is stored. - @param callback Callback function when read completes - */ + /** + * Fetch an object without waiting. + * If I/O is required to determine whether or not the object is present, + * `false` is returned. Otherwise, `true` is returned and `object` is set + * to refer to the object, or `nullptr` if the object is not present. + * If I/O is required, the I/O is scheduled and `true` is returned + * + * @note This can be called concurrently. + * @param hash The key of the object to retrieve + * @param ledgerSeq The sequence of the ledger where the + * object is stored. + * @param callback Callback function when read completes + */ virtual void asyncFetch( uint256 const& hash, std::uint32_t ledgerSeq, std::function const&)>&& callback); - /** Remove expired entries from the positive and negative caches. */ + /** + * Remove expired entries from the positive and negative caches. + */ virtual void sweep() = 0; - /** Gather statistics pertaining to read and write activities. + /** + * Gather statistics pertaining to read and write activities. * * @param obj Json object reference into which to place counters. */ @@ -175,7 +200,9 @@ public: void getCountsJson(json::Value& obj); - /** Returns the number of file descriptors the database expects to need */ + /** + * Returns the number of file descriptors the database expects to need + */ int fdRequired() const { @@ -188,7 +215,8 @@ public: bool isStopping() const; - /** @return The earliest ledger sequence allowed + /** + * @return The earliest ledger sequence allowed */ [[nodiscard]] std::uint32_t earliestLedgerSeq() const noexcept @@ -265,13 +293,14 @@ private: FetchReport& fetchReport, bool duplicate) = 0; - /** Visit every object in the database - This is usually called during import. - - @note This routine will not be called concurrently with itself - or other methods. - @see import - */ + /** + * Visit every object in the database + * This is usually called during import. + * + * @note This routine will not be called concurrently with itself + * or other methods. + * @see import + */ virtual void forEach(std::function)> f) = 0; diff --git a/include/xrpl/nodestore/DatabaseRotating.h b/include/xrpl/nodestore/DatabaseRotating.h index a7deed294a..5381b5c435 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 { @@ -21,18 +28,32 @@ public: { } - /** Rotates the backends. - - @param newBackend New writable backend - @param f A function executed after the rotation outside of lock. The - values passed to f will be the new backend database names _after_ - rotation. - */ + /** + * Rotates the backends. + * + * @param newBackend New writable backend + * @param f A function executed after the rotation outside of lock. The + * values passed to f will be the new backend database names _after_ + * rotation. + */ virtual void rotate( std::unique_ptr&& newBackend, std::function const& f) = 0; + + /** + * Marks an online-delete rotation as in progress (or completed). + * + * While in flight, a read served by the archive backend is copied + * forward into the writable backend even for ordinary + * (duplicate == false) fetches: the archive is about to be deleted, + * and a node body canonicalized into caches during the rotation + * window would otherwise survive only in RAM once the archive is + * dropped. + */ + virtual void + setRotationInFlight(bool inFlight) = 0; }; } // namespace xrpl::NodeStore diff --git a/include/xrpl/nodestore/DummyScheduler.h b/include/xrpl/nodestore/DummyScheduler.h index 472684ff13..49b0d37462 100644 --- a/include/xrpl/nodestore/DummyScheduler.h +++ b/include/xrpl/nodestore/DummyScheduler.h @@ -1,10 +1,13 @@ #pragma once #include +#include namespace xrpl::NodeStore { -/** Simple NodeStore Scheduler that just performs the tasks synchronously. */ +/** + * Simple NodeStore Scheduler that just performs the tasks synchronously. + */ class DummyScheduler : public Scheduler { public: diff --git a/include/xrpl/nodestore/Factory.h b/include/xrpl/nodestore/Factory.h index 3e6ba76a08..a18023a8a8 100644 --- a/include/xrpl/nodestore/Factory.h +++ b/include/xrpl/nodestore/Factory.h @@ -4,7 +4,11 @@ #include #include -#include +#include + +#include +#include +#include namespace xrpl { class Section; @@ -12,24 +16,29 @@ class Section; namespace xrpl::NodeStore { -/** Base class for backend factories. */ +/** + * Base class for backend factories. + */ class Factory { public: virtual ~Factory() = default; - /** Retrieve the name of this factory. */ + /** + * Retrieve the name of this factory. + */ [[nodiscard]] virtual std::string getName() const = 0; - /** Create an instance of this factory's backend. - - @param keyBytes The fixed number of bytes per key. - @param parameters A set of key/value configuration pairs. - @param burstSize Backend burst size in bytes. - @param scheduler The scheduler to use for running tasks. - @return A pointer to the Backend object. - */ + /** + * Create an instance of this factory's backend. + * + * @param keyBytes The fixed number of bytes per key. + * @param parameters A set of key/value configuration pairs. + * @param burstSize Backend burst size in bytes. + * @param scheduler The scheduler to use for running tasks. + * @return A pointer to the Backend object. + */ virtual std::unique_ptr createInstance( size_t keyBytes, @@ -38,15 +47,16 @@ public: Scheduler& scheduler, beast::Journal journal) = 0; - /** Create an instance of this factory's backend. - - @param keyBytes The fixed number of bytes per key. - @param parameters A set of key/value configuration pairs. - @param burstSize Backend burst size in bytes. - @param scheduler The scheduler to use for running tasks. - @param context The context used by database. - @return A pointer to the Backend object. - */ + /** + * Create an instance of this factory's backend. + * + * @param keyBytes The fixed number of bytes per key. + * @param parameters A set of key/value configuration pairs. + * @param burstSize Backend burst size in bytes. + * @param scheduler The scheduler to use for running tasks. + * @param context The context used by database. + * @return A pointer to the Backend object. + */ virtual std::unique_ptr createInstance( size_t keyBytes, diff --git a/include/xrpl/nodestore/Manager.h b/include/xrpl/nodestore/Manager.h index 1c4e5b63cf..54d99fe94b 100644 --- a/include/xrpl/nodestore/Manager.h +++ b/include/xrpl/nodestore/Manager.h @@ -1,11 +1,20 @@ #pragma once -#include +#include +#include +#include #include +#include + +#include +#include +#include namespace xrpl::NodeStore { -/** Singleton for managing NodeStore factories and back ends. */ +/** + * Singleton for managing NodeStore factories and back ends. + */ class Manager { public: @@ -15,26 +24,35 @@ public: Manager& operator=(Manager const&) = delete; - /** Returns the instance of the manager singleton. */ + /** + * Returns the instance of the manager singleton. + */ static Manager& instance(); - /** Add a factory. */ + /** + * Add a factory. + */ virtual void insert(Factory& factory) = 0; - /** Remove a factory. */ + /** + * Remove a factory. + */ virtual void erase(Factory& factory) = 0; - /** Return a pointer to the matching factory if it exists. - @param name The name to match, performed case-insensitive. - @return `nullptr` if a match was not found. - */ + /** + * Return a pointer to the matching factory if it exists. + * @param name The name to match, performed case-insensitive. + * @return `nullptr` if a match was not found. + */ virtual Factory* find(std::string const& name) = 0; - /** Create a backend. */ + /** + * Create a backend. + */ virtual std::unique_ptr makeBackend( Section const& parameters, @@ -42,34 +60,35 @@ public: Scheduler& scheduler, beast::Journal journal) = 0; - /** Construct a NodeStore database. - - The parameters are key value pairs passed to the backend. The - 'type' key must exist, it defines the choice of backend. Most - backends also require a 'path' field. - - Some choices for 'type' are: - HyperLevelDB, LevelDBFactory, SQLite, MDB - - If the fastBackendParameter is omitted or empty, no ephemeral database - is used. If the scheduler parameter is omitted or unspecified, a - synchronous scheduler is used which performs all tasks immediately on - the caller's thread. - - @note If the database cannot be opened or created, an exception is - thrown. - - @param name A diagnostic label for the database. - @param burstSize Backend burst size in bytes. - @param scheduler The scheduler to use for performing asynchronous tasks. - @param readThreads The number of async read threads to create - @param backendParameters The parameter string for the persistent - backend. - @param fastBackendParameters [optional] The parameter string for the - ephemeral backend. - - @return The opened database. - */ + /** + * Construct a NodeStore database. + * + * The parameters are key value pairs passed to the backend. The + * 'type' key must exist, it defines the choice of backend. Most + * backends also require a 'path' field. + * + * Some choices for 'type' are: + * HyperLevelDB, LevelDBFactory, SQLite, MDB + * + * If the fastBackendParameter is omitted or empty, no ephemeral database + * is used. If the scheduler parameter is omitted or unspecified, a + * synchronous scheduler is used which performs all tasks immediately on + * the caller's thread. + * + * @note If the database cannot be opened or created, an exception is + * thrown. + * + * @param name A diagnostic label for the database. + * @param burstSize Backend burst size in bytes. + * @param scheduler The scheduler to use for performing asynchronous tasks. + * @param readThreads The number of async read threads to create + * @param backendParameters The parameter string for the persistent + * backend. + * @param fastBackendParameters [optional] The parameter string for the + * ephemeral backend. + * + * @return The opened database. + */ virtual std::unique_ptr makeDatabase( std::size_t burstSize, diff --git a/include/xrpl/nodestore/NodeObject.h b/include/xrpl/nodestore/NodeObject.h index 04ba391b2b..b96d65fa12 100644 --- a/include/xrpl/nodestore/NodeObject.h +++ b/include/xrpl/nodestore/NodeObject.h @@ -4,11 +4,17 @@ #include #include +#include +#include +#include + // VFALCO NOTE Intentionally not in the NodeStore namespace namespace xrpl { -/** The types of node objects. */ +/** + * The types of node objects. + */ enum class NodeObjectType : std::uint32_t { Unknown = 0, Ledger = 1, @@ -17,15 +23,16 @@ enum class NodeObjectType : std::uint32_t { Dummy = 512 // an invalid or missing object }; -/** A simple object that the Ledger uses to store entries. - NodeObjects are comprised of a type, a hash, and a blob. - They can be uniquely identified by the hash, which is a half-SHA512 of - the blob. The blob is a variable length block of serialized data. The - type identifies what the blob contains. - - @note No checking is performed to make sure the hash matches the data. - @see SHAMap -*/ +/** + * A simple object that the Ledger uses to store entries. + * NodeObjects are comprised of a type, a hash, and a blob. + * They can be uniquely identified by the hash, which is a half-SHA512 of + * the blob. The blob is a variable length block of serialized data. The + * type identifies what the blob contains. + * + * @note No checking is performed to make sure the hash matches the data. + * @see SHAMap + */ class NodeObject : public CountedObject { public: @@ -44,29 +51,36 @@ public: // This constructor is private, use createObject instead. NodeObject(NodeObjectType type, Blob&& data, uint256 const& hash, PrivateAccess); - /** Create an object from fields. - - The caller's variable is modified during this call. The - underlying storage for the Blob is taken over by the NodeObject. - - @param type The type of object. - @param ledgerIndex The ledger in which this object appears. - @param data A buffer containing the payload. The caller's variable - is overwritten. - @param hash The 256-bit hash of the payload data. - */ + /** + * Create an object from fields. + * + * The caller's variable is modified during this call. The + * underlying storage for the Blob is taken over by the NodeObject. + * + * @param type The type of object. + * @param ledgerIndex The ledger in which this object appears. + * @param data A buffer containing the payload. The caller's variable + * is overwritten. + * @param hash The 256-bit hash of the payload data. + */ static std::shared_ptr createObject(NodeObjectType type, Blob&& data, uint256 const& hash); - /** Returns the type of this object. */ + /** + * Returns the type of this object. + */ [[nodiscard]] NodeObjectType getType() const; - /** Returns the hash of the data. */ + /** + * Returns the hash of the data. + */ [[nodiscard]] uint256 const& getHash() const; - /** Returns the underlying data. */ + /** + * Returns the underlying data. + */ [[nodiscard]] Blob const& getData() const; diff --git a/include/xrpl/nodestore/Scheduler.h b/include/xrpl/nodestore/Scheduler.h index 588ff19bdc..5d93a80eaa 100644 --- a/include/xrpl/nodestore/Scheduler.h +++ b/include/xrpl/nodestore/Scheduler.h @@ -8,7 +8,9 @@ namespace xrpl::NodeStore { enum class FetchType { Synchronous, Async }; -/** Contains information about a fetch operation. */ +/** + * Contains information about a fetch operation. + */ struct FetchReport { explicit FetchReport(FetchType fetchType) : fetchType(fetchType) @@ -20,7 +22,9 @@ struct FetchReport bool wasFound = false; }; -/** Contains information about a batch write operation. */ +/** + * Contains information about a batch write operation. + */ struct BatchWriteReport { explicit BatchWriteReport() = default; @@ -29,36 +33,40 @@ struct BatchWriteReport int writeCount; }; -/** Scheduling for asynchronous backend activity - - For improved performance, a backend has the option of performing writes - in batches. These writes can be scheduled using the provided scheduler - object. - - @see BatchWriter -*/ +/** + * Scheduling for asynchronous backend activity + * + * For improved performance, a backend has the option of performing writes + * in batches. These writes can be scheduled using the provided scheduler + * object. + * + * @see BatchWriter + */ class Scheduler { public: virtual ~Scheduler() = default; - /** Schedules a task. - Depending on the implementation, the task may be invoked either on - the current thread of execution, or an unspecified - implementation-defined foreign thread. - */ + /** + * Schedules a task. + * Depending on the implementation, the task may be invoked either on + * the current thread of execution, or an unspecified + * implementation-defined foreign thread. + */ virtual void scheduleTask(Task& task) = 0; - /** Reports completion of a fetch - Allows the scheduler to monitor the node store's performance - */ + /** + * Reports completion of a fetch + * Allows the scheduler to monitor the node store's performance + */ virtual void onFetch(FetchReport const& report) = 0; - /** Reports the completion of a batch write - Allows the scheduler to monitor the node store's performance - */ + /** + * Reports the completion of a batch write + * Allows the scheduler to monitor the node store's performance + */ virtual void onBatchWrite(BatchWriteReport const& report) = 0; }; diff --git a/include/xrpl/nodestore/Task.h b/include/xrpl/nodestore/Task.h index 0695970a68..59fe648476 100644 --- a/include/xrpl/nodestore/Task.h +++ b/include/xrpl/nodestore/Task.h @@ -2,14 +2,17 @@ namespace xrpl::NodeStore { -/** Derived classes perform scheduled tasks. */ +/** + * Derived classes perform scheduled tasks. + */ struct Task { virtual ~Task() = default; - /** Performs the task. - The call may take place on a foreign thread. - */ + /** + * Performs the task. + * The call may take place on a foreign thread. + */ virtual void performScheduledTask() = 0; }; diff --git a/include/xrpl/nodestore/Types.h b/include/xrpl/nodestore/Types.h index 21c01e9111..872d948a36 100644 --- a/include/xrpl/nodestore/Types.h +++ b/include/xrpl/nodestore/Types.h @@ -2,6 +2,7 @@ #include +#include #include namespace xrpl::NodeStore { @@ -17,7 +18,9 @@ static constexpr auto kBatchWritePreallocationSize = 256; // static constexpr auto kBatchWriteLimitSize = 65536; -/** Return codes from Backend operations. */ +/** + * Return codes from Backend operations. + */ enum class Status { Ok = 0, NotFound = 1, @@ -28,7 +31,9 @@ enum class Status { CustomCode = 100 }; -/** A batch of NodeObjects to write at once. */ +/** + * A batch of NodeObjects to write at once. + */ using Batch = std::vector>; } // namespace xrpl::NodeStore diff --git a/include/xrpl/nodestore/detail/BatchWriter.h b/include/xrpl/nodestore/detail/BatchWriter.h index b0383838dc..b89df0da14 100644 --- a/include/xrpl/nodestore/detail/BatchWriter.h +++ b/include/xrpl/nodestore/detail/BatchWriter.h @@ -1,26 +1,31 @@ #pragma once +#include #include #include #include #include +#include #include namespace xrpl::NodeStore { -/** Batch-writing assist logic. - - The batch writes are performed with a scheduled task. Use of the - class it not required. A backend can implement its own write batching, - or skip write batching if doing so yields a performance benefit. - - @see Scheduler -*/ +/** + * Batch-writing assist logic. + * + * The batch writes are performed with a scheduled task. Use of the + * class it not required. A backend can implement its own write batching, + * or skip write batching if doing so yields a performance benefit. + * + * @see Scheduler + */ class BatchWriter : private Task { public: - /** This callback does the actual writing. */ + /** + * This callback does the actual writing. + */ struct Callback { virtual ~Callback() = default; @@ -33,24 +38,30 @@ public: writeBatch(Batch const& batch) = 0; }; - /** Create a batch writer. */ + /** + * Create a batch writer. + */ BatchWriter(Callback& callback, Scheduler& scheduler); - /** Destroy a batch writer. - - Anything pending in the batch is written out before this returns. - */ + /** + * Destroy a batch writer. + * + * Anything pending in the batch is written out before this returns. + */ ~BatchWriter() override; - /** Store the object. - - This will add to the batch and initiate a scheduled task to - write the batch out. - */ + /** + * Store the object. + * + * This will add to the batch and initiate a scheduled task to + * write the batch out. + */ void store(std::shared_ptr const& object); - /** Get an estimate of the amount of writing I/O pending. */ + /** + * Get an estimate of the amount of writing I/O pending. + */ int getWriteLoad(); diff --git a/include/xrpl/nodestore/detail/DatabaseNodeImp.h b/include/xrpl/nodestore/detail/DatabaseNodeImp.h index 38b8763f31..6f2fca682f 100644 --- a/include/xrpl/nodestore/detail/DatabaseNodeImp.h +++ b/include/xrpl/nodestore/detail/DatabaseNodeImp.h @@ -1,10 +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 { diff --git a/include/xrpl/nodestore/detail/DatabaseRotatingImp.h b/include/xrpl/nodestore/detail/DatabaseRotatingImp.h index 1ba9435a5f..ecbe9a513d 100644 --- a/include/xrpl/nodestore/detail/DatabaseRotatingImp.h +++ b/include/xrpl/nodestore/detail/DatabaseRotatingImp.h @@ -1,8 +1,20 @@ #pragma once +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include #include +#include namespace xrpl::NodeStore { @@ -58,11 +70,22 @@ public: void sweep() override; + void + setRotationInFlight(bool inFlight) override; + private: std::shared_ptr writableBackend_; std::shared_ptr archiveBackend_; mutable std::mutex mutex_; + // True between SHAMapStore starting the cache-freshen phase and the + // completion of rotate(). While true, archive hits on ordinary + // (duplicate == false) fetches are copied forward into the writable + // backend; copyForwardCount_ tallies them per rotation for the + // summary line logged at swap. + std::atomic rotationInFlight_{false}; + std::atomic copyForwardCount_{0}; + std::shared_ptr fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate) override; diff --git a/include/xrpl/nodestore/detail/DecodedBlob.h b/include/xrpl/nodestore/detail/DecodedBlob.h index 90a7b6c9cb..d0cc5e3404 100644 --- a/include/xrpl/nodestore/detail/DecodedBlob.h +++ b/include/xrpl/nodestore/detail/DecodedBlob.h @@ -2,41 +2,50 @@ #include +#include + namespace xrpl::NodeStore { -/** Parsed key/value blob into NodeObject components. - - This will extract the information required to construct a NodeObject. It - also does consistency checking and returns the result, so it is possible - to determine if the data is corrupted without throwing an exception. Not - all forms of corruption are detected so further analysis will be needed - to eliminate false negatives. - - @note This defines the database format of a NodeObject! -*/ +/** + * Parsed key/value blob into NodeObject components. + * + * This will extract the information required to construct a NodeObject. It + * also does consistency checking and returns the result, so it is possible + * to determine if the data is corrupted without throwing an exception. Not + * all forms of corruption are detected so further analysis will be needed + * to eliminate false negatives. + * + * @note This defines the database format of a NodeObject! + */ class DecodedBlob { public: - /** Construct the decoded blob from raw data. */ + /** + * Construct the decoded blob from raw data. + */ DecodedBlob(void const* key, void const* value, int valueBytes); - /** Determine if the decoding was successful. */ + /** + * Determine if the decoding was successful. + */ [[nodiscard]] bool wasOk() const noexcept { return success_; } - /** Create a NodeObject from this data. */ + /** + * Create a NodeObject from this data. + */ std::shared_ptr 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..d668cdccd8 100644 --- a/include/xrpl/nodestore/detail/EncodedBlob.h +++ b/include/xrpl/nodestore/detail/EncodedBlob.h @@ -7,51 +7,61 @@ #include #include +#include #include +#include +#include namespace xrpl::NodeStore { -/** Convert a NodeObject from in-memory to database format. - - The (suboptimal) database format consists of: - - - 8 prefix bytes which will typically be 0, but don't assume that's the - case; earlier versions of the code would use these bytes to store the - ledger index either once or twice. - - A single byte denoting the type of the object. - - The payload. - - @note This class is typically instantiated on the stack, so the size of - the object does not matter as much as it normally would since the - allocation is, effectively, free. - - We leverage that fact to preallocate enough memory to handle most - payloads as part of this object, eliminating the need for dynamic - allocation. As of this writing ~94% of objects require fewer than - 1024 payload bytes. +/** + * Convert a NodeObject from in-memory to database format. + * + * The (suboptimal) database format consists of: + * + * - 8 prefix bytes which will typically be 0, but don't assume that's the + * case; earlier versions of the code would use these bytes to store the + * ledger index either once or twice. + * - A single byte denoting the type of the object. + * - The payload. + * + * @note This class is typically instantiated on the stack, so the size of + * the object does not matter as much as it normally would since the + * allocation is, effectively, free. + * + * We leverage that fact to preallocate enough memory to handle most + * payloads as part of this object, eliminating the need for dynamic + * allocation. As of this writing ~94% of objects require fewer than + * 1024 payload bytes. */ class EncodedBlob { - /** The 32-byte key of the serialized object. */ + /** + * The 32-byte key of the serialized object. + */ std::array key_{}; - /** A pre-allocated buffer for the serialized object. - - The buffer is large enough for the 9 byte prefix and at least - 1024 more bytes. The precise size is calculated automatically - at compile time so as to avoid wasting space on padding bytes. + /** + * A pre-allocated buffer for the serialized object. + * + * The buffer is large enough for the 9 byte prefix and at least + * 1024 more bytes. The precise size is calculated automatically + * at compile time so as to avoid wasting space on padding bytes. */ std::array payload_{}; - /** The size of the serialized data. */ + /** + * The size of the serialized data. + */ std::uint32_t size_; - /** A pointer to the serialized data. - - This may point to the pre-allocated buffer (if it is sufficiently - large) or to a dynamically allocated buffer. + /** + * A pointer to the serialized data. + * + * This may point to the pre-allocated buffer (if it is sufficiently + * large) or to a dynamically allocated buffer. */ std::uint8_t* const ptr_; 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 0c49274d70..5a65545d3a 100644 --- a/include/xrpl/nodestore/detail/varint.h +++ b/include/xrpl/nodestore/detail/varint.h @@ -2,6 +2,7 @@ #include +#include #include #include @@ -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 a83c8bfa84..a3666c7960 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% @@ -28,17 +33,20 @@ class STObject; class STAmount; class Rules; -/** Calculate Liquidity Provider Token (LPT) Currency. +/** + * Calculate Liquidity Provider Token (LPT) Currency. */ Currency ammLPTCurrency(Asset const& asset1, Asset const& asset2); -/** Calculate LPT Issue from AMM asset pair. +/** + * Calculate LPT Issue from AMM asset pair. */ Issue ammLPTIssue(Asset const& asset1, Asset const& asset2, AccountID const& ammAccountID); -/** Validate the amount. +/** + * Validate the amount. * If validZero is false and amount is beast::zero then invalid amount. * Return error code if invalid amount. * If pair then validate amount's issue matches one of the pair's issue. @@ -60,17 +68,20 @@ invalidAMMAssetPair( Asset const& asset2, std::optional> const& pair = std::nullopt); -/** Get time slot of the auction slot. +/** + * Get time slot of the auction slot. */ std::optional ammAuctionTimeSlot(std::uint64_t current, STObject const& auctionSlot); -/** Return true if required AMM amendment is enabled +/** + * Return true if required AMM amendment is enabled */ bool ammEnabled(Rules const&); -/** Convert to the fee from the basis points +/** + * Convert to the fee from the basis points * @param tfee trading fee in {0, 1000} * 1 = 1/10bps or 0.001%, 1000 = 1% */ @@ -80,7 +91,8 @@ getFee(std::uint16_t tfee) return Number{tfee} / kAuctionSlotFeeScaleFactor; } -/** Get fee multiplier (1 - tfee) +/** + * Get fee multiplier (1 - tfee) * @tfee trading fee in basis points */ inline Number @@ -89,7 +101,8 @@ feeMult(std::uint16_t tfee) return 1 - getFee(tfee); } -/** Get fee multiplier (1 - tfee / 2) +/** + * Get fee multiplier (1 - tfee / 2) * @tfee trading fee in basis points */ inline Number diff --git a/include/xrpl/protocol/AccountID.h b/include/xrpl/protocol/AccountID.h index 4938812ffa..ab3c5a996b 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 { @@ -24,43 +28,53 @@ public: } // namespace detail -/** A 160-bit unsigned that uniquely identifies an account. */ +/** + * A 160-bit unsigned that uniquely identifies an account. + */ using AccountID = BaseUInt<160, detail::AccountIDTag>; -/** Convert AccountID to base58 checked string */ +/** + * Convert AccountID to base58 checked string + */ std::string toBase58(AccountID const& v); -/** Parse AccountID from checked, base58 string. - @return std::nullopt if a parse error occurs -*/ +/** + * Parse AccountID from checked, base58 string. + * @return std::nullopt if a parse error occurs + */ template <> std::optional parseBase58(std::string const& s); -/** Compute AccountID from public key. - - The account ID is computed as the 160-bit hash of the - public key data. This excludes the version byte and - guard bytes included in the base58 representation. - -*/ +/** + * Compute AccountID from public key. + * + * The account ID is computed as the 160-bit hash of the + * public key data. This excludes the version byte and + * guard bytes included in the base58 representation. + */ // VFALCO In PublicKey.h for now // AccountID // calcAccountID (PublicKey const& pk); -/** A special account that's used as the "issuer" for XRP. */ +/** + * A special account that's used as the "issuer" for XRP. + */ AccountID const& xrpAccount(); -/** A placeholder for empty accounts. */ +/** + * A placeholder for empty accounts. + */ AccountID const& noAccount(); -/** Convert hex or base58 string to AccountID. - - @return `true` if the parsing was successful. -*/ +/** + * Convert hex or base58 string to AccountID. + * + * @return `true` if the parsing was successful. + */ // DEPRECATED bool toIssuer(AccountID&, std::string const&); @@ -87,17 +101,18 @@ operator<<(std::ostream& os, AccountID const& x) return os; } -/** Initialize the global cache used to map AccountID to base58 conversions. - - The cache is optional and need not be initialized. But because conversion - is expensive (it requires a SHA-256 operation) in most cases the overhead - of the cache is worth the benefit. - - @param count The number of entries the cache should accommodate. Zero will - disable the cache, releasing any memory associated with it. - - @note The function will only initialize the cache the first time it is - invoked. Subsequent invocations do nothing. +/** + * Initialize the global cache used to map AccountID to base58 conversions. + * + * The cache is optional and need not be initialized. But because conversion + * is expensive (it requires a SHA-256 operation) in most cases the overhead + * of the cache is worth the benefit. + * + * @param count The number of entries the cache should accommodate. Zero will + * disable the cache, releasing any memory associated with it. + * + * @note The function will only initialize the cache the first time it is + * invoked. Subsequent invocations do nothing. */ void initAccountIdCache(std::size_t count); 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..8e9c09eb89 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 { @@ -53,7 +62,8 @@ private: public: Asset() = default; - /** Conversions to Asset are implicit and conversions to specific issue + /** + * Conversions to Asset are implicit and conversions to specific issue * type are explicit. This design facilitates the use of Asset. */ Asset(Issue const& issue) : issue_(issue) @@ -140,7 +150,8 @@ public: friend constexpr bool operator==(BadAsset const& lhs, Asset const& rhs); - /** Return true if both assets refer to the same currency (regardless of + /** + * Return true if both assets refer to the same currency (regardless of * issuer) or MPT issuance. Otherwise return false. */ friend constexpr bool 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 01dc40075b..a83eb41b24 100644 --- a/include/xrpl/protocol/Book.h +++ b/include/xrpl/protocol/Book.h @@ -2,16 +2,28 @@ #include #include +#include #include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include + namespace xrpl { -/** Specifies an order book. - The order book is a pair of Issues called in and out. - @see Issue. -*/ +/** + * Specifies an order book. + * The order book is a pair of Issues called in and out. + * @see Issue. + */ class Book final : public CountedObject { public: @@ -49,7 +61,9 @@ hash_append(Hasher& h, Book const& b) Book reversed(Book const& book); -/** Equality comparison. */ +/** + * Equality comparison. + */ /** @{ */ [[nodiscard]] constexpr bool operator==(Book const& lhs, Book const& rhs) @@ -58,14 +72,16 @@ operator==(Book const& lhs, Book const& rhs) } /** @} */ -/** Strict weak ordering. */ +/** + * Strict weak ordering. + */ /** @{ */ [[nodiscard]] constexpr std::weak_ordering operator<=>(Book const& lhs, Book const& rhs) { - if (auto const c{lhs.in <=> rhs.in}; c != 0) + if (auto const c{lhs.in <=> rhs.in}; c != 0) // NOLINT(modernize-use-nullptr) return c; - if (auto const c{lhs.out <=> rhs.out}; c != 0) + if (auto const c{lhs.out <=> rhs.out}; c != 0) // NOLINT(modernize-use-nullptr) return c; // Manually compare optionals diff --git a/include/xrpl/protocol/BuildInfo.h b/include/xrpl/protocol/BuildInfo.h index 47a27339a8..18ba20f23c 100644 --- a/include/xrpl/protocol/BuildInfo.h +++ b/include/xrpl/protocol/BuildInfo.h @@ -2,75 +2,85 @@ #include #include +#include -/** Versioning information for this build. */ +/** + * Versioning information for this build. + */ // VFALCO The namespace is deprecated namespace xrpl::BuildInfo { -/** Server version. - Follows the Semantic Versioning Specification: - http://semver.org/ -*/ +/** + * Server version. + * Follows the Semantic Versioning Specification: + * http://semver.org/ + */ std::string const& getVersionString(); -/** Full server version string. - This includes the name of the server. It is used in the peer - protocol hello message and also the headers of some HTTP replies. -*/ +/** + * Full server version string. + * This includes the name of the server. It is used in the peer + * protocol hello message and also the headers of some HTTP replies. + */ std::string const& getFullVersionString(); -/** Encode an arbitrary server software version in a 64-bit integer. - - The general format is: - - ........-........-........-........-........-........-........-........ - XXXXXXXX-XXXXXXXX-YYYYYYYY-YYYYYYYY-YYYYYYYY-YYYYYYYY-YYYYYYYY-YYYYYYYY - - X: 16 bits identifying the particular implementation - Y: 48 bits of data specific to the implementation - - The xrpld-specific format (implementation ID is: 0x18 0x3B) is: - - 00011000-00111011-MMMMMMMM-mmmmmmmm-pppppppp-TTNNNNNN-00000000-00000000 - - M: 8-bit major version (0-255) - m: 8-bit minor version (0-255) - p: 8-bit patch version (0-255) - T: 11 if neither an RC nor a beta - 10 if an RC - 01 if a beta - N: 6-bit rc/beta number (1-63) - - @param the version string - @return the encoded version in a 64-bit integer -*/ +/** + * Encode an arbitrary server software version in a 64-bit integer. + * + * The general format is: + * + * ........-........-........-........-........-........-........-........ + * XXXXXXXX-XXXXXXXX-YYYYYYYY-YYYYYYYY-YYYYYYYY-YYYYYYYY-YYYYYYYY-YYYYYYYY + * + * X: 16 bits identifying the particular implementation + * Y: 48 bits of data specific to the implementation + * + * The xrpld-specific format (implementation ID is: 0x18 0x3B) is: + * + * 00011000-00111011-MMMMMMMM-mmmmmmmm-pppppppp-TTNNNNNN-00000000-00000000 + * + * M: 8-bit major version (0-255) + * m: 8-bit minor version (0-255) + * p: 8-bit patch version (0-255) + * T: 11 if neither an RC nor a beta + * 10 if an RC + * 01 if a beta + * N: 6-bit rc/beta number (1-63) + * + * @param the version string + * @return the encoded version in a 64-bit integer + */ std::uint64_t encodeSoftwareVersion(std::string_view versionStr); -/** Returns this server's version packed in a 64-bit integer. */ +/** + * Returns this server's version packed in a 64-bit integer. + */ std::uint64_t getEncodedVersion(); -/** Check if the encoded software version is an xrpld software version. - - @param version another node's encoded software version - @return true if the version is an xrpld software version, false otherwise -*/ +/** + * Check if the encoded software version is an xrpld software version. + * + * @param version another node's encoded software version + * @return true if the version is an xrpld software version, false otherwise + */ bool isXrpldVersion(std::uint64_t version); -/** Check if the version is newer than the local node's xrpld software - version. - - @param version another node's encoded software version - @return true if the version is newer than the local node's xrpld software - version, false otherwise. - - @note This function only understands version numbers that are generated by - xrpld. Please see the encodeSoftwareVersion() function for detail. -*/ +/** + * Check if the version is newer than the local node's xrpld software + * version. + * + * @param version another node's encoded software version + * @return true if the version is newer than the local node's xrpld software + * version, false otherwise. + * + * @note This function only understands version numbers that are generated by + * xrpld. Please see the encodeSoftwareVersion() function for detail. + */ bool isNewerVersion(std::uint64_t version); diff --git a/include/xrpl/protocol/ConfidentialTransfer.h b/include/xrpl/protocol/ConfidentialTransfer.h new file mode 100644 index 0000000000..ecf7970aba --- /dev/null +++ b/include/xrpl/protocol/ConfidentialTransfer.h @@ -0,0 +1,436 @@ +#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 f5e67fd572..8ac7c8c58f 100644 --- a/include/xrpl/protocol/ErrorCodes.h +++ b/include/xrpl/protocol/ErrorCodes.h @@ -1,7 +1,8 @@ #pragma once #include -#include + +#include namespace xrpl { @@ -146,10 +147,11 @@ enum ErrorCodeI { RpcLast = RpcUnexpectedLedgerType // rpcLAST should always equal the last code. }; -/** Codes returned in the `warnings` array of certain RPC commands. - - These values need to remain stable. -*/ +/** + * Codes returned in the `warnings` array of certain RPC commands. + * + * These values need to remain stable. + */ // Protocol-wide, 50+ files // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) enum WarningCodeI { @@ -167,7 +169,9 @@ enum WarningCodeI { namespace RPC { -/** Maps an rpc error code to its token, default message, and HTTP status. */ +/** + * Maps an rpc error code to its token, default message, and HTTP status. + */ struct ErrorInfo { // Default ctor needed to produce an empty std::array during constexpr eval. @@ -192,11 +196,15 @@ struct ErrorInfo int httpStatus; }; -/** Returns an ErrorInfo that reflects the error code. */ +/** + * Returns an ErrorInfo that reflects the error code. + */ ErrorInfo const& getErrorInfo(ErrorCodeI code); -/** Add or update the json update to reflect the error code. */ +/** + * Add or update the json update to reflect the error code. + */ /** @{ */ void injectError(ErrorCodeI code, json::Value& json); @@ -205,7 +213,9 @@ void injectError(ErrorCodeI code, std::string const& message, json::Value& json); /** @} */ -/** Returns a new json object that reflects the error code. */ +/** + * Returns a new json object that reflects the error code. + */ /** @{ */ json::Value makeError(ErrorCodeI code); @@ -213,7 +223,9 @@ json::Value makeError(ErrorCodeI code, std::string const& message); /** @} */ -/** Returns a new json object that indicates invalid parameters. */ +/** + * Returns a new json object that indicates invalid parameters. + */ /** @{ */ inline json::Value makeParamError(std::string const& message) @@ -313,17 +325,23 @@ notValidatorError() /** @} */ -/** Returns `true` if the json contains an rpc error specification. */ +/** + * Returns `true` if the json contains an rpc error specification. + */ bool containsError(json::Value const& json); -/** Returns http status that corresponds to the error code. */ +/** + * Returns http status that corresponds to the error code. + */ int errorCodeHttpStatus(ErrorCodeI code); } // namespace RPC -/** Returns a single string with the contents of an RPC error. */ +/** + * Returns a single string with the contents of an RPC error. + */ std::string rpcErrorString(json::Value const& jv); diff --git a/include/xrpl/protocol/Feature.h b/include/xrpl/protocol/Feature.h index 5de8ca64a9..f15b7e2d3f 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 @@ -110,7 +112,9 @@ validFeatureName(auto fn) -> bool enum class VoteBehavior : int { Obsolete = -1, DefaultNo = 0, DefaultYes = 1 }; enum class AmendmentSupport : int { Retired = -1, Supported = 0, Unsupported = 1 }; -/** All amendments libxrpl knows about. */ +/** + * All amendments libxrpl knows about. + */ std::map const& allAmendments(); @@ -150,23 +154,27 @@ static constexpr std::size_t kNumFeatures = #undef XRPL_FEATURE #pragma pop_macro("XRPL_FEATURE") -/** Amendments that this server supports and the default voting behavior. - Whether they are enabled depends on the Rules defined in the validated - ledger */ +/** + * Amendments that this server supports and the default voting behavior. + * Whether they are enabled depends on the Rules defined in the validated + * ledger + */ std::map const& supportedAmendments(); -/** Amendments that this server won't vote for by default. - - This function is only used in unit tests. -*/ +/** + * Amendments that this server won't vote for by default. + * + * This function is only used in unit tests. + */ std::size_t numDownVotedAmendments(); -/** Amendments that this server will vote for by default. - - This function is only used in unit tests. -*/ +/** + * Amendments that this server will vote for by default. + * + * This function is only used in unit tests. + */ std::size_t numUpVotedAmendments(); diff --git a/include/xrpl/protocol/Fees.h b/include/xrpl/protocol/Fees.h index 14bcc068bf..fdfadcd8fd 100644 --- a/include/xrpl/protocol/Fees.h +++ b/include/xrpl/protocol/Fees.h @@ -2,26 +2,35 @@ #include +#include + namespace xrpl { // Deprecated constant for backwards compatibility with pre-XRPFees amendment. // This was the reference fee units used in the old fee calculation. inline constexpr std::uint32_t kFeeUnitsDeprecated = 10; -/** Reflects the fee settings for a particular ledger. - - The fees are always the same for any transactions applied - to a ledger. Changes to fees occur in between ledgers. -*/ +/** + * Reflects the fee settings for a particular ledger. + * + * The fees are always the same for any transactions applied + * to a ledger. Changes to fees occur in between ledgers. + */ struct Fees { - /** @brief Cost of a reference transaction in drops. */ + /** + * @brief Cost of a reference transaction in drops. + */ XRPAmount base{0}; - /** @brief Minimum XRP an account must hold to exist on the ledger. */ + /** + * @brief Minimum XRP an account must hold to exist on the ledger. + */ XRPAmount reserve{0}; - /** @brief Additional XRP reserve required per owned ledger object. */ + /** + * @brief Additional XRP reserve required per owned ledger object. + */ XRPAmount increment{0}; explicit Fees() = default; @@ -34,15 +43,16 @@ struct Fees { } - /** Returns the account reserve given the owner count, in drops. - - The reserve is calculated as the reserve base plus - the reserve increment times the number of increments. - */ + /** + * Returns the account reserve given the owner count, in drops. + * + * The reserve is calculated as the reserve base times the number of accounts plus the reserve + * increment times the number of increments. + */ [[nodiscard]] XRPAmount - accountReserve(std::size_t ownerCount) const + accountReserve(std::uint32_t ownerCount, std::uint32_t accountCount) const { - return reserve + ownerCount * increment; + return (reserve * accountCount) + (increment * ownerCount); } }; diff --git a/include/xrpl/protocol/HashPrefix.h b/include/xrpl/protocol/HashPrefix.h index 1b05d450a1..9d4471d05c 100644 --- a/include/xrpl/protocol/HashPrefix.h +++ b/include/xrpl/protocol/HashPrefix.h @@ -17,55 +17,80 @@ makeHashPrefix(char a, char b, char c) } // namespace detail -/** Prefix for hashing functions. - - These prefixes are inserted before the source material used to generate - various hashes. This is done to put each hash in its own "space." This way, - two different types of objects with the same binary data will produce - different hashes. - - Each prefix is a 4-byte value with the last byte set to zero and the first - three bytes formed from the ASCII equivalent of some arbitrary string. For - example "TXN". - - @note Hash prefixes are part of the protocol; you cannot, arbitrarily, - change the type or the value of any of these without causing breakage. -*/ +/** + * Prefix for hashing functions. + * + * These prefixes are inserted before the source material used to generate + * various hashes. This is done to put each hash in its own "space." This way, + * two different types of objects with the same binary data will produce + * different hashes. + * + * Each prefix is a 4-byte value with the last byte set to zero and the first + * three bytes formed from the ASCII equivalent of some arbitrary string. For + * example "TXN". + * + * @note Hash prefixes are part of the protocol; you cannot, arbitrarily, + * change the type or the value of any of these without causing breakage. + */ enum class HashPrefix : std::uint32_t { - /** transaction plus signature to give transaction ID */ + /** + * transaction plus signature to give transaction ID + */ TransactionId = detail::makeHashPrefix('T', 'X', 'N'), - /** transaction plus metadata */ + /** + * transaction plus metadata + */ TxNode = detail::makeHashPrefix('S', 'N', 'D'), - /** account state */ + /** + * account state + */ LeafNode = detail::makeHashPrefix('M', 'L', 'N'), - /** inner node in V1 tree */ + /** + * inner node in V1 tree + */ InnerNode = detail::makeHashPrefix('M', 'I', 'N'), - /** ledger master data for signing */ + /** + * ledger master data for signing + */ LedgerMaster = detail::makeHashPrefix('L', 'W', 'R'), - /** inner transaction to sign */ + /** + * inner transaction to sign + */ TxSign = detail::makeHashPrefix('S', 'T', 'X'), - /** inner transaction to multi-sign */ + /** + * inner transaction to multi-sign + */ TxMultiSign = detail::makeHashPrefix('S', 'M', 'T'), - /** validation for signing */ + /** + * validation for signing + */ Validation = detail::makeHashPrefix('V', 'A', 'L'), - /** proposal for signing */ + /** + * proposal for signing + */ Proposal = detail::makeHashPrefix('P', 'R', 'P'), - /** Manifest */ + /** + * Manifest + */ Manifest = detail::makeHashPrefix('M', 'A', 'N'), - /** Payment Channel Claim */ + /** + * Payment Channel Claim + */ PaymentChannelClaim = detail::makeHashPrefix('C', 'L', 'M'), - /** Batch */ + /** + * Batch + */ Batch = detail::makeHashPrefix('B', 'C', 'H'), }; diff --git a/include/xrpl/protocol/IOUAmount.h b/include/xrpl/protocol/IOUAmount.h index b057f1c245..060ad3d828 100644 --- a/include/xrpl/protocol/IOUAmount.h +++ b/include/xrpl/protocol/IOUAmount.h @@ -6,20 +6,22 @@ #include #include +#include #include namespace xrpl { -/** Floating point representation of amounts with high dynamic range - - Amounts are stored as a normalized signed mantissa and an exponent. The - range of the normalized exponent is [-96,80] and the range of the absolute - value of the normalized mantissa is [1000000000000000, 9999999999999999]. - - Arithmetic operations can throw std::overflow_error during normalization - if the amount exceeds the largest representable amount, but underflows - will silently truncate to zero. -*/ +/** + * Floating point representation of amounts with high dynamic range + * + * Amounts are stored as a normalized signed mantissa and an exponent. The + * range of the normalized exponent is [-96,80] and the range of the absolute + * value of the normalized mantissa is [1000000000000000, 9999999999999999]. + * + * Arithmetic operations can throw std::overflow_error during normalization + * if the amount exceeds the largest representable amount, but underflows + * will silently truncate to zero. + */ class IOUAmount : private boost::totally_ordered, private boost::additive { private: @@ -28,12 +30,13 @@ private: mantissa_type mantissa_{}; exponent_type exponent_{}; - /** Adjusts the mantissa and exponent to the proper range. - - This can throw if the amount cannot be normalized, or is larger than - the largest value that can be represented as an IOU amount. Amounts - that are too small to be represented normalize to 0. - */ + /** + * Adjusts the mantissa and exponent to the proper range. + * + * This can throw if the amount cannot be normalized, or is larger than + * the largest value that can be represented as an IOU amount. Amounts + * that are too small to be represented normalize to 0. + */ void normalize(); @@ -65,11 +68,15 @@ public: bool operator<(IOUAmount const& other) const; - /** Returns true if the amount is not zero */ + /** + * Returns true if the amount is not zero + */ explicit operator bool() const noexcept; - /** Return the sign of the amount */ + /** + * Return the sign of the amount + */ [[nodiscard]] int signum() const noexcept; diff --git a/include/xrpl/protocol/Indexes.h b/include/xrpl/protocol/Indexes.h index 887a208ec6..07493da0bd 100644 --- a/include/xrpl/protocol/Indexes.h +++ b/include/xrpl/protocol/Indexes.h @@ -1,108 +1,126 @@ #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 { class SeqProxy; -/** Keylet computation functions. - - Entries in the ledger are located using 256-bit locators. The locators are - calculated using a wide range of parameters specific to the entry whose - locator we are calculating (e.g. an account's locator is derived from the - account's address, whereas the locator for an offer is derived from the - account and the offer sequence.) - - To enhance type safety during lookup and make the code more robust, we use - keylets, which contain not only the locator of the object but also the type - of the object being referenced. - - These functions each return a type-specific keylet. -*/ +/** + * Keylet computation functions. + * + * Entries in the ledger are located using 256-bit locators. The locators are + * calculated using a wide range of parameters specific to the entry whose + * locator we are calculating (e.g. an account's locator is derived from the + * account's address, whereas the locator for an offer is derived from the + * account and the offer sequence.) + * + * To enhance type safety during lookup and make the code more robust, we use + * keylets, which contain not only the locator of the object but also the type + * of the object being referenced. + * + * These functions each return a type-specific keylet. + */ namespace keylet { -/** AccountID root */ +/** + * AccountID root + */ Keylet account(AccountID const& id) noexcept; -/** The index of the amendment table */ +/** + * The index of the amendment table + */ Keylet const& amendments() noexcept; -/** Any item that can be in an owner dir. */ +/** + * Any item that can be in an owner dir. + */ Keylet child(uint256 const& key) noexcept; -/** The index of the "short" skip list - - The "short" skip list is a node (at a fixed index) that holds the hashes - of ledgers since the last flag ledger. It will contain, at most, 256 hashes. -*/ +/** + * The index of the "short" skip list + * + * The "short" skip list is a node (at a fixed index) that holds the hashes + * of ledgers since the last flag ledger. It will contain, at most, 256 hashes. + */ Keylet const& skip() noexcept; -/** The index of the long skip for a particular ledger range. - - The "long" skip list is a node that holds the hashes of (up to) 256 flag - ledgers. - - It can be used to efficiently skip back to any ledger using only two hops: - the first hop gets the "long" skip list for the ledger it wants to retrieve - and uses it to get the hash of the flag ledger whose short skip list will - contain the hash of the requested ledger. -*/ +/** + * The index of the long skip for a particular ledger range. + * + * The "long" skip list is a node that holds the hashes of (up to) 256 flag + * ledgers. + * + * It can be used to efficiently skip back to any ledger using only two hops: + * the first hop gets the "long" skip list for the ledger it wants to retrieve + * and uses it to get the hash of the flag ledger whose short skip list will + * contain the hash of the requested ledger. + */ Keylet skip(LedgerIndex ledger) noexcept; -/** The (fixed) index of the object containing the ledger fees. */ +/** + * 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. */ +/** + * 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; +/** + * The beginning of an order book + */ +Keylet +book(Book const& b); - Keylet - operator()(Book const& b) const; -}; -static BookT const kBook{}; - -/** The index of a trust line for a given currency - - Note that a trustline is *shared* between two accounts (commonly referred - to as the issuer and the holder); if Alice sets up a trust line to Bob for - BTC, and Bob trusts Alice for BTC, here is only a single BTC trust line - between them. -*/ +/** + * The index of a trust line for a given currency + * + * Note that a trustline is *shared* between two accounts (commonly referred + * to as the issuer and the holder); if Alice sets up a trust line to Bob for + * BTC, and Bob trusts Alice for BTC, here is only a single BTC trust line + * between them. + */ /** @{ */ 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); } /** @} */ -/** An offer from an account */ +/** + * An offer from an account + */ /** @{ */ Keylet offer(AccountID const& id, std::uint32_t seq) noexcept; @@ -114,44 +132,50 @@ offer(uint256 const& key) noexcept } /** @} */ -/** The initial directory page for a specific quality */ +/** + * The initial directory page for a specific quality + */ 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{}; - -/** A ticket belonging to an account */ -struct TicketT -{ - 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{}; - -/** A SignerList */ +/** + * The directory for the next lower quality + */ Keylet -signers(AccountID const& account) noexcept; +next(Keylet const& k); -/** A Check */ +/** + * A ticket belonging to an account + */ +/** @{ */ +Keylet +ticket(AccountID const& id, std::uint32_t ticketSeq); + +Keylet +ticket(AccountID const& id, SeqProxy ticketSeq); + +inline Keylet +ticket(uint256 const& key) +{ + return {ltTICKET, key}; +} +/** @} */ + +/** + * A SignerList + */ +Keylet +signerList(AccountID const& account) noexcept; + +/** + * A Sponsorship + */ +Keylet +sponsorship(AccountID const& sponsor, AccountID const& sponsee) noexcept; + +/** + * A Check + */ /** @{ */ Keylet check(AccountID const& id, std::uint32_t seq) noexcept; @@ -163,7 +187,9 @@ check(uint256 const& key) noexcept } /** @} */ -/** A DepositPreauth */ +/** + * A DepositPreauth + */ /** @{ */ Keylet depositPreauth(AccountID const& owner, AccountID const& preauthorized) noexcept; @@ -182,15 +208,21 @@ depositPreauth(uint256 const& key) noexcept //------------------------------------------------------------------------------ -/** Any ledger entry */ +/** + * Any ledger entry + */ Keylet unchecked(uint256 const& key) noexcept; -/** The root page of an account's directory */ +/** + * The root page of an account's directory + */ Keylet ownerDir(AccountID const& id) noexcept; -/** A page in a directory */ +/** + * A page in a directory + */ /** @{ */ Keylet page(uint256 const& root, std::uint64_t index = 0) noexcept; @@ -203,60 +235,79 @@ page(Keylet const& root, std::uint64_t index = 0) noexcept } /** @} */ -/** An escrow entry */ +/** + * An escrow entry + */ Keylet escrow(AccountID const& src, std::uint32_t seq) noexcept; -/** A PaymentChannel */ +/** + * 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 - - Unlike objects whose ledger identifiers are produced by hashing data, - NFT page identifiers are composite identifiers, consisting of the owner's - 160-bit AccountID, followed by a 96-bit value that determines which NFT - tokens are candidates for that page. +/** + * NFT page keylets + * + * Unlike objects whose ledger identifiers are produced by hashing data, + * NFT page identifiers are composite identifiers, consisting of the owner's + * 160-bit AccountID, followed by a 96-bit value that determines which NFT + * tokens are candidates for that page. */ /** @{ */ -/** A keylet for the owner's first possible NFT page. */ +/** + * 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. */ +/** + * 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 */ +/** + * 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}; } -/** The directory of buy offers for the specified NFT */ +/** + * The directory of buy offers for the specified NFT + */ Keylet nftBuys(uint256 const& id) noexcept; -/** The directory of sell offers for the specified NFT */ +/** + * The directory of sell offers for the specified NFT + */ Keylet nftSells(uint256 const& id) noexcept; -/** AMM entry */ +/** + * AMM entry + */ Keylet amm(Asset const& issue1, Asset const& issue2) noexcept; Keylet amm(uint256 const& amm) noexcept; -/** A keylet for Delegate object */ +/** + * A keylet for Delegate object + */ Keylet delegate(AccountID const& account, AccountID const& authorizedAccount) noexcept; @@ -287,13 +338,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 +371,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}; } @@ -371,17 +422,8 @@ struct KeyletDesc bool includeInTests{}; }; -// This list should include all of the keylet functions that take a single -// AccountID parameter. -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}, - // 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::did, .expectedLEName = jss::DID, .includeInTests = true}}}; +// This list should include all of the keylet functions that take a single AccountID parameter. +extern std::array, 6> const kDirectAccountKeylets; MPTID makeMptID(std::uint32_t sequence, AccountID const& account); diff --git a/include/xrpl/protocol/InnerObjectFormats.h b/include/xrpl/protocol/InnerObjectFormats.h index 9d07a21d1c..7364e83cfd 100644 --- a/include/xrpl/protocol/InnerObjectFormats.h +++ b/include/xrpl/protocol/InnerObjectFormats.h @@ -1,17 +1,21 @@ #pragma once #include +#include +#include namespace xrpl { -/** Manages the list of known inner object formats. +/** + * Manages the list of known inner object formats. */ class InnerObjectFormats : public KnownFormats { private: - /** Create the object. - This will load the object with all the known inner object formats. - */ + /** + * Create the object. + * This will load the object with all the known inner object formats. + */ InnerObjectFormats(); public: diff --git a/include/xrpl/protocol/Issue.h b/include/xrpl/protocol/Issue.h index c8022698d3..5cd8731609 100644 --- a/include/xrpl/protocol/Issue.h +++ b/include/xrpl/protocol/Issue.h @@ -1,14 +1,19 @@ #pragma once -#include #include +#include #include +#include +#include +#include + namespace xrpl { -/** A currency issued by an account. - @see Currency, AccountID, Issue, Book -*/ +/** + * A currency issued by an account. + * @see Currency, AccountID, Issue, Book + */ class Issue { public: @@ -66,7 +71,9 @@ hash_append(Hasher& h, Issue const& r) hash_append(h, r.currency, r.account); } -/** Equality comparison. */ +/** + * Equality comparison. + */ /** @{ */ [[nodiscard]] constexpr bool operator==(Issue const& lhs, Issue const& rhs) @@ -75,12 +82,14 @@ operator==(Issue const& lhs, Issue const& rhs) } /** @} */ -/** Strict weak ordering. */ +/** + * Strict weak ordering. + */ /** @{ */ [[nodiscard]] constexpr std::weak_ordering operator<=>(Issue const& lhs, Issue const& rhs) { - if (auto const c{lhs.currency <=> rhs.currency}; c != 0) + if (auto const c{lhs.currency <=> rhs.currency}; c != 0) // NOLINT(modernize-use-nullptr) return c; if (isXRP(lhs.currency)) @@ -92,7 +101,9 @@ operator<=>(Issue const& lhs, Issue const& rhs) //------------------------------------------------------------------------------ -/** Returns an asset specifier that represents XRP. */ +/** + * Returns an asset specifier that represents XRP. + */ inline Issue const& xrpIssue() { @@ -100,7 +111,9 @@ xrpIssue() return kIssue; } -/** Returns an asset specifier that represents no account and currency. */ +/** + * Returns an asset specifier that represents no account and currency. + */ inline Issue const& noIssue() { diff --git a/include/xrpl/protocol/Keylet.h b/include/xrpl/protocol/Keylet.h index 19704e2a11..48d494f564 100644 --- a/include/xrpl/protocol/Keylet.h +++ b/include/xrpl/protocol/Keylet.h @@ -7,14 +7,15 @@ namespace xrpl { class STLedgerEntry; -/** A pair of SHAMap key and LedgerEntryType. - - A Keylet identifies both a key in the state map - and its ledger entry type. - - @note Keylet is a portmanteau of the words key - and LET, an acronym for LedgerEntryType. -*/ +/** + * A pair of SHAMap key and LedgerEntryType. + * + * A Keylet identifies both a key in the state map + * and its ledger entry type. + * + * @note Keylet is a portmanteau of the words key + * and LET, an acronym for LedgerEntryType. + */ struct Keylet { uint256 key; @@ -24,7 +25,9 @@ struct Keylet { } - /** Returns true if the SLE matches the type */ + /** + * Returns true if the SLE matches the type + */ [[nodiscard]] bool check(STLedgerEntry const&) const; }; diff --git a/include/xrpl/protocol/KnownFormats.h b/include/xrpl/protocol/KnownFormats.h index 9aa914ba97..385feb2c27 100644 --- a/include/xrpl/protocol/KnownFormats.h +++ b/include/xrpl/protocol/KnownFormats.h @@ -7,22 +7,28 @@ #include #include +#include #include +#include +#include +#include namespace xrpl { -/** Manages a list of known formats. - - Each format has a name, an associated KeyType (typically an enumeration), - and a predefined @ref SOElement. - - @tparam KeyType The type of key identifying the format. -*/ +/** + * Manages a list of known formats. + * + * Each format has a name, an associated KeyType (typically an enumeration), + * and a predefined @ref SOElement. + * + * @tparam KeyType The type of key identifying the format. + */ template class KnownFormats { public: - /** A known format. + /** + * A known format. */ class Item { @@ -42,7 +48,8 @@ public: "KnownFormats KeyType must be integral or enum."); } - /** Retrieve the name of the format. + /** + * Retrieve the name of the format. */ [[nodiscard]] std::string const& getName() const @@ -50,7 +57,8 @@ public: return name_; } - /** Retrieve the transaction type this format represents. + /** + * Retrieve the transaction type this format represents. */ [[nodiscard]] KeyType getType() const @@ -70,32 +78,35 @@ public: KeyType const type_; }; - /** Create the known formats object. - - Derived classes will load the object with all the known formats. - */ + /** + * Create the known formats object. + * + * Derived classes will load the object with all the known formats. + */ private: KnownFormats() : name_(beast::typeName()) { } public: - /** Destroy the known formats object. - - The defined formats are deleted. - */ + /** + * Destroy the known formats object. + * + * The defined formats are deleted. + */ virtual ~KnownFormats() = default; KnownFormats(KnownFormats const&) = delete; KnownFormats& operator=(KnownFormats const&) = delete; - /** Retrieve the type for a format specified by name. - - If the format name is unknown, an exception is thrown. - - @param name The name of the type. - @return The type. - */ + /** + * Retrieve the type for a format specified by name. + * + * If the format name is unknown, an exception is thrown. + * + * @param name The name of the type. + * @return The type. + */ [[nodiscard]] KeyType findTypeByName(std::string const& name) const { @@ -106,7 +117,8 @@ public: name.substr(0, std::min(name.size(), std::size_t(32))) + "'"); } - /** Retrieve a format based on its type. + /** + * Retrieve a format based on its type. */ [[nodiscard]] Item const* findByType(KeyType type) const @@ -118,20 +130,21 @@ 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(); } protected: - /** Retrieve a format based on its name. + /** + * Retrieve a format based on its name. */ [[nodiscard]] Item const* findByName(std::string const& name) const @@ -142,15 +155,16 @@ protected: return itr->second; } - /** Add a new format. - - @param name The name of this format. - @param type The type of this format. - @param uniqueFields A std::vector of unique fields - @param commonFields A std::vector of common fields - - @return The created format. - */ + /** + * Add a new format. + * + * @param name The name of this format. + * @param type The type of this format. + * @param uniqueFields A std::vector of unique fields + * @param commonFields A std::vector of common fields + * + * @return The created format. + */ Item const& add(char const* name, KeyType type, diff --git a/include/xrpl/protocol/LedgerFormats.h b/include/xrpl/protocol/LedgerFormats.h index 99d5d818f1..7c504f6bdd 100644 --- a/include/xrpl/protocol/LedgerFormats.h +++ b/include/xrpl/protocol/LedgerFormats.h @@ -3,34 +3,38 @@ // NOLINTBEGIN(readability-identifier-naming) #include +#include +#include #include #include +#include #include namespace xrpl { -/** Identifiers for on-ledger objects. - - Each ledger object requires a unique type identifier, which is stored within the object itself; - this makes it possible to iterate the entire ledger and determine each object's type and verify - that the object you retrieved from a given hash matches the expected type. - - @warning Since these values are stored inside objects stored on the ledger they are part of the - protocol. - **Changing them should be avoided because without special handling, this will result in a hard - fork.** - - @note Values outside this range may be used internally by the code for various purposes, but - attempting to use such values to identify on-ledger objects will result in an invariant failure. - - @note When retiring types, the specific values should not be removed but should be marked as - [[deprecated]]. This is to avoid accidental reuse of identifiers. - - @todo The C++ language does not enable checking for duplicate values here. - If it becomes possible then we should do this. - - @ingroup protocol -*/ +/** + * Identifiers for on-ledger objects. + * + * Each ledger object requires a unique type identifier, which is stored within the object itself; + * this makes it possible to iterate the entire ledger and determine each object's type and verify + * that the object you retrieved from a given hash matches the expected type. + * + * @warning Since these values are stored inside objects stored on the ledger they are part of the + * protocol. + * **Changing them should be avoided because without special handling, this will result in a hard + * fork.** + * + * @note Values outside this range may be used internally by the code for various purposes, but + * attempting to use such values to identify on-ledger objects will result in an invariant failure. + * + * @note When retiring types, the specific values should not be removed but should be marked as + * [[deprecated]]. This is to avoid accidental reuse of identifiers. + * + * @todo The C++ language does not enable checking for duplicate values here. + * If it becomes possible then we should do this. + * + * @ingroup protocol + */ // Protocol-critical, hundreds of usages // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) enum LedgerEntryType : std::uint16_t { @@ -46,66 +50,72 @@ enum LedgerEntryType : std::uint16_t { #pragma pop_macro("LEDGER_ENTRY") //--------------------------------------------------------------------------- - /** A special type, matching any ledger entry type. - - The value does not represent a concrete type, but rather is used in contexts where the - specific type of a ledger object is unimportant, unknown or unavailable. - - Objects with this special type cannot be created or stored on the ledger. - - \sa keylet::unchecked - */ + /** + * A special type, matching any ledger entry type. + * + * The value does not represent a concrete type, but rather is used in contexts where the + * specific type of a ledger object is unimportant, unknown or unavailable. + * + * Objects with this special type cannot be created or stored on the ledger. + * + * @see keylet::unchecked + */ ltANY = 0, - /** A special type, matching any ledger type except directory nodes. - - The value does not represent a concrete type, but rather is used in contexts where the - ledger object must not be a directory node but its specific type is otherwise unimportant, - unknown or unavailable. - - Objects with this special type cannot be created or stored on the ledger. - - \sa keylet::child + /** + * A special type, matching any ledger type except directory nodes. + * + * The value does not represent a concrete type, but rather is used in contexts where the + * ledger object must not be a directory node but its specific type is otherwise unimportant, + * unknown or unavailable. + * + * Objects with this special type cannot be created or stored on the ledger. + * + * @see keylet::child */ ltCHILD = 0x1CD2, //--------------------------------------------------------------------------- - /** A legacy, deprecated type. - - \deprecated **This object type is not supported and should not be used.** - Support for this type of object was never implemented. - No objects of this type were ever created. + /** + * A legacy, deprecated type. + * + * @deprecated **This object type is not supported and should not be used.** + * Support for this type of object was never implemented. + * No objects of this type were ever created. */ ltNICKNAME [[deprecated("This object type is not supported and should not be used.")]] = 0x006e, - /** A legacy, deprecated type. - - \deprecated **This object type is not supported and should not be used.** - Support for this type of object was never implemented. - No objects of this type were ever created. + /** + * A legacy, deprecated type. + * + * @deprecated **This object type is not supported and should not be used.** + * Support for this type of object was never implemented. + * No objects of this type were ever created. */ ltCONTRACT [[deprecated("This object type is not supported and should not be used.")]] = 0x0063, - /** A legacy, deprecated type. - - \deprecated **This object type is not supported and should not be used.** - Support for this type of object was never implemented. - No objects of this type were ever created. + /** + * A legacy, deprecated type. + * + * @deprecated **This object type is not supported and should not be used.** + * Support for this type of object was never implemented. + * No objects of this type were ever created. */ ltGENERATOR_MAP [[deprecated("This object type is not supported and should not be used.")]] = 0x0067, }; -/** Ledger object flags. - - These flags are specified in ledger objects and modify their behavior. - - @warning Ledger object flags form part of the protocol. - **Changing them should be avoided because without special handling, this will result in a hard - fork.** - - @ingroup protocol -*/ +/** + * Ledger object flags. + * + * These flags are specified in ledger objects and modify their behavior. + * + * @warning Ledger object flags form part of the protocol. + * **Changing them should be avoided because without special handling, this will result in a hard + * fork.** + * + * @ingroup protocol + */ #pragma push_macro("XMACRO") #pragma push_macro("TO_VALUE") #pragma push_macro("VALUE_TO_MAP") @@ -177,17 +187,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) \ @@ -203,7 +215,11 @@ enum LedgerEntryType : std::uint16_t { LEDGER_OBJECT(Loan, \ LSF_FLAG(lsfLoanDefault, 0x00010000) \ LSF_FLAG(lsfLoanImpaired, 0x00020000) \ - LSF_FLAG(lsfLoanOverpayment, 0x00040000)) /* True, loan allows overpayments */ + LSF_FLAG(lsfLoanOverpayment, 0x00040000)) /* True, loan allows overpayments */ \ + \ + LEDGER_OBJECT(Sponsorship, \ + LSF_FLAG(lsfSponsorshipRequireSignForFee, 0x00010000) \ + LSF_FLAG(lsfSponsorshipRequireSignForReserve, 0x00020000)) // clang-format on @@ -280,14 +296,16 @@ getAllLedgerFlags() //------------------------------------------------------------------------------ -/** Holds the list of known ledger entry formats. +/** + * Holds the list of known ledger entry formats. */ class LedgerFormats : public KnownFormats { private: - /** Create the object. - This will load the object with all the known ledger formats. - */ + /** + * Create the object. + * This will load the object with all the known ledger formats. + */ LedgerFormats(); public: diff --git a/include/xrpl/protocol/LedgerHeader.h b/include/xrpl/protocol/LedgerHeader.h index f05b11d1eb..d169e53e2c 100644 --- a/include/xrpl/protocol/LedgerHeader.h +++ b/include/xrpl/protocol/LedgerHeader.h @@ -3,13 +3,18 @@ #include #include #include +#include #include #include #include +#include + namespace xrpl { -/** Information about the notional ledger backing the view. */ +/** + * Information about the notional ledger backing the view. + */ struct LedgerHeader { explicit LedgerHeader() = default; @@ -64,15 +69,21 @@ getCloseAgree(LedgerHeader const& info) void addRaw(LedgerHeader const&, Serializer&, bool includeHash = false); -/** Deserialize a ledger header from a byte array. */ +/** + * Deserialize a ledger header from a byte array. + */ LedgerHeader deserializeHeader(Slice data, bool hasHash = false); -/** Deserialize a ledger header (prefixed with 4 bytes) from a byte array. */ +/** + * Deserialize a ledger header (prefixed with 4 bytes) from a byte array. + */ LedgerHeader deserializePrefixedHeader(Slice data, bool hasHash = false); -/** Calculate the hash of a ledger header. */ +/** + * Calculate the hash of a ledger header. + */ uint256 calculateLedgerHash(LedgerHeader const& info); diff --git a/include/xrpl/protocol/LedgerShortcut.h b/include/xrpl/protocol/LedgerShortcut.h index 68c31c4c3c..037621121d 100644 --- a/include/xrpl/protocol/LedgerShortcut.h +++ b/include/xrpl/protocol/LedgerShortcut.h @@ -9,13 +9,19 @@ namespace xrpl { * without needing to specify their exact hash or sequence number. */ enum class LedgerShortcut { - /** The current working ledger (open, not yet closed) */ + /** + * The current working ledger (open, not yet closed) + */ Current, - /** The most recently closed ledger (may not be validated) */ + /** + * The most recently closed ledger (may not be validated) + */ Closed, - /** The most recently validated ledger */ + /** + * The most recently validated ledger + */ Validated }; diff --git a/include/xrpl/protocol/MPTAmount.h b/include/xrpl/protocol/MPTAmount.h index 6ea36fc294..462092f7dd 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 { @@ -58,7 +60,9 @@ public: bool operator<(MPTAmount const& other) const; - /** Returns true if the amount is not zero */ + /** + * Returns true if the amount is not zero + */ explicit constexpr operator bool() const noexcept; @@ -67,14 +71,17 @@ public: return value(); } - /** Return the sign of the amount */ + /** + * Return the sign of the amount + */ [[nodiscard]] constexpr int signum() const noexcept; - /** Returns the underlying value. Code SHOULD NOT call this - function unless the type has been abstracted away, - e.g. in a templated function. - */ + /** + * Returns the underlying value. Code SHOULD NOT call this + * function unless the type has been abstracted away, + * e.g. in a templated function. + */ [[nodiscard]] constexpr value_type value() const; @@ -98,14 +105,18 @@ MPTAmount::operator=(beast::Zero) return *this; } -/** Returns true if the amount is not zero */ +/** + * Returns true if the amount is not zero + */ constexpr MPTAmount:: operator bool() const noexcept { return value_ != 0; } -/** Return the sign of the amount */ +/** + * Return the sign of the amount + */ constexpr int MPTAmount::signum() const noexcept { @@ -114,10 +125,11 @@ MPTAmount::signum() const noexcept return (value_ != 0) ? 1 : 0; } -/** Returns the underlying value. Code SHOULD NOT call this - function unless the type has been abstracted away, - e.g. in a templated function. -*/ +/** + * Returns the underlying value. Code SHOULD NOT call this + * function unless the type has been abstracted away, + * e.g. in a templated function. + */ constexpr MPTAmount::value_type MPTAmount::value() const { diff --git a/include/xrpl/protocol/MPTIssue.h b/include/xrpl/protocol/MPTIssue.h index f55029f50d..7f473da6a2 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 @@ -72,7 +82,8 @@ operator<=>(MPTIssue const& lhs, MPTIssue const& rhs) return lhs.mptID_ <=> rhs.mptID_; } -/** MPT is a non-native token. +/** + * MPT is a non-native token. */ inline bool isXRP(MPTID const&) diff --git a/include/xrpl/protocol/NFTSyntheticSerializer.h b/include/xrpl/protocol/NFTSyntheticSerializer.h index a1d8bce985..bef05b9a8f 100644 --- a/include/xrpl/protocol/NFTSyntheticSerializer.h +++ b/include/xrpl/protocol/NFTSyntheticSerializer.h @@ -9,10 +9,9 @@ namespace xrpl::RPC { /** - Adds common synthetic fields to transaction-related JSON responses - - @{ + * Adds common synthetic fields to transaction-related JSON responses */ +/** @{ */ void insertNFTSyntheticInJson(json::Value&, std::shared_ptr const&, TxMeta const&); /** @} */ diff --git a/include/xrpl/protocol/NFTokenID.h b/include/xrpl/protocol/NFTokenID.h index f61c6bd5cb..b1b994eabd 100644 --- a/include/xrpl/protocol/NFTokenID.h +++ b/include/xrpl/protocol/NFTokenID.h @@ -12,13 +12,13 @@ namespace xrpl { /** - Add a `nftoken_ids` field to the `meta` output parameter. - The field is only added to successful NFTokenMint, NFTokenAcceptOffer, - and NFTokenCancelOffer transactions. - - Helper functions are not static because they can be used by Clio. - @{ + * Add a `nftoken_ids` field to the `meta` output parameter. + * The field is only added to successful NFTokenMint, NFTokenAcceptOffer, + * and NFTokenCancelOffer transactions. + * + * Helper functions are not static because they can be used by Clio. */ +/** @{ */ bool canHaveNFTokenID(std::shared_ptr const& serializedTx, TxMeta const& transactionMeta); diff --git a/include/xrpl/protocol/NFTokenOfferID.h b/include/xrpl/protocol/NFTokenOfferID.h index c4a80356bf..4810f7932a 100644 --- a/include/xrpl/protocol/NFTokenOfferID.h +++ b/include/xrpl/protocol/NFTokenOfferID.h @@ -11,12 +11,12 @@ namespace xrpl { /** - Add an `offer_id` field to the `meta` output parameter. - The field is only added to successful NFTokenCreateOffer transactions. - - Helper functions are not static because they can be used by Clio. - @{ + * Add an `offer_id` field to the `meta` output parameter. + * The field is only added to successful NFTokenCreateOffer transactions. + * + * Helper functions are not static because they can be used by Clio. */ +/** @{ */ bool canHaveNFTokenOfferID( std::shared_ptr const& serializedTx, 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..e83e1c97b6 100644 --- a/include/xrpl/protocol/Protocol.h +++ b/include/xrpl/protocol/Protocol.h @@ -1,71 +1,101 @@ #pragma once #include +#include #include +#include #include +#include +#include + +#include #include namespace xrpl { -/** Protocol specific constants. - - This information is, implicitly, part of the protocol. - - @note Changing these values without adding code to the - server to detect "pre-change" and "post-change" - will result in a hard fork. - - @ingroup protocol -*/ -/** Smallest legal byte size of a transaction. */ +/** + * Protocol specific constants. + * + * This information is, implicitly, part of the protocol. + * + * @note Changing these values without adding code to the + * server to detect "pre-change" and "post-change" + * will result in a hard fork. + * + * @ingroup protocol + */ +/** + * Smallest legal byte size of a transaction. + */ constexpr std::size_t kTxMinSizeBytes = 32; -/** Largest legal byte size of a transaction. */ +/** + * Largest legal byte size of a transaction. + */ constexpr std::size_t kTxMaxSizeBytes = megabytes(1); -/** The maximum number of unfunded offers to delete at once */ +/** + * The maximum number of unfunded offers to delete at once + */ constexpr std::size_t kUnfundedOfferRemoveLimit = 1000; -/** The maximum number of expired offers to delete at once */ +/** + * The maximum number of expired offers to delete at once + */ constexpr std::size_t kExpiredOfferRemoveLimit = 256; -/** The maximum number of metadata entries allowed in one transaction */ +/** + * The maximum number of metadata entries allowed in one transaction + */ constexpr std::size_t kOversizeMetaDataCap = 5200; -/** The maximum number of entries per directory page */ +/** + * The maximum number of entries per directory page + */ constexpr std::size_t kDirNodeMaxEntries = 32; -/** The maximum number of pages allowed in a directory - - Made obsolete by fixDirectoryLimit amendment. -*/ +/** + * The maximum number of pages allowed in a directory + * + * Made obsolete by fixDirectoryLimit amendment. + */ constexpr std::uint64_t kDirNodeMaxPages = 262144; -/** The maximum number of items in an NFT page */ +/** + * The maximum number of items in an NFT page + */ constexpr std::size_t kDirMaxTokensPerPage = 32; -/** The maximum number of owner directory entries for account to be deletable */ +/** + * The maximum number of owner directory entries for account to be deletable + */ constexpr std::size_t kMaxDeletableDirEntries = 1000; -/** The maximum number of token offers that can be canceled at once */ +/** + * The maximum number of token offers that can be canceled at once + */ constexpr std::size_t kMaxTokenOfferCancelCount = 500; -/** The maximum number of offers in an offer directory for NFT to be burnable */ +/** + * The maximum number of offers in an offer directory for NFT to be burnable + */ constexpr std::size_t kMaxDeletableTokenOfferEntries = 500; -/** The maximum token transfer fee allowed. - - Token transfer fees can range from 0% to 50% and are specified in tenths of - a basis point; that is a value of 1000 represents a transfer fee of 1% and - a value of 10000 represents a transfer fee of 10%. - - Note that for extremely low transfer fees values, it is possible that the - calculated fee will be 0. +/** + * The maximum token transfer fee allowed. + * + * Token transfer fees can range from 0% to 50% and are specified in tenths of + * a basis point; that is a value of 1000 represents a transfer fee of 1% and + * a value of 10000 represents a transfer fee of 10%. + * + * Note that for extremely low transfer fees values, it is possible that the + * calculated fee will be 0. */ constexpr std::uint16_t kMaxTransferFee = 50000; -/** There are 10,000 basis points (bips) in 100%. +/** + * There are 10,000 basis points (bips) in 100%. * * Basis points represent 0.01%. * @@ -110,36 +140,41 @@ tenthBipsOfValue(T value, TenthBips bips) } namespace Lending { -/** The maximum management fee rate allowed by a loan broker in 1/10 bips. - - Valid values are between 0 and 10% inclusive. -*/ +/** + * The maximum management fee rate allowed by a loan broker in 1/10 bips. + * + * Valid values are between 0 and 10% inclusive. + */ constexpr TenthBips16 kMaxManagementFeeRate( unsafeCast(percentageToTenthBips(10).value())); static_assert(kMaxManagementFeeRate == TenthBips16(std::uint16_t(10'000u))); -/** The maximum coverage rate required of a loan broker in 1/10 bips. - - Valid values are between 0 and 100% inclusive. -*/ +/** + * The maximum coverage rate required of a loan broker in 1/10 bips. + * + * Valid values are between 0 and 100% inclusive. + */ constexpr TenthBips32 kMaxCoverRate = percentageToTenthBips(100); static_assert(kMaxCoverRate == TenthBips32(100'000u)); -/** The maximum overpayment fee on a loan in 1/10 bips. -* - Valid values are between 0 and 100% inclusive. -*/ +/** + * The maximum overpayment fee on a loan in 1/10 bips. + * + * Valid values are between 0 and 100% inclusive. + */ constexpr TenthBips32 kMaxOverpaymentFee = percentageToTenthBips(100); static_assert(kMaxOverpaymentFee == TenthBips32(100'000u)); -/** Annualized interest rate of the Loan in 1/10 bips. +/** + * Annualized interest rate of the Loan in 1/10 bips. * * Valid values are between 0 and 100% inclusive. */ constexpr TenthBips32 kMaxInterestRate = percentageToTenthBips(100); static_assert(kMaxInterestRate == TenthBips32(100'000u)); -/** The maximum premium added to the interest rate for late payments on a loan +/** + * The maximum premium added to the interest rate for late payments on a loan * in 1/10 bips. * * Valid values are between 0 and 100% inclusive. @@ -147,7 +182,8 @@ static_assert(kMaxInterestRate == TenthBips32(100'000u)); constexpr TenthBips32 kMaxLateInterestRate = percentageToTenthBips(100); static_assert(kMaxLateInterestRate == TenthBips32(100'000u)); -/** The maximum close interest rate charged for repaying a loan early in 1/10 +/** + * The maximum close interest rate charged for repaying a loan early in 1/10 * bips. * * Valid values are between 0 and 100% inclusive. @@ -155,7 +191,8 @@ static_assert(kMaxLateInterestRate == TenthBips32(100'000u)); constexpr TenthBips32 kMaxCloseInterestRate = percentageToTenthBips(100); static_assert(kMaxCloseInterestRate == TenthBips32(100'000u)); -/** The maximum overpayment interest rate charged on loan overpayments in 1/10 +/** + * The maximum overpayment interest rate charged on loan overpayments in 1/10 * bips. * * Valid values are between 0 and 100% inclusive. @@ -163,7 +200,8 @@ static_assert(kMaxCloseInterestRate == TenthBips32(100'000u)); constexpr TenthBips32 kMaxOverpaymentInterestRate = percentageToTenthBips(100); static_assert(kMaxOverpaymentInterestRate == TenthBips32(100'000u)); -/** LoanPay transaction cost will be one base fee per X combined payments +/** + * LoanPay transaction cost will be one base fee per X combined payments * * The number of payments is estimated based on the Amount paid and the Loan's * Fixed Payment size. Overpayments (indicated with the tfLoanOverpayment flag) @@ -174,7 +212,8 @@ static_assert(kMaxOverpaymentInterestRate == TenthBips32(100'000u)); */ static constexpr int kLoanPaymentsPerFeeIncrement = 5; -/** Maximum number of combined payments that a LoanPay transaction will process +/** + * Maximum number of combined payments that a LoanPay transaction will process * * This limit is enforced during the loan payment process, and thus is not * estimated. If the limit is hit, no further payments or overpayments will be @@ -199,112 +238,267 @@ static constexpr int kLoanPaymentsPerFeeIncrement = 5; static constexpr int kLoanMaximumPaymentsPerTransaction = 100; } // namespace Lending -/** The maximum length of a URI inside an NFT */ +/** + * The maximum length of a URI inside an NFT + */ constexpr std::size_t kMaxTokenUriLength = 256; -/** The maximum length of a Data element inside a DID */ +/** + * The maximum length of a Data element inside a DID + */ constexpr std::size_t kMaxDidDocumentLength = 256; -/** The maximum length of a URI inside a DID */ +/** + * The maximum length of a URI inside a DID + */ constexpr std::size_t kMaxDidUriLength = 256; -/** The maximum length of an Attestation inside a DID */ +/** + * The maximum length of an Attestation inside a DID + */ constexpr std::size_t kMaxDidDataLength = 256; -/** The maximum length of a domain */ +/** + * The maximum length of a domain + */ constexpr std::size_t kMaxDomainLength = 256; -/** The maximum length of a URI inside a Credential */ +/** + * The maximum length of a URI inside a Credential + */ constexpr std::size_t kMaxCredentialUriLength = 256; -/** The maximum length of a CredentialType inside a Credential */ +/** + * The maximum length of a CredentialType inside a Credential + */ constexpr std::size_t kMaxCredentialTypeLength = 64; -/** The maximum number of credentials can be passed in array */ +/** + * The maximum number of credentials can be passed in array + */ constexpr std::size_t kMaxCredentialsArraySize = 8; -/** The maximum number of credentials can be passed in array for permissioned - * domain */ +/** + * The maximum number of credentials can be passed in array for permissioned + * domain + */ constexpr std::size_t kMaxPermissionedDomainCredentialsArraySize = 10; -/** The maximum length of MPTokenMetadata */ +/** + * The maximum length of MPTokenMetadata + */ constexpr std::size_t kMaxMpTokenMetadataLength = 1024; -/** The maximum amount of MPTokenIssuance */ +/** + * The maximum amount of MPTokenIssuance + */ constexpr std::uint64_t kMaxMpTokenAmount = 0x7FFF'FFFF'FFFF'FFFFull; static_assert(Number::kMaxRep >= kMaxMpTokenAmount); -/** The maximum length of Data payload */ +/** + * The maximum length of Data payload + */ constexpr std::size_t kMaxDataPayloadLength = 256; -/** Vault withdrawal policies */ +/** + * Vault withdrawal policies + */ constexpr std::uint8_t kVaultStrategyFirstComeFirstServe = 1; -/** Default IOU scale factor for a Vault */ +/** + * Default IOU scale factor for a Vault + */ constexpr std::uint8_t kVaultDefaultIouScale = 6; -/** Maximum scale factor for a Vault. The number is chosen to ensure that -1 IOU can be always converted to shares. -10^19 > maxMPTokenAmount (2^64-1) > 10^18 */ +/** + * Maximum scale factor for a Vault. The number is chosen to ensure that + * 1 IOU can be always converted to shares. + * 10^19 > maxMPTokenAmount (2^64-1) > 10^18 + */ constexpr std::uint8_t kVaultMaximumIouScale = 18; -/** Maximum recursion depth for vault shares being put as an asset inside - * another vault; counted from 0 */ +/** + * Maximum recursion depth for vault shares being put as an asset inside + * another vault; counted from 0 + */ constexpr std::uint8_t kMaxAssetCheckDepth = 5; -/** A ledger index. */ +/** + * A ledger index. + */ using LedgerIndex = std::uint32_t; constexpr std::uint32_t kFlagLedgerInterval = 256; -/** Returns true if the given ledgerIndex is a voting ledgerIndex */ +/** + * Returns true if the given ledgerIndex is a voting ledgerIndex + */ bool isVotingLedger(LedgerIndex seq); -/** Returns true if the given ledgerIndex is a flag ledgerIndex */ +/** + * Returns true if the given ledgerIndex is a flag ledgerIndex + */ bool isFlagLedger(LedgerIndex seq); -/** A transaction identifier. - The value is computed as the hash of the - canonicalized, serialized transaction object. -*/ +/** + * A transaction identifier. + * The value is computed as the hash of the + * canonicalized, serialized transaction object. + */ using TxID = uint256; -/** The maximum number of trustlines to delete as part of AMM account +/** + * The maximum number of trustlines to delete as part of AMM account * deletion cleanup. */ constexpr std::uint16_t kMaxDeletableAmmTrustLines = 512; -/** The maximum length of a URI inside an Oracle */ +/** + * The maximum length of a URI inside an Oracle + */ constexpr std::size_t kMaxOracleUri = 256; -/** The maximum length of a Provider inside an Oracle */ +/** + * The maximum length of a Provider inside an Oracle + */ constexpr std::size_t kMaxOracleProvider = 256; -/** The maximum size of a data series array inside an Oracle */ +/** + * The maximum size of a data series array inside an Oracle + */ constexpr std::size_t kMaxOracleDataSeries = 10; -/** The maximum length of a SymbolClass inside an Oracle */ +/** + * The maximum length of a SymbolClass inside an Oracle + */ constexpr std::size_t kMaxOracleSymbolClass = 16; -/** The maximum allowed time difference between lastUpdateTime and the time - of the last closed ledger -*/ +/** + * The maximum allowed time difference between lastUpdateTime and the time + * of the last closed ledger + */ constexpr std::size_t kMaxLastUpdateTimeDelta = 300; -/** The maximum price scaling factor +/** + * The maximum price scaling factor */ constexpr std::size_t kMaxPriceScale = 20; -/** The maximum percentage of outliers to trim +/** + * The maximum percentage of outliers to trim */ constexpr std::size_t kMaxTrim = 25; -/** The maximum number of delegate permissions an account can grant +/** + * The maximum number of delegate permissions an account can grant */ constexpr std::size_t kPermissionMaxSize = 10; -/** The maximum number of transactions that can be in a batch. */ +/** + * 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..98301af487 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,33 +18,37 @@ #include #include #include +#include #include #include +#include +#include namespace xrpl { -/** A public key. - - Public keys are used in the public-key cryptography - system used to verify signatures attached to messages. - - The format of the public key is XRPL specific, - information needed to determine the cryptosystem - parameters used is stored inside the key. - - As of this writing two systems are supported: - - secp256k1 - ed25519 - - secp256k1 public keys consist of a 33 byte - compressed public key, with the lead byte equal - to 0x02 or 0x03. - - The ed25519 public keys consist of a 1 byte - prefix constant 0xED, followed by 32 bytes of - public key data. -*/ +/** + * A public key. + * + * Public keys are used in the public-key cryptography + * system used to verify signatures attached to messages. + * + * The format of the public key is XRPL specific, + * information needed to determine the cryptosystem + * parameters used is stored inside the key. + * + * As of this writing two systems are supported: + * + * secp256k1 + * ed25519 + * + * secp256k1 public keys consist of a 33 byte + * compressed public key, with the lead byte equal + * to 0x02 or 0x03. + * + * The ed25519 public keys consist of a 1 byte + * prefix constant 0xED, followed by 32 bytes of + * public key data. + */ class PublicKey { protected: @@ -56,11 +67,12 @@ public: PublicKey& operator=(PublicKey const& other); - /** Create a public key. - - Preconditions: - publicKeyType(slice) != std::nullopt - */ + /** + * Create a public key. + * + * Preconditions: + * publicKeyType(slice) != std::nullopt + */ explicit PublicKey(Slice const& slice); [[nodiscard]] std::uint8_t const* @@ -111,7 +123,8 @@ public: } }; -/** Print the public key to a stream. +/** + * Print the public key to a stream. */ std::ostream& operator<<(std::ostream& os, PublicKey const& pk); @@ -170,39 +183,41 @@ parseBase58(TokenType type, std::string const& s); enum class ECDSACanonicality { Canonical, FullyCanonical }; -/** Determines the canonicality of a signature. - - A canonical signature is in its most reduced form. - For example the R and S components do not contain - additional leading zeroes. However, even in - canonical form, (R,S) and (R,G-S) are both - valid signatures for message M. - - Therefore, to prevent malleability attacks we - define a fully canonical signature as one where: - - R < G - S - - where G is the curve order. - - This routine returns std::nullopt if the format - of the signature is invalid (for example, the - points are encoded incorrectly). - - @return std::nullopt if the signature fails - validity checks. - - @note Only the format of the signature is checked, - no verification cryptography is performed. -*/ +/** + * Determines the canonicality of a signature. + * + * A canonical signature is in its most reduced form. + * For example the R and S components do not contain + * additional leading zeroes. However, even in + * canonical form, (R,S) and (R,G-S) are both + * valid signatures for message M. + * + * Therefore, to prevent malleability attacks we + * define a fully canonical signature as one where: + * + * R < G - S + * + * where G is the curve order. + * + * This routine returns std::nullopt if the format + * of the signature is invalid (for example, the + * points are encoded incorrectly). + * + * @return std::nullopt if the signature fails + * validity checks. + * + * @note Only the format of the signature is checked, + * no verification cryptography is performed. + */ std::optional ecdsaCanonicality(Slice const& sig); -/** Returns the type of public key. - - @return std::nullopt If the public key does not - represent a known type. -*/ +/** + * Returns the type of public key. + * + * @return std::nullopt If the public key does not + * represent a known type. + */ /** @{ */ [[nodiscard]] std::optional publicKeyType(Slice const& slice); @@ -214,7 +229,9 @@ publicKeyType(PublicKey const& publicKey) } /** @} */ -/** Verify a secp256k1 signature on the digest of a message. */ +/** + * Verify a secp256k1 signature on the digest of a message. + */ [[nodiscard]] bool verifyDigest( PublicKey const& publicKey, @@ -222,14 +239,17 @@ verifyDigest( Slice const& sig, bool mustBeFullyCanonical = true) noexcept; -/** Verify a signature on a message. - With secp256k1 signatures, the data is first hashed with - SHA512-Half, and the resulting digest is signed. -*/ +/** + * Verify a signature on a message. + * With secp256k1 signatures, the data is first hashed with + * SHA512-Half, and the resulting digest is signed. + */ [[nodiscard]] bool verify(PublicKey const& publicKey, Slice const& m, Slice const& sig) noexcept; -/** Calculate the 160-bit node ID from a node public key. */ +/** + * Calculate the 160-bit node ID from a node public key. + */ NodeID calcNodeID(PublicKey const&); diff --git a/include/xrpl/protocol/Quality.h b/include/xrpl/protocol/Quality.h index e261025cb8..3475efa977 100644 --- a/include/xrpl/protocol/Quality.h +++ b/include/xrpl/protocol/Quality.h @@ -1,26 +1,29 @@ #pragma once +#include +#include #include -#include #include -#include #include +#include +#include #include #include #include namespace xrpl { -/** Represents a pair of input and output currencies. - - The input currency can be converted to the output - currency by multiplying by the rate, represented by - Quality. - - For offers, "in" is always TakerPays and "out" is - always TakerGets. -*/ +/** + * Represents a pair of input and output currencies. + * + * The input currency can be converted to the output + * currency by multiplying by the rate, represented by + * Quality. + * + * For offers, "in" is always TakerPays and "out" is + * always TakerGets. + */ template struct TAmounts { @@ -34,7 +37,9 @@ struct TAmounts { } - /** Returns `true` if either quantity is not positive. */ + /** + * Returns `true` if either quantity is not positive. + */ [[nodiscard]] bool empty() const noexcept { @@ -82,11 +87,12 @@ operator!=(TAmounts const& lhs, TAmounts const& rhs) noexcept // XRPL specific constant used for parsing qualities and other things #define QUALITY_ONE 1'000'000'000 -/** Represents the logical ratio of output currency to input currency. - Internally this is stored using a custom floating point representation, - as the inverse of the ratio, so that quality will be descending in - a sequence of actual values that represent qualities. -*/ +/** + * Represents the logical ratio of output currency to input currency. + * Internally this is stored using a custom floating point representation, + * as the inverse of the ratio, so that quality will be descending in + * a sequence of actual values that represent qualities. + */ class Quality { public: @@ -107,26 +113,36 @@ private: public: Quality() = default; - /** Create a quality from the integer encoding of an STAmount */ + /** + * Create a quality from the integer encoding of an STAmount + */ explicit Quality(std::uint64_t value); - /** Create a quality from the ratio of two amounts. */ + /** + * Create a quality from the ratio of two amounts. + */ explicit Quality(Amounts const& amount); - /** Create a quality from the ratio of two amounts. */ + /** + * Create a quality from the ratio of two amounts. + */ template explicit Quality(TAmounts const& amount) : Quality(Amounts(toSTAmount(amount.in), toSTAmount(amount.out))) { } - /** Create a quality from the ratio of two amounts. */ + /** + * Create a quality from the ratio of two amounts. + */ template Quality(Out const& out, In const& in) : Quality(Amounts(toSTAmount(in), toSTAmount(out))) { } - /** Advances to the next higher quality level. */ + /** + * Advances to the next higher quality level. + */ /** @{ */ Quality& operator++(); @@ -135,7 +151,9 @@ public: operator++(int); /** @} */ - /** Advances to the next lower quality level. */ + /** + * Advances to the next lower quality level. + */ /** @{ */ Quality& operator--(); @@ -144,23 +162,27 @@ public: operator--(int); /** @} */ - /** Returns the quality as STAmount. */ + /** + * Returns the quality as STAmount. + */ [[nodiscard]] STAmount rate() const { return amountFromQuality(value_); } - /** Returns the quality rounded up to the specified number - of decimal digits. - */ + /** + * Returns the quality rounded up to the specified number + * of decimal digits. + */ [[nodiscard]] Quality round(int tickSize) const; - /** Returns the scaled amount with in capped. - Math is avoided if the result is exact. The output is clamped - to prevent money creation. - */ + /** + * Returns the scaled amount with in capped. + * Math is avoided if the result is exact. The output is clamped + * to prevent money creation. + */ [[nodiscard]] Amounts ceilIn(Amounts const& amount, STAmount const& limit) const; @@ -178,10 +200,11 @@ public: [[nodiscard]] TAmounts ceilInStrict(TAmounts const& amount, In const& limit, bool roundUp) const; - /** Returns the scaled amount with out capped. - Math is avoided if the result is exact. The input is clamped - to prevent money creation. - */ + /** + * Returns the scaled amount with out capped. + * Math is avoided if the result is exact. The input is clamped + * to prevent money creation. + */ [[nodiscard]] Amounts ceilOut(Amounts const& amount, STAmount const& limit) const; @@ -213,10 +236,11 @@ private: Round... round) const; public: - /** Returns `true` if lhs is lower quality than `rhs`. - Lower quality means the taker receives a worse deal. - Higher quality is better for the taker. - */ + /** + * Returns `true` if lhs is lower quality than `rhs`. + * Lower quality means the taker receives a worse deal. + * Higher quality is better for the taker. + */ friend bool operator<(Quality const& lhs, Quality const& rhs) noexcept { @@ -280,7 +304,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); @@ -355,10 +379,11 @@ Quality::ceilOutStrict(TAmounts const& amount, Out const& limit, bool r return ceilTAmountsHelper(amount, limit, amount.out, kCeilOutFnPtr, roundUp); } -/** Calculate the quality of a two-hop path given the two hops. - @param lhs The first leg of the path: input to intermediate. - @param rhs The second leg of the path: intermediate to output. -*/ +/** + * Calculate the quality of a two-hop path given the two hops. + * @param lhs The first leg of the path: input to intermediate. + * @param rhs The second leg of the path: intermediate to output. + */ Quality composedQuality(Quality const& lhs, Quality const& rhs); diff --git a/include/xrpl/protocol/QualityFunction.h b/include/xrpl/protocol/QualityFunction.h index 96d30735b8..128b37ce12 100644 --- a/include/xrpl/protocol/QualityFunction.h +++ b/include/xrpl/protocol/QualityFunction.h @@ -1,12 +1,19 @@ #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, +/** + * Average quality of a path as a function of `out`: q(out) = m * out + b, * where m = -1 / poolGets, b = poolPays / poolGets. If CLOB offer then * `m` is equal to 0 `b` is equal to the offer's quality. The function * is derived by substituting `in` in q = out / in with the swap out formula @@ -39,19 +46,22 @@ public: template QualityFunction(TAmounts const& amounts, std::uint32_t tfee, AMMTag); - /** Combines QF with the next step QF + /** + * Combines QF with the next step QF */ void combine(QualityFunction const& qf); - /** Find output to produce the requested + /** + * Find output to produce the requested * average quality. * @param quality requested average quality (quality limit) */ std::optional outFromAvgQ(Quality const& quality); - /** Return true if the quality function is constant + /** + * Return true if the quality function is constant */ [[nodiscard]] bool isConst() const diff --git a/include/xrpl/protocol/Rate.h b/include/xrpl/protocol/Rate.h index 504b17ed80..048787cab5 100644 --- a/include/xrpl/protocol/Rate.h +++ b/include/xrpl/protocol/Rate.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -10,12 +10,13 @@ namespace xrpl { -/** Represents a transfer rate - - Transfer rates are specified as fractions of 1 billion. - For example, a transfer rate of 1% is represented as - 1,010,000,000. -*/ +/** + * Represents a transfer rate + * + * Transfer rates are specified as fractions of 1 billion. + * For example, a transfer rate of 1% is represented as + * 1,010,000,000. + */ struct Rate : private boost::totally_ordered { std::uint32_t value; @@ -65,13 +66,17 @@ STAmount divideRound(STAmount const& amount, Rate const& rate, Asset const& asset, bool roundUp); namespace nft { -/** Given a transfer fee (in basis points) convert it to a transfer rate. */ +/** + * Given a transfer fee (in basis points) convert it to a transfer rate. + */ Rate transferFeeAsRate(std::uint16_t fee); } // namespace nft -/** A transfer rate signifying a 1:1 exchange */ +/** + * A transfer rate signifying a 1:1 exchange + */ extern Rate const kParityRate; } // namespace xrpl diff --git a/include/xrpl/protocol/Rules.h b/include/xrpl/protocol/Rules.h index 47b20756db..2c2136b6e8 100644 --- a/include/xrpl/protocol/Rules.h +++ b/include/xrpl/protocol/Rules.h @@ -4,11 +4,15 @@ #include #include +#include +#include #include +#include namespace xrpl { -/** Check whether a feature is enabled in the current ledger rules +/** + * Check whether a feature is enabled in the current ledger rules * * @param feature The feature to be tested. * @param resultIfNoRules What to return if called from outside a Transactor context. @@ -16,7 +20,8 @@ namespace xrpl { bool isFeatureEnabled(uint256 const& feature, bool resultIfNoRules); -/** Check whether a feature is enabled in the current ledger rules +/** + * Check whether a feature is enabled in the current ledger rules * * @param feature The feature to be tested. * @@ -28,7 +33,9 @@ isFeatureEnabled(uint256 const& feature); class DigestAwareReadView; -/** Rules controlling protocol behavior. */ +/** + * Rules controlling protocol behavior. + */ class Rules { private: @@ -51,11 +58,12 @@ public: Rules() = delete; - /** Construct an empty rule set. - - These are the rules reflected by - the genesis ledger. - */ + /** + * Construct an empty rule set. + * + * These are the rules reflected by + * the genesis ledger. + */ explicit Rules(std::unordered_set> const& presets); private: @@ -77,14 +85,17 @@ private: presets() const; public: - /** Returns `true` if a feature is enabled. */ + /** + * Returns `true` if a feature is enabled. + */ [[nodiscard]] bool enabled(uint256 const& feature) const; - /** Returns `true` if two rule sets are identical. - - @note This is for diagnostics. - */ + /** + * Returns `true` if two rule sets are identical. + * + * @note This is for diagnostics. + */ bool operator==(Rules const&) const; @@ -98,7 +109,8 @@ getCurrentTransactionRules(); void setCurrentTransactionRules(std::optional r); -/** RAII class to set and restore the current transaction rules +/** + * RAII class to set and restore the current transaction rules */ class CurrentTransactionRulesGuard { diff --git a/include/xrpl/protocol/SField.h b/include/xrpl/protocol/SField.h index 34fb66ce00..21ab7813f9 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 { @@ -116,16 +117,17 @@ fieldCode(int id, int index) return (id << 16) | index; } -/** Identifies fields. - - Fields are necessary to tag data in signed transactions so that - the binary format of the transaction can be canonicalized. All - SFields are created at compile time. - - Each SField, once constructed, lives until program termination, and there - is only one instance per fieldType/fieldValue pair which serves the - entire application. -*/ +/** + * Identifies fields. + * + * Fields are necessary to tag data in signed transactions so that + * the binary format of the transaction can be canonicalized. All + * SFields are created at compile time. + * + * Each SField, once constructed, lives until program termination, and there + * is only one instance per fieldType/fieldValue pair which serves the + * entire application. + */ class SField { public: @@ -298,7 +300,9 @@ private: static std::unordered_map knownNameToField; }; -/** A field with a type known at compile time. */ +/** + * A field with a type known at compile time. + */ template struct TypedField : SField { @@ -308,7 +312,9 @@ struct TypedField : SField explicit TypedField(PrivateAccessTagT pat, Args&&... args); }; -/** Indicate std::optional field semantics. */ +/** + * Indicate std::optional field semantics. + */ template struct OptionaledField { diff --git a/include/xrpl/protocol/SOTemplate.h b/include/xrpl/protocol/SOTemplate.h index 72e0573d29..cb24ee315a 100644 --- a/include/xrpl/protocol/SOTemplate.h +++ b/include/xrpl/protocol/SOTemplate.h @@ -3,14 +3,18 @@ #include #include +#include #include #include #include +#include #include namespace xrpl { -/** Kind of element in each entry of an SOTemplate. */ +/** + * Kind of element in each entry of an SOTemplate. + */ // 2026 usages, 129 files // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) enum SOEStyle { @@ -23,13 +27,17 @@ enum SOEStyle { }; // Part of a Python-parsed DSL (transactions.macro); bare enumerator names required by the parser -/** Amount fields that can support MPT */ +/** + * Amount fields that can support MPT + */ // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) enum SOETxMPTIssue { SoeMptNone, SoeMptSupported, SoeMptNotSupported }; //------------------------------------------------------------------------------ -/** An element in a SOTemplate. */ +/** + * An element in a SOTemplate. + */ class SOElement { // Use std::reference_wrapper so SOElement can be stored in a std::vector. @@ -88,10 +96,11 @@ public: //------------------------------------------------------------------------------ -/** Defines the fields and their attributes within a STObject. - Each subclass of SerializedObject will provide its own template - describing the available fields and their metadata attributes. -*/ +/** + * Defines the fields and their attributes within a STObject. + * Each subclass of SerializedObject will provide its own template + * describing the available fields and their metadata attributes. + */ class SOTemplate { public: @@ -101,14 +110,16 @@ public: SOTemplate& operator=(SOTemplate&& other) = default; - /** Create a template populated with all fields. - After creating the template fields cannot be added, modified, or removed. - */ + /** + * Create a template populated with all fields. + * After creating the template fields cannot be added, modified, or removed. + */ SOTemplate(std::vector uniqueFields, std::vector commonFields = {}); - /** Create a template populated with all fields. - Note: Defers to the vector constructor above. - */ + /** + * Create a template populated with all fields. + * Note: Defers to the vector constructor above. + */ SOTemplate( std::initializer_list uniqueFields, std::initializer_list commonFields = {}); @@ -138,14 +149,18 @@ public: return end(); } - /** The number of entries in this template */ + /** + * The number of entries in this template + */ [[nodiscard]] std::size_t size() const { return elements_.size(); } - /** Retrieve the position of a named field. */ + /** + * Retrieve the position of a named field. + */ [[nodiscard]] int getIndex(SField const&) const; 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 1a5b442d8b..cc80481582 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -1,14 +1,20 @@ #pragma once #include -#include #include +#include +#include #include +#include #include +#include +#include #include +#include #include #include #include +#include #include #include #include @@ -16,6 +22,14 @@ #include #include +#include +#include +#include +#include +#include +#include +#include + namespace xrpl { // Internal form: @@ -176,7 +190,9 @@ public: [[nodiscard]] int signum() const noexcept; - /** Returns a zero value with the same issuer and currency. */ + /** + * Returns a zero value with the same issuer and currency. + */ [[nodiscard]] STAmount zeroed() const; @@ -241,7 +257,9 @@ public: void clear(Asset const& asset); - /** Set the Issue for this amount. */ + /** + * Set the Issue for this amount. + */ void setIssue(Asset const& asset); @@ -690,7 +708,8 @@ divRoundStrict(STAmount const& v1, STAmount const& v2, Asset const& asset, bool std::uint64_t getRate(STAmount const& offerOut, STAmount const& offerIn); -/** Round an arbitrary precision Amount to the precision of an STAmount that has +/** + * Round an arbitrary precision Amount to the precision of an STAmount that has * a given exponent. * * This is used to ensure that calculations involving IOU amounts do not collect @@ -700,7 +719,6 @@ getRate(STAmount const& offerOut, STAmount const& offerIn); * @param scale An exponent value to establish the precision limit of * `value`. Should be larger than `value.exponent()`. * @param rounding Optional Number rounding mode - * */ [[nodiscard]] STAmount roundToScale( @@ -708,7 +726,8 @@ roundToScale( std::int32_t scale, Number::RoundingMode rounding = Number::getround()); -/** Round an arbitrary precision Number IN PLACE to the precision of a given +/** + * Round an arbitrary precision Number IN PLACE to the precision of a given * Asset. * * This is used to ensure that calculations do not collect dust for IOUs, or @@ -724,7 +743,8 @@ roundToAsset(A const& asset, Number& value) value = STAmount{asset, value}; } -/** Round an arbitrary precision Number to the precision of a given Asset. +/** + * Round an arbitrary precision Number to the precision of a given Asset. * * This is used to ensure that calculations do not collect dust beyond specified * scale for IOUs, or fractional amounts for the integral types XRP and MPT. @@ -766,7 +786,8 @@ canAdd(STAmount const& amt1, STAmount const& amt2); bool canSubtract(STAmount const& amt1, STAmount const& amt2); -/** Get the scale of a Number for a given asset. +/** + * Get the scale of a Number for a given asset. * * "scale" is similar to "exponent", but from the perspective of STAmount, which has different rules * and mantissa ranges for determining the exponent than Number. 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..acc5500a57 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 @@ -12,7 +15,9 @@ namespace xrpl { -/// Note, should be treated as flags that can be | and & +/** + * Note, should be treated as flags that can be | and & + */ struct JsonOptions { using underlying_t = unsigned int; @@ -50,22 +55,28 @@ struct JsonOptions [[nodiscard]] constexpr auto friend operator!=(JsonOptions lh, JsonOptions rh) noexcept -> bool = default; - /// Returns JsonOptions union of lh and rh + /** + * Returns JsonOptions union of lh and rh + */ [[nodiscard]] constexpr JsonOptions friend operator|(JsonOptions lh, JsonOptions rh) noexcept { return {lh.value | rh.value}; } - /// Returns JsonOptions intersection of lh and rh + /** + * Returns JsonOptions intersection of lh and rh + */ [[nodiscard]] constexpr JsonOptions friend operator&(JsonOptions lh, JsonOptions rh) noexcept { return {lh.value & rh.value}; } - /// Returns JsonOptions binary negation, can be used with & (above) for set - /// difference e.g. `(options & ~JsonOptions::kIncludeDate)` + /** + * Returns JsonOptions binary negation, can be used with & (above) for set + * difference e.g. `(options & ~JsonOptions::kIncludeDate)` + */ [[nodiscard]] constexpr JsonOptions friend operator~(JsonOptions v) noexcept { @@ -100,19 +111,20 @@ class STVar; //------------------------------------------------------------------------------ -/** A type which can be exported to a well known binary format. - - A STBase: - - Always a field - - Can always go inside an eligible enclosing STBase - (such as STArray) - - Has a field name - - Like JSON, a SerializedObject is a basket which has rules - on what it can hold. - - @note "ST" stands for "Serialized Type." -*/ +/** + * A type which can be exported to a well known binary format. + * + * A STBase: + * - Always a field + * - Can always go inside an eligible enclosing STBase + * (such as STArray) + * - Has a field name + * + * Like JSON, a SerializedObject is a basket which has rules + * on what it can hold. + * + * @note "ST" stands for "Serialized Type." + */ class STBase { SField const* fName_; @@ -159,9 +171,10 @@ public: [[nodiscard]] virtual bool isDefault() const; - /** A STBase is a field. - This sets the name. - */ + /** + * A STBase is a field. + * This sets the name. + */ void setFName(SField const& n); 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 0667c54e30..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 { 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..ad5bd4c012 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 @@ -17,7 +18,9 @@ namespace xrpl { -/** Convert between serialized type U and C++ type T. */ +/** + * Convert between serialized type U and C++ type T. + */ template struct STExchange; @@ -89,7 +92,9 @@ struct STExchange //------------------------------------------------------------------------------ -/** Return the value of a field in an STObject as a given type. */ +/** + * Return the value of a field in an STObject as a given type. + */ /** @{ */ template std::optional @@ -118,7 +123,9 @@ get(STObject const& st, TypedField const& f) } /** @} */ -/** Set a field value in an STObject. */ +/** + * Set a field value in an STObject. + */ template void set(STObject& st, TypedField const& f, T&& t) @@ -126,7 +133,9 @@ set(STObject& st, TypedField const& f, T&& t) st.set(STExchange>::set(f, std::forward(t))); } -/** Set a blob field using an init function. */ +/** + * Set a blob field using an init function. + */ template void set(STObject& st, TypedField const& f, std::size_t size, Init&& init) @@ -134,7 +143,9 @@ set(STObject& st, TypedField const& f, std::size_t size, Init&& init) st.set(std::make_unique(f, size, init)); } -/** Set a blob field from data. */ +/** + * Set a blob field from data. + */ template void set(STObject& st, TypedField const& f, void const* data, std::size_t size) @@ -142,7 +153,9 @@ set(STObject& st, TypedField const& f, void const* data, std::size_t siz st.set(std::make_unique(f, data, size)); } -/** Remove a field in an STObject. */ +/** + * Remove a field in an STObject. + */ template void erase(STObject& st, TypedField const& f) 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..8731488adb 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 { @@ -21,7 +33,9 @@ public: using const_pointer = std::shared_ptr; using const_ref = std::shared_ptr const&; - /** Create an empty object with the given key and type. */ + /** + * Create an empty object with the given key and type. + */ explicit STLedgerEntry(Keylet const& k); STLedgerEntry(LedgerEntryType type, uint256 const& key); STLedgerEntry(SerialIter& sit, uint256 const& index); @@ -40,10 +54,11 @@ public: [[nodiscard]] json::Value getJson(JsonOptions options = JsonOptions::Values::None) const override; - /** Returns the 'key' (or 'index') of this item. - The key identifies this entry's position in - the SHAMap associative container. - */ + /** + * Returns the 'key' (or 'index') of this item. + * The key identifies this entry's position in + * the SHAMap associative container. + */ [[nodiscard]] uint256 const& key() const; @@ -93,10 +108,11 @@ inline STLedgerEntry::STLedgerEntry( { } -/** Returns the 'key' (or 'index') of this item. - The key identifies this entry's position in - the SHAMap associative container. -*/ +/** + * Returns the 'key' (or 'index') of this item. + * The key identifies this entry's position in + * the SHAMap associative container. + */ inline uint256 const& STLedgerEntry::key() const { 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..a1cdff22e0 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,13 @@ public: [[nodiscard]] AccountID getAccountID(SField const& field) const; + /** + * The account responsible for the authorization: the delegate when + * sfDelegate is present, otherwise the account. + */ + [[nodiscard]] AccountID + getInitiator() const; + [[nodiscard]] Blob getFieldVL(SField const& field) const; [[nodiscard]] STAmount const& @@ -235,103 +254,112 @@ public: [[nodiscard]] STNumber const& getFieldNumber(SField const& field) const; - /** Get the value of a field. - @param A TypedField built from an SField value representing the desired - object field. In typical use, the TypedField will be implicitly - constructed. - @return The value of the specified field. - @throws STObject::FieldErr if the field is not present. - */ + /** + * Get the value of a field. + * @param A TypedField built from an SField value representing the desired + * object field. In typical use, the TypedField will be implicitly + * constructed. + * @return The value of the specified field. + * @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 - - @param An OptionaledField built from an SField value representing the - desired object field. In typical use, the OptionaledField will be - constructed by using the ~ operator on an SField. - @return std::nullopt if the field is not present, else the value of - the specified field. - */ + /** + * Get the value of a field as a std::optional + * + * @param An OptionaledField built from an SField value representing the + * desired object field. In typical use, the OptionaledField will be + * constructed by using the ~ operator on an SField. + * @return std::nullopt if the field is not present, else the value of + * the specified field. + */ template std::optional> operator[](OptionaledField const& of) const; - /** Get a modifiable field value. - @param A TypedField built from an SField value representing the desired - object field. In typical use, the TypedField will be implicitly - constructed. - @return A modifiable reference to the value of the specified field. - @throws STObject::FieldErr if the field is not present. - */ + /** + * Get a modifiable field value. + * @param A TypedField built from an SField value representing the desired + * object field. In typical use, the TypedField will be implicitly + * constructed. + * @return A modifiable reference to the value of the specified field. + * @throws STObject::FieldErr if the field is not present. + */ template ValueProxy operator[](TypedField const& f); - /** Return a modifiable field value as std::optional - - @param An OptionaledField built from an SField value representing the - desired object field. In typical use, the OptionaledField will be - constructed by using the ~ operator on an SField. - @return Transparent proxy object to an `optional` holding a modifiable - reference to the value of the specified field. Returns - std::nullopt if the field is not present. - */ + /** + * Return a modifiable field value as std::optional + * + * @param An OptionaledField built from an SField value representing the + * desired object field. In typical use, the OptionaledField will be + * constructed by using the ~ operator on an SField. + * @return Transparent proxy object to an `optional` holding a modifiable + * reference to the value of the specified field. Returns + * std::nullopt if the field is not present. + */ template OptionalProxy operator[](OptionaledField const& of); - /** Get the value of a field. - @param A TypedField built from an SField value representing the desired - object field. In typical use, the TypedField will be implicitly - constructed. - @return The value of the specified field. - @throws STObject::FieldErr if the field is not present. - */ + /** + * Get the value of a field. + * @param A TypedField built from an SField value representing the desired + * object field. In typical use, the TypedField will be implicitly + * constructed. + * @return The value of the specified field. + * @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 - - @param An OptionaledField built from an SField value representing the - desired object field. In typical use, the OptionaledField will be - constructed by using the ~ operator on an SField. - @return std::nullopt if the field is not present, else the value of - the specified field. - */ + /** + * Get the value of a field as std::optional + * + * @param An OptionaledField built from an SField value representing the + * desired object field. In typical use, the OptionaledField will be + * constructed by using the ~ operator on an SField. + * @return std::nullopt if the field is not present, else the value of + * the specified field. + */ template [[nodiscard]] std::optional> at(OptionaledField const& of) const; - /** Get a modifiable field value. - @param A TypedField built from an SField value representing the desired - object field. In typical use, the TypedField will be implicitly - constructed. - @return A modifiable reference to the value of the specified field. - @throws STObject::FieldErr if the field is not present. - */ + /** + * Get a modifiable field value. + * @param A TypedField built from an SField value representing the desired + * object field. In typical use, the TypedField will be implicitly + * constructed. + * @return A modifiable reference to the value of the specified field. + * @throws STObject::FieldErr if the field is not present. + */ template ValueProxy at(TypedField const& f); - /** Return a modifiable field value as std::optional - - @param An OptionaledField built from an SField value representing the - desired object field. In typical use, the OptionaledField will be - constructed by using the ~ operator on an SField. - @return Transparent proxy object to an `optional` holding a modifiable - reference to the value of the specified field. Returns - std::nullopt if the field is not present. - */ + /** + * Return a modifiable field value as std::optional + * + * @param An OptionaledField built from an SField value representing the + * desired object field. In typical use, the OptionaledField will be + * constructed by using the ~ operator on an SField. + * @return Transparent proxy object to an `optional` holding a modifiable + * reference to the value of the specified field. Returns + * std::nullopt if the field is not present. + */ template OptionalProxy at(OptionaledField const& of); - /** Set a field. - if the field already exists, it is replaced. - */ + /** + * Set a field. + * if the field already exists, it is replaced. + */ void set(std::unique_ptr v); @@ -478,7 +506,7 @@ template class STObject::Proxy { public: - using value_type = typename T::value_type; + using value_type = T::value_type; [[nodiscard]] value_type value() const; @@ -486,8 +514,10 @@ public: value_type operator*() const; - /// Do not use operator->() unless the field is required, or you've checked - /// that it's set. + /** + * Do not use operator->() unless the field is required, or you've checked + * that it's set. + */ T const* operator->() const; @@ -513,13 +543,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 +561,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 +608,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>; @@ -585,17 +617,20 @@ public: OptionalProxy& operator=(OptionalProxy const&) = delete; - /** Returns `true` if the field is set. - - Fields with soeDEFAULT and set to the - default value will return `true` - */ + /** + * Returns `true` if the field is set. + * + * Fields with soeDEFAULT and set to the + * default value will return `true` + */ explicit operator bool() const noexcept; operator optional_type() const; - /** Explicit conversion to std::optional */ + /** + * Explicit conversion to std::optional + */ optional_type operator~() const; @@ -677,8 +712,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; @@ -741,8 +777,10 @@ STObject::Proxy::operator*() const -> value_type return this->value(); } -/// Do not use operator->() unless the field is required, or you've checked that -/// it's set. +/** + * Do not use operator->() unless the field is required, or you've checked that + * it's set. + */ template T const* STObject::Proxy::operator->() const @@ -784,8 +822,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 +880,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 +928,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 +974,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 +1081,7 @@ STObject::getPIndex(int offset) } template -typename T::value_type +T::value_type STObject::operator[](TypedField const& f) const { return at(f); @@ -1068,7 +1109,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 +1268,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..7189e0ec89 100644 --- a/include/xrpl/protocol/STParsedJSON.h +++ b/include/xrpl/protocol/STParsedJSON.h @@ -1,31 +1,41 @@ #pragma once -#include +#include +#include +#include #include +#include namespace xrpl { -/** Maximum JSON object nesting depth permitted during parsing. */ +/** + * Maximum JSON object nesting depth permitted during parsing. + */ inline constexpr std::size_t kMaxParsedJsonDepth = 64; -/** Maximum number of elements permitted in any JSON array field during parsing. - Requests exceeding this limit are rejected with an invalidParams error. */ +/** + * Maximum number of elements permitted in any JSON array field during parsing. + * Requests exceeding this limit are rejected with an invalidParams error. + */ inline constexpr std::size_t kMaxParsedJsonArraySize = 512; -/** Holds the serialized result of parsing an input JSON object. - This does validation and checking on the provided JSON. -*/ +/** + * Holds the serialized result of parsing an input JSON object. + * This does validation and checking on the provided JSON. + */ class STParsedJSONObject { public: - /** Parses and creates an STParsedJSON object. - The result of the parsing is stored in object and error. - Exceptions: - Does not throw. - @param name The name of the JSON field, used in diagnostics. - @param json The JSON-RPC to parse. - */ + /** + * Parses and creates an STParsedJSON object. + * The result of the parsing is stored in object and error. + * + * @note Does not throw. + * + * @param name The name of the JSON field, used in diagnostics. + * @param json The JSON-RPC to parse. + */ STParsedJSONObject(std::string const& name, json::Value const& json); STParsedJSONObject() = delete; @@ -34,10 +44,14 @@ public: operator=(STParsedJSONObject const&) = delete; ~STParsedJSONObject() = default; - /** The STObject if the parse was successful. */ + /** + * The STObject if the parse was successful. + */ std::optional object; - /** On failure, an appropriate set of error values. */ + /** + * On failure, an appropriate set of error values. + */ json::Value error; }; diff --git a/include/xrpl/protocol/STPathSet.h b/include/xrpl/protocol/STPathSet.h index 1508dcb727..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 { @@ -237,6 +240,9 @@ private: inline STPathElement::STPathElement() : type_(TypeNone), isOffer_(true) { + // 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); } @@ -312,6 +318,9 @@ inline STPathElement::STPathElement( assetID_.visit( [&](Currency const&) { type_ = type_ & (~Type::TypeMpt); }, [&](MPTID const&) { type_ = type_ & (~Type::TypeCurrency); }); + // hashValue_ must be computed after type_ is adjusted above, so this cannot + // be a member initializer. + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) hashValue_ = getHash(*this); } diff --git a/include/xrpl/protocol/STTakesAsset.h b/include/xrpl/protocol/STTakesAsset.h index bf75ffccf7..95667e4868 100644 --- a/include/xrpl/protocol/STTakesAsset.h +++ b/include/xrpl/protocol/STTakesAsset.h @@ -3,9 +3,12 @@ #include #include +#include + namespace xrpl { -/** Intermediate class for any STBase-derived class to store an Asset. +/** + * Intermediate class for any STBase-derived class to store an Asset. * * In the class definition, this class should be specified as a base class * _instead_ of STBase. @@ -39,7 +42,8 @@ STTakesAsset::associateAsset(Asset const& a) class STLedgerEntry; -/** Associate an Asset with all sMD_NeedsAsset fields in a ledger entry. +/** + * Associate an Asset with all sMD_NeedsAsset fields in a ledger entry. * * This function iterates over all fields in the given ledger entry. For each * field that is set and has the SField::sMD_NeedsAsset metadata flag, it calls @@ -52,7 +56,6 @@ class STLedgerEntry; * * @param sle The ledger entry whose fields will be updated. * @param asset The Asset to associate with the relevant fields. - * */ void associateAsset(STLedgerEntry& sle, Asset const& asset); diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index 659fede31d..989bd11c10 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 namespace xrpl { @@ -42,60 +55,60 @@ public: explicit STTx(SerialIter&& sit); explicit STTx(STObject&& object); - /** Constructs a transaction. - - The returned transaction will have the specified type and - any fields that the callback function adds to the object - that's passed in. - */ + /** + * Constructs a transaction. + * + * The returned transaction will have the specified type and + * any fields that the callback function adds to the object + * that's passed in. + */ 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 + /** + * Returns the first non-zero value of (Sequence, TicketSequence). + */ + [[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 @@ -104,78 +117,89 @@ public: SecretKey const& secretKey, std::optional> signatureTarget = {}); - /** Check the signature. - @param rules The current ledger rules. - @return `true` if valid signature. If invalid, the error message string. - */ - std::expected + /** + * Check the signature. + * @param rules The current ledger rules. + * @return `true` if valid signature. If invalid, the error message string. + */ + [[nodiscard]] std::expected checkSign(Rules const& rules) const; - std::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; + [[nodiscard]] AccountID + getFeePayerID() const; + private: - /** Check the signature. - @param rules The current ledger rules. - @param sigObject Reference to object that contains the signature fields. - Will be *this more often than not. - @return `true` if valid signature. If invalid, the error message string. - */ - std::expected + /** + * Check the signature. + * @param rules The current ledger rules. + * @param sigObject Reference to object that contains the signature fields. + * Will be *this more often than not. + * @return `true` if valid signature. If invalid, the error message string. + */ + [[nodiscard]] std::expected checkSign(Rules const& rules, STObject const& sigObject) const; - std::expected + [[nodiscard]] std::expected checkSingleSign(STObject const& sigObject) const; - std::expected + [[nodiscard]] std::expected checkMultiSign(Rules const& rules, STObject const& sigObject) const; - std::expected + [[nodiscard]] std::expected checkBatchSingleSign(STObject const& batchSigner) const; - std::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 passesLocalChecks(STObject const& st, std::string&); -/** Sterilize a transaction. - - The transaction is serialized and then deserialized, - ensuring that all equivalent transactions are in canonical - form. This also ensures that program metadata such as - the transaction's digest, are all computed. -*/ +/** + * Sterilize a transaction. + * + * The transaction is serialized and then deserialized, + * ensuring that all equivalent transactions are in canonical + * form. This also ensures that program metadata such as + * the transaction's digest, are all computed. + */ std::shared_ptr sterilize(STTx const& stx); -/** Check whether a transaction is a pseudo-transaction */ +/** + * Check whether a transaction is a pseudo-transaction + */ bool isPseudoTx(STObject const& tx); diff --git a/include/xrpl/protocol/STValidation.h b/include/xrpl/protocol/STValidation.h index 91ce88b441..444fdfa600 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 { @@ -39,30 +54,32 @@ class STValidation final : public STObject, public CountedObject NetClock::time_point seenTime_; public: - /** Construct a STValidation from a peer from serialized data. - - @param sit Iterator over serialized data - @param lookupNodeID Invocable with signature - NodeID(PublicKey const&) - used to find the Node ID based on the public key - that signed the validation. For manifest based - validators, this should be the NodeID of the master - public key. - @param checkSignature Whether to verify the data was signed properly - - @note Throws if the object is not valid - */ + /** + * Construct a STValidation from a peer from serialized data. + * + * @param sit Iterator over serialized data + * @param lookupNodeID Invocable with signature + * NodeID(PublicKey const&) + * used to find the Node ID based on the public key + * that signed the validation. For manifest based + * validators, this should be the NodeID of the master + * public key. + * @param checkSignature Whether to verify the data was signed properly + * + * @note Throws if the object is not valid + */ template STValidation(SerialIter& sit, LookupNodeID&& lookupNodeID, bool checkSignature); - /** Construct, sign and trust a new STValidation issued by this node. - - @param signTime When the validation is signed - @param publicKey The current signing public key - @param secretKey The current signing secret key - @param nodeID ID corresponding to node's public master key - @param f callback function to "fill" the validation with necessary data - */ + /** + * Construct, sign and trust a new STValidation issued by this node. + * + * @param signTime When the validation is signed + * @param publicKey The current signing public key + * @param secretKey The current signing secret key + * @param nodeID ID corresponding to node's public master key + * @param f callback function to "fill" the validation with necessary data + */ template STValidation( NetClock::time_point signTime, @@ -72,35 +89,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 +129,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; @@ -168,14 +185,15 @@ STValidation::STValidation(SerialIter& sit, LookupNodeID&& lookupNodeID, bool ch XRPL_ASSERT(nodeID_.isNonZero(), "xrpl::STValidation::STValidation(SerialIter) : nonzero node"); } -/** Construct, sign and trust a new STValidation issued by this node. - - @param signTime When the validation is signed - @param publicKey The current signing public key - @param secretKey The current signing secret key - @param nodeID ID corresponding to node's public master key - @param f callback function to "fill" the validation with necessary data -*/ +/** + * Construct, sign and trust a new STValidation issued by this node. + * + * @param signTime When the validation is signed + * @param publicKey The current signing public key + * @param secretKey The current signing secret key + * @param nodeID ID corresponding to node's public master key + * @param f callback function to "fill" the validation with necessary data + */ template STValidation::STValidation( NetClock::time_point signTime, diff --git a/include/xrpl/protocol/STVector256.h b/include/xrpl/protocol/STVector256.h index 5c454b6be0..5a8418fef5 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 { @@ -44,7 +50,9 @@ public: void setValue(STVector256 const& v); - /** Retrieve a copy of the vector we contain */ + /** + * Retrieve a copy of the vector we contain + */ explicit operator std::vector() const; @@ -132,7 +140,9 @@ STVector256::setValue(STVector256 const& v) value_ = v.value_; } -/** Retrieve a copy of the vector we contain */ +/** + * Retrieve a copy of the vector we contain + */ inline STVector256:: operator std::vector() const { 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..6d353acac0 100644 --- a/include/xrpl/protocol/SecretKey.h +++ b/include/xrpl/protocol/SecretKey.h @@ -2,18 +2,24 @@ #include #include +#include #include #include #include #include #include +#include #include +#include #include +#include namespace xrpl { -/** A secret key. */ +/** + * A secret key. + */ class SecretKey { public: @@ -52,11 +58,12 @@ public: return sizeof(buf_); } - /** Convert the secret key to a hexadecimal string. - - @note The operator<< function is deliberately omitted - to avoid accidental exposure of secret key material. - */ + /** + * Convert the secret key to a hexadecimal string. + * + * @note The operator<< function is deliberately omitted + * to avoid accidental exposure of secret key material. + */ [[nodiscard]] std::string toString() const; @@ -93,7 +100,9 @@ operator!=(SecretKey const& lhs, SecretKey const& rhs) = delete; //------------------------------------------------------------------------------ -/** Parse a secret key */ +/** + * Parse a secret key + */ template <> std::optional parseBase58(TokenType type, std::string const& s); @@ -104,38 +113,48 @@ toBase58(TokenType type, SecretKey const& sk) return encodeBase58Token(type, sk.data(), sk.size()); } -/** Create a secret key using secure random numbers. */ +/** + * Create a secret key using secure random numbers. + */ SecretKey randomSecretKey(); -/** Generate a new secret key deterministically. */ +/** + * Generate a new secret key deterministically. + */ SecretKey generateSecretKey(KeyType type, Seed const& seed); -/** Derive the public key from a secret key. */ +/** + * Derive the public key from a secret key. + */ PublicKey derivePublicKey(KeyType type, SecretKey const& sk); -/** Generate a key pair deterministically. - - This algorithm is specific to the XRPL: - - For secp256k1 key pairs, the seed is converted - to a Generator and used to compute the key pair - corresponding to ordinal 0 for the generator. -*/ +/** + * Generate a key pair deterministically. + * + * This algorithm is specific to the XRPL: + * + * For secp256k1 key pairs, the seed is converted + * to a Generator and used to compute the key pair + * corresponding to ordinal 0 for the generator. + */ std::pair generateKeyPair(KeyType type, Seed const& seed); -/** Create a key pair using secure random numbers. */ +/** + * Create a key pair using secure random numbers. + */ std::pair randomKeyPair(KeyType type); -/** Generate a signature for a message digest. - This can only be used with secp256k1 since Ed25519's - security properties come, in part, from how the message - is hashed. -*/ +/** + * Generate a signature for a message digest. + * This can only be used with secp256k1 since Ed25519's + * security properties come, in part, from how the message + * is hashed. + */ /** @{ */ Buffer signDigest(PublicKey const& pk, SecretKey const& sk, uint256 const& digest); @@ -147,10 +166,11 @@ signDigest(KeyType type, SecretKey const& sk, uint256 const& digest) } /** @} */ -/** Generate a signature for a message. - With secp256k1 signatures, the data is first hashed with - SHA512-Half, and the resulting digest is signed. -*/ +/** + * Generate a signature for a message. + * With secp256k1 signatures, the data is first hashed with + * SHA512-Half, and the resulting digest is signed. + */ /** @{ */ Buffer sign(PublicKey const& pk, SecretKey const& sk, Slice const& message); diff --git a/include/xrpl/protocol/Seed.h b/include/xrpl/protocol/Seed.h index 0b93b84516..4ccdd6707f 100644 --- a/include/xrpl/protocol/Seed.h +++ b/include/xrpl/protocol/Seed.h @@ -5,11 +5,16 @@ #include #include +#include +#include #include +#include namespace xrpl { -/** Seeds are used to generate deterministic secret keys. */ +/** + * Seeds are used to generate deterministic secret keys. + */ class Seed { private: @@ -24,12 +29,15 @@ public: Seed& operator=(Seed const&) = default; - /** Destroy the seed. - The buffer will first be securely erased. - */ + /** + * Destroy the seed. + * The buffer will first be securely erased. + */ ~Seed(); - /** Construct a seed */ + /** + * Construct a seed + */ /** @{ */ explicit Seed(Slice const& slice); explicit Seed(uint128 const& seed); @@ -74,42 +82,52 @@ public: //------------------------------------------------------------------------------ -/** Create a seed using secure random numbers. */ +/** + * Create a seed using secure random numbers. + */ Seed randomSeed(); -/** Generate a seed deterministically. - - The algorithm is specific to the XRPL: - - The seed is calculated as the first 128 bits - of the SHA512-Half of the string text excluding - any terminating null. - - @note This will not attempt to determine the format of - the string (e.g. hex or base58). -*/ +/** + * Generate a seed deterministically. + * + * The algorithm is specific to the XRPL: + * + * The seed is calculated as the first 128 bits + * of the SHA512-Half of the string text excluding + * any terminating null. + * + * @note This will not attempt to determine the format of + * the string (e.g. hex or base58). + */ Seed generateSeed(std::string const& passPhrase); -/** Parse a Base58 encoded string into a seed */ +/** + * Parse a Base58 encoded string into a seed + */ template <> std::optional parseBase58(std::string const& s); -/** Attempt to parse a string as a seed. - - @param str the string to parse - @param rfc1751 true if we should attempt RFC1751 style parsing (deprecated) - * */ +/** + * Attempt to parse a string as a seed. + * + * @param str the string to parse + * @param rfc1751 true if we should attempt RFC1751 style parsing (deprecated) + */ std::optional parseGenericSeed(std::string const& str, bool rfc1751 = true); -/** Encode a Seed in RFC1751 format */ +/** + * Encode a Seed in RFC1751 format + */ std::string seedAs1751(Seed const& seed); -/** Format a seed as a Base58 string */ +/** + * Format a seed as a Base58 string + */ inline std::string toBase58(Seed const& seed) { diff --git a/include/xrpl/protocol/SeqProxy.h b/include/xrpl/protocol/SeqProxy.h index be040cceec..e6a97be0e7 100644 --- a/include/xrpl/protocol/SeqProxy.h +++ b/include/xrpl/protocol/SeqProxy.h @@ -5,33 +5,34 @@ namespace xrpl { -/** A type that represents either a sequence value or a ticket value. - - We use the value() of a SeqProxy in places where a sequence was used - before. An example of this is the sequence of an Offer stored in the - ledger. We do the same thing with the in-ledger identifier of a - Check, Payment Channel, and Escrow. - - Why is this safe? If we use the SeqProxy::value(), how do we know that - each ledger entry will be unique? - - There are two components that make this safe: - - 1. A "TicketCreate" transaction carefully avoids creating a ticket - that corresponds with an already used Sequence or Ticket value. - The transactor does this by referring to the account root's - sequence number. Creating the ticket advances the account root's - sequence number so the same ticket (or sequence) value cannot be - used again. - - 2. When a "TicketCreate" transaction creates a batch of tickets it advances - the account root sequence to one past the largest created ticket. - - Therefore all tickets in a batch other than the first may never have - the same value as a sequence on that same account. And since a ticket - may only be used once there will never be any duplicates within this - account. -*/ +/** + * A type that represents either a sequence value or a ticket value. + * + * We use the value() of a SeqProxy in places where a sequence was used + * before. An example of this is the sequence of an Offer stored in the + * ledger. We do the same thing with the in-ledger identifier of a + * Check, Payment Channel, and Escrow. + * + * Why is this safe? If we use the SeqProxy::value(), how do we know that + * each ledger entry will be unique? + * + * There are two components that make this safe: + * + * 1. A "TicketCreate" transaction carefully avoids creating a ticket + * that corresponds with an already used Sequence or Ticket value. + * The transactor does this by referring to the account root's + * sequence number. Creating the ticket advances the account root's + * sequence number so the same ticket (or sequence) value cannot be + * used again. + * + * 2. When a "TicketCreate" transaction creates a batch of tickets it advances + * the account root sequence to one past the largest created ticket. + * + * Therefore all tickets in a batch other than the first may never have + * the same value as a sequence on that same account. And since a ticket + * may only be used once there will never be any duplicates within this + * account. + */ class SeqProxy { public: @@ -51,7 +52,9 @@ public: SeqProxy& operator=(SeqProxy const& other) = default; - /** Factory function to return a sequence-based SeqProxy */ + /** + * Factory function to return a sequence-based SeqProxy + */ static constexpr SeqProxy sequence(std::uint32_t v) { 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..fad2c35c9e 100644 --- a/include/xrpl/protocol/Sign.h +++ b/include/xrpl/protocol/Sign.h @@ -1,23 +1,28 @@ #pragma once +#include #include +#include #include +#include #include #include +#include namespace xrpl { -/** Sign an STObject - - @param st Object to sign - @param prefix Prefix to insert before serialized object when hashing - @param type Signing key type used to derive public key - @param sk Signing secret key - @param sigField Field in which to store the signature on the object. - If not specified the value defaults to `sfSignature`. - - @note If a signature already exists, it is overwritten. -*/ +/** + * Sign an STObject + * + * @param st Object to sign + * @param prefix Prefix to insert before serialized object when hashing + * @param type Signing key type used to derive public key + * @param sk Signing secret key + * @param sigField Field in which to store the signature on the object. + * If not specified the value defaults to `sfSignature`. + * + * @note If a signature already exists, it is overwritten. + */ void sign( STObject& st, @@ -26,14 +31,15 @@ sign( SecretKey const& sk, SF_VL const& sigField = sfSignature); -/** Returns `true` if STObject contains valid signature - - @param st Signed object - @param prefix Prefix inserted before serialized object when hashing - @param pk Public key for verifying signature - @param sigField Object's field containing the signature. - If not specified the value defaults to `sfSignature`. -*/ +/** + * Returns `true` if STObject contains valid signature + * + * @param st Signed object + * @param prefix Prefix inserted before serialized object when hashing + * @param pk Public key for verifying signature + * @param sigField Object's field containing the signature. + * If not specified the value defaults to `sfSignature`. + */ bool verify( STObject const& st, @@ -41,22 +47,25 @@ verify( PublicKey const& pk, SF_VL const& sigField = sfSignature); -/** Return a Serializer suitable for computing a multisigning TxnSignature. */ +/** + * Return a Serializer suitable for computing a multisigning TxnSignature. + */ Serializer buildMultiSigningData(STObject const& obj, AccountID const& signingID); -/** Break the multi-signing hash computation into 2 parts for optimization. - - We can optimize verifying multiple multisignatures by splitting the - data building into two parts; - o A large part that is shared by all of the computations. - o A small part that is unique to each signer in the multisignature. - - The following methods support that optimization: - 1. startMultiSigningData provides the large part which can be shared. - 2. finishMultiSigningData caps the passed in serializer with each - signer's unique data. -*/ +/** + * Break the multi-signing hash computation into 2 parts for optimization. + * + * We can optimize verifying multiple multisignatures by splitting the + * data building into two parts; + * o A large part that is shared by all of the computations. + * o A small part that is unique to each signer in the multisignature. + * + * The following methods support that optimization: + * 1. startMultiSigningData provides the large part which can be shared. + * 2. finishMultiSigningData caps the passed in serializer with each + * signer's unique data. + */ Serializer startMultiSigningData(STObject const& obj); diff --git a/include/xrpl/protocol/SystemParameters.h b/include/xrpl/protocol/SystemParameters.h index 1cc35a0f31..6ca36c8d9a 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 { @@ -18,22 +21,30 @@ systemName() return kName; } -/** Configure the native currency. */ +/** + * Configure the native currency. + */ -/** Number of drops in the genesis account. */ +/** + * Number of drops in the genesis account. + */ constexpr XRPAmount kInitialXrp{100'000'000'000 * kDropsPerXrp}; static_assert(kInitialXrp.drops() == 100'000'000'000'000'000); static_assert(Number::kMaxRep >= kInitialXrp.drops()); -/** Returns true if the amount does not exceed the initial XRP in existence. */ +/** + * Returns true if the amount does not exceed the initial XRP in existence. + */ inline bool isLegalAmount(XRPAmount const& amount) { return amount <= kInitialXrp; } -/** Returns true if the absolute value of the amount does not exceed the initial - * XRP in existence. */ +/** + * Returns true if the absolute value of the amount does not exceed the initial + * XRP in existence. + */ inline bool isLegalAmountSigned(XRPAmount const& amount) { @@ -48,20 +59,30 @@ systemCurrencyCode() return kCode; } -/** The XRP ledger network's earliest allowed sequence */ +/** + * The XRP ledger network's earliest allowed sequence + */ static constexpr std::uint32_t kXrpLedgerEarliestSeq{32570u}; -/** The XRP Ledger mainnet's earliest ledger with a FeeSettings object. Only - * used in asserts and tests. */ +/** + * The XRP Ledger mainnet's earliest ledger with a FeeSettings object. Only + * used in asserts and tests. + */ static constexpr std::uint32_t kXrpLedgerEarliestFees{562177u}; -/** The minimum amount of support an amendment should have. */ +/** + * The minimum amount of support an amendment should have. + */ constexpr std::ratio<80, 100> kAmendmentMajorityCalcThreshold; -/** The minimum amount of time an amendment must hold a majority */ +/** + * The minimum amount of time an amendment must hold a majority + */ constexpr std::chrono::seconds const kDefaultAmendmentMajorityTime = weeks{2}; } // namespace xrpl -/** Default peer port (IANA registered) */ +/** + * Default peer port (IANA registered) + */ inline constexpr std::uint16_t kDefaultPeerPort{2459}; diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index c89610f354..730d021254 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, }; //------------------------------------------------------------------------------ @@ -220,6 +225,7 @@ enum TERcodes : TERUnderlyingType { // create a pseudo-account terNO_DELEGATE_PERMISSION, // Delegate does not have permission terLOCKED, // MPT is locked + terNO_PERMISSION, // No permission but retry }; //------------------------------------------------------------------------------ @@ -358,6 +364,12 @@ 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, + tecNO_SPONSOR_PERMISSION = 200, }; //------------------------------------------------------------------------------ @@ -401,7 +413,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