Compare commits

..

16 Commits

Author SHA1 Message Date
Peter Chen
7eaf0005e4 Merge tag 'experiment' into experimental_keyspace
test keyspace
2025-09-16 09:08:58 -07:00
Peter Chen
497721ee7c change connections per host 2025-09-15 10:06:12 -07:00
Peter Chen
26530108e3 fix comments 2025-09-15 08:55:14 -07:00
Alex Kremer
fc88abdaeb Merge branch 'develop' into SupportKeyspace 2025-09-15 14:59:54 +01:00
Ayaz Salikhov
8bc36c2c0b Merge branch 'develop' into SupportKeyspace 2025-09-04 18:23:37 +01:00
Peter Chen
4e9558f76b 'Merge branch 'SupportKeyspace' into experimental_keyspace 2025-09-04 08:44:50 -07:00
Ayaz Salikhov
84db880ce7 Merge branch 'develop' into SupportKeyspace 2025-08-28 15:11:58 +01:00
Peter Chen
f88ce31363 let scylladb use same schema to update ledger range as before 2025-08-27 14:34:34 -07:00
Peter Chen
e03f5e46c0 Merge branch 'develop' into SupportKeyspace 2025-08-24 18:58:38 -04:00
Peter Chen
30da8d8f63 fix val 2025-08-24 18:58:07 -04:00
Peter Chen
8f6bec2e25 merge develop 2025-08-21 12:37:19 -04:00
Peter Chen
0d9a83fd4d fix comments 2025-08-20 12:29:01 -04:00
Peter Chen
47c2af0421 Merge branch 'develop' into SupportKeyspace 2025-08-19 16:37:08 -04:00
Peter Chen
c3e04426d3 fix bug and comments 2025-08-19 16:19:44 -04:00
Peter Chen
d598396445 remove unnecessary comment 2025-08-18 16:33:12 -04:00
Peter Chen
bbd2884e3b feat: Support Keyspace 2025-08-18 15:38:56 -04:00
136 changed files with 1746 additions and 3849 deletions

View File

@@ -62,8 +62,6 @@ jobs:
container: ${{ matrix.container }}
conan_profile: ${{ matrix.conan_profile }}
build_type: ${{ matrix.build_type }}
download_ccache: true
upload_ccache: true
static: ${{ matrix.static }}
run_unit_tests: true
run_integration_tests: false
@@ -78,8 +76,7 @@ jobs:
container: '{ "image": "ghcr.io/xrplf/clio-ci:384e79cd32f5f6c0ab9be3a1122ead41c5a7e67d" }'
conan_profile: gcc
build_type: Debug
download_ccache: true
upload_ccache: false
disable_cache: false
code_coverage: true
static: true
upload_clio_server: false
@@ -97,8 +94,7 @@ jobs:
container: '{ "image": "ghcr.io/xrplf/clio-ci:384e79cd32f5f6c0ab9be3a1122ead41c5a7e67d" }'
conan_profile: gcc
build_type: Release
download_ccache: true
upload_ccache: false
disable_cache: false
code_coverage: false
static: true
upload_clio_server: false

View File

@@ -23,14 +23,8 @@ on:
required: true
type: string
download_ccache:
description: Whether to download ccache from the cache
required: false
type: boolean
default: true
upload_ccache:
description: Whether to upload ccache to the cache
disable_cache:
description: Whether ccache should be disabled
required: false
type: boolean
default: false
@@ -83,8 +77,7 @@ jobs:
container: ${{ inputs.container }}
conan_profile: ${{ inputs.conan_profile }}
build_type: ${{ inputs.build_type }}
download_ccache: ${{ inputs.download_ccache }}
upload_ccache: ${{ inputs.upload_ccache }}
disable_cache: ${{ inputs.disable_cache }}
code_coverage: false
static: ${{ inputs.static }}
upload_clio_server: ${{ inputs.upload_clio_server }}

View File

@@ -23,17 +23,10 @@ on:
required: true
type: string
download_ccache:
description: Whether to download ccache from the cache
disable_cache:
description: Whether ccache should be disabled
required: false
type: boolean
default: true
upload_ccache:
description: Whether to upload ccache to the cache
required: false
type: boolean
default: false
code_coverage:
description: Whether to enable code coverage
@@ -97,15 +90,15 @@ jobs:
- name: Prepare runner
uses: XRPLF/actions/.github/actions/prepare-runner@7951b682e5a2973b28b0719a72f01fc4b0d0c34f
with:
disable_ccache: ${{ !inputs.download_ccache }}
disable_ccache: ${{ inputs.disable_cache }}
- name: Setup conan on macOS
if: ${{ runner.os == 'macOS' }}
if: runner.os == 'macOS'
shell: bash
run: ./.github/scripts/conan/init.sh
- name: Restore cache
if: ${{ inputs.download_ccache }}
if: ${{ !inputs.disable_cache }}
uses: ./.github/actions/restore_cache
id: restore_cache
with:
@@ -151,7 +144,7 @@ jobs:
path: build_time_report.txt
- name: Show ccache's statistics
if: ${{ inputs.download_ccache }}
if: ${{ !inputs.disable_cache }}
shell: bash
id: ccache_stats
run: |
@@ -169,7 +162,7 @@ jobs:
run: strip build/clio_integration_tests
- name: Upload clio_server
if: ${{ inputs.upload_clio_server && !inputs.code_coverage && !inputs.analyze_build_time }}
if: inputs.upload_clio_server && !inputs.code_coverage && !inputs.analyze_build_time
uses: actions/upload-artifact@v4
with:
name: clio_server_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }}
@@ -190,14 +183,14 @@ jobs:
path: build/clio_integration_tests
- name: Upload Clio Linux package
if: ${{ inputs.package }}
if: inputs.package
uses: actions/upload-artifact@v4
with:
name: clio_deb_package_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }}
path: build/*.deb
- name: Save cache
if: ${{ inputs.upload_ccache && github.ref == 'refs/heads/develop' }}
if: ${{ !inputs.disable_cache && github.ref == 'refs/heads/develop' }}
uses: ./.github/actions/save_cache
with:
conan_profile: ${{ inputs.conan_profile }}

View File

@@ -1,8 +1,6 @@
name: Clang-tidy check
on:
push:
branches: [develop]
schedule:
- cron: "0 9 * * 1-5"
workflow_dispatch:
@@ -24,7 +22,6 @@ env:
jobs:
clang_tidy:
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:384e79cd32f5f6c0ab9be3a1122ead41c5a7e67d

View File

@@ -0,0 +1,30 @@
name: Restart clang-tidy workflow
on:
push:
branches: [develop]
workflow_dispatch:
jobs:
restart_clang_tidy:
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- uses: actions/checkout@v4
- name: Check last commit matches clang-tidy auto fixes
id: check
shell: bash
run: |
passed=$(if [[ "$(git log -1 --pretty=format:%s | grep 'style: clang-tidy auto fixes')" ]]; then echo 'true' ; else echo 'false' ; fi)
echo "passed=\"$passed\"" >> $GITHUB_OUTPUT
- name: Run clang-tidy workflow
if: ${{ contains(steps.check.outputs.passed, 'true') }}
shell: bash
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: gh workflow run clang-tidy.yml

View File

@@ -61,8 +61,7 @@ jobs:
run_unit_tests: true
run_integration_tests: true
upload_clio_server: true
download_ccache: false
upload_ccache: false
disable_cache: true
analyze_build_time:
name: Analyze Build Time
@@ -85,8 +84,7 @@ jobs:
container: ${{ matrix.container }}
conan_profile: ${{ matrix.conan_profile }}
build_type: Release
download_ccache: false
upload_ccache: false
disable_cache: true
code_coverage: false
static: ${{ matrix.static }}
upload_clio_server: false

View File

@@ -41,8 +41,7 @@ jobs:
run_unit_tests: true
run_integration_tests: true
upload_clio_server: true
download_ccache: false
upload_ccache: false
disable_cache: true
expected_version: ${{ github.event_name == 'push' && github.ref_name || '' }}
release:

View File

@@ -45,8 +45,7 @@ jobs:
with:
runs_on: heavy
container: '{ "image": "ghcr.io/xrplf/clio-ci:384e79cd32f5f6c0ab9be3a1122ead41c5a7e67d" }'
download_ccache: false
upload_ccache: false
disable_cache: true
conan_profile: ${{ matrix.compiler }}${{ matrix.sanitizer_ext }}
build_type: ${{ matrix.build_type }}
static: false

View File

@@ -39,11 +39,11 @@ jobs:
runs-on: ${{ inputs.runs_on }}
container: ${{ inputs.container != '' && fromJson(inputs.container) || null }}
if: ${{ inputs.run_unit_tests }}
if: inputs.run_unit_tests
env:
# TODO: remove completely when we have fixed all currently existing issues with sanitizers
SANITIZER_IGNORE_ERRORS: ${{ endsWith(inputs.conan_profile, '.tsan') || inputs.conan_profile == 'clang.asan' || (inputs.conan_profile == 'gcc.asan' && inputs.build_type == 'Release') }}
SANITIZER_IGNORE_ERRORS: ${{ endsWith(inputs.conan_profile, '.asan') || endsWith(inputs.conan_profile, '.tsan') }}
steps:
- name: Cleanup workspace
@@ -63,15 +63,15 @@ jobs:
run: chmod +x ./clio_tests
- name: Run clio_tests (regular)
if: ${{ env.SANITIZER_IGNORE_ERRORS == 'false' }}
if: env.SANITIZER_IGNORE_ERRORS == 'false'
run: ./clio_tests
- name: Run clio_tests (sanitizer errors ignored)
if: ${{ env.SANITIZER_IGNORE_ERRORS == 'true' }}
if: env.SANITIZER_IGNORE_ERRORS == 'true'
run: ./.github/scripts/execute-tests-under-sanitizer ./clio_tests
- name: Check for sanitizer report
if: ${{ env.SANITIZER_IGNORE_ERRORS == 'true' }}
if: env.SANITIZER_IGNORE_ERRORS == 'true'
shell: bash
id: check_report
run: |
@@ -82,7 +82,7 @@ jobs:
fi
- name: Upload sanitizer report
if: ${{ env.SANITIZER_IGNORE_ERRORS == 'true' && steps.check_report.outputs.found_report == 'true' }}
if: env.SANITIZER_IGNORE_ERRORS == 'true' && steps.check_report.outputs.found_report == 'true'
uses: actions/upload-artifact@v4
with:
name: sanitizer_report_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }}
@@ -90,7 +90,7 @@ jobs:
include-hidden-files: true
- name: Create an issue
if: ${{ false && env.SANITIZER_IGNORE_ERRORS == 'true' && steps.check_report.outputs.found_report == 'true' }}
if: false && env.SANITIZER_IGNORE_ERRORS == 'true' && steps.check_report.outputs.found_report == 'true'
uses: ./.github/actions/create_issue
env:
GH_TOKEN: ${{ github.token }}
@@ -108,7 +108,7 @@ jobs:
runs-on: ${{ inputs.runs_on }}
container: ${{ inputs.container != '' && fromJson(inputs.container) || null }}
if: ${{ inputs.run_integration_tests }}
if: inputs.run_integration_tests
services:
scylladb:

View File

@@ -61,7 +61,7 @@ jobs:
files: "docker/compilers/gcc/**"
- uses: ./.github/actions/build_docker_image
if: ${{ steps.changed-files.outputs.any_changed == 'true' }}
if: steps.changed-files.outputs.any_changed == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }}
@@ -99,7 +99,7 @@ jobs:
files: "docker/compilers/gcc/**"
- uses: ./.github/actions/build_docker_image
if: ${{ steps.changed-files.outputs.any_changed == 'true' }}
if: steps.changed-files.outputs.any_changed == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }}
@@ -140,7 +140,7 @@ jobs:
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
if: ${{ github.event_name != 'pull_request' }}
if: github.event_name != 'pull_request'
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
registry: ghcr.io
@@ -148,14 +148,14 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to DockerHub
if: ${{ github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' }}
if: github.repository_owner == 'XRPLF' && github.event_name != 'pull_request'
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_PW }}
- name: Create and push multi-arch manifest
if: ${{ github.event_name != 'pull_request' && steps.changed-files.outputs.any_changed == 'true' }}
if: github.event_name != 'pull_request' && steps.changed-files.outputs.any_changed == 'true'
run: |
push_image() {
image=$1
@@ -188,7 +188,7 @@ jobs:
files: "docker/compilers/clang/**"
- uses: ./.github/actions/build_docker_image
if: ${{ steps.changed-files.outputs.any_changed == 'true' }}
if: steps.changed-files.outputs.any_changed == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }}
@@ -224,7 +224,7 @@ jobs:
files: "docker/tools/**"
- uses: ./.github/actions/build_docker_image
if: ${{ steps.changed-files.outputs.any_changed == 'true' }}
if: steps.changed-files.outputs.any_changed == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@@ -255,7 +255,7 @@ jobs:
files: "docker/tools/**"
- uses: ./.github/actions/build_docker_image
if: ${{ steps.changed-files.outputs.any_changed == 'true' }}
if: steps.changed-files.outputs.any_changed == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@@ -289,7 +289,7 @@ jobs:
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
if: ${{ github.event_name != 'pull_request' }}
if: github.event_name != 'pull_request'
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
registry: ghcr.io
@@ -297,7 +297,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push multi-arch manifest
if: ${{ github.event_name != 'pull_request' && steps.changed-files.outputs.any_changed == 'true' }}
if: github.event_name != 'pull_request' && steps.changed-files.outputs.any_changed == 'true'
run: |
image=${{ needs.repo.outputs.GHCR_REPO }}/clio-tools
docker buildx imagetools create \

View File

@@ -77,7 +77,7 @@ jobs:
disable_ccache: true
- name: Setup conan on macOS
if: ${{ runner.os == 'macOS' }}
if: runner.os == 'macOS'
shell: bash
run: ./.github/scripts/conan/init.sh
@@ -94,9 +94,9 @@ jobs:
build_type: ${{ matrix.build_type }}
- name: Login to Conan
if: ${{ github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' }}
if: github.repository_owner == 'XRPLF' && github.event_name != 'pull_request'
run: conan remote login -p ${{ secrets.CONAN_PASSWORD }} xrplf ${{ secrets.CONAN_USERNAME }}
- name: Upload Conan packages
if: ${{ github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' && github.event_name != 'schedule' }}
if: github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' && github.event_name != 'schedule'
run: conan upload "*" -r=xrplf --confirm ${{ github.event.inputs.force_upload == 'true' && '--force' || '' }}

View File

@@ -37,13 +37,13 @@ repos:
exclude: LICENSE.md
- repo: https://github.com/hadolint/hadolint
rev: 4e697ba704fd23b2409b947a319c19c3ee54d24f # frozen: v2.14.0
rev: c3dc18df7a501f02a560a2cc7ba3c69a85ca01d3 # frozen: v2.13.1-beta
hooks:
- id: hadolint-docker
# hadolint-docker is a special hook that runs hadolint in a Docker container
# Docker is not installed in the environment where pre-commit is run
stages: [manual]
entry: hadolint/hadolint:v2.14 hadolint
entry: hadolint/hadolint:v2.12.1-beta hadolint
- repo: https://github.com/codespell-project/codespell
rev: 63c8f8312b7559622c0d82815639671ae42132ac # frozen: v2.4.1
@@ -80,7 +80,7 @@ repos:
language: script
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: 719856d56a62953b8d2839fb9e851f25c3cfeef8 # frozen: v21.1.2
rev: 86fdcc9bd34d6afbbd29358b97436c8ffe3aa3b2 # frozen: v21.1.0
hooks:
- id: clang-format
args: [--style=file]

View File

@@ -3,7 +3,7 @@
"requires": [
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1756234269.497",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1756234289.683",
"xrpl/2.6.1#973af2bf9631f239941dd9f5a100bb84%1759275059.342",
"xrpl/2.6.0#57b93b5a6c99dc8511fccb3bb5390352%1756820296.642",
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1756234266.869",
"spdlog/1.15.3#3ca0e9e6b83af4d0151e26541d140c86%1754401846.61",
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1756234262.318",
@@ -41,12 +41,15 @@
"overrides": {
"boost/1.83.0": [
null,
"boost/1.83.0#5d975011d65b51abb2d2f6eb8386b368"
"boost/1.83.0"
],
"protobuf/3.21.12": [
null,
"protobuf/3.21.12"
],
"boost/1.86.0": [
"boost/1.83.0#5d975011d65b51abb2d2f6eb8386b368"
],
"lz4/1.9.4": [
"lz4/1.10.0"
],
@@ -55,4 +58,4 @@
]
},
"config_requires": []
}
}

View File

@@ -18,7 +18,7 @@ class ClioConan(ConanFile):
'protobuf/3.21.12',
'grpc/1.50.1',
'openssl/1.1.1w',
'xrpl/2.6.1',
'xrpl/2.6.0',
'zlib/1.3.1',
'libbacktrace/cci.20210118',
'spdlog/1.15.3',

View File

@@ -3,8 +3,6 @@ PROJECT_LOGO = ${SOURCE}/docs/img/xrpl-logo.svg
PROJECT_NUMBER = ${DOC_CLIO_VERSION}
PROJECT_BRIEF = The XRP Ledger API server.
DOT_GRAPH_MAX_NODES = 100
EXTRACT_ALL = NO
EXTRACT_PRIVATE = NO
EXTRACT_PACKAGE = YES
@@ -34,18 +32,12 @@ SORT_MEMBERS_CTORS_1ST = YES
GENERATE_TREEVIEW = YES
DISABLE_INDEX = NO
FULL_SIDEBAR = NO
HTML_HEADER = ${SOURCE}/docs/doxygen-awesome-theme/doxygen-custom/header.html
HTML_HEADER = ${SOURCE}/docs/doxygen-awesome-theme/header.html
HTML_EXTRA_STYLESHEET = ${SOURCE}/docs/doxygen-awesome-theme/doxygen-awesome.css \
${SOURCE}/docs/doxygen-awesome-theme/doxygen-awesome-sidebar-only.css \
${SOURCE}/docs/doxygen-awesome-theme/doxygen-awesome-sidebar-only-darkmode-toggle.css \
${SOURCE}/docs/doxygen-awesome-theme/doxygen-custom/custom.css \
${SOURCE}/docs/doxygen-awesome-theme/doxygen-custom/theme-robot.css \
${SOURCE}/docs/doxygen-awesome-theme/doxygen-custom/theme-round.css \
${SOURCE}/docs/github-corner-disable.css
${SOURCE}/docs/doxygen-awesome-theme/doxygen-awesome-sidebar-only-darkmode-toggle.css
HTML_EXTRA_FILES = ${SOURCE}/docs/doxygen-awesome-theme/doxygen-awesome-darkmode-toggle.js \
${SOURCE}/docs/doxygen-awesome-theme/doxygen-awesome-fragment-copy-button.js \
${SOURCE}/docs/doxygen-awesome-theme/doxygen-awesome-interactive-toc.js \
${SOURCE}/docs/doxygen-awesome-theme/doxygen-custom/toggle-alternative-theme.js
${SOURCE}/docs/doxygen-awesome-theme/doxygen-awesome-interactive-toc.js
HTML_COLORSTYLE = LIGHT
HTML_COLORSTYLE_HUE = 209

View File

@@ -1,10 +1,29 @@
// SPDX-License-Identifier: MIT
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
Copyright (c) 2021 - 2025 jothepro
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

View File

@@ -1,66 +0,0 @@
// SPDX-License-Identifier: MIT
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
Copyright (c) 2022 - 2025 jothepro
*/
class DoxygenAwesomeFragmentCopyButton extends HTMLElement {
constructor() {
super();
this.onclick=this.copyContent
}
static title = "Copy to clipboard"
static copyIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M23.04,10.322c0,-2.582 -2.096,-4.678 -4.678,-4.678l-6.918,-0c-2.582,-0 -4.678,2.096 -4.678,4.678c0,-0 0,8.04 0,8.04c0,2.582 2.096,4.678 4.678,4.678c0,-0 6.918,-0 6.918,-0c2.582,-0 4.678,-2.096 4.678,-4.678c0,-0 0,-8.04 0,-8.04Zm-2.438,-0l-0,8.04c-0,1.236 -1.004,2.24 -2.24,2.24l-6.918,-0c-1.236,-0 -2.239,-1.004 -2.239,-2.24l-0,-8.04c-0,-1.236 1.003,-2.24 2.239,-2.24c0,0 6.918,0 6.918,0c1.236,0 2.24,1.004 2.24,2.24Z"/><path d="M5.327,16.748c-0,0.358 -0.291,0.648 -0.649,0.648c0,0 0,0 0,0c-2.582,0 -4.678,-2.096 -4.678,-4.678c0,0 0,-8.04 0,-8.04c0,-2.582 2.096,-4.678 4.678,-4.678l6.918,0c2.168,0 3.994,1.478 4.523,3.481c0.038,0.149 0.005,0.306 -0.09,0.428c-0.094,0.121 -0.239,0.191 -0.392,0.191c-0.451,0.005 -1.057,0.005 -1.457,0.005c-0.238,0 -0.455,-0.14 -0.553,-0.357c-0.348,-0.773 -1.128,-1.31 -2.031,-1.31c-0,0 -6.918,0 -6.918,0c-1.236,0 -2.24,1.004 -2.24,2.24l0,8.04c0,1.236 1.004,2.24 2.24,2.24l0,-0c0.358,-0 0.649,0.29 0.649,0.648c-0,0.353 -0,0.789 -0,1.142Z" style="fill-opacity:0.6;"/></svg>`
static successIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M8.084,16.111c-0.09,0.09 -0.212,0.141 -0.34,0.141c-0.127,-0 -0.249,-0.051 -0.339,-0.141c-0.746,-0.746 -2.538,-2.538 -3.525,-3.525c-0.375,-0.375 -0.983,-0.375 -1.357,0c-0.178,0.178 -0.369,0.369 -0.547,0.547c-0.375,0.375 -0.375,0.982 -0,1.357c1.135,1.135 3.422,3.422 4.75,4.751c0.27,0.27 0.637,0.421 1.018,0.421c0.382,0 0.749,-0.151 1.019,-0.421c2.731,-2.732 10.166,-10.167 12.454,-12.455c0.375,-0.375 0.375,-0.982 -0,-1.357c-0.178,-0.178 -0.369,-0.369 -0.547,-0.547c-0.375,-0.375 -0.982,-0.375 -1.357,0c-2.273,2.273 -9.567,9.567 -11.229,11.229Z"/></svg>`
static successDuration = 980
static init() {
$(function() {
$(document).ready(function() {
if(navigator.clipboard) {
const fragments = document.getElementsByClassName("fragment")
for(const fragment of fragments) {
const fragmentWrapper = document.createElement("div")
fragmentWrapper.className = "doxygen-awesome-fragment-wrapper"
const fragmentCopyButton = document.createElement("doxygen-awesome-fragment-copy-button")
fragmentCopyButton.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
fragmentCopyButton.title = DoxygenAwesomeFragmentCopyButton.title
fragment.parentNode.replaceChild(fragmentWrapper, fragment)
fragmentWrapper.appendChild(fragment)
fragmentWrapper.appendChild(fragmentCopyButton)
}
}
})
})
}
copyContent() {
const content = this.previousSibling.cloneNode(true)
// filter out line number from file listings
content.querySelectorAll(".lineno, .ttc").forEach((node) => {
node.remove()
})
let textContent = content.textContent
// remove trailing newlines that appear in file listings
let numberOfTrailingNewlines = 0
while(textContent.charAt(textContent.length - (numberOfTrailingNewlines + 1)) == '\n') {
numberOfTrailingNewlines++;
}
textContent = textContent.substring(0, textContent.length - numberOfTrailingNewlines)
navigator.clipboard.writeText(textContent);
this.classList.add("success")
this.innerHTML = DoxygenAwesomeFragmentCopyButton.successIcon
window.setTimeout(() => {
this.classList.remove("success")
this.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
}, DoxygenAwesomeFragmentCopyButton.successDuration);
}
}
customElements.define("doxygen-awesome-fragment-copy-button", DoxygenAwesomeFragmentCopyButton)

View File

@@ -1,10 +1,29 @@
// SPDX-License-Identifier: MIT
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
Copyright (c) 2022 - 2025 jothepro
MIT License
Copyright (c) 2022 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
@@ -36,7 +55,9 @@ class DoxygenAwesomeInteractiveToc {
headerNode: document.getElementById(id)
})
document.getElementById("doc-content")?.addEventListener("scroll",this.throttle(DoxygenAwesomeInteractiveToc.update, 100))
document.getElementById("doc-content")?.addEventListener("scroll", () => {
DoxygenAwesomeInteractiveToc.update()
})
})
DoxygenAwesomeInteractiveToc.update()
}
@@ -57,16 +78,4 @@ class DoxygenAwesomeInteractiveToc {
active?.classList.add("active")
active?.classList.remove("aboveActive")
}
static throttle(func, delay) {
let lastCall = 0;
return function (...args) {
const now = new Date().getTime();
if (now - lastCall < delay) {
return;
}
lastCall = now;
return setTimeout(() => {func(...args)}, delay);
};
}
}
}

View File

@@ -1,10 +1,30 @@
/* SPDX-License-Identifier: MIT */
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
Copyright (c) 2021 - 2025 jothepro
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

View File

@@ -1,10 +1,29 @@
/* SPDX-License-Identifier: MIT */
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
Copyright (c) 2021 - 2025 jothepro
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
@@ -41,6 +60,10 @@ html {
height: calc(100vh - var(--top-height)) !important;
}
#nav-tree {
padding: 0;
}
#top {
display: block;
border-bottom: none;
@@ -50,24 +73,22 @@ html {
overflow: hidden;
background: var(--side-nav-background);
}
#main-nav {
float: left;
padding-right: 0;
}
.ui-resizable-handle {
display: none;
}
.ui-resizable-e {
width: 0;
cursor: default;
width: 1px !important;
background: var(--separator-color);
box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color);
}
#nav-path {
position: fixed;
right: 0;
left: calc(var(--side-nav-fixed-width) + 1px);
left: var(--side-nav-fixed-width);
bottom: 0;
width: auto;
}
@@ -92,14 +113,4 @@ html {
left: var(--spacing-medium) !important;
right: auto;
}
#nav-sync {
bottom: 4px;
right: auto;
left: 300px;
width: 35px;
top: auto !important;
user-select: none;
position: fixed
}
}

View File

@@ -1,10 +1,29 @@
/* SPDX-License-Identifier: MIT */
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
Copyright (c) 2021 - 2025 jothepro
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
@@ -13,9 +32,6 @@ html {
--primary-color: #1779c4;
--primary-dark-color: #335c80;
--primary-light-color: #70b1e9;
--on-primary-color: #ffffff;
--link-color: var(--primary-color);
/* page base colors */
--page-background-color: #ffffff;
@@ -26,15 +42,14 @@ html {
--separator-color: #dedede;
/* border radius for all rounded components. Will affect many components, like dropdowns, memitems, codeblocks, ... */
--border-radius-large: 10px;
--border-radius-small: 5px;
--border-radius-medium: 8px;
--border-radius-large: 8px;
--border-radius-small: 4px;
--border-radius-medium: 6px;
/* default spacings. Most components reference these values for spacing, to provide uniform spacing on the page. */
--spacing-small: 5px;
--spacing-medium: 10px;
--spacing-large: 16px;
--spacing-xlarge: 20px;
/* default box shadow used for raising an element above the normal content. Used in dropdowns, search result, ... */
--box-shadow: 0 2px 8px 0 rgba(0,0,0,.075);
@@ -98,7 +113,7 @@ html {
*/
--menu-display: block;
--menu-focus-foreground: var(--on-primary-color);
--menu-focus-foreground: var(--page-background-color);
--menu-focus-background: var(--primary-color);
--menu-selected-background: rgba(0,0,0,.05);
@@ -295,11 +310,10 @@ body {
font-size: var(--page-font-size);
}
body, table, div, p, dl, #nav-tree .label, #nav-tree a, .title,
body, table, div, p, dl, #nav-tree .label, .title,
.sm-dox a, .sm-dox a:hover, .sm-dox a:focus, #projectname,
.SelectItem, #MSearchField, .navpath li.navelem a,
.navpath li.navelem a:hover, p.reference, p.definition, div.toc li, div.toc h3,
#page-nav ul.page-outline li a {
.navpath li.navelem a:hover, p.reference, p.definition, div.toc li, div.toc h3 {
font-family: var(--font-family);
}
@@ -318,13 +332,8 @@ p.reference, p.definition {
}
a:link, a:visited, a:hover, a:focus, a:active {
color: var(--link-color) !important;
color: var(--primary-color) !important;
font-weight: 500;
background: none;
}
a:hover {
text-decoration: underline;
}
a.anchor {
@@ -339,8 +348,6 @@ a.anchor {
#top {
background: var(--header-background);
border-bottom: 1px solid var(--separator-color);
position: relative;
z-index: 99;
}
@media screen and (min-width: 768px) {
@@ -355,7 +362,6 @@ a.anchor {
#main-nav {
flex-grow: 5;
padding: var(--spacing-small) var(--spacing-medium);
border-bottom: 0;
}
#titlearea {
@@ -435,36 +441,19 @@ a.anchor {
}
.sm-dox a span.sub-arrow {
top: 15px;
right: 10px;
box-sizing: content-box;
padding: 0;
margin: 0;
display: inline-block;
width: 5px;
height: 5px;
transform: rotate(45deg);
border-width: 0;
border-right: 2px solid var(--header-foreground);
border-bottom: 2px solid var(--header-foreground);
background: none;
border-color: var(--header-foreground) transparent transparent transparent;
}
.sm-dox a:hover span.sub-arrow {
border-color: var(--menu-focus-foreground);
background: none;
border-color: var(--menu-focus-foreground) transparent transparent transparent;
}
.sm-dox ul a span.sub-arrow {
transform: rotate(-45deg);
border-width: 0;
border-right: 2px solid var(--header-foreground);
border-bottom: 2px solid var(--header-foreground);
border-color: transparent transparent transparent var(--page-foreground-color);
}
.sm-dox ul a:hover span.sub-arrow {
border-color: var(--menu-focus-foreground);
background: none;
border-color: transparent transparent transparent var(--menu-focus-foreground);
}
}
@@ -491,7 +480,7 @@ a.anchor {
.sm-dox ul a {
color: var(--page-foreground-color) !important;
background: none;
background: var(--page-background-color);
font-size: var(--navigation-font-size);
}
@@ -563,13 +552,6 @@ a.anchor {
box-shadow: none;
display: block;
margin-top: 0;
margin-right: 0;
}
@media (min-width: 768px) {
.sm-dox li {
padding: 0;
}
}
/* until Doxygen 1.9.4 */
@@ -591,17 +573,6 @@ a.anchor {
padding-left: 0
}
/* Doxygen 1.14.0 */
.search-icon::before {
background: none;
top: 5px;
}
.search-icon::after {
background: none;
top: 12px;
}
.SelectionMark {
user-select: none;
}
@@ -805,15 +776,12 @@ html.dark-mode iframe#MSearchResults {
*/
#side-nav {
padding: 0 !important;
background: var(--side-nav-background);
min-width: 8px;
max-width: 50vw;
}
#nav-tree, #top {
border-right: 1px solid var(--separator-color);
}
@media screen and (max-width: 767px) {
#side-nav {
display: none;
@@ -822,95 +790,34 @@ html.dark-mode iframe#MSearchResults {
#doc-content {
margin-left: 0 !important;
}
#top {
border-right: none;
}
}
#nav-tree {
background: var(--side-nav-background);
margin-right: -1px;
padding: 0;
background: transparent;
margin-right: 1px;
}
#nav-tree .label {
font-size: var(--navigation-font-size);
line-height: var(--tree-item-height);
}
#nav-tree span.label a:hover {
background: none;
}
#nav-tree .item {
height: var(--tree-item-height);
line-height: var(--tree-item-height);
overflow: hidden;
text-overflow: ellipsis;
margin: 0;
padding: 0;
}
#nav-tree-contents {
margin: 0;
}
#main-menu > li:last-child {
height: auto;
}
#nav-tree .item > a:focus {
outline: none;
}
#nav-sync {
bottom: var(--spacing-medium);
right: var(--spacing-medium) !important;
bottom: 12px;
right: 12px;
top: auto !important;
user-select: none;
}
div.nav-sync-icon {
border: 1px solid var(--separator-color);
border-radius: var(--border-radius-medium);
background: var(--page-background-color);
width: 30px;
height: 20px;
}
div.nav-sync-icon:hover {
background: var(--page-background-color);
}
span.sync-icon-left, div.nav-sync-icon:hover span.sync-icon-left {
border-left: 2px solid var(--primary-color);
border-top: 2px solid var(--primary-color);
top: 5px;
left: 6px;
}
span.sync-icon-right, div.nav-sync-icon:hover span.sync-icon-right {
border-right: 2px solid var(--primary-color);
border-bottom: 2px solid var(--primary-color);
top: 5px;
left: initial;
right: 6px;
}
div.nav-sync-icon.active::after, div.nav-sync-icon.active:hover::after {
border-top: 2px solid var(--primary-color);
top: 9px;
left: 6px;
width: 19px;
}
#nav-tree .selected {
text-shadow: none;
background-image: none;
background-color: transparent;
position: relative;
color: var(--primary-color) !important;
font-weight: 500;
}
#nav-tree .selected::after {
@@ -936,27 +843,9 @@ div.nav-sync-icon.active::after, div.nav-sync-icon.active:hover::after {
#nav-tree .arrow {
opacity: var(--side-nav-arrow-opacity);
background: none;
}
#nav-tree span.arrowhead {
margin: 0 0 1px 2px;
}
span.arrowhead {
border-color: var(--primary-light-color);
}
.selected span.arrowhead {
border-color: var(--primary-color);
}
#nav-tree ul li:first-child > div > a {
opacity: 0;
pointer-events: none;
}
.contents .arrow {
.arrow {
color: inherit;
cursor: pointer;
font-size: 45%;
@@ -964,7 +853,7 @@ span.arrowhead {
margin-right: 2px;
font-family: serif;
height: auto;
padding-bottom: 4px;
text-align: right;
}
#nav-tree div.item:hover .arrow, #nav-tree a:focus .arrow {
@@ -978,11 +867,9 @@ span.arrowhead {
}
.ui-resizable-e {
background: none;
}
.ui-resizable-e:hover {
background: var(--separator-color);
width: 4px;
background: transparent;
box-shadow: inset -1px 0 0 0 var(--separator-color);
}
/*
@@ -991,7 +878,7 @@ span.arrowhead {
div.header {
border-bottom: 1px solid var(--separator-color);
background: none;
background-color: var(--page-background-color);
background-image: none;
}
@@ -1030,7 +917,7 @@ div.headertitle {
div.header .title {
font-weight: 600;
font-size: 225%;
padding: var(--spacing-medium) var(--spacing-xlarge);
padding: var(--spacing-medium) var(--spacing-large);
word-break: break-word;
}
@@ -1047,10 +934,9 @@ td.memSeparator {
span.mlabel {
background: var(--primary-color);
color: var(--on-primary-color);
border: none;
padding: 4px 9px;
border-radius: var(--border-radius-large);
border-radius: 12px;
margin-right: var(--spacing-medium);
}
@@ -1059,7 +945,7 @@ span.mlabel:last-of-type {
}
div.contents {
padding: 0 var(--spacing-xlarge);
padding: 0 var(--spacing-large);
}
div.contents p, div.contents li {
@@ -1070,16 +956,6 @@ div.contents div.dyncontent {
margin: var(--spacing-medium) 0;
}
@media screen and (max-width: 767px) {
div.contents {
padding: 0 var(--spacing-large);
}
div.header .title {
padding: var(--spacing-medium) var(--spacing-large);
}
}
@media (prefers-color-scheme: dark) {
html:not(.light-mode) div.contents div.dyncontent img,
html:not(.light-mode) div.contents center img,
@@ -1103,7 +979,7 @@ html.dark-mode div.contents .dotgraph iframe
filter: brightness(89%) hue-rotate(180deg) invert();
}
td h2.groupheader, h2.groupheader {
h2.groupheader {
border-bottom: 0px;
color: var(--page-foreground-color);
box-shadow:
@@ -1164,7 +1040,7 @@ blockquote::after {
blockquote p {
margin: var(--spacing-small) 0 var(--spacing-medium) 0;
}
.paramname, .paramname em {
.paramname {
font-weight: 600;
color: var(--primary-dark-color);
}
@@ -1214,7 +1090,7 @@ div.contents .toc {
border: 0;
border-left: 1px solid var(--separator-color);
border-radius: 0;
background-color: var(--page-background-color);
background-color: transparent;
box-shadow: none;
position: sticky;
top: var(--toc-sticky-top);
@@ -1322,115 +1198,24 @@ div.toc li a.aboveActive {
}
}
/*
Page Outline (Doxygen >= 1.14.0)
*/
#page-nav {
background: var(--page-background-color);
border-left: 1px solid var(--separator-color);
}
#page-nav #page-nav-resize-handle {
background: var(--separator-color);
}
#page-nav #page-nav-resize-handle::after {
border-left: 1px solid var(--primary-color);
border-right: 1px solid var(--primary-color);
}
#page-nav #page-nav-tree #page-nav-contents {
top: var(--spacing-large);
}
#page-nav ul.page-outline {
margin: 0;
padding: 0;
}
#page-nav ul.page-outline li a {
font-size: var(--toc-font-size) !important;
color: var(--page-secondary-foreground-color) !important;
display: inline-block;
line-height: calc(2 * var(--toc-font-size));
}
#page-nav ul.page-outline li a a.anchorlink {
display: none;
}
#page-nav ul.page-outline li.vis ~ * a {
color: var(--page-foreground-color) !important;
}
#page-nav ul.page-outline li.vis:not(.vis ~ .vis) a, #page-nav ul.page-outline li a:hover {
color: var(--primary-color) !important;
}
#page-nav ul.page-outline .vis {
background: var(--page-background-color);
position: relative;
}
#page-nav ul.page-outline .vis::after {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 4px;
background: var(--page-secondary-foreground-color);
}
#page-nav ul.page-outline .vis:not(.vis ~ .vis)::after {
top: 1px;
border-top-right-radius: var(--border-radius-small);
}
#page-nav ul.page-outline .vis:not(:has(~ .vis))::after {
bottom: 1px;
border-bottom-right-radius: var(--border-radius-small);
}
#page-nav ul.page-outline .arrow {
display: inline-block;
}
#page-nav ul.page-outline .arrow span {
display: none;
}
@media screen and (max-width: 767px) {
#container {
grid-template-columns: initial !important;
}
#page-nav {
display: none;
}
}
/*
Code & Fragments
*/
code, div.fragment, pre.fragment, span.tt {
code, div.fragment, pre.fragment {
border-radius: var(--border-radius-small);
border: 1px solid var(--separator-color);
overflow: hidden;
}
code, span.tt {
code {
display: inline;
background: var(--code-background);
color: var(--code-foreground);
padding: 2px 6px;
border-radius: var(--border-radius-small);
}
div.fragment, pre.fragment {
border-radius: var(--border-radius-medium);
margin: var(--spacing-medium) 0;
padding: calc(var(--spacing-large) - (var(--spacing-large) / 6)) var(--spacing-large);
background: var(--fragment-background);
@@ -1488,7 +1273,7 @@ div.fragment, pre.fragment {
}
}
code, code a, pre.fragment, div.fragment, div.fragment .line, div.fragment span, div.fragment .line a, div.fragment .line span, span.tt {
code, code a, pre.fragment, div.fragment, div.fragment .line, div.fragment span, div.fragment .line a, div.fragment .line span {
font-family: var(--font-family-monospace);
font-size: var(--code-font-size) !important;
}
@@ -1562,10 +1347,6 @@ div.line.glow {
dl warning, attention, note, deprecated, bug, ...
*/
dl {
line-height: calc(1.65 * var(--page-font-size));
}
dl.bug dt a, dl.deprecated dt a, dl.todo dt a {
font-weight: bold !important;
}
@@ -1731,7 +1512,6 @@ div.memitem {
border-top-right-radius: var(--border-radius-medium);
border-bottom-right-radius: var(--border-radius-medium);
border-bottom-left-radius: var(--border-radius-medium);
border-top-left-radius: 0;
overflow: hidden;
display: block !important;
}
@@ -1963,7 +1743,7 @@ table.fieldtable th {
color: var(--tablehead-foreground);
}
table.fieldtable td.fieldtype, .fieldtable td.fieldname, .fieldtable td.fieldinit, .fieldtable td.fielddoc, .fieldtable th {
table.fieldtable td.fieldtype, .fieldtable td.fieldname, .fieldtable td.fielddoc, .fieldtable th {
border-bottom: 1px solid var(--separator-color);
border-right: 1px solid var(--separator-color);
}
@@ -1998,10 +1778,8 @@ table.memberdecls tr[class^='memitem'] .memTemplParams {
white-space: normal;
}
table.memberdecls tr.heading + tr[class^='memitem'] td.memItemLeft,
table.memberdecls tr.heading + tr[class^='memitem'] td.memItemRight,
table.memberdecls td.memItemLeft,
table.memberdecls td.memItemRight,
table.memberdecls .memItemLeft,
table.memberdecls .memItemRight,
table.memberdecls .memTemplItemLeft,
table.memberdecls .memTemplItemRight,
table.memberdecls .memTemplParams {
@@ -2013,34 +1791,8 @@ table.memberdecls .memTemplParams {
background-color: var(--fragment-background);
}
@media screen and (min-width: 768px) {
tr.heading + tr[class^='memitem'] td.memItemRight, tr.groupHeader + tr[class^='memitem'] td.memItemRight, tr.inherit_header + tr[class^='memitem'] td.memItemRight {
border-top-right-radius: var(--border-radius-small);
}
table.memberdecls tr:last-child td.memItemRight, table.memberdecls tr:last-child td.mdescRight, table.memberdecls tr[class^='memitem']:has(+ tr.groupHeader) td.memItemRight, table.memberdecls tr[class^='memitem']:has(+ tr.inherit_header) td.memItemRight, table.memberdecls tr[class^='memdesc']:has(+ tr.groupHeader) td.mdescRight, table.memberdecls tr[class^='memdesc']:has(+ tr.inherit_header) td.mdescRight {
border-bottom-right-radius: var(--border-radius-small);
}
table.memberdecls tr:last-child td.memItemLeft, table.memberdecls tr:last-child td.mdescLeft, table.memberdecls tr[class^='memitem']:has(+ tr.groupHeader) td.memItemLeft, table.memberdecls tr[class^='memitem']:has(+ tr.inherit_header) td.memItemLeft, table.memberdecls tr[class^='memdesc']:has(+ tr.groupHeader) td.mdescLeft, table.memberdecls tr[class^='memdesc']:has(+ tr.inherit_header) td.mdescLeft {
border-bottom-left-radius: var(--border-radius-small);
}
tr.heading + tr[class^='memitem'] td.memItemLeft, tr.groupHeader + tr[class^='memitem'] td.memItemLeft, tr.inherit_header + tr[class^='memitem'] td.memItemLeft {
border-top-left-radius: var(--border-radius-small);
}
}
table.memname td.memname {
font-size: var(--memname-font-size);
}
table.memberdecls .memTemplItemLeft,
table.memberdecls .template .memItemLeft,
table.memberdecls .memTemplItemRight,
table.memberdecls .template .memItemRight {
table.memberdecls .memTemplItemRight {
padding-top: 2px;
}
@@ -2052,13 +1804,13 @@ table.memberdecls .memTemplParams {
padding-bottom: var(--spacing-small);
}
table.memberdecls .memTemplItemLeft, table.memberdecls .template .memItemLeft {
table.memberdecls .memTemplItemLeft {
border-radius: 0 0 0 var(--border-radius-small);
border-left: 1px solid var(--separator-color);
border-top: 0;
}
table.memberdecls .memTemplItemRight, table.memberdecls .template .memItemRight {
table.memberdecls .memTemplItemRight {
border-radius: 0 0 var(--border-radius-small) 0;
border-right: 1px solid var(--separator-color);
padding-left: 0;
@@ -2084,14 +1836,8 @@ table.memberdecls .mdescLeft, table.memberdecls .mdescRight {
background: none;
color: var(--page-foreground-color);
padding: var(--spacing-small) 0;
border: 0;
}
table.memberdecls [class^="memdesc"] {
box-shadow: none;
}
table.memberdecls .memItemLeft,
table.memberdecls .memTemplItemLeft {
padding-right: var(--spacing-medium);
@@ -2114,10 +1860,6 @@ table.memberdecls .inherit_header td {
color: var(--page-secondary-foreground-color);
}
table.memberdecls span.dynarrow {
left: 10px;
}
table.memberdecls img[src="closed.png"],
table.memberdecls img[src="open.png"],
div.dynheader img[src="open.png"],
@@ -2134,10 +1876,6 @@ div.dynheader img[src="closed.png"] {
transition: transform var(--animation-duration) ease-out;
}
tr.heading + tr[class^='memitem'] td.memItemLeft, tr.groupHeader + tr[class^='memitem'] td.memItemLeft, tr.inherit_header + tr[class^='memitem'] td.memItemLeft, tr.heading + tr[class^='memitem'] td.memItemRight, tr.groupHeader + tr[class^='memitem'] td.memItemRight, tr.inherit_header + tr[class^='memitem'] td.memItemRight {
border-top: 1px solid var(--separator-color);
}
table.memberdecls img {
margin-right: 10px;
}
@@ -2162,10 +1900,7 @@ div.dynheader img[src="closed.png"] {
table.memberdecls .mdescRight,
table.memberdecls .memTemplItemLeft,
table.memberdecls .memTemplItemRight,
table.memberdecls .memTemplParams,
table.memberdecls .template .memItemLeft,
table.memberdecls .template .memItemRight,
table.memberdecls .template .memParams {
table.memberdecls .memTemplParams {
display: block;
text-align: left;
padding-left: var(--spacing-large);
@@ -2178,14 +1913,12 @@ div.dynheader img[src="closed.png"] {
table.memberdecls .memItemLeft,
table.memberdecls .mdescLeft,
table.memberdecls .memTemplItemLeft,
table.memberdecls .template .memItemLeft {
border-bottom: 0 !important;
padding-bottom: 0 !important;
table.memberdecls .memTemplItemLeft {
border-bottom: 0;
padding-bottom: 0;
}
table.memberdecls .memTemplItemLeft,
table.memberdecls .template .memItemLeft {
table.memberdecls .memTemplItemLeft {
padding-top: 0;
}
@@ -2195,12 +1928,10 @@ div.dynheader img[src="closed.png"] {
table.memberdecls .memItemRight,
table.memberdecls .mdescRight,
table.memberdecls .memTemplItemRight,
table.memberdecls .template .memItemRight {
border-top: 0 !important;
padding-top: 0 !important;
table.memberdecls .memTemplItemRight {
border-top: 0;
padding-top: 0;
padding-right: var(--spacing-large);
padding-bottom: var(--spacing-medium);
overflow-x: auto;
}
@@ -2235,22 +1966,6 @@ div.dynheader img[src="closed.png"] {
max-height: 200px;
}
}
tr.heading + tr[class^='memitem'] td.memItemRight, tr.groupHeader + tr[class^='memitem'] td.memItemRight, tr.inherit_header + tr[class^='memitem'] td.memItemRight {
border-top-right-radius: 0;
}
table.memberdecls tr:last-child td.memItemRight, table.memberdecls tr:last-child td.mdescRight, table.memberdecls tr[class^='memitem']:has(+ tr.groupHeader) td.memItemRight, table.memberdecls tr[class^='memitem']:has(+ tr.inherit_header) td.memItemRight, table.memberdecls tr[class^='memdesc']:has(+ tr.groupHeader) td.mdescRight, table.memberdecls tr[class^='memdesc']:has(+ tr.inherit_header) td.mdescRight {
border-bottom-right-radius: 0;
}
table.memberdecls tr:last-child td.memItemLeft, table.memberdecls tr:last-child td.mdescLeft, table.memberdecls tr[class^='memitem']:has(+ tr.groupHeader) td.memItemLeft, table.memberdecls tr[class^='memitem']:has(+ tr.inherit_header) td.memItemLeft, table.memberdecls tr[class^='memdesc']:has(+ tr.groupHeader) td.mdescLeft, table.memberdecls tr[class^='memdesc']:has(+ tr.inherit_header) td.mdescLeft {
border-bottom-left-radius: 0;
}
tr.heading + tr[class^='memitem'] td.memItemLeft, tr.groupHeader + tr[class^='memitem'] td.memItemLeft, tr.inherit_header + tr[class^='memitem'] td.memItemLeft {
border-top-left-radius: 0;
}
}
@@ -2267,16 +1982,14 @@ hr {
}
.contents hr {
box-shadow: 100px 0 var(--separator-color),
-100px 0 var(--separator-color),
500px 0 var(--separator-color),
-500px 0 var(--separator-color),
900px 0 var(--separator-color),
-900px 0 var(--separator-color),
1400px 0 var(--separator-color),
-1400px 0 var(--separator-color),
1900px 0 var(--separator-color),
-1900px 0 var(--separator-color);
box-shadow: 100px 0 0 var(--separator-color),
-100px 0 0 var(--separator-color),
500px 0 0 var(--separator-color),
-500px 0 0 var(--separator-color),
1500px 0 0 var(--separator-color),
-1500px 0 0 var(--separator-color),
2000px 0 0 var(--separator-color),
-2000px 0 0 var(--separator-color);
}
.contents img, .contents .center, .contents center, .contents div.image object {
@@ -2439,7 +2152,9 @@ div.qindex {
background: var(--page-background-color);
border: none;
border-top: 1px solid var(--separator-color);
border-bottom: 1px solid var(--separator-color);
border-bottom: 0;
box-shadow: 0 0.75px 0 var(--separator-color);
font-size: var(--navigation-font-size);
}
@@ -2468,10 +2183,6 @@ address.footer {
color: var(--primary-color) !important;
}
.navpath li.navelem a:hover {
text-shadow: none;
}
.navpath li.navelem b {
color: var(--primary-dark-color);
font-weight: 500;
@@ -2490,11 +2201,7 @@ li.navelem:first-child:before {
display: none;
}
#nav-path ul {
padding-left: 0;
}
#nav-path li.navelem:has(.el):after {
#nav-path li.navelem:after {
content: '';
border: 5px solid var(--page-background-color);
border-bottom-color: transparent;
@@ -2505,21 +2212,7 @@ li.navelem:first-child:before {
margin-left: 6px;
}
#nav-path li.navelem:not(:has(.el)):after {
background: var(--page-background-color);
box-shadow: 1px -1px 0 1px var(--separator-color);
border-radius: 0 var(--border-radius-medium) 0 50px;
}
#nav-path li.navelem:not(:has(.el)) {
margin-left: 0;
}
#nav-path li.navelem:not(:has(.el)):hover, #nav-path li.navelem:not(:has(.el)):hover:after {
background-color: var(--separator-color);
}
#nav-path li.navelem:has(.el):before {
#nav-path li.navelem:before {
content: '';
border: 5px solid var(--separator-color);
border-bottom-color: transparent;
@@ -2645,7 +2338,7 @@ doxygen-awesome-dark-mode-toggle {
height: var(--searchbar-height);
background: none;
border: none;
border-radius: var(--searchbar-border-radius);
border-radius: var(--searchbar-height);
vertical-align: middle;
text-align: center;
line-height: var(--searchbar-height);
@@ -2830,7 +2523,6 @@ h2:hover a.anchorlink, h1:hover a.anchorlink, h3:hover a.anchorlink, h4:hover a.
float: left;
white-space: nowrap;
font-weight: normal;
font-family: var(--font-family);
padding: calc(var(--spacing-large) / 2) var(--spacing-large);
border-radius: var(--border-radius-medium);
transition: background-color var(--animation-duration) ease-in-out, font-weight var(--animation-duration) ease-in-out;
@@ -2975,46 +2667,3 @@ h2:hover a.anchorlink, h1:hover a.anchorlink, h3:hover a.anchorlink, h4:hover a.
border-radius: 0 var(--border-radius-medium) var(--border-radius-medium) 0;
}
}
/*
Bordered image
*/
html.dark-mode .darkmode_inverted_image img, /* < doxygen 1.9.3 */
html.dark-mode .darkmode_inverted_image object[type="image/svg+xml"] /* doxygen 1.9.3 */ {
filter: brightness(89%) hue-rotate(180deg) invert();
}
.bordered_image {
border-radius: var(--border-radius-small);
border: 1px solid var(--separator-color);
display: inline-block;
overflow: hidden;
}
.bordered_image:empty {
border: none;
}
html.dark-mode .bordered_image img, /* < doxygen 1.9.3 */
html.dark-mode .bordered_image object[type="image/svg+xml"] /* doxygen 1.9.3 */ {
border-radius: var(--border-radius-small);
}
/*
Button
*/
.primary-button {
display: inline-block;
cursor: pointer;
background: var(--primary-color);
color: var(--page-background-color) !important;
border-radius: var(--border-radius-medium);
padding: var(--spacing-small) var(--spacing-medium);
text-decoration: none;
}
.primary-button:hover {
background: var(--primary-dark-color);
}

View File

@@ -1,129 +0,0 @@
.github-corner svg {
fill: var(--primary-light-color);
color: var(--page-background-color);
width: 72px;
height: 72px;
}
@media screen and (max-width: 767px) {
.github-corner svg {
width: 50px;
height: 50px;
}
#projectnumber {
margin-right: 22px;
}
}
.title_screenshot {
filter: drop-shadow(0px 3px 10px rgba(0,0,0,0.22));
max-width: 500px;
margin: var(--spacing-large) 0;
}
.title_screenshot .caption {
display: none;
}
#theme-selection {
position: fixed;
bottom: 0;
left: 0;
background: var(--side-nav-background);
padding: 5px 2px 5px 8px;
box-shadow: 0 -4px 4px -2px var(--side-nav-background);
display: flex;
}
#theme-selection label {
border: 1px solid var(--separator-color);
border-right: 0;
color: var(--page-foreground-color);
font-size: var(--toc-font-size);
padding: 0 8px;
display: inline-block;
height: 22px;
box-sizing: border-box;
border-radius: var(--border-radius-medium) 0 0 var(--border-radius-medium);
line-height: 20px;
background: var(--page-background-color);
opacity: 0.7;
}
@media (prefers-color-scheme: dark) {
html:not(.light-mode) #theme-select {
background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='%23aaaaaa'><polygon points='0,0 100,0 50,50'/></svg>") no-repeat;
background-size: 8px;
background-position: calc(100% - 6px) 65%;
background-color: var(--page-background-color);
}
}
html.dark-mode #theme-select {
background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='%23aaaaaa'><polygon points='0,0 100,0 50,50'/></svg>") no-repeat;
background-size: 8px;
background-position: calc(100% - 6px) 65%;
background-color: var(--page-background-color);
}
#theme-select {
border: 1px solid var(--separator-color);
border-radius: 0 var(--border-radius-medium) var(--border-radius-medium) 0;
padding: 0;
height: 22px;
font-size: var(--toc-font-size);
font-family: var(--font-family);
width: 215px;
color: var(--primary-color);
border-left: 0;
display: inline-block;
opacity: 0.7;
outline: none;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='%23888888'><polygon points='0,0 100,0 50,50'/></svg>") no-repeat;
background-size: 8px;
background-position: calc(100% - 6px) 65%;
background-repeat: no-repeat;
background-color: var(--page-background-color);
}
#theme-selection:hover #theme-select, #theme-selection:hover label {
opacity: 1;
}
#nav-tree-contents {
margin-bottom: 30px;
}
@media screen and (max-width: 767px) {
#theme-selection {
box-shadow: none;
background: none;
height: 20px;
}
#theme-select {
width: 80px;
opacity: 1;
}
#theme-selection label {
opacity: 1;
}
#nav-path ul li.navelem:first-child {
margin-left: 160px;
}
ul li.footer:not(:first-child) {
display: none;
}
#nav-path {
position: fixed;
bottom: 0;
background: var(--page-background-color);
}
}

View File

@@ -1,98 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<!-- BEGIN opengraph metadata -->
<meta property="og:title" content="Doxygen Awesome" />
<meta property="og:image" content="https://repository-images.githubusercontent.com/348492097/4f16df80-88fb-11eb-9d31-4015ff22c452" />
<meta property="og:description" content="Custom CSS theme for doxygen html-documentation with lots of customization parameters." />
<meta property="og:url" content="https://jothepro.github.io/doxygen-awesome-css/" />
<!-- END opengraph metadata -->
<!-- BEGIN twitter metadata -->
<meta name="twitter:image:src" content="https://repository-images.githubusercontent.com/348492097/4f16df80-88fb-11eb-9d31-4015ff22c452" />
<meta name="twitter:title" content="Doxygen Awesome" />
<meta name="twitter:description" content="Custom CSS theme for doxygen html-documentation with lots of customization parameters." />
<!-- END twitter metadata -->
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<link rel="icon" type="image/svg+xml" href="logo.drawio.svg"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-fragment-copy-button.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-paragraph-link.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-interactive-toc.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-tabs.js"></script>
<script type="text/javascript" src="$relpath^toggle-alternative-theme.js"></script>
<script type="text/javascript">
DoxygenAwesomeFragmentCopyButton.init()
DoxygenAwesomeDarkModeToggle.init()
DoxygenAwesomeParagraphLink.init()
DoxygenAwesomeInteractiveToc.init()
DoxygenAwesomeTabs.init()
</script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
</head>
<body>
<!-- https://tholman.com/github-corners/ -->
<a href="https://github.com/jothepro/doxygen-awesome-css" class="github-corner" title="View source on GitHub" target="_blank" rel="noopener noreferrer">
<svg viewBox="0 0 250 250" width="40" height="40" style="position: absolute; top: 0; border: 0; right: 0; z-index: 99;" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">$projectname
<!--BEGIN PROJECT_NUMBER-->&#160;<span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td style="padding-left: 0.5em;">
<div id="projectbrief">$projectbrief</div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<td>$searchbox</td>
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
</tbody>
</table>
<div id="theme-selection">
<label for="theme-select">Theme:</label>
<select id="theme-select">
<option value="theme-default">Default</option>
<option value="theme-round">Round</option>
<option value="theme-robot">Robot</option>
</select>
</div>
</div>
<!--END TITLEAREA-->
<!-- end header part -->

View File

@@ -1,62 +0,0 @@
html.theme-robot {
/* primary theme color. This will affect the entire websites color scheme: links, arrows, labels, ... */
--primary-color: #1c89a4;
--primary-dark-color: #1a6f84;
--primary-light-color: #5abcd4;
--primary-lighter-color: #cae1f1;
--primary-lightest-color: #e9f1f8;
--fragment-background: #ececec;
--code-background: #ececec;
/* page base colors */
--page-background-color: white;
--page-foreground-color: #2c3e50;
--page-secondary-foreground-color: #67727e;
--border-radius-large: 0px;
--border-radius-small: 0px;
--border-radius-medium: 0px;
--spacing-small: 3px;
--spacing-medium: 6px;
--spacing-large: 12px;
--top-height: 125px;
--side-nav-background: var(--page-background-color);
--side-nav-foreground: var(--page-foreground-color);
--header-foreground: var(--side-nav-foreground);
--searchbar-border-radius: var(--border-radius-medium);
--header-background: var(--side-nav-background);
--header-foreground: var(--side-nav-foreground);
--toc-background: rgb(243, 240, 252);
--toc-foreground: var(--page-foreground-color);
--font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
--page-font-size: 14px;
--box-shadow: none;
--separator-color: #cdcdcd;
}
html.theme-robot.dark-mode {
color-scheme: dark;
--primary-color: #49cad3;
--primary-dark-color: #8ed2d7;
--primary-light-color: #377479;
--primary-lighter-color: #191e21;
--primary-lightest-color: #191a1c;
--fragment-background: #000000;
--code-background: #000000;
--page-background-color: #161616;
--page-foreground-color: #d2dbde;
--page-secondary-foreground-color: #555555;
--separator-color: #545454;
--toc-background: #20142C;
}

View File

@@ -1,55 +0,0 @@
html.theme-round {
/* primary theme color. This will affect the entire websites color scheme: links, arrows, labels, ... */
--primary-color: #AF7FE4;
--primary-dark-color: #9270E4;
--primary-light-color: #d2b7ef;
--primary-lighter-color: #cae1f1;
--primary-lightest-color: #e9f1f8;
/* page base colors */
--page-background-color: white;
--page-foreground-color: #2c3e50;
--page-secondary-foreground-color: #67727e;
--border-radius-large: 22px;
--border-radius-small: 9px;
--border-radius-medium: 14px;
--spacing-small: 8px;
--spacing-medium: 14px;
--spacing-large: 19px;
--spacing-xlarge: 21px;
--top-height: 125px;
--side-nav-background: #324067;
--side-nav-foreground: #F1FDFF;
--header-foreground: var(--side-nav-foreground);
--searchbar-background: var(--side-nav-foreground);
--searchbar-border-radius: var(--border-radius-medium);
--header-background: var(--side-nav-background);
--header-foreground: var(--side-nav-foreground);
--toc-background: rgb(243, 240, 252);
--toc-foreground: var(--page-foreground-color);
}
html.theme-round.dark-mode {
color-scheme: dark;
--primary-color: #AF7FE4;
--primary-dark-color: #715292;
--primary-light-color: #ae97c7;
--primary-lighter-color: #191e21;
--primary-lightest-color: #191a1c;
--page-background-color: #1C1D1F;
--page-foreground-color: #d2dbde;
--page-secondary-foreground-color: #859399;
--separator-color: #3a3246;
--side-nav-background: #171D32;
--side-nav-foreground: #F1FDFF;
--toc-background: #20142C;
--searchbar-background: var(--page-background-color);
}

View File

@@ -1,52 +0,0 @@
// Toggle zwischen drei Theme-Zuständen und speichere im localStorage
const THEME_CLASSES = ['theme-default', 'theme-round', 'theme-robot'];
// Ermögliche das Umschalten per Button/Funktion (z.B. für onclick im HTML)
function toggleThemeVariant() {
let idx = getCurrentThemeIndex();
idx = (idx + 1) % THEME_CLASSES.length;
applyThemeClass(idx);
}
// Funktion global verfügbar machen
window.toggleThemeVariant = toggleThemeVariant;
function getCurrentThemeIndex() {
const stored = localStorage.getItem('theme-variant');
if (stored === null) return 0;
const idx = THEME_CLASSES.indexOf(stored);
return idx === -1 ? 0 : idx;
}
function applyThemeClass(idx) {
document.documentElement.classList.remove(...THEME_CLASSES);
if (THEME_CLASSES[idx] && THEME_CLASSES[idx] !== 'theme-default') {
document.documentElement.classList.add(THEME_CLASSES[idx]);
}
localStorage.setItem('theme-variant', THEME_CLASSES[idx] || 'theme-default');
// Select synchronisieren, falls vorhanden
const select = document.getElementById('theme-select');
if (select) select.value = THEME_CLASSES[idx];
}
function setThemeByName(themeName) {
const idx = THEME_CLASSES.indexOf(themeName);
applyThemeClass(idx === -1 ? 0 : idx);
}
document.addEventListener('DOMContentLoaded', () => {
const select = document.getElementById('theme-select');
if (select) {
// Initialisiere Auswahl aus localStorage
const idx = getCurrentThemeIndex();
select.value = THEME_CLASSES[idx];
applyThemeClass(idx);
// Theme bei Auswahl ändern
select.addEventListener('change', e => {
setThemeByName(e.target.value);
});
} else {
// Fallback: Theme trotzdem setzen
applyThemeClass(getCurrentThemeIndex());
}
});

View File

@@ -0,0 +1,82 @@
<!-- HTML header for doxygen 1.9.7-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="$langISO">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN FULL_SIDEBAR-->
<script type="text/javascript">var page_layout=1;</script>
<!--END FULL_SIDEBAR-->
<!--END DISABLE_INDEX-->
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
$darkmode
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
<script type="text/javascript">
DoxygenAwesomeDarkModeToggle.init()
</script>
<script type="text/javascript" src="$relpath^doxygen-awesome-interactive-toc.js"></script>
<script type="text/javascript">
DoxygenAwesomeInteractiveToc.init()
</script>
</head>
<body>
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN FULL_SIDEBAR-->
<div id="side-nav" class="ui-resizable side-nav-resizable"><!-- do not remove this div, it is closed by doxygen! -->
<!--END FULL_SIDEBAR-->
<!--END DISABLE_INDEX-->
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr id="projectrow">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td id="projectalign">
<div id="projectname">$projectname<!--BEGIN PROJECT_NUMBER--><span id="projectnumber">&#160;$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td>
<div id="projectbrief">$projectbrief</div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<!--BEGIN !FULL_SIDEBAR-->
<td>$searchbox</td>
<!--END !FULL_SIDEBAR-->
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
<!--BEGIN SEARCHENGINE-->
<!--BEGIN FULL_SIDEBAR-->
<tr><td colspan="2">$searchbox</td></tr>
<!--END FULL_SIDEBAR-->
<!--END SEARCHENGINE-->
</tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->

View File

@@ -1,3 +0,0 @@
.github-corner {
display: none !important;
}

View File

@@ -189,7 +189,6 @@ ClioApplication::run(bool const useNgWebServer)
httpServer->onGet("/metrics", MetricsHandler{adminVerifier});
httpServer->onGet("/health", HealthCheckHandler{});
httpServer->onGet("/cache_state", CacheStateHandler{cache});
auto requestHandler = RequestHandler{adminVerifier, handler};
httpServer->onPost("/", requestHandler);
httpServer->onWs(std::move(requestHandler));
@@ -215,7 +214,7 @@ ClioApplication::run(bool const useNgWebServer)
// Init the web server
auto handler = std::make_shared<web::RPCServerHandler<RPCEngineType>>(config_, backend, rpcEngine, etl, dosGuard);
auto const httpServer = web::makeHttpServer(config_, ioc, dosGuard, handler, cache);
auto const httpServer = web::makeHttpServer(config_, ioc, dosGuard, handler);
// Blocks until stopped.
// When stopped, shared_ptrs fall out of scope

View File

@@ -120,34 +120,4 @@ HealthCheckHandler::operator()(
return web::ng::Response{boost::beast::http::status::ok, kHEALTH_CHECK_HTML, request};
}
web::ng::Response
CacheStateHandler::operator()(
web::ng::Request const& request,
web::ng::ConnectionMetadata&,
web::SubscriptionContextPtr,
boost::asio::yield_context
)
{
static constexpr auto kCACHE_CHECK_LOADED_HTML = R"html(
<!DOCTYPE html>
<html>
<head><title>Cache state</title></head>
<body><h1>Cache state</h1><p>Cache is fully loaded</p></body>
</html>
)html";
static constexpr auto kCACHE_CHECK_NOT_LOADED_HTML = R"html(
<!DOCTYPE html>
<html>
<head><title>Cache state</title></head>
<body><h1>Cache state</h1><p>Cache is not yet loaded</p></body>
</html>
)html";
if (cache_.get().isFull())
return web::ng::Response{boost::beast::http::status::ok, kCACHE_CHECK_LOADED_HTML, request};
return web::ng::Response{boost::beast::http::status::service_unavailable, kCACHE_CHECK_NOT_LOADED_HTML, request};
}
} // namespace app

View File

@@ -19,7 +19,6 @@
#pragma once
#include "data/LedgerCacheInterface.hpp"
#include "rpc/Errors.hpp"
#include "util/log/Logger.hpp"
#include "web/AdminVerificationStrategy.hpp"
@@ -164,37 +163,6 @@ public:
);
};
/**
* @brief A function object that handles the cache state check endpoint.
*/
class CacheStateHandler {
std::reference_wrapper<data::LedgerCacheInterface const> cache_;
public:
/**
* @brief Construct a new CacheStateHandler object.
*
* @param cache The ledger cache to use.
*/
CacheStateHandler(data::LedgerCacheInterface const& cache) : cache_{cache}
{
}
/**
* @brief The call of the function object.
*
* @param request The request to handle.
* @return The response to the request
*/
web::ng::Response
operator()(
web::ng::Request const& request,
web::ng::ConnectionMetadata&,
web::SubscriptionContextPtr,
boost::asio::yield_context
);
};
/**
* @brief A function object that handles the websocket endpoint.
*

View File

@@ -21,7 +21,6 @@
#include "data/BackendInterface.hpp"
#include "data/CassandraBackend.hpp"
#include "data/KeyspaceBackend.hpp"
#include "data/LedgerCacheInterface.hpp"
#include "data/cassandra/SettingsProvider.hpp"
#include "util/config/ConfigDefinition.hpp"
@@ -56,15 +55,9 @@ makeBackend(util::config::ClioConfigDefinition const& config, data::LedgerCacheI
if (boost::iequals(type, "cassandra")) {
auto const cfg = config.getObject("database." + type);
if (cfg.getValueView("provider").asString() == toString(cassandra::impl::Provider::Keyspace)) {
backend = std::make_shared<data::cassandra::KeyspaceBackend>(
data::cassandra::SettingsProvider{cfg}, cache, readOnly
);
} else {
backend = std::make_shared<data::cassandra::CassandraBackend>(
data::cassandra::SettingsProvider{cfg}, cache, readOnly
);
}
backend = std::make_shared<data::cassandra::CassandraBackend>(
data::cassandra::SettingsProvider{cfg}, cache, readOnly
);
}
if (!backend)

View File

@@ -295,7 +295,7 @@ public:
* @param account The account to fetch transactions for
* @param limit The maximum number of transactions per result page
* @param forward Whether to fetch the page forwards or backwards from the given cursor
* @param txnCursor The cursor to resume fetching from
* @param cursor The cursor to resume fetching from
* @param yield The coroutine context
* @return Results and a cursor to resume from
*/
@@ -304,7 +304,7 @@ public:
ripple::AccountID const& account,
std::uint32_t limit,
bool forward,
std::optional<TransactionsCursor> const& txnCursor,
std::optional<TransactionsCursor> const& cursor,
boost::asio::yield_context yield
) const = 0;

File diff suppressed because it is too large Load Diff

View File

@@ -1,308 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of clio: https://github.com/XRPLF/clio
Copyright (c) 2025, the clio developers.
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#pragma once
#include "data/LedgerHeaderCache.hpp"
#include "data/Types.hpp"
#include "data/cassandra/CassandraBackendFamily.hpp"
#include "data/cassandra/Concepts.hpp"
#include "data/cassandra/KeyspaceSchema.hpp"
#include "data/cassandra/SettingsProvider.hpp"
#include "data/cassandra/Types.hpp"
#include "data/cassandra/impl/ExecutionStrategy.hpp"
#include "util/Assert.hpp"
#include "util/log/Logger.hpp"
#include <boost/asio/spawn.hpp>
#include <boost/json/object.hpp>
#include <boost/uuid/string_generator.hpp>
#include <boost/uuid/uuid.hpp>
#include <cassandra.h>
#include <fmt/format.h>
#include <xrpl/basics/Blob.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerHeader.h>
#include <xrpl/protocol/nft.h>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <optional>
#include <stdexcept>
#include <utility>
#include <vector>
namespace data::cassandra {
/**
* @brief Implements @ref CassandraBackendFamily for Keyspace
*
* @tparam SettingsProviderType The settings provider type to use
* @tparam ExecutionStrategyType The execution strategy type to use
* @tparam FetchLedgerCacheType The ledger header cache type to use
*/
template <
SomeSettingsProvider SettingsProviderType,
SomeExecutionStrategy ExecutionStrategyType,
typename FetchLedgerCacheType = FetchLedgerCache>
class BasicKeyspaceBackend : public CassandraBackendFamily<
SettingsProviderType,
ExecutionStrategyType,
KeyspaceSchema<SettingsProviderType>,
FetchLedgerCacheType> {
using DefaultCassandraFamily = CassandraBackendFamily<
SettingsProviderType,
ExecutionStrategyType,
KeyspaceSchema<SettingsProviderType>,
FetchLedgerCacheType>;
using DefaultCassandraFamily::executor_;
using DefaultCassandraFamily::ledgerSequence_;
using DefaultCassandraFamily::log_;
using DefaultCassandraFamily::range_;
using DefaultCassandraFamily::schema_;
public:
/**
* @brief Inherit the constructors of the base class.
*/
using DefaultCassandraFamily::DefaultCassandraFamily;
/**
* @brief Move constructor is deleted because handle_ is shared by reference with executor
*/
BasicKeyspaceBackend(BasicKeyspaceBackend&&) = delete;
bool
doFinishWrites() override
{
this->waitForWritesToFinish();
// !range_.has_value() means the table 'ledger_range' is not populated;
// This would be the first write to the table.
// In this case, insert both min_sequence/max_sequence range into the table.
if (not(range_.has_value())) {
executor_.writeSync(schema_->insertLedgerRange, false, ledgerSequence_);
executor_.writeSync(schema_->insertLedgerRange, true, ledgerSequence_);
}
if (not this->executeSyncUpdate(schema_->updateLedgerRange.bind(ledgerSequence_, true, ledgerSequence_ - 1))) {
log_.warn() << "Update failed for ledger " << ledgerSequence_;
return false;
}
log_.info() << "Committed ledger " << ledgerSequence_;
return true;
}
NFTsAndCursor
fetchNFTsByIssuer(
ripple::AccountID const& issuer,
std::optional<std::uint32_t> const& taxon,
std::uint32_t const ledgerSequence,
std::uint32_t const limit,
std::optional<ripple::uint256> const& cursorIn,
boost::asio::yield_context yield
) const override
{
std::vector<ripple::uint256> nftIDs;
if (taxon.has_value()) {
// Keyspace and ScyllaDB uses the same logic for taxon-filtered queries
nftIDs = fetchNFTIDsByTaxon(issuer, *taxon, limit, cursorIn, yield);
} else {
// --- Amazon Keyspaces Workflow for non-taxon queries ---
auto const startTaxon = cursorIn.has_value() ? ripple::nft::toUInt32(ripple::nft::getTaxon(*cursorIn)) : 0;
auto const startTokenID = cursorIn.value_or(ripple::uint256(0));
Statement firstQuery = schema_->selectNFTIDsByIssuerTaxon.bind(issuer);
firstQuery.bindAt(1, startTaxon);
firstQuery.bindAt(2, startTokenID);
firstQuery.bindAt(3, Limit{limit});
auto const firstRes = executor_.read(yield, firstQuery);
if (firstRes) {
for (auto const [nftID] : extract<ripple::uint256>(firstRes.value()))
nftIDs.push_back(nftID);
}
if (nftIDs.size() < limit) {
auto const remainingLimit = limit - nftIDs.size();
Statement secondQuery = schema_->selectNFTsAfterTaxonKeyspaces.bind(issuer);
secondQuery.bindAt(1, startTaxon);
secondQuery.bindAt(2, Limit{remainingLimit});
auto const secondRes = executor_.read(yield, secondQuery);
if (secondRes) {
for (auto const [nftID] : extract<ripple::uint256>(secondRes.value()))
nftIDs.push_back(nftID);
}
}
}
return populateNFTsAndCreateCursor(nftIDs, ledgerSequence, limit, yield);
}
/**
* @brief (Unsupported in Keyspaces) Fetches account root object indexes by page.
* * @note Loading the cache by enumerating all accounts is currently unsupported by the AWS Keyspaces backend.
* This function's logic relies on "PER PARTITION LIMIT 1", which Keyspaces does not support, and there is
* no efficient alternative. This is acceptable as the cache is primarily loaded via diffs. Calling this
* function will throw an exception.
*
* @param number The total number of accounts to fetch.
* @param pageSize The maximum number of accounts per page.
* @param seq The accounts need to exist at this ledger sequence.
* @param yield The coroutine context.
* @return A vector of ripple::uint256 representing the account root hashes.
*/
std::vector<ripple::uint256>
fetchAccountRoots(
[[maybe_unused]] std::uint32_t number,
[[maybe_unused]] std::uint32_t pageSize,
[[maybe_unused]] std::uint32_t seq,
[[maybe_unused]] boost::asio::yield_context yield
) const override
{
ASSERT(false, "Fetching account roots is not supported by the Keyspaces backend.");
std::unreachable();
}
private:
std::vector<ripple::uint256>
fetchNFTIDsByTaxon(
ripple::AccountID const& issuer,
std::uint32_t const taxon,
std::uint32_t const limit,
std::optional<ripple::uint256> const& cursorIn,
boost::asio::yield_context yield
) const
{
std::vector<ripple::uint256> nftIDs;
Statement statement = schema_->selectNFTIDsByIssuerTaxon.bind(issuer);
statement.bindAt(1, taxon);
statement.bindAt(2, cursorIn.value_or(ripple::uint256(0)));
statement.bindAt(3, Limit{limit});
auto const res = executor_.read(yield, statement);
if (res && res.value().hasRows()) {
for (auto const [nftID] : extract<ripple::uint256>(res.value()))
nftIDs.push_back(nftID);
}
return nftIDs;
}
std::vector<ripple::uint256>
fetchNFTIDsWithoutTaxon(
ripple::AccountID const& issuer,
std::uint32_t const limit,
std::optional<ripple::uint256> const& cursorIn,
boost::asio::yield_context yield
) const
{
std::vector<ripple::uint256> nftIDs;
auto const startTaxon = cursorIn.has_value() ? ripple::nft::toUInt32(ripple::nft::getTaxon(*cursorIn)) : 0;
auto const startTokenID = cursorIn.value_or(ripple::uint256(0));
Statement firstQuery = schema_->selectNFTIDsByIssuerTaxon.bind(issuer);
firstQuery.bindAt(1, startTaxon);
firstQuery.bindAt(2, startTokenID);
firstQuery.bindAt(3, Limit{limit});
auto const firstRes = executor_.read(yield, firstQuery);
if (firstRes) {
for (auto const [nftID] : extract<ripple::uint256>(firstRes.value()))
nftIDs.push_back(nftID);
}
if (nftIDs.size() < limit) {
auto const remainingLimit = limit - nftIDs.size();
Statement secondQuery = schema_->selectNFTsAfterTaxonKeyspaces.bind(issuer);
secondQuery.bindAt(1, startTaxon);
secondQuery.bindAt(2, Limit{remainingLimit});
auto const secondRes = executor_.read(yield, secondQuery);
if (secondRes) {
for (auto const [nftID] : extract<ripple::uint256>(secondRes.value()))
nftIDs.push_back(nftID);
}
}
return nftIDs;
}
/**
* @brief Takes a list of NFT IDs, fetches their full data, and assembles the final result with a cursor.
*/
NFTsAndCursor
populateNFTsAndCreateCursor(
std::vector<ripple::uint256> const& nftIDs,
std::uint32_t const ledgerSequence,
std::uint32_t const limit,
boost::asio::yield_context yield
) const
{
if (nftIDs.empty()) {
LOG(log_.debug()) << "No rows returned";
return {};
}
NFTsAndCursor ret;
if (nftIDs.size() == limit)
ret.cursor = nftIDs.back();
// Prepare and execute queries to fetch NFT info and URIs in parallel.
std::vector<Statement> selectNFTStatements;
selectNFTStatements.reserve(nftIDs.size());
std::transform(
std::cbegin(nftIDs), std::cend(nftIDs), std::back_inserter(selectNFTStatements), [&](auto const& nftID) {
return schema_->selectNFT.bind(nftID, ledgerSequence);
}
);
std::vector<Statement> selectNFTURIStatements;
selectNFTURIStatements.reserve(nftIDs.size());
std::transform(
std::cbegin(nftIDs), std::cend(nftIDs), std::back_inserter(selectNFTURIStatements), [&](auto const& nftID) {
return schema_->selectNFTURI.bind(nftID, ledgerSequence);
}
);
auto const nftInfos = executor_.readEach(yield, selectNFTStatements);
auto const nftUris = executor_.readEach(yield, selectNFTURIStatements);
// Combine the results into final NFT objects.
for (auto i = 0u; i < nftIDs.size(); ++i) {
if (auto const maybeRow = nftInfos[i].template get<uint32_t, ripple::AccountID, bool>(); maybeRow) {
auto [seq, owner, isBurned] = *maybeRow;
NFT nft(nftIDs[i], seq, owner, isBurned);
if (auto const maybeUri = nftUris[i].template get<ripple::Blob>(); maybeUri)
nft.uri = *maybeUri;
ret.nfts.push_back(nft);
}
}
return ret;
}
};
using KeyspaceBackend = BasicKeyspaceBackend<SettingsProvider, impl::DefaultExecutionStrategy<>>;
} // namespace data::cassandra

File diff suppressed because it is too large Load Diff

View File

@@ -1,178 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of clio: https://github.com/XRPLF/clio
Copyright (c) 2025, the clio developers.
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#pragma once
#include "data/cassandra/Concepts.hpp"
#include "data/cassandra/Handle.hpp"
#include "data/cassandra/Schema.hpp"
#include "data/cassandra/SettingsProvider.hpp"
#include "data/cassandra/Types.hpp"
#include "util/log/Logger.hpp"
#include <boost/json/string.hpp>
#include <fmt/compile.h>
#include <functional>
#include <memory>
namespace data::cassandra {
/**
* @brief Manages the DB schema and provides access to prepared statements.
*/
template <SomeSettingsProvider SettingsProviderType>
class CassandraSchema : public Schema<SettingsProvider> {
using Schema::Schema;
public:
/**
* @brief Construct a new Cassandra Schema object
*
* @param settingsProvider The settings provider
*/
struct CassandraStatements : public Schema<SettingsProvider>::Statements {
using Schema<SettingsProvider>::Statements::Statements;
//
// Update (and "delete") queries
//
PreparedStatement updateLedgerRange = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
UPDATE {}
SET sequence = ?
WHERE is_latest = ?
IF sequence IN (?, null)
)",
qualifiedTableName(settingsProvider_.get(), "ledger_range")
)
);
}();
//
// Select queries
//
PreparedStatement selectNFTIDsByIssuer = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
SELECT token_id
FROM {}
WHERE issuer = ?
AND (taxon, token_id) > ?
ORDER BY taxon ASC, token_id ASC
LIMIT ?
)",
qualifiedTableName(settingsProvider_.get(), "issuer_nf_tokens_v2")
)
);
}();
PreparedStatement selectAccountFromBeginning = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
SELECT account
FROM {}
WHERE token(account) > 0
PER PARTITION LIMIT 1
LIMIT ?
)",
qualifiedTableName(settingsProvider_.get(), "account_tx")
)
);
}();
PreparedStatement selectAccountFromToken = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
SELECT account
FROM {}
WHERE token(account) > token(?)
PER PARTITION LIMIT 1
LIMIT ?
)",
qualifiedTableName(settingsProvider_.get(), "account_tx")
)
);
}();
PreparedStatement selectLedgerPageKeys = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
SELECT key
FROM {}
WHERE TOKEN(key) >= ?
AND sequence <= ?
PER PARTITION LIMIT 1
LIMIT ?
ALLOW FILTERING
)",
qualifiedTableName(settingsProvider_.get(), "objects")
)
);
}();
PreparedStatement selectLedgerPage = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
SELECT object, key
FROM {}
WHERE TOKEN(key) >= ?
AND sequence <= ?
PER PARTITION LIMIT 1
LIMIT ?
ALLOW FILTERING
)",
qualifiedTableName(settingsProvider_.get(), "objects")
)
);
}();
};
void
prepareStatements(Handle const& handle) override
{
LOG(log_.info()) << "Preparing cassandra statements";
statements_ = std::make_unique<CassandraStatements>(settingsProvider_, handle);
LOG(log_.info()) << "Finished preparing statements";
}
/**
* @brief Provides access to statements.
*
* @return The statements
*/
std::unique_ptr<CassandraStatements> const&
operator->() const
{
return statements_;
}
private:
std::unique_ptr<CassandraStatements> statements_{nullptr};
};
} // namespace data::cassandra

View File

@@ -1,140 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of clio: https://github.com/XRPLF/clio
Copyright (c) 2025, the clio developers.
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#pragma once
#include "data/cassandra/Concepts.hpp"
#include "data/cassandra/Handle.hpp"
#include "data/cassandra/Schema.hpp"
#include "data/cassandra/SettingsProvider.hpp"
#include "data/cassandra/Types.hpp"
#include "util/log/Logger.hpp"
#include <boost/json/string.hpp>
#include <fmt/compile.h>
#include <functional>
#include <memory>
namespace data::cassandra {
/**
* @brief Manages the DB schema and provides access to prepared statements.
*/
template <SomeSettingsProvider SettingsProviderType>
class KeyspaceSchema : public Schema<SettingsProvider> {
public:
using Schema::Schema;
/**
* @brief Construct a new Keyspace Schema object
*
* @param settingsProvider The settings provider
*/
struct KeyspaceStatements : public Schema<SettingsProvider>::Statements {
using Schema<SettingsProvider>::Statements::Statements;
//
// Insert queries
//
PreparedStatement insertLedgerRange = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
INSERT INTO {} (is_latest, sequence) VALUES (?, ?) IF NOT EXISTS
)",
qualifiedTableName(settingsProvider_.get(), "ledger_range")
)
);
}();
//
// Update (and "delete") queries
//
PreparedStatement updateLedgerRange = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
UPDATE {}
SET sequence = ?
WHERE is_latest = ?
IF sequence = ?
)",
qualifiedTableName(settingsProvider_.get(), "ledger_range")
)
);
}();
PreparedStatement selectLedgerRange = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
SELECT sequence
FROM {}
WHERE is_latest in (True, False)
)",
qualifiedTableName(settingsProvider_.get(), "ledger_range")
)
);
}();
//
// Select queries
//
PreparedStatement selectNFTsAfterTaxonKeyspaces = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
SELECT token_id
FROM {}
WHERE issuer = ?
AND taxon > ?
ORDER BY taxon ASC, token_id ASC
LIMIT ?
)",
qualifiedTableName(settingsProvider_.get(), "issuer_nf_tokens_v2")
)
);
}();
};
void
prepareStatements(Handle const& handle) override
{
LOG(log_.info()) << "Preparing aws keyspace statements";
statements_ = std::make_unique<KeyspaceStatements>(settingsProvider_, handle);
LOG(log_.info()) << "Finished preparing statements";
}
/**
* @brief Provides access to statements.
*
* @return The statements
*/
std::unique_ptr<KeyspaceStatements> const&
operator->() const
{
return statements_;
}
private:
std::unique_ptr<KeyspaceStatements> statements_{nullptr};
};
} // namespace data::cassandra

View File

@@ -24,10 +24,11 @@
#include "data/cassandra/Types.hpp"
#include "util/log/Logger.hpp"
#include <boost/json/string.hpp>
#include <fmt/compile.h>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
@@ -53,15 +54,12 @@ template <SomeSettingsProvider SettingsProviderType>
*/
template <SomeSettingsProvider SettingsProviderType>
class Schema {
protected:
util::Logger log_{"Backend"};
std::reference_wrapper<SettingsProviderType const> settingsProvider_;
public:
virtual ~Schema() = default;
/**
* @brief Shared Schema's between all Schema classes (Cassandra and Keyspace)
* @brief Construct a new Schema object
*
* @param settingsProvider The settings provider
*/
@@ -337,7 +335,6 @@ public:
* @brief Prepared statements holder.
*/
class Statements {
protected:
std::reference_wrapper<SettingsProviderType const> settingsProvider_;
std::reference_wrapper<Handle const> handle_;
@@ -351,6 +348,86 @@ public:
Statements(SettingsProviderType const& settingsProvider, Handle const& handle)
: settingsProvider_{settingsProvider}, handle_{std::cref(handle)}
{
// initialize scylladb supported queries
if (settingsProvider_.get().getSettings().provider == "scylladb") {
selectAccountFromBeginningScylla = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
SELECT account
FROM {}
WHERE token(account) > 0
PER PARTITION LIMIT 1
LIMIT ?
)",
qualifiedTableName(settingsProvider_.get(), "account_tx")
)
);
}();
selectAccountFromTokenScylla = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
SELECT account
FROM {}
WHERE token(account) > token(?)
PER PARTITION LIMIT 1
LIMIT ?
)",
qualifiedTableName(settingsProvider_.get(), "account_tx")
)
);
}();
selectNFTsByIssuerScylla = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
SELECT token_id
FROM {}
WHERE issuer = ?
AND (taxon, token_id) > ?
ORDER BY taxon ASC, token_id ASC
LIMIT ?
)",
qualifiedTableName(settingsProvider_.get(), "issuer_nf_tokens_v2")
)
);
}();
updateLedgerRange = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
UPDATE {}
SET sequence = ?
WHERE is_latest = ?
IF sequence IN (?, null)
)",
qualifiedTableName(settingsProvider_.get(), "ledger_range")
)
);
}();
// AWS_keyspace supported queries
} else if (settingsProvider_.get().getSettings().provider == "aws_keyspace") {
selectNFTsAfterTaxonKeyspaces = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
SELECT token_id
FROM {}
WHERE issuer = ?
AND taxon > ?
ORDER BY taxon ASC, token_id ASC
LIMIT ?
)",
qualifiedTableName(settingsProvider_.get(), "issuer_nf_tokens_v2")
)
);
}();
}
}
//
@@ -530,6 +607,31 @@ public:
// Update (and "delete") queries
//
PreparedStatement insertLedgerRange = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
INSERT INTO {} (is_latest, sequence) VALUES (?, ?) IF NOT EXISTS
)",
qualifiedTableName(settingsProvider_.get(), "ledger_range")
)
);
}();
PreparedStatement updateLedgerRange = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
UPDATE {}
SET sequence = ?
WHERE is_latest = ?
IF sequence = ?
)",
qualifiedTableName(settingsProvider_.get(), "ledger_range")
)
);
}();
PreparedStatement deleteLedgerRange = [this]() {
return handle_.get().prepare(
fmt::format(
@@ -644,6 +746,45 @@ public:
);
}();
/*
Currently, these two SELECT statements is not used.
If we ever use them, will need to change the PER PARTITION LIMIT to support for Keyspace
PreparedStatement selectLedgerPageKeys = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
SELECT key
FROM {}
WHERE TOKEN(key) >= ?
AND sequence <= ?
PER PARTITION LIMIT 1
LIMIT ?
ALLOW FILTERING
)",
qualifiedTableName(settingsProvider_.get(), "objects")
)
);
}();
PreparedStatement selectLedgerPage = [this]() {
return handle_.get().prepare(
fmt::format(
R"(
SELECT object, key
FROM {}
WHERE TOKEN(key) >= ?
AND sequence <= ?
PER PARTITION LIMIT 1
LIMIT ?
ALLOW FILTERING
)",
qualifiedTableName(settingsProvider_.get(), "objects")
)
);
}();
*/
PreparedStatement getToken = [this]() {
return handle_.get().prepare(
fmt::format(
@@ -863,6 +1004,15 @@ public:
)
);
}();
// For ScyllaDB / Cassandra ONLY
std::optional<PreparedStatement> selectAccountFromBeginningScylla;
std::optional<PreparedStatement> selectAccountFromTokenScylla;
std::optional<PreparedStatement> selectNFTsByIssuerScylla;
// For AWS Keyspaces ONLY
// NOTE: AWS keyspace is not able to load cache with accounts
std::optional<PreparedStatement> selectNFTsAfterTaxonKeyspaces;
};
/**
@@ -870,8 +1020,27 @@ public:
*
* @param handle The handle to the DB
*/
virtual void
prepareStatements(Handle const& handle) = 0;
void
prepareStatements(Handle const& handle)
{
LOG(log_.info()) << "Preparing cassandra statements";
statements_ = std::make_unique<Statements>(settingsProvider_, handle);
LOG(log_.info()) << "Finished preparing statements";
}
/**
* @brief Provides access to statements.
*
* @return The statements
*/
std::unique_ptr<Statements> const&
operator->() const
{
return statements_;
}
private:
std::unique_ptr<Statements> statements_{nullptr};
};
} // namespace data::cassandra

View File

@@ -25,8 +25,8 @@
#include <utility>
namespace data::cassandra {
namespace impl {
namespace impl {
struct Settings;
class Session;
class Cluster;
@@ -36,7 +36,6 @@ struct Result;
class Statement;
class PreparedStatement;
struct Batch;
} // namespace impl
using Settings = impl::Settings;

View File

@@ -61,12 +61,8 @@ Cluster::Cluster(Settings const& settings) : ManagedObject{cass_cluster_new(), k
cass_cluster_set_request_timeout(*this, settings.requestTimeout.count());
// TODO: AWS keyspace reads should be local_one to save cost
if (settings.provider == toString(cassandra::impl::Provider::Keyspace)) {
if (settings.provider == "aws_keyspace") {
if (auto const rc = cass_cluster_set_consistency(*this, CASS_CONSISTENCY_LOCAL_QUORUM); rc != CASS_OK) {
throw std::runtime_error(fmt::format("Error setting keyspace consistency: {}", cass_error_desc(rc)));
}
} else {
if (auto const rc = cass_cluster_set_consistency(*this, CASS_CONSISTENCY_QUORUM); rc != CASS_OK) {
throw std::runtime_error(fmt::format("Error setting cassandra consistency: {}", cass_error_desc(rc)));
}
}

View File

@@ -31,29 +31,10 @@
#include <string>
#include <string_view>
#include <thread>
#include <utility>
#include <variant>
namespace data::cassandra::impl {
namespace {
enum class Provider { Cassandra, Keyspace };
inline std::string
toString(Provider provider)
{
switch (provider) {
case Provider::Cassandra:
return "cassandra";
case Provider::Keyspace:
return "aws_keyspace";
}
std::unreachable();
}
} // namespace
// TODO: move Settings to public interface, not impl
/**
@@ -64,7 +45,7 @@ struct Settings {
static constexpr uint32_t kDEFAULT_MAX_WRITE_REQUESTS_OUTSTANDING = 10'000;
static constexpr uint32_t kDEFAULT_MAX_READ_REQUESTS_OUTSTANDING = 100'000;
static constexpr std::size_t kDEFAULT_BATCH_SIZE = 20;
static constexpr Provider kDEFAULT_PROVIDER = Provider::Cassandra;
static constexpr std::string kDEFAULT_PROVIDER = "cassandra";
/**
* @brief Represents the configuration of contact points for cassandra.
@@ -109,7 +90,7 @@ struct Settings {
std::size_t writeBatchSize = kDEFAULT_BATCH_SIZE;
/** @brief Provider to know if we are using scylladb or keyspace */
std::string provider = toString(kDEFAULT_PROVIDER);
std::string provider = kDEFAULT_PROVIDER;
/** @brief Size of the IO queue */
std::optional<uint32_t> queueSizeIO = std::nullopt; // NOLINT(readability-redundant-member-init)

View File

@@ -58,16 +58,14 @@ public:
explicit Statement(std::string_view query, Args&&... args)
: ManagedObject{cass_statement_new_n(query.data(), query.size(), sizeof...(args)), kDELETER}
{
// TODO: figure out how to set consistency level in config
// NOTE: Keyspace doesn't support QUORUM at write level
// cass_statement_set_consistency(*this, CASS_CONSISTENCY_LOCAL_QUORUM);
cass_statement_set_consistency(*this, CASS_CONSISTENCY_LOCAL_QUORUM);
cass_statement_set_is_idempotent(*this, cass_true);
bind<Args...>(std::forward<Args>(args)...);
}
/* implicit */ Statement(CassStatement* ptr) : ManagedObject{ptr, kDELETER}
{
// cass_statement_set_consistency(*this, CASS_CONSISTENCY_LOCAL_QUORUM);
cass_statement_set_consistency(*this, CASS_CONSISTENCY_LOCAL_QUORUM);
cass_statement_set_is_idempotent(*this, cass_true);
}

View File

@@ -22,7 +22,6 @@
#include "etl/NetworkValidatedLedgersInterface.hpp"
#include "feed/SubscriptionManagerInterface.hpp"
#include "rpc/JS.hpp"
#include "util/JsonUtils.hpp"
#include "util/Retry.hpp"
#include "util/Spawn.hpp"
#include "util/log/Logger.hpp"
@@ -216,7 +215,7 @@ SubscriptionSource::handleMessage(std::string const& message)
if (object.contains(JS(result))) {
auto const& result = object.at(JS(result)).as_object();
if (result.contains(JS(ledger_index)))
ledgerIndex = util::integralValueAs<uint32_t>(result.at(JS(ledger_index)));
ledgerIndex = result.at(JS(ledger_index)).as_int64();
if (result.contains(JS(validated_ledgers))) {
auto validatedLedgers = boost::json::value_to<std::string>(result.at(JS(validated_ledgers)));
@@ -228,7 +227,7 @@ SubscriptionSource::handleMessage(std::string const& message)
LOG(log_.debug()) << "Received a message of type 'ledgerClosed' on ledger subscription stream. Message: "
<< object;
if (object.contains(JS(ledger_index))) {
ledgerIndex = util::integralValueAs<uint32_t>(object.at(JS(ledger_index)));
ledgerIndex = object.at(JS(ledger_index)).as_int64();
}
if (object.contains(JS(validated_ledgers))) {
auto validatedLedgers = boost::json::value_to<std::string>(object.at(JS(validated_ledgers)));

View File

@@ -26,7 +26,6 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include "util/log/Logger.hpp"
#include <boost/asio/spawn.hpp>
@@ -210,7 +209,6 @@ TransactionFeed::pub(
auto& txnPubobj = pubObj[txKey].as_object();
rpc::insertDeliverMaxAlias(txnPubobj, version);
rpc::insertMPTIssuanceID(txnPubobj, meta);
Json::Value nftJson;
ripple::RPC::insertNFTSyntheticInJson(nftJson, tx, *meta);
@@ -224,9 +222,8 @@ TransactionFeed::pub(
auto const& metaObj = pubObj[JS(meta)];
ASSERT(metaObj.is_object(), "meta must be an obj in rippled and clio");
if (metaObj.as_object().contains("TransactionIndex") && metaObj.as_object().at("TransactionIndex").is_int64()) {
if (auto const& ctid = rpc::encodeCTID(
lgrInfo.seq, util::integralValueAs<uint16_t>(metaObj.as_object().at("TransactionIndex")), networkID
);
if (auto const& ctid =
rpc::encodeCTID(lgrInfo.seq, metaObj.as_object().at("TransactionIndex").as_int64(), networkID);
ctid)
pubObj[JS(ctid)] = ctid.value();
}

View File

@@ -30,52 +30,15 @@
#include <cstdint>
#include <iterator>
#include <optional>
#include <ostream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#include <variant>
using namespace std;
namespace rpc {
std::ostream&
operator<<(std::ostream& stream, Status const& status)
{
std::visit(
util::OverloadSet{
[&stream, &status](RippledError err) {
stream << "Code: " << static_cast<std::underlying_type_t<RippledError>>(err);
if (!status.error.empty())
stream << ", Error: " << status.error;
if (!status.message.empty()) {
stream << ", Message: " << status.message;
} else {
stream << ", Message: " << ripple::RPC::get_error_info(err).message;
}
},
[&stream, &status](ClioError err) {
stream << "Code: " << static_cast<std::underlying_type_t<ClioError>>(err);
if (!status.error.empty())
stream << ", Error: " << status.error;
if (!status.message.empty()) {
stream << ", Message: " << status.message;
} else {
stream << ", Message: " << getErrorInfo(err).message;
}
}
},
status.code
);
if (status.extraInfo.has_value())
stream << ", Extra Info: " << *status.extraInfo;
return stream;
}
WarningInfo const&
getWarningInfo(WarningCode code)
{

View File

@@ -182,16 +182,6 @@ struct Status {
return false;
}
/**
* @brief Custom output stream for Status
*
* @param stream The output stream
* @param status The Status
* @return The same ostream we were given
*/
friend std::ostream&
operator<<(std::ostream& stream, Status const& status);
};
/** @brief Warning codes that can be returned by clio. */

View File

@@ -28,13 +28,11 @@
#include "rpc/common/Types.hpp"
#include "util/AccountUtils.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include "util/Profiler.hpp"
#include "util/log/Logger.hpp"
#include "web/Context.hpp"
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/format/format_fwd.hpp>
#include <boost/format/free_funcs.hpp>
@@ -174,7 +172,10 @@ canHaveDeliveredAmount(
if (tt != ripple::ttPAYMENT && tt != ripple::ttCHECK_CASH && tt != ripple::ttACCOUNT_DELETE)
return false;
return meta->getResultTER() == ripple::tesSUCCESS;
if (meta->getResultTER() != ripple::tesSUCCESS)
return false;
return true;
}
std::optional<ripple::AccountID>
@@ -258,7 +259,6 @@ toExpandedJson(
auto metaJson = toJson(*meta);
insertDeliveredAmount(metaJson, txn, meta, blobs.date);
insertDeliverMaxAlias(txnJson, apiVersion);
insertMPTIssuanceID(txnJson, meta);
if (nftEnabled == NFTokenjson::ENABLE) {
Json::Value nftJson;
@@ -317,66 +317,6 @@ insertDeliveredAmount(
return false;
}
/**
* @brief Get the delivered amount
*
* @param meta The metadata
* @return The mpt_issuance_id or std::nullopt if not available
*/
static std::optional<ripple::uint192>
getMPTIssuanceID(std::shared_ptr<ripple::TxMeta const> const& meta)
{
ripple::TxMeta const& transactionMeta = *meta;
for (ripple::STObject const& node : transactionMeta.getNodes()) {
if (node.getFieldU16(ripple::sfLedgerEntryType) != ripple::ltMPTOKEN_ISSUANCE ||
node.getFName() != ripple::sfCreatedNode)
continue;
auto const& mptNode = node.peekAtField(ripple::sfNewFields).downcast<ripple::STObject>();
return ripple::makeMptID(mptNode[ripple::sfSequence], mptNode[ripple::sfIssuer]);
}
return {};
}
/**
* @brief Check if transaction has a new MPToken created
*
* @param txnJson The transaction Json
* @param meta The metadata
* @return true if the transaction can have a mpt_issuance_id
*/
static bool
canHaveMPTIssuanceID(boost::json::object const& txnJson, std::shared_ptr<ripple::TxMeta const> const& meta)
{
if (txnJson.at(JS(TransactionType)).is_string() and
not boost::iequals(txnJson.at(JS(TransactionType)).as_string(), JS(MPTokenIssuanceCreate)))
return false;
if (meta->getResultTER() != ripple::tesSUCCESS)
return false;
return true;
}
bool
insertMPTIssuanceID(boost::json::object& txnJson, std::shared_ptr<ripple::TxMeta const> const& meta)
{
if (!canHaveMPTIssuanceID(txnJson, meta))
return false;
if (txnJson.contains(JS(TransactionType)) && txnJson.at(JS(TransactionType)).is_string() and
txnJson.at(JS(TransactionType)).as_string() == JS(MPTokenIssuanceCreate))
return false;
auto const id = getMPTIssuanceID(meta);
ASSERT(id.has_value(), "MPTIssuanceID must have value");
txnJson[JS(mpt_issuance_id)] = ripple::to_string(*id);
return true;
}
void
insertDeliverMaxAlias(boost::json::object& txJson, std::uint32_t const apiVersion)
{
@@ -493,8 +433,8 @@ ledgerHeaderFromRequest(std::shared_ptr<data::BackendInterface const> const& bac
} else {
ledgerSequence = parseStringAsUInt(stringIndex);
}
} else if (indexValue.is_int64() or indexValue.is_uint64()) {
ledgerSequence = util::integralValueAs<uint32_t>(indexValue);
} else if (indexValue.is_int64()) {
ledgerSequence = indexValue.as_int64();
}
} else {
ledgerSequence = ctx.range.maxSequence;

View File

@@ -199,18 +199,6 @@ insertDeliveredAmount(
uint32_t date
);
/**
* @brief Add "mpt_issuance_id" into various MPTToken transaction json.
* @note We exclude "mpt_issuance_id" for MPTokenIssuanceCreate only. The reason is because the mpt_issuance_id
* is generated only after one submits MPTokenIssuanceCreate, so theres no way to know what the id is. (rippled)
*
* @param txnJson The transaction Json object
* @param meta The metadata object
* @return true if the "mpt_issuance_id" is added to the txnJson JSON object
*/
bool
insertMPTIssuanceID(boost::json::object& txnJson, std::shared_ptr<ripple::TxMeta const> const& meta);
/**
* @brief Convert STBase object to JSON
*

View File

@@ -23,49 +23,10 @@
#include <boost/json/object.hpp>
#include <boost/json/value.hpp>
#include <concepts>
#include <cstdint>
#include <limits>
#include <string>
#include <type_traits>
namespace rpc::validation {
namespace impl {
template <std::unsigned_integral Expected>
void
clampAs(boost::json::value& value)
{
if (value.is_uint64()) {
auto const valueUint = value.as_uint64();
if (valueUint > static_cast<uint64_t>(std::numeric_limits<Expected>::max()))
value = std::numeric_limits<Expected>::max();
} else if (value.is_int64()) {
auto const valueInt = value.as_int64();
if (valueInt > static_cast<int64_t>(std::numeric_limits<Expected>::max()))
value = std::numeric_limits<Expected>::max();
}
}
template <std::signed_integral Expected>
void
clampAs(boost::json::value& value)
{
if (value.is_uint64()) {
auto const valueUint = value.as_uint64();
if (valueUint > static_cast<uint64_t>(std::numeric_limits<Expected>::max()))
value = std::numeric_limits<Expected>::max();
} else if (value.is_int64()) {
auto const valueInt = value.as_int64();
if (valueInt > static_cast<int64_t>(std::numeric_limits<Expected>::max())) {
value = std::numeric_limits<Expected>::max();
} else if (valueInt < static_cast<int64_t>(std::numeric_limits<Expected>::min())) {
value = std::numeric_limits<Expected>::min();
}
}
}
} // namespace impl
/**
* @brief Check that the type is the same as what was expected.
@@ -75,7 +36,7 @@ clampAs(boost::json::value& value)
* @return true if convertible; false otherwise
*/
template <typename Expected>
[[nodiscard]] bool
[[nodiscard]] static bool
checkType(boost::json::value const& value)
{
auto hasError = false;
@@ -97,7 +58,7 @@ checkType(boost::json::value const& value)
} else if constexpr (std::is_convertible_v<Expected, uint64_t> or std::is_convertible_v<Expected, int64_t>) {
if (not value.is_int64() && not value.is_uint64())
hasError = true;
// if the type specified is unsigned, it should not be negative
// specify the type is unsigened, it can not be negative
if constexpr (std::is_unsigned_v<Expected>) {
if (value.is_int64() and value.as_int64() < 0)
hasError = true;
@@ -107,28 +68,4 @@ checkType(boost::json::value const& value)
return not hasError;
}
/**
* @brief Check that the type is the same as what was expected optionally clamping it into range.
*
* This is used to automatically clamp the value into the range available to the specified type. It is needed in
* order to avoid Min, Max and other validators throw "not exact" error from Boost.Json library if the value does not
* fit in the specified type.
*
* @tparam Expected The expected type that value should be convertible to
* @param value The json value to check the type of
* @return true if convertible; false otherwise
*/
template <typename Expected>
[[nodiscard]] bool
checkTypeAndClamp(boost::json::value& value)
{
if (not checkType<Expected>(value))
return false; // fails basic type check
if constexpr (std::is_integral_v<Expected> and not std::is_same_v<Expected, bool>)
impl::clampAs<Expected>(value);
return true;
}
} // namespace rpc::validation

View File

@@ -142,21 +142,19 @@ template <typename... Types>
struct Type final {
/**
* @brief Verify that the JSON value is (one) of specified type(s).
* @note The value itself can only change for integral types and only if the value is outside of the range of the
* expected integer type (see checkTypeAndClamp).
*
* @param value The JSON value representing the outer object
* @param key The key used to retrieve the tested value from the outer object
* @return `RippledError::rpcINVALID_PARAMS` if validation failed; otherwise no error is returned
*/
[[nodiscard]] MaybeError
verify(boost::json::value& value, std::string_view key) const
verify(boost::json::value const& value, std::string_view key) const
{
if (not value.is_object() or not value.as_object().contains(key))
return {}; // ignore. If field is supposed to exist, let 'required' fail instead
auto& res = value.as_object().at(key);
auto const convertible = (checkTypeAndClamp<Types>(res) || ...);
auto const& res = value.as_object().at(key);
auto const convertible = (checkType<Types>(res) || ...);
if (not convertible)
return Error{Status{RippledError::rpcINVALID_PARAMS}};

View File

@@ -19,7 +19,6 @@
#include "rpc/common/impl/APIVersionParser.hpp"
#include "util/JsonUtils.hpp"
#include "util/config/ObjectView.hpp"
#include "util/log/Logger.hpp"
@@ -63,7 +62,7 @@ ProductionAPIVersionParser::parse(boost::json::object const& request) const
if (!request.at("api_version").is_int64())
return Error{"API version must be an integer"};
auto const version = util::integralValueAs<uint32_t>(request.at("api_version"));
auto const version = request.at("api_version").as_int64();
if (version > maxVersion_)
return Error{fmt::format("Requested API version is higher than maximum supported ({})", maxVersion_)};

View File

@@ -29,7 +29,6 @@
#include "rpc/common/Types.hpp"
#include "rpc/common/Validators.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
@@ -318,7 +317,7 @@ tag_invoke(boost::json::value_to_tag<AMMInfoHandler::Input>, boost::json::value
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jv.at(JS(ledger_index)));
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
}

View File

@@ -24,7 +24,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
@@ -42,7 +41,6 @@
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol/tokens.h>
#include <cstdint>
#include <optional>
#include <string>
#include <utility>
@@ -143,7 +141,7 @@ tag_invoke(boost::json::value_to_tag<AccountChannelsHandler::Input>, boost::json
input.account = boost::json::value_to<std::string>(jv.at(JS(account)));
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jv.at(JS(limit)));
input.limit = jv.at(JS(limit)).as_int64();
if (jsonObject.contains(JS(marker)))
input.marker = boost::json::value_to<std::string>(jv.at(JS(marker)));
@@ -156,7 +154,7 @@ tag_invoke(boost::json::value_to_tag<AccountChannelsHandler::Input>, boost::json
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jv.at(JS(ledger_index)));
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
}

View File

@@ -24,7 +24,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/value.hpp>
@@ -130,7 +129,7 @@ tag_invoke(boost::json::value_to_tag<AccountCurrenciesHandler::Input>, boost::js
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jv.at(JS(ledger_index)));
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
}

View File

@@ -26,7 +26,6 @@
#include "rpc/common/JsonBool.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
@@ -43,7 +42,6 @@
#include <xrpl/protocol/jss.h>
#include <algorithm>
#include <cstdint>
#include <iterator>
#include <optional>
#include <string>
@@ -206,7 +204,7 @@ tag_invoke(boost::json::value_to_tag<AccountInfoHandler::Input>, boost::json::va
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index)));
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jsonObject.at(JS(ledger_index))));
}

View File

@@ -24,7 +24,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
@@ -41,7 +40,6 @@
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/jss.h>
#include <cstdint>
#include <optional>
#include <string>
#include <utility>
@@ -201,7 +199,7 @@ tag_invoke(boost::json::value_to_tag<AccountLinesHandler::Input>, boost::json::v
input.account = boost::json::value_to<std::string>(jv.at(JS(account)));
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jv.at(JS(limit)));
input.limit = jv.at(JS(limit)).as_int64();
if (jsonObject.contains(JS(marker)))
input.marker = boost::json::value_to<std::string>(jv.at(JS(marker)));
@@ -217,7 +215,7 @@ tag_invoke(boost::json::value_to_tag<AccountLinesHandler::Input>, boost::json::v
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jv.at(JS(ledger_index)));
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
}

View File

@@ -24,7 +24,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/value.hpp>
@@ -159,14 +158,14 @@ tag_invoke(boost::json::value_to_tag<AccountNFTsHandler::Input>, boost::json::va
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index)));
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jsonObject.at(JS(ledger_index))));
}
}
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));
input.limit = jsonObject.at(JS(limit)).as_int64();
if (jsonObject.contains(JS(marker)))
input.marker = boost::json::value_to<std::string>(jsonObject.at(JS(marker)));

View File

@@ -24,7 +24,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include "util/LedgerUtils.hpp"
#include <boost/json/array.hpp>
@@ -39,7 +38,6 @@
#include <xrpl/protocol/jss.h>
#include <algorithm>
#include <cstdint>
#include <iterator>
#include <optional>
#include <string>
@@ -155,7 +153,7 @@ tag_invoke(boost::json::value_to_tag<AccountObjectsHandler::Input>, boost::json:
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jv.at(JS(ledger_index)));
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
}
@@ -167,7 +165,7 @@ tag_invoke(boost::json::value_to_tag<AccountObjectsHandler::Input>, boost::json:
}
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jv.at(JS(limit)));
input.limit = jv.at(JS(limit)).as_int64();
if (jsonObject.contains(JS(marker)))
input.marker = boost::json::value_to<std::string>(jv.at(JS(marker)));

View File

@@ -24,7 +24,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/value.hpp>
@@ -41,7 +40,6 @@
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/jss.h>
#include <cstdint>
#include <string>
#include <vector>
@@ -171,14 +169,14 @@ tag_invoke(boost::json::value_to_tag<AccountOffersHandler::Input>, boost::json::
}
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index)));
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jsonObject.at(JS(ledger_index))));
}
}
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));
input.limit = jsonObject.at(JS(limit)).as_int64();
if (jsonObject.contains(JS(marker)))
input.marker = boost::json::value_to<std::string>(jsonObject.at(JS(marker)));

View File

@@ -167,8 +167,7 @@ AccountTxHandler::process(AccountTxHandler::Input const& input, Context const& c
if (auto const& etlState = etl_->getETLState(); etlState.has_value())
networkID = etlState->networkID;
auto const txnIdx =
util::integralValueAs<uint16_t>(obj[JS(meta)].as_object().at("TransactionIndex"));
auto const txnIdx = obj[JS(meta)].as_object().at("TransactionIndex").as_int64();
if (auto const& ctid = rpc::encodeCTID(txnPlusMeta.ledgerSequence, txnIdx, networkID); ctid)
obj[txKey].as_object()[JS(ctid)] = ctid.value();
}
@@ -246,20 +245,18 @@ tag_invoke(boost::json::value_to_tag<AccountTxHandler::Input>, boost::json::valu
input.account = boost::json::value_to<std::string>(jsonObject.at(JS(account)));
if (jsonObject.contains(JS(ledger_index_min)) &&
util::integralValueAs<int32_t>(jsonObject.at(JS(ledger_index_min))) != -1)
input.ledgerIndexMin = util::integralValueAs<int32_t>(jsonObject.at(JS(ledger_index_min)));
if (jsonObject.contains(JS(ledger_index_min)) && jsonObject.at(JS(ledger_index_min)).as_int64() != -1)
input.ledgerIndexMin = jsonObject.at(JS(ledger_index_min)).as_int64();
if (jsonObject.contains(JS(ledger_index_max)) &&
util::integralValueAs<int32_t>(jsonObject.at(JS(ledger_index_max))) != -1)
input.ledgerIndexMax = util::integralValueAs<int32_t>(jsonObject.at(JS(ledger_index_max)));
if (jsonObject.contains(JS(ledger_index_max)) && jsonObject.at(JS(ledger_index_max)).as_int64() != -1)
input.ledgerIndexMax = jsonObject.at(JS(ledger_index_max)).as_int64();
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))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index)));
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jsonObject.at(JS(ledger_index))));
} else {
@@ -275,12 +272,12 @@ tag_invoke(boost::json::value_to_tag<AccountTxHandler::Input>, boost::json::valu
input.forward = boost::json::value_to<JsonBool>(jsonObject.at(JS(forward)));
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));
input.limit = jsonObject.at(JS(limit)).as_int64();
if (jsonObject.contains(JS(marker))) {
input.marker = AccountTxHandler::Marker{
.ledger = util::integralValueAs<uint32_t>(jsonObject.at(JS(marker)).as_object().at(JS(ledger))),
.seq = util::integralValueAs<uint32_t>(jsonObject.at(JS(marker)).as_object().at(JS(seq)))
.ledger = jsonObject.at(JS(marker)).as_object().at(JS(ledger)).as_int64(),
.seq = jsonObject.at(JS(marker)).as_object().at(JS(seq)).as_int64()
};
}

View File

@@ -25,7 +25,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
@@ -36,7 +35,6 @@
#include <xrpl/protocol/LedgerHeader.h>
#include <xrpl/protocol/jss.h>
#include <cstdint>
#include <string>
#include <vector>
@@ -93,7 +91,7 @@ tag_invoke(boost::json::value_to_tag<BookChangesHandler::Input>, boost::json::va
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jv.at(JS(ledger_index)));
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
}

View File

@@ -23,7 +23,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
@@ -38,7 +37,6 @@
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/jss.h>
#include <cstdint>
#include <string>
namespace rpc {
@@ -124,7 +122,7 @@ tag_invoke(boost::json::value_to_tag<BookOffersHandler::Input>, boost::json::val
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jv.at(JS(ledger_index)));
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
}
@@ -137,7 +135,7 @@ tag_invoke(boost::json::value_to_tag<BookOffersHandler::Input>, boost::json::val
input.domain = boost::json::value_to<std::string>(jv.at(JS(domain)));
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jv.at(JS(limit)));
input.limit = jv.at(JS(limit)).as_int64();
return input;
}

View File

@@ -25,7 +25,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
@@ -43,7 +42,6 @@
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/jss.h>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
@@ -148,7 +146,7 @@ tag_invoke(boost::json::value_to_tag<DepositAuthorizedHandler::Input>, boost::js
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jv.at(JS(ledger_index)));
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
}

View File

@@ -28,7 +28,6 @@
#include "rpc/common/Types.hpp"
#include "rpc/common/Validators.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/value.hpp>
@@ -170,7 +169,7 @@ tag_invoke(boost::json::value_to_tag<FeatureHandler::Input>, boost::json::value
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jv.at(JS(ledger_index)));
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
}

View File

@@ -24,7 +24,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
@@ -251,7 +250,7 @@ tag_invoke(boost::json::value_to_tag<GatewayBalancesHandler::Input>, boost::json
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jv.at(JS(ledger_index)));
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
}

View File

@@ -25,7 +25,6 @@
#include "rpc/common/Types.hpp"
#include "util/AccountUtils.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/asio/spawn.hpp>
#include <boost/bimap/bimap.hpp>
@@ -265,7 +264,7 @@ tag_invoke(boost::json::value_to_tag<GetAggregatePriceHandler::Input>, boost::js
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jv.at(JS(ledger_index)));
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
}
@@ -285,10 +284,10 @@ tag_invoke(boost::json::value_to_tag<GetAggregatePriceHandler::Input>, boost::js
input.quoteAsset = boost::json::value_to<std::string>(jv.at(JS(quote_asset)));
if (jsonObject.contains(JS(trim)))
input.trim = util::integralValueAs<uint8_t>(jv.at(JS(trim)));
input.trim = jv.at(JS(trim)).as_int64();
if (jsonObject.contains(JS(time_threshold)))
input.timeThreshold = util::integralValueAs<uint32_t>(jv.at(JS(time_threshold)));
input.timeThreshold = jv.at(JS(time_threshold)).as_int64();
return input;
}

View File

@@ -131,8 +131,9 @@ public:
!oracle.as_object().contains(JS(account)))
return Error{Status{RippledError::rpcORACLE_MALFORMED}};
auto maybeError =
validation::Type<std::uint32_t, std::string>{}.verify(oracle, JS(oracle_document_id));
auto maybeError = validation::Type<std::uint32_t, std::string>{}.verify(
oracle.as_object(), JS(oracle_document_id)
);
if (!maybeError)
return maybeError;

View File

@@ -24,7 +24,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
@@ -43,7 +42,6 @@
#include <xrpl/protocol/jss.h>
#include <algorithm>
#include <cstdint>
#include <iterator>
#include <string>
#include <utility>
@@ -211,7 +209,7 @@ tag_invoke(boost::json::value_to_tag<LedgerHandler::Input>, boost::json::value c
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jv.at(JS(ledger_index)));
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
}

View File

@@ -25,7 +25,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include "util/LedgerUtils.hpp"
#include "util/log/Logger.hpp"
@@ -45,7 +44,6 @@
#include <algorithm>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
@@ -194,7 +192,7 @@ tag_invoke(boost::json::value_to_tag<LedgerDataHandler::Input>, boost::json::val
}
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));
input.limit = jsonObject.at(JS(limit)).as_int64();
if (jsonObject.contains("out_of_order"))
input.outOfOrder = jsonObject.at("out_of_order").as_bool();
@@ -203,7 +201,7 @@ tag_invoke(boost::json::value_to_tag<LedgerDataHandler::Input>, boost::json::val
if (jsonObject.at(JS(marker)).is_string()) {
input.marker = ripple::uint256{boost::json::value_to<std::string>(jsonObject.at(JS(marker))).data()};
} else {
input.diffMarker = util::integralValueAs<uint32_t>(jsonObject.at(JS(marker)));
input.diffMarker = jsonObject.at(JS(marker)).as_int64();
}
}
@@ -212,7 +210,7 @@ tag_invoke(boost::json::value_to_tag<LedgerDataHandler::Input>, boost::json::val
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index)));
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jsonObject.at(JS(ledger_index))));
}

View File

@@ -26,7 +26,6 @@
#include "rpc/common/Types.hpp"
#include "util/AccountUtils.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
@@ -95,7 +94,7 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input const& input, Context cons
auto const id = util::parseBase58Wrapper<ripple::AccountID>(
boost::json::value_to<std::string>(input.escrow->at(JS(owner)))
);
key = ripple::keylet::escrow(*id, util::integralValueAs<uint32_t>(input.escrow->at(JS(seq)))).key;
key = ripple::keylet::escrow(*id, input.escrow->at(JS(seq)).as_int64()).key;
} else if (input.depositPreauth) {
auto const owner = util::parseBase58Wrapper<ripple::AccountID>(
boost::json::value_to<std::string>(input.depositPreauth->at(JS(owner)))
@@ -129,7 +128,7 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input const& input, Context cons
boost::json::value_to<std::string>(input.ticket->at(JS(account)))
);
key = ripple::getTicketIndex(*id, util::integralValueAs<uint32_t>(input.ticket->at(JS(ticket_seq))));
key = ripple::getTicketIndex(*id, input.ticket->at(JS(ticket_seq)).as_int64());
} else if (input.amm) {
auto const getIssuerFromJson = [](auto const& assetJson) {
// the field check has been done in validator
@@ -183,12 +182,12 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input const& input, Context cons
auto const account = ripple::parseBase58<ripple::AccountID>(
boost::json::value_to<std::string>(input.permissionedDomain->at(JS(account)))
);
auto const seq = util::integralValueAs<uint32_t>(input.permissionedDomain->at(JS(seq)));
auto const seq = input.permissionedDomain->at(JS(seq)).as_int64();
key = ripple::keylet::permissionedDomain(*account, seq).key;
} else if (input.vault) {
auto const account =
ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(input.vault->at(JS(owner))));
auto const seq = util::integralValueAs<uint32_t>(input.vault->at(JS(seq)));
auto const seq = input.vault->at(JS(seq)).as_int64();
key = ripple::keylet::vault(*account, seq).key;
} else if (input.delegate) {
auto const account =
@@ -305,7 +304,7 @@ tag_invoke(boost::json::value_to_tag<LedgerEntryHandler::Input>, boost::json::va
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jv.at(JS(ledger_index)));
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
}

View File

@@ -24,7 +24,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
@@ -39,7 +38,6 @@
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <cstdint>
#include <optional>
#include <string>
@@ -126,14 +124,14 @@ tag_invoke(boost::json::value_to_tag<MPTHoldersHandler::Input>, boost::json::val
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index)));
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(jsonObject.at(JS(ledger_index)).as_string().c_str());
}
}
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));
input.limit = jsonObject.at(JS(limit)).as_int64();
if (jsonObject.contains(JS(marker)))
input.marker = jsonObject.at(JS(marker)).as_string().c_str();

View File

@@ -25,7 +25,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include "util/Profiler.hpp"
#include "util/log/Logger.hpp"
@@ -204,20 +203,18 @@ tag_invoke(boost::json::value_to_tag<NFTHistoryHandler::Input>, boost::json::val
input.nftID = boost::json::value_to<std::string>(jsonObject.at(JS(nft_id)));
if (jsonObject.contains(JS(ledger_index_min)) &&
util::integralValueAs<int32_t>(jsonObject.at(JS(ledger_index_min))) != -1)
input.ledgerIndexMin = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index_min)));
if (jsonObject.contains(JS(ledger_index_min)) && jsonObject.at(JS(ledger_index_min)).as_int64() != -1)
input.ledgerIndexMin = jsonObject.at(JS(ledger_index_min)).as_int64();
if (jsonObject.contains(JS(ledger_index_max)) &&
util::integralValueAs<int32_t>(jsonObject.at(JS(ledger_index_max))) != -1)
input.ledgerIndexMax = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index_max)));
if (jsonObject.contains(JS(ledger_index_max)) && jsonObject.at(JS(ledger_index_max)).as_int64() != -1)
input.ledgerIndexMax = jsonObject.at(JS(ledger_index_max)).as_int64();
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))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index)));
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jsonObject.at(JS(ledger_index))));
}
@@ -230,12 +227,12 @@ tag_invoke(boost::json::value_to_tag<NFTHistoryHandler::Input>, boost::json::val
input.forward = jsonObject.at(JS(forward)).as_bool();
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));
input.limit = jsonObject.at(JS(limit)).as_int64();
if (jsonObject.contains(JS(marker))) {
input.marker = NFTHistoryHandler::Marker{
.ledger = util::integralValueAs<uint32_t>(jsonObject.at(JS(marker)).as_object().at(JS(ledger))),
.seq = util::integralValueAs<uint32_t>(jsonObject.at(JS(marker)).as_object().at(JS(seq)))
.ledger = jsonObject.at(JS(marker)).as_object().at(JS(ledger)).as_int64(),
.seq = jsonObject.at(JS(marker)).as_object().at(JS(seq)).as_int64()
};
}

View File

@@ -24,7 +24,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
@@ -37,7 +36,6 @@
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol/nft.h>
#include <cstdint>
#include <string>
using namespace ripple;
@@ -118,7 +116,7 @@ tag_invoke(boost::json::value_to_tag<NFTInfoHandler::Input>, boost::json::value
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index)));
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jsonObject.at(JS(ledger_index))));
}

View File

@@ -24,7 +24,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/asio/spawn.hpp>
#include <boost/json/conversion.hpp>
@@ -196,7 +195,7 @@ tag_invoke(boost::json::value_to_tag<NFTOffersHandlerBase::Input>, boost::json::
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index)));
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jsonObject.at(JS(ledger_index))));
}
@@ -206,7 +205,7 @@ tag_invoke(boost::json::value_to_tag<NFTOffersHandlerBase::Input>, boost::json::
input.marker = boost::json::value_to<std::string>(jsonObject.at(JS(marker)));
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));
input.limit = jsonObject.at(JS(limit)).as_int64();
return input;
}

View File

@@ -24,7 +24,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
@@ -38,7 +37,6 @@
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol/nft.h>
#include <cstdint>
#include <optional>
#include <string>
@@ -138,17 +136,17 @@ tag_invoke(boost::json::value_to_tag<NFTsByIssuerHandler::Input>, boost::json::v
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index)));
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jsonObject.at(JS(ledger_index))));
}
}
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));
input.limit = jsonObject.at(JS(limit)).as_int64();
if (jsonObject.contains(JS(nft_taxon)))
input.nftTaxon = util::integralValueAs<uint32_t>(jsonObject.at(JS(nft_taxon)));
input.nftTaxon = jsonObject.at(JS(nft_taxon)).as_int64();
if (jsonObject.contains(JS(marker)))
input.marker = boost::json::value_to<std::string>(jsonObject.at(JS(marker)));

View File

@@ -25,7 +25,6 @@
#include "rpc/common/JsonBool.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
@@ -188,7 +187,7 @@ tag_invoke(boost::json::value_to_tag<NoRippleCheckHandler::Input>, boost::json::
input.roleGateway = jsonObject.at(JS(role)).as_string() == "gateway";
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));
input.limit = jsonObject.at(JS(limit)).as_int64();
if (jsonObject.contains(JS(transactions)))
input.transactions = boost::json::value_to<JsonBool>(jsonObject.at(JS(transactions)));
@@ -198,7 +197,7 @@ tag_invoke(boost::json::value_to_tag<NoRippleCheckHandler::Input>, boost::json::
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index)));
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jsonObject.at(JS(ledger_index))));
}

View File

@@ -24,7 +24,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/value.hpp>
@@ -34,7 +33,6 @@
#include <xrpl/basics/strHex.h>
#include <xrpl/protocol/jss.h>
#include <cstdint>
#include <string>
#include <utility>
@@ -112,7 +110,7 @@ tag_invoke(boost::json::value_to_tag<TransactionEntryHandler::Input>, boost::jso
if (jsonObject.contains(JS(ledger_index))) {
if (!jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jv.at(JS(ledger_index)));
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
}

View File

@@ -332,10 +332,10 @@ private:
input.binary = boost::json::value_to<JsonBool>(jsonObject.at(JS(binary)));
if (jsonObject.contains(JS(min_ledger)))
input.minLedger = util::integralValueAs<uint32_t>(jv.at(JS(min_ledger)));
input.minLedger = jv.at(JS(min_ledger)).as_int64();
if (jsonObject.contains(JS(max_ledger)))
input.maxLedger = util::integralValueAs<uint32_t>(jv.at(JS(max_ledger)));
input.maxLedger = jv.at(JS(max_ledger)).as_int64();
return input;
}

View File

@@ -25,7 +25,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
@@ -172,14 +171,14 @@ tag_invoke(boost::json::value_to_tag<VaultInfoHandler::Input>, boost::json::valu
input.owner = jsonObject.at(JS(owner)).as_string();
if (jsonObject.contains(JS(seq)))
input.tnxSequence = util::integralValueAs<uint32_t>(jsonObject.at(JS(seq)));
input.tnxSequence = static_cast<uint32_t>(jsonObject.at(JS(seq)).as_int64());
if (jsonObject.contains(JS(vault_id)))
input.vaultID = jsonObject.at(JS(vault_id)).as_string();
if (jsonObject.contains(JS(ledger_index))) {
if (not jsonObject.at(JS(ledger_index)).is_string()) {
input.ledgerIndex = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index)));
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
input.ledgerIndex = std::stoi(jsonObject.at(JS(ledger_index)).as_string().c_str());
}

View File

@@ -24,8 +24,6 @@
#include <algorithm>
#include <cctype>
#include <concepts>
#include <stdexcept>
#include <string>
/**
@@ -88,26 +86,4 @@ 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
* @throws logic_error if the underlying number is neither int64 nor uint64
*/
template <std::integral Type>
Type
integralValueAs(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());
throw std::logic_error("Value neither uint64 nor int64");
}
} // namespace util

View File

@@ -30,6 +30,7 @@
#include <utility>
namespace util {
namespace impl {
class SignalsHandlerStatic {

View File

@@ -37,10 +37,9 @@
#include <optional>
namespace util {
namespace impl {
class SignalsHandlerStatic;
} // namespace impl
/**

View File

@@ -41,7 +41,6 @@
#include <spdlog/spdlog.h>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <filesystem>

View File

@@ -19,7 +19,6 @@
#pragma once
#include "data/LedgerCacheInterface.hpp"
#include "util/Taggable.hpp"
#include "web/AdminVerificationStrategy.hpp"
#include "web/PlainWsSession.hpp"
@@ -70,7 +69,6 @@ public:
* @param tagFactory A factory that is used to generate tags to track requests and sessions
* @param dosGuard The denial of service guard to use
* @param handler The server handler to use
* @param cache The ledger cache to use
* @param buffer Buffer with initial data received from the peer
* @param maxWsSendingQueueSize The maximum size of the sending queue for websocket
*/
@@ -82,7 +80,6 @@ public:
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
std::shared_ptr<HandlerType> const& handler,
std::reference_wrapper<data::LedgerCacheInterface const> cache,
boost::beast::flat_buffer buffer,
std::uint32_t maxWsSendingQueueSize
)
@@ -93,7 +90,6 @@ public:
std::move(proxyIpResolver),
dosGuard,
handler,
cache,
std::move(buffer)
)
, stream_(std::move(socket))

View File

@@ -19,7 +19,6 @@
#pragma once
#include "data/LedgerCacheInterface.hpp"
#include "util/Taggable.hpp"
#include "util/log/Logger.hpp"
#include "web/AdminVerificationStrategy.hpp"
@@ -86,7 +85,6 @@ class Detector : public std::enable_shared_from_this<Detector<PlainSessionType,
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory_;
std::reference_wrapper<dosguard::DOSGuardInterface> const dosGuard_;
std::shared_ptr<HandlerType> const handler_;
std::reference_wrapper<data::LedgerCacheInterface const> cache_;
boost::beast::flat_buffer buffer_;
std::shared_ptr<AdminVerificationStrategy> const adminVerification_;
std::uint32_t maxWsSendingQueueSize_;
@@ -101,7 +99,6 @@ public:
* @param tagFactory A factory that is used to generate tags to track requests and sessions
* @param dosGuard The denial of service guard to use
* @param handler The server handler to use
* @param cache The ledger cache to use
* @param adminVerification The admin verification strategy to use
* @param maxWsSendingQueueSize The maximum size of the sending queue for websocket
* @param proxyIpResolver The client ip resolver if a request was forwarded by a proxy
@@ -112,7 +109,6 @@ public:
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
std::shared_ptr<HandlerType> handler,
std::reference_wrapper<data::LedgerCacheInterface const> cache,
std::shared_ptr<AdminVerificationStrategy> adminVerification,
std::uint32_t maxWsSendingQueueSize,
std::shared_ptr<ProxyIpResolver> proxyIpResolver
@@ -122,7 +118,6 @@ public:
, tagFactory_(std::cref(tagFactory))
, dosGuard_(dosGuard)
, handler_(std::move(handler))
, cache_(cache)
, adminVerification_(std::move(adminVerification))
, maxWsSendingQueueSize_(maxWsSendingQueueSize)
, proxyIpResolver_(std::move(proxyIpResolver))
@@ -184,7 +179,6 @@ public:
tagFactory_,
dosGuard_,
handler_,
cache_,
std::move(buffer_),
maxWsSendingQueueSize_
)
@@ -200,7 +194,6 @@ public:
tagFactory_,
dosGuard_,
handler_,
cache_,
std::move(buffer_),
maxWsSendingQueueSize_
)
@@ -230,7 +223,6 @@ class Server : public std::enable_shared_from_this<Server<PlainSessionType, SslS
util::TagDecoratorFactory tagFactory_;
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard_;
std::shared_ptr<HandlerType> handler_;
std::reference_wrapper<data::LedgerCacheInterface const> cache_;
tcp::acceptor acceptor_;
std::shared_ptr<AdminVerificationStrategy> adminVerification_;
std::uint32_t maxWsSendingQueueSize_;
@@ -246,7 +238,6 @@ public:
* @param tagFactory A factory that is used to generate tags to track requests and sessions
* @param dosGuard The denial of service guard to use
* @param handler The server handler to use
* @param cache The ledger cache to use
* @param adminVerification The admin verification strategy to use
* @param maxWsSendingQueueSize The maximum size of the sending queue for websocket
* @param proxyIpResolver The client ip resolver if a request was forwarded by a proxy
@@ -258,7 +249,6 @@ public:
util::TagDecoratorFactory tagFactory,
dosguard::DOSGuardInterface& dosGuard,
std::shared_ptr<HandlerType> handler,
std::reference_wrapper<data::LedgerCacheInterface const> cache,
std::shared_ptr<AdminVerificationStrategy> adminVerification,
std::uint32_t maxWsSendingQueueSize,
ProxyIpResolver proxyIpResolver
@@ -268,7 +258,6 @@ public:
, tagFactory_(tagFactory)
, dosGuard_(std::ref(dosGuard))
, handler_(std::move(handler))
, cache_(cache)
, acceptor_(boost::asio::make_strand(ioc))
, adminVerification_(std::move(adminVerification))
, maxWsSendingQueueSize_(maxWsSendingQueueSize)
@@ -331,7 +320,6 @@ private:
std::cref(tagFactory_),
dosGuard_,
handler_,
cache_,
adminVerification_,
maxWsSendingQueueSize_,
proxyIpResolver_
@@ -355,7 +343,6 @@ using HttpServer = Server<HttpSession, SslHttpSession, HandlerType>;
* @param ioc The server will run under this io_context
* @param dosGuard The dos guard to protect the server
* @param handler The handler to process the request
* @param cache The ledger cache to use
* @return The server instance
*/
template <typename HandlerType>
@@ -364,8 +351,7 @@ makeHttpServer(
util::config::ClioConfigDefinition const& config,
boost::asio::io_context& ioc,
dosguard::DOSGuardInterface& dosGuard,
std::shared_ptr<HandlerType> const& handler,
std::reference_wrapper<data::LedgerCacheInterface const> cache
std::shared_ptr<HandlerType> const& handler
)
{
static util::Logger const log{"WebServer"}; // NOLINT(readability-identifier-naming)
@@ -399,7 +385,6 @@ makeHttpServer(
util::TagDecoratorFactory(config),
dosGuard,
handler,
cache,
std::move(expectedAdminVerification).value(),
maxWsSendingQueueSize,
std::move(proxyIpResolver)

View File

@@ -19,7 +19,6 @@
#pragma once
#include "data/LedgerCacheInterface.hpp"
#include "util/Taggable.hpp"
#include "web/AdminVerificationStrategy.hpp"
#include "web/ProxyIpResolver.hpp"
@@ -77,7 +76,6 @@ public:
* @param tagFactory A factory that is used to generate tags to track requests and sessions
* @param dosGuard The denial of service guard to use
* @param handler The server handler to use
* @param cache The ledger cache to use
* @param buffer Buffer with initial data received from the peer
* @param maxWsSendingQueueSize The maximum size of the sending queue for websocket
*/
@@ -90,7 +88,6 @@ public:
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
std::shared_ptr<HandlerType> const& handler,
std::reference_wrapper<data::LedgerCacheInterface const> cache,
boost::beast::flat_buffer buffer,
std::uint32_t maxWsSendingQueueSize
)
@@ -101,7 +98,6 @@ public:
std::move(proxyIpResolver),
dosGuard,
handler,
cache,
std::move(buffer)
)
, stream_(std::move(socket), ctx)

View File

@@ -19,7 +19,6 @@
#pragma once
#include "data/LedgerCacheInterface.hpp"
#include "rpc/Errors.hpp"
#include "util/Assert.hpp"
#include "util/Taggable.hpp"
@@ -72,22 +71,6 @@ static constexpr auto kHEALTH_CHECK_HTML = R"html(
</html>
)html";
static constexpr auto kCACHE_CHECK_LOADED_HTML = R"html(
<!DOCTYPE html>
<html>
<head><title>Cache state</title></head>
<body><h1>Cache state</h1><p>Cache is fully loaded</p></body>
</html>
)html";
static constexpr auto kCACHE_CHECK_NOT_LOADED_HTML = R"html(
<!DOCTYPE html>
<html>
<head><title>Cache state</title></head>
<body><h1>Cache state</h1><p>Cache is not yet loaded</p></body>
</html>
)html";
using tcp = boost::asio::ip::tcp;
/**
@@ -145,7 +128,6 @@ protected:
http::request<http::string_body> req_;
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard_;
std::shared_ptr<HandlerType> const handler_;
std::reference_wrapper<data::LedgerCacheInterface const> cache_;
util::Logger log_{"WebServer"};
util::Logger perfLog_{"Performance"};
@@ -187,7 +169,6 @@ public:
std::shared_ptr<ProxyIpResolver> proxyIpResolver,
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
std::shared_ptr<HandlerType> handler,
std::reference_wrapper<data::LedgerCacheInterface const> cache,
boost::beast::flat_buffer buffer
)
: ConnectionBase(tagFactory, ip)
@@ -197,7 +178,6 @@ public:
, buffer_(std::move(buffer))
, dosGuard_(dosGuard)
, handler_(std::move(handler))
, cache_(cache)
{
LOG(perfLog_.debug()) << tag() << "http session created";
dosGuard_.get().increment(ip);
@@ -242,13 +222,6 @@ public:
if (req_.method() == http::verb::get and req_.target() == "/health")
return sender_(httpResponse(http::status::ok, "text/html", kHEALTH_CHECK_HTML));
if (req_.method() == http::verb::get and req_.target() == "/cache_state") {
if (cache_.get().isFull())
return sender_(httpResponse(http::status::ok, "text/html", kCACHE_CHECK_LOADED_HTML));
return sender_(httpResponse(http::status::service_unavailable, "text/html", kCACHE_CHECK_NOT_LOADED_HTML));
}
if (auto resolvedIp = proxyIpResolver_->resolveClientIp(clientIp_, req_); resolvedIp != clientIp_) {
LOG(log_.info()) << tag() << "Detected a forwarded request from proxy. Proxy ip: " << clientIp_
<< ". Resolved client ip: " << resolvedIp;

View File

@@ -46,6 +46,7 @@
#include <openssl/tls1.h>
#include <chrono>
#include <optional>
#include <string>
#include <string_view>
#include <utility>

View File

@@ -1528,66 +1528,6 @@ createMPTIssuanceCreateTxWithMetadata(std::string_view accountId, uint32_t fee,
return ret;
}
ripple::STObject
createMPTokenAuthorizeTx(
std::string_view accountId,
ripple::uint192 const& mptIssuanceID,
uint32_t fee,
uint32_t seq,
std::optional<std::string_view> holder,
std::optional<std::uint32_t> flags
)
{
ripple::STObject tx(ripple::sfTransaction);
tx.setFieldU16(ripple::sfTransactionType, ripple::ttMPTOKEN_AUTHORIZE);
tx.setAccountID(ripple::sfAccount, getAccountIdWithString(accountId));
tx[ripple::sfMPTokenIssuanceID] = mptIssuanceID;
tx.setFieldAmount(ripple::sfFee, ripple::STAmount(fee, false));
tx.setFieldU32(ripple::sfSequence, seq);
tx.setFieldVL(ripple::sfSigningPubKey, kSLICE);
if (holder)
tx.setAccountID(ripple::sfHolder, getAccountIdWithString(*holder));
if (flags)
tx.setFieldU32(ripple::sfFlags, *flags);
return tx;
}
data::TransactionAndMetadata
createMPTokenAuthorizeTxWithMetadata(
std::string_view accountId,
ripple::uint192 const& mptIssuanceID,
uint32_t fee,
uint32_t seq
)
{
ripple::STObject const tx = createMPTokenAuthorizeTx(accountId, mptIssuanceID, fee, seq);
ripple::STObject metaObj(ripple::sfTransactionMetaData);
metaObj.setFieldU8(ripple::sfTransactionResult, ripple::tesSUCCESS);
metaObj.setFieldU32(ripple::sfTransactionIndex, 0);
ripple::STObject finalFields(ripple::sfFinalFields);
finalFields.setFieldU16(ripple::sfLedgerEntryType, ripple::ltMPTOKEN);
finalFields[ripple::sfMPTokenIssuanceID] = mptIssuanceID;
finalFields.setFieldU64(ripple::sfMPTAmount, 0);
ripple::STObject modifiedNode(ripple::sfModifiedNode);
modifiedNode.setFieldU16(ripple::sfLedgerEntryType, ripple::ltMPTOKEN);
modifiedNode.setFieldH256(ripple::sfLedgerIndex, ripple::uint256{});
modifiedNode.emplace_back(std::move(finalFields));
ripple::STArray affectedNodes(ripple::sfAffectedNodes);
affectedNodes.push_back(std::move(modifiedNode));
metaObj.setFieldArray(ripple::sfAffectedNodes, affectedNodes);
data::TransactionAndMetadata ret;
ret.transaction = tx.getSerializer().peekData();
ret.metadata = metaObj.getSerializer().peekData();
return ret;
}
ripple::STObject
createPermissionedDomainObject(
std::string_view accountId,

View File

@@ -462,26 +462,6 @@ createMPTIssuanceCreateTx(std::string_view accountId, uint32_t fee, uint32_t seq
[[nodiscard]] data::TransactionAndMetadata
createMPTIssuanceCreateTxWithMetadata(std::string_view accountId, uint32_t fee, uint32_t seq);
[[nodiscard]]
ripple::STObject
createMPTokenAuthorizeTx(
std::string_view accountId,
ripple::uint192 const& mptIssuanceID,
uint32_t fee,
uint32_t seq,
std::optional<std::string_view> holder = std::nullopt,
std::optional<std::uint32_t> flags = std::nullopt
);
[[nodiscard]]
data::TransactionAndMetadata
createMPTokenAuthorizeTxWithMetadata(
std::string_view accountId,
ripple::uint192 const& mptIssuanceID,
uint32_t fee,
uint32_t seq
);
[[nodiscard]] ripple::STObject
createPermissionedDomainObject(
std::string_view accountId,

View File

@@ -17,7 +17,6 @@
*/
//==============================================================================
#include "util/LoggerFixtures.hpp"
#include "util/TerminationHandler.hpp"
#include <TestGlobals.hpp>
@@ -34,8 +33,6 @@ main(int argc, char* argv[])
{
util::setTerminationHandler();
testing::InitGoogleTest(&argc, argv);
LoggerFixture::init();
TestGlobals::instance().parse(argc, argv);
return RUN_ALL_TESTS();

View File

@@ -19,6 +19,7 @@
#include "data/cassandra/Handle.hpp"
#include "data/cassandra/Types.hpp"
#include "util/LoggerFixtures.hpp"
#include <TestGlobals.hpp>
#include <cassandra.h>

View File

@@ -33,6 +33,7 @@
#include "migration/impl/MigrationManagerBase.hpp"
#include "migration/impl/MigratorsRegister.hpp"
#include "util/CassandraDBHelper.hpp"
#include "util/LoggerFixtures.hpp"
#include "util/MockPrometheus.hpp"
#include "util/config/ConfigConstraints.hpp"
#include "util/config/ConfigDefinition.hpp"

View File

@@ -17,8 +17,6 @@
*/
//==============================================================================
#include "util/CassandraDBHelper.hpp"
#include "data/cassandra/Handle.hpp"
#include "data/cassandra/Types.hpp"
#include "data/cassandra/impl/Result.hpp"

View File

@@ -19,6 +19,7 @@
#pragma once
#include "data/cassandra/Handle.hpp"
#include "data/cassandra/Types.hpp"
#include <cstdint>

View File

@@ -20,13 +20,8 @@
#include "util/JsonUtils.hpp"
#include <boost/json/parse.hpp>
#include <boost/json/value.hpp>
#include <gtest/gtest.h>
#include <cstdint>
#include <limits>
#include <stdexcept>
TEST(JsonUtils, RemoveSecrets)
{
auto json = boost::json::parse(R"JSON({
@@ -65,26 +60,3 @@ TEST(JsonUtils, RemoveSecrets)
EXPECT_EQ(json2.at("seed_hex").as_string(), "*");
EXPECT_EQ(json2.at("passphrase").as_string(), "*");
}
TEST(JsonUtils, integralValueAs)
{
auto const expectedResultUint64 = static_cast<uint64_t>(std::numeric_limits<int32_t>::max()) + 1u;
auto const uint64Json = boost::json::value(expectedResultUint64);
EXPECT_EQ(util::integralValueAs<int32_t>(uint64Json), std::numeric_limits<int32_t>::min());
EXPECT_EQ(util::integralValueAs<uint32_t>(uint64Json), expectedResultUint64);
EXPECT_EQ(util::integralValueAs<int64_t>(uint64Json), expectedResultUint64);
EXPECT_EQ(util::integralValueAs<uint64_t>(uint64Json), expectedResultUint64);
auto const expectedResultInt64 = static_cast<int64_t>(std::numeric_limits<int32_t>::max()) + 1u;
auto const int64Json = boost::json::value(expectedResultInt64);
EXPECT_EQ(util::integralValueAs<int32_t>(int64Json), std::numeric_limits<int32_t>::min());
EXPECT_EQ(util::integralValueAs<uint32_t>(int64Json), expectedResultInt64);
EXPECT_EQ(util::integralValueAs<int64_t>(int64Json), expectedResultInt64);
EXPECT_EQ(util::integralValueAs<uint64_t>(int64Json), expectedResultInt64);
auto const doubleJson = boost::json::value(3.14);
EXPECT_THROW(util::integralValueAs<int>(doubleJson), std::logic_error);
auto const stringJson = boost::json::value("not a number");
EXPECT_THROW(util::integralValueAs<int>(stringJson), std::logic_error);
}

View File

@@ -18,6 +18,7 @@
//==============================================================================
#include "app/Stopper.hpp"
#include "util/AsioContextTestFixture.hpp"
#include "util/LoggerFixtures.hpp"
#include "util/MockBackend.hpp"
#include "util/MockETLService.hpp"
#include "util/MockLoadBalancer.hpp"

View File

@@ -20,7 +20,7 @@
#include "app/WebHandlers.hpp"
#include "rpc/Errors.hpp"
#include "util/AsioContextTestFixture.hpp"
#include "util/MockLedgerCache.hpp"
#include "util/LoggerFixtures.hpp"
#include "util/MockPrometheus.hpp"
#include "util/Taggable.hpp"
#include "util/config/ConfigDefinition.hpp"
@@ -150,32 +150,6 @@ TEST_F(HealthCheckHandlerTests, Call)
});
}
struct CacheStateHandlerTests : SyncAsioContextTest, WebHandlersTest {
web::ng::Request request{http::request<http::string_body>{http::verb::get, "/", 11}};
MockLedgerCache cache;
CacheStateHandler cacheStateHandler{cache};
};
TEST_F(CacheStateHandlerTests, CallWithCacheLoaded)
{
EXPECT_CALL(cache, isFull()).WillRepeatedly(testing::Return(true));
runSpawn([&](boost::asio::yield_context yield) {
auto response = cacheStateHandler(request, connectionMock, nullptr, yield);
auto const httpResponse = std::move(response).intoHttpResponse();
EXPECT_EQ(httpResponse.result(), boost::beast::http::status::ok);
});
}
TEST_F(CacheStateHandlerTests, CallWithoutCacheLoaded)
{
EXPECT_CALL(cache, isFull()).WillRepeatedly(testing::Return(false));
runSpawn([&](boost::asio::yield_context yield) {
auto response = cacheStateHandler(request, connectionMock, nullptr, yield);
auto const httpResponse = std::move(response).intoHttpResponse();
EXPECT_EQ(httpResponse.result(), boost::beast::http::status::service_unavailable);
});
}
struct RequestHandlerTest : SyncAsioContextTest, WebHandlersTest {
AdminVerificationStrategyStrictMockPtr adminVerifier{
std::make_shared<testing::StrictMock<AdminVerificationStrategyMock>>()

View File

@@ -18,6 +18,7 @@
//==============================================================================
#include "data/LedgerCache.hpp"
#include "util/LoggerFixtures.hpp"
#include "util/MockPrometheus.hpp"
#include "util/prometheus/Bool.hpp"

Some files were not shown because too many files have changed in this diff Show More