mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-12 22:58:11 +00:00
Compare commits
58 Commits
mvadari/re
...
bthomee/rp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce1b80d7b9 | ||
|
|
2e776af602 | ||
|
|
6356023cce | ||
|
|
7f794a0ef8 | ||
|
|
c4ab814ebd | ||
|
|
f0d5248174 | ||
|
|
3ee5bad8d6 | ||
|
|
96d8be6d83 | ||
|
|
373e1dc4c2 | ||
|
|
e2ce3de0fc | ||
|
|
cf19602641 | ||
|
|
fdcf2992d0 | ||
|
|
44a5da0632 | ||
|
|
db3764b231 | ||
|
|
5795eb3be2 | ||
|
|
8eae0c338e | ||
|
|
f0d66e2da7 | ||
|
|
212621c9a0 | ||
|
|
8e24943093 | ||
|
|
6d6ab2d067 | ||
|
|
00e6407514 | ||
|
|
00c06edffb | ||
|
|
de5053ae0d | ||
|
|
1940ec5c2a | ||
|
|
19c94c73f4 | ||
|
|
cd005ff60d | ||
|
|
578224f2e6 | ||
|
|
3857ce21cd | ||
|
|
aa490df46b | ||
|
|
88c1f1e7ac | ||
|
|
ede8af8191 | ||
|
|
3c24b605a1 | ||
|
|
d343b542f1 | ||
|
|
7acd719bf7 | ||
|
|
b4564d5301 | ||
|
|
60195e6d37 | ||
|
|
3d526d456e | ||
|
|
f05c9f7913 | ||
|
|
c2a4bc3aa1 | ||
|
|
718185e3b2 | ||
|
|
1d7783bb86 | ||
|
|
9fd2c552f5 | ||
|
|
70b8fd301b | ||
|
|
e6564f553d | ||
|
|
ed96e60ce3 | ||
|
|
2ebd745a1b | ||
|
|
cfcbe45b60 | ||
|
|
9cbf78ba99 | ||
|
|
b8d8738f81 | ||
|
|
63c97e719f | ||
|
|
3ac26f23c7 | ||
|
|
ff9410bc56 | ||
|
|
1798262d7a | ||
|
|
cd633b9ac9 | ||
|
|
5c5f7d315a | ||
|
|
0f7493ce50 | ||
|
|
40f61f828a | ||
|
|
108277f4b7 |
16
.clang-tidy
16
.clang-tidy
@@ -5,7 +5,10 @@ Checks: "-*,
|
||||
-bugprone-exception-escape,
|
||||
-bugprone-implicit-widening-of-multiplication-result,
|
||||
-bugprone-narrowing-conversions,
|
||||
-bugprone-signed-bitwise,
|
||||
-bugprone-std-exception-baseclass,
|
||||
-bugprone-throwing-static-initialization,
|
||||
-bugprone-unhandled-code-paths,
|
||||
|
||||
cppcoreguidelines-*,
|
||||
-cppcoreguidelines-avoid-c-arrays,
|
||||
@@ -14,6 +17,7 @@ Checks: "-*,
|
||||
-cppcoreguidelines-avoid-magic-numbers,
|
||||
-cppcoreguidelines-avoid-non-const-global-variables,
|
||||
-cppcoreguidelines-c-copy-assignment-signature,
|
||||
-cppcoreguidelines-explicit-constructor,
|
||||
-cppcoreguidelines-interfaces-global-init,
|
||||
-cppcoreguidelines-macro-usage,
|
||||
-cppcoreguidelines-missing-std-forward,
|
||||
@@ -32,6 +36,7 @@ Checks: "-*,
|
||||
llvm-namespace-comment,
|
||||
|
||||
misc-*,
|
||||
-misc-explicit-constructor,
|
||||
-misc-multiple-inheritance,
|
||||
-misc-no-recursion,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
@@ -45,6 +50,8 @@ Checks: "-*,
|
||||
-modernize-avoid-c-style-cast,
|
||||
-modernize-return-braced-init-list,
|
||||
-modernize-use-integer-sign-comparison,
|
||||
-modernize-use-string-view,
|
||||
-modernize-use-structured-binding,
|
||||
-modernize-use-trailing-return-type,
|
||||
|
||||
performance-*,
|
||||
@@ -53,6 +60,7 @@ Checks: "-*,
|
||||
-performance-noexcept-move-constructor,
|
||||
-performance-unnecessary-copy-initialization,
|
||||
-performance-unnecessary-value-param,
|
||||
-performance-use-std-move,
|
||||
|
||||
readability-*,
|
||||
-readability-avoid-const-params-in-decls,
|
||||
@@ -65,7 +73,11 @@ Checks: "-*,
|
||||
-readability-named-parameter,
|
||||
-readability-qualified-auto,
|
||||
-readability-redundant-access-specifiers,
|
||||
-readability-redundant-nested-if,
|
||||
-readability-redundant-qualified-alias,
|
||||
-readability-static-accessed-through-instance,
|
||||
-readability-trailing-comma,
|
||||
-readability-trivial-switch,
|
||||
-readability-uppercase-literal-suffix
|
||||
"
|
||||
# ---
|
||||
@@ -81,6 +93,10 @@ CheckOptions:
|
||||
bugprone-unsafe-functions.ReportMoreUnsafeFunctions: true
|
||||
bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc
|
||||
|
||||
# New in clang-tidy 23; disabled until the code is updated
|
||||
misc-const-correctness.AnalyzeAutoVariables: false
|
||||
misc-const-correctness.AnalyzeLambdas: false
|
||||
misc-const-correctness.AnalyzeParameters: false
|
||||
misc-include-cleaner.IgnoreHeaders: ".*/(detail|impl)/.*;.*fwd\\.h(pp)?;time.h;stdlib.h;sqlite3.h;netinet/in\\.h;sys/resource\\.h;sys/sysinfo\\.h;linux/sysinfo\\.h;__chrono/.*;bits/.*;_abort\\.h;boost/.*;openssl/obj_mac\\.h"
|
||||
|
||||
readability-braces-around-statements.ShortStatementLines: 2
|
||||
|
||||
@@ -108,3 +108,75 @@ endfunction()
|
||||
|
||||
function(patch_nix_binary target)
|
||||
endfunction()
|
||||
|
||||
function(rpcspec_generate_instantiations)
|
||||
set(options)
|
||||
set(oneValueArgs OUT_VAR VALUE_TYPE VIEW_HEADER INCLUDE_DIR)
|
||||
set(multiValueArgs HANDLERS)
|
||||
cmake_parse_arguments(
|
||||
THIS_FUNCTION_PREFIX
|
||||
"${options}"
|
||||
"${oneValueArgs}"
|
||||
"${multiValueArgs}"
|
||||
${ARGN}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(corrosion_import_crate)
|
||||
set(options
|
||||
ALL_FEATURES
|
||||
NO_DEFAULT_FEATURES
|
||||
NO_STD
|
||||
NO_LINKER_OVERRIDE
|
||||
NO_USES_TERMINAL
|
||||
LOCKED
|
||||
FROZEN
|
||||
)
|
||||
set(oneValueArgs MANIFEST_PATH PROFILE IMPORTED_CRATES)
|
||||
set(multiValueArgs
|
||||
CRATE_TYPES
|
||||
CRATES
|
||||
FEATURES
|
||||
FLAGS
|
||||
OVERRIDE_CRATE_TYPE
|
||||
)
|
||||
cmake_parse_arguments(
|
||||
THIS_FUNCTION_PREFIX
|
||||
"${options}"
|
||||
"${oneValueArgs}"
|
||||
"${multiValueArgs}"
|
||||
${ARGN}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(corrosion_set_env_vars target_name env_var)
|
||||
endfunction()
|
||||
|
||||
function(corrosion_add_cxxbridge cxx_target)
|
||||
set(options)
|
||||
set(oneValueArgs CRATE)
|
||||
set(multiValueArgs FILES)
|
||||
cmake_parse_arguments(
|
||||
THIS_FUNCTION_PREFIX
|
||||
"${options}"
|
||||
"${oneValueArgs}"
|
||||
"${multiValueArgs}"
|
||||
${ARGN}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(_unlink_libgcc_s crate)
|
||||
endfunction()
|
||||
|
||||
function(add_xrpl_crate name)
|
||||
set(options)
|
||||
set(oneValueArgs CRATE)
|
||||
set(multiValueArgs FILES)
|
||||
cmake_parse_arguments(
|
||||
THIS_FUNCTION_PREFIX
|
||||
"${options}"
|
||||
"${oneValueArgs}"
|
||||
"${multiValueArgs}"
|
||||
${ARGN}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
27
.github/actions/build-deps/action.yml
vendored
27
.github/actions/build-deps/action.yml
vendored
@@ -15,9 +15,9 @@ inputs:
|
||||
required: false
|
||||
default: "false"
|
||||
log_verbosity:
|
||||
description: "The logging verbosity."
|
||||
description: 'The logging verbosity ("quiet", "verbose"), or empty to use the Conan defaults.'
|
||||
required: false
|
||||
default: "verbose"
|
||||
default: ""
|
||||
sanitizers:
|
||||
description: "The sanitizers to enable."
|
||||
required: false
|
||||
@@ -35,6 +35,16 @@ runs:
|
||||
LOG_VERBOSITY: ${{ inputs.log_verbosity }}
|
||||
SANITIZERS: ${{ inputs.sanitizers }}
|
||||
run: |
|
||||
# By default, leave the verbosity unset, so CMake configure output is
|
||||
# shown, but compile commands and Boost's b2 debug output (~85k lines
|
||||
# when "verbose") are not.
|
||||
VERBOSITY_ARGS=()
|
||||
if [[ -n "${LOG_VERBOSITY}" ]]; then
|
||||
VERBOSITY_ARGS=(
|
||||
--conf:all tools.build:verbosity="${LOG_VERBOSITY}"
|
||||
--conf:all tools.compilation:verbosity="${LOG_VERBOSITY}"
|
||||
)
|
||||
fi
|
||||
conan install \
|
||||
--profile:all ci \
|
||||
--build="${BUILD_OPTION}" \
|
||||
@@ -42,6 +52,13 @@ runs:
|
||||
--options:host='&:xrpld=True' \
|
||||
--settings:all build_type="${BUILD_TYPE}" \
|
||||
--conf:all tools.build:jobs=${BUILD_NPROC} \
|
||||
--conf:all tools.build:verbosity="${LOG_VERBOSITY}" \
|
||||
--conf:all tools.compilation:verbosity="${LOG_VERBOSITY}" \
|
||||
.
|
||||
"${VERBOSITY_ARGS[@]}" \
|
||||
--format=json \
|
||||
. >"${RUNNER_TEMP}/conan-graph.json"
|
||||
|
||||
# Tools that run during the build may only load glibc from the Nix store,
|
||||
# as their package ID survives a GCC runtime update.
|
||||
- name: Check build-context packages for Nix store dependencies (Linux)
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
shell: bash
|
||||
run: ./bin/nix/check-build-context-runtime.sh "${RUNNER_TEMP}/conan-graph.json"
|
||||
|
||||
@@ -174,8 +174,10 @@ test.server > xrpl.basics
|
||||
test.server > xrpl.config
|
||||
test.server > xrpld.app
|
||||
test.server > xrpld.core
|
||||
test.server > xrpld.rpc
|
||||
test.server > xrpl.json
|
||||
test.server > xrpl.protocol
|
||||
test.server > xrpl.resource
|
||||
test.server > xrpl.server
|
||||
test.unit_test > xrpl.basics
|
||||
test.unit_test > xrpl.protocol
|
||||
|
||||
8
.github/scripts/strategy-matrix/linux.json
vendored
8
.github/scripts/strategy-matrix/linux.json
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"image_tag": "sha-060957e",
|
||||
"image_tag": "sha-3d526d4",
|
||||
"configs": {
|
||||
"ubuntu": [
|
||||
{
|
||||
@@ -74,7 +74,7 @@
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON",
|
||||
"package": {
|
||||
"type": "deb",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-3a2d19f"
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-e6055dd"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -86,7 +86,7 @@
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON -Dassert=ON",
|
||||
"package": {
|
||||
"type": "deb",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-3a2d19f",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-e6055dd",
|
||||
"variant": "assert"
|
||||
}
|
||||
}
|
||||
@@ -101,7 +101,7 @@
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON",
|
||||
"package": {
|
||||
"type": "rpm",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-3a2d19f"
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-e6055dd"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
7
.github/workflows/build-nix-images.yml
vendored
7
.github/workflows/build-nix-images.yml
vendored
@@ -12,8 +12,7 @@ on:
|
||||
- "!nix/docker/README.md"
|
||||
- "!nix/devshell.nix"
|
||||
- "!nix/check-tools/**"
|
||||
- "bin/default-loader-path.sh"
|
||||
- "bin/install-sanitizer-libs.sh"
|
||||
- "bin/nix/default-loader-path.sh"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/build-nix-images.yml"
|
||||
@@ -25,8 +24,8 @@ on:
|
||||
- "!nix/devshell.nix"
|
||||
- "!nix/check-tools/**"
|
||||
- "bin/check-tools.sh"
|
||||
- "bin/default-loader-path.sh"
|
||||
- "bin/install-sanitizer-libs.sh"
|
||||
- "bin/nix/default-loader-path.sh"
|
||||
- "bin/install/sanitizer-libs.sh"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
|
||||
5
.github/workflows/build-packaging-images.yml
vendored
5
.github/workflows/build-packaging-images.yml
vendored
@@ -5,13 +5,12 @@ on:
|
||||
branches:
|
||||
- develop
|
||||
paths:
|
||||
- ".github/workflows/build-packaging-images.yml"
|
||||
- "bin/install-packaging-tools.sh"
|
||||
- "bin/install/packaging-tools.sh"
|
||||
- "package/images/packaging/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/build-packaging-images.yml"
|
||||
- "bin/install-packaging-tools.sh"
|
||||
- "bin/install/packaging-tools.sh"
|
||||
- "package/images/packaging/**"
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
1
.github/workflows/build-pre-commit-image.yml
vendored
1
.github/workflows/build-pre-commit-image.yml
vendored
@@ -5,7 +5,6 @@ on:
|
||||
branches:
|
||||
- develop
|
||||
paths:
|
||||
- ".github/workflows/build-pre-commit-image.yml"
|
||||
- "bin/pre-commit/Dockerfile"
|
||||
- "rust-toolchain.toml"
|
||||
pull_request:
|
||||
|
||||
2
.github/workflows/cargo-audit.yml
vendored
2
.github/workflows/cargo-audit.yml
vendored
@@ -34,7 +34,7 @@ permissions:
|
||||
jobs:
|
||||
audit:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
|
||||
permissions:
|
||||
contents: read
|
||||
# Needed to open an issue on scheduled failures.
|
||||
|
||||
3
.github/workflows/on-pr.yml
vendored
3
.github/workflows/on-pr.yml
vendored
@@ -93,9 +93,8 @@ jobs:
|
||||
.github/workflows/reusable-upload-recipe.yml
|
||||
.clang-tidy
|
||||
.codecov.yml
|
||||
bin/check-nix-store-refs.sh
|
||||
bin/check-tools.sh
|
||||
bin/default-loader-path.sh
|
||||
bin/nix/**
|
||||
cfg/**
|
||||
cmake/**
|
||||
conan/**
|
||||
|
||||
3
.github/workflows/on-trigger.yml
vendored
3
.github/workflows/on-trigger.yml
vendored
@@ -31,9 +31,8 @@ on:
|
||||
- ".github/workflows/reusable-upload-recipe.yml"
|
||||
- ".clang-tidy"
|
||||
- ".codecov.yml"
|
||||
- "bin/check-nix-store-refs.sh"
|
||||
- "bin/check-tools.sh"
|
||||
- "bin/default-loader-path.sh"
|
||||
- "bin/nix/**"
|
||||
- "cfg/**"
|
||||
- "cmake/**"
|
||||
- "conan/**"
|
||||
|
||||
2
.github/workflows/pre-commit.yml
vendored
2
.github/workflows/pre-commit.yml
vendored
@@ -17,4 +17,4 @@ jobs:
|
||||
uses: XRPLF/actions/.github/workflows/pre-commit.yml@279ec358f4a1be4088be3e024b07916fa97c75b6
|
||||
with:
|
||||
runs_on: ubuntu-latest
|
||||
container: '{ "image": "ghcr.io/xrplf/xrpld/pre-commit:sha-473fe44" }'
|
||||
container: '{ "image": "ghcr.io/xrplf/xrpld/pre-commit:sha-70b8fd3" }'
|
||||
|
||||
2
.github/workflows/publish-docs.yml
vendored
2
.github/workflows/publish-docs.yml
vendored
@@ -41,7 +41,7 @@ env:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
13
.github/workflows/reusable-build-test-config.yml
vendored
13
.github/workflows/reusable-build-test-config.yml
vendored
@@ -186,9 +186,6 @@ jobs:
|
||||
with:
|
||||
build_nproc: ${{ steps.nproc.outputs.nproc }}
|
||||
build_type: ${{ inputs.build_type }}
|
||||
# Set the verbosity to "quiet" for Windows to avoid an excessive
|
||||
# amount of logs. For other OSes, the "verbose" logs are more useful.
|
||||
log_verbosity: ${{ runner.os == 'Windows' && 'quiet' || 'verbose' }}
|
||||
sanitizers: ${{ inputs.sanitizers }}
|
||||
|
||||
- name: Configure CMake
|
||||
@@ -257,20 +254,20 @@ jobs:
|
||||
# cache included, since what it holds is what gets uploaded and reused.
|
||||
- name: Check the build output for Nix store references (Nix toolchain)
|
||||
if: ${{ inputs.toolchain == 'nix' }}
|
||||
run: ./bin/check-nix-store-refs.sh "${BUILD_DIR}"
|
||||
run: ./bin/nix/check-nix-store-refs.sh "${BUILD_DIR}"
|
||||
|
||||
- name: Check the Conan cache for Nix store references (Nix toolchain)
|
||||
if: ${{ inputs.toolchain == 'nix' }}
|
||||
run: ./bin/check-nix-store-refs.sh "${CONAN_HOME}"
|
||||
run: ./bin/nix/check-nix-store-refs.sh "${CONAN_HOME}"
|
||||
|
||||
# Only what PatchNixBinary.cmake retargets: the toolchain in the Linux
|
||||
# images always references the store. Same condition it uses.
|
||||
- name: Check for Nix store references (Linux)
|
||||
if: ${{ runner.os == 'Linux' && env.SANITIZERS_ENABLED == 'false' }}
|
||||
run: |
|
||||
./bin/check-nix-store-refs.sh "${BUILD_DIR}/xrpld"
|
||||
./bin/check-nix-store-refs.sh "${BUILD_DIR}/xrpl_tests"
|
||||
./bin/check-nix-store-refs.sh "${BUILD_DIR}/xrpld_tests"
|
||||
./bin/nix/check-nix-store-refs.sh "${BUILD_DIR}/xrpld"
|
||||
./bin/nix/check-nix-store-refs.sh "${BUILD_DIR}/xrpl_tests"
|
||||
./bin/nix/check-nix-store-refs.sh "${BUILD_DIR}/xrpld_tests"
|
||||
|
||||
- name: Show ccache statistics
|
||||
if: ${{ inputs.ccache_enabled }}
|
||||
|
||||
3
.github/workflows/reusable-clang-tidy.yml
vendored
3
.github/workflows/reusable-clang-tidy.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
needs: [determine-files]
|
||||
if: ${{ needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.need_full_run == 'true' }}
|
||||
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
|
||||
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-060957e"
|
||||
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-3d526d4"
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
@@ -73,7 +73,6 @@ jobs:
|
||||
with:
|
||||
build_nproc: ${{ steps.nproc.outputs.nproc }}
|
||||
build_type: ${{ env.BUILD_TYPE }}
|
||||
log_verbosity: verbose
|
||||
|
||||
- name: Configure CMake
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
|
||||
6
.github/workflows/reusable-rust.yml
vendored
6
.github/workflows/reusable-rust.yml
vendored
@@ -28,7 +28,7 @@ permissions:
|
||||
jobs:
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
|
||||
doc:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
2
.github/workflows/reusable-upload-recipe.yml
vendored
2
.github/workflows/reusable-upload-recipe.yml
vendored
@@ -40,7 +40,7 @@ defaults:
|
||||
jobs:
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-3d526d4
|
||||
env:
|
||||
REMOTE_NAME: ${{ inputs.remote_name }}
|
||||
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.remote_username }}
|
||||
|
||||
5
.github/workflows/upload-conan-deps.yml
vendored
5
.github/workflows/upload-conan-deps.yml
vendored
@@ -108,14 +108,11 @@ jobs:
|
||||
build_nproc: ${{ steps.nproc.outputs.nproc }}
|
||||
build_type: ${{ matrix.build_type }}
|
||||
force_build: ${{ github.event_name == 'schedule' || github.event.inputs.force_source_build == 'true' }}
|
||||
# Set the verbosity to "quiet" for Windows to avoid an excessive
|
||||
# amount of logs. For other OSes, the "verbose" logs are more useful.
|
||||
log_verbosity: ${{ runner.os == 'Windows' && 'quiet' || 'verbose' }}
|
||||
sanitizers: ${{ matrix.sanitizers }}
|
||||
|
||||
- name: Check the Conan cache for Nix store references (Nix toolchain)
|
||||
if: ${{ matrix.toolchain == 'nix' }}
|
||||
run: ./bin/check-nix-store-refs.sh "${CONAN_HOME}"
|
||||
run: ./bin/nix/check-nix-store-refs.sh "${CONAN_HOME}"
|
||||
|
||||
- name: Log into Conan remote
|
||||
if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
|
||||
|
||||
@@ -60,7 +60,7 @@ repos:
|
||||
types_or: [c++, c]
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: e2b496dc2bd8340c2524cb9a2d2a943cde1bb6df # frozen: v23.1.1
|
||||
rev: a9a8a861f30ed207ead7d5a3b7e8032283ba5da7 # frozen: v23.1.2
|
||||
hooks:
|
||||
- id: clang-format
|
||||
args: [--style=file]
|
||||
@@ -82,9 +82,10 @@ repos:
|
||||
files: ^crates/.*\.rs$
|
||||
|
||||
- repo: https://github.com/BlankSpruce/gersemi-pre-commit
|
||||
rev: f1c4833f8cf23c6d952673abc73525411a5719e8 # frozen: 0.29.1
|
||||
rev: 28010ddd6016e1a0f7bd232acb6536ef996ae897 # frozen: 0.29.2
|
||||
hooks:
|
||||
- id: gersemi
|
||||
args: [-i, --warnings-as-errors]
|
||||
|
||||
- repo: https://github.com/rbubley/mirrors-prettier
|
||||
rev: ef4a397f916211b4a39ccf9d3d9cbb6562157251 # frozen: v3.9.9
|
||||
@@ -95,19 +96,19 @@ repos:
|
||||
# Scoped to package/: the rest of the repo's Python has pre-existing findings,
|
||||
# so widening these is its own change.
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: a56c0b927e6465d37cae3e97d35d4d18ab2b96cd # frozen: v0.16.9
|
||||
rev: f12be1ebaa5351c1fc76472de98db2c3446c8253 # frozen: v0.16.10
|
||||
hooks:
|
||||
- id: ruff-check
|
||||
args: [--fix]
|
||||
files: ^package/.*\.py$
|
||||
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: 4160603246a6b365d4a2af661c6d71b0a0f50478 # frozen: 26.5.1
|
||||
rev: 96ae3e5802f3fe2d551e703e18f0a367d1a81ac2 # frozen: 26.10.0
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: 7ff8d35ae36a7d2b968f2f90b4c723e292e594ee # frozen: v2.3.1
|
||||
rev: 2834ec6639549dd6796205c8f011dedcd587288b # frozen: v2.4.0
|
||||
hooks:
|
||||
- id: mypy
|
||||
args: [--strict]
|
||||
|
||||
@@ -22,13 +22,51 @@ API version 2 is available in `xrpld` version 2.0.0 and later. See [API-VERSION-
|
||||
|
||||
This version is supported by all `xrpld` versions. For WebSocket and HTTP JSON-RPC requests, it is currently the default API version used when no `api_version` is specified.
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- The `ripplerpc` request field, which selects the shape of the JSON-RPC reply envelope, is now validated, and a value that is not exactly `"1.0"`, `"2.0"` or `"3.0"` is rejected. Two things it does not reach: a WebSocket session, which reads the field only to echo it back, and API version 3, where the field selects nothing and so is neither read nor checked, `ripplerpc: "banana"` and no `ripplerpc` at all being served alike. A request sending `"2"`, `" 2.0"`, `"2.00"`, `"02.0"`, `"2.0.0"`, `"10.0"` or any other text, such as `"abc"` or `"x2"`, gets a working reply today. After this ships, such a request sent alone gets HTTP 400 with `ripplerpc is not a supported version`, charged as a malformed request, and such an entry of a `"method": "batch"` body gets that error in its own reply while the batch answers 200. A client that spells the version loosely must therefore be corrected to one of the three exact values, or omit the field. Previously the value was compared as a string, which both accepted values that name no version and ordered multi-digit versions incorrectly: `"abc"` and `"x2"` sorted above `"3.0"` and so selected version 3, and `"10.0"` sorted below `"2.0"` and so selected version 1. Requests that send one of the three supported values, or omit the field, are unaffected.
|
||||
- `subscribe`: every subscription a connection holds must name the same `api_version`. The first `subscribe` establishes it, and a later one naming a different version is refused with `apiVersionConflict`, registering nothing; the message names the version the connection's subscriptions are served at. The HTTP status is 400 where the envelope derives it from the error, with `ripplerpc: "3.0"` or at API version 3, and 200 with `ripplerpc` `"1.0"` or `"2.0"`, as for every error; a WebSocket frame carries no status. A `subscribe` refused for another reason, a stream name the server does not have for one, still fixes that version, since the registrations it makes are spread through the handler. A connection that subscribed one stream at `api_version` 2 and another at 1, in two calls, was previously served both at version 1, the later call's, so the first stream's shape changed under it. The version is fixed for the connection's life: a client that wants another version opens another connection. On the webhook path the subscriber is keyed on its `url` alone, so the refusal reaches a second admin because of a first admin's version, and the message names the version the url's subscriptions are served at, and no remedy: releasing a url means unsubscribing its streams, which would remove that first admin's subscription, and the server cannot tell the two callers apart. Naming the url alone does not release it, a subscriber a stream map still holds not being evicted. This reaches every API version, since version 1 and version 2 content is not interchangeable either: version 2 renames `transaction` to `tx_json` and hoists `hash`, so a version 1 client handed version 2 content finds no `transaction` member.
|
||||
- `batch`: An entry of a `"method": "batch"` request that names `api_version` both inside `params` and at its own top level is now served at the version inside `params`. It was previously served at the top-level version whenever the one inside `params` resolved to version 1, so an entry asking for version 1 explicitly and something else at the top level changes which version answers it. A lone request can name both values too, and for it the version inside `params` has always won; reversing that precedence was confined to a batch entry.
|
||||
|
||||
### Additions
|
||||
|
||||
- A JSON-RPC (HTTP) request may send its parameters as an object, `"params": {"account": "r..."}`, as well as the array of one object that was already accepted. This reaches every API version: such a request was previously rejected with HTTP 400 and `params unparsable`, and is now served. A request that already sends the array form is unaffected. An entry of a `"method": "batch"` request that carries a by-name `params` object is read for `api_version` and credentials from that object. Previously such an entry had its `api_version` read from its top level and its credentials ignored, since credentials were read only from an array-form `params`.
|
||||
- A JSON-RPC (HTTP) request may name its `api_version` at its own top level, beside `method` and `id`, as well as inside `params`. The member is an XRPL extension, which the JSON-RPC 2.0 specification does not define; placing it beside the specification's own members is what lets a version 3 client send a specification-shaped request. This is honored from API version 3 up, so it needs `[beta_rpc_api]`; a version below that named at the top level is ignored, exactly as it always has been. Previously only an entry of a `"method": "batch"` request had its own top level read; a WebSocket request, which is one flat object, has always been read there.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
- A request echoed back in an error reply now has every credential-bearing field masked: `admin_password`, `admin_user`, `passphrase`, `password`, `secret`, `seed`, `seed_hex`, `url_password`, `url_username` and `username`. Nesting no longer matters, so a credential inside `params` is masked too. The same masking is applied to every request and reply written to the log, and it covers six further names that only a reply carries: `master_key`, `master_seed`, `master_seed_hex`, `validation_key`, `validation_private_key` and `validation_seed`, which is how `wallet_propose` and `validation_create` used to write a live private key to the log. A request or reply written to the log is truncated at 10,000 characters.
|
||||
- The command line client no longer prints a credential the operator did not type. A failing command echoes the request it built under `request_sent`, which carries the `admin_password` the client copies out of `[port_rpc]` in the config, so `./xrpld account_info rBogus` printed that password to stdout and into any captured output. `request_sent` is now masked. The `rpc` member beside it, which echoes the arguments as they were typed, is unchanged. The command line client also no longer writes an unparsed `json` or `ripple_path_find` argument to its trace log before parsing it, where a `secret` inside that argument could not be masked; it logs the parsed request instead, masked. The reply it receives is logged the same way, parsed and masked, where the raw body was written before, a `validation_create` answer included.
|
||||
- A WebSocket frame that does not parse, or exceeds the request size limit, is answered `{"type": "error", "error": "jsonInvalid", "size": <bytes>}`. The frame's body is reported by size rather than echoed back in a `value` member, since a body that does not parse has no fields to mask. A client that read `value` gets `size` instead.
|
||||
- Four error codes that named no HTTP status of their own, and so answered 200 on a reply reporting an error, now name one: `actMalformed`, `alreadyMultisig` and `alreadySingleSig` answer 400, and `actNotFound` answers 404. **Only API version 3 reports these four.** A request sending `ripplerpc: "3.0"` at API version 1 or 2 still receives 200 for all four, as it always has, so `account_info` on a malformed account or one the ledger does not hold answers 200 for those clients exactly as before.
|
||||
- `submit`, `simulate`, `transaction_entry`, `ledger_entry` and `ledger_accept`: Errors from these methods now include `error_code` and `error_message` alongside the `error` token, as every other method already did. Each error now answers the status its code names: 400 for a malformed request, 404 for `transactionNotFound`, 500 for an internal failure, and 501 for `notYetImplemented` and `notStandAlone`. That status change reaches only a request sending `ripplerpc: "3.0"`, or API version 3, which are the envelopes that derive the status from the error. With `ripplerpc` `"1.0"` the status stays 200 and the two new members appear beside `error`; with `"2.0"` the status stays 200, `error_code` appears, and the `code` and `message` members carry the code and the message rather than null, since that envelope copies them from `error_code` and `error_message` and drops `error_message`. From API version 3 the twenty-one `malformed*` tokens `ledger_entry` names each report the code they own instead of 31.
|
||||
- A reply reporting HTTP 402 or 502 now carries a status line. Those two statuses named no case in the switch that writes one, so such a reply began with a header instead and did not parse as an HTTP response at all. Both are reachable at any API version with `ripplerpc: "3.0"`, which derives the status from the error code: 402 through `highFee` from `sign`, `sign_for` or `submit` with a low `fee_mult_max`, and 502 through `dbDeserialization` from `tx`. The eleven statuses that already named a case report the same phrase they always have.
|
||||
- An error reply to a request sending `ripplerpc: "2.0"` or `"3.0"` no longer carries a stray `"error_message": null` beside the error it reports. The member appeared only when the `Server` log partition was set to debug or lower, because the log statement read `error_message` after the reply had renamed it to `message`, and reading it put it back as null. So the reply a client received depended on the server's log level, and the log line itself printed an empty message. Both are fixed.
|
||||
- A body the server rejects before it reads a request out of it now says which of four things was wrong. A body over the size limit answers `Request is too large`. A body that parses to `{}`, `[]` or `null` answers `Request is empty`. A body that parses to a non-empty array answers `Request is not a JSON object`; that is the only other document the parser accepts at the top level. All three previously answered `Unable to parse request: ` with nothing after the colon, the parser having recorded no error for them. Any other body does not parse, which includes one that is only whitespace and one whose top-level value is a string, number or boolean; it answers `Unable to parse request: ` followed by the parser's own reason, as it did before. The status is 400 for all four, as before, and this reaches every API version.
|
||||
- `batch`: An entry that is not identified through a secure gateway no longer clears the connection's `X-User` and forwarded-for values for the entries after it, so every entry of one body reports the role and username it would have reported on its own.
|
||||
- `batch`: Every entry of a `"method": "batch"` request is now charged against the sender's resource allowance, including one rejected before it reaches a handler, and the request stops at the first entry the connection is too loaded to serve. A reply array can therefore be shorter than the request array, and its last element depends on where the batch stopped. An entry that is not a JSON object, or names an API version the server does not serve, is charged before its role is known and answered with its own rejection; if that charge took the connection over the drop threshold, the batch stops there and that rejection is the last answer. Every other entry met over the threshold is answered `Server is overloaded` and nothing follows. A client should read any reply array shorter than its request as a connection over the drop threshold, whatever the last element says. A well-behaved client is unaffected; one that sends thousands of entries in a single body no longer gets every one of them answered.
|
||||
- A body the server rejects before it reads a request out of it is now charged against the sender's resource allowance, as a malformed request already was. Five conditions were free: a body over the size limit, one that does not parse, one carrying no document, one that is not a JSON object, and a `"method": "batch"` naming no entry array. The answer to each is unchanged. A well-behaved client is unaffected; one that repeats such a body exhausts its allowance and is refused the next request a handler would have served. A WebSocket frame that does not parse, or exceeds the request size limit, is charged the same way, and a connection that sends only such frames is closed once it crosses the drop threshold.
|
||||
- `subscribe`, `path_find`: A subscription is now served at the API version its own `subscribe` call named, rather than at the version of the most recent `subscribe` or `path_find` on the same connection. A `path_find` no longer changes the version anything that connection has subscribed is served at. An ordinary request is unaffected: it names its own version and is answered at it, so a connection subscribed at `api_version` 3 still calls `account_info` at version 1 and reads the version 1 shape.
|
||||
|
||||
## XRP Ledger server version 3.5.0
|
||||
|
||||
Version 3.5.0 is not yet released.
|
||||
|
||||
### Additions in 3.5.0
|
||||
|
||||
- `subscribe`, `unsubscribe`: Added an optional `mpt_issuances` request field, an array of MPT issuance IDs (hex strings). Subscribers receive a message with `type` `mptTransaction` for each validated transaction whose metadata affects a subscribed issuance; the message has the same fields as the `transactions` stream. MPT issuance subscriptions count toward the per-connection subscription limit. An empty array, a non-array value, or an invalid ID returns `invalidParams`. ([#5671](https://github.com/XRPLF/rippled/pull/5671))
|
||||
- `subscribe`, `unsubscribe`: Added an optional `mpt_issuances` request field, an array of MPT issuance IDs (hex strings). Subscribers receive the same `transaction` message as the `transactions` stream for each validated transaction whose metadata affects a subscribed issuance. MPT issuance subscriptions count toward the per-connection subscription limit. An empty array, a non-array value, or an invalid ID returns `invalidParams`. ([#5671](https://github.com/XRPLF/rippled/pull/5671))
|
||||
- `ledger_entry`: Add full support for checks, NFT offers, payment channels, and signer lists. ([#6319](https://github.com/XRPLF/rippled/pull/6319))
|
||||
|
||||
### Bugfixes in 3.5.0
|
||||
|
||||
- `channel_authorize`: The `channel_id` field now returns an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
|
||||
- `channel_verify`: The `channel_id` and `signature` fields now return an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
|
||||
|
||||
### Bugfixes in 3.5.0
|
||||
|
||||
- `feature`: The admin-only `vetoed` field now returns `invalidParams` unless its value is a boolean. [#7583](https://github.com/XRPLF/rippled/pull/7583)
|
||||
|
||||
## XRP Ledger server version 3.4.0
|
||||
|
||||
@@ -50,6 +88,7 @@ Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta
|
||||
- `account_lines`: The `peer` field now returns an error if the value is not a string. [#7728](https://github.com/XRPLF/rippled/pull/7728)
|
||||
- `ledger`: `delivered_amount` is now included in the metadata of successful `AccountDelete` transactions when transactions are expanded (`expand`, or admin-only `full`). Previously it was only added for `Payment` and `CheckCash`, which made `ledger` inconsistent with `tx` and `account_tx`. [#5706](https://github.com/XRPLF/rippled/pull/5706)
|
||||
- `noripple_check`: The `transactions` field is no longer included in error responses; it is still returned (possibly as an empty array) whenever `transactions` is `true` and the request succeeds. A malformed `account` is now rejected before the ledger is looked up, so that error response no longer carries the `ledger_hash`, `ledger_index`, and `validated` fields ([#6303](https://github.com/XRPLF/rippled/pull/6303)).
|
||||
- `transaction_entry`: An object or an array in `tx_hash` now returns `malformedRequest`, like any other value that is not a hex hash, instead of an `internal` error.
|
||||
|
||||
## XRP Ledger server version 3.3.0
|
||||
|
||||
|
||||
@@ -20,9 +20,11 @@
|
||||
# development setups, but not in the macOS CI environment. They are checked
|
||||
# everywhere except when running in CI on macOS.
|
||||
#
|
||||
# Tools that Nix also exposes under a version-suffixed name (`clang-tidy-22`,
|
||||
# `g++-15`, ...) are probed under both names: a suffixed name can break while
|
||||
# the plain one still works (see mkVersionedToolLinks in nix/packages.nix).
|
||||
# Tools that Nix also exposes under a version-suffixed name
|
||||
# (`clang-tidy-<v>`, `g++-<v>`, ...) are probed under both names:
|
||||
# a suffixed name can break while the plain one still works
|
||||
# (see mkVersionedToolLinks in nix/packages.nix).
|
||||
# The suffix is the major version of the plain `clang` / `gcc` on PATH.
|
||||
#
|
||||
# Tools scoped to a single dev shell rather than to commonPackages are checked
|
||||
# only in that shell, keyed off XRPL_DEVSHELL.
|
||||
@@ -34,9 +36,16 @@
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
# Version suffixes of the Nix tool links, tracking nix/packages.nix.
|
||||
gcc_version=15
|
||||
llvm_version=22
|
||||
# major_version <compiler>
|
||||
# Major version of a compiler on PATH, or "unknown" when it isn't there.
|
||||
major_version() {
|
||||
local version
|
||||
version="$("$1" -dumpversion 2>/dev/null)" || version=""
|
||||
version="${version%%.*}"
|
||||
printf '%s' "${version:-unknown}"
|
||||
}
|
||||
|
||||
llvm_version="$(major_version clang)"
|
||||
|
||||
missing=()
|
||||
checked=0
|
||||
@@ -112,6 +121,7 @@ if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
|
||||
check ClangBuildAnalyzer
|
||||
check curl
|
||||
check file
|
||||
check jq
|
||||
check less
|
||||
check make
|
||||
# net-tools netstat reports "net-tools X.Y"; macOS ships BSD netstat with no
|
||||
@@ -180,6 +190,7 @@ fi
|
||||
if [ "${os}" = "linux" ]; then
|
||||
echo
|
||||
echo "GCC toolchain:"
|
||||
gcc_version="$(major_version gcc)"
|
||||
check gcc
|
||||
check "gcc-${gcc_version}"
|
||||
check g++
|
||||
|
||||
65
bin/nix/check-build-context-runtime.sh
Executable file
65
bin/nix/check-build-context-runtime.sh
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env bash
|
||||
# Fail if a binary of a build-context Conan package loads anything from the Nix
|
||||
# store other than glibc, or cannot resolve a library at all.
|
||||
#
|
||||
# Only binaries linked by the Nix toolchain are checked, i.e. those recording a
|
||||
# store path as their interpreter or RUNPATH. Prebuilt upstream binaries (such
|
||||
# as the ones the cmake package ships) use the system loader instead.
|
||||
#
|
||||
# Build-context packages provide the tools that run during the build (protoc,
|
||||
# grpc_cpp_plugin, ...). Their package ID does not change when a Nix toolchain
|
||||
# update moves the GCC runtime to a new store path, so a cached binary has to
|
||||
# get by with the pinned glibc alone. See docs/build/nix.md.
|
||||
#
|
||||
# Usage: bin/nix/check-build-context-runtime.sh <graph.json>
|
||||
# <graph.json> is the output of `conan install --format=json`.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "usage: $0 <graph.json>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ "$(uname -s)" != "Linux" ]; then
|
||||
echo "$0: Linux only" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
folders="$(jq -r '.graph.nodes[] | select(.context == "build" and .package_folder) | .package_folder' "$1" | sort -u)"
|
||||
|
||||
checked=0
|
||||
failed=0
|
||||
|
||||
while IFS= read -r file; do
|
||||
case "$(file -b "${file}")" in
|
||||
ELF*) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
[[ "$(readelf -ldW "${file}")" == */nix/store/* ]] || continue
|
||||
checked=$((checked + 1))
|
||||
|
||||
# `ldd` lists the interpreter and every library as the loader resolves them.
|
||||
if deps="$(ldd "${file}" 2>&1)"; then
|
||||
bad="$(printf '%s\n' "${deps}" |
|
||||
grep -E 'not found|/nix/store/' |
|
||||
grep -vE '/nix/store/[^/]+-glibc-[^/]+/' || true)"
|
||||
else
|
||||
case "${deps}" in
|
||||
*"not a dynamic executable"*) continue ;;
|
||||
esac
|
||||
bad="${deps}"
|
||||
fi
|
||||
if [ -n "${bad}" ]; then
|
||||
failed=$((failed + 1))
|
||||
echo "::error file=${file}::loads a library from the Nix store other than glibc"
|
||||
echo "${file}"
|
||||
echo "${bad}" | sed 's/^/ /'
|
||||
fi
|
||||
done < <(
|
||||
# shellcheck disable=SC2086 # one folder per line, no spaces in Conan paths
|
||||
[ -z "${folders}" ] || find ${folders} -type f \( -perm -u+x -o -name '*.so*' \)
|
||||
)
|
||||
|
||||
echo "Build-context packages: checked ${checked} binaries, ${failed} failed."
|
||||
[ "${failed}" -eq 0 ]
|
||||
@@ -10,7 +10,7 @@
|
||||
# alone; the scripts in a Conan cache are all git hook samples and autotools
|
||||
# scratch, 36 false positives to 0 real.
|
||||
#
|
||||
# Usage: bin/check-nix-store-refs.sh <path>
|
||||
# Usage: bin/nix/check-nix-store-refs.sh <path>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -23,7 +23,7 @@ apt-get clean
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
EOF
|
||||
|
||||
ARG PRE_COMMIT_VERSION=4.6.0
|
||||
ARG PRE_COMMIT_VERSION=4.6.2
|
||||
RUN pip install --no-cache --break-system-packages \
|
||||
pre-commit==${PRE_COMMIT_VERSION}
|
||||
|
||||
|
||||
@@ -26,8 +26,6 @@ import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
CLANG_TIDY_VERSION = 22
|
||||
|
||||
# Extensions run-clang-tidy can analyse: `.cpp` translation units and, thanks to
|
||||
# the `verify_headers` build option, `.h`/`.hpp` headers (each has its own
|
||||
# compile_commands.json entry). `.ipp` fragments have no entry and are skipped.
|
||||
@@ -39,8 +37,21 @@ TIDY_EXTENSIONS = {".cpp", ".h", ".hpp"}
|
||||
FILEPATH_RE = re.compile(r"^(\s*(?:-\s+)?FilePath:\s*)'((?:[^']|'')*)'\s*$")
|
||||
|
||||
|
||||
def find_tool(name: str) -> str | None:
|
||||
for candidate in (f"{name}-{CLANG_TIDY_VERSION}", name):
|
||||
def clang_tidy_major() -> str | None:
|
||||
"""Major version of the `clang-tidy` on PATH, which run-clang-tidy invokes."""
|
||||
if not (clang_tidy := shutil.which("clang-tidy")):
|
||||
return None
|
||||
output = subprocess.run(
|
||||
[clang_tidy, "--version"], capture_output=True, text=True
|
||||
).stdout
|
||||
m = re.search(r"LLVM version (\d+)", output)
|
||||
return m.group(1) if m else None
|
||||
|
||||
|
||||
def find_tool(name: str, version: str | None) -> str | None:
|
||||
"""Prefer `<name>-<version>`, so a host tool of another version can't win."""
|
||||
candidates = ([f"{name}-{version}"] if version else []) + [name]
|
||||
for candidate in candidates:
|
||||
if path := shutil.which(candidate):
|
||||
return path
|
||||
return None
|
||||
@@ -103,8 +114,9 @@ def main():
|
||||
if not files:
|
||||
return 0
|
||||
|
||||
run_clang_tidy = find_tool("run-clang-tidy")
|
||||
clang_apply_replacements = find_tool("clang-apply-replacements")
|
||||
version = clang_tidy_major()
|
||||
run_clang_tidy = find_tool("run-clang-tidy", version)
|
||||
clang_apply_replacements = find_tool("clang-apply-replacements", version)
|
||||
missing = [
|
||||
name
|
||||
for name, path in (
|
||||
@@ -114,9 +126,10 @@ def main():
|
||||
if not path
|
||||
]
|
||||
if missing:
|
||||
tried = f" (tried the '-{version}' suffix too)" if version else ""
|
||||
print(
|
||||
f"clang-tidy check failed: TIDY is enabled but {' and '.join(missing)} "
|
||||
f"was not found in PATH (tried the '-{CLANG_TIDY_VERSION}' suffix too).",
|
||||
f"was not found in PATH{tried}.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
(runtime libraries resolved through the rpath) are skipped too.
|
||||
Everywhere else `patch_nix_binary` is a no-op.
|
||||
|
||||
The default loader is resolved by bin/default-loader-path.sh.
|
||||
The default loader is resolved by bin/nix/default-loader-path.sh.
|
||||
#]===================================================================]
|
||||
|
||||
include_guard(GLOBAL)
|
||||
@@ -25,7 +25,7 @@ include_guard(GLOBAL)
|
||||
include(CompilationEnv)
|
||||
|
||||
# Resolves the system default ELF loader path for the current architecture.
|
||||
set(_loader_path_script "${CMAKE_SOURCE_DIR}/bin/default-loader-path.sh")
|
||||
set(_loader_path_script "${CMAKE_SOURCE_DIR}/bin/nix/default-loader-path.sh")
|
||||
|
||||
if(
|
||||
is_linux
|
||||
|
||||
@@ -292,6 +292,14 @@ if(xrpld)
|
||||
)
|
||||
target_sources(xrpld PRIVATE ${sources})
|
||||
|
||||
rpcspec_generate_instantiations(
|
||||
OUT_VAR rpcspec_instantiations
|
||||
VALUE_TYPE "::json::Value"
|
||||
VIEW_HEADER "xrpld/rpc/detail/JsonObjectView.hpp"
|
||||
HANDLERS book_changes ledger transaction_entry
|
||||
)
|
||||
target_sources(xrpld PRIVATE ${rpcspec_instantiations})
|
||||
|
||||
if(tests)
|
||||
file(
|
||||
GLOB_RECURSE sources
|
||||
@@ -322,6 +330,7 @@ if(xrpld)
|
||||
# antithesis_instrumentation.h, which is not exported as INTERFACE
|
||||
target_include_directories(
|
||||
xrpld
|
||||
SYSTEM
|
||||
PRIVATE ${CMAKE_SOURCE_DIR}/external/antithesis-sdk
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -77,19 +77,24 @@ if(is_clang)
|
||||
message(STATUS " Ignorelist: ${ignorelist_path}")
|
||||
endif()
|
||||
|
||||
# Define SANITIZERS macro for BuildInfo.cpp
|
||||
# Define the SANITIZERS macro for BuildInfo.cpp, plus one of XRPL_ASAN,
|
||||
# XRPL_TSAN and XRPL_UBSAN per active sanitizer, so that code can test for a
|
||||
# specific one with #ifdef instead of parsing the dot-joined SANITIZERS string.
|
||||
set(sanitizers_list)
|
||||
if(SANITIZERS MATCHES "address")
|
||||
set(enable_asan ON)
|
||||
list(APPEND sanitizers_list "ASAN")
|
||||
target_compile_definitions(common INTERFACE XRPL_ASAN)
|
||||
endif()
|
||||
if(SANITIZERS MATCHES "thread")
|
||||
set(enable_tsan ON)
|
||||
list(APPEND sanitizers_list "TSAN")
|
||||
target_compile_definitions(common INTERFACE XRPL_TSAN)
|
||||
endif()
|
||||
if(SANITIZERS MATCHES "undefinedbehavior")
|
||||
set(enable_ubsan ON)
|
||||
list(APPEND sanitizers_list "UBSAN")
|
||||
target_compile_definitions(common INTERFACE XRPL_UBSAN)
|
||||
endif()
|
||||
|
||||
if(sanitizers_list)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"requires": [
|
||||
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
|
||||
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1782392402.420688",
|
||||
"xrpl-rpc-spec/0.1.19#870b2d3abcfbbf13b61c2d3c69495060%1790348286.187549",
|
||||
"xrpl-rpc-spec/0.1.21#d536f87a2ae7d313452746cfa3ac4404%1790869005.122975",
|
||||
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1782392403.185447",
|
||||
"soci/4.0.3#e726491a03468795453f7c83fc924a96%1782392402.679521",
|
||||
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1782307151.633168",
|
||||
@@ -20,7 +20,7 @@
|
||||
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1782392403.066892",
|
||||
"jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
|
||||
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1782392402.791979",
|
||||
"grpc/1.81.1#b87796a4269034856cbc1a2522db16eb%1788275071.530512",
|
||||
"grpc/1.81.1#aaa93ab6cda2f2baa6a84490582c8adf%1791284951.826256",
|
||||
"fast_float/8.2.10#f6f28d6bb22112078e7dbda611caf681%1785888854.601666",
|
||||
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1782307148.15562",
|
||||
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1782392402.538492",
|
||||
@@ -34,11 +34,15 @@
|
||||
"build_requires": [
|
||||
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
|
||||
"strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1782395692.540639",
|
||||
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1782392402.431897",
|
||||
"protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1782392410.233933",
|
||||
"openssl/3.6.3#f806de8933e3bf6f01016c6a888cee2e%1783945160.863288",
|
||||
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1782395690.33162",
|
||||
"msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649",
|
||||
"m4/1.4.19#1727f439cf74e83826ec96d0b4904eee%1784541921.659",
|
||||
"grpc/1.81.1#aaa93ab6cda2f2baa6a84490582c8adf%1791284951.826256",
|
||||
"cmake/4.3.3#840cf00ea09777e05c2050a50a82c722%1782392418.696091",
|
||||
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1782392402.681654",
|
||||
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1782392402.624226",
|
||||
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
|
||||
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86",
|
||||
|
||||
@@ -3,5 +3,13 @@
|
||||
core:non_interactive=True
|
||||
core.download:parallel={{ os.cpu_count() }}
|
||||
core.upload:parallel={{ os.cpu_count() }}
|
||||
tools.files.download:retry=5
|
||||
# Fall back to Conan Center's source backups when a recipe's upstream URL is down
|
||||
# (e.g. the GNU FTP mirrors), see
|
||||
# https://github.com/conan-io/conan-center-index/issues/28147#issuecomment-3183544772
|
||||
# The backups are only tried once every upstream URL has used up its retries,
|
||||
# so keep the retries low.
|
||||
core.sources:download_urls=["origin", "https://c3i.jfrog.io/artifactory/conan-center-backup-sources"]
|
||||
tools.files.download:retry=1
|
||||
tools.files.download:retry_wait=10
|
||||
# Fail fast on unreachable hosts (default connect timeout is 30s), keep the 60s read timeout.
|
||||
core.net.http:timeout=(5, 60)
|
||||
|
||||
@@ -29,9 +29,9 @@ os.version={{ min_macos_version }}
|
||||
[conf]
|
||||
{# The Boost recipe builds with b2, which doesn't use Conan's toolchain files. #}
|
||||
{# Instead it hand-rolls the compiler for user-config.jam, #}
|
||||
{# and its fallback probes a version-suffixed binary (e.g. `g++-15`) before plain `g++`. #}
|
||||
{# Inside the Nix shell the wrapper only provides `g++`/`gcc` (no `-15` suffix), #}
|
||||
{# so on a host that also has a system `g++-15` the probe escapes Nix #}
|
||||
{# and its fallback probes a version-suffixed binary (e.g. `g++-<major>`) before plain `g++`. #}
|
||||
{# Inside the Nix shell the wrapper only provides `g++`/`gcc` (no `-<major>` suffix), #}
|
||||
{# so on a host that also has a system `g++-<major>` the probe escapes Nix #}
|
||||
{# and picks the system compiler, which is mismatched with the Nix libraries #}
|
||||
{# and breaks the build (e.g. Boost.Stacktrace link checks fail). #}
|
||||
{# Pinning the executables here short-circuits that probe so Boost (and the rest of the toolchain) #}
|
||||
@@ -49,8 +49,31 @@ tools.build:compiler_executables={'c':'{{ cc_exe }}','cpp':'{{ cxx_exe }}'}
|
||||
user.package:cppstd_version=23
|
||||
tools.info.package_id:confs+=["user.package:cppstd_version"]
|
||||
|
||||
{% if os == "Macos" %}
|
||||
{% if os == "Linux" and context == "build" %}
|
||||
{# Build-context executables (protoc, grpc_cpp_plugin, build tools) run during the build #}
|
||||
{# and would otherwise load libstdc++/libgcc from a Nix store path #}
|
||||
{# that might change with a Nix toolchain update. #}
|
||||
{# --as-needed drops the ones they link but don't use, #}
|
||||
{# such as libatomic for grpc_cpp_plugin on arm64. #}
|
||||
{% set static_runtime_flags = ["-static-libstdc++", "-static-libgcc", "-Wl,--as-needed"] %}
|
||||
tools.build:exelinkflags+={{ static_runtime_flags }}
|
||||
tools.info.package_id:confs+=["tools.build:exelinkflags"]
|
||||
{% endif %}
|
||||
|
||||
{% if os == "Linux" and context == "build" %}
|
||||
{# b2 links itself with its own script, which ignores exelinkflags #}
|
||||
{# and only takes CXXFLAGS when use_cxx_env is set (see [buildenv] below). #}
|
||||
[options]
|
||||
b2/*:use_cxx_env=True
|
||||
{% endif %}
|
||||
|
||||
[buildenv]
|
||||
{# gRPC emits thousands of compiler warnings that we cannot act on. #}
|
||||
{# CMake picks up CXXFLAGS, and unlike tools.build:cxxflags, #}
|
||||
{# this is not part of the package ID, so binaries stay shareable. #}
|
||||
grpc/*:CXXFLAGS=-w
|
||||
|
||||
{% if os == "Macos" %}
|
||||
{# os.version adds -mmacosx-version-min to compiler command lines, #}
|
||||
{# but Boost.Context's b2 assembly (.S) rule ignores it, #}
|
||||
{# so those objects keep the host SDK version and still warn at link time. #}
|
||||
@@ -58,3 +81,7 @@ tools.info.package_id:confs+=["user.package:cppstd_version"]
|
||||
{# Scoped to boost/* since it is the only gap. #}
|
||||
boost/*:MACOSX_DEPLOYMENT_TARGET={{ min_macos_version }}
|
||||
{% endif %}
|
||||
|
||||
{% if os == "Linux" and context == "build" %}
|
||||
b2/*:CXXFLAGS={{ static_runtime_flags | join(" ") }}
|
||||
{% endif %}
|
||||
|
||||
@@ -5,6 +5,9 @@ include(default)
|
||||
|
||||
{% if not sanitizers %}
|
||||
{# Sanitizers not configured; no additional settings needed #}
|
||||
{% elif context == "build" %}
|
||||
{# Build-context packages are tools we run, not code we test, #}
|
||||
{# so don't instrument them #}
|
||||
{% else %}
|
||||
|
||||
{% if compiler == "msvc" %}
|
||||
|
||||
11
conanfile.py
11
conanfile.py
@@ -40,7 +40,7 @@ class Xrpl(ConanFile):
|
||||
"nudb/2.0.9",
|
||||
"openssl/3.6.3",
|
||||
"soci/4.0.3",
|
||||
"xrpl-rpc-spec/0.1.19",
|
||||
"xrpl-rpc-spec/0.1.21",
|
||||
"zlib/1.3.2",
|
||||
]
|
||||
|
||||
@@ -49,7 +49,8 @@ class Xrpl(ConanFile):
|
||||
]
|
||||
|
||||
tool_requires = [
|
||||
"protobuf/6.33.5",
|
||||
"grpc/<host_version>",
|
||||
"protobuf/<host_version>",
|
||||
]
|
||||
|
||||
default_options = {
|
||||
@@ -123,6 +124,10 @@ class Xrpl(ConanFile):
|
||||
"xxhash/*:shared": False,
|
||||
}
|
||||
|
||||
# default_options only reach the host context;
|
||||
# give tool_requires (and their dependencies) the same dependency options.
|
||||
default_build_options = {k: v for k, v in default_options.items() if "/" in k}
|
||||
|
||||
def set_version(self):
|
||||
self.version = self.version or DEV_VERSION
|
||||
|
||||
@@ -149,7 +154,7 @@ class Xrpl(ConanFile):
|
||||
self.requires("xxhash/0.8.3", transitive_headers=True)
|
||||
|
||||
exports_sources = (
|
||||
"bin/default-loader-path.sh",
|
||||
"bin/nix/default-loader-path.sh",
|
||||
"CMakeLists.txt",
|
||||
"cfg/*",
|
||||
"cmake/*",
|
||||
|
||||
@@ -80,13 +80,12 @@ function(add_xrpl_crate name)
|
||||
# `cc` picks its runtime flag from `crt-static` alone, so it compiles a
|
||||
# crate's C++ with `-MT`; Debug needs `-MTd` (to match cmake/XrplCompiler.cmake).
|
||||
if(is_msvc)
|
||||
corrosion_set_env_vars(
|
||||
${ARG_CRATE}
|
||||
"$<$<CONFIG:Debug>:CXXFLAGS=-MTd>"
|
||||
)
|
||||
corrosion_set_env_vars(${ARG_CRATE} "$<$<CONFIG:Debug>:CXXFLAGS=-MTd>")
|
||||
endif()
|
||||
corrosion_add_cxxbridge(${name}_cxxbridge CRATE ${ARG_CRATE} FILES
|
||||
${ARG_FILES}
|
||||
corrosion_add_cxxbridge(
|
||||
${name}_cxxbridge
|
||||
CRATE ${ARG_CRATE}
|
||||
FILES ${ARG_FILES}
|
||||
)
|
||||
# Generated cxxbridge headers don't exist at configure time; CMake 3.28+
|
||||
# validates INTERFACE_SOURCES on consuming targets. Clear it to skip the
|
||||
|
||||
16
docs/build/environment.md
vendored
16
docs/build/environment.md
vendored
@@ -10,14 +10,15 @@ This document explains how to set one up.
|
||||
support it — see [compiler support for C++23][cpp23-support].
|
||||
The versions currently tested in CI are:
|
||||
|
||||
| Compiler | Version |
|
||||
| ----------- | ------------------ |
|
||||
| GCC | 15.2 |
|
||||
| Clang | 22 |
|
||||
| Apple Clang | 21 |
|
||||
| MSVC | Visual Studio 2026 |
|
||||
| Compiler | Version |
|
||||
| ----------- | ------------------------------- |
|
||||
| GCC | `gccVersion` in [packages.nix] |
|
||||
| Clang | `llvmVersion` in [packages.nix] |
|
||||
| Apple Clang | 21 |
|
||||
| MSVC | Visual Studio 2026 |
|
||||
|
||||
LLVM tools (`clang-tidy` and `clang-format`) are also pinned to version 22.
|
||||
LLVM tools (`clang-tidy` and `clang-format`)
|
||||
come from the same LLVM release as Clang.
|
||||
|
||||
### Older compilers
|
||||
|
||||
@@ -156,3 +157,4 @@ version out of the box — run it via `run-clang-tidy`. No separate installation
|
||||
is needed.
|
||||
|
||||
[cpp23-support]: https://en.cppreference.com/w/cpp/compiler_support/23
|
||||
[packages.nix]: ../../nix/packages.nix
|
||||
|
||||
30
docs/build/nix.md
vendored
30
docs/build/nix.md
vendored
@@ -183,24 +183,36 @@ at link or run time.
|
||||
> configuration CI covers, and no dependency binaries are published for it.
|
||||
|
||||
This is checked rather than assumed.
|
||||
[`bin/check-nix-store-refs.sh`](../../bin/check-nix-store-refs.sh) takes one file
|
||||
or directory and fails if a binary under it resolves a store path at run time.
|
||||
[`bin/nix/check-nix-store-refs.sh`](../../bin/nix/check-nix-store-refs.sh) takes one
|
||||
file or directory and fails if a binary under it resolves a store path at run time.
|
||||
CI runs it over the build output and the Conan cache, and again in the upload job
|
||||
before anything is published. You can run it yourself:
|
||||
|
||||
```bash
|
||||
bin/check-nix-store-refs.sh build
|
||||
bin/check-nix-store-refs.sh ~/.conan2-nix
|
||||
bin/nix/check-nix-store-refs.sh build
|
||||
bin/nix/check-nix-store-refs.sh ~/.conan2-nix
|
||||
```
|
||||
|
||||
It works on Linux too, but asserts something narrower there: the toolchain always
|
||||
writes the store into `PT_INTERP` and `RUNPATH`, and CI builds inside an image
|
||||
whose store is fixed for its lifetime, so that is fine. Only the binaries
|
||||
[`PatchNixBinary.cmake`](../../cmake/PatchNixBinary.cmake) retargets to the
|
||||
system loader have to be clean, and those are what CI checks:
|
||||
writes the store into `PT_INTERP` and `RUNPATH`. That is fine for the pinned
|
||||
glibc, whose path does not move, but not for the GCC runtime, which moves with
|
||||
every GCC update. So [`conan/profiles/default`](../../conan/profiles/default)
|
||||
links build-context packages, whose executables run during the build, with
|
||||
`-static-libstdc++ -static-libgcc -Wl,--as-needed`, and
|
||||
[`conan/profiles/sanitizers`](../../conan/profiles/sanitizers) does not
|
||||
instrument them. CI checks that they load nothing from the store but glibc, from
|
||||
the graph `conan install --format=json` writes:
|
||||
|
||||
```bash
|
||||
bin/check-nix-store-refs.sh build/xrpld
|
||||
bin/nix/check-build-context-runtime.sh graph.json
|
||||
```
|
||||
|
||||
Only the binaries [`PatchNixBinary.cmake`](../../cmake/PatchNixBinary.cmake)
|
||||
retargets to the system loader have to be fully clean, and those are what CI
|
||||
checks:
|
||||
|
||||
```bash
|
||||
bin/nix/check-nix-store-refs.sh build/xrpld
|
||||
```
|
||||
|
||||
### The libresolv stub
|
||||
|
||||
6
docs/build/nix_troubleshooting.md
vendored
6
docs/build/nix_troubleshooting.md
vendored
@@ -178,11 +178,11 @@ A binary stops starting after a `nix flake update`, or after
|
||||
dyld[57271]: Library not loaded: /nix/store/…-libresolv-93/lib/libresolv.9.dylib
|
||||
```
|
||||
|
||||
[`bin/check-nix-store-refs.sh`](../../bin/check-nix-store-refs.sh) finds the same
|
||||
thing without having to run anything, and names the file:
|
||||
[`bin/nix/check-nix-store-refs.sh`](../../bin/nix/check-nix-store-refs.sh) finds the
|
||||
same thing without having to run anything, and names the file:
|
||||
|
||||
```
|
||||
$ bin/check-nix-store-refs.sh ~/.conan2-nix
|
||||
$ bin/nix/check-nix-store-refs.sh ~/.conan2-nix
|
||||
::error file=/Users/you/.conan2-nix/p/b/c-area24ded30c388c/p/bin/adig::references the Nix store at run time
|
||||
/Users/you/.conan2-nix/p/b/c-area24ded30c388c/p/bin/adig
|
||||
/nix/store/p4lp3xq4imd1qzqh08x8vcq2zfhi7rca-libresolv-93/lib/libresolv.9.dylib
|
||||
|
||||
2
external/antithesis-sdk/CMakeLists.txt
vendored
2
external/antithesis-sdk/CMakeLists.txt
vendored
@@ -6,7 +6,7 @@ project(antithesis-sdk-cpp VERSION 0.4.4 LANGUAGES CXX)
|
||||
add_library(antithesis-sdk-cpp INTERFACE antithesis_sdk.h)
|
||||
|
||||
# Note, both sections below created by xrpld project
|
||||
target_include_directories(antithesis-sdk-cpp INTERFACE
|
||||
target_include_directories(antithesis-sdk-cpp SYSTEM INTERFACE
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
)
|
||||
|
||||
12
flake.lock
generated
12
flake.lock
generated
@@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1781173989,
|
||||
"narHash": "sha256-fnzKKPvS+oieI/pTzotA5tkoM47EB1NpaBcgk4R97hE=",
|
||||
"lastModified": 1791130267,
|
||||
"narHash": "sha256-1sjwcQMcgAbmiip/VivBSiK8p2bTkQGOvCuO7vnwfx4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "8c91a71d13451abc40eb9dae8910f972f979852f",
|
||||
"rev": "9013764fcc0ea99fa16cf7aa7decf8a5c3889dd2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -47,11 +47,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1784611586,
|
||||
"narHash": "sha256-OfqgY+0hp/zseZB7uyH0U8kIDPS4scZZCyAurEplvG0=",
|
||||
"lastModified": 1791189933,
|
||||
"narHash": "sha256-Y8eN7ki0Urx4Ojf4w1xSpWg8uk/3lJqK+E0Oth77rH0=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "14f58845249f3552a89b07772626b8d3c632fa86",
|
||||
"rev": "e60029353d0c48d216bc4b065168ccd4079c166f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -3,9 +3,84 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Add two signed 64-bit integers, returning std::nullopt when the exact
|
||||
* mathematical sum is not representable in std::int64_t.
|
||||
*/
|
||||
[[nodiscard]] constexpr std::optional<std::int64_t>
|
||||
checkedAdd(std::int64_t a, std::int64_t b) noexcept
|
||||
{
|
||||
using L = std::numeric_limits<std::int64_t>;
|
||||
|
||||
if ((b > 0 && a > L::max() - b) || (b < 0 && a < L::min() - b))
|
||||
return std::nullopt;
|
||||
|
||||
return a + b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract two signed 64-bit integers, returning std::nullopt when the exact
|
||||
* mathematical difference is not representable in std::int64_t.
|
||||
*/
|
||||
[[nodiscard]] constexpr std::optional<std::int64_t>
|
||||
checkedSub(std::int64_t a, std::int64_t b) noexcept
|
||||
{
|
||||
using L = std::numeric_limits<std::int64_t>;
|
||||
|
||||
if ((b > 0 && a < L::min() + b) || (b < 0 && a > L::max() + b))
|
||||
return std::nullopt;
|
||||
|
||||
return a - b;
|
||||
}
|
||||
|
||||
static_assert(checkedAdd(0, 0) == 0);
|
||||
static_assert(checkedAdd(1, -1) == 0);
|
||||
static_assert(checkedAdd(-5, 2) == -3);
|
||||
static_assert(!checkedAdd(std::numeric_limits<std::int64_t>::max(), 1).has_value());
|
||||
static_assert(!checkedAdd(std::numeric_limits<std::int64_t>::min(), -1).has_value());
|
||||
static_assert(
|
||||
checkedAdd(std::numeric_limits<std::int64_t>::max() - 1, 1) ==
|
||||
std::numeric_limits<std::int64_t>::max());
|
||||
static_assert(
|
||||
checkedAdd(
|
||||
std::numeric_limits<std::int64_t>::min(),
|
||||
std::numeric_limits<std::int64_t>::max()) == -1);
|
||||
static_assert(
|
||||
checkedAdd(
|
||||
std::numeric_limits<std::int64_t>::max(),
|
||||
std::numeric_limits<std::int64_t>::min()) == -1);
|
||||
static_assert(
|
||||
!checkedAdd(std::numeric_limits<std::int64_t>::max(), std::numeric_limits<std::int64_t>::max())
|
||||
.has_value());
|
||||
static_assert(
|
||||
!checkedAdd(std::numeric_limits<std::int64_t>::min(), std::numeric_limits<std::int64_t>::min())
|
||||
.has_value());
|
||||
|
||||
static_assert(checkedSub(0, 0) == 0);
|
||||
static_assert(checkedSub(1, 1) == 0);
|
||||
static_assert(checkedSub(-5, 2) == -7);
|
||||
static_assert(checkedSub(-5, -2) == -3);
|
||||
static_assert(!checkedSub(std::numeric_limits<std::int64_t>::min(), 1).has_value());
|
||||
static_assert(!checkedSub(std::numeric_limits<std::int64_t>::max(), -1).has_value());
|
||||
static_assert(
|
||||
checkedSub(std::numeric_limits<std::int64_t>::min() + 1, 1) ==
|
||||
std::numeric_limits<std::int64_t>::min());
|
||||
static_assert(
|
||||
checkedSub(-1, std::numeric_limits<std::int64_t>::max()) ==
|
||||
std::numeric_limits<std::int64_t>::min());
|
||||
static_assert(
|
||||
!checkedSub(std::numeric_limits<std::int64_t>::max(), std::numeric_limits<std::int64_t>::min())
|
||||
.has_value());
|
||||
static_assert(
|
||||
!checkedSub(std::numeric_limits<std::int64_t>::min(), std::numeric_limits<std::int64_t>::max())
|
||||
.has_value());
|
||||
|
||||
/**
|
||||
* Calculate one number divided by another number in percentage.
|
||||
* The result is rounded up to the next integer, and capped in the range [0,100]
|
||||
|
||||
@@ -36,7 +36,7 @@ template <typename T>
|
||||
concept SomeChar = std::same_as<std::remove_cvref_t<T>, int8_t> ||
|
||||
std::same_as<std::remove_cvref_t<T>, char> || std::same_as<std::remove_cvref_t<T>, uint8_t>;
|
||||
|
||||
inline constexpr std::array<std::optional<int>, 256> const kDigitLookupTable = []() {
|
||||
inline constexpr std::array<std::optional<int>, 256> const kDigitLookupTable = [] {
|
||||
std::array<std::optional<int>, 256> t{};
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
|
||||
@@ -783,7 +783,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::scoped_lock<std::recursive_mutex> const&)
|
||||
{
|
||||
return std::thread([&, this]() {
|
||||
return std::thread([&, this] {
|
||||
int cacheRemovals = 0;
|
||||
int mapRemovals = 0;
|
||||
|
||||
@@ -863,7 +863,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::scoped_lock<std::recursive_mutex> const&)
|
||||
{
|
||||
return std::thread([&, this]() {
|
||||
return std::thread([&, this] {
|
||||
// NOLINTBEGIN https://github.com/XRPLF/rippled/issues/7056
|
||||
int cacheRemovals = 0;
|
||||
int mapRemovals = 0;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
namespace xrpl {
|
||||
|
||||
template <typename Key>
|
||||
static std::size_t
|
||||
std::size_t
|
||||
extract(Key const& key)
|
||||
{
|
||||
return key;
|
||||
|
||||
@@ -1604,7 +1604,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::erase(
|
||||
beast::detail::AgedContainerIterator<IsConst, Iterator> pos)
|
||||
requires(!IsBoostReverseIterator<Iterator>::value)
|
||||
{
|
||||
unlinkAndDeleteElement(&*((pos++).iterator()));
|
||||
unlinkAndDeleteElement(&*(pos++).iterator());
|
||||
return beast::detail::AgedContainerIterator<false, Iterator>(pos.iterator());
|
||||
}
|
||||
|
||||
@@ -1617,7 +1617,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::erase(
|
||||
requires(!IsBoostReverseIterator<Iterator>::value)
|
||||
{
|
||||
for (; first != last;)
|
||||
unlinkAndDeleteElement(&*((first++).iterator()));
|
||||
unlinkAndDeleteElement(&*(first++).iterator());
|
||||
|
||||
return beast::detail::AgedContainerIterator<false, Iterator>(first.iterator());
|
||||
}
|
||||
|
||||
@@ -2404,7 +2404,7 @@ beast::detail::AgedContainerIterator<false, Iterator>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::erase(
|
||||
beast::detail::AgedContainerIterator<IsConst, Iterator> pos)
|
||||
{
|
||||
unlinkAndDeleteElement(&*((pos++).iterator()));
|
||||
unlinkAndDeleteElement(&*(pos++).iterator());
|
||||
return beast::detail::AgedContainerIterator<false, Iterator>(pos.iterator());
|
||||
}
|
||||
|
||||
@@ -2424,7 +2424,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
|
||||
beast::detail::AgedContainerIterator<IsConst, Iterator> last)
|
||||
{
|
||||
for (; first != last;)
|
||||
unlinkAndDeleteElement(&*((first++).iterator()));
|
||||
unlinkAndDeleteElement(&*(first++).iterator());
|
||||
|
||||
return beast::detail::AgedContainerIterator<false, Iterator>(first.iterator());
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ void
|
||||
hash_append(Hasher& h, boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept
|
||||
requires(IsContiguouslyHashable<Key, Hasher>::value)
|
||||
{
|
||||
h(&(v.begin()), v.size() * sizeof(Key));
|
||||
h(&v.begin(), v.size() * sizeof(Key));
|
||||
}
|
||||
// tuple
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace beast::insight {
|
||||
class HookImpl : public std::enable_shared_from_this<HookImpl>
|
||||
{
|
||||
public:
|
||||
using HandlerType = std::function<void(void)>;
|
||||
using HandlerType = std::function<void()>;
|
||||
|
||||
virtual ~HookImpl() = 0;
|
||||
};
|
||||
|
||||
@@ -61,7 +61,7 @@ isMulticast(Address const& addr)
|
||||
inline bool
|
||||
isPrivate(Address const& addr)
|
||||
{
|
||||
return (addr.is_v4()) ? isPrivate(addr.to_v4()) : isPrivate(addr.to_v6());
|
||||
return addr.is_v4() ? isPrivate(addr.to_v4()) : isPrivate(addr.to_v6());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +70,7 @@ isPrivate(Address const& addr)
|
||||
inline bool
|
||||
isPublic(Address const& addr)
|
||||
{
|
||||
return (addr.is_v4()) ? isPublic(addr.to_v4()) : isPublic(addr.to_v6());
|
||||
return addr.is_v4() ? isPublic(addr.to_v4()) : isPublic(addr.to_v6());
|
||||
}
|
||||
|
||||
} // namespace ip
|
||||
|
||||
@@ -34,7 +34,7 @@ typeName()
|
||||
name += " volatile";
|
||||
if (std::is_lvalue_reference_v<T>)
|
||||
{
|
||||
name += "&";
|
||||
name += '&';
|
||||
}
|
||||
else if (std::is_rvalue_reference_v<T>)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace beast::unit_test {
|
||||
namespace detail {
|
||||
|
||||
template <class String>
|
||||
static std::string
|
||||
std::string
|
||||
makeReason(String const& reason, char const* file, int line)
|
||||
{
|
||||
std::string s(reason);
|
||||
|
||||
@@ -47,8 +47,8 @@ public:
|
||||
template <class F, class... Args>
|
||||
explicit Thread(Suite& s, F&& f, Args&&... args) : s_(&s)
|
||||
{
|
||||
std::function<void(void)> b = [f = std::forward<F>(f),
|
||||
... args = std::forward<Args>(args)]() mutable {
|
||||
std::function<void()> b = [f = std::forward<F>(f),
|
||||
... args = std::forward<Args>(args)] mutable {
|
||||
std::invoke(f, args...);
|
||||
};
|
||||
t_ = std::thread(&Thread::run, this, std::move(b));
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
|
||||
private:
|
||||
void
|
||||
run(std::function<void(void)> f)
|
||||
run(std::function<void()> f)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -54,7 +54,7 @@ JobQueue::Coro::post()
|
||||
}
|
||||
|
||||
// sp keeps 'this' alive
|
||||
if (jq_.addJob(type_, name_, [this, sp = shared_from_this()]() { resume(); }))
|
||||
if (jq_.addJob(type_, name_, [this, sp = shared_from_this()] { resume(); }))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -130,7 +130,7 @@ inline void
|
||||
JobQueue::Coro::join()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mutexRun_);
|
||||
cv_.wait(lk, [this]() { return !running_; });
|
||||
cv_.wait(lk, [this] { return !running_; });
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/json/json_forwards.h>
|
||||
|
||||
#include <concepts>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
@@ -198,6 +200,15 @@ public:
|
||||
*/
|
||||
Value(StaticString const& value);
|
||||
Value(std::string const& value);
|
||||
/**
|
||||
* @brief Constructs a value from a string view.
|
||||
*
|
||||
* The characters are copied, so the view need not outlive the call and need
|
||||
* not be NUL-terminated.
|
||||
*
|
||||
* @param value The characters to copy.
|
||||
*/
|
||||
Value(std::string_view value);
|
||||
Value(bool value);
|
||||
Value(Value const& other);
|
||||
~Value();
|
||||
@@ -472,6 +483,32 @@ toJson(xrpl::Number const& number)
|
||||
bool
|
||||
operator==(Value const&, Value const&);
|
||||
|
||||
/**
|
||||
* Compares a value with a string view, reading the value's characters in place
|
||||
* rather than building a Value from the view.
|
||||
*
|
||||
* Constrained to the exact type: a string literal converts equally well to a
|
||||
* view and to a Value, so a plain overload makes `value == "literal"`
|
||||
* ambiguous.
|
||||
*
|
||||
* @param x The value to compare.
|
||||
* @param y The characters to compare it against.
|
||||
* @return Whether `x` is a string whose characters up to its first NUL are
|
||||
* exactly the characters of `y`.
|
||||
*/
|
||||
template <class T>
|
||||
requires std::same_as<T, std::string_view>
|
||||
bool
|
||||
operator==(Value const& x, T y)
|
||||
{
|
||||
if (!x.isString())
|
||||
return false;
|
||||
|
||||
// A string `Value` can hold a null pointer, which names no characters, so it equals no view.
|
||||
char const* const s = x.asCString();
|
||||
return s != nullptr && std::string_view{s} == y;
|
||||
}
|
||||
|
||||
bool
|
||||
operator<(Value const&, Value const&);
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
@@ -212,7 +211,7 @@ canWithdraw(
|
||||
ReadView const& view,
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
AccountRootEntryR const& toSle,
|
||||
SLE::ConstRef toSle,
|
||||
STAmount const& amount,
|
||||
bool hasDestinationTag,
|
||||
std::optional<std::vector<UInt256>> const& credentialIDs = std::nullopt);
|
||||
|
||||
@@ -7,20 +7,9 @@
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
struct Adjustment
|
||||
{
|
||||
std::int32_t ownerCountDelta = 0;
|
||||
std::int32_t accountCountDelta = 0;
|
||||
};
|
||||
|
||||
template <typename ViewT>
|
||||
class AccountRootEntry : public SLEBase<ViewT, ltACCOUNT_ROOT>
|
||||
{
|
||||
@@ -38,123 +27,6 @@ public:
|
||||
: Base(keylet::account(id), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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
|
||||
reserve(Adjustment adj = {}) const;
|
||||
|
||||
/**
|
||||
* Return number of the objects which reserve is covered by the account (so called "owner
|
||||
* count"). Actual owner count can be adjusted by delta in ownerCountAdj.
|
||||
*
|
||||
* @param ownerCountAdj Adjustment to the owner count (default: 0)
|
||||
* @return The adjusted owner count
|
||||
*/
|
||||
[[nodiscard]] std::uint32_t
|
||||
ownerCount(std::int32_t ownerCountAdj = 0) const;
|
||||
|
||||
/**
|
||||
* Check if this account has sufficient reserve.
|
||||
*
|
||||
* @param ctx The apply-view context (view + tx)
|
||||
* @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 insufReserveCode The transaction result code to return if the reserve is
|
||||
* insufficient (default: tecINSUFFICIENT_RESERVE).
|
||||
* @return Transaction result code
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkReserve(
|
||||
ApplyViewContext ctx,
|
||||
XRPAmount accBalance,
|
||||
std::optional<AccountRootEntry<ReadView>> const& sponsorSle,
|
||||
Adjustment adj,
|
||||
TER insufReserveCode = tecINSUFFICIENT_RESERVE) const;
|
||||
|
||||
/**
|
||||
* Check if this account has sufficient reserve, deriving the sponsor internally.
|
||||
*
|
||||
* Equivalent to the overload above, but resolves the sponsor via
|
||||
* getEffectiveTxReserveSponsor(ctx, *this) instead of taking it explicitly. Use this
|
||||
* in the common case where the sponsor is simply the transaction's reserve
|
||||
* sponsor for this account. 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 accBalance The account's balance
|
||||
* @param adj Reserve adjustments (owner/account count deltas)
|
||||
* @return Transaction result code
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkReserve(ApplyViewContext ctx, XRPAmount accBalance, Adjustment adj) const;
|
||||
|
||||
/**
|
||||
* 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 sponsorSle The sponsor's ledger entry (if applicable)
|
||||
* @param count Amount to add to the owner count
|
||||
*/
|
||||
void
|
||||
increaseOwnerCount(std::optional<AccountRootEntry<ApplyView>>& sponsorSle, std::uint32_t count)
|
||||
requires Base::kIsWritable;
|
||||
|
||||
/**
|
||||
* Increase owner-count fields, deriving the tx reserve sponsor internally.
|
||||
*
|
||||
* Equivalent to the overload above, but resolves the sponsor via
|
||||
* getEffectiveTxReserveSponsor(ctx, *this) instead of taking it explicitly. Use
|
||||
* this when the sponsor is the transaction's reserve sponsor for this account
|
||||
* (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 count Amount to add to the owner count
|
||||
*/
|
||||
void
|
||||
increaseOwnerCount(ApplyViewContext ctx, std::uint32_t count)
|
||||
requires Base::kIsWritable;
|
||||
|
||||
/**
|
||||
* 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 sponsorSle The sponsor's ledger entry (if applicable)
|
||||
* @param count Amount to remove from the owner count
|
||||
*/
|
||||
void
|
||||
decreaseOwnerCount(std::optional<AccountRootEntry<ApplyView>>& sponsorSle, std::uint32_t count)
|
||||
requires Base::kIsWritable;
|
||||
|
||||
private:
|
||||
void
|
||||
adjustOwnerCountSigned(
|
||||
std::optional<AccountRootEntry<ApplyView>>& sponsorSle,
|
||||
std::int32_t adjustment)
|
||||
requires Base::kIsWritable;
|
||||
};
|
||||
|
||||
using AccountRootEntryR = AccountRootEntry<ReadView>;
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -25,6 +31,52 @@ public:
|
||||
: Base(keylet::amendments(), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of amendments this entry reports as enabled.
|
||||
*
|
||||
* @return the set of enabled amendments.
|
||||
*/
|
||||
[[nodiscard]] std::set<UInt256>
|
||||
enabledAmendments() const
|
||||
{
|
||||
std::set<UInt256> amendments;
|
||||
|
||||
if (this->exists() && (*this)->isFieldPresent(sfAmendments))
|
||||
{
|
||||
auto const& v = (*this)->getFieldV256(sfAmendments);
|
||||
amendments.insert_range(v);
|
||||
}
|
||||
|
||||
return amendments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of amendments that have achieved majority, to the time
|
||||
* majority was reached.
|
||||
*
|
||||
* @return a map of amendment to the time majority was reached.
|
||||
*/
|
||||
[[nodiscard]] std::map<UInt256, NetClock::time_point>
|
||||
majorityAmendments() const
|
||||
{
|
||||
std::map<UInt256, NetClock::time_point> ret;
|
||||
|
||||
if (this->exists() && (*this)->isFieldPresent(sfMajorities))
|
||||
{
|
||||
using TimePoint = NetClock::time_point;
|
||||
using Duration = TimePoint::duration;
|
||||
|
||||
auto const majorities = (*this)->getFieldArray(sfMajorities);
|
||||
|
||||
for (auto const& m : majorities)
|
||||
{
|
||||
ret[m.getFieldH256(sfAmendment)] = TimePoint(Duration(m.getFieldU32(sfCloseTime)));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
using AmendmentsEntryR = AmendmentsEntry<ReadView>;
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STVector256.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -25,6 +31,31 @@ public:
|
||||
: Base(keylet::skip(), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a hash `diff` slots back from the most recent entry in the
|
||||
* sfHashes vector (diff == 0 is the most recent entry).
|
||||
*
|
||||
* Callers decide which skip list entry to read and how to translate a
|
||||
* target ledger sequence into `diff`; this only does the bounds check
|
||||
* and vector indexing shared by both the recent (stride 1) and distant
|
||||
* (stride 256) skip lists.
|
||||
*
|
||||
* @param diff how many slots back from the most recent hash to look up;
|
||||
* 0 is the most recent hash.
|
||||
* @return the hash at that slot, or std::nullopt if the entry does not
|
||||
* exist or `diff` is out of range.
|
||||
*/
|
||||
[[nodiscard]] std::optional<UInt256>
|
||||
hashAt(std::size_t diff) const
|
||||
{
|
||||
if (!this->exists())
|
||||
return std::nullopt;
|
||||
STVector256 const vec = (*this)->getFieldV256(sfHashes);
|
||||
if (vec.size() > diff)
|
||||
return vec[vec.size() - diff - 1];
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
using LedgerHashesEntryR = LedgerHashesEntry<ReadView>;
|
||||
|
||||
@@ -353,7 +353,7 @@ changeSpotPriceQuality(
|
||||
}
|
||||
if (auto const nTakerPaysPropose = (-b + root2(res)) / (2 * a); nTakerPaysPropose > 0)
|
||||
{
|
||||
auto const nTakerPays = [&]() {
|
||||
auto const nTakerPays = [&] {
|
||||
// The fee might make the AMM offer quality less than CLOB
|
||||
// quality. Therefore, AMM offer has to satisfy this constraint:
|
||||
// o / i >= q. Substituting o with swapAssetIn() gives: i <= O /
|
||||
@@ -372,8 +372,8 @@ changeSpotPriceQuality(
|
||||
auto const takerPays =
|
||||
toAmount<TIn>(getAsset(pool.in), nTakerPays, Number::RoundingMode::Upward);
|
||||
// should not fail
|
||||
if (auto amounts = TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
|
||||
Quality{amounts} < quality &&
|
||||
auto amounts = TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
|
||||
if (Quality{amounts} < quality &&
|
||||
!withinRelativeDistance(Quality{amounts}, quality, Number(1, -7)))
|
||||
{
|
||||
JLOG(j.error()) << "changeSpotPriceQuality failed: " << to_string(pool.in) << " "
|
||||
@@ -382,21 +382,19 @@ changeSpotPriceQuality(
|
||||
<< " " << to_string(amounts.out);
|
||||
Throw<std::runtime_error>("changeSpotPriceQuality failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j.trace()) << "changeSpotPriceQuality succeeded: " << to_string(pool.in) << " "
|
||||
<< to_string(pool.out) << " "
|
||||
<< " " << quality << " " << tfee << " " << to_string(amounts.in)
|
||||
<< " " << to_string(amounts.out);
|
||||
return amounts;
|
||||
}
|
||||
|
||||
JLOG(j.trace()) << "changeSpotPriceQuality succeeded: " << to_string(pool.in) << " "
|
||||
<< to_string(pool.out) << " "
|
||||
<< " " << quality << " " << tfee << " " << to_string(amounts.in) << " "
|
||||
<< to_string(amounts.out);
|
||||
return amounts;
|
||||
}
|
||||
JLOG(j.trace()) << "changeSpotPriceQuality calc failed: " << to_string(pool.in) << " "
|
||||
<< to_string(pool.out) << " " << quality << " " << tfee;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto amounts = [&]() {
|
||||
auto amounts = [&] {
|
||||
bool const inIntegral = getAsset(pool.in).integral();
|
||||
bool const outIntegral = getAsset(pool.out).integral();
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -46,55 +46,30 @@ isGlobalFrozen(ReadView const& view, AccountID const& issuer);
|
||||
[[nodiscard]] XRPAmount
|
||||
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j);
|
||||
|
||||
namespace detail {
|
||||
struct Adjustment
|
||||
{
|
||||
std::int32_t ownerCountDelta = 0;
|
||||
std::int32_t accountCountDelta = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* An owner count cannot be negative. If adjustment would cause a negative
|
||||
* owner count, clamp the owner count at 0. Similarly for overflow. This
|
||||
* adjustment allows the ownerCount to be adjusted up or down in multiple steps.
|
||||
* If id != std::nullopt, then do error reporting.
|
||||
* Returns the account reserve, in drops.
|
||||
*
|
||||
* Shared by AccountRootEntry::ownerCount() and the owner-count helpers.
|
||||
* 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 currentOwnerCount The owner count before adjustment
|
||||
* @param ownerCountAdj Positive to add to count, negative to reduce count
|
||||
* @param id The account ID to use for error reporting, or std::nullopt to
|
||||
* skip error reporting
|
||||
* @param view The ledger view to read from
|
||||
* @param sle The ledger entry for the account
|
||||
* @param j Journal for logging
|
||||
* @return The adjusted owner count
|
||||
* @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to
|
||||
* subtract.
|
||||
* @return The account reserve amount in drops
|
||||
*/
|
||||
std::uint32_t
|
||||
confineOwnerCount(
|
||||
std::uint32_t currentOwnerCount,
|
||||
std::int32_t ownerCountAdj,
|
||||
std::optional<AccountID> const& id = std::nullopt,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
|
||||
|
||||
/**
|
||||
* Clamps and writes an owner-count field of sle, then updates sle in view.
|
||||
*
|
||||
* Shared by AccountRootEntry and adjustLoanBrokerOwnerCount().
|
||||
*
|
||||
* @return The adjusted owner count
|
||||
*/
|
||||
std::uint32_t
|
||||
adjustOwnerCountImpl(
|
||||
ApplyView& view,
|
||||
SLE::Ref sle,
|
||||
SF_UINT32 const& sfield,
|
||||
AccountID const& accID,
|
||||
std::int32_t ownerCountAdj,
|
||||
beast::Journal j);
|
||||
|
||||
/**
|
||||
* Returns the number of account reserves funded by this account: 1 for itself
|
||||
* (0 if sponsored by another account) plus the count of accounts it sponsors.
|
||||
* Shared by AccountRootEntry::reserve() and xrpLiquid().
|
||||
*/
|
||||
std::uint32_t
|
||||
accountCountImpl(AccountRootEntryR const& sle, std::int32_t accountCountAdj, beast::Journal j);
|
||||
|
||||
} // namespace detail
|
||||
[[nodiscard]] XRPAmount
|
||||
accountReserve(ReadView const& view, SLE::ConstRef sle, beast::Journal j, Adjustment adj = {});
|
||||
|
||||
/**
|
||||
* Convenience overload that accepts AccountID instead of SLE.
|
||||
@@ -109,9 +84,114 @@ accountCountImpl(AccountRootEntryR const& sle, std::int32_t accountCountAdj, bea
|
||||
[[nodiscard]] inline XRPAmount
|
||||
accountReserve(ReadView const& view, AccountID const& id, beast::Journal j, Adjustment adj = {})
|
||||
{
|
||||
return AccountRootEntryR(id, view, j).reserve(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::ConstRef accSle,
|
||||
XRPAmount accBalance,
|
||||
SLE::ConstRef 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::ConstRef 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::ConstRef 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.
|
||||
*
|
||||
@@ -129,13 +209,36 @@ increaseOwnerCount(
|
||||
std::uint32_t count,
|
||||
beast::Journal j)
|
||||
{
|
||||
AccountRootEntryW accountSle(account, view, j);
|
||||
std::optional<AccountRootEntryW> sponsorSle;
|
||||
if (sponsor)
|
||||
sponsorSle.emplace(*sponsor, view);
|
||||
accountSle.increaseOwnerCount(sponsorSle, count);
|
||||
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.
|
||||
*
|
||||
@@ -153,11 +256,12 @@ decreaseOwnerCount(
|
||||
std::uint32_t count,
|
||||
beast::Journal j)
|
||||
{
|
||||
AccountRootEntryW accountSle(account, view, j);
|
||||
std::optional<AccountRootEntryW> sponsorSle;
|
||||
if (sponsor)
|
||||
sponsorSle.emplace(*sponsor, view);
|
||||
accountSle.decreaseOwnerCount(sponsorSle, count);
|
||||
decreaseOwnerCount(
|
||||
view,
|
||||
view.peek(keylet::account(account)),
|
||||
sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(),
|
||||
count,
|
||||
j);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,7 +281,7 @@ decreaseOwnerCount(
|
||||
void
|
||||
decreaseOwnerCountForObject(
|
||||
ApplyView& view,
|
||||
AccountRootEntryW& accountSle,
|
||||
SLE::Ref accountSle,
|
||||
SLE::Ref objectSle,
|
||||
std::uint32_t count,
|
||||
beast::Journal j);
|
||||
@@ -199,7 +303,7 @@ decreaseOwnerCountForObject(
|
||||
std::uint32_t count,
|
||||
beast::Journal j)
|
||||
{
|
||||
AccountRootEntryW accountSle(account, view);
|
||||
SLE::Ref accountSle = view.peek(keylet::account(account));
|
||||
decreaseOwnerCountForObject(view, accountSle, objectSle, count, j);
|
||||
}
|
||||
|
||||
@@ -263,7 +367,7 @@ getPseudoAccountFields();
|
||||
* - null pointer
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isPseudoAccount(AccountRootEntryR const& sleAcct);
|
||||
isPseudoAccount(SLE::const_pointer sleAcct);
|
||||
|
||||
/**
|
||||
* Convenience overload that reads the account from the view.
|
||||
@@ -271,7 +375,7 @@ isPseudoAccount(AccountRootEntryR const& sleAcct);
|
||||
[[nodiscard]] inline bool
|
||||
isPseudoAccount(ReadView const& view, AccountID const& accountId)
|
||||
{
|
||||
return isPseudoAccount(AccountRootEntryR(accountId, view));
|
||||
return isPseudoAccount(view.read(keylet::account(accountId)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -292,6 +396,6 @@ createPseudoAccount(ApplyView& view, UInt256 const& pseudoOwnerKey, SField const
|
||||
* - If the SLE requires a destination tag, checks that there is a tag.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkDestinationAndTag(AccountRootEntryR const& toSle, bool hasDestinationTag);
|
||||
checkDestinationAndTag(SLE::ConstRef toSle, bool hasDestinationTag);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
@@ -27,31 +29,290 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Before fixCleanup3_2_0, EscrowCancel passes the escrow SLE as sleDest.
|
||||
* Return what the old reserve check returned for it, without adopting a
|
||||
* non-AccountRoot SLE into an AccountRootEntry: the tx reserve sponsor's
|
||||
* error if the escrow owner submitted the tx, else tefINTERNAL.
|
||||
* Validate that @p account may lock @p amount of a token for later delivery
|
||||
* to @p dest.
|
||||
*
|
||||
* @param ctx the apply view context for the escrow transaction.
|
||||
* @param sleDest the non-AccountRoot SLE that was passed as the destination.
|
||||
* @return the tx reserve sponsor's error code if the escrow owner submitted
|
||||
* the tx, else tefINTERNAL.
|
||||
* The lock-side counterpart of escrowUnlockPreclaimHelper: every issuer
|
||||
* control (locking opt-in, authorization, freeze/lock, transferability,
|
||||
* spendable balance) that gates locking token value lives here, so any
|
||||
* transactor that locks funds applies the same rules. The signature is
|
||||
* view-based rather than PreclaimContext-based so it can also run from
|
||||
* doApply.
|
||||
*/
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
escrowLockPreclaimHelper(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal j);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowDestNotAccountResult(ApplyViewContext ctx, SLE::ConstRef sleDest)
|
||||
escrowLockPreclaimHelper<Issue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal j)
|
||||
{
|
||||
if (sleDest->getAccountID(sfAccount) == ctx.tx[sfAccount])
|
||||
{
|
||||
if (auto const sponsor = getTxReserveSponsor(ctx); !sponsor)
|
||||
return sponsor.error(); // LCOV_EXCL_LINE
|
||||
}
|
||||
return tefINTERNAL;
|
||||
auto const& issue = amount.get<Issue>();
|
||||
auto const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tecNO_PERMISSION
|
||||
if (issuer == account)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION
|
||||
auto const sleIssuer = view.read(keylet::account(issuer));
|
||||
if (!sleIssuer)
|
||||
return tecNO_ISSUER;
|
||||
if (!sleIssuer->isFlag(lsfAllowTrustLineLocking))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the account does not have a trustline to the issuer, return tecNO_LINE
|
||||
auto const sleRippleState = view.read(keylet::trustLine(account, issuer, issue.currency));
|
||||
if (!sleRippleState)
|
||||
return tecNO_LINE;
|
||||
|
||||
STAmount const balance = (*sleRippleState)[sfBalance];
|
||||
|
||||
// If balance is positive, issuer must have higher address than account
|
||||
if (balance > beast::kZero && issuer < account)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If balance is negative, issuer must have lower address than account
|
||||
if (balance < beast::kZero && issuer > account)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is authorized
|
||||
if (auto const ter = requireAuth(view, issue, account); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is authorized
|
||||
if (auto const ter = requireAuth(view, issue, dest); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecFROZEN
|
||||
if (isFrozen(view, account, issue))
|
||||
return tecFROZEN;
|
||||
|
||||
// If the issuer has frozen the destination, return tecFROZEN
|
||||
if (isFrozen(view, dest, issue))
|
||||
return tecFROZEN;
|
||||
|
||||
STAmount const spendableAmount =
|
||||
accountHolds(view, account, issue.currency, issuer, FreezeHandling::IgnoreFreeze, j);
|
||||
|
||||
// If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount <= beast::kZero)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the spendable amount is less than the amount, return
|
||||
// tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount < amount)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the amount is not addable to the balance, return tecPRECISION_LOSS
|
||||
if (!canAdd(spendableAmount, amount))
|
||||
return tecPRECISION_LOSS;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
template <>
|
||||
inline TER
|
||||
escrowLockPreclaimHelper<MPTIssue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal j)
|
||||
{
|
||||
AccountID const issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tecNO_PERMISSION
|
||||
if (issuer == account)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the lsfMPTCanEscrow is not enabled, return tecNO_PERMISSION
|
||||
if (!sleIssuance->isFlag(lsfMPTCanEscrow))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the issuer is not the same as the issuer of the mpt, return
|
||||
// tecNO_PERMISSION
|
||||
if (sleIssuance->getAccountID(sfIssuer) != issuer)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If the account does not have the mpt, return tecOBJECT_NOT_FOUND
|
||||
if (!view.exists(keylet::mptoken(issuanceKey.key, account)))
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is
|
||||
// authorized
|
||||
auto const& mptIssue = amount.get<MPTIssue>();
|
||||
if (auto const ter = requireAuth(view, mptIssue, account, AuthType::WeakAuth);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is
|
||||
// authorized
|
||||
if (auto const ter = requireAuth(view, mptIssue, dest, AuthType::WeakAuth); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecLOCKED
|
||||
if (isFrozen(view, account, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
// If the issuer has frozen the destination, return tecLOCKED
|
||||
if (isFrozen(view, dest, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
// If the mpt cannot be transferred, return tecNO_AUTH
|
||||
if (auto const ter = canTransfer(view, mptIssue, account, dest); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
STAmount const spendableAmount = accountHolds(
|
||||
view,
|
||||
account,
|
||||
amount.get<MPTIssue>(),
|
||||
FreezeHandling::IgnoreFreeze,
|
||||
AuthHandling::IgnoreAuth,
|
||||
j);
|
||||
|
||||
// If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount <= beast::kZero)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the spendable amount is less than the amount, return
|
||||
// tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount < amount)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
escrowLockApplyHelper(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowLockApplyHelper<Issue>(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal)
|
||||
{
|
||||
// Defensive: Issuer cannot create an escrow
|
||||
if (issuer == sender)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ter =
|
||||
directSendNoFee(view, sender, issuer, amount, !amount.holds<MPTIssue>(), journal);
|
||||
if (!isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowLockApplyHelper<MPTIssue>(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal)
|
||||
{
|
||||
// Defensive: Issuer cannot create an escrow
|
||||
if (issuer == sender)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ter = lockEscrowMPT(view, sender, amount, journal);
|
||||
if (!isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
escrowUnlockPreclaimHelper(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
bool checkFreeze = true);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowUnlockPreclaimHelper<Issue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
bool checkFreeze)
|
||||
{
|
||||
AccountID const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tesSUCCESS
|
||||
if (issuer == account)
|
||||
return tesSUCCESS;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is authorized
|
||||
if (auto const ter = requireAuth(view, amount.get<Issue>(), account); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has deep frozen the destination, return tecFROZEN
|
||||
if (checkFreeze &&
|
||||
isDeepFrozen(view, account, amount.get<Issue>().currency, amount.getIssuer()))
|
||||
return tecFROZEN;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowUnlockPreclaimHelper<MPTIssue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
bool checkFreeze)
|
||||
{
|
||||
AccountID const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tesSUCCESS
|
||||
if (issuer == account)
|
||||
return tesSUCCESS;
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is
|
||||
// authorized
|
||||
auto const& mptIssue = amount.get<MPTIssue>();
|
||||
if (auto const ter = requireAuth(view, mptIssue, account, AuthType::WeakAuth);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecLOCKED
|
||||
if (checkFreeze && isFrozen(view, account, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
@@ -81,9 +342,6 @@ escrowUnlockApplyHelper<Issue>(
|
||||
bool createAsset,
|
||||
beast::Journal journal)
|
||||
{
|
||||
auto const& issue = amount.get<Issue>();
|
||||
Keylet const trustLineKey = keylet::trustLine(receiver, issue);
|
||||
bool const recvLow = issuer > receiver;
|
||||
bool const senderIssuer = issuer == sender;
|
||||
bool const receiverIssuer = issuer == receiver;
|
||||
|
||||
@@ -93,20 +351,25 @@ escrowUnlockApplyHelper<Issue>(
|
||||
if (receiverIssuer)
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const& issue = amount.get<Issue>();
|
||||
Keylet const trustLineKey = keylet::trustLine(receiver, issue);
|
||||
bool const recvLow = issuer > receiver;
|
||||
|
||||
if (!ctx.view.exists(trustLineKey) && createAsset)
|
||||
{
|
||||
// Can the account cover the trust line's reserve?
|
||||
if (sleDest->getType() != ltACCOUNT_ROOT)
|
||||
return detail::escrowDestNotAccountResult(ctx, sleDest);
|
||||
auto const destSle = AccountRootEntryR(sleDest, ctx.view, journal);
|
||||
auto sponsorSle = getEffectiveTxReserveSponsor(ctx, destSle);
|
||||
auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest);
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
|
||||
auto& sponsor = *sponsorSle;
|
||||
|
||||
if (auto const ret = destSle.checkReserve(
|
||||
ctx, xrpBalance, sponsor, {.ownerCountDelta = 1}, tecNO_LINE_INSUF_RESERVE);
|
||||
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. "
|
||||
@@ -119,22 +382,22 @@ escrowUnlockApplyHelper<Issue>(
|
||||
initialBalance.get<Issue>().account = noAccount();
|
||||
|
||||
if (TER const ter = trustCreate(
|
||||
ctx.view, // payment sandbox
|
||||
recvLow, // is dest low?
|
||||
issuer, // source
|
||||
receiver, // destination
|
||||
trustLineKey.key, // ledger index
|
||||
sleDest, // Account to add to
|
||||
false, // authorize account
|
||||
!sleDest->isFlag(lsfDefaultRipple), //
|
||||
false, // freeze trust line
|
||||
false, // deep freeze trust line
|
||||
initialBalance, // zero initial balance
|
||||
Issue(currency, receiver), // limit of zero
|
||||
0, // quality in
|
||||
0, // quality out
|
||||
sponsor ? sponsor->mutableRawSle() : SLE::pointer(), // sponsor
|
||||
journal); // journal
|
||||
ctx.view, // payment sandbox
|
||||
recvLow, // is dest low?
|
||||
issuer, // source
|
||||
receiver, // destination
|
||||
trustLineKey.key, // ledger index
|
||||
sleDest, // Account to add to
|
||||
false, // authorize account
|
||||
!sleDest->isFlag(lsfDefaultRipple), //
|
||||
false, // freeze trust line
|
||||
false, // deep freeze trust line
|
||||
initialBalance, // zero initial balance
|
||||
Issue(currency, receiver), // limit of zero
|
||||
0, // quality in
|
||||
0, // quality out
|
||||
*sponsorSle, // sponsor
|
||||
journal); // journal
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
@@ -229,29 +492,23 @@ escrowUnlockApplyHelper<MPTIssue>(
|
||||
auto const mptKeylet = keylet::mptoken(issuanceKey.key, receiver);
|
||||
if (!ctx.view.exists(mptKeylet) && createAsset && !receiverIssuer)
|
||||
{
|
||||
if (sleDest->getType() != ltACCOUNT_ROOT)
|
||||
return detail::escrowDestNotAccountResult(ctx, sleDest);
|
||||
auto const destSle = AccountRootEntryR(sleDest, ctx.view, journal);
|
||||
auto sponsorSle = getEffectiveTxReserveSponsor(ctx, destSle);
|
||||
auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest);
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
|
||||
auto& sponsor = *sponsorSle;
|
||||
|
||||
if (auto const ret = destSle.checkReserve(ctx, xrpBalance, sponsor, {.ownerCountDelta = 1});
|
||||
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, sponsor ? sponsor->mutableRawSle() : SLE::pointer(), 0);
|
||||
if (auto const ter = createMPToken(ctx.view, mptID, receiver, *sponsorSle, 0);
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
// update owner count.
|
||||
AccountRootEntryW receiverSle(receiver, ctx.view, journal);
|
||||
receiverSle.increaseOwnerCount(sponsor, 1);
|
||||
increaseOwnerCount(ctx.view, sleDest, *sponsorSle, 1, journal);
|
||||
}
|
||||
|
||||
if (!ctx.view.exists(mptKeylet) && !receiverIssuer)
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -55,11 +54,11 @@ 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 entry, nullopt if the tx is not
|
||||
* @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<std::optional<AccountRootEntryW>, TER>
|
||||
std::expected<SLE::pointer, TER>
|
||||
getTxReserveSponsor(ApplyViewContext ctx);
|
||||
|
||||
/**
|
||||
@@ -67,29 +66,29 @@ getTxReserveSponsor(ApplyViewContext ctx);
|
||||
*
|
||||
* @param view The ledger read view
|
||||
* @param tx The transaction to inspect
|
||||
* @return The sponsor account entry, nullopt if the tx is not
|
||||
* @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<std::optional<AccountRootEntryR>, TER>
|
||||
std::expected<SLE::const_pointer, TER>
|
||||
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 entry only when accountSle is the tx's
|
||||
* own (non-pseudo) account; otherwise it returns nullopt. This is
|
||||
* 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 entry (nullopt if unsponsored), or tecINTERNAL if the
|
||||
* @return The sponsor SLE (nullptr if unsponsored), or tecINTERNAL if the
|
||||
* sponsor account cannot be loaded (an already-checked invariant)
|
||||
*/
|
||||
[[nodiscard]] std::expected<std::optional<AccountRootEntryW>, TER>
|
||||
getEffectiveTxReserveSponsor(ApplyViewContext ctx, AccountRootEntryR const& accountSle);
|
||||
[[nodiscard]] std::expected<SLE::pointer, TER>
|
||||
getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::ConstRef accountSle);
|
||||
|
||||
/**
|
||||
* Return the AccountID stored in the given sponsor field of a ledger entry, or nullopt if absent.
|
||||
@@ -106,9 +105,9 @@ getLedgerEntryReserveSponsorID(SLE::ConstRef sle, SF_ACCOUNT const& field = sfSp
|
||||
* @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 entry, or nullopt if the entry is unsponsored.
|
||||
* @return The sponsor account SLE, or a null pointer if the entry is unsponsored.
|
||||
*/
|
||||
std::optional<AccountRootEntryW>
|
||||
SLE::pointer
|
||||
getLedgerEntryReserveSponsor(
|
||||
ApplyView& view,
|
||||
SLE::ConstRef sle,
|
||||
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
void
|
||||
asyncHandshake(HandshakeType type, Callback cbFunc)
|
||||
{
|
||||
if ((type == SslSocket::client) || (secure_))
|
||||
if ((type == SslSocket::client) || secure_)
|
||||
{
|
||||
// must be ssl
|
||||
secure_ = true;
|
||||
|
||||
@@ -67,7 +67,7 @@ class EncodedBlob
|
||||
|
||||
public:
|
||||
explicit EncodedBlob(std::shared_ptr<NodeObject> const& obj)
|
||||
: size_([&obj]() {
|
||||
: size_([&obj] {
|
||||
XRPL_ASSERT(obj, "xrpl::node_store::EncodedBlob::EncodedBlob : non-null input");
|
||||
|
||||
if (!obj)
|
||||
|
||||
@@ -6,31 +6,25 @@
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* API version numbers used in later API versions
|
||||
* The `api_version` numbers this server serves.
|
||||
*
|
||||
* Requests with a version number in the range
|
||||
* [apiMinimumSupportedVersion, apiMaximumSupportedVersion]
|
||||
* are supported.
|
||||
* A request naming a version in [kApiMinimumSupportedVersion,
|
||||
* kApiMaximumSupportedVersion] is served. With `[beta_rpc_api]` set to `1` in
|
||||
* the config the range extends to kApiBetaVersion.
|
||||
*
|
||||
* If [beta_rpc_api] is enabled in config, the version numbers
|
||||
* in the range [apiMinimumSupportedVersion, apiBetaVersion]
|
||||
* are supported.
|
||||
*
|
||||
* Network Requests without explicit version numbers use
|
||||
* apiVersionIfUnspecified. apiVersionIfUnspecified is 1,
|
||||
* because all the RPC requests with a version >= 2 must
|
||||
* explicitly specify the version in the requests.
|
||||
* Note that apiVersionIfUnspecified will be lower than
|
||||
* apiMinimumSupportedVersion when we stop supporting API
|
||||
* version 1.
|
||||
*
|
||||
* Command line Requests use apiCommandLineVersion.
|
||||
* A request naming no version is served at kApiVersionIfUnspecified, which is 1
|
||||
* because a request wanting any later version states it. A WebSocket message is
|
||||
* one flat object and names it at its top level at every version. A JSON-RPC
|
||||
* request names it in its parameters, or at its own top level: an entry of a
|
||||
* `"method": "batch"` body at every version, a lone request from
|
||||
* kApiMinimumSpecVersion up.
|
||||
*/
|
||||
|
||||
namespace rpc {
|
||||
@@ -46,6 +40,31 @@ static constexpr auto kApiCommandLineVersion = kApiVersion<1>; // TODO Bump to
|
||||
static constexpr auto kApiBetaVersion = kApiVersion<3>;
|
||||
static constexpr auto kApiMaximumValidVersion = kApiBetaVersion;
|
||||
|
||||
/**
|
||||
* First API version that follows the JSON-RPC 2.0 specification.
|
||||
*
|
||||
* From this version up a lone JSON-RPC request may state its `api_version` at
|
||||
* its own top level, beside the members the specification defines there, as a
|
||||
* WebSocket message always has. The member is an XRPL extension: the
|
||||
* specification defines no version member.
|
||||
*
|
||||
* Distinct from kApiBetaVersion, the same number today, which says instead that
|
||||
* the version needs [beta_rpc_api] to be reachable.
|
||||
*/
|
||||
static constexpr auto kApiMinimumSpecVersion = kApiVersion<3>;
|
||||
|
||||
/**
|
||||
* Whether @p apiVersion follows the JSON-RPC 2.0 specification.
|
||||
*
|
||||
* @param apiVersion The version a request asked for.
|
||||
* @return Whether that version follows the specification.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool
|
||||
isSpecVersion(unsigned apiVersion)
|
||||
{
|
||||
return apiVersion >= kApiMinimumSpecVersion;
|
||||
}
|
||||
|
||||
static_assert(kApiInvalidVersion < kApiMinimumSupportedVersion);
|
||||
static_assert(
|
||||
kApiVersionIfUnspecified >= kApiMinimumSupportedVersion &&
|
||||
@@ -57,6 +76,16 @@ static_assert(kApiMaximumSupportedVersion >= kApiMinimumSupportedVersion);
|
||||
static_assert(kApiBetaVersion >= kApiMaximumSupportedVersion);
|
||||
static_assert(kApiMaximumValidVersion >= kApiMaximumSupportedVersion);
|
||||
|
||||
/**
|
||||
* Values accepted in the `ripplerpc` request field, which selects the shape of
|
||||
* the JSON-RPC reply envelope. Distinct from `kJsonRpcVersion` in JsonRpc.h,
|
||||
* which names the JSON-RPC protocol itself, and from the `api_version`
|
||||
* constants above, which select the content of the response.
|
||||
*/
|
||||
inline constexpr std::string_view kRippleRpcVersion1{"1.0"};
|
||||
inline constexpr std::string_view kRippleRpcVersion2{"2.0"};
|
||||
inline constexpr std::string_view kRippleRpcVersion3{"3.0"};
|
||||
|
||||
inline void
|
||||
setVersion(json::Value& parent, unsigned int apiVersion, bool betaEnabled)
|
||||
{
|
||||
|
||||
@@ -55,7 +55,7 @@ hash_append(Hasher& h, Book const& b)
|
||||
using beast::hash_append;
|
||||
hash_append(h, b.in, b.out);
|
||||
if (b.domain)
|
||||
hash_append(h, *(b.domain));
|
||||
hash_append(h, *b.domain);
|
||||
}
|
||||
|
||||
Book
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -144,7 +145,58 @@ enum ErrorCodeI {
|
||||
RpcEntryNotFound = 98,
|
||||
RpcUnexpectedLedgerType = 99,
|
||||
|
||||
RpcLast = RpcUnexpectedLedgerType // rpcLAST should always equal the last code.
|
||||
// submit + simulate
|
||||
RpcInvalidTransaction = 100,
|
||||
RpcInternalSubmit = 101,
|
||||
RpcInternalJson = 102,
|
||||
RpcInternalSimulate = 103,
|
||||
|
||||
// transaction_entry
|
||||
RpcFieldNotFoundTransaction = 104,
|
||||
RpcNotYetImplemented = 105,
|
||||
RpcTransactionNotFound = 106,
|
||||
|
||||
// transaction_entry + ledger_entry
|
||||
RpcMalformedRequest = 107,
|
||||
|
||||
// ledger_accept
|
||||
RpcNotStandAlone = 108,
|
||||
|
||||
// ledger_entry, API version 1 only
|
||||
RpcUnknownOption = 109,
|
||||
|
||||
// ledger_entry field validation, one code per malformed field. The ledger_entry helpers report
|
||||
// invalidParams (31) for all of them, so each token needs a row of its own. Alphabetical by
|
||||
// token here only: the enum is append-only once a code ships.
|
||||
//
|
||||
// `malformedIssue` duplicates `issueMalformed` (93): same message, same status, two tokens,
|
||||
// both on the wire already. Do not add a third spelling.
|
||||
RpcMalformedAccount = 110,
|
||||
RpcMalformedAddress = 111,
|
||||
RpcMalformedAuthorized = 112,
|
||||
RpcMalformedAuthorizedCredentials = 113,
|
||||
RpcMalformedBridgeAccount = 114,
|
||||
RpcMalformedBroker = 115,
|
||||
RpcMalformedCurrency = 116,
|
||||
RpcMalformedDirRoot = 117,
|
||||
RpcMalformedDocumentID = 118,
|
||||
RpcMalformedIssue = 119,
|
||||
RpcMalformedIssuingChainDoor = 120,
|
||||
RpcMalformedLockingChainDoor = 121,
|
||||
RpcMalformedMPTIssuanceID = 122,
|
||||
RpcMalformedMPTokenIssuance = 123,
|
||||
RpcMalformedOwner = 124,
|
||||
RpcMalformedSeq = 125,
|
||||
RpcMalformedSponsee = 126,
|
||||
RpcMalformedSponsor = 127,
|
||||
RpcMalformedXChainOwnedClaimID = 128,
|
||||
RpcMalformedXChainOwnedCreateAccountClaimID = 129,
|
||||
|
||||
// subscribe
|
||||
RpcApiVersionConflict = 130,
|
||||
|
||||
// RpcLast should always equal the last code.
|
||||
RpcLast = RpcApiVersionConflict
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -180,11 +232,6 @@ struct ErrorInfo
|
||||
{
|
||||
}
|
||||
|
||||
constexpr ErrorInfo(ErrorCodeI code, char const* token, char const* message)
|
||||
: code(code), token(token), message(message), httpStatus(200)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr ErrorInfo(ErrorCodeI code, char const* token, char const* message, int httpStatus)
|
||||
: code(code), token(token), message(message), httpStatus(httpStatus)
|
||||
{
|
||||
@@ -202,6 +249,18 @@ struct ErrorInfo
|
||||
ErrorInfo const&
|
||||
getErrorInfo(ErrorCodeI code);
|
||||
|
||||
/**
|
||||
* Returns the error code that owns @p token.
|
||||
*
|
||||
* A linear scan over views measured at compile time, run once per error reply.
|
||||
* A duplicate token is a build error, so the answer is never ambiguous.
|
||||
*
|
||||
* @param token The error token to resolve.
|
||||
* @return The code the table gives @p token, or RpcUnknown if no row names it.
|
||||
*/
|
||||
ErrorCodeI
|
||||
codeForToken(std::string_view token);
|
||||
|
||||
/**
|
||||
* Add or update the json update to reflect the error code.
|
||||
*/
|
||||
|
||||
45
include/xrpl/protocol/JsonRpc.h
Normal file
45
include/xrpl/protocol/JsonRpc.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/json/json_forwards.h>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace xrpl::rpc {
|
||||
|
||||
/**
|
||||
* Constants of the JSON-RPC 2.0 protocol itself.
|
||||
*
|
||||
* Kept apart from the `api_version` and `ripplerpc` constants in ApiVersion.h,
|
||||
* which go when support for API versions 1 and 2 goes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Value of the `jsonrpc` member of a request and of its reply, fixed at "2.0"
|
||||
* by the JSON-RPC specification.
|
||||
*/
|
||||
inline constexpr std::string_view kJsonRpcVersion{"2.0"};
|
||||
|
||||
/**
|
||||
* Codes for the `code` member of a JSON-RPC error object.
|
||||
*
|
||||
* The specification reserves -32768 to -32000 for the protocol and leaves
|
||||
* -32000 to -32099 of it to the implementation.
|
||||
*
|
||||
* kJsonRpcServerError is the code for an error an XRPL handler reports. The
|
||||
* XRPL token and code travel in the error's `data` member, so renumbering an
|
||||
* XRPL error cannot change a JSON-RPC code.
|
||||
*
|
||||
* The codes from kJsonRpcServerOverloaded on lie outside the
|
||||
* implementation-defined sub-range, which the specification does not allow.
|
||||
* They are the codes every shipped version reports, so moving one breaks the
|
||||
* clients matching on it.
|
||||
*/
|
||||
inline constexpr json::Int kJsonRpcServerError{-32000};
|
||||
inline constexpr json::Int kJsonRpcInvalidRequest{-32600};
|
||||
inline constexpr json::Int kJsonRpcMethodNotFound{-32601};
|
||||
inline constexpr json::Int kJsonRpcInvalidParams{-32602};
|
||||
inline constexpr json::Int kJsonRpcServerOverloaded{-32604};
|
||||
inline constexpr json::Int kJsonRpcForbidden{-32605};
|
||||
inline constexpr json::Int kJsonRpcWrongVersion{-32606};
|
||||
|
||||
} // namespace xrpl::rpc
|
||||
@@ -208,7 +208,10 @@ enum LedgerEntryType : std::uint16_t {
|
||||
\
|
||||
LEDGER_OBJECT(Sponsorship, \
|
||||
LSF_FLAG(lsfSponsorshipRequireSignForFee, 0x00010000) \
|
||||
LSF_FLAG(lsfSponsorshipRequireSignForReserve, 0x00020000))
|
||||
LSF_FLAG(lsfSponsorshipRequireSignForReserve, 0x00020000)) \
|
||||
\
|
||||
LEDGER_OBJECT(LoanBroker, \
|
||||
LSF_FLAG(lsfLoanBrokerPrivate, 0x00010000))
|
||||
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Enumeration of ledger shortcuts for specifying which ledger to use.
|
||||
*
|
||||
* These shortcuts provide a convenient way to reference commonly used ledgers
|
||||
* without needing to specify their exact hash or sequence number.
|
||||
*/
|
||||
enum class LedgerShortcut {
|
||||
/**
|
||||
* The current working ledger (open, not yet closed)
|
||||
*/
|
||||
Current,
|
||||
|
||||
/**
|
||||
* The most recently closed ledger (may not be validated)
|
||||
*/
|
||||
Closed,
|
||||
|
||||
/**
|
||||
* The most recently validated ledger
|
||||
*/
|
||||
Validated
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -457,9 +457,7 @@ private:
|
||||
// The remove_cv and remove_reference are necessitated by the STBitString
|
||||
// types. Their value() returns by const ref. We return those types
|
||||
// by value.
|
||||
template <
|
||||
typename T,
|
||||
typename V = std::remove_cv_t<std::remove_reference_t<decltype(std::declval<T>().value())>>>
|
||||
template <typename T, typename V = std::remove_cvref_t<decltype(std::declval<T>().value())>>
|
||||
V
|
||||
getFieldByValue(SField const& field) const;
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ private:
|
||||
template <class LookupNodeID>
|
||||
STValidation::STValidation(SerialIter& sit, LookupNodeID&& lookupNodeID, DeserializeOptions options)
|
||||
: STObject(validationFormat(), sit, sfValidation, options.requireCanonicalOrder)
|
||||
, signingPubKey_([this]() {
|
||||
, signingPubKey_([this] {
|
||||
auto const spk = getFieldVL(sfSigningPubKey);
|
||||
|
||||
if (publicKeyType(makeSlice(spk)) != KeyType::Secp256k1)
|
||||
|
||||
@@ -252,7 +252,7 @@ public:
|
||||
{
|
||||
auto success = (offset + (Bits / 8)) <= data_.size();
|
||||
if (success)
|
||||
memcpy(data.begin(), &(data_.front()) + offset, (Bits / 8));
|
||||
memcpy(data.begin(), &data_.front() + offset, (Bits / 8));
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
@@ -449,7 +449,7 @@ public:
|
||||
// Trait tells the requires-clause which types are allowed for construction.
|
||||
template <typename T>
|
||||
constexpr TERSubset(T rhs)
|
||||
requires(Trait<std::remove_cv_t<std::remove_reference_t<T>>>::value)
|
||||
requires(Trait<std::remove_cvref_t<T>>::value)
|
||||
: code_(TERtoInt(rhs))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -226,6 +226,10 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal;
|
||||
TF_FLAG(tfLoanUnimpair, 0x00040000), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(LoanBrokerSet, \
|
||||
TF_FLAG(tfLoanBrokerPrivate, 0x00010000), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(SponsorshipSet, \
|
||||
TF_FLAG(tfSponsorshipSetRequireSignForFee, 0x00010000) \
|
||||
TF_FLAG(tfSponsorshipClearRequireSignForFee, 0x00020000) \
|
||||
|
||||
@@ -20,6 +20,7 @@ XRPL_FEATURE(SmartEscrow, Supported::No, VoteBehavior::DefaultN
|
||||
XRPL_FEATURE(LendingProtocolV1_2, Supported::No, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (Cleanup3_5_0, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(ConfidentialMPTKeyRotation, Supported::No, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (BatchV1_2, Supported::Yes, VoteBehavior::DefaultYes)
|
||||
XRPL_FIX (Cleanup3_4_0, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(Sponsor, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(BatchV1_1, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -549,6 +549,7 @@ LEDGER_ENTRY(ltLOAN_BROKER, 0x0088, LoanBroker, loan_broker, ({
|
||||
{sfCoverAvailable, SoeDefault},
|
||||
{sfCoverRateMinimum, SoeDefault},
|
||||
{sfCoverRateLiquidation, SoeDefault},
|
||||
{sfDomainID, SoeOptional},
|
||||
}))
|
||||
|
||||
/** A ledger object representing a loan between a Borrower and a Loan Broker
|
||||
|
||||
@@ -900,6 +900,7 @@ TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet,
|
||||
{sfDebtMaximum, SoeOptional},
|
||||
{sfCoverRateMinimum, SoeOptional},
|
||||
{sfCoverRateLiquidation, SoeOptional},
|
||||
{sfDomainID, SoeOptional},
|
||||
}))
|
||||
|
||||
/** This transaction deletes a Loan Broker */
|
||||
|
||||
@@ -245,7 +245,7 @@ JSS(ephemeral_key); // out: ValidatorInfo
|
||||
JSS(error); // out: error
|
||||
JSS(errored); //
|
||||
JSS(error_code); // out: error
|
||||
JSS(error_exception); // out: Submit
|
||||
JSS(error_exception); // out: Submit, Simulate
|
||||
JSS(error_message); // out: error
|
||||
JSS(expand); // in: handler/Ledger
|
||||
JSS(expected_date); // out: any (warnings)
|
||||
@@ -559,7 +559,7 @@ JSS(signing_key); // out: NetworkOPs
|
||||
JSS(signing_keys); // out: ValidatorList
|
||||
JSS(signing_time); // out: NetworkOPs
|
||||
JSS(signer_lists); // in/out: AccountInfo
|
||||
JSS(size); // out: get_aggregate_price
|
||||
JSS(size); // out: get_aggregate_price, ServerHandler
|
||||
JSS(snapshot); // in: Subscribe
|
||||
JSS(source_account); // in: PathRequest, RipplePathFind
|
||||
JSS(source_amount); // in: PathRequest, RipplePathFind
|
||||
|
||||
@@ -335,6 +335,30 @@ public:
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfCoverRateLiquidation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfDomainID (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT256::type::value_type>
|
||||
getDomainID() const
|
||||
{
|
||||
if (hasDomainID())
|
||||
return this->sle_->at(sfDomainID);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfDomainID is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasDomainID() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfDomainID);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -578,6 +602,17 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfDomainID (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
LoanBrokerBuilder&
|
||||
setDomainID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
|
||||
{
|
||||
object_[sfDomainID] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the completed LoanBroker wrapper.
|
||||
* @param index The ledger entry index.
|
||||
|
||||
@@ -213,6 +213,32 @@ public:
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfCoverRateLiquidation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfDomainID (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT256::type::value_type>
|
||||
getDomainID() const
|
||||
{
|
||||
if (hasDomainID())
|
||||
{
|
||||
return this->tx_->at(sfDomainID);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfDomainID is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasDomainID() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfDomainID);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -336,6 +362,17 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfDomainID (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
LoanBrokerSetBuilder&
|
||||
setDomainID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
|
||||
{
|
||||
object_[sfDomainID] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the LoanBrokerSet wrapper.
|
||||
* @param publicKey The public key for signing.
|
||||
|
||||
@@ -178,7 +178,7 @@ public:
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
LockedSociSession session = perf::measureDurationAndLog(
|
||||
[&]() { return LockedSociSession(session_, lock_); }, "checkoutDb", 10ms, j_);
|
||||
[&] { return LockedSociSession(session_, lock_); }, "checkoutDb", 10ms, j_);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/LedgerHeader.h>
|
||||
#include <xrpl/protocol/LedgerShortcut.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/TxMeta.h>
|
||||
#include <xrpl/protocol/TxSearched.h>
|
||||
@@ -105,21 +104,6 @@ public:
|
||||
using TxnMetaLedgerType = std::tuple<Blob, Blob, std::uint32_t>;
|
||||
using MetaTxsList = std::vector<TxnMetaLedgerType>;
|
||||
|
||||
using LedgerSequence = uint32_t;
|
||||
using LedgerHash = UInt256;
|
||||
using LedgerSpecifier = std::variant<LedgerRange, LedgerShortcut, LedgerSequence, LedgerHash>;
|
||||
|
||||
struct AccountTxArgs
|
||||
{
|
||||
AccountID account;
|
||||
std::optional<LedgerSpecifier> ledger;
|
||||
bool binary = false;
|
||||
bool forward = false;
|
||||
uint32_t limit = 0;
|
||||
std::optional<AccountTxMarker> marker;
|
||||
std::optional<DelegateFilter> delegate;
|
||||
};
|
||||
|
||||
struct AccountTxResult
|
||||
{
|
||||
std::variant<AccountTxs, MetaTxsList> transactions;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -113,7 +114,11 @@ public:
|
||||
// you get transactions as they occur or once their
|
||||
// results are confirmed
|
||||
virtual void
|
||||
subAccount(Ref ispListener, HashSet<AccountID> const& vnaAccountIDs, bool realTime) = 0;
|
||||
subAccount(
|
||||
Ref ispListener,
|
||||
HashSet<AccountID> const& vnaAccountIDs,
|
||||
bool realTime,
|
||||
unsigned int apiVersion) = 0;
|
||||
|
||||
// for normal use, removes from InfoSub and server
|
||||
virtual void
|
||||
@@ -130,10 +135,12 @@ public:
|
||||
/**
|
||||
* subscribe an account's new transactions and retrieve the account's
|
||||
* historical transactions
|
||||
* @param apiVersion The version the subscription is registered at,
|
||||
* which is the version its messages are shaped for.
|
||||
* @return rpcSUCCESS if successful, otherwise an error code
|
||||
*/
|
||||
virtual ErrorCodeI
|
||||
subAccountHistory(Ref ispListener, AccountID const& account) = 0;
|
||||
subAccountHistory(Ref ispListener, AccountID const& account, unsigned int apiVersion) = 0;
|
||||
|
||||
/**
|
||||
* unsubscribe an account's transactions
|
||||
@@ -196,30 +203,34 @@ public:
|
||||
HashSet<AccountID> historyAccounts) = 0;
|
||||
|
||||
// VFALCO TODO Document the bool return value
|
||||
//
|
||||
// Every sub* below takes the version the subscription is registered at, which is the
|
||||
// version its messages are shaped for. Registering the same subscription again replaces
|
||||
// the entry, so the newest registration decides that subscription's version.
|
||||
virtual bool
|
||||
subLedger(Ref ispListener, json::Value& jvResult) = 0;
|
||||
subLedger(Ref ispListener, json::Value& jvResult, unsigned int apiVersion) = 0;
|
||||
virtual bool
|
||||
unsubLedger(std::uint64_t uListener) = 0;
|
||||
|
||||
virtual bool
|
||||
subBookChanges(Ref ispListener) = 0;
|
||||
subBookChanges(Ref ispListener, unsigned int apiVersion) = 0;
|
||||
virtual bool
|
||||
unsubBookChanges(std::uint64_t uListener) = 0;
|
||||
|
||||
virtual bool
|
||||
subManifests(Ref ispListener) = 0;
|
||||
subManifests(Ref ispListener, unsigned int apiVersion) = 0;
|
||||
virtual bool
|
||||
unsubManifests(std::uint64_t uListener) = 0;
|
||||
virtual void
|
||||
pubManifest(Manifest const&) = 0;
|
||||
|
||||
virtual bool
|
||||
subServer(Ref ispListener, json::Value& jvResult, bool admin) = 0;
|
||||
subServer(Ref ispListener, json::Value& jvResult, bool admin, unsigned int apiVersion) = 0;
|
||||
virtual bool
|
||||
unsubServer(std::uint64_t uListener) = 0;
|
||||
|
||||
virtual bool
|
||||
subBook(Ref ispListener, Book const&) = 0;
|
||||
subBook(Ref ispListener, Book const&, unsigned int apiVersion) = 0;
|
||||
|
||||
/**
|
||||
* Remove a book subscription for a live subscriber.
|
||||
@@ -248,7 +259,8 @@ public:
|
||||
* InfoSub::bookSubscriptions_ because the InfoSub is being destroyed.
|
||||
* Called by ~InfoSub() for each book in bookSubscriptions_.
|
||||
*
|
||||
* @param uListener The sequence number of the subscriber being torn down.
|
||||
* @param uListener The sequence number of the subscriber being torn
|
||||
* down.
|
||||
* @param book The order book entry to remove.
|
||||
* @return true if the entry was present and removed, false otherwise
|
||||
* (e.g., already removed by a concurrent RPC unsubscribe).
|
||||
@@ -259,35 +271,35 @@ public:
|
||||
unsubBookInternal(std::uint64_t uListener, Book const&) = 0;
|
||||
|
||||
virtual bool
|
||||
subTransactions(Ref ispListener) = 0;
|
||||
subTransactions(Ref ispListener, unsigned int apiVersion) = 0;
|
||||
virtual bool
|
||||
unsubTransactions(std::uint64_t uListener) = 0;
|
||||
|
||||
virtual bool
|
||||
subRTTransactions(Ref ispListener) = 0;
|
||||
subRTTransactions(Ref ispListener, unsigned int apiVersion) = 0;
|
||||
virtual bool
|
||||
unsubRTTransactions(std::uint64_t uListener) = 0;
|
||||
|
||||
virtual bool
|
||||
subValidations(Ref ispListener) = 0;
|
||||
subValidations(Ref ispListener, unsigned int apiVersion) = 0;
|
||||
virtual bool
|
||||
unsubValidations(std::uint64_t uListener) = 0;
|
||||
|
||||
virtual bool
|
||||
subPeerStatus(Ref ispListener) = 0;
|
||||
subPeerStatus(Ref ispListener, unsigned int apiVersion) = 0;
|
||||
|
||||
virtual bool
|
||||
unsubPeerStatus(std::uint64_t uListener) = 0;
|
||||
virtual void
|
||||
pubPeerStatus(std::function<json::Value(void)> const&) = 0;
|
||||
pubPeerStatus(std::function<json::Value()> const&) = 0;
|
||||
|
||||
virtual bool
|
||||
subConsensus(Ref ispListener) = 0;
|
||||
subConsensus(Ref ispListener, unsigned int apiVersion) = 0;
|
||||
virtual bool
|
||||
unsubConsensus(std::uint64_t uListener) = 0;
|
||||
|
||||
virtual void
|
||||
subMPT(InfoSub::Ref ispListener, HashSet<MPTID> const& mptIDs) = 0;
|
||||
subMPT(InfoSub::Ref ispListener, HashSet<MPTID> const& mptIDs, unsigned int apiVersion) = 0;
|
||||
virtual void
|
||||
unsubMPT(InfoSub::Ref ispListener, HashSet<MPTID> const& mptIDs) = 0;
|
||||
|
||||
@@ -385,8 +397,9 @@ public:
|
||||
/**
|
||||
* Whether this connection already tracks an account-history for @p account.
|
||||
*
|
||||
* `doSubscribe` reads this to charge the cap for an account_history_tx_stream
|
||||
* only when it is net-new, matching the account branches.
|
||||
* `doSubscribe` reads this to charge the cap for an
|
||||
* account_history_tx_stream only when it is net-new, matching the account
|
||||
* branches.
|
||||
*
|
||||
* @param account The account an account_history_tx_stream would add.
|
||||
* @return true if @p account is already in the account-history set.
|
||||
@@ -447,11 +460,26 @@ public:
|
||||
std::shared_ptr<InfoSubRequest> const&
|
||||
getRequest();
|
||||
|
||||
void
|
||||
setApiVersion(unsigned int apiVersion);
|
||||
|
||||
[[nodiscard]] unsigned int
|
||||
getApiVersion() const noexcept;
|
||||
/**
|
||||
* Establishes @p apiVersion as the version every subscription on this
|
||||
* connection is served at.
|
||||
*
|
||||
* A connection serves one version: one message has one shape, and several
|
||||
* of its subscriptions may match it. The first `subscribe` decides it and
|
||||
* it is never cleared. A `subscribe` refused for another reason still
|
||||
* establishes it, the registrations it makes being spread through the
|
||||
* handler.
|
||||
*
|
||||
* For the subscribe path alone. Nothing reads this value back: a publisher
|
||||
* reads the version recorded on the subscription it took the sink from.
|
||||
*
|
||||
* @param apiVersion The version the `subscribe` named.
|
||||
* @return nullopt when @p apiVersion is this connection's version;
|
||||
* otherwise the version already established.
|
||||
* @note Thread-safe: takes `lock_`.
|
||||
*/
|
||||
[[nodiscard]] std::optional<unsigned int>
|
||||
establishSubscriptionVersion(unsigned int apiVersion);
|
||||
|
||||
void
|
||||
insertSubMPTInfo(MPTID const& mptID);
|
||||
@@ -485,9 +513,20 @@ private:
|
||||
HashSet<AccountID> accountHistorySubscriptions_;
|
||||
HashSet<Book> bookSubscriptions_;
|
||||
HashSet<MPTID> mptSubscriptions_;
|
||||
unsigned int apiVersion_ = 0;
|
||||
// The API version every subscription on this connection was registered at, set by the first
|
||||
// `subscribe` and never cleared. Guarded by lock_. Read only by establishSubscriptionVersion,
|
||||
// so no publisher can reach it.
|
||||
std::optional<unsigned int> subscriptionApiVersion_;
|
||||
|
||||
static int
|
||||
/**
|
||||
* The next sequence number, which is what identifies a connection.
|
||||
*
|
||||
* 64 bits wide, as the counter and `seq_` are, so no two live connections
|
||||
* share an identity.
|
||||
*
|
||||
* @return A sequence number no live connection holds.
|
||||
*/
|
||||
static std::uint64_t
|
||||
assignId()
|
||||
{
|
||||
static std::atomic<std::uint64_t> kID(0);
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
}
|
||||
|
||||
bool
|
||||
prepare(std::size_t bytes, std::function<void(void)>) override
|
||||
prepare(std::size_t bytes, std::function<void()>) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
* empty vector.
|
||||
*/
|
||||
virtual std::pair<boost::tribool, std::vector<boost::asio::const_buffer>>
|
||||
prepare(std::size_t bytes, std::function<void(void)> resume) = 0;
|
||||
prepare(std::size_t bytes, std::function<void()> resume) = 0;
|
||||
};
|
||||
|
||||
template <class Streambuf>
|
||||
@@ -62,7 +62,7 @@ public:
|
||||
}
|
||||
|
||||
std::pair<boost::tribool, std::vector<boost::asio::const_buffer>>
|
||||
prepare(std::size_t bytes, std::function<void(void)>) override
|
||||
prepare(std::size_t bytes, std::function<void()>) override
|
||||
{
|
||||
if (sb_.size() == 0)
|
||||
return {true, {}};
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
* @return `true` if the writer is ready to provide more data.
|
||||
*/
|
||||
virtual bool
|
||||
prepare(std::size_t bytes, std::function<void(void)> resume) = 0;
|
||||
prepare(std::size_t bytes, std::function<void()> resume) = 0;
|
||||
|
||||
/**
|
||||
* Returns a ConstBufferSequence representing the input sequence.
|
||||
|
||||
@@ -342,10 +342,10 @@ BaseHTTPPeer<Handler, Impl>::doWriter(
|
||||
bool keepAlive,
|
||||
YieldContext doYield)
|
||||
{
|
||||
std::function<void(void)> resume;
|
||||
std::function<void()> resume;
|
||||
{
|
||||
auto const p = impl().shared_from_this();
|
||||
resume = std::function<void(void)>([this, p, writer, keepAlive]() {
|
||||
resume = std::function<void()>([this, p, writer, keepAlive] {
|
||||
util::spawn(strand_, [p, writer, keepAlive](YieldContext doYield) {
|
||||
p->doWriter(writer, keepAlive, doYield);
|
||||
});
|
||||
|
||||
@@ -99,7 +99,7 @@ private:
|
||||
port_.protocol.contains("wss2") || port_.protocol.contains("peer")};
|
||||
bool plain_{
|
||||
port_.protocol.contains("http") || port_.protocol.contains("ws") ||
|
||||
(port_.protocol.contains("ws2"))};
|
||||
port_.protocol.contains("ws2")};
|
||||
static constexpr std::chrono::milliseconds kInitialAcceptDelay{50};
|
||||
static constexpr std::chrono::milliseconds kMaxAcceptDelay{2000};
|
||||
std::chrono::milliseconds acceptDelay_{kInitialAcceptDelay};
|
||||
|
||||
@@ -7,6 +7,20 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Writes an HTTP reply carrying @p strMsg with status @p nStatus to @p output,
|
||||
* and logs the status at trace. The body is not logged here: it may carry a
|
||||
* credential this library cannot mask, so the caller logs it masked.
|
||||
*
|
||||
* A 401 with an empty body is answered with the fixed authentication page.
|
||||
* The status line carries the phrase Beast's registry gives @p nStatus, except
|
||||
* for 401 and 503, which carry a phrase of this server's own.
|
||||
*
|
||||
* @param nStatus The HTTP status code.
|
||||
* @param strMsg The body.
|
||||
* @param output Where the reply bytes are written.
|
||||
* @param j The journal the status is logged to.
|
||||
*/
|
||||
void
|
||||
httpReply(int nStatus, std::string const& strMsg, json::Output const&, beast::Journal j);
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ private:
|
||||
bool closed_ = false;
|
||||
std::condition_variable cv_;
|
||||
boost::container::flat_map<Work*, std::weak_ptr<Work>> map_;
|
||||
std::function<void(void)> f_;
|
||||
std::function<void()> f_;
|
||||
|
||||
public:
|
||||
IOList() = default;
|
||||
@@ -171,7 +171,7 @@ IOList::Work::destroy()
|
||||
{
|
||||
if (!ios_)
|
||||
return;
|
||||
std::function<void(void)> f;
|
||||
std::function<void()> f;
|
||||
{
|
||||
std::scoped_lock const lock(ios_->m_);
|
||||
ios_->map_.erase(this);
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include <xrpl/tx/invariants/SponsorshipInvariant.h>
|
||||
#include <xrpl/tx/invariants/VaultInvariant.h>
|
||||
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <string>
|
||||
@@ -27,18 +29,6 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Returns true if sleAcct is an AccountRoot carrying a pseudo-account field.
|
||||
*
|
||||
* isPseudoAccount() takes a view-bound AccountRootEntry, which is not
|
||||
* available while visiting entries, so invariant checks use this instead.
|
||||
*
|
||||
* @param sleAcct the SLE to check.
|
||||
* @return true if sleAcct is an AccountRoot carrying a pseudo-account field.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isPseudoAccountSle(SLE::ConstRef sleAcct);
|
||||
|
||||
#if GENERATING_DOCS
|
||||
/**
|
||||
* @brief Prototype for invariant check implementations.
|
||||
@@ -151,7 +141,7 @@ public:
|
||||
*/
|
||||
class XRPNotCreated
|
||||
{
|
||||
std::int64_t drops_ = 0;
|
||||
boost::multiprecision::int128_t drops_ = 0;
|
||||
|
||||
public:
|
||||
void
|
||||
|
||||
@@ -42,6 +42,14 @@ class ValidMPTIssuance
|
||||
*/
|
||||
bool referenceHoldingMutated_ = false;
|
||||
|
||||
/**
|
||||
* Flags cleared on an existing MPTokenIssuance, except lsfMPTLocked,
|
||||
* which tfMPTUnlock clears legitimately. Every other issuance flag is
|
||||
* fixed at creation or set-once via MPTokenIssuanceSet, so any bit
|
||||
* accumulated here is a bug. Enforced post-fixCleanup3_5_0.
|
||||
*/
|
||||
std::uint32_t issuanceFlagsCleared_ = 0;
|
||||
|
||||
/**
|
||||
* MPTokens and RippleStates deleted during apply. finalize() checks each
|
||||
* holder's AccountRoot to detect vault pseudo-account holdings deleted
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -26,7 +25,7 @@ checkFreeze(
|
||||
XRPL_ASSERT(src != dst, "xrpl::checkFreeze : unequal input accounts");
|
||||
|
||||
// check freeze
|
||||
if (auto sle = AccountRootEntryR(dst, view))
|
||||
if (auto sle = view.read(keylet::account(dst)))
|
||||
{
|
||||
if (sle->isFlag(lsfGlobalFreeze))
|
||||
{
|
||||
@@ -50,7 +49,7 @@ checkFreeze(
|
||||
|
||||
if (view.rules().enabled(fixFrozenLPTokenTransfer))
|
||||
{
|
||||
if (auto const sleDst = AccountRootEntryR(dst, view);
|
||||
if (auto const sleDst = view.read(keylet::account(dst));
|
||||
sleDst && sleDst->isFieldPresent(sfAMMID))
|
||||
{
|
||||
auto const sleAmm = view.read(keylet::amm((*sleDst)[sfAMMID]));
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/MathUtilities.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -23,6 +25,7 @@
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -502,6 +505,46 @@ public:
|
||||
};
|
||||
/** @endcond */
|
||||
|
||||
/** @cond INTERNAL */
|
||||
template <class T>
|
||||
[[nodiscard]] std::optional<T>
|
||||
checkedStepAddOpt(T const& lhs, T const& rhs)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, XRPAmount>)
|
||||
{
|
||||
if (auto const r = checkedAdd(lhs.drops(), rhs.drops()))
|
||||
return XRPAmount{*r};
|
||||
return std::nullopt;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, MPTAmount>)
|
||||
{
|
||||
if (auto const r = checkedAdd(lhs.value(), rhs.value()))
|
||||
return MPTAmount{*r};
|
||||
return std::nullopt;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, IOUAmount>)
|
||||
{
|
||||
// IOUAmount is Number-backed and throws on overflow.
|
||||
return lhs + rhs;
|
||||
}
|
||||
else
|
||||
{
|
||||
// A new amount type must decide explicitly how to add; do not fall back
|
||||
// to an unchecked add.
|
||||
static_assert(sizeof(T) == 0, "checkedStepAddOpt: unsupported amount type");
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
[[nodiscard]] T
|
||||
checkedStepAdd(T const& lhs, T const& rhs)
|
||||
{
|
||||
if (auto const r = checkedStepAddOpt(lhs, rhs))
|
||||
return *r;
|
||||
Throw<FlowException>(tecPATH_DRY);
|
||||
}
|
||||
/** @endcond */
|
||||
|
||||
/** @cond INTERNAL */
|
||||
// Check equal with tolerance
|
||||
bool
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
@@ -408,7 +407,7 @@ limitOut(
|
||||
if (!qf || qf->isConst())
|
||||
return remainingOut;
|
||||
|
||||
auto const out = [&]() {
|
||||
auto const out = [&] {
|
||||
auto const out = qf->outFromAvgQ(limitQuality);
|
||||
if (!out)
|
||||
return remainingOut;
|
||||
@@ -646,11 +645,21 @@ flow(
|
||||
boost::container::flat_multiset<TOutAmt> savedOuts;
|
||||
savedOuts.reserve(maxTries);
|
||||
|
||||
auto sum = [](auto const& col) {
|
||||
// Returns std::nullopt if the aggregate overflows; callers treat that as a
|
||||
// dry path.
|
||||
auto sum = [](auto const& col) -> std::optional<std::decay_t<decltype(*col.begin())>> {
|
||||
using TResult = std::decay_t<decltype(*col.begin())>;
|
||||
if (col.empty())
|
||||
return TResult{beast::kZero};
|
||||
return std::accumulate(col.begin() + 1, col.end(), *col.begin());
|
||||
TResult total = *col.begin();
|
||||
for (auto it = col.begin() + 1; it != col.end(); ++it)
|
||||
{
|
||||
auto const next = checkedStepAddOpt(total, *it);
|
||||
if (!next)
|
||||
return std::nullopt;
|
||||
total = *next;
|
||||
}
|
||||
return total;
|
||||
};
|
||||
|
||||
// These offers only need to be removed if the payment is not
|
||||
@@ -670,7 +679,7 @@ flow(
|
||||
ammContext.setMultiPath(activeStrands.size() > 1);
|
||||
|
||||
// Limit only if one strand and limitQuality
|
||||
auto const limitRemainingOut = [&]() {
|
||||
auto const limitRemainingOut = [&] {
|
||||
if (activeStrands.size() == 1 && limitQuality)
|
||||
{
|
||||
if (auto const strand = activeStrands.get(0))
|
||||
@@ -749,9 +758,17 @@ flow(
|
||||
{
|
||||
savedIns.insert(best->in);
|
||||
savedOuts.insert(best->out);
|
||||
remainingOut = outReq - sum(savedOuts);
|
||||
auto const sumOut = sum(savedOuts);
|
||||
if (!sumOut)
|
||||
return {tecPATH_DRY, std::move(ofrsToRmOnFail)};
|
||||
remainingOut = outReq - *sumOut;
|
||||
if (sendMax)
|
||||
remainingIn = *sendMax - sum(savedIns);
|
||||
{
|
||||
auto const sumIn = sum(savedIns);
|
||||
if (!sumIn)
|
||||
return {tecPATH_DRY, std::move(ofrsToRmOnFail)};
|
||||
remainingIn = *sendMax - *sumIn;
|
||||
}
|
||||
|
||||
if (flowDebugInfo)
|
||||
{
|
||||
@@ -786,8 +803,12 @@ flow(
|
||||
break;
|
||||
}
|
||||
|
||||
auto const actualOut = sum(savedOuts);
|
||||
auto const actualIn = sum(savedIns);
|
||||
auto const actualOutOpt = sum(savedOuts);
|
||||
auto const actualInOpt = sum(savedIns);
|
||||
if (!actualOutOpt || !actualInOpt)
|
||||
return {tecPATH_DRY, std::move(ofrsToRmOnFail)};
|
||||
auto const actualOut = *actualOutOpt;
|
||||
auto const actualIn = *actualInOpt;
|
||||
|
||||
JLOG(j.trace()) << "Total flow: in: " << to_string(actualIn)
|
||||
<< " out: " << to_string(actualOut);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -32,6 +33,9 @@ public:
|
||||
static std::vector<OptionaledField<STNumber>> const&
|
||||
getValueFields();
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -13,7 +12,6 @@
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -58,8 +56,8 @@ private:
|
||||
Keylet const& sponsorshipKeylet,
|
||||
AccountID const& sponsorID,
|
||||
AccountID const& sponseeID,
|
||||
AccountRootEntryW& sponsorAccSle,
|
||||
std::optional<AccountRootEntryW>& reserveSponsorAccSle);
|
||||
SLE::Ref sponsorAccSle,
|
||||
SLE::Ref reserveSponsorAccSle);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user