Compare commits

..

28 Commits

Author SHA1 Message Date
Ed Hennis
b6e4620349 Merge branch 'develop' into ximinez/fix/validator-cache 2026-01-15 13:03:28 -04:00
Ed Hennis
db0ef6a370 Merge branch 'develop' into ximinez/fix/validator-cache 2026-01-15 12:05:56 -04:00
Ed Hennis
11a45a0ac2 Merge branch 'develop' into ximinez/fix/validator-cache 2026-01-13 18:19:08 -04:00
Ed Hennis
aa035f4cfd Merge branch 'develop' into ximinez/fix/validator-cache 2026-01-13 15:27:57 -04:00
Ed Hennis
8988f9117f Merge branch 'develop' into ximinez/fix/validator-cache 2026-01-12 14:52:12 -04:00
Ed Hennis
ae4f379845 Merge branch 'develop' into ximinez/fix/validator-cache 2026-01-11 00:50:40 -04:00
Ed Hennis
671aa11649 Merge branch 'develop' into ximinez/fix/validator-cache 2026-01-08 17:06:06 -04:00
Ed Hennis
53d35fd8ea Merge branch 'develop' into ximinez/fix/validator-cache 2026-01-08 13:04:16 -04:00
Ed Hennis
0c7ea2e333 Merge branch 'develop' into ximinez/fix/validator-cache 2026-01-06 14:02:10 -05:00
Ed Hennis
5f54be25e9 Merge branch 'develop' into ximinez/fix/validator-cache 2025-12-22 17:39:55 -05:00
Ed Hennis
d82756519c Merge branch 'develop' into ximinez/fix/validator-cache 2025-12-18 19:59:49 -05:00
Ed Hennis
1f23832659 Merge branch 'develop' into ximinez/fix/validator-cache 2025-12-12 20:34:55 -05:00
Ed Hennis
4c50969bde Merge branch 'develop' into ximinez/fix/validator-cache 2025-12-11 15:31:29 -05:00
Ed Hennis
aabdf372dd Merge branch 'develop' into ximinez/fix/validator-cache 2025-12-05 21:13:06 -05:00
Ed Hennis
c6d63a4b90 Merge branch 'develop' into ximinez/fix/validator-cache 2025-12-02 17:37:25 -05:00
Ed Hennis
1e6c3208db Merge branch 'develop' into ximinez/fix/validator-cache 2025-12-01 14:40:41 -05:00
Ed Hennis
a74f223efb Merge branch 'develop' into ximinez/fix/validator-cache 2025-11-28 15:46:40 -05:00
Ed Hennis
1eb3a3ea5a Merge branch 'develop' into ximinez/fix/validator-cache 2025-11-27 01:48:53 -05:00
Ed Hennis
630e428929 Merge branch 'develop' into ximinez/fix/validator-cache 2025-11-26 00:25:12 -05:00
Ed Hennis
3f93edc5e0 Merge branch 'develop' into ximinez/fix/validator-cache 2025-11-25 14:55:02 -05:00
Ed Hennis
baf62689ff Merge branch 'develop' into ximinez/fix/validator-cache 2025-11-24 21:49:07 -05:00
Ed Hennis
ddf7d6cac4 Merge branch 'develop' into ximinez/fix/validator-cache 2025-11-24 21:30:18 -05:00
Ed Hennis
fcd2ea2d6e Merge branch 'develop' into ximinez/fix/validator-cache 2025-11-21 12:47:54 -05:00
Ed Hennis
a16aa5b12f Merge branch 'develop' into ximinez/fix/validator-cache 2025-11-18 22:39:25 -05:00
Ed Hennis
ef2de81870 Merge branch 'develop' into ximinez/fix/validator-cache 2025-11-15 03:08:38 -05:00
Ed Hennis
fce6757260 Merge branch 'develop' into ximinez/fix/validator-cache 2025-11-13 12:19:10 -05:00
Ed Hennis
d759a0a2b0 Merge branch 'develop' into ximinez/fix/validator-cache 2025-11-12 14:12:51 -05:00
Ed Hennis
d2dda416e8 Use Validator List (VL) cache files in more scenarios
- If any [validator_list_keys] are not available after all
  [validator_list_sites] have had a chance to be queried, then fall
  back to loading cache files. Currently, cache files are only used if
  no sites are defined, or the request to one of them has an error. It
  does not include cases where not enough sites are defined, or if a
  site returns an invalid VL (or something else entirely).
- Resolves #5320
2025-11-10 19:53:02 -05:00
44 changed files with 411 additions and 1276 deletions

View File

@@ -1,44 +0,0 @@
name: Generate build version number
description: "Generate build version number."
outputs:
version:
description: "The generated build version number."
value: ${{ steps.version.outputs.version }}
runs:
using: composite
steps:
# When a tag is pushed, the version is used as-is.
- name: Generate version for tag event
if: ${{ github.event_name == 'tag' }}
shell: bash
env:
VERSION: ${{ github.ref_name }}
run: echo "VERSION=${VERSION}" >> "${GITHUB_ENV}"
# When a tag is not pushed, then the version (e.g. 1.2.3-b0) is extracted
# from the BuildInfo.cpp file and the shortened commit hash appended to it.
# We use a plus sign instead of a hyphen because Conan recipe versions do
# not support two hyphens.
- name: Generate version for non-tag event
if: ${{ github.event_name != 'tag' }}
shell: bash
run: |
echo 'Extracting version from BuildInfo.cpp.'
VERSION="$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')"
if [[ -z "${VERSION}" ]]; then
echo 'Unable to extract version from BuildInfo.cpp.'
exit 1
fi
echo 'Appending shortened commit hash to version.'
SHA='${{ github.sha }}'
VERSION="${VERSION}+${SHA:0:7}"
echo "VERSION=${VERSION}" >> "${GITHUB_ENV}"
- name: Output version
id: version
shell: bash
run: echo "version=${VERSION}" >> "${GITHUB_OUTPUT}"

View File

@@ -2,11 +2,11 @@ name: Setup Conan
description: "Set up Conan configuration, profile, and remote." description: "Set up Conan configuration, profile, and remote."
inputs: inputs:
remote_name: conan_remote_name:
description: "The name of the Conan remote to use." description: "The name of the Conan remote to use."
required: false required: false
default: xrplf default: xrplf
remote_url: conan_remote_url:
description: "The URL of the Conan endpoint to use." description: "The URL of the Conan endpoint to use."
required: false required: false
default: https://conan.ripplex.io default: https://conan.ripplex.io
@@ -36,11 +36,11 @@ runs:
- name: Set up Conan remote - name: Set up Conan remote
shell: bash shell: bash
env: env:
REMOTE_NAME: ${{ inputs.remote_name }} CONAN_REMOTE_NAME: ${{ inputs.conan_remote_name }}
REMOTE_URL: ${{ inputs.remote_url }} CONAN_REMOTE_URL: ${{ inputs.conan_remote_url }}
run: | run: |
echo "Adding Conan remote '${REMOTE_NAME}' at '${REMOTE_URL}'." echo "Adding Conan remote '${CONAN_REMOTE_NAME}' at '${CONAN_REMOTE_URL}'."
conan remote add --index 0 --force "${REMOTE_NAME}" "${REMOTE_URL}" conan remote add --index 0 --force "${CONAN_REMOTE_NAME}" "${CONAN_REMOTE_URL}"
echo 'Listing Conan remotes.' echo 'Listing Conan remotes.'
conan remote list conan remote list

View File

@@ -104,7 +104,6 @@ test.overlay > xrpl.basics
test.overlay > xrpld.app test.overlay > xrpld.app
test.overlay > xrpld.overlay test.overlay > xrpld.overlay
test.overlay > xrpld.peerfinder test.overlay > xrpld.peerfinder
test.overlay > xrpl.nodestore
test.overlay > xrpl.protocol test.overlay > xrpl.protocol
test.overlay > xrpl.shamap test.overlay > xrpl.shamap
test.peerfinder > test.beast test.peerfinder > test.beast

View File

@@ -20,8 +20,8 @@ class Config:
Generate a strategy matrix for GitHub Actions CI. Generate a strategy matrix for GitHub Actions CI.
On each PR commit we will build a selection of Debian, RHEL, Ubuntu, MacOS, and On each PR commit we will build a selection of Debian, RHEL, Ubuntu, MacOS, and
Windows configurations, while upon merge into the develop or release branches, Windows configurations, while upon merge into the develop, release, or master
we will build all configurations, and test most of them. branches, we will build all configurations, and test most of them.
We will further set additional CMake arguments as follows: We will further set additional CMake arguments as follows:
- All builds will have the `tests`, `werr`, and `xrpld` options. - All builds will have the `tests`, `werr`, and `xrpld` options.

View File

