Compare commits

..

1 Commits

Author SHA1 Message Date
Sergey Kuznetsov
4da4b49eda chore: Commits for 2.7.0-b2 (#2795) 2025-11-20 14:53:12 +00:00
78 changed files with 342 additions and 740 deletions

View File

@@ -4,7 +4,7 @@ import json
LINUX_OS = ["heavy", "heavy-arm64"]
LINUX_CONTAINERS = [
'{ "image": "ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f" }'
'{ "image": "ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7" }'
]
LINUX_COMPILERS = ["gcc", "clang"]

View File

@@ -48,7 +48,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Download Clio binary from artifact
if: ${{ inputs.artifact_name != null }}

View File

@@ -49,7 +49,7 @@ jobs:
build_type: [Release, Debug]
container:
[
'{ "image": "ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f" }',
'{ "image": "ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7" }',
]
static: [true]
@@ -79,7 +79,7 @@ jobs:
uses: ./.github/workflows/reusable-build.yml
with:
runs_on: heavy
container: '{ "image": "ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f" }'
container: '{ "image": "ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7" }'
conan_profile: gcc
build_type: Debug
download_ccache: true
@@ -98,7 +98,7 @@ jobs:
uses: ./.github/workflows/reusable-build.yml
with:
runs_on: heavy
container: '{ "image": "ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f" }'
container: '{ "image": "ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7" }'
conan_profile: gcc
build_type: Release
download_ccache: true
@@ -115,10 +115,10 @@ jobs:
needs: build-and-test
runs-on: heavy
container:
image: ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f
image: ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:

View File

@@ -21,10 +21,10 @@ jobs:
name: Build Clio / `libXRPL ${{ github.event.client_payload.version }}`
runs-on: heavy
container:
image: ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f
image: ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
@@ -69,7 +69,7 @@ jobs:
needs: build
runs-on: heavy
container:
image: ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f
image: ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7
steps:
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
@@ -92,7 +92,7 @@ jobs:
issues: write
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Create an issue
uses: ./.github/actions/create-issue

View File

@@ -31,7 +31,7 @@ jobs:
if: github.event_name != 'push' || contains(github.event.head_commit.message, 'clang-tidy auto fixes')
runs-on: heavy
container:
image: ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f
image: ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7
permissions:
contents: write
@@ -39,7 +39,7 @@ jobs:
pull-requests: write
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
@@ -62,30 +62,27 @@ jobs:
uses: XRPLF/actions/.github/actions/get-nproc@046b1620f6bfd6cd0985dc82c3df02786801fe0a
id: nproc
- name: Run clang-tidy (several times)
- name: Run clang-tidy
continue-on-error: true
id: clang_tidy
id: run_clang_tidy
run: |
# We run clang-tidy several times, because some fixes may enable new fixes in subsequent runs.
CLANG_TIDY_COMMAND="run-clang-tidy-${{ env.LLVM_TOOLS_VERSION }} -p build -j ${{ steps.nproc.outputs.nproc }} -fix -quiet"
${CLANG_TIDY_COMMAND} ||
${CLANG_TIDY_COMMAND} ||
${CLANG_TIDY_COMMAND}
- name: Check for changes
id: files_changed
continue-on-error: true
run: |
git diff --exit-code
run-clang-tidy-${{ env.LLVM_TOOLS_VERSION }} -p build -j "${{ steps.nproc.outputs.nproc }}" -fix -quiet 1>output.txt
- name: Fix local includes and clang-format style
if: ${{ steps.files_changed.outcome != 'success' }}
if: ${{ steps.run_clang_tidy.outcome != 'success' }}
run: |
pre-commit run --all-files fix-local-includes || true
pre-commit run --all-files clang-format || true
- name: Print issues found
if: ${{ steps.run_clang_tidy.outcome != 'success' }}
run: |
sed -i '/error\||/!d' ./output.txt
cat output.txt
rm output.txt
- name: Create an issue
if: ${{ (steps.clang_tidy.outcome != 'success' || steps.files_changed.outcome != 'success') && github.event_name != 'pull_request' }}
if: ${{ steps.run_clang_tidy.outcome != 'success' && github.event_name != 'pull_request' }}
id: create_issue
uses: ./.github/actions/create-issue
env:
@@ -98,7 +95,7 @@ jobs:
List of the issues found: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/
- uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.0
if: ${{ steps.files_changed.outcome != 'success' && github.event_name != 'pull_request' }}
if: ${{ steps.run_clang_tidy.outcome != 'success' && github.event_name != 'pull_request' }}
with:
gpg_private_key: ${{ secrets.ACTIONS_GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.ACTIONS_GPG_PASSPHRASE }}
@@ -106,8 +103,8 @@ jobs:
git_commit_gpgsign: true
- name: Create PR with fixes
if: ${{ steps.files_changed.outcome != 'success' && github.event_name != 'pull_request' }}
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9
if: ${{ steps.run_clang_tidy.outcome != 'success' && github.event_name != 'pull_request' }}
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
env:
GH_REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
@@ -122,5 +119,5 @@ jobs:
reviewers: "godexsoft,kuznetsss,PeterChen13579,mathbunnyru"
- name: Fail the job
if: ${{ steps.clang_tidy.outcome != 'success' || steps.files_changed.outcome != 'success' }}
if: ${{ steps.run_clang_tidy.outcome != 'success' }}
run: exit 1

View File

@@ -18,11 +18,11 @@ jobs:
build:
runs-on: ubuntu-latest
container:
image: ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f
image: ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7
steps:
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
lfs: true

View File

@@ -43,17 +43,17 @@ jobs:
conan_profile: gcc
build_type: Release
static: true
container: '{ "image": "ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f" }'
container: '{ "image": "ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7" }'
- os: heavy
conan_profile: gcc
build_type: Debug
static: true
container: '{ "image": "ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f" }'
container: '{ "image": "ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7" }'
- os: heavy
conan_profile: gcc.ubsan
build_type: Release
static: false
container: '{ "image": "ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f" }'
container: '{ "image": "ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7" }'
uses: ./.github/workflows/reusable-build-test.yml
with:
@@ -77,7 +77,7 @@ jobs:
include:
- os: heavy
conan_profile: clang
container: '{ "image": "ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f" }'
container: '{ "image": "ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7" }'
static: true
- os: macos15
conan_profile: apple-clang
@@ -145,7 +145,7 @@ jobs:
issues: write
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Create an issue
uses: ./.github/actions/create-issue

View File

@@ -11,4 +11,4 @@ jobs:
uses: XRPLF/actions/.github/workflows/pre-commit.yml@34790936fae4c6c751f62ec8c06696f9c1a5753a
with:
runs_on: heavy
container: '{ "image": "ghcr.io/xrplf/clio-pre-commit:067449c3f8ae6755ea84752ea2962b589fe56c8f" }'
container: '{ "image": "ghcr.io/xrplf/clio-pre-commit:c117f470f2ef954520ab5d1c8a5ed2b9e68d6f8a" }'

View File

@@ -29,7 +29,7 @@ jobs:
conan_profile: gcc
build_type: Release
static: true
container: '{ "image": "ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f" }'
container: '{ "image": "ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7" }'
uses: ./.github/workflows/reusable-build-test.yml
with:

View File

@@ -90,7 +90,7 @@ jobs:
if: ${{ runner.os == 'macOS' }}
uses: XRPLF/actions/.github/actions/cleanup-workspace@ea9970b7c211b18f4c8bcdb28c29f5711752029f
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
# We need to fetch tags to have correct version in the release

View File

@@ -46,7 +46,7 @@ jobs:
release:
runs-on: heavy
container:
image: ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f
image: ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7
env:
GH_REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
@@ -55,7 +55,7 @@ jobs:
contents: write
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0

View File

@@ -54,7 +54,7 @@ jobs:
if: ${{ runner.os == 'macOS' }}
uses: XRPLF/actions/.github/actions/cleanup-workspace@ea9970b7c211b18f4c8bcdb28c29f5711752029f
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0

View File

@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0

View File

@@ -44,7 +44,7 @@ jobs:
uses: ./.github/workflows/reusable-build-test.yml
with:
runs_on: heavy
container: '{ "image": "ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f" }'
container: '{ "image": "ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7" }'
download_ccache: false
upload_ccache: false
conan_profile: ${{ matrix.compiler }}${{ matrix.sanitizer_ext }}

View File

@@ -56,7 +56,7 @@ jobs:
needs: repo
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Get changed files
id: changed-files
@@ -94,7 +94,7 @@ jobs:
needs: repo
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Get changed files
id: changed-files
@@ -132,7 +132,7 @@ jobs:
needs: [repo, gcc-amd64, gcc-arm64]
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Get changed files
id: changed-files
@@ -183,7 +183,7 @@ jobs:
needs: repo
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Get changed files
id: changed-files
@@ -219,7 +219,7 @@ jobs:
needs: [repo, gcc-merge]
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Get changed files
id: changed-files
@@ -250,7 +250,7 @@ jobs:
needs: [repo, gcc-merge]
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Get changed files
id: changed-files
@@ -281,7 +281,7 @@ jobs:
needs: [repo, tools-amd64, tools-arm64]
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Get changed files
id: changed-files
@@ -316,7 +316,7 @@ jobs:
needs: [repo, tools-merge]
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: ./.github/actions/build-docker-image
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -338,7 +338,7 @@ jobs:
needs: [repo, gcc-merge, clang, tools-merge]
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: ./.github/actions/build-docker-image
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -50,7 +50,7 @@ jobs:
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Calculate conan matrix
id: set-matrix
@@ -73,7 +73,7 @@ jobs:
CONAN_PROFILE: ${{ matrix.compiler }}${{ matrix.sanitizer_ext }}
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Prepare runner
uses: XRPLF/actions/.github/actions/prepare-runner@8abb0722cbff83a9a2dc7d06c473f7a4964b7382

View File

@@ -58,11 +58,6 @@ repos:
--ignore-words=pre-commit-hooks/codespell_ignore.txt,
]
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 25.11.0
hooks:
- id: black
# Running some C++ hooks before clang-format
# to ensure that the style is consistent.
- repo: local

View File

@@ -180,7 +180,6 @@ Existing maintainers can resign, or be subject to a vote for removal at the behe
- [kuznetsss](https://github.com/kuznetsss) (Ripple)
- [legleux](https://github.com/legleux) (Ripple)
- [PeterChen13579](https://github.com/PeterChen13579) (Ripple)
- [mathbunnyru](https://github.com/mathbunnyru) (Ripple)
### Honorable ex-Maintainers

View File

@@ -5,11 +5,11 @@
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1756234289.683",
"xrpl/3.0.0-rc1#f5c8ecd42bdf511ad36f57bc702dacd2%1762975621.294",
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1756234266.869",
"spdlog/1.16.0#942c2c39562ae25ba575d9c8e2bdf3b6%1763984117.108",
"spdlog/1.15.3#3ca0e9e6b83af4d0151e26541d140c86%1754401846.61",
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1756234262.318",
"re2/20230301#ca3b241baec15bd31ea9187150e0b333%1764175362.029",
"re2/20230301#dfd6e2bf050eb90ddd8729cfb4c844a4%1756234257.976",
"rapidjson/cci.20220822#1b9d8c2256876a154172dc5cfbe447c6%1754325007.656",
"protobuf/3.21.12#44ee56c0a6eea0c19aeeaca680370b88%1764175361.456",
"protobuf/3.21.12#d927114e28de9f4691a6bbcdd9a529d1%1756234251.614",
"openssl/1.1.1w#a8f0792d7c5121b954578a7149d23e03%1756223730.729",
"nudb/2.0.9#fb8dfd1a5557f5e0528114c2da17721e%1763150366.909",
"minizip/1.2.13#9e87d57804bd372d6d1e32b1871517a3%1754325004.374",
@@ -17,45 +17,41 @@
"libuv/1.46.0#dc28c1f653fa197f00db5b577a6f6011%1754325003.592",
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1756223727.64",
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1756230911.03",
"libarchive/3.8.1#ffee18995c706e02bf96e7a2f7042e0d%1764175360.142",
"libarchive/3.8.1#5cf685686322e906cb42706ab7e099a8%1756234256.696",
"http_parser/2.9.4#98d91690d6fd021e9e624218a85d9d97%1754325001.385",
"gtest/1.14.0#f8f0757a574a8dd747d16af62d6eb1b7%1754325000.842",
"grpc/1.50.1#02291451d1e17200293a409410d1c4e1%1756234248.958",
"fmt/12.1.0#50abab23274d56bb8f42c94b3b9a40c7%1763984116.926",
"fmt/11.2.0#579bb2cdf4a7607621beea4eb4651e0f%1754324999.086",
"doctest/2.4.11#a4211dfc329a16ba9f280f9574025659%1756234220.819",
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1763584497.32",
"cassandra-cpp-driver/2.17.0#bd3934138689482102c265d01288a316%1764175359.611",
"c-ares/1.34.5#5581c2b62a608b40bb85d965ab3ec7c8%1764175359.429",
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1764175359.429",
"boost/1.83.0#91d8b1572534d2c334d6790e3c34d0c1%1764175359.61",
"date/3.0.4#f74bbba5a08fa388256688743136cb6f%1756234217.493",
"cassandra-cpp-driver/2.17.0#e50919efac8418c26be6671fd702540a%1754324997.363",
"c-ares/1.34.5#b78b91e7cfb1f11ce777a285bbf169c6%1756234217.915",
"bzip2/1.0.8#00b4a4658791c1f06914e087f0e792f5%1756234261.716",
"boost/1.83.0#5d975011d65b51abb2d2f6eb8386b368%1754325043.336",
"benchmark/1.9.4#ce4403f7a24d3e1f907cd9da4b678be4%1754578869.672",
"abseil/20230802.1#90ba607d4ee8fb5fb157c3db540671fc%1764175359.429"
"abseil/20230802.1#f0f91485b111dc9837a68972cb19ca7b%1756234220.907"
],
"build_requires": [
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1756234269.497",
"protobuf/3.21.12#44ee56c0a6eea0c19aeeaca680370b88%1764175361.456",
"cmake/4.2.0#ae0a44f44a1ef9ab68fd4b3e9a1f8671%1764175359.44",
"cmake/3.31.10#313d16a1aa16bbdb2ca0792467214b76%1764175359.429",
"protobuf/3.21.12#d927114e28de9f4691a6bbcdd9a529d1%1756234251.614",
"cmake/3.31.8#dde3bde00bb843687e55aea5afa0e220%1756234232.89",
"b2/5.3.3#107c15377719889654eb9a162a673975%1756234226.28"
],
"python_requires": [],
"overrides": {
"boost/1.83.0": [
null,
"boost/1.83.0#91d8b1572534d2c334d6790e3c34d0c1"
"boost/1.83.0#5d975011d65b51abb2d2f6eb8386b368"
],
"protobuf/3.21.12": [
null,
"protobuf/3.21.12#44ee56c0a6eea0c19aeeaca680370b88"
"protobuf/3.21.12#d927114e28de9f4691a6bbcdd9a529d1"
],
"lz4/1.9.4": [
"lz4/1.10.0"
],
"sqlite3/3.44.2": [
"sqlite3/3.49.1"
],
"fmt/12.0.0": [
"fmt/12.1.0"
]
},
"config_requires": []

View File

@@ -3,60 +3,62 @@ from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
class ClioConan(ConanFile):
name = "clio"
license = "ISC"
author = "Alex Kremer <akremer@ripple.com>, John Freeman <jfreeman@ripple.com>, Ayaz Salikhov <asalikhov@ripple.com>"
url = "https://github.com/xrplf/clio"
description = "Clio RPC server"
settings = "os", "compiler", "build_type", "arch"
name = 'clio'
license = 'ISC'
author = 'Alex Kremer <akremer@ripple.com>, John Freeman <jfreeman@ripple.com>, Ayaz Salikhov <asalikhov@ripple.com>'
url = 'https://github.com/xrplf/clio'
description = 'Clio RPC server'
settings = 'os', 'compiler', 'build_type', 'arch'
options = {}
requires = [
"boost/1.83.0",
"cassandra-cpp-driver/2.17.0",
"protobuf/3.21.12",
"grpc/1.50.1",
"openssl/1.1.1w",
"xrpl/3.0.0-rc1",
"zlib/1.3.1",
"libbacktrace/cci.20210118",
"spdlog/1.16.0",
'boost/1.83.0',
'cassandra-cpp-driver/2.17.0',
'fmt/11.2.0',
'protobuf/3.21.12',
'grpc/1.50.1',
'openssl/1.1.1w',
'xrpl/3.0.0-rc1',
'zlib/1.3.1',
'libbacktrace/cci.20210118',
'spdlog/1.15.3',
]
default_options = {
"xrpl/*:tests": False,
"xrpl/*:rocksdb": False,
"cassandra-cpp-driver/*:shared": False,
"date/*:header_only": True,
"grpc/*:shared": False,
"grpc/*:secure": True,
"libpq/*:shared": False,
"lz4/*:shared": False,
"openssl/*:shared": False,
"protobuf/*:shared": False,
"protobuf/*:with_zlib": True,
"snappy/*:shared": False,
"gtest/*:no_main": True,
'xrpl/*:tests': False,
'xrpl/*:rocksdb': False,
'cassandra-cpp-driver/*:shared': False,
'date/*:header_only': True,
'grpc/*:shared': False,
'grpc/*:secure': True,
'libpq/*:shared': False,
'lz4/*:shared': False,
'openssl/*:shared': False,
'protobuf/*:shared': False,
'protobuf/*:with_zlib': True,
'snappy/*:shared': False,
'gtest/*:no_main': True,
}
exports_sources = ("CMakeLists.txt", "cmake/*", "src/*")
exports_sources = (
'CMakeLists.txt', 'cmake/*', 'src/*'
)
def requirements(self):
self.requires("gtest/1.14.0")
self.requires("benchmark/1.9.4")
self.requires("fmt/12.1.0", force=True)
self.requires('gtest/1.14.0')
self.requires('benchmark/1.9.4')
def configure(self):
if self.settings.compiler == "apple-clang":
self.options["boost"].visibility = "global"
if self.settings.compiler == 'apple-clang':
self.options['boost'].visibility = 'global'
def layout(self):
cmake_layout(self)
# Fix this setting to follow the default introduced in Conan 1.48
# to align with our build instructions.
self.folders.generators = "build/generators"
self.folders.generators = 'build/generators'
generators = "CMakeDeps"
generators = 'CMakeDeps'
def generate(self):
tc = CMakeToolchain(self)

View File

@@ -36,6 +36,7 @@ RUN apt-get update \
libmpfr-dev \
libncurses-dev \
make \
ninja-build \
wget \
zip \
&& apt-get clean \
@@ -106,7 +107,6 @@ COPY --from=clio-tools \
/usr/local/bin/git-cliff \
/usr/local/bin/gh \
/usr/local/bin/gdb \
/usr/local/bin/ninja \
/usr/local/bin/
WORKDIR /root

View File

@@ -15,7 +15,6 @@ The image is based on Ubuntu 20.04 and contains:
- gh 2.82.1
- git-cliff 2.10.1
- mold 2.40.4
- Ninja 1.13.2
- Python 3.8
- and some other useful tools

View File

@@ -1,6 +1,6 @@
services:
clio_develop:
image: ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f
image: ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7
volumes:
- clio_develop_conan_data:/root/.conan2/p
- clio_develop_ccache:/root/.ccache

View File

@@ -12,6 +12,7 @@ ARG BUILD_VERSION=0
RUN apt-get update \
&& apt-get install -y --no-install-recommends --no-install-suggests \
ninja-build \
python3 \
python3-pip \
software-properties-common \
@@ -23,15 +24,6 @@ RUN apt-get update \
WORKDIR /tmp
ARG NINJA_VERSION=1.13.2
RUN wget --progress=dot:giga "https://github.com/ninja-build/ninja/archive/refs/tags/v${NINJA_VERSION}.tar.gz" \
&& tar xf "v${NINJA_VERSION}.tar.gz" \
&& cd "ninja-${NINJA_VERSION}" \
&& ./configure.py --bootstrap \
&& mv ninja /usr/local/bin/ninja \
&& rm -rf /tmp/* /var/tmp/*
ARG MOLD_VERSION=2.40.4
RUN wget --progress=dot:giga "https://github.com/rui314/mold/archive/refs/tags/v${MOLD_VERSION}.tar.gz" \
&& tar xf "v${MOLD_VERSION}.tar.gz" \

View File

@@ -191,7 +191,7 @@ Open the `index.html` file in your browser to see the documentation pages.
It is also possible to build Clio using [Docker](https://www.docker.com/) if you don't want to install all the dependencies on your machine.
```sh
docker run -it ghcr.io/xrplf/clio-ci:067449c3f8ae6755ea84752ea2962b589fe56c8f
docker run -it ghcr.io/xrplf/clio-ci:77387d8f9f13aea8f23831d221ac3e7683bb69b7
git clone https://github.com/XRPLF/clio
cd clio
```

View File

@@ -4,6 +4,7 @@ import argparse
import re
from pathlib import Path
PATTERN = r'R"JSON\((.*?)\)JSON"'
@@ -39,7 +40,6 @@ def fix_colon_spacing(cpp_content: str) -> str:
raw_json = match.group(1)
raw_json = re.sub(r'":\n\s*(\[|\{)', r'": \1', raw_json)
return f'R"JSON({raw_json})JSON"'
return re.sub(PATTERN, replace_json, cpp_content, flags=re.DOTALL)
@@ -49,12 +49,12 @@ def fix_indentation(cpp_content: str) -> str:
lines = cpp_content.splitlines()
ends_with_newline = cpp_content.endswith("\n")
ends_with_newline = cpp_content.endswith('\n')
def find_indentation(line: str) -> int:
return len(line) - len(line.lstrip())
for line_num, (line, next_line) in enumerate(zip(lines[:-1], lines[1:])):
for (line_num, (line, next_line)) in enumerate(zip(lines[:-1], lines[1:])):
if "JSON(" in line and ")JSON" not in line:
indent = find_indentation(line)
next_indent = find_indentation(next_line)
@@ -69,11 +69,7 @@ def fix_indentation(cpp_content: str) -> str:
if ")JSON" in lines[i]:
lines[i] = " " * indent + lines[i].lstrip()
break
lines[i] = (
lines[i][by_how_much:]
if by_how_much > 0
else " " * (-by_how_much) + lines[i]
)
lines[i] = lines[i][by_how_much:] if by_how_much > 0 else " " * (-by_how_much) + lines[i]
result = "\n".join(lines)

View File

@@ -91,7 +91,6 @@ ClioApplication::ClioApplication(util::config::ClioConfigDefinition const& confi
{
LOG(util::LogService::info()) << "Clio version: " << util::build::getClioFullVersionString();
signalsHandler_.subscribeToStop([this]() { appStopper_.stop(); });
appStopper_.setOnComplete([this]() { signalsHandler_.notifyGracefulShutdownComplete(); });
}
int
@@ -183,7 +182,7 @@ ClioApplication::run(bool const useNgWebServer)
return EXIT_FAILURE;
}
httpServer->onGet("/metrics", MetricsHandler{adminVerifier, workQueue});
httpServer->onGet("/metrics", MetricsHandler{adminVerifier});
httpServer->onGet("/health", HealthCheckHandler{});
httpServer->onGet("/cache_state", CacheStateHandler{cache});
auto requestHandler = RequestHandler{adminVerifier, handler};

View File

@@ -38,18 +38,7 @@ Stopper::~Stopper()
void
Stopper::setOnStop(std::function<void(boost::asio::yield_context)> cb)
{
util::spawn(ctx_, [this, cb = std::move(cb)](auto yield) {
cb(yield);
if (onCompleteCallback_)
onCompleteCallback_();
});
}
void
Stopper::setOnComplete(std::function<void()> cb)
{
onCompleteCallback_ = std::move(cb);
util::spawn(ctx_, std::move(cb));
}
void

View File

@@ -43,7 +43,6 @@ namespace app {
class Stopper {
boost::asio::io_context ctx_;
std::thread worker_;
std::function<void()> onCompleteCallback_;
public:
/**
@@ -59,14 +58,6 @@ public:
void
setOnStop(std::function<void(boost::asio::yield_context)> cb);
/**
* @brief Set the callback to be called when graceful shutdown completes.
*
* @param cb The callback to be called when shutdown completes.
*/
void
setOnComplete(std::function<void()> cb);
/**
* @brief Stop the application and run the shutdown tasks.
*/

View File

@@ -19,10 +19,7 @@
#include "app/WebHandlers.hpp"
#include "rpc/Errors.hpp"
#include "rpc/WorkQueue.hpp"
#include "util/Assert.hpp"
#include "util/CoroutineGroup.hpp"
#include "util/prometheus/Http.hpp"
#include "web/AdminVerificationStrategy.hpp"
#include "web/SubscriptionContextInterface.hpp"
@@ -34,7 +31,6 @@
#include <boost/asio/spawn.hpp>
#include <boost/beast/http/status.hpp>
#include <functional>
#include <memory>
#include <optional>
#include <string>
@@ -80,8 +76,8 @@ DisconnectHook::operator()(web::ng::Connection const& connection)
dosguard_.get().decrement(connection.ip());
}
MetricsHandler::MetricsHandler(std::shared_ptr<web::AdminVerificationStrategy> adminVerifier, rpc::WorkQueue& workQueue)
: adminVerifier_{std::move(adminVerifier)}, workQueue_{std::ref(workQueue)}
MetricsHandler::MetricsHandler(std::shared_ptr<web::AdminVerificationStrategy> adminVerifier)
: adminVerifier_{std::move(adminVerifier)}
{
}
@@ -90,45 +86,19 @@ MetricsHandler::operator()(
web::ng::Request const& request,
web::ng::ConnectionMetadata& connectionMetadata,
web::SubscriptionContextPtr,
boost::asio::yield_context yield
boost::asio::yield_context
)
{
std::optional<web::ng::Response> response;
util::CoroutineGroup coroutineGroup{yield, 1};
auto const onTaskComplete = coroutineGroup.registerForeign(yield);
ASSERT(onTaskComplete.has_value(), "Coroutine group can't be full");
auto const maybeHttpRequest = request.asHttpRequest();
ASSERT(maybeHttpRequest.has_value(), "Got not a http request in Get");
auto const& httpRequest = maybeHttpRequest->get();
bool const postSuccessful = workQueue_.get().postCoro(
[this, &request, &response, &onTaskComplete = onTaskComplete.value(), &connectionMetadata](
boost::asio::yield_context
) mutable {
auto const maybeHttpRequest = request.asHttpRequest();
ASSERT(maybeHttpRequest.has_value(), "Got not a http request in Get");
auto const& httpRequest = maybeHttpRequest->get();
auto maybeResponse = util::prometheus::handlePrometheusRequest(
httpRequest, adminVerifier_->isAdmin(httpRequest, connectionMetadata.ip())
);
ASSERT(maybeResponse.has_value(), "Got unexpected request for Prometheus");
response = web::ng::Response{std::move(maybeResponse).value(), request};
// notify the coroutine group that the foreign task is done
onTaskComplete();
},
/* isWhiteListed= */ true,
rpc::WorkQueue::Priority::High
// FIXME(#1702): Using veb server thread to handle prometheus request. Better to post on work queue.
auto maybeResponse = util::prometheus::handlePrometheusRequest(
httpRequest, adminVerifier_->isAdmin(httpRequest, connectionMetadata.ip())
);
if (!postSuccessful) {
return web::ng::Response{
boost::beast::http::status::too_many_requests, rpc::makeError(rpc::RippledError::rpcTOO_BUSY), request
};
}
// Put the coroutine to sleep until the foreign task is done
coroutineGroup.asyncWait(yield);
ASSERT(response.has_value(), "Woke up coroutine without setting response");
return std::move(response).value();
ASSERT(maybeResponse.has_value(), "Got unexpected request for Prometheus");
return web::ng::Response{std::move(maybeResponse).value(), request};
}
web::ng::Response

View File

@@ -21,7 +21,6 @@
#include "data/LedgerCacheInterface.hpp"
#include "rpc/Errors.hpp"
#include "rpc/WorkQueue.hpp"
#include "util/log/Logger.hpp"
#include "web/AdminVerificationStrategy.hpp"
#include "web/SubscriptionContextInterface.hpp"
@@ -120,23 +119,20 @@ public:
*/
class MetricsHandler {
std::shared_ptr<web::AdminVerificationStrategy> adminVerifier_;
std::reference_wrapper<rpc::WorkQueue> workQueue_;
public:
/**
* @brief Construct a new MetricsHandler object
*
* @param adminVerifier The AdminVerificationStrategy to use for verifying the connection for admin access.
* @param workQueue The WorkQueue to use for handling the request.
*/
MetricsHandler(std::shared_ptr<web::AdminVerificationStrategy> adminVerifier, rpc::WorkQueue& workQueue);
MetricsHandler(std::shared_ptr<web::AdminVerificationStrategy> adminVerifier);
/**
* @brief The call of the function object.
*
* @param request The request to handle.
* @param connectionMetadata The connection metadata.
* @param yield The yield context.
* @return The response to the request.
*/
web::ng::Response
@@ -144,7 +140,7 @@ public:
web::ng::Request const& request,
web::ng::ConnectionMetadata& connectionMetadata,
web::SubscriptionContextPtr,
boost::asio::yield_context yield
boost::asio::yield_context
);
};

View File

@@ -316,11 +316,8 @@ tag_invoke(boost::json::value_to_tag<AMMInfoHandler::Input>, boost::json::value
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (jsonObject.contains(JS(asset)))
input.issue1 = parseIssue(jsonObject.at(JS(asset)).as_object());

View File

@@ -154,11 +154,8 @@ tag_invoke(boost::json::value_to_tag<AccountChannelsHandler::Input>, boost::json
if (jsonObject.contains(JS(destination_account)))
input.destinationAccount = boost::json::value_to<std::string>(jv.at(JS(destination_account)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
return input;
}

View File

@@ -128,11 +128,8 @@ tag_invoke(boost::json::value_to_tag<AccountCurrenciesHandler::Input>, boost::js
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
return input;
}

View File

@@ -204,11 +204,8 @@ tag_invoke(boost::json::value_to_tag<AccountInfoHandler::Input>, boost::json::va
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jsonObject.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (jsonObject.contains(JS(signer_lists)))
input.signerLists = boost::json::value_to<JsonBool>(jsonObject.at(JS(signer_lists)));

View File

@@ -215,11 +215,8 @@ tag_invoke(boost::json::value_to_tag<AccountLinesHandler::Input>, boost::json::v
if (jsonObject.contains(JS(ignore_default)))
input.ignoreDefault = jv.at(JS(ignore_default)).as_bool();
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
return input;
}

View File

@@ -164,11 +164,8 @@ tag_invoke(boost::json::value_to_tag<AccountMPTokenIssuancesHandler::Input>, boo
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
return input;
}

View File

@@ -139,11 +139,8 @@ tag_invoke(boost::json::value_to_tag<AccountMPTokensHandler::Input>, boost::json
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
return input;
}

View File

@@ -157,11 +157,8 @@ tag_invoke(boost::json::value_to_tag<AccountNFTsHandler::Input>, boost::json::va
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jsonObject.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));

View File

@@ -153,11 +153,8 @@ tag_invoke(boost::json::value_to_tag<AccountObjectsHandler::Input>, boost::json:
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (jsonObject.contains(JS(type))) {
input.type =

View File

@@ -169,11 +169,8 @@ tag_invoke(boost::json::value_to_tag<AccountOffersHandler::Input>, boost::json::
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jsonObject.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));

View File

@@ -258,10 +258,8 @@ tag_invoke(boost::json::value_to_tag<AccountTxHandler::Input>, boost::json::valu
input.ledgerHash = boost::json::value_to<std::string>(jsonObject.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value()) {
input.ledgerIndex = *expectedLedgerIndex;
} else {
input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (not input.ledgerIndex.has_value()) {
// could not get the latest validated ledger seq here, using this flag to indicate that
input.usingValidatedLedger = true;
}

View File

@@ -90,11 +90,8 @@ tag_invoke(boost::json::value_to_tag<BookChangesHandler::Input>, boost::json::va
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
return input;
}

View File

@@ -122,11 +122,8 @@ tag_invoke(boost::json::value_to_tag<BookOffersHandler::Input>, boost::json::val
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (jsonObject.contains(JS(taker)))
input.taker = accountFromStringStrict(boost::json::value_to<std::string>(jv.at(JS(taker))));

View File

@@ -145,11 +145,8 @@ tag_invoke(boost::json::value_to_tag<DepositAuthorizedHandler::Input>, boost::js
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (jsonObject.contains(JS(credentials)))
input.credentials = boost::json::value_to<boost::json::array>(jv.at(JS(credentials)));

View File

@@ -168,11 +168,8 @@ tag_invoke(boost::json::value_to_tag<FeatureHandler::Input>, boost::json::value
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
return input;
}

View File

@@ -249,11 +249,8 @@ tag_invoke(boost::json::value_to_tag<GatewayBalancesHandler::Input>, boost::json
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (jsonObject.contains(JS(hotwallet))) {
if (jsonObject.at(JS(hotwallet)).is_string()) {

View File

@@ -263,11 +263,8 @@ tag_invoke(boost::json::value_to_tag<GetAggregatePriceHandler::Input>, boost::js
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
for (auto const& oracle : jsonObject.at(JS(oracles)).as_array()) {
input.oracles.push_back(

View File

@@ -208,11 +208,8 @@ tag_invoke(boost::json::value_to_tag<LedgerHandler::Input>, boost::json::value c
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (jsonObject.contains(JS(transactions)))
input.transactions = jv.at(JS(transactions)).as_bool();

View File

@@ -210,11 +210,8 @@ tag_invoke(boost::json::value_to_tag<LedgerDataHandler::Input>, boost::json::val
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jsonObject.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (jsonObject.contains(JS(type)))
input.type = util::LedgerTypes::getLedgerEntryTypeFromStr(boost::json::value_to<std::string>(jv.at(JS(type))));

View File

@@ -305,11 +305,8 @@ tag_invoke(boost::json::value_to_tag<LedgerEntryHandler::Input>, boost::json::va
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (jsonObject.contains(JS(binary)))
input.binary = jv.at(JS(binary)).as_bool();

View File

@@ -124,11 +124,8 @@ tag_invoke(boost::json::value_to_tag<MPTHoldersHandler::Input>, boost::json::val
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = jsonObject.at(JS(ledger_hash)).as_string().c_str();
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));

View File

@@ -215,11 +215,8 @@ tag_invoke(boost::json::value_to_tag<NFTHistoryHandler::Input>, boost::json::val
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jsonObject.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (jsonObject.contains(JS(binary)))
input.binary = jsonObject.at(JS(binary)).as_bool();

View File

@@ -115,11 +115,8 @@ tag_invoke(boost::json::value_to_tag<NFTInfoHandler::Input>, boost::json::value
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jsonObject.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
return input;
}

View File

@@ -194,11 +194,8 @@ tag_invoke(boost::json::value_to_tag<NFTOffersHandlerBase::Input>, boost::json::
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jsonObject.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (jsonObject.contains(JS(marker)))
input.marker = boost::json::value_to<std::string>(jsonObject.at(JS(marker)));

View File

@@ -136,11 +136,8 @@ tag_invoke(boost::json::value_to_tag<NFTsByIssuerHandler::Input>, boost::json::v
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jsonObject.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));

View File

@@ -196,11 +196,8 @@ tag_invoke(boost::json::value_to_tag<NoRippleCheckHandler::Input>, boost::json::
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jsonObject.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
return input;
}

View File

@@ -109,11 +109,8 @@ tag_invoke(boost::json::value_to_tag<TransactionEntryHandler::Input>, boost::jso
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
return input;
}

View File

@@ -177,11 +177,8 @@ tag_invoke(boost::json::value_to_tag<VaultInfoHandler::Input>, boost::json::valu
if (jsonObject.contains(JS(vault_id)))
input.vaultID = jsonObject.at(JS(vault_id)).as_string();
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(ledger_index)))
input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
return input;
}

View File

@@ -23,13 +23,10 @@
#include <boost/json.hpp>
#include <boost/json/object.hpp>
#include <xrpl/beast/core/LexicalCast.h>
#include <algorithm>
#include <cctype>
#include <charconv>
#include <concepts>
#include <expected>
#include <stdexcept>
#include <string>
@@ -93,27 +90,6 @@ removeSecret(boost::json::object const& object)
return newObject;
}
/**
* @brief Detects the type of number stored in value and casts it back to the requested Type.
* @note This conversion can possibly cause wrapping around or UB. Use with caution.
*
* @tparam Type The type to cast to
* @param value The JSON value to cast
* @return Value casted to the requested type or an error message
*/
template <std::integral Type>
std::expected<Type, std::string>
tryIntegralValueAs(boost::json::value const& value)
{
if (value.is_uint64())
return static_cast<Type>(value.as_uint64());
if (value.is_int64())
return static_cast<Type>(value.as_int64());
return std::unexpected("Value neither uint64 nor int64");
}
/**
* @brief Detects the type of number stored in value and casts it back to the requested Type.
* @note This conversion can possibly cause wrapping around or UB. Use with caution.
@@ -127,33 +103,35 @@ template <std::integral Type>
Type
integralValueAs(boost::json::value const& value)
{
auto expectedResult = tryIntegralValueAs<Type>(value);
if (expectedResult.has_value())
return *expectedResult;
if (value.is_uint64())
return static_cast<Type>(value.as_uint64());
throw std::logic_error(std::move(expectedResult).error());
if (value.is_int64())
return static_cast<Type>(value.as_int64());
throw std::logic_error("Value neither uint64 nor int64");
}
/**
* @brief Extracts ledger index from a JSON value which can be either a number or a string.
*
* @param value The JSON value to extract ledger index from
* @return The extracted ledger index or an error message
* @return An optional containing the ledger index if it is a number; std::nullopt otherwise
* @throws logic_error comes from integralValueAs if the underlying number is neither int64 nor uint64
* @throws std::invalid_argument or std::out_of_range if the string cannot be converted to a number
*/
[[nodiscard]] inline std::expected<uint32_t, std::string>
[[nodiscard]] inline std::optional<uint32_t>
getLedgerIndex(boost::json::value const& value)
{
std::optional<uint32_t> ledgerIndex;
if (not value.is_string()) {
return tryIntegralValueAs<uint32_t>(value);
ledgerIndex = util::integralValueAs<uint32_t>(value);
} else if (value.as_string() != "validated") {
ledgerIndex = std::stoi(value.as_string().c_str());
}
if (value.as_string() != "validated") {
uint32_t ledgerIndex{};
if (beast::lexicalCastChecked(ledgerIndex, value.as_string().c_str())) {
return ledgerIndex;
}
return std::unexpected("Invalid ledger index string");
}
return std::unexpected("'validated' ledger index is requested");
return ledgerIndex;
}
} // namespace util

View File

@@ -19,8 +19,6 @@
#include "util/Repeat.hpp"
#include <boost/asio/post.hpp>
namespace util {
void
@@ -29,11 +27,8 @@ Repeat::stop()
if (control_->stopping)
return;
boost::asio::post(control_->strand, [control = control_] {
control->stopping = true;
control->timer.cancel();
});
control_->stopping = true;
control_->timer.cancel();
control_->semaphore.acquire();
}

View File

@@ -21,11 +21,9 @@
#include "util/Assert.hpp"
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/strand.hpp>
#include <atomic>
#include <chrono>
@@ -43,11 +41,10 @@ namespace util {
class Repeat {
struct Control {
boost::asio::steady_timer timer;
boost::asio::strand<boost::asio::any_io_executor> strand;
std::atomic_bool stopping{true};
std::binary_semaphore semaphore{0};
Control(auto& ctx) : timer(ctx), strand(boost::asio::make_strand(ctx))
Control(auto& ctx) : timer(ctx)
{
}
};
@@ -101,24 +98,15 @@ private:
static void
startImpl(std::shared_ptr<Control> control, std::chrono::steady_clock::duration interval, Action&& action)
{
boost::asio::post(control->strand, [control, interval, action = std::forward<Action>(action)]() mutable {
if (control->stopping) {
control->timer.expires_after(interval);
control->timer.async_wait([control, interval, action = std::forward<Action>(action)](auto const& ec) mutable {
if (ec or control->stopping) {
control->semaphore.release();
return;
}
action();
control->timer.expires_after(interval);
control->timer.async_wait(
[control, interval, action = std::forward<Action>(action)](auto const& ec) mutable {
if (ec or control->stopping) {
control->semaphore.release();
return;
}
action();
startImpl(std::move(control), interval, std::forward<Action>(action));
}
);
startImpl(std::move(control), interval, std::forward<Action>(action));
});
}
};

View File

@@ -23,13 +23,10 @@
#include "util/config/ConfigDefinition.hpp"
#include "util/log/Logger.hpp"
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <csignal>
#include <functional>
#include <mutex>
#include <thread>
#include <optional>
#include <utility>
namespace util {
@@ -53,11 +50,17 @@ public:
}
static void
handleSignal(int /* signal */)
handleSignal(int signal)
{
ASSERT(installedHandler != nullptr, "SignalsHandler is not initialized");
installedHandler->signalReceived_ = true;
installedHandler->cv_.notify_one();
installedHandler->stopHandler_(signal);
}
static void
handleSecondSignal(int signal)
{
ASSERT(installedHandler != nullptr, "SignalsHandler is not initialized");
installedHandler->secondSignalHandler_(signal);
}
};
@@ -66,109 +69,56 @@ SignalsHandler* SignalsHandlerStatic::installedHandler = nullptr;
} // namespace impl
SignalsHandler::SignalsHandler(config::ClioConfigDefinition const& config, std::function<void()> forceExitHandler)
: gracefulPeriod_(util::config::ClioConfigDefinition::toMilliseconds(config.get<float>("graceful_period")))
, forceExitHandler_(std::move(forceExitHandler))
: gracefulPeriod_(0)
, context_(1)
, stopHandler_([this, forceExitHandler](int) mutable {
LOG(LogService::info()) << "Got stop signal. Stopping Clio. Graceful period is "
<< std::chrono::duration_cast<std::chrono::milliseconds>(gracefulPeriod_).count()
<< " milliseconds.";
setHandler(impl::SignalsHandlerStatic::handleSecondSignal);
timer_.emplace(context_.scheduleAfter(
gracefulPeriod_, [forceExitHandler = std::move(forceExitHandler)](auto&& stopToken, bool canceled) {
// TODO: Update this after https://github.com/XRPLF/clio/issues/1380
if (not stopToken.isStopRequested() and not canceled) {
LOG(LogService::warn()) << "Force exit at the end of graceful period.";
forceExitHandler();
}
}
));
stopSignal_();
})
, secondSignalHandler_([this, forceExitHandler = std::move(forceExitHandler)](int) {
LOG(LogService::warn()) << "Force exit on second signal.";
forceExitHandler();
cancelTimer();
setHandler();
})
{
impl::SignalsHandlerStatic::registerHandler(*this);
workerThread_ = std::thread([this]() { runStateMachine(); });
gracefulPeriod_ = util::config::ClioConfigDefinition::toMilliseconds(config.get<float>("graceful_period"));
setHandler(impl::SignalsHandlerStatic::handleSignal);
}
SignalsHandler::~SignalsHandler()
{
cancelTimer();
setHandler();
state_ = State::NormalExit;
cv_.notify_one();
if (workerThread_.joinable())
workerThread_.join();
impl::SignalsHandlerStatic::resetHandler(); // This is needed mostly for tests to reset static state
}
void
SignalsHandler::notifyGracefulShutdownComplete()
SignalsHandler::cancelTimer()
{
if (state_ == State::GracefulShutdown) {
LOG(LogService::info()) << "Graceful shutdown completed successfully.";
state_ = State::NormalExit;
cv_.notify_one();
}
if (timer_.has_value())
timer_->abort();
}
void
SignalsHandler::setHandler(void (*handler)(int))
{
for (int const signal : kHANDLED_SIGNALS)
for (int const signal : kHANDLED_SIGNALS) {
std::signal(signal, handler == nullptr ? SIG_DFL : handler);
}
void
SignalsHandler::runStateMachine()
{
while (state_ != State::NormalExit) {
auto currentState = state_.load();
switch (currentState) {
case State::WaitingForSignal: {
{
std::unique_lock<std::mutex> lock(mutex_);
cv_.wait(lock, [this]() { return signalReceived_ or state_ == State::NormalExit; });
}
if (state_ == State::NormalExit)
return;
LOG(
LogService::info()
) << "Got stop signal. Stopping Clio. Graceful period is "
<< std::chrono::duration_cast<std::chrono::milliseconds>(gracefulPeriod_).count() << " milliseconds.";
state_ = State::GracefulShutdown;
signalReceived_ = false;
stopSignal_();
break;
}
case State::GracefulShutdown: {
bool waitResult = false;
{
std::unique_lock<std::mutex> lock(mutex_);
// Wait for either:
// 1. Graceful period to elapse (timeout)
// 2. Another signal (signalReceived_)
// 3. Graceful shutdown completion (state changes to NormalExit)
waitResult = cv_.wait_for(lock, gracefulPeriod_, [this]() {
return signalReceived_ or state_ == State::NormalExit;
});
}
if (state_ == State::NormalExit)
break;
if (signalReceived_) {
LOG(LogService::warn()) << "Force exit on second signal.";
state_ = State::ForceExit;
signalReceived_ = false;
} else if (not waitResult) {
LOG(LogService::warn()) << "Force exit at the end of graceful period.";
state_ = State::ForceExit;
}
break;
}
case State::ForceExit: {
forceExitHandler_();
state_ = State::NormalExit;
break;
}
case State::NormalExit:
return;
}
}
}

View File

@@ -19,20 +19,22 @@
#pragma once
#include "util/async/context/BasicExecutionContext.hpp"
#include "util/config/ConfigDefinition.hpp"
#include "util/log/Logger.hpp"
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/signals2/signal.hpp>
#include <boost/signals2/variadic_signal.hpp>
#include <atomic>
#include <chrono>
#include <concepts>
#include <condition_variable>
#include <csignal>
#include <cstdlib>
#include <functional>
#include <mutex>
#include <thread>
#include <optional>
namespace util {
namespace impl {
@@ -46,22 +48,13 @@ class SignalsHandlerStatic;
* @note There could be only one instance of this class.
*/
class SignalsHandler {
/**
* @brief States of the signal handler state machine.
*/
enum class State { WaitingForSignal, GracefulShutdown, ForceExit, NormalExit };
std::chrono::steady_clock::duration gracefulPeriod_;
std::function<void()> forceExitHandler_;
async::PoolExecutionContext context_;
std::optional<async::PoolExecutionContext::ScheduledOperation<void>> timer_;
boost::signals2::signal<void()> stopSignal_;
std::atomic<bool> signalReceived_{false};
std::atomic<State> state_{State::WaitingForSignal};
std::mutex mutex_;
std::condition_variable cv_;
std::thread workerThread_;
std::function<void(int)> stopHandler_;
std::function<void(int)> secondSignalHandler_;
friend class impl::SignalsHandlerStatic;
@@ -108,16 +101,15 @@ public:
stopSignal_.connect(static_cast<int>(priority), std::forward<SomeCallback>(callback));
}
/**
* @brief Notify the signal handler that graceful shutdown has completed.
* This allows the handler to transition to NormalExit state.
*/
void
notifyGracefulShutdownComplete();
static constexpr auto kHANDLED_SIGNALS = {SIGINT, SIGTERM};
private:
/**
* @brief Cancel scheduled force exit if any.
*/
void
cancelTimer();
/**
* @brief Set signal handler for handled signals.
*
@@ -126,12 +118,6 @@ private:
static void
setHandler(void (*handler)(int) = nullptr);
/**
* @brief Run the state machine loop in a worker thread.
*/
void
runStateMachine();
static constexpr auto kDEFAULT_FORCE_EXIT_HANDLER = []() { std::exit(EXIT_FAILURE); };
};

View File

@@ -35,6 +35,7 @@
#include <boost/beast/http/status.hpp>
#include <boost/beast/http/string_body.hpp>
#include <boost/beast/http/verb.hpp>
#include <boost/beast/ssl/ssl_stream.hpp>
#include <openssl/err.h>
#include <openssl/tls1.h>

View File

@@ -78,8 +78,8 @@ private:
}
};
using SslTcpStreamData = SslStreamData<boost::asio::ssl::stream<boost::beast::tcp_stream>>;
using SslTcpStreamData = SslStreamData<boost::beast::ssl_stream<boost::beast::tcp_stream>>;
using SslWsStreamData =
SslStreamData<boost::beast::websocket::stream<boost::asio::ssl::stream<boost::beast::tcp_stream>>>;
SslStreamData<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::tcp_stream>>>;
} // namespace util::requests::impl

View File

@@ -124,6 +124,6 @@ public:
using PlainWsConnection = WsConnectionImpl<boost::beast::websocket::stream<boost::beast::tcp_stream>>;
using SslWsConnection =
WsConnectionImpl<boost::beast::websocket::stream<boost::asio::ssl::stream<boost::beast::tcp_stream>>>;
WsConnectionImpl<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::tcp_stream>>>;
} // namespace util::requests::impl

View File

@@ -61,7 +61,7 @@ using tcp = boost::asio::ip::tcp;
template <SomeServerHandler HandlerType>
class SslHttpSession : public impl::HttpBase<SslHttpSession, HandlerType>,
public std::enable_shared_from_this<SslHttpSession<HandlerType>> {
boost::asio::ssl::stream<boost::beast::tcp_stream> stream_;
boost::beast::ssl_stream<boost::beast::tcp_stream> stream_;
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory_;
std::uint32_t maxWsSendingQueueSize_;
@@ -113,7 +113,7 @@ public:
~SslHttpSession() override = default;
/** @return The SSL stream. */
boost::asio::ssl::stream<boost::beast::tcp_stream>&
boost::beast::ssl_stream<boost::beast::tcp_stream>&
stream()
{
return stream_;

View File

@@ -51,7 +51,7 @@ namespace web {
*/
template <SomeServerHandler HandlerType>
class SslWsSession : public impl::WsBase<SslWsSession, HandlerType> {
using StreamType = boost::beast::websocket::stream<boost::asio::ssl::stream<boost::beast::tcp_stream>>;
using StreamType = boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::tcp_stream>>;
StreamType ws_;
public:
@@ -68,7 +68,7 @@ public:
* @param maxWsSendingQueueSize The maximum size of the sending queue for websocket
*/
explicit SslWsSession(
boost::asio::ssl::stream<boost::beast::tcp_stream>&& stream,
boost::beast::ssl_stream<boost::beast::tcp_stream>&& stream,
std::string ip,
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
@@ -107,7 +107,7 @@ template <SomeServerHandler HandlerType>
class SslWsUpgrader : public std::enable_shared_from_this<SslWsUpgrader<HandlerType>> {
using std::enable_shared_from_this<SslWsUpgrader<HandlerType>>::shared_from_this;
boost::asio::ssl::stream<boost::beast::tcp_stream> https_;
boost::beast::ssl_stream<boost::beast::tcp_stream> https_;
boost::optional<http::request_parser<http::string_body>> parser_;
boost::beast::flat_buffer buffer_;
std::string ip_;
@@ -133,7 +133,7 @@ public:
* @param maxWsSendingQueueSize The maximum size of the sending queue for websocket
*/
SslWsUpgrader(
boost::asio::ssl::stream<boost::beast::tcp_stream> stream,
boost::beast::ssl_stream<boost::beast::tcp_stream> stream,
std::string ip,
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,

View File

@@ -26,7 +26,6 @@
#include <boost/asio/spawn.hpp>
#include <boost/asio/ssl/context.hpp>
#include <boost/asio/ssl/error.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <boost/asio/ssl/stream_base.hpp>
#include <boost/asio/ssl/verify_context.hpp>
#include <boost/asio/ssl/verify_mode.hpp>
@@ -41,6 +40,7 @@
#include <boost/beast/http/string_body.hpp>
#include <boost/beast/http/verb.hpp>
#include <boost/beast/http/write.hpp> // IWYU pragma: keep
#include <boost/beast/ssl/ssl_stream.hpp>
#include <boost/beast/version.hpp>
#include <openssl/err.h>
#include <openssl/tls1.h>
@@ -148,7 +148,7 @@ HttpsSyncClient::syncPost(std::string const& host, std::string const& port, std:
ctx.set_verify_mode(ssl::verify_none);
tcp::resolver resolver(ioc);
boost::asio::ssl::stream<boost::beast::tcp_stream> stream(ioc, ctx);
boost::beast::ssl_stream<boost::beast::tcp_stream> stream(ioc, ctx);
// We can't fix this so have to ignore
#pragma GCC diagnostic push

View File

@@ -80,7 +80,7 @@ public:
class WebServerSslSyncClient {
boost::asio::io_context ioc_;
std::optional<boost::beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>> ws_;
std::optional<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>> ws_;
public:
void

View File

@@ -18,7 +18,6 @@
//==============================================================================
#include "util/JsonUtils.hpp"
#include "util/NameGenerator.hpp"
#include <boost/json/parse.hpp>
#include <boost/json/value.hpp>
@@ -27,8 +26,7 @@
#include <cstdint>
#include <limits>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <tuple>
TEST(JsonUtils, RemoveSecrets)
{
@@ -92,123 +90,28 @@ TEST(JsonUtils, integralValueAs)
EXPECT_THROW(util::integralValueAs<int>(stringJson), std::logic_error);
}
TEST(JsonUtils, tryIntegralValueAs)
TEST(JsonUtils, getLedgerIndex)
{
auto const expectedResultUint64 = static_cast<uint64_t>(std::numeric_limits<int32_t>::max()) + 1u;
auto const uint64Json = boost::json::value(expectedResultUint64);
auto const emptyJson = boost::json::value();
EXPECT_THROW(std::ignore = util::getLedgerIndex(emptyJson), std::logic_error);
auto const expectedResultInt64 = static_cast<int64_t>(std::numeric_limits<int32_t>::max()) + 1u;
auto const int64Json = boost::json::value(expectedResultInt64);
auto const boolJson = boost::json::value(true);
EXPECT_THROW(std::ignore = util::getLedgerIndex(emptyJson), std::logic_error);
auto checkHasValue = [&](boost::json::value const& jv, auto const& expectedValue) {
using T = std::remove_cvref_t<decltype(expectedValue)>;
auto const res = util::tryIntegralValueAs<T>(jv);
ASSERT_TRUE(res.has_value());
EXPECT_EQ(res.value(), expectedValue);
};
auto const numberJson = boost::json::value(12345);
auto ledgerIndex = util::getLedgerIndex(numberJson);
EXPECT_TRUE(ledgerIndex.has_value());
EXPECT_EQ(ledgerIndex.value(), 12345u);
auto checkError = [&](boost::json::value const& jv) {
auto res = util::tryIntegralValueAs<int>(jv);
EXPECT_FALSE(res.has_value());
EXPECT_EQ(res.error(), "Value neither uint64 nor int64");
};
auto const validStringJson = boost::json::value("12345");
ledgerIndex = util::getLedgerIndex(validStringJson);
EXPECT_TRUE(ledgerIndex.has_value());
EXPECT_EQ(ledgerIndex.value(), 12345u);
// checks for uint64Json
checkHasValue(uint64Json, std::numeric_limits<int32_t>::min());
checkHasValue(uint64Json, static_cast<uint32_t>(expectedResultUint64));
checkHasValue(uint64Json, static_cast<int64_t>(expectedResultUint64));
checkHasValue(uint64Json, expectedResultUint64);
auto const invalidStringJson = boost::json::value("invalid123");
EXPECT_THROW(std::ignore = util::getLedgerIndex(invalidStringJson), std::invalid_argument);
// checks for int64Json
checkHasValue(int64Json, std::numeric_limits<int32_t>::min());
checkHasValue(int64Json, static_cast<uint32_t>(expectedResultInt64));
checkHasValue(int64Json, expectedResultInt64);
checkHasValue(int64Json, static_cast<uint64_t>(expectedResultInt64));
// non-integral inputs
checkError(boost::json::value());
checkError(boost::json::value(false));
checkError(boost::json::value(3.14));
checkError(boost::json::value("not a number"));
}
struct GetLedgerIndexParameterTestBundle {
std::string testName;
boost::json::value jv;
std::expected<uint32_t, std::string> expectedResult;
};
// parameterized test cases for parameters check
struct GetLedgerIndexParameterTest : ::testing::TestWithParam<GetLedgerIndexParameterTestBundle> {};
INSTANTIATE_TEST_CASE_P(
JsonUtils,
GetLedgerIndexParameterTest,
testing::Values(
GetLedgerIndexParameterTestBundle{
.testName = "EmptyValue",
.jv = boost::json::value(),
.expectedResult = std::unexpected{"Value neither uint64 nor int64"}
},
GetLedgerIndexParameterTestBundle{
.testName = "BoolValue",
.jv = boost::json::value(false),
.expectedResult = std::unexpected{"Value neither uint64 nor int64"}
},
GetLedgerIndexParameterTestBundle{
.testName = "NumberValue",
.jv = boost::json::value(123),
.expectedResult = 123u
},
GetLedgerIndexParameterTestBundle{
.testName = "StringNumberValue",
.jv = boost::json::value("123"),
.expectedResult = 123u
},
GetLedgerIndexParameterTestBundle{
.testName = "StringNumberWithPlusSignValue",
.jv = boost::json::value("+123"),
.expectedResult = 123u
},
GetLedgerIndexParameterTestBundle{
.testName = "StringEmptyValue",
.jv = boost::json::value(""),
.expectedResult = std::unexpected{"Invalid ledger index string"}
},
GetLedgerIndexParameterTestBundle{
.testName = "StringWithLeadingCharsValue",
.jv = boost::json::value("123invalid"),
.expectedResult = std::unexpected{"Invalid ledger index string"}
},
GetLedgerIndexParameterTestBundle{
.testName = "StringWithTrailingCharsValue",
.jv = boost::json::value("invalid123"),
.expectedResult = std::unexpected{"Invalid ledger index string"}
},
GetLedgerIndexParameterTestBundle{
.testName = "StringWithLeadingAndTrailingCharsValue",
.jv = boost::json::value("123invalid123"),
.expectedResult = std::unexpected{"Invalid ledger index string"}
},
GetLedgerIndexParameterTestBundle{
.testName = "ValidatedStringValue",
.jv = boost::json::value("validated"),
.expectedResult = std::unexpected{"'validated' ledger index is requested"}
}
),
tests::util::kNAME_GENERATOR
);
TEST_P(GetLedgerIndexParameterTest, getLedgerIndexParams)
{
auto const& testBundle = GetParam();
auto const ledgerIndex = util::getLedgerIndex(testBundle.jv);
if (testBundle.expectedResult.has_value()) {
EXPECT_TRUE(ledgerIndex.has_value());
EXPECT_EQ(ledgerIndex.value(), testBundle.expectedResult.value());
} else {
EXPECT_FALSE(ledgerIndex.has_value());
EXPECT_EQ(ledgerIndex.error(), testBundle.expectedResult.error());
}
auto const validatedJson = boost::json::value("validated");
ledgerIndex = util::getLedgerIndex(validatedJson);
EXPECT_FALSE(ledgerIndex.has_value());
}

View File

@@ -40,7 +40,6 @@ struct StopperTest : virtual public ::testing::Test {
protected:
// Order here is important, stopper_ should die before mockCallback_, otherwise UB
testing::StrictMock<testing::MockFunction<void(boost::asio::yield_context)>> mockCallback_;
testing::StrictMock<testing::MockFunction<void()>> mockCompleteCallback_;
Stopper stopper_;
};
@@ -61,22 +60,6 @@ TEST_F(StopperTest, stopCalledMultipleTimes)
stopper_.stop();
}
TEST_F(StopperTest, stopCallsCompletionCallback)
{
stopper_.setOnStop(mockCallback_.AsStdFunction());
stopper_.setOnComplete(mockCompleteCallback_.AsStdFunction());
EXPECT_CALL(mockCallback_, Call);
EXPECT_CALL(mockCompleteCallback_, Call);
stopper_.stop();
}
TEST_F(StopperTest, stopWithoutCompletionCallback)
{
stopper_.setOnStop(mockCallback_.AsStdFunction());
EXPECT_CALL(mockCallback_, Call);
stopper_.stop();
}
struct StopperMakeCallbackTest : util::prometheus::WithPrometheus, SyncAsioContextTest {
struct ServerMock : web::ServerTag {
MOCK_METHOD(void, stop, (boost::asio::yield_context), ());

View File

@@ -19,7 +19,6 @@
#include "app/WebHandlers.hpp"
#include "rpc/Errors.hpp"
#include "rpc/WorkQueue.hpp"
#include "util/AsioContextTestFixture.hpp"
#include "util/MockLedgerCache.hpp"
#include "util/MockPrometheus.hpp"
@@ -123,9 +122,7 @@ struct MetricsHandlerTests : util::prometheus::WithPrometheus, SyncAsioContextTe
std::make_shared<testing::StrictMock<AdminVerificationStrategyMock>>()
};
rpc::WorkQueue workQueue{1};
MetricsHandler metricsHandler{adminVerifier, workQueue};
MetricsHandler metricsHandler{adminVerifier};
web::ng::Request request{http::request<http::string_body>{http::verb::get, "/metrics", 11}};
};

View File

@@ -53,16 +53,16 @@ struct RepeatTests : SyncAsioContextTest {
TEST_F(RepeatTests, CallsHandler)
{
EXPECT_CALL(handlerMock, Call).Times(testing::AtMost(22));
repeat.start(std::chrono::milliseconds{1}, handlerMock.AsStdFunction());
EXPECT_CALL(handlerMock, Call).Times(testing::AtMost(22));
runContextFor(std::chrono::milliseconds{20});
}
TEST_F(RepeatTests, StopsOnStop)
{
withRunningContext([this]() {
EXPECT_CALL(handlerMock, Call).Times(AtLeast(1));
repeat.start(std::chrono::milliseconds{1}, handlerMock.AsStdFunction());
EXPECT_CALL(handlerMock, Call).Times(AtLeast(1));
std::this_thread::sleep_for(std::chrono::milliseconds{10});
repeat.stop();
});
@@ -72,8 +72,8 @@ TEST_F(RepeatTests, RunsAfterStop)
{
withRunningContext([this]() {
for ([[maybe_unused]] auto i : std::ranges::iota_view(0, 2)) {
EXPECT_CALL(handlerMock, Call).Times(AtLeast(1));
repeat.start(std::chrono::milliseconds{1}, handlerMock.AsStdFunction());
EXPECT_CALL(handlerMock, Call).Times(AtLeast(1));
std::this_thread::sleep_for(std::chrono::milliseconds{10});
repeat.stop();
}

View File

@@ -70,7 +70,7 @@ TEST_F(SignalsHandlerAssertTest, CantCreateTwoSignalsHandlers)
{
auto makeHandler = []() {
return SignalsHandler{
ClioConfigDefinition{{"graceful_period", ConfigValue{ConfigType::Double}.defaultValue(1.f)}}, []() {}
ClioConfigDefinition{{"graceful_period", ConfigValue{ConfigType::Double}.defaultValue(10.f)}}, []() {}
};
};
auto const handler = makeHandler();
@@ -96,11 +96,7 @@ TEST_F(SignalsHandlerTests, OneSignal)
handler_.subscribeToStop(stopHandler_.AsStdFunction());
handler_.subscribeToStop(anotherStopHandler_.AsStdFunction());
EXPECT_CALL(stopHandler_, Call());
EXPECT_CALL(anotherStopHandler_, Call()).WillOnce([this] {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
handler_.notifyGracefulShutdownComplete();
allowTestToFinish();
});
EXPECT_CALL(anotherStopHandler_, Call()).WillOnce([this]() { allowTestToFinish(); });
std::raise(SIGINT);
wait();
@@ -117,44 +113,21 @@ protected:
TEST_F(SignalsHandlerTimeoutTests, OneSignalTimeout)
{
handler_.subscribeToStop(stopHandler_.AsStdFunction());
EXPECT_CALL(stopHandler_, Call()).WillOnce([] {
// Don't notify completion, let it timeout
std::this_thread::sleep_for(std::chrono::milliseconds(2));
});
EXPECT_CALL(forceExitHandler_, Call()).WillOnce([this]() { allowTestToFinish(); });
EXPECT_CALL(stopHandler_, Call()).WillOnce([] { std::this_thread::sleep_for(std::chrono::milliseconds(2)); });
EXPECT_CALL(forceExitHandler_, Call());
std::raise(SIGINT);
wait();
}
TEST_F(SignalsHandlerTests, TwoSignals)
{
handler_.subscribeToStop(stopHandler_.AsStdFunction());
EXPECT_CALL(stopHandler_, Call()).WillOnce([] {
// Raise second signal during graceful shutdown
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::raise(SIGINT);
});
EXPECT_CALL(stopHandler_, Call()).WillOnce([] { std::raise(SIGINT); });
EXPECT_CALL(forceExitHandler_, Call()).WillOnce([this]() { allowTestToFinish(); });
std::raise(SIGINT);
wait();
}
TEST_F(SignalsHandlerTests, GracefulShutdownCompletes)
{
handler_.subscribeToStop(stopHandler_.AsStdFunction());
EXPECT_CALL(stopHandler_, Call()).WillOnce([this] {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
handler_.notifyGracefulShutdownComplete();
allowTestToFinish();
});
EXPECT_CALL(forceExitHandler_, Call()).Times(0);
std::raise(SIGINT);
wait();
}
struct SignalsHandlerPriorityTestsBundle {
std::string name;
SignalsHandler::Priority stopHandlerPriority;
@@ -191,10 +164,9 @@ TEST_P(SignalsHandlerPriorityTests, Priority)
EXPECT_CALL(stopHandler_, Call()).WillOnce([&] { stopHandlerCalled = true; });
EXPECT_CALL(anotherStopHandler_, Call()).WillOnce([&] {
EXPECT_TRUE(stopHandlerCalled);
handler_.notifyGracefulShutdownComplete();
allowTestToFinish();
});
std::raise(SIGINT);
wait();
}

View File

@@ -54,9 +54,11 @@
#include <gtest/gtest.h>
#include <test_data/SslCert.hpp>
#include <condition_variable>
#include <cstdint>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <stdexcept>
#include <string>
@@ -229,7 +231,25 @@ makeServerSync(
std::reference_wrapper<data::LedgerCacheInterface const> cache
)
{
return web::makeHttpServer(config, ioc, dosGuard, handler, cache);
auto server = std::shared_ptr<web::HttpServer<Executor>>();
std::mutex m;
std::condition_variable cv;
bool ready = false;
boost::asio::dispatch(ioc.get_executor(), [&]() mutable {
server = web::makeHttpServer(config, ioc, dosGuard, handler, cache);
{
std::lock_guard const lk(m);
ready = true;
}
cv.notify_one();
});
{
std::unique_lock lk(m);
cv.wait(lk, [&] { return ready; });
}
return server;
}
} // namespace

View File

@@ -1,8 +1,7 @@
#!/usr/bin/env python3
import json
from pathlib import Path
import plumbum
from pathlib import Path
THIS_DIR = Path(__file__).parent.resolve()
ROOT_DIR = THIS_DIR.parent.resolve()
@@ -22,23 +21,15 @@ def rebuild():
for profile in profiles:
print(f"Rebuilding {profile} with build type {build_type}")
with plumbum.local.cwd(ROOT_DIR):
(
CONAN[
"install",
".",
"--build=missing",
f"--output-folder=build_{profile}_{build_type}",
"-s",
f"build_type={build_type}",
"-o",
"&:tests=True",
"-o",
"&:benchmark=True",
"--profile:all",
profile,
]
& plumbum.FG
)
CONAN[
"install", ".",
"--build=missing",
f"--output-folder=build_{profile}_{build_type}",
"-s", f"build_type={build_type}",
"-o", "&:tests=True",
"-o", "&:benchmark=True",
"--profile:all", profile
] & plumbum.FG
if __name__ == "__main__":