@@ -1,8 +1,7 @@
# This workflow runs all workflows to check, build and test the project on # This workflow runs all workflows to check, build and test the project on
# various Linux flavors, as well as on MacOS and Windows, on every push to a # various Linux flavors, as well as on MacOS and Windows, on every push to a
# user branch. However, it will not run if the pull request is a draft unless it # user branch. However, it will not run if the pull request is a draft unless it
# has the 'DraftRunCI' label. For commits to PRs that target a release branch, # has the 'DraftRunCI' label.
# it also uploads the libxrpl recipe to the Conan remote.
name: PR name: PR
on: on:
@@ -54,12 +53,12 @@ jobs:
.github/scripts/rename/** .github/scripts/rename/**
.github/workflows/reusable-check-levelization.yml .github/workflows/reusable-check-levelization.yml
.github/workflows/reusable-check-rename.yml .github/workflows/reusable-check-rename.yml
.github/workflows/reusable-notify-clio.yml
.github/workflows/on-pr.yml .github/workflows/on-pr.yml
# Keep the paths below in sync with those in `on-trigger.yml`. # Keep the paths below in sync with those in `on-trigger.yml`.
.github/actions/build-deps/** .github/actions/build-deps/**
.github/actions/build-test/** .github/actions/build-test/**
.github/actions/generate-version/**
.github/actions/setup-conan/** .github/actions/setup-conan/**
.github/scripts/strategy-matrix/** .github/scripts/strategy-matrix/**
.github/workflows/reusable-build.yml .github/workflows/reusable-build.yml
@@ -67,7 +66,6 @@ jobs:
.github/workflows/reusable-build-test.yml .github/workflows/reusable-build-test.yml
.github/workflows/reusable-strategy-matrix.yml .github/workflows/reusable-strategy-matrix.yml
.github/workflows/reusable-test.yml .github/workflows/reusable-test.yml
.github/workflows/reusable-upload-recipe.yml
.codecov.yml .codecov.yml
cmake/** cmake/**
conan/** conan/**
@@ -123,42 +121,22 @@ jobs:
secrets: secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
upload-recipe: notify-clio:
needs: needs:
- should-run - should-run
- build-test - build-test
# Only run when committing to a PR that targets a release branch in the if: ${{ needs.should-run.outputs.go == 'true' && (startsWith(github.base_ref, 'release') || github.base_ref == 'master') }}
# XRPLF repository. uses: ./.github/workflows/reusable-notify-clio.yml
if: ${{ github.repository_owner == 'XRPLF' && needs.should-run.outputs.go == 'true' && startsWith(github.ref, 'refs/heads/release') }}
uses: ./.github/workflows/reusable-upload-recipe.yml
secrets: secrets:
remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} clio_notify_token: ${{ secrets.CLIO_NOTIFY_TOKEN }}
remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
notify-clio:
needs: upload-recipe
runs-on: ubuntu-latest
steps:
# Notify the Clio repository about the newly proposed release version, so
# it can be checked for compatibility before the release is actually made.
- name: Notify Clio
env:
GH_TOKEN: ${{ secrets.CLIO_NOTIFY_TOKEN }}
PR_URL: ${{ github.event.pull_request.html_url }}
run: |
gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \
/repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \
-F "client_payload[ref]=${{ needs.upload-recipe.outputs.recipe_ref }}" \
-F "client_payload[pr_url]=${PR_URL}"
passed: passed:
if: failure() || cancelled() if: failure() || cancelled()
needs: needs:
- check-levelization
- check-rename
- build-test - build-test
- upload-recipe - check-levelization
- notify-clio
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Fail - name: Fail

View File

@@ -1,25 +0,0 @@
# This workflow uploads the libxrpl recipe to the Conan remote when a versioned
# tag is pushed.
name: Tag
on:
push:
tags:
- "v*"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
upload-recipe:
# Only run when a tag is pushed to the XRPLF repository.
if: ${{ github.repository_owner == 'XRPLF' }}
uses: ./.github/workflows/reusable-upload-recipe.yml
secrets:
remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}

View File

@@ -1,7 +1,9 @@
# This workflow runs all workflows to build and test the code on various Linux # This workflow runs all workflows to build the dependencies required for the
# flavors, as well as on MacOS and Windows, on a scheduled basis, on merge into # project on various Linux flavors, as well as on MacOS and Windows, on a
# the 'develop' or 'release*' branches, or when requested manually. Upon pushes # scheduled basis, on merge into the 'develop', 'release', or 'master' branches,
# to the develop branch it also uploads the libxrpl recipe to the Conan remote. # or manually. The missing commits check is only run when the code is merged
# into the 'develop' or 'release' branches, and the documentation is built when
# the code is merged into the 'develop' branch.
name: Trigger name: Trigger
on: on:
@@ -9,6 +11,7 @@ on:
branches: branches:
- "develop" - "develop"
- "release*" - "release*"
- "master"
paths: paths:
# These paths are unique to `on-trigger.yml`. # These paths are unique to `on-trigger.yml`.
- ".github/workflows/on-trigger.yml" - ".github/workflows/on-trigger.yml"
@@ -16,7 +19,6 @@ on:
# Keep the paths below in sync with those in `on-pr.yml`. # Keep the paths below in sync with those in `on-pr.yml`.
- ".github/actions/build-deps/**" - ".github/actions/build-deps/**"
- ".github/actions/build-test/**" - ".github/actions/build-test/**"
- ".github/actions/generate-version/**"
- ".github/actions/setup-conan/**" - ".github/actions/setup-conan/**"
- ".github/scripts/strategy-matrix/**" - ".github/scripts/strategy-matrix/**"
- ".github/workflows/reusable-build.yml" - ".github/workflows/reusable-build.yml"
@@ -24,7 +26,6 @@ on:
- ".github/workflows/reusable-build-test.yml" - ".github/workflows/reusable-build-test.yml"
- ".github/workflows/reusable-strategy-matrix.yml" - ".github/workflows/reusable-strategy-matrix.yml"
- ".github/workflows/reusable-test.yml" - ".github/workflows/reusable-test.yml"
- ".github/workflows/reusable-upload-recipe.yml"
- ".codecov.yml" - ".codecov.yml"
- "cmake/**" - "cmake/**"
- "conan/**" - "conan/**"
@@ -69,20 +70,11 @@ jobs:
with: with:
# Enable ccache only for events targeting the XRPLF repository, since # Enable ccache only for events targeting the XRPLF repository, since
# other accounts will not have access to our remote cache storage. # other accounts will not have access to our remote cache storage.
# However, we do not enable ccache for events targeting a release branch, # However, we do not enable ccache for events targeting the master or a
# to protect against the rare case that the output produced by ccache is # release branch, to protect against the rare case that the output
# not identical to a regular compilation. # produced by ccache is not identical to a regular compilation.
ccache_enabled: ${{ github.repository_owner == 'XRPLF' && !startsWith(github.ref, 'refs/heads/release') }} ccache_enabled: ${{ github.repository_owner == 'XRPLF' && !(github.base_ref == 'master' || startsWith(github.base_ref, 'release')) }}
os: ${{ matrix.os }} os: ${{ matrix.os }}
strategy_matrix: ${{ github.event_name == 'schedule' && 'all' || 'minimal' }} strategy_matrix: ${{ github.event_name == 'schedule' && 'all' || 'minimal' }}
secrets: secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
upload-recipe:
needs: build-test
# Only run when pushing to the develop branch in the XRPLF repository.
if: ${{ github.repository_owner == 'XRPLF' && github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
uses: ./.github/workflows/reusable-upload-recipe.yml
secrets:
remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}

View File

@@ -3,9 +3,7 @@ name: Run pre-commit hooks
on: on:
pull_request: pull_request:
push: push:
branches: branches: [develop, release, master]
- "develop"
- "release*"
workflow_dispatch: workflow_dispatch:
jobs: jobs:

View File

@@ -0,0 +1,91 @@
# This workflow exports the built libxrpl package to the Conan remote on a
# a channel named after the pull request, and notifies the Clio repository about
# the new version so it can check for compatibility.
name: Notify Clio
# This workflow can only be triggered by other workflows.
on:
workflow_call:
inputs:
conan_remote_name:
description: "The name of the Conan remote to use."
required: false
type: string
default: xrplf
conan_remote_url:
description: "The URL of the Conan endpoint to use."
required: false
type: string
default: https://conan.ripplex.io
secrets:
clio_notify_token:
description: "The GitHub token to notify Clio about new versions."
required: true
conan_remote_username:
description: "The username for logging into the Conan remote."
required: true
conan_remote_password:
description: "The password for logging into the Conan remote."
required: true
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-clio
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
upload:
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
runs-on: ubuntu-latest
container: ghcr.io/xrplf/ci/ubuntu-noble:gcc-13-sha-5dd7158
steps:
- name: Checkout repository
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- name: Generate outputs
id: generate
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
echo 'Generating user and channel.'
echo "user=clio" >> "${GITHUB_OUTPUT}"
echo "channel=pr_${PR_NUMBER}" >> "${GITHUB_OUTPUT}"
echo 'Extracting version.'
echo "version=$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')" >> "${GITHUB_OUTPUT}"
- name: Calculate conan reference
id: conan_ref
run: |
echo "conan_ref=${{ steps.generate.outputs.version }}@${{ steps.generate.outputs.user }}/${{ steps.generate.outputs.channel }}" >> "${GITHUB_OUTPUT}"
- name: Set up Conan
uses: ./.github/actions/setup-conan
with:
conan_remote_name: ${{ inputs.conan_remote_name }}
conan_remote_url: ${{ inputs.conan_remote_url }}
- name: Log into Conan remote
env:
CONAN_REMOTE_NAME: ${{ inputs.conan_remote_name }}
run: conan remote login "${CONAN_REMOTE_NAME}" "${{ secrets.conan_remote_username }}" --password "${{ secrets.conan_remote_password }}"
- name: Upload package
env:
CONAN_REMOTE_NAME: ${{ inputs.conan_remote_name }}
run: |
conan export --user=${{ steps.generate.outputs.user }} --channel=${{ steps.generate.outputs.channel }} .
conan upload --confirm --check --remote="${CONAN_REMOTE_NAME}" xrpl/${{ steps.conan_ref.outputs.conan_ref }}
outputs:
conan_ref: ${{ steps.conan_ref.outputs.conan_ref }}
notify:
needs: upload
runs-on: ubuntu-latest
steps:
- name: Notify Clio
env:
GH_TOKEN: ${{ secrets.clio_notify_token }}
PR_URL: ${{ github.event.pull_request.html_url }}
run: |
gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \
/repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \
-F "client_payload[conan_ref]=${{ needs.upload.outputs.conan_ref }}" \
-F "client_payload[pr_url]=${PR_URL}"

View File

@@ -1,73 +0,0 @@
# This workflow exports the built libxrpl package to the Conan remote.
name: Upload Conan recipe
# This workflow can only be triggered by other workflows.
on:
workflow_call:
inputs:
remote_name:
description: "The name of the Conan remote to use."
required: false
type: string
default: xrplf
remote_url:
description: "The URL of the Conan endpoint to use."
required: false
type: string
default: https://conan.ripplex.io
secrets:
remote_username:
description: "The username for logging into the Conan remote."
required: true
remote_password:
description: "The password for logging into the Conan remote."
required: true
outputs:
recipe_ref:
description: "The Conan recipe reference ('name/version') that was uploaded."
value: ${{ jobs.upload.outputs.ref }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-upload-recipe
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
upload:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/ci/ubuntu-noble:gcc-13-sha-5dd7158
steps:
- name: Checkout repository
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- name: Generate build version number
id: version
uses: ./.github/actions/generate-version
- name: Set up Conan
uses: ./.github/actions/setup-conan
with:
remote_name: ${{ inputs.remote_name }}
remote_url: ${{ inputs.remote_url }}
- name: Log into Conan remote
env:
REMOTE_NAME: ${{ inputs.remote_name }}
REMOTE_USERNAME: ${{ secrets.remote_username }}
REMOTE_PASSWORD: ${{ secrets.remote_password }}
run: conan remote login "${REMOTE_NAME}" "${REMOTE_USERNAME}" --password "${REMOTE_PASSWORD}"
- name: Upload Conan recipe
env:
REMOTE_NAME: ${{ inputs.remote_name }}
run: |
conan export . --version=${{ steps.version.outputs.version }}
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/${{ steps.version.outputs.version }}
outputs:
ref: xrpl/${{ steps.version.outputs.version }}

View File

@@ -86,8 +86,8 @@ jobs:
- name: Setup Conan - name: Setup Conan
uses: ./.github/actions/setup-conan uses: ./.github/actions/setup-conan
with: with:
remote_name: ${{ env.CONAN_REMOTE_NAME }} conan_remote_name: ${{ env.CONAN_REMOTE_NAME }}
remote_url: ${{ env.CONAN_REMOTE_URL }} conan_remote_url: ${{ env.CONAN_REMOTE_URL }}
- name: Build dependencies - name: Build dependencies
uses: ./.github/actions/build-deps uses: ./.github/actions/build-deps

View File

@@ -8,9 +8,7 @@ if(POLICY CMP0077)
endif() endif()
# Fix "unrecognized escape" issues when passing CMAKE_MODULE_PATH on Windows. # Fix "unrecognized escape" issues when passing CMAKE_MODULE_PATH on Windows.
if(DEFINED CMAKE_MODULE_PATH) file(TO_CMAKE_PATH "${CMAKE_MODULE_PATH}" CMAKE_MODULE_PATH)
file(TO_CMAKE_PATH "${CMAKE_MODULE_PATH}" CMAKE_MODULE_PATH)
endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
project(xrpl) project(xrpl)

View File

@@ -78,61 +78,72 @@ To report a qualifying bug, please send a detailed report to:
| Email Address | bugs@ripple.com | | Email Address | bugs@ripple.com |
| :-----------: | :-------------------------------------------------- | | :-----------: | :-------------------------------------------------- |
| Short Key ID | `0xA9F514E0` | | Short Key ID | `0xC57929BE` |
| Long Key ID | `0xD900855AA9F514E0` | | Long Key ID | `0xCD49A0AFC57929BE` |
| Fingerprint | `B72C 0654 2F2A E250 2763 A268 D900 855A A9F5 14E0` | | Fingerprint | `24E6 3B02 37E0 FA9C 5E96 8974 CD49 A0AF C579 29BE` |
The full PGP key for this address, which is also available on several key servers (e.g. on [keyserver.ubuntu.com](https://keyserver.ubuntu.com)), is: The full PGP key for this address, which is also available on several key servers (e.g. on [keyserver.ubuntu.com](https://keyserver.ubuntu.com)), is:
``` ```
-----BEGIN PGP PUBLIC KEY BLOCK----- -----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGkSZAQBEACprU199OhgdsOsygNjiQV4msuN3vDOUooehL+NwfsGfW79Tbqq mQINBFUwGHYBEAC0wpGpBPkd8W1UdQjg9+cEFzeIEJRaoZoeuJD8mofwI5Ejnjdt
Q2u7uQ3NZjW+M2T4nsDwuhkr7pe7xSReR5W8ssaczvtUyxkvbMClilcgZ2OSCAuC kCpUYEDal0ygkKobu8SzOoATcDl18iCrScX39VpTm96vISFZMhmOryYCIp4QLJNN
N9tzJsqOqkwBvXoNXkn//T2jnPz0ZU2wSF+NrEibq5FeuyGdoX3yXXBxq9pW9HzK 4HKc2ZdBj6W4igNi6vj5Qo6JMyGpLY2mz4CZskbt0TNuUxWrGood+UrCzpY8x7/N
HkQll63QSl6BzVSGRQq+B6lGgaZGLwf3mzmIND9Z5VGLNK2jKynyz9z091whNG/M a93fcvNw+prgCr0rCH3hAPmAFfsOBbtGzNnmq7xf3jg5r4Z4sDiNIF1X1y53DAfV
kV+E7/r/bujHk7WIVId07G5/COTXmSr7kFnNEkd2Umw42dkgfiNKvlmJ9M7c1wLK rWDx49IKsuCEJfPMp1MnBSvDvLaQ2hKXs+cOpx1BCZgHn3skouEUxxgqbtTzBLt1
KbL9Eb4ADuW6rRc5k4s1e6GT8R4/VPliWbCl9SE32hXH8uTkqVIFZP2eyM5WRRHs xXpmuijsaltWngPnGO7mOAzbpZSdBm82/Emrk9bPMuD0QaLQjWr7HkTSUs6ZsKt4
aKzitkQG9UK9gcb0kdgUkxOvvgPHAe5IuZlcHFzU4y0dBbU1VEFWVpiLU0q+IuNw 7CLPdWqxyY/QVw9UaxeHEtWGQGMIQGgVJGh1fjtUr5O1sC9z9jXcQ0HuIHnRCTls
5BRemeHc59YNsngkmAZ+/9zouoShRusZmC8Wzotv75C2qVBcjijPvmjWAUz0Zunm GP7hklJmfH5V4SyAJQ06/hLuEhUJ7dn+BlqCsT0tLmYTgZYNzNcLHcqBFMEZHvHw
Lsr+O71vqHE73pERjD07wuD/ISjiYRYYE/bVrXtXLZijC7qAH4RE3nID+2ojcZyO 9GENMx/tDXgajKql4bJnzuTK0iGU/YepanANLd1JHECJ4jzTtmKOus9SOGlB2/l1
/2jMQvt7un56RsGH4UBHi3aBHi9bUoDGCXKiQY981cEuNaOxpou7Mh3x/ONzzSvk 0t0ADDYAS3eqOdOcUvo9ElSLCI5vSVHhShSte/n2FMWU+kMUboTUisEG8CgQnrng
sTV6nl1LOZHykN1JyKwaNbTSAiuyoN+7lOBqbV04DNYAHL88PrT21P83aQARAQAB g2CvvQvqDkeOtZeqMcC7HdiZS0q3LJUWtwA/ViwxrVlBDCxiTUXCotyBWwARAQAB
tB1SaXBwbGUgTGFicyA8YnVnc0ByaXBwbGUuY29tPokCTgQTAQgAOBYhBLcsBlQv tDBSaXBwbGUgTGFicyBCdWcgQm91bnR5IFByb2dyYW0gPGJ1Z3NAcmlwcGxlLmNv
KuJQJ2OiaNkAhVqp9RTgBQJpEmQEAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheA bT6JAjcEEwEKACEFAlUwGHYCGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQ
AAoJENkAhVqp9RTgBzgP/i7y+aDWl1maig1XMdyb+o0UGusumFSW4Hmj278wlKVv zUmgr8V5Kb6R0g//SwY/mVJY59k87iL26/KayauSoOcz7xjcST26l4ZHVVX85gOY
usgLPihYgHE0PKrv6WRyKOMC1tQEcYYN93M+OeQ1vFhS2YyURq6RCMmh4zq/awXG HYZl8k0+m8X3zxeYm9a3QAoAml8sfoaFRFQP8ynnefRrLUPaZ2MjbJ0SACMwZNef
uZbG36OURB5NH8lGBOHiN/7O+nY0CgenBT2JWm+GW3nEOAVOVm4+r5GlpPlv+Dp1 T6o7Mi8LBAaiNZdYVyIfX1oM6YXtqYkuJdav6ZCyvVYqc9OvMJPY2ZzJYuI/ZtvQ
NPBThcKXFMnH73++NpSQoDzTfRYHPxhDAX3jkLi/moXfSanOLlR6l94XNNN0jBHW /lTndxCeg9ALNX/iezOLGdfMpf4HuIFVwcPPlwGi+HDlB9/bggDEHC8z434SXVFc
Quao0rzf4WSXq9g6AS224xhAA5JyIcFl8TX7hzj5HaFn3VWo3COoDu4U7H+BM0fl aQatXAPcDkjMUweU7y0CZtYEj00HITd4pSX6MqGiHrxlDZTqinCOPs1Ieqp7qufs
85yqiMQypp7EhN2gxpMMWaHY5TFM85U/bFXFYfEgihZ4/gt4uoIzsNI9jlX7mYvG MzlM6irLGucxj1+wa16ieyYvEtGaPIsksUKkywx0O7cf8N2qKg+eIkUk6O0Uc6eO
KFdDij+oTlRsuOxdIy60B3dKcwOH9nZZCz0SPsN/zlRWgKzK4gDKdGhFkU9OlvPu CszizmiXIXy4O6OiLlVHGKkXHMSW9Nwe9GE95O8G9WR8OZCEuDv+mHPAutO+IjdP
94ZqscanoiWKDoZkF96+sjgfjkuHsDK7Lwc1Xi+T4drHG/3aVpkYabXox+lrKB/S PDAAUvy+3XnkceO+HGWRpVvJZfFP2YH4A33InFL5yqlJmSoR/yVingGLxk55bZDM
yxZjeqOIQzWPhnLgCaLyvsKo5hxKzL0w3eURu8F3IS7RgOOlljv4M+Me9sEVcdNV +HYGR3VeMb8Xj1rf/02qERsZyccMCFdAvKDbTwmvglyHdVLu5sPmktxbBYiemfyJ
aN3/tQwbaomSX1X5D5YXqhBwC3rU3wXwamsscRTGEpkV+JCX6KUqGP7nWmxCpAly qxMxmYXCc9S0hWrWZW7edktBa9NpE58z1mx+hRIrDNbS2sDHrib9PULYCySyVYcF
FL05XuOd5SVHJjXLeuje0JqLUpN514uL+bThWwDbDTdAdwW3oK/2WbXz7IfJRLBj P+PWEe1CAS5jqkR2ker5td2/pHNnJIycynBEs7l6zbc9fu+nktFJz0q2B+GJAhwE
uQINBGkSZAQBEADdI3SL2F72qkrgFqXWE6HSRBu9bsAvTE5QrRPWk7ux6at537r4 EAEKAAYFAlUwGaQACgkQ+tiY1qQ2QkjMFw//f2hNY3BPNe+1qbhzumMDCnbTnGif
S4sIw2dOwLvbyIrDgKNq3LQ5wCK88NO/NeCOFm4AiCJSl3pJHXYnTDoUxTrrxx+o kLuAGl9OKt81VHG1f6RnaGiLpR696+6Ja45KzH15cQ5JJl5Bgs1YkR/noTGX8IAD
vSRI4I3fHEql/MqzgiAb0YUezjgFdh3vYheMPp/309PFbOLhiFqEcx80Mx5h06UH c70eNwiFu8JXTaaeeJrsmFkF9Tueufb364risYkvPP8tNUD3InBFEZT3WN7JKwix
gDzu1qNj3Ec+31NLic5zwkrAkvFvD54d6bqYR3SEgMau6aYEewpGHbWBi2pLqSi2 coD4/BwekUwOZVDd/uCFEyhlhZsROxdKNisNo3VtAq2s+3tIBAmTrriFUl0K+ZC5
lQcAeOFixqGpTwDmAnYR8YtjBYepy0MojEAdTHcQQlOYSDk4q4elG+io2N8vECfU zgavcpnPN57zMtW9aK+VO3wXqAKYLYmtgxkVzSLUZt2M7JuwOaAdyuYWAneKZPCu
rD6ORecN48GXdZINYWTAdslrUeanmBdgQrYkSpce8TSghgT9P01SNaXxmyaehVUO 1AXkmyo+d84sd5mZaKOr5xArAFiNMWPUcZL4rkS1Fq4dKtGAqzzR7a7hWtA5o27T
lqI4pcg5G2oojAE8ncNS3TwDtt7daTaTC3bAdr4PXDVAzNAiewjMNZPB7xidkDGQ 6vynuxZ1n0PPh0er2O/zF4znIjm5RhTlfjp/VmhZdQfpulFEQ/dMxxGkQ9z5IYbX
Y4W1LxTMXyJVWxehYOH7tsbBRKninlfRnLgYzmtIbNRAAvNcsxU6ihv3AV0WFknN mTlSDbCSb+FMsanRBJ7Drp5EmBIudVGY6SHI5Re1RQiEh7GoDfUMUwZO+TVDII5R
YbSzotEv1Xq/5wk309x8zCDe+sP0cQicvbXafXmUzPAZzeqFg+VLFn7F9MP1WGlW Ra7WyuimYleJgDo/+7HyfuIyGDaUCVj6pwVtYtYIdOI3tTw1R1Mr0V8yaNVnJghL
B1u7VIvBF1Mp9Nd3EAGBAoLRdRu+0dVWIjPTQuPIuD9cCatJA0wVaKUrjYbBMl88 CHcEJQL+YHSmiMM3ySil3O6tm1By6lFz8bVe/rgG/5uklQrnjMR37jYboi1orCC4
a12LixNVGeSFS9N7ADHx0/o7GNT6l88YbaLP6zggUHpUD/bR+cDN7vllIQARAQAB yeIoQeV0ItlxeTyBwYIV/o1DBNxDevTZvJabC93WiGLw2XFjpZ0q/9+zI2rJUZJh
iQI2BBgBCAAgFiEEtywGVC8q4lAnY6Jo2QCFWqn1FOAFAmkSZAQCGwwACgkQ2QCF qxmKP+D4e27lCI65Ag0EVTAYdgEQAMvttYNqeRNBRpSX8fk45WVIV8Fb21fWdwk6
Wqn1FOAfAA/8CYq4p0p4bobY20CKEMsZrkBTFJyPDqzFwMeTjgpzqbD7Y3Qq5QCK 2SkZnJURbiC0LxQnOi7wrtii7DeFZtwM2kFHihS1VHekBnIKKZQSgGoKuFAQMGyu
OBbvY02GWdiIsNOzKdBxiuam2xYP9WHZj4y7/uWEvT0qlPVmDFu+HXjoJ43oxwFd a426H4ZsSmA9Ufd7kRbvdtEcp7/RTAanhrSL4lkBhaKJrXlxBJ27o3nd7/rh7r3a
CUp2gMuQ4cSL3X94VRJ3BkVL+tgBm8CNY0vnTLLOO3kum/R69VsGJS1JSGUWjNM+ OszbPY6DJ5bWClX3KooPTDl/RF2lHn+fweFk58UvuunHIyo4BWJUdilSXIjLun+P
4qwS3mz+73xJu1HmERyN2RZF/DGIZI2PyONQQ6aH85G1Dd2ohu2/DBAkQAMBrPbj Qaik4ZAsZVwNhdNz05d+vtai4AwbYoO7adboMLRkYaXSQwGytkm+fM6r7OpXHYuS
FrbDaBLyFhODxU3kTWqnfLlaElSm2EGdIU2yx7n4BggEa//NZRMm5kyeo4vzhtlQ cR4zB/OK5hxCVEpWfiwN71N2NMvnEMaWd/9uhqxJzyvYgkVUXV9274TUe16pzXnW
YIVUMLAOLZvnEqDnsLKp+22FzNR/O+htBQC4lPywl53oYSALdhz1IQlcAC1ru5KR ZLfmitjwc91e7mJBBfKNenDdhaLEIlDRwKTLj7k58f9srpMnyZFacntu5pUMNblB
XPzhIXV6IIzkcx9xNkEclZxmsuy5ERXyKEmLbIHAlzFmnrldlt2ZgXDtzaorLmxj cjXwWxz5ZaQikLnKYhIvrIEwtWPyjqOzNXNvYfZamve/LJ8HmWGCKao3QHoAIDvB
klKibxd5tF50qOpOivz+oPtFo7n+HmFa1nlVAMxlDCUdM0pEVeYDKI5zfVwalyhZ 9XBxrDyTJDpxbog6Qu4SY8AdgVlan6c/PsLDc7EUegeYiNTzsOK+eq3G5/E92eIu
NnjpakdZSXMwgc7NP/hH9buF35hKDp7EckT2y3JNYwHsDdy1icXN2q40XZw5tSIn TsUXlciypFcRm1q8vLRr+HYYe2mJDo4GetB1zLkAFBcYJm/x9iJQbu0hn5NxJvZO
zkPWdu3OUY8PISohN6Pw4h0RH4ZmoX97E8sEfmdKaT58U4Hf2aAv5r9IWCSrAVqY R0Y5nOJQdyi+muJzKYwhkuzaOlswzqVXkq/7+QCjg7QsycdcwDjiQh3OrsgXHrwl
u5jvac29CzQR9Kal0A+8phHAXHNFD83SwzIC0syaT9ficAguwGH8X6Q= M7gyafL9ABEBAAGJAh8EGAEKAAkFAlUwGHYCGwwACgkQzUmgr8V5Kb50BxAAhj9T
=nGuD TwmNrgRldTHszj+Qc+v8RWqV6j+R+zc0cn5XlUa6XFaXI1OFFg71H4dhCPEiYeN0
IrnocyMNvCol+eKIlPKbPTmoixjQ4udPTR1DC1Bx1MyW5FqOrsgBl5t0e1VwEViM
NspSStxu5Hsr6oWz2GD48lXZWJOgoL1RLs+uxjcyjySD/em2fOKASwchYmI+ezRv
plfhAFIMKTSCN2pgVTEOaaz13M0U+MoprThqF1LWzkGkkC7n/1V1f5tn83BWiagG
2N2Q4tHLfyouzMUKnX28kQ9sXfxwmYb2sA9FNIgxy+TdKU2ofLxivoWT8zS189z/
Yj9fErmiMjns2FzEDX+bipAw55X4D/RsaFgC+2x2PDbxeQh6JalRA2Wjq32Ouubx
u+I4QhEDJIcVwt9x6LPDuos1F+M5QW0AiUhKrZJ17UrxOtaquh/nPUL9T3l2qPUn
1ChrZEEEhHO6vA8+jn0+cV9n5xEz30Str9iHnDQ5QyR5LyV4UBPgTdWyQzNVKA69
KsSr9lbHEtQFRzGuBKwt6UlSFv9vPWWJkJit5XDKAlcKuGXj0J8OlltToocGElkF
+gEBZfoOWi/IBjRLrFW2cT3p36DTR5O1Ud/1DLnWRqgWNBLrbs2/KMKE6EnHttyD
7Tz8SQkuxltX/yBXMV3Ddy0t6nWV2SZEfuxJAQI=
=spg4
-----END PGP PUBLIC KEY BLOCK----- -----END PGP PUBLIC KEY BLOCK-----
``` ```

View File

@@ -13,7 +13,6 @@ include_guard(GLOBAL)
set(is_clang FALSE) set(is_clang FALSE)
set(is_gcc FALSE) set(is_gcc FALSE)
set(is_msvc FALSE) set(is_msvc FALSE)
set(is_xcode FALSE)
if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") # Clang or AppleClang if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") # Clang or AppleClang
set(is_clang TRUE) set(is_clang TRUE)
@@ -25,11 +24,6 @@ else()
message(FATAL_ERROR "Unsupported C++ compiler: ${CMAKE_CXX_COMPILER_ID}") message(FATAL_ERROR "Unsupported C++ compiler: ${CMAKE_CXX_COMPILER_ID}")
endif() endif()
# Xcode generator detection
if(CMAKE_GENERATOR STREQUAL "Xcode")
set(is_xcode TRUE)
endif()
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Operating system detection # Operating system detection

View File

@@ -32,14 +32,14 @@ target_protobuf_sources(xrpl.libpb xrpl/proto
target_compile_options(xrpl.libpb target_compile_options(xrpl.libpb
PUBLIC PUBLIC
$<$<BOOL:${is_msvc}>:-wd4996> $<$<BOOL:${MSVC}>:-wd4996>
$<$<BOOL:${is_xcode}>: $<$<BOOL:${XCODE}>:
--system-header-prefix="google/protobuf" --system-header-prefix="google/protobuf"
-Wno-deprecated-dynamic-exception-spec -Wno-deprecated-dynamic-exception-spec
> >
PRIVATE PRIVATE
$<$<BOOL:${is_msvc}>:-wd4065> $<$<BOOL:${MSVC}>:-wd4065>
$<$<NOT:$<BOOL:${is_msvc}>>:-Wno-deprecated-declarations> $<$<NOT:$<BOOL:${MSVC}>>:-Wno-deprecated-declarations>
) )
target_link_libraries(xrpl.libpb target_link_libraries(xrpl.libpb

View File

@@ -4,12 +4,6 @@
include(create_symbolic_link) include(create_symbolic_link)
# If no suffix is defined for executables (e.g. Windows uses .exe but Linux
# and macOS use none), then explicitly set it to the empty string.
if(NOT DEFINED suffix)
set(suffix "")
endif()
install ( install (
TARGETS TARGETS
common common

View File

@@ -4,11 +4,6 @@
include(CompilationEnv) include(CompilationEnv)
# Set defaults for optional variables to avoid uninitialized variable warnings
if(NOT DEFINED voidstar)
set(voidstar OFF)
endif()
add_library (opts INTERFACE) add_library (opts INTERFACE)
add_library (Xrpl::opts ALIAS opts) add_library (Xrpl::opts ALIAS opts)
target_compile_definitions (opts target_compile_definitions (opts
@@ -57,7 +52,7 @@ add_library (xrpl_syslibs INTERFACE)
add_library (Xrpl::syslibs ALIAS xrpl_syslibs) add_library (Xrpl::syslibs ALIAS xrpl_syslibs)
target_link_libraries (xrpl_syslibs target_link_libraries (xrpl_syslibs
INTERFACE INTERFACE
$<$<BOOL:${is_msvc}>: $<$<BOOL:${MSVC}>:
legacy_stdio_definitions.lib legacy_stdio_definitions.lib
Shlwapi Shlwapi
kernel32 kernel32
@@ -74,10 +69,10 @@ target_link_libraries (xrpl_syslibs
odbccp32 odbccp32
crypt32 crypt32
> >
$<$<NOT:$<BOOL:${is_msvc}>>:dl> $<$<NOT:$<BOOL:${MSVC}>>:dl>
$<$<NOT:$<OR:$<BOOL:${is_msvc}>,$<BOOL:${is_macos}>>>:rt>) $<$<NOT:$<OR:$<BOOL:${MSVC}>,$<BOOL:${APPLE}>>>:rt>)
if (NOT is_msvc) if (NOT MSVC)
set (THREADS_PREFER_PTHREAD_FLAG ON) set (THREADS_PREFER_PTHREAD_FLAG ON)
find_package (Threads) find_package (Threads)
target_link_libraries (xrpl_syslibs INTERFACE Threads::Threads) target_link_libraries (xrpl_syslibs INTERFACE Threads::Threads)

View File

@@ -43,10 +43,7 @@
include(CompilationEnv) include(CompilationEnv)
# Read environment variable # Read environment variable
set(SANITIZERS "") set(SANITIZERS $ENV{SANITIZERS})
if(DEFINED ENV{SANITIZERS})
set(SANITIZERS "$ENV{SANITIZERS}")
endif()
# Set SANITIZERS_ENABLED flag for use in other modules # Set SANITIZERS_ENABLED flag for use in other modules
if(SANITIZERS MATCHES "address|thread|undefinedbehavior") if(SANITIZERS MATCHES "address|thread|undefinedbehavior")

View File

@@ -4,11 +4,10 @@
include(CompilationEnv) include(CompilationEnv)
set(is_ci FALSE) if("$ENV{CI}" STREQUAL "true" OR "$ENV{CONTINUOUS_INTEGRATION}" STREQUAL "true")
if(DEFINED ENV{CI})
if("$ENV{CI}" STREQUAL "true")
set(is_ci TRUE) set(is_ci TRUE)
endif() else()
set(is_ci FALSE)
endif() endif()
get_directory_property(has_parent PARENT_DIRECTORY) get_directory_property(has_parent PARENT_DIRECTORY)

View File

@@ -4,7 +4,6 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <cstdint> #include <cstdint>
#include <functional>
#include <limits> #include <limits>
#include <optional> #include <optional>
#include <ostream> #include <ostream>

View File

@@ -1,5 +1,4 @@
#include <xrpl/beast/core/CurrentThreadName.h> #include <xrpl/beast/core/CurrentThreadName.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <string> #include <string>
#include <string_view> #include <string_view>
@@ -96,11 +95,6 @@ setCurrentThreadNameImpl(std::string_view name)
std::cerr << "WARNING: Thread name \"" << name << "\" (length " std::cerr << "WARNING: Thread name \"" << name << "\" (length "
<< name.size() << ") exceeds maximum of " << name.size() << ") exceeds maximum of "
<< maxThreadNameLength << " characters on Linux.\n"; << maxThreadNameLength << " characters on Linux.\n";
XRPL_ASSERT(
false,
"beast::detail::setCurrentThreadNameImpl : Thread name exceeds "
"maximum length for Linux");
} }
#endif #endif
} }

View File

@@ -41,7 +41,7 @@ Job::queue_time() const
void void
Job::doJob() Job::doJob()
{ {
beast::setCurrentThreadName("j:" + mName); beast::setCurrentThreadName("doJob: " + mName);
m_loadEvent->start(); m_loadEvent->start();
m_loadEvent->setName(mName); m_loadEvent->setName(mName);

View File

@@ -88,15 +88,20 @@ public:
BEAST_EXPECT(stateB == 2); BEAST_EXPECT(stateB == 2);
} }
#if BOOST_OS_LINUX #if BOOST_OS_LINUX
// On Linux, verify that thread names within the 15 character limit // On Linux, verify that thread names longer than 15 characters
// are set correctly (the 16th character is reserved for the null // are truncated to 15 characters (the 16th character is reserved for
// terminator). // the null terminator).
{ {
testName( testName(
"123456789012345", "123456789012345",
"123456789012345"); // 15 chars, maximum allowed "123456789012345"); // 15 chars, no truncation
testName(
"1234567890123456", "123456789012345"); // 16 chars, truncated
testName(
"ThisIsAVeryLongThreadNameExceedingLimit",
"ThisIsAVeryLong"); // 39 chars, truncated
testName("", ""); // empty name testName("", ""); // empty name
testName("short", "short"); // short name testName("short", "short"); // short name, no truncation
} }
#endif #endif
} }

View File

@@ -56,7 +56,7 @@ public:
gate g1, g2; gate g1, g2;
std::shared_ptr<JobQueue::Coro> c; std::shared_ptr<JobQueue::Coro> c;
env.app().getJobQueue().postCoro( env.app().getJobQueue().postCoro(
jtCLIENT, "CoroTest", [&](auto const& cr) { jtCLIENT, "Coroutine-Test", [&](auto const& cr) {
c = cr; c = cr;
g1.signal(); g1.signal();
c->yield(); c->yield();
@@ -83,7 +83,7 @@ public:
gate g; gate g;
env.app().getJobQueue().postCoro( env.app().getJobQueue().postCoro(
jtCLIENT, "CoroTest", [&](auto const& c) { jtCLIENT, "Coroutine-Test", [&](auto const& c) {
c->post(); c->post();
c->yield(); c->yield();
g.signal(); g.signal();
@@ -109,7 +109,7 @@ public:
BEAST_EXPECT(*lv == -1); BEAST_EXPECT(*lv == -1);
gate g; gate g;
jq.addJob(jtCLIENT, "LocalValTest", [&]() { jq.addJob(jtCLIENT, "LocalValue-Test", [&]() {
this->BEAST_EXPECT(*lv == -1); this->BEAST_EXPECT(*lv == -1);
*lv = -2; *lv = -2;
this->BEAST_EXPECT(*lv == -2); this->BEAST_EXPECT(*lv == -2);
@@ -120,7 +120,7 @@ public:
for (int i = 0; i < N; ++i) for (int i = 0; i < N; ++i)
{ {
jq.postCoro(jtCLIENT, "CoroTest", [&, id = i](auto const& c) { jq.postCoro(jtCLIENT, "Coroutine-Test", [&, id = i](auto const& c) {
a[id] = c; a[id] = c;
g.signal(); g.signal();
c->yield(); c->yield();
@@ -148,7 +148,7 @@ public:
c->join(); c->join();
} }
jq.addJob(jtCLIENT, "LocalValTest", [&]() { jq.addJob(jtCLIENT, "LocalValue-Test", [&]() {
this->BEAST_EXPECT(*lv == -2); this->BEAST_EXPECT(*lv == -2);
g.signal(); g.signal();
}); });

View File

@@ -1,211 +0,0 @@
#include <test/jtx.h>
#include <test/jtx/Env.h>
#include <xrpld/overlay/Message.h>
#include <xrpld/overlay/detail/OverlayImpl.h>
#include <xrpld/overlay/detail/PeerImp.h>
#include <xrpld/overlay/detail/Tuning.h>
#include <xrpld/peerfinder/detail/SlotImp.h>
#include <xrpl/basics/make_SSLContext.h>
#include <xrpl/beast/unit_test.h>
#include <xrpl/nodestore/NodeObject.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/messages.h>
namespace xrpl {
namespace test {
using namespace jtx;
/**
* Test for TMGetObjectByHash reply size limiting.
*
* This verifies the fix that limits TMGetObjectByHash replies to
* Tuning::hardMaxReplyNodes to prevent excessive memory usage and
* potential DoS attacks from peers requesting large numbers of objects.
*/
class TMGetObjectByHash_test : public beast::unit_test::suite
{
using middle_type = boost::beast::tcp_stream;
using stream_type = boost::beast::ssl_stream<middle_type>;
using socket_type = boost::asio::ip::tcp::socket;
using shared_context = std::shared_ptr<boost::asio::ssl::context>;
/**
* Test peer that captures sent messages for verification.
*/
class PeerTest : public PeerImp
{
public:
PeerTest(
Application& app,
std::shared_ptr<PeerFinder::Slot> const& slot,
http_request_type&& request,
PublicKey const& publicKey,
ProtocolVersion protocol,
Resource::Consumer consumer,
std::unique_ptr<TMGetObjectByHash_test::stream_type>&& stream_ptr,
OverlayImpl& overlay)
: PeerImp(
app,
id_++,
slot,
std::move(request),
publicKey,
protocol,
consumer,
std::move(stream_ptr),
overlay)
{
}
~PeerTest() = default;
void
run() override
{
}
void
send(std::shared_ptr<Message> const& m) override
{
lastSentMessage_ = m;
}
std::shared_ptr<Message>
getLastSentMessage() const
{
return lastSentMessage_;
}
static void
resetId()
{
id_ = 0;
}
private:
inline static Peer::id_t id_ = 0;
std::shared_ptr<Message> lastSentMessage_;
};
shared_context context_{make_SSLContext("")};
ProtocolVersion protocolVersion_{1, 7};
std::shared_ptr<PeerTest>
createPeer(jtx::Env& env)
{
auto& overlay = dynamic_cast<OverlayImpl&>(env.app().overlay());
boost::beast::http::request<boost::beast::http::dynamic_body> request;
auto stream_ptr = std::make_unique<stream_type>(
socket_type(env.app().getIOContext()), *context_);
beast::IP::Endpoint local(
boost::asio::ip::make_address("172.1.1.1"), 51235);
beast::IP::Endpoint remote(
boost::asio::ip::make_address("172.1.1.2"), 51235);
PublicKey key(std::get<0>(randomKeyPair(KeyType::ed25519)));
auto consumer = overlay.resourceManager().newInboundEndpoint(remote);
auto [slot, _] = overlay.peerFinder().new_inbound_slot(local, remote);
auto peer = std::make_shared<PeerTest>(
env.app(),
slot,
std::move(request),
key,
protocolVersion_,
consumer,
std::move(stream_ptr),
overlay);
overlay.add_active(peer);
return peer;
}
std::shared_ptr<protocol::TMGetObjectByHash>
createRequest(size_t const numObjects, Env& env)
{
// Store objects in the NodeStore that will be found during the query
auto& nodeStore = env.app().getNodeStore();
// Create and store objects
std::vector<uint256> hashes;
hashes.reserve(numObjects);
for (int i = 0; i < numObjects; ++i)
{
uint256 hash(xrpl::sha512Half(i));
hashes.push_back(hash);
Blob data(100, static_cast<unsigned char>(i % 256));
nodeStore.store(
hotLEDGER,
std::move(data),
hash,
nodeStore.earliestLedgerSeq());
}
// Create a request with more objects than hardMaxReplyNodes
auto request = std::make_shared<protocol::TMGetObjectByHash>();
request->set_type(protocol::TMGetObjectByHash_ObjectType_otLEDGER);
request->set_query(true);
for (int i = 0; i < numObjects; ++i)
{
auto object = request->add_objects();
object->set_hash(hashes[i].data(), hashes[i].size());
object->set_ledgerseq(i);
}
return request;
}
/**
* Test that reply is limited to hardMaxReplyNodes when more objects
* are requested than the limit allows.
*/
void
testReplyLimit(size_t const numObjects, int const expectedReplySize)
{
testcase("Reply Limit");
Env env(*this);
PeerTest::resetId();
auto peer = createPeer(env);
auto request = createRequest(numObjects, env);
// Call the onMessage handler
peer->onMessage(request);
// Verify that a reply was sent
auto sentMessage = peer->getLastSentMessage();
BEAST_EXPECT(sentMessage != nullptr);
// Parse the reply message
auto const& buffer =
sentMessage->getBuffer(compression::Compressed::Off);
BEAST_EXPECT(buffer.size() > 6);
// Skip the message header (6 bytes: 4 for size, 2 for type)
protocol::TMGetObjectByHash reply;
BEAST_EXPECT(
reply.ParseFromArray(buffer.data() + 6, buffer.size() - 6) == true);
// Verify the reply is limited to expectedReplySize
BEAST_EXPECT(reply.objects_size() == expectedReplySize);
}
void
run() override
{
int const limit = static_cast<int>(Tuning::hardMaxReplyNodes);
testReplyLimit(limit + 1, limit);
testReplyLimit(limit, limit);
testReplyLimit(limit - 1, limit - 1);
}
};
BEAST_DEFINE_TESTSUITE(TMGetObjectByHash, overlay, xrpl);
} // namespace test
} // namespace xrpl

View File

@@ -5,8 +5,6 @@
#include <test/jtx/multisign.h> #include <test/jtx/multisign.h>
#include <test/jtx/xchain_bridge.h> #include <test/jtx/xchain_bridge.h>
#include <xrpld/app/tx/apply.h>
#include <xrpl/beast/unit_test.h> #include <xrpl/beast/unit_test.h>
#include <xrpl/json/json_value.h> #include <xrpl/json/json_value.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -32,7 +30,6 @@ enum class FieldType {
CurrencyField, CurrencyField,
HashField, HashField,
HashOrObjectField, HashOrObjectField,
FixedHashField,
IssueField, IssueField,
ObjectField, ObjectField,
StringField, StringField,
@@ -89,7 +86,6 @@ getTypeName(FieldType typeID)
case FieldType::CurrencyField: case FieldType::CurrencyField:
return "Currency"; return "Currency";
case FieldType::HashField: case FieldType::HashField:
case FieldType::FixedHashField:
return "hex string"; return "hex string";
case FieldType::HashOrObjectField: case FieldType::HashOrObjectField:
return "hex string or object"; return "hex string or object";
@@ -206,7 +202,6 @@ class LedgerEntry_test : public beast::unit_test::suite
static auto const& badBlobValues = remove({3, 7, 8, 16}); static auto const& badBlobValues = remove({3, 7, 8, 16});
static auto const& badCurrencyValues = remove({14}); static auto const& badCurrencyValues = remove({14});
static auto const& badHashValues = remove({2, 3, 7, 8, 16}); static auto const& badHashValues = remove({2, 3, 7, 8, 16});
static auto const& badFixedHashValues = remove({1, 2, 3, 4, 7, 8, 16});
static auto const& badIndexValues = remove({12, 16, 18, 19}); static auto const& badIndexValues = remove({12, 16, 18, 19});
static auto const& badUInt32Values = remove({2, 3}); static auto const& badUInt32Values = remove({2, 3});
static auto const& badUInt64Values = remove({2, 3}); static auto const& badUInt64Values = remove({2, 3});
@@ -227,8 +222,6 @@ class LedgerEntry_test : public beast::unit_test::suite
return badHashValues; return badHashValues;
case FieldType::HashOrObjectField: case FieldType::HashOrObjectField:
return badIndexValues; return badIndexValues;
case FieldType::FixedHashField:
return badFixedHashValues;
case FieldType::IssueField: case FieldType::IssueField:
return badIssueValues; return badIssueValues;
case FieldType::UInt32Field: case FieldType::UInt32Field:
@@ -724,12 +717,7 @@ class LedgerEntry_test : public beast::unit_test::suite
} }
// negative tests // negative tests
testMalformedField( runLedgerEntryTest(env, jss::amendments);
env,
Json::Value{},
jss::amendments,
FieldType::FixedHashField,
"malformedRequest");
} }
void void
@@ -1550,12 +1538,7 @@ class LedgerEntry_test : public beast::unit_test::suite
} }
// negative tests // negative tests
testMalformedField( runLedgerEntryTest(env, jss::fee);
env,
Json::Value{},
jss::fee,
FieldType::FixedHashField,
"malformedRequest");
} }
void void
@@ -1578,12 +1561,7 @@ class LedgerEntry_test : public beast::unit_test::suite
} }
// negative tests // negative tests
testMalformedField( runLedgerEntryTest(env, jss::hashes);
env,
Json::Value{},
jss::hashes,
FieldType::FixedHashField,
"malformedRequest");
} }
void void
@@ -1708,12 +1686,7 @@ class LedgerEntry_test : public beast::unit_test::suite
} }
// negative tests // negative tests
testMalformedField( runLedgerEntryTest(env, jss::nunl);
env,
Json::Value{},
jss::nunl,
FieldType::FixedHashField,
"malformedRequest");
} }
void void
@@ -2370,438 +2343,6 @@ class LedgerEntry_test : public beast::unit_test::suite
} }
} }
/// Test the ledger entry types that don't take parameters
void
testFixed()
{
using namespace test::jtx;
Account const alice{"alice"};
Account const bob{"bob"};
Env env{*this, envconfig([](auto cfg) {
cfg->START_UP = Config::FRESH;
return cfg;
})};
env.close();
/** Verifies that the RPC result has the expected data
*
* @param good: Indicates that the request should have succeeded
* and returned a ledger object of `expectedType` type.
* @param jv: The RPC result Json value
* @param expectedType: The type that the ledger object should
* have if "good".
* @param expectedError: Optional. The expected error if not
* good. Defaults to "entryNotFound".
*/
auto checkResult =
[&](bool good,
Json::Value const& jv,
Json::StaticString const& expectedType,
std::optional<std::string> const& expectedError = {}) {
if (good)
{
BEAST_EXPECTS(
jv.isObject() && jv.isMember(jss::result) &&
!jv[jss::result].isMember(jss::error) &&
jv[jss::result].isMember(jss::node) &&
jv[jss::result][jss::node].isMember(
sfLedgerEntryType.jsonName) &&
jv[jss::result][jss::node]
[sfLedgerEntryType.jsonName] == expectedType,
to_string(jv));
}
else
{
BEAST_EXPECTS(
jv.isObject() && jv.isMember(jss::result) &&
jv[jss::result].isMember(jss::error) &&
!jv[jss::result].isMember(jss::node) &&
jv[jss::result][jss::error] ==
expectedError.value_or("entryNotFound"),
to_string(jv));
}
};
/** Runs a series of tests for a given fixed-position ledger
* entry.
*
* @param field: The Json request field to use.
* @param expectedType: The type that the ledger object should
* have if "good".
* @param expectedKey: The keylet of the fixed object.
* @param good: Indicates whether the object is expected to
* exist.
*/
auto test = [&](Json::StaticString const& field,
Json::StaticString const& expectedType,
Keylet const& expectedKey,
bool good) {
testcase << expectedType.c_str() << (good ? "" : " not")
<< " found";
auto const hexKey = strHex(expectedKey.key);
{
// Test bad values
// "field":null
Json::Value params;
params[jss::ledger_index] = jss::validated;
params[field] = Json::nullValue;
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
checkResult(false, jv, expectedType, "malformedRequest");
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
}
{
Json::Value params;
// "field":"string"
params[jss::ledger_index] = jss::validated;
params[field] = "arbitrary string";
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
checkResult(false, jv, expectedType, "malformedRequest");
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
}
{
Json::Value params;
// "field":false
params[jss::ledger_index] = jss::validated;
params[field] = false;
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
checkResult(false, jv, expectedType, "invalidParams");
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
}
{
Json::Value params;
// "field":[incorrect index hash]
auto const badKey = strHex(expectedKey.key + uint256{1});
params[jss::ledger_index] = jss::validated;
params[field] = badKey;
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
checkResult(false, jv, expectedType, "entryNotFound");
BEAST_EXPECTS(
jv[jss::result][jss::index] == badKey, to_string(jv));
}
{
Json::Value params;
// "index":"field" using API 2
params[jss::ledger_index] = jss::validated;
params[jss::index] = field;
params[jss::api_version] = 2;
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
checkResult(false, jv, expectedType, "malformedRequest");
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
}
std::string const pdIdx = [&]() {
{
Json::Value params;
// Test good values
// Use the "field":true notation
params[jss::ledger_index] = jss::validated;
params[field] = true;
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
// Index will always be returned for valid parameters.
std::string const pdIdx =
jv[jss::result][jss::index].asString();
BEAST_EXPECTS(hexKey == pdIdx, to_string(jv));
checkResult(good, jv, expectedType);
return pdIdx;
}
}();
{
Json::Value params;
// "field":"[index hash]"
params[jss::ledger_index] = jss::validated;
params[field] = hexKey;
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
checkResult(good, jv, expectedType);
BEAST_EXPECT(jv[jss::result][jss::index].asString() == hexKey);
}
{
// Bad value
// Use the "index":"field" notation with API 2
Json::Value params;
params[jss::ledger_index] = jss::validated;
params[jss::index] = field;
params[jss::api_version] = 2;
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
checkResult(false, jv, expectedType, "malformedRequest");
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
}
{
Json::Value params;
// Use the "index":"field" notation with API 3
params[jss::ledger_index] = jss::validated;
params[jss::index] = field;
params[jss::api_version] = 3;
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
// Index is correct either way
BEAST_EXPECT(jv[jss::result][jss::index].asString() == hexKey);
checkResult(good, jv, expectedType);
}
{
Json::Value params;
// Use the "index":"[index hash]" notation
params[jss::ledger_index] = jss::validated;
params[jss::index] = pdIdx;
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
// Index is correct either way
BEAST_EXPECT(jv[jss::result][jss::index].asString() == hexKey);
checkResult(good, jv, expectedType);
}
};
test(jss::amendments, jss::Amendments, keylet::amendments(), true);
test(jss::fee, jss::FeeSettings, keylet::fees(), true);
// There won't be an nunl
test(jss::nunl, jss::NegativeUNL, keylet::negativeUNL(), false);
// Can only get the short skip list this way
test(jss::hashes, jss::LedgerHashes, keylet::skip(), true);
}
void
testHashes()
{
using namespace test::jtx;
Account const alice{"alice"};
Account const bob{"bob"};
Env env{*this, envconfig([](auto cfg) {
cfg->START_UP = Config::FRESH;
return cfg;
})};
env.close();
/** Verifies that the RPC result has the expected data
*
* @param good: Indicates that the request should have succeeded
* and returned a ledger object of `expectedType` type.
* @param jv: The RPC result Json value
* @param expectedCount: The number of Hashes expected in the
* object if "good".
* @param expectedError: Optional. The expected error if not
* good. Defaults to "entryNotFound".
*/
auto checkResult =
[&](bool good,
Json::Value const& jv,
int expectedCount,
std::optional<std::string> const& expectedError = {}) {
if (good)
{
BEAST_EXPECTS(
jv.isObject() && jv.isMember(jss::result) &&
!jv[jss::result].isMember(jss::error) &&
jv[jss::result].isMember(jss::node) &&
jv[jss::result][jss::node].isMember(
sfLedgerEntryType.jsonName) &&
jv[jss::result][jss::node]
[sfLedgerEntryType.jsonName] == jss::LedgerHashes,
to_string(jv));
BEAST_EXPECTS(
jv[jss::result].isMember(jss::node) &&
jv[jss::result][jss::node].isMember("Hashes") &&
jv[jss::result][jss::node]["Hashes"].size() ==
expectedCount,
to_string(jv[jss::result][jss::node]["Hashes"].size()));
}
else
{
BEAST_EXPECTS(
jv.isObject() && jv.isMember(jss::result) &&
jv[jss::result].isMember(jss::error) &&
!jv[jss::result].isMember(jss::node) &&
jv[jss::result][jss::error] ==
expectedError.value_or("entryNotFound"),
to_string(jv));
}
};
/** Runs a series of tests for a given ledger index.
*
* @param ledger: The ledger index value of the "hashes" request
* parameter. May not necessarily be a number.
* @param expectedKey: The expected keylet of the object.
* @param good: Indicates whether the object is expected to
* exist.
* @param expectedCount: The number of Hashes expected in the
* object if "good".
*/
auto test = [&](Json::Value ledger,
Keylet const& expectedKey,
bool good,
int expectedCount = 0) {
testcase << "LedgerHashes: seq: " << env.current()->header().seq
<< " \"hashes\":" << to_string(ledger)
<< (good ? "" : " not") << " found";
auto const hexKey = strHex(expectedKey.key);
{
// Test bad values
// "hashes":null
Json::Value params;
params[jss::ledger_index] = jss::validated;
params[jss::hashes] = Json::nullValue;
auto jv = env.rpc("json", "ledger_entry", to_string(params));
checkResult(false, jv, 0, "malformedRequest");
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
}
{
Json::Value params;
// "hashes":"non-uint string"
params[jss::ledger_index] = jss::validated;
params[jss::hashes] = "arbitrary string";
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
checkResult(false, jv, 0, "malformedRequest");
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
}
{
Json::Value params;
// "hashes":"uint string" is invalid, too
params[jss::ledger_index] = jss::validated;
params[jss::hashes] = "10";
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
checkResult(false, jv, 0, "malformedRequest");
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
}
{
Json::Value params;
// "hashes":false
params[jss::ledger_index] = jss::validated;
params[jss::hashes] = false;
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
checkResult(false, jv, 0, "invalidParams");
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
}
{
Json::Value params;
// "hashes":-1
params[jss::ledger_index] = jss::validated;
params[jss::hashes] = -1;
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
checkResult(false, jv, 0, "internal");
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
}
// "hashes":[incorrect index hash]
{
Json::Value params;
auto const badKey = strHex(expectedKey.key + uint256{1});
params[jss::ledger_index] = jss::validated;
params[jss::hashes] = badKey;
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
checkResult(false, jv, 0, "entryNotFound");
BEAST_EXPECT(jv[jss::result][jss::index] == badKey);
}
{
Json::Value params;
// Test good values
// Use the "hashes":ledger notation
params[jss::ledger_index] = jss::validated;
params[jss::hashes] = ledger;
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
checkResult(good, jv, expectedCount);
// Index will always be returned for valid parameters.
std::string const pdIdx =
jv[jss::result][jss::index].asString();
BEAST_EXPECTS(hexKey == pdIdx, strHex(pdIdx));
}
{
Json::Value params;
// "hashes":"[index hash]"
params[jss::ledger_index] = jss::validated;
params[jss::hashes] = hexKey;
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
checkResult(good, jv, expectedCount);
// Index is correct either way
BEAST_EXPECTS(
hexKey == jv[jss::result][jss::index].asString(),
strHex(jv[jss::result][jss::index].asString()));
}
{
Json::Value params;
// Use the "index":"[index hash]" notation
params[jss::ledger_index] = jss::validated;
params[jss::index] = hexKey;
auto const jv =
env.rpc("json", "ledger_entry", to_string(params));
checkResult(good, jv, expectedCount);
// Index is correct either way
BEAST_EXPECTS(
hexKey == jv[jss::result][jss::index].asString(),
strHex(jv[jss::result][jss::index].asString()));
}
};
// short skip list
test(true, keylet::skip(), true, 2);
// long skip list at index 0
test(1, keylet::skip(1), false);
// long skip list at index 1
test(1 << 17, keylet::skip(1 << 17), false);
// Close more ledgers, but stop short of the flag ledger
for (auto i = env.current()->seq(); i <= 250; ++i)
env.close();
// short skip list
test(true, keylet::skip(), true, 249);
// long skip list at index 0
test(1, keylet::skip(1), false);
// long skip list at index 1
test(1 << 17, keylet::skip(1 << 17), false);
// Close a flag ledger so the first "long" skip list is created
for (auto i = env.current()->seq(); i <= 260; ++i)
env.close();
// short skip list
test(true, keylet::skip(), true, 256);
// long skip list at index 0
test(1, keylet::skip(1), true, 1);
// long skip list at index 1
test(1 << 17, keylet::skip(1 << 17), false);
}
void void
testCLI() testCLI()
{ {
@@ -2859,8 +2400,6 @@ public:
testOracleLedgerEntry(); testOracleLedgerEntry();
testMPT(); testMPT();
testPermissionedDomain(); testPermissionedDomain();
testFixed();
testHashes();
testCLI(); testCLI();
} }
}; };

View File

@@ -119,7 +119,9 @@ RCLConsensus::Adaptor::acquireLedger(LedgerHash const& hash)
acquiringLedger_ = hash; acquiringLedger_ = hash;
app_.getJobQueue().addJob( app_.getJobQueue().addJob(
jtADVANCE, "GetConsL1", [id = hash, &app = app_, this]() { jtADVANCE,
"getConsensusLedger1",
[id = hash, &app = app_, this]() {
JLOG(j_.debug()) JLOG(j_.debug())
<< "JOB advanceLedger getConsensusLedger1 started"; << "JOB advanceLedger getConsensusLedger1 started";
app.getInboundLedgers().acquireAsync( app.getInboundLedgers().acquireAsync(
@@ -418,7 +420,7 @@ RCLConsensus::Adaptor::onAccept(
{ {
app_.getJobQueue().addJob( app_.getJobQueue().addJob(
jtACCEPT, jtACCEPT,
"AcceptLedger", "acceptLedger",
[=, this, cj = std::move(consensusJson)]() mutable { [=, this, cj = std::move(consensusJson)]() mutable {
// Note that no lock is held or acquired during this job. // Note that no lock is held or acquired during this job.
// This is because generic Consensus guarantees that once a ledger // This is because generic Consensus guarantees that once a ledger

View File

@@ -122,8 +122,10 @@ RCLValidationsAdaptor::acquire(LedgerHash const& hash)
Application* pApp = &app_; Application* pApp = &app_;
app_.getJobQueue().addJob(jtADVANCE, "GetConsL2", [pApp, hash, this]() { app_.getJobQueue().addJob(
JLOG(j_.debug()) << "JOB advanceLedger getConsensusLedger2 started"; jtADVANCE, "getConsensusLedger2", [pApp, hash, this]() {
JLOG(j_.debug())
<< "JOB advanceLedger getConsensusLedger2 started";
pApp->getInboundLedgers().acquireAsync( pApp->getInboundLedgers().acquireAsync(
hash, 0, InboundLedger::Reason::CONSENSUS); hash, 0, InboundLedger::Reason::CONSENSUS);
}); });

View File

@@ -46,7 +46,7 @@ ConsensusTransSetSF::gotNode(
"xrpl::ConsensusTransSetSF::gotNode : transaction hash " "xrpl::ConsensusTransSetSF::gotNode : transaction hash "
"match"); "match");
auto const pap = &app_; auto const pap = &app_;
app_.getJobQueue().addJob(jtTRANSACTION, "TxsToTxn", [pap, stx]() { app_.getJobQueue().addJob(jtTRANSACTION, "TXS->TXN", [pap, stx]() {
pap->getOPs().submitTransaction(stx); pap->getOPs().submitTransaction(stx);
}); });
} }

View File

@@ -48,9 +48,9 @@ OrderBookDB::setup(std::shared_ptr<ReadView const> const& ledger)
update(ledger); update(ledger);
else else
app_.getJobQueue().addJob( app_.getJobQueue().addJob(
jtUPDATE_PF, "OrderBookUpd", [this, ledger]() { jtUPDATE_PF,
update(ledger); "OrderBookDB::update: " + std::to_string(ledger->seq()),
}); [this, ledger]() { update(ledger); });
} }
} }

View File

@@ -454,7 +454,7 @@ InboundLedger::done()
// We hold the PeerSet lock, so must dispatch // We hold the PeerSet lock, so must dispatch
app_.getJobQueue().addJob( app_.getJobQueue().addJob(
jtLEDGER_DATA, "AcqDone", [self = shared_from_this()]() { jtLEDGER_DATA, "AcquisitionDone", [self = shared_from_this()]() {
if (self->complete_ && !self->failed_) if (self->complete_ && !self->failed_)
{ {
self->app_.getLedgerMaster().checkAccept(self->getLedger()); self->app_.getLedgerMaster().checkAccept(self->getLedger());

View File

@@ -192,7 +192,7 @@ public:
// dispatch // dispatch
if (ledger->gotData(std::weak_ptr<Peer>(peer), packet)) if (ledger->gotData(std::weak_ptr<Peer>(peer), packet))
app_.getJobQueue().addJob( app_.getJobQueue().addJob(
jtLEDGER_DATA, "ProcessLData", [ledger]() { jtLEDGER_DATA, "processLedgerData", [ledger]() {
ledger->runData(); ledger->runData();
}); });
@@ -207,7 +207,7 @@ public:
if (packet->type() == protocol::liAS_NODE) if (packet->type() == protocol::liAS_NODE)
{ {
app_.getJobQueue().addJob( app_.getJobQueue().addJob(
jtLEDGER_DATA, "GotStaleData", [this, packet]() { jtLEDGER_DATA, "gotStaleData", [this, packet]() {
gotStaleData(packet); gotStaleData(packet);
}); });
} }

View File

@@ -21,7 +21,7 @@ LedgerDeltaAcquire::LedgerDeltaAcquire(
ledgerHash, ledgerHash,
LedgerReplayParameters::SUB_TASK_TIMEOUT, LedgerReplayParameters::SUB_TASK_TIMEOUT,
{jtREPLAY_TASK, {jtREPLAY_TASK,
"LedReplDelta", "LedgerReplayDelta",
LedgerReplayParameters::MAX_QUEUED_TASKS}, LedgerReplayParameters::MAX_QUEUED_TASKS},
app.journal("LedgerReplayDelta")) app.journal("LedgerReplayDelta"))
, inboundLedgers_(inboundLedgers) , inboundLedgers_(inboundLedgers)
@@ -225,7 +225,7 @@ LedgerDeltaAcquire::onLedgerBuilt(
} }
app_.getJobQueue().addJob( app_.getJobQueue().addJob(
jtREPLAY_TASK, jtREPLAY_TASK,
"OnLedBuilt", "onLedgerBuilt",
[=, ledger = this->fullLedger_, &app = this->app_]() { [=, ledger = this->fullLedger_, &app = this->app_]() {
for (auto reason : reasons) for (auto reason : reasons)
{ {

View File

@@ -1344,7 +1344,7 @@ LedgerMaster::tryAdvance()
if (!mAdvanceThread && !mValidLedger.empty()) if (!mAdvanceThread && !mValidLedger.empty())
{ {
mAdvanceThread = true; mAdvanceThread = true;
app_.getJobQueue().addJob(jtADVANCE, "AdvanceLedger", [this]() { app_.getJobQueue().addJob(jtADVANCE, "advanceLedger", [this]() {
std::unique_lock sl(m_mutex); std::unique_lock sl(m_mutex);
XRPL_ASSERT( XRPL_ASSERT(
@@ -1482,7 +1482,7 @@ bool
LedgerMaster::newPathRequest() LedgerMaster::newPathRequest()
{ {
std::unique_lock ml(m_mutex); std::unique_lock ml(m_mutex);
mPathFindNewRequest = newPFWork("PthFindNewReq", ml); mPathFindNewRequest = newPFWork("pf:newRequest", ml);
return mPathFindNewRequest; return mPathFindNewRequest;
} }
@@ -1503,7 +1503,7 @@ LedgerMaster::newOrderBookDB()
std::unique_lock ml(m_mutex); std::unique_lock ml(m_mutex);
mPathLedger.reset(); mPathLedger.reset();
return newPFWork("PthFindOBDB", ml); return newPFWork("pf:newOBDB", ml);
} }
/** A thread needs to be dispatched to handle pathfinding work of some kind. /** A thread needs to be dispatched to handle pathfinding work of some kind.
@@ -1841,7 +1841,7 @@ LedgerMaster::fetchForHistory(
mFillInProgress = seq; mFillInProgress = seq;
} }
app_.getJobQueue().addJob( app_.getJobQueue().addJob(
jtADVANCE, "TryFill", [this, ledger]() { jtADVANCE, "tryFill", [this, ledger]() {
tryFill(ledger); tryFill(ledger);
}); });
} }
@@ -1980,7 +1980,7 @@ LedgerMaster::doAdvance(std::unique_lock<std::recursive_mutex>& sl)
} }
app_.getOPs().clearNeedNetworkLedger(); app_.getOPs().clearNeedNetworkLedger();
progress = newPFWork("PthFindNewLed", sl); progress = newPFWork("pf:newLedger", sl);
} }
if (progress) if (progress)
mAdvanceWork = true; mAdvanceWork = true;
@@ -2011,7 +2011,7 @@ LedgerMaster::gotFetchPack(bool progress, std::uint32_t seq)
{ {
if (!mGotFetchPackThread.test_and_set(std::memory_order_acquire)) if (!mGotFetchPackThread.test_and_set(std::memory_order_acquire))
{ {
app_.getJobQueue().addJob(jtLEDGER_DATA, "GotFetchPack", [&]() { app_.getJobQueue().addJob(jtLEDGER_DATA, "gotFetchPack", [&]() {
app_.getInboundLedgers().gotFetchPack(); app_.getInboundLedgers().gotFetchPack();
mGotFetchPackThread.clear(std::memory_order_release); mGotFetchPackThread.clear(std::memory_order_release);
}); });

View File

@@ -77,7 +77,7 @@ LedgerReplayTask::LedgerReplayTask(
parameter.finishHash_, parameter.finishHash_,
LedgerReplayParameters::TASK_TIMEOUT, LedgerReplayParameters::TASK_TIMEOUT,
{jtREPLAY_TASK, {jtREPLAY_TASK,
"LedReplTask", "LedgerReplayTask",
LedgerReplayParameters::MAX_QUEUED_TASKS}, LedgerReplayParameters::MAX_QUEUED_TASKS},
app.journal("LedgerReplayTask")) app.journal("LedgerReplayTask"))
, inboundLedgers_(inboundLedgers) , inboundLedgers_(inboundLedgers)

View File

@@ -16,7 +16,7 @@ SkipListAcquire::SkipListAcquire(
ledgerHash, ledgerHash,
LedgerReplayParameters::SUB_TASK_TIMEOUT, LedgerReplayParameters::SUB_TASK_TIMEOUT,
{jtREPLAY_TASK, {jtREPLAY_TASK,
"SkipListAcq", "SkipListAcquire",
LedgerReplayParameters::MAX_QUEUED_TASKS}, LedgerReplayParameters::MAX_QUEUED_TASKS},
app.journal("LedgerReplaySkipList")) app.journal("LedgerReplaySkipList"))
, inboundLedgers_(inboundLedgers) , inboundLedgers_(inboundLedgers)

View File

@@ -27,7 +27,7 @@ TransactionAcquire::TransactionAcquire(
app, app,
hash, hash,
TX_ACQUIRE_TIMEOUT, TX_ACQUIRE_TIMEOUT,
{jtTXN_DATA, "TxAcq", {}}, {jtTXN_DATA, "TransactionAcquire", {}},
app.journal("TransactionAcquire")) app.journal("TransactionAcquire"))
, mHaveRoot(false) , mHaveRoot(false)
, mPeerSet(std::move(peerSet)) , mPeerSet(std::move(peerSet))
@@ -60,7 +60,7 @@ TransactionAcquire::done()
// just updates the consensus and related structures when we acquire // just updates the consensus and related structures when we acquire
// a transaction set. No need to update them if we're shutting down. // a transaction set. No need to update them if we're shutting down.
app_.getJobQueue().addJob( app_.getJobQueue().addJob(
jtTXN_DATA, "ComplAcquire", [pap, hash, map]() { jtTXN_DATA, "completeAcquire", [pap, hash, map]() {
pap->getInboundTransactions().giveSet(hash, map, true); pap->getInboundTransactions().giveSet(hash, map, true);
}); });
} }

View File

@@ -331,7 +331,8 @@ run(int argc, char** argv)
{ {
using namespace std; using namespace std;
beast::setCurrentThreadName("main"); beast::setCurrentThreadName(
"rippled: main " + BuildInfo::getVersionString());
po::variables_map vm; po::variables_map vm;

View File

@@ -12,8 +12,9 @@ NodeStoreScheduler::scheduleTask(NodeStore::Task& task)
if (jobQueue_.isStopped()) if (jobQueue_.isStopped())
return; return;
if (!jobQueue_.addJob( if (!jobQueue_.addJob(jtWRITE, "NodeObject::store", [&task]() {
jtWRITE, "NObjStore", [&task]() { task.performScheduledTask(); })) task.performScheduledTask();
}))
{ {
// Job not added, presumably because we're shutting down. // Job not added, presumably because we're shutting down.
// Recover by executing the task synchronously. // Recover by executing the task synchronously.

View File

@@ -981,7 +981,7 @@ NetworkOPsImp::setHeartbeatTimer()
heartbeatTimer_, heartbeatTimer_,
mConsensus.parms().ledgerGRANULARITY, mConsensus.parms().ledgerGRANULARITY,
[this]() { [this]() {
m_job_queue.addJob(jtNETOP_TIMER, "NetHeart", [this]() { m_job_queue.addJob(jtNETOP_TIMER, "NetOPs.heartbeat", [this]() {
processHeartbeatTimer(); processHeartbeatTimer();
}); });
}, },
@@ -997,7 +997,7 @@ NetworkOPsImp::setClusterTimer()
clusterTimer_, clusterTimer_,
10s, 10s,
[this]() { [this]() {
m_job_queue.addJob(jtNETOP_CLUSTER, "NetCluster", [this]() { m_job_queue.addJob(jtNETOP_CLUSTER, "NetOPs.cluster", [this]() {
processClusterTimer(); processClusterTimer();
}); });
}, },
@@ -1225,7 +1225,7 @@ NetworkOPsImp::submitTransaction(std::shared_ptr<STTx const> const& iTrans)
auto tx = std::make_shared<Transaction>(trans, reason, app_); auto tx = std::make_shared<Transaction>(trans, reason, app_);
m_job_queue.addJob(jtTRANSACTION, "SubmitTxn", [this, tx]() { m_job_queue.addJob(jtTRANSACTION, "submitTxn", [this, tx]() {
auto t = tx; auto t = tx;
processTransaction(t, false, false, FailHard::no); processTransaction(t, false, false, FailHard::no);
}); });
@@ -1323,7 +1323,7 @@ NetworkOPsImp::doTransactionAsync(
if (mDispatchState == DispatchState::none) if (mDispatchState == DispatchState::none)
{ {
if (m_job_queue.addJob( if (m_job_queue.addJob(
jtBATCH, "TxBatchAsync", [this]() { transactionBatch(); })) jtBATCH, "transactionBatch", [this]() { transactionBatch(); }))
{ {
mDispatchState = DispatchState::scheduled; mDispatchState = DispatchState::scheduled;
} }
@@ -1370,7 +1370,7 @@ NetworkOPsImp::doTransactionSyncBatch(
if (mTransactions.size()) if (mTransactions.size())
{ {
// More transactions need to be applied, but by another job. // More transactions need to be applied, but by another job.
if (m_job_queue.addJob(jtBATCH, "TxBatchSync", [this]() { if (m_job_queue.addJob(jtBATCH, "transactionBatch", [this]() {
transactionBatch(); transactionBatch();
})) }))
{ {
@@ -3208,16 +3208,19 @@ NetworkOPsImp::reportFeeChange()
if (f != mLastFeeSummary) if (f != mLastFeeSummary)
{ {
m_job_queue.addJob( m_job_queue.addJob(
jtCLIENT_FEE_CHANGE, "PubFee", [this]() { pubServer(); }); jtCLIENT_FEE_CHANGE, "reportFeeChange->pubServer", [this]() {
pubServer();
});
} }
} }
void void
NetworkOPsImp::reportConsensusStateChange(ConsensusPhase phase) NetworkOPsImp::reportConsensusStateChange(ConsensusPhase phase)
{ {
m_job_queue.addJob(jtCLIENT_CONSENSUS, "PubCons", [this, phase]() { m_job_queue.addJob(
pubConsensus(phase); jtCLIENT_CONSENSUS,
}); "reportConsensusStateChange->pubConsensus",
[this, phase]() { pubConsensus(phase); });
} }
inline void inline void
@@ -3725,7 +3728,7 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo)
app_.getJobQueue().addJob( app_.getJobQueue().addJob(
jtCLIENT_ACCT_HIST, jtCLIENT_ACCT_HIST,
"HistTxStream", "AccountHistoryTxStream",
[this, dbType = databaseType, subInfo]() { [this, dbType = databaseType, subInfo]() {
auto const& accountId = subInfo.index_->accountId_; auto const& accountId = subInfo.index_->accountId_;
auto& lastLedgerSeq = subInfo.index_->historyLastLedgerSeq_; auto& lastLedgerSeq = subInfo.index_->historyLastLedgerSeq_;

View File

@@ -129,6 +129,11 @@ ValidatorSite::load(
{ {
try try
{ {
// This is not super efficient, but it doesn't happen often.
bool found = std::ranges::any_of(sites_, [&uri](auto const& site) {
return site.loadedResource->uri == uri;
});
if (!found)
sites_.emplace_back(uri); sites_.emplace_back(uri);
} }
catch (std::exception const& e) catch (std::exception const& e)
@@ -191,6 +196,17 @@ ValidatorSite::setTimer(
std::lock_guard<std::mutex> const& site_lock, std::lock_guard<std::mutex> const& site_lock,
std::lock_guard<std::mutex> const& state_lock) std::lock_guard<std::mutex> const& state_lock)
{ {
if (!sites_.empty() && //
std::ranges::all_of(sites_, [](auto const& site) {
return site.lastRefreshStatus.has_value();
}))
{
// If all of the sites have been handled at least once (including
// errors and timeouts), call missingSite, which will load the cache
// files for any lists that are still unavailable.
missingSite(site_lock);
}
auto next = std::min_element( auto next = std::min_element(
sites_.begin(), sites_.end(), [](Site const& a, Site const& b) { sites_.begin(), sites_.end(), [](Site const& a, Site const& b) {
return a.nextRefresh < b.nextRefresh; return a.nextRefresh < b.nextRefresh;
@@ -303,13 +319,16 @@ ValidatorSite::onRequestTimeout(std::size_t siteIdx, error_code const& ec)
// processes a network error. Usually, this function runs first, // processes a network error. Usually, this function runs first,
// but on extremely rare occasions, the response handler can run // but on extremely rare occasions, the response handler can run
// first, which will leave activeResource empty. // first, which will leave activeResource empty.
auto const& site = sites_[siteIdx]; auto& site = sites_[siteIdx];
if (site.activeResource) if (site.activeResource)
JLOG(j_.warn()) << "Request for " << site.activeResource->uri JLOG(j_.warn()) << "Request for " << site.activeResource->uri
<< " took too long"; << " took too long";
else else
JLOG(j_.error()) << "Request took too long, but a response has " JLOG(j_.error()) << "Request took too long, but a response has "
"already been processed"; "already been processed";
if (!site.lastRefreshStatus)
site.lastRefreshStatus.emplace(Site::Status{
clock_type::now(), ListDisposition::invalid, "timeout"});
} }
std::lock_guard lock_state{state_mutex_}; std::lock_guard lock_state{state_mutex_};

View File

@@ -1158,7 +1158,7 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMManifests> const& m)
fee_.update(Resource::feeModerateBurdenPeer, "oversize"); fee_.update(Resource::feeModerateBurdenPeer, "oversize");
app_.getJobQueue().addJob( app_.getJobQueue().addJob(
jtMANIFEST, "RcvManifests", [this, that = shared_from_this(), m]() { jtMANIFEST, "receiveManifests", [this, that = shared_from_this(), m]() {
overlay_.onManifests(m, that); overlay_.onManifests(m, that);
}); });
} }
@@ -1351,8 +1351,8 @@ PeerImp::handleTransaction(
{ {
// If we've never been in synch, there's nothing we can do // If we've never been in synch, there's nothing we can do
// with a transaction // with a transaction
JLOG(p_journal_.debug()) JLOG(p_journal_.debug()) << "Ignoring incoming transaction: "
<< "Ignoring incoming transaction: Need network ledger"; << "Need network ledger";
return; return;
} }
@@ -1452,7 +1452,7 @@ PeerImp::handleTransaction(
{ {
app_.getJobQueue().addJob( app_.getJobQueue().addJob(
jtTRANSACTION, jtTRANSACTION,
"RcvCheckTx", "recvTransaction->checkTransaction",
[weak = std::weak_ptr<PeerImp>(shared_from_this()), [weak = std::weak_ptr<PeerImp>(shared_from_this()),
flags, flags,
checkSignature, checkSignature,
@@ -1555,7 +1555,7 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMGetLedger> const& m)
// Queue a job to process the request // Queue a job to process the request
std::weak_ptr<PeerImp> weak = shared_from_this(); std::weak_ptr<PeerImp> weak = shared_from_this();
app_.getJobQueue().addJob(jtLEDGER_REQ, "RcvGetLedger", [weak, m]() { app_.getJobQueue().addJob(jtLEDGER_REQ, "recvGetLedger", [weak, m]() {
if (auto peer = weak.lock()) if (auto peer = weak.lock())
peer->processLedgerRequest(m); peer->processLedgerRequest(m);
}); });
@@ -1575,7 +1575,8 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMProofPathRequest> const& m)
fee_.update( fee_.update(
Resource::feeModerateBurdenPeer, "received a proof path request"); Resource::feeModerateBurdenPeer, "received a proof path request");
std::weak_ptr<PeerImp> weak = shared_from_this(); std::weak_ptr<PeerImp> weak = shared_from_this();
app_.getJobQueue().addJob(jtREPLAY_REQ, "RcvProofPReq", [weak, m]() { app_.getJobQueue().addJob(
jtREPLAY_REQ, "recvProofPathRequest", [weak, m]() {
if (auto peer = weak.lock()) if (auto peer = weak.lock())
{ {
auto reply = auto reply =
@@ -1584,7 +1585,8 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMProofPathRequest> const& m)
{ {
if (reply.error() == protocol::TMReplyError::reBAD_REQUEST) if (reply.error() == protocol::TMReplyError::reBAD_REQUEST)
peer->charge( peer->charge(
Resource::feeMalformedRequest, "proof_path_request"); Resource::feeMalformedRequest,
"proof_path_request");
else else
peer->charge( peer->charge(
Resource::feeRequestNoReply, "proof_path_request"); Resource::feeRequestNoReply, "proof_path_request");
@@ -1627,7 +1629,8 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMReplayDeltaRequest> const& m)
fee_.fee = Resource::feeModerateBurdenPeer; fee_.fee = Resource::feeModerateBurdenPeer;
std::weak_ptr<PeerImp> weak = shared_from_this(); std::weak_ptr<PeerImp> weak = shared_from_this();
app_.getJobQueue().addJob(jtREPLAY_REQ, "RcvReplDReq", [weak, m]() { app_.getJobQueue().addJob(
jtREPLAY_REQ, "recvReplayDeltaRequest", [weak, m]() {
if (auto peer = weak.lock()) if (auto peer = weak.lock())
{ {
auto reply = auto reply =
@@ -1636,10 +1639,12 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMReplayDeltaRequest> const& m)
{ {
if (reply.error() == protocol::TMReplyError::reBAD_REQUEST) if (reply.error() == protocol::TMReplyError::reBAD_REQUEST)
peer->charge( peer->charge(
Resource::feeMalformedRequest, "replay_delta_request"); Resource::feeMalformedRequest,
"replay_delta_request");
else else
peer->charge( peer->charge(
Resource::feeRequestNoReply, "replay_delta_request"); Resource::feeRequestNoReply,
"replay_delta_request");
} }
else else
{ {
@@ -1743,7 +1748,7 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMLedgerData> const& m)
{ {
std::weak_ptr<PeerImp> weak{shared_from_this()}; std::weak_ptr<PeerImp> weak{shared_from_this()};
app_.getJobQueue().addJob( app_.getJobQueue().addJob(
jtTXN_DATA, "RcvPeerData", [weak, ledgerHash, m]() { jtTXN_DATA, "recvPeerData", [weak, ledgerHash, m]() {
if (auto peer = weak.lock()) if (auto peer = weak.lock())
{ {
peer->app_.getInboundTransactions().gotData( peer->app_.getInboundTransactions().gotData(
@@ -1871,7 +1876,7 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMProposeSet> const& m)
std::weak_ptr<PeerImp> weak = shared_from_this(); std::weak_ptr<PeerImp> weak = shared_from_this();
app_.getJobQueue().addJob( app_.getJobQueue().addJob(
isTrusted ? jtPROPOSAL_t : jtPROPOSAL_ut, isTrusted ? jtPROPOSAL_t : jtPROPOSAL_ut,
"checkPropose", "recvPropose->checkPropose",
[weak, isTrusted, m, proposal]() { [weak, isTrusted, m, proposal]() {
if (auto peer = weak.lock()) if (auto peer = weak.lock())
peer->checkPropose(isTrusted, m, proposal); peer->checkPropose(isTrusted, m, proposal);
@@ -2485,7 +2490,18 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMValidation> const& m)
} }
else if (isTrusted || !app_.getFeeTrack().isLoadedLocal()) else if (isTrusted || !app_.getFeeTrack().isLoadedLocal())
{ {
std::string const name = isTrusted ? "ChkTrust" : "ChkUntrust"; std::string const name = [isTrusted, val]() {
std::string ret =
isTrusted ? "Trusted validation" : "Untrusted validation";
#ifdef DEBUG
ret += " " +
std::to_string(val->getFieldU32(sfLedgerSequence)) + ": " +
to_string(val->getNodeID());
#endif
return ret;
}();
std::weak_ptr<PeerImp> weak = shared_from_this(); std::weak_ptr<PeerImp> weak = shared_from_this();
app_.getJobQueue().addJob( app_.getJobQueue().addJob(
@@ -2545,7 +2561,8 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMGetObjectByHash> const& m)
} }
std::weak_ptr<PeerImp> weak = shared_from_this(); std::weak_ptr<PeerImp> weak = shared_from_this();
app_.getJobQueue().addJob(jtREQUESTED_TXN, "DoTxs", [weak, m]() { app_.getJobQueue().addJob(
jtREQUESTED_TXN, "doTransactions", [weak, m]() {
if (auto peer = weak.lock()) if (auto peer = weak.lock())
peer->doTransactions(m); peer->doTransactions(m);
}); });
@@ -2601,16 +2618,6 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMGetObjectByHash> const& m)
newObj.set_ledgerseq(obj.ledgerseq()); newObj.set_ledgerseq(obj.ledgerseq());
// VFALCO NOTE "seq" in the message is obsolete // VFALCO NOTE "seq" in the message is obsolete
// Check if by adding this object, reply has reached its
// limit
if (reply.objects_size() >= Tuning::hardMaxReplyNodes)
{
fee_.update(
Resource::feeModerateBurdenPeer,
" Reply limit reached. Truncating reply.");
break;
}
} }
} }
} }
@@ -2688,7 +2695,8 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMHaveTransactions> const& m)
} }
std::weak_ptr<PeerImp> weak = shared_from_this(); std::weak_ptr<PeerImp> weak = shared_from_this();
app_.getJobQueue().addJob(jtMISSING_TXN, "HandleHaveTxs", [weak, m]() { app_.getJobQueue().addJob(
jtMISSING_TXN, "handleHaveTransactions", [weak, m]() {
if (auto peer = weak.lock()) if (auto peer = weak.lock())
peer->handleHaveTransactions(m); peer->handleHaveTransactions(m);
}); });

View File

@@ -72,7 +72,7 @@ public:
JLOG(j_.info()) << "RPCCall::fromNetwork start"; JLOG(j_.info()) << "RPCCall::fromNetwork start";
mSending = m_jobQueue.addJob( mSending = m_jobQueue.addJob(
jtCLIENT_SUBSCRIBE, "RPCSubSendThr", [this]() { jtCLIENT_SUBSCRIBE, "RPCSub::sendThread", [this]() {
sendThread(); sendThread();
}); });
} }

View File

@@ -18,32 +18,6 @@
namespace xrpl { namespace xrpl {
using FunctionType = std::function<Expected<uint256, Json::Value>(
Json::Value const&,
Json::StaticString const,
unsigned const apiVersion)>;
static Expected<uint256, Json::Value>
parseFixed(
Keylet const& keylet,
Json::Value const& params,
Json::StaticString const& fieldName,
unsigned const apiVersion);
// Helper function to return FunctionType for objects that have a fixed
// location. That is, they don't take parameters to compute the index.
// e.g. amendments, fees, negative UNL, etc.
static FunctionType
fixed(Keylet const& keylet)
{
return [keylet](
Json::Value const& params,
Json::StaticString const fieldName,
unsigned const apiVersion) -> Expected<uint256, Json::Value> {
return parseFixed(keylet, params, fieldName, apiVersion);
};
}
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseObjectID( parseObjectID(
Json::Value const& params, Json::Value const& params,
@@ -59,33 +33,13 @@ parseObjectID(
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseIndex( parseIndex(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
unsigned const apiVersion)
{ {
if (apiVersion > 2u && params.isString())
{
std::string const index = params.asString();
if (index == jss::amendments.c_str())
return keylet::amendments().key;
if (index == jss::fee.c_str())
return keylet::fees().key;
if (index == jss::nunl)
return keylet::negativeUNL().key;
if (index == jss::hashes)
// Note this only finds the "short" skip list. Use "hashes":index to
// get the long list.
return keylet::skip().key;
}
return parseObjectID(params, fieldName, "hex string"); return parseObjectID(params, fieldName, "hex string");
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseAccountRoot( parseAccountRoot(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
if (auto const account = LedgerEntryHelpers::parse<AccountID>(params)) if (auto const account = LedgerEntryHelpers::parse<AccountID>(params))
{ {
@@ -96,13 +50,14 @@ parseAccountRoot(
"malformedAddress", fieldName, "AccountID"); "malformedAddress", fieldName, "AccountID");
} }
auto const parseAmendments = fixed(keylet::amendments()); static Expected<uint256, Json::Value>
parseAmendments(Json::Value const& params, Json::StaticString const fieldName)
{
return parseObjectID(params, fieldName, "hex string");
}
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseAMM( parseAMM(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!params.isObject()) if (!params.isObject())
{ {
@@ -130,10 +85,7 @@ parseAMM(
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseBridge( parseBridge(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!params.isMember(jss::bridge)) if (!params.isMember(jss::bridge))
{ {
@@ -164,19 +116,13 @@ parseBridge(
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseCheck( parseCheck(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
return parseObjectID(params, fieldName, "hex string"); return parseObjectID(params, fieldName, "hex string");
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseCredential( parseCredential(Json::Value const& cred, Json::StaticString const fieldName)
Json::Value const& cred,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!cred.isObject()) if (!cred.isObject())
{ {
@@ -207,10 +153,7 @@ parseCredential(
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseDelegate( parseDelegate(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!params.isObject()) if (!params.isObject())
{ {
@@ -301,10 +244,7 @@ parseAuthorizeCredentials(Json::Value const& jv)
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseDepositPreauth( parseDepositPreauth(Json::Value const& dp, Json::StaticString const fieldName)
Json::Value const& dp,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!dp.isObject()) if (!dp.isObject())
{ {
@@ -357,10 +297,7 @@ parseDepositPreauth(
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseDID( parseDID(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
auto const account = LedgerEntryHelpers::parse<AccountID>(params); auto const account = LedgerEntryHelpers::parse<AccountID>(params);
if (!account) if (!account)
@@ -375,8 +312,7 @@ parseDID(
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseDirectoryNode( parseDirectoryNode(
Json::Value const& params, Json::Value const& params,
Json::StaticString const fieldName, Json::StaticString const fieldName)
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!params.isObject()) if (!params.isObject())
{ {
@@ -429,10 +365,7 @@ parseDirectoryNode(
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseEscrow( parseEscrow(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!params.isObject()) if (!params.isObject())
{ {
@@ -451,53 +384,20 @@ parseEscrow(
return keylet::escrow(*id, *seq).key; return keylet::escrow(*id, *seq).key;
} }
auto const parseFeeSettings = fixed(keylet::fees());
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseFixed( parseFeeSettings(Json::Value const& params, Json::StaticString const fieldName)
Keylet const& keylet,
Json::Value const& params,
Json::StaticString const& fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!params.isBool())
{
return parseObjectID(params, fieldName, "hex string"); return parseObjectID(params, fieldName, "hex string");
}
if (!params.asBool())
{
return LedgerEntryHelpers::invalidFieldError(
"invalidParams", fieldName, "true");
}
return keylet.key;
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseLedgerHashes( parseLedgerHashes(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
unsigned const apiVersion)
{ {
if (params.isUInt() || params.isInt()) return parseObjectID(params, fieldName, "hex string");
{
// If the index doesn't parse as a UInt, throw
auto const index = params.asUInt();
// Return the "long" skip list for the given ledger index.
auto const keylet = keylet::skip(index);
return keylet.key;
}
// Return the key in `params` or the "short" skip list, which contains
// hashes since the last flag ledger.
return parseFixed(keylet::skip(), params, fieldName, apiVersion);
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseLoanBroker( parseLoanBroker(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!params.isObject()) if (!params.isObject())
{ {
@@ -517,10 +417,7 @@ parseLoanBroker(
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseLoan( parseLoan(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!params.isObject()) if (!params.isObject())
{ {
@@ -540,10 +437,7 @@ parseLoan(
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseMPToken( parseMPToken(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!params.isObject()) if (!params.isObject())
{ {
@@ -566,8 +460,7 @@ parseMPToken(
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseMPTokenIssuance( parseMPTokenIssuance(
Json::Value const& params, Json::Value const& params,
Json::StaticString const fieldName, Json::StaticString const fieldName)
[[maybe_unused]] unsigned const apiVersion)
{ {
auto const mptIssuanceID = LedgerEntryHelpers::parse<uint192>(params); auto const mptIssuanceID = LedgerEntryHelpers::parse<uint192>(params);
if (!mptIssuanceID) if (!mptIssuanceID)
@@ -578,30 +471,25 @@ parseMPTokenIssuance(
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseNFTokenOffer( parseNFTokenOffer(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
return parseObjectID(params, fieldName, "hex string"); return parseObjectID(params, fieldName, "hex string");
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseNFTokenPage( parseNFTokenPage(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
return parseObjectID(params, fieldName, "hex string"); return parseObjectID(params, fieldName, "hex string");
} }
auto const parseNegativeUNL = fixed(keylet::negativeUNL()); static Expected<uint256, Json::Value>
parseNegativeUNL(Json::Value const& params, Json::StaticString const fieldName)
{
return parseObjectID(params, fieldName, "hex string");
}
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseOffer( parseOffer(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!params.isObject()) if (!params.isObject())
{ {
@@ -622,10 +510,7 @@ parseOffer(
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseOracle( parseOracle(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!params.isObject()) if (!params.isObject())
{ {
@@ -646,10 +531,7 @@ parseOracle(
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parsePayChannel( parsePayChannel(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
return parseObjectID(params, fieldName, "hex string"); return parseObjectID(params, fieldName, "hex string");
} }
@@ -657,8 +539,7 @@ parsePayChannel(
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parsePermissionedDomain( parsePermissionedDomain(
Json::Value const& pd, Json::Value const& pd,
Json::StaticString const fieldName, Json::StaticString const fieldName)
[[maybe_unused]] unsigned const apiVersion)
{ {
if (pd.isString()) if (pd.isString())
{ {
@@ -687,8 +568,7 @@ parsePermissionedDomain(
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseRippleState( parseRippleState(
Json::Value const& jvRippleState, Json::Value const& jvRippleState,
Json::StaticString const fieldName, Json::StaticString const fieldName)
[[maybe_unused]] unsigned const apiVersion)
{ {
Currency uCurrency; Currency uCurrency;
@@ -738,19 +618,13 @@ parseRippleState(
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseSignerList( parseSignerList(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
return parseObjectID(params, fieldName, "hex string"); return parseObjectID(params, fieldName, "hex string");
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseTicket( parseTicket(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!params.isObject()) if (!params.isObject())
{ {
@@ -771,10 +645,7 @@ parseTicket(
} }
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseVault( parseVault(Json::Value const& params, Json::StaticString const fieldName)
Json::Value const& params,
Json::StaticString const fieldName,
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!params.isObject()) if (!params.isObject())
{ {
@@ -797,8 +668,7 @@ parseVault(
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseXChainOwnedClaimID( parseXChainOwnedClaimID(
Json::Value const& claim_id, Json::Value const& claim_id,
Json::StaticString const fieldName, Json::StaticString const fieldName)
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!claim_id.isObject()) if (!claim_id.isObject())
{ {
@@ -823,8 +693,7 @@ parseXChainOwnedClaimID(
static Expected<uint256, Json::Value> static Expected<uint256, Json::Value>
parseXChainOwnedCreateAccountClaimID( parseXChainOwnedCreateAccountClaimID(
Json::Value const& claim_id, Json::Value const& claim_id,
Json::StaticString const fieldName, Json::StaticString const fieldName)
[[maybe_unused]] unsigned const apiVersion)
{ {
if (!claim_id.isObject()) if (!claim_id.isObject())
{ {
@@ -848,6 +717,10 @@ parseXChainOwnedCreateAccountClaimID(
return keylet.key; return keylet.key;
} }
using FunctionType = Expected<uint256, Json::Value> (*)(
Json::Value const&,
Json::StaticString const);
struct LedgerEntry struct LedgerEntry
{ {
Json::StaticString fieldName; Json::StaticString fieldName;
@@ -880,7 +753,7 @@ doLedgerEntry(RPC::JsonContext& context)
{jss::ripple_state, parseRippleState, ltRIPPLE_STATE}, {jss::ripple_state, parseRippleState, ltRIPPLE_STATE},
}); });
auto const hasMoreThanOneMember = [&]() { auto hasMoreThanOneMember = [&]() {
int count = 0; int count = 0;
for (auto const& ledgerEntry : ledgerEntryParsers) for (auto const& ledgerEntry : ledgerEntryParsers)
@@ -924,8 +797,8 @@ doLedgerEntry(RPC::JsonContext& context)
Json::Value const& params = ledgerEntry.fieldName == jss::bridge Json::Value const& params = ledgerEntry.fieldName == jss::bridge
? context.params ? context.params
: context.params[ledgerEntry.fieldName]; : context.params[ledgerEntry.fieldName];
auto const result = ledgerEntry.parseFunction( auto const result =
params, ledgerEntry.fieldName, context.apiVersion); ledgerEntry.parseFunction(params, ledgerEntry.fieldName);
if (!result) if (!result)
return result.error(); return result.error();
@@ -956,13 +829,9 @@ doLedgerEntry(RPC::JsonContext& context)
throw; throw;
} }
// Return the computed index regardless of whether the node exists.
jvResult[jss::index] = to_string(uNodeIndex);
if (uNodeIndex.isZero()) if (uNodeIndex.isZero())
{ {
RPC::inject_error(rpcENTRY_NOT_FOUND, jvResult); return RPC::make_error(rpcENTRY_NOT_FOUND);
return jvResult;
} }
auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex)); auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex));
@@ -974,14 +843,12 @@ doLedgerEntry(RPC::JsonContext& context)
if (!sleNode) if (!sleNode)
{ {
// Not found. // Not found.
RPC::inject_error(rpcENTRY_NOT_FOUND, jvResult); return RPC::make_error(rpcENTRY_NOT_FOUND);
return jvResult;
} }
if ((expectedType != ltANY) && (expectedType != sleNode->getType())) if ((expectedType != ltANY) && (expectedType != sleNode->getType()))
{ {
RPC::inject_error(rpcUNEXPECTED_LEDGER_TYPE, jvResult); return RPC::make_error(rpcUNEXPECTED_LEDGER_TYPE);
return jvResult;
} }
if (bNodeBinary) if (bNodeBinary)
@@ -991,10 +858,12 @@ doLedgerEntry(RPC::JsonContext& context)
sleNode->add(s); sleNode->add(s);
jvResult[jss::node_binary] = strHex(s.peekData()); jvResult[jss::node_binary] = strHex(s.peekData());
jvResult[jss::index] = to_string(uNodeIndex);
} }
else else
{ {
jvResult[jss::node] = sleNode->getJson(JsonOptions::none); jvResult[jss::node] = sleNode->getJson(JsonOptions::none);
jvResult[jss::index] = to_string(uNodeIndex);
} }
return jvResult; return jvResult;