Compare commits

..

6 Commits

Author SHA1 Message Date
Alex Kremer
d053a1f8c8 Merge branch 'develop' into release/2.6.0 2025-10-20 14:05:37 +01:00
Alex Kremer
c12601c0f5 Merge branch 'develop' into release/2.6.0 2025-10-09 16:58:32 +01:00
Alex Kremer
381009807f Merge remote-tracking branch 'origin/develop' into release/2.6.0 2025-10-08 13:52:49 +01:00
Alex Kremer
f530216688 Merge branch 'develop' into release/2.6.0 2025-10-03 16:30:41 +01:00
Alex Kremer
0cccb1a2c0 Merge branch 'develop' into release/2.6.0 2025-10-01 13:51:22 +01:00
Alex Kremer
1803990aa8 Use xrpl/2.6.1-rc1 2025-09-18 17:07:59 +01:00
499 changed files with 14783 additions and 18457 deletions

View File

@@ -49,7 +49,6 @@ IndentFunctionDeclarationAfterType: false
IndentWidth: 4
IndentWrappedFunctionNames: false
IndentRequiresClause: true
InsertNewlineAtEOF: true
RequiresClausePosition: OwnLine
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 1

View File

@@ -54,7 +54,7 @@ format:
_help_max_pargs_hwrap:
- If a positional argument group contains more than this many
- arguments, then force it to a vertical layout.
max_pargs_hwrap: 5
max_pargs_hwrap: 6
_help_max_rows_cmdline:
- If a cmdline positional group consumes more than this many
- lines without nesting, then invalidate the layout (and nest)

View File

@@ -5,27 +5,25 @@ inputs:
targets:
description: Space-separated build target names
default: all
nproc_subtract:
description: The number of processors to subtract when calculating parallelism.
subtract_threads:
description: An option for the action get-threads-number.
required: true
default: "0"
runs:
using: composite
steps:
- name: Get number of processors
uses: XRPLF/actions/get-nproc@cf0433aa74563aead044a1e395610c96d65a37cf
id: nproc
- name: Get number of threads
uses: ./.github/actions/get-threads-number
id: number_of_threads
with:
subtract: ${{ inputs.nproc_subtract }}
subtract_threads: ${{ inputs.subtract_threads }}
- name: Build targets
shell: bash
env:
CMAKE_TARGETS: ${{ inputs.targets }}
run: |
cd build
cmake \
--build . \
--parallel "${{ steps.nproc.outputs.nproc }}" \
--target ${CMAKE_TARGETS}
--parallel "${{ steps.number_of_threads.outputs.threads_number }}" \
--target ${{ inputs.targets }}

View File

@@ -34,32 +34,32 @@ runs:
steps:
- name: Login to DockerHub
if: ${{ inputs.push_image == 'true' && inputs.dockerhub_repo != '' }}
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ env.DOCKERHUB_USER }}
password: ${{ env.DOCKERHUB_PW }}
- name: Login to GitHub Container Registry
if: ${{ inputs.push_image == 'true' }}
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ env.GITHUB_TOKEN }}
- uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
- uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
with:
cache-image: false
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
- uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
id: meta
with:
images: ${{ inputs.images }}
tags: ${{ inputs.tags }}
- name: Build and push
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: ${{ inputs.directory }}
platforms: ${{ inputs.platforms }}

View File

@@ -1,41 +0,0 @@
name: Cache key
description: Generate cache key for ccache
inputs:
conan_profile:
description: Conan profile name
required: true
build_type:
description: Current build type (e.g. Release, Debug)
required: true
default: Release
code_coverage:
description: Whether code coverage is on
required: true
default: "false"
outputs:
key:
description: Generated cache key for ccache
value: ${{ steps.key_without_commit.outputs.key }}-${{ steps.git_common_ancestor.outputs.commit }}
restore_keys:
description: Cache restore keys for fallback
value: ${{ steps.key_without_commit.outputs.key }}
runs:
using: composite
steps:
- name: Find common commit
id: git_common_ancestor
uses: ./.github/actions/git-common-ancestor
- name: Set cache key without commit
id: key_without_commit
shell: bash
env:
RUNNER_OS: ${{ runner.os }}
BUILD_TYPE: ${{ inputs.build_type }}
CODE_COVERAGE: ${{ inputs.code_coverage == 'true' && '-code_coverage' || '' }}
CONAN_PROFILE: ${{ inputs.conan_profile }}
run: |
echo "key=clio-ccache-${RUNNER_OS}-${BUILD_TYPE}${CODE_COVERAGE}-${CONAN_PROFILE}-develop" >> "${GITHUB_OUTPUT}"

View File

@@ -37,10 +37,6 @@ inputs:
description: Whether to generate Debian package
required: true
default: "false"
version:
description: Version of the clio_server binary
required: false
default: ""
runs:
using: composite
@@ -48,7 +44,6 @@ runs:
- name: Run cmake
shell: bash
env:
BUILD_DIR: "${{ inputs.build_dir }}"
BUILD_TYPE: "${{ inputs.build_type }}"
SANITIZER_OPTION: |-
${{ endsWith(inputs.conan_profile, '.asan') && '-Dsan=address' ||
@@ -61,22 +56,9 @@ runs:
STATIC: "${{ inputs.static == 'true' && 'ON' || 'OFF' }}"
TIME_TRACE: "${{ inputs.time_trace == 'true' && 'ON' || 'OFF' }}"
PACKAGE: "${{ inputs.package == 'true' && 'ON' || 'OFF' }}"
# GitHub creates a merge commit for a PR
# https://www.kenmuse.com/blog/the-many-shas-of-a-github-pull-request/
#
# We:
# - explicitly provide branch name
# - use `github.head_ref` to get the SHA of last commit in the PR branch
#
# This way it works both for PRs and pushes to branches.
GITHUB_BRANCH_NAME: "${{ github.head_ref || github.ref_name }}"
GITHUB_HEAD_SHA: "${{ github.event.pull_request.head.sha || github.sha }}"
#
# If tag is being pushed, or it's a nightly release, we use that version.
FORCE_CLIO_VERSION: ${{ inputs.version }}
run: |
cmake \
-B "${BUILD_DIR}" \
-B ${{inputs.build_dir}} \
-S . \
-G Ninja \
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \

View File

@@ -24,7 +24,7 @@ runs:
-j8 --exclude-throw-branches
- name: Archive coverage report
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@v4
with:
name: coverage-report.xml
path: build/coverage_report.xml

View File

@@ -21,17 +21,18 @@ inputs:
runs:
using: composite
steps:
- name: Create build directory
shell: bash
run: mkdir -p "${{ inputs.build_dir }}"
- name: Run conan
shell: bash
env:
BUILD_DIR: "${{ inputs.build_dir }}"
CONAN_BUILD_OPTION: "${{ inputs.force_conan_source_build == 'true' && '*' || 'missing' }}"
BUILD_TYPE: "${{ inputs.build_type }}"
CONAN_PROFILE: "${{ inputs.conan_profile }}"
run: |
conan \
install . \
-of "${BUILD_DIR}" \
-b "${CONAN_BUILD_OPTION}" \
-s "build_type=${BUILD_TYPE}" \
--profile:all "${CONAN_PROFILE}"
-of build \
-b "$CONAN_BUILD_OPTION" \
-s "build_type=${{ inputs.build_type }}" \
--profile:all "${{ inputs.conan_profile }}"

View File

@@ -28,17 +28,12 @@ runs:
- name: Create an issue
id: create_issue
shell: bash
env:
ISSUE_BODY: ${{ inputs.body }}
ISSUE_ASSIGNEES: ${{ inputs.assignees }}
ISSUE_LABELS: ${{ inputs.labels }}
ISSUE_TITLE: ${{ inputs.title }}
run: |
echo -e "${ISSUE_BODY}" > issue.md
echo -e '${{ inputs.body }}' > issue.md
gh issue create \
--assignee "${ISSUE_ASSIGNEES}" \
--label "${ISSUE_LABELS}" \
--title "${ISSUE_TITLE}" \
--assignee '${{ inputs.assignees }}' \
--label '${{ inputs.labels }}' \
--title '${{ inputs.title }}' \
--body-file ./issue.md \
> create_issue.log
created_issue="$(sed 's|.*/||' create_issue.log)"

View File

@@ -0,0 +1,36 @@
name: Get number of threads
description: Determines number of threads to use on macOS and Linux
inputs:
subtract_threads:
description: How many threads to subtract from the calculated number
required: true
default: "0"
outputs:
threads_number:
description: Number of threads to use
value: ${{ steps.number_of_threads_export.outputs.num }}
runs:
using: composite
steps:
- name: Get number of threads on mac
id: mac_threads
if: ${{ runner.os == 'macOS' }}
shell: bash
run: echo "num=$(($(sysctl -n hw.logicalcpu) - 2))" >> $GITHUB_OUTPUT
- name: Get number of threads on Linux
id: linux_threads
if: ${{ runner.os == 'Linux' }}
shell: bash
run: echo "num=$(($(nproc) - 2))" >> $GITHUB_OUTPUT
- name: Shift and export number of threads
id: number_of_threads_export
shell: bash
run: |
num_of_threads="${{ steps.mac_threads.outputs.num || steps.linux_threads.outputs.num }}"
shift_by="${{ inputs.subtract_threads }}"
shifted="$((num_of_threads - shift_by))"
echo "num=$(( shifted > 1 ? shifted : 1 ))" >> $GITHUB_OUTPUT

View File

@@ -0,0 +1,38 @@
name: Restore cache
description: Find and restores ccache cache
inputs:
conan_profile:
description: Conan profile name
required: true
ccache_dir:
description: Path to .ccache directory
required: true
build_type:
description: Current build type (e.g. Release, Debug)
required: true
default: Release
code_coverage:
description: Whether code coverage is on
required: true
default: "false"
outputs:
ccache_cache_hit:
description: True if ccache cache has been downloaded
value: ${{ steps.ccache_cache.outputs.cache-hit }}
runs:
using: composite
steps:
- name: Find common commit
id: git_common_ancestor
uses: ./.github/actions/git-common-ancestor
- name: Restore ccache cache
uses: actions/cache/restore@v4
id: ccache_cache
if: ${{ env.CCACHE_DISABLE != '1' }}
with:
path: ${{ inputs.ccache_dir }}
key: clio-ccache-${{ runner.os }}-${{ inputs.build_type }}${{ inputs.code_coverage == 'true' && '-code_coverage' || '' }}-${{ inputs.conan_profile }}-develop-${{ steps.git_common_ancestor.outputs.commit }}

38
.github/actions/save-cache/action.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
name: Save cache
description: Save ccache cache for develop branch
inputs:
conan_profile:
description: Conan profile name
required: true
ccache_dir:
description: Path to .ccache directory
required: true
build_type:
description: Current build type (e.g. Release, Debug)
required: true
default: Release
code_coverage:
description: Whether code coverage is on
required: true
default: "false"
ccache_cache_hit:
description: Whether ccache cache has been downloaded
required: true
ccache_cache_miss_rate:
description: How many ccache cache misses happened
runs:
using: composite
steps:
- name: Find common commit
id: git_common_ancestor
uses: ./.github/actions/git-common-ancestor
- name: Save ccache cache
if: ${{ inputs.ccache_cache_hit != 'true' || inputs.ccache_cache_miss_rate == '100.0' }}
uses: actions/cache/save@v4
with:
path: ${{ inputs.ccache_dir }}
key: clio-ccache-${{ runner.os }}-${{ inputs.build_type }}${{ inputs.code_coverage == 'true' && '-code_coverage' || '' }}-${{ inputs.conan_profile }}-develop-${{ steps.git_common_ancestor.outputs.commit }}

View File

@@ -91,6 +91,19 @@ updates:
prefix: "ci: [DEPENDABOT] "
target-branch: develop
- package-ecosystem: github-actions
directory: .github/actions/get-threads-number/
schedule:
interval: weekly
day: monday
time: "04:00"
timezone: Etc/GMT
reviewers:
- XRPLF/clio-dev-team
commit-message:
prefix: "ci: [DEPENDABOT] "
target-branch: develop
- package-ecosystem: github-actions
directory: .github/actions/git-common-ancestor/
schedule:
@@ -105,7 +118,20 @@ updates:
target-branch: develop
- package-ecosystem: github-actions
directory: .github/actions/cache-key/
directory: .github/actions/restore-cache/
schedule:
interval: weekly
day: monday
time: "04:00"
timezone: Etc/GMT
reviewers:
- XRPLF/clio-dev-team
commit-message:
prefix: "ci: [DEPENDABOT] "
target-branch: develop
- package-ecosystem: github-actions
directory: .github/actions/save-cache/
schedule:
interval: weekly
day: monday

View File

@@ -4,7 +4,7 @@ build_type=Release
compiler=apple-clang
compiler.cppstd=20
compiler.libcxx=libc++
compiler.version=17.0
compiler.version=17
os=Macos
[conf]

View File

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

View File

@@ -40,9 +40,9 @@ mkdir -p "$PROFILES_DIR"
if [[ "$(uname)" == "Darwin" ]]; then
create_profile_with_sanitizers "apple-clang" "$APPLE_CLANG_PROFILE"
echo "include(apple-clang)" >"$PROFILES_DIR/default"
echo "include(apple-clang)" > "$PROFILES_DIR/default"
else
create_profile_with_sanitizers "clang" "$CLANG_PROFILE"
create_profile_with_sanitizers "gcc" "$GCC_PROFILE"
echo "include(gcc)" >"$PROFILES_DIR/default"
echo "include(gcc)" > "$PROFILES_DIR/default"
fi

View File

@@ -1,25 +0,0 @@
#!/usr/bin/env bash
set -ex
TEMP_DIR=$(mktemp -d)
trap "rm -rf $TEMP_DIR" EXIT
echo "Using temporary CONAN_HOME: $TEMP_DIR"
# We use a temporary Conan home to avoid polluting the user's existing Conan
# configuration and to not use local cache (which leads to non-reproducible lockfiles).
export CONAN_HOME="$TEMP_DIR"
# Ensure that the xrplf remote is the first to be consulted, so any recipes we
# patched are used. We also add it there to not created huge diff when the
# official Conan Center Index is updated.
conan remote add --force --index 0 xrplf https://conan.ripplex.io
# Delete any existing lockfile.
rm -f conan.lock
# Create a new lockfile that is compatible with macOS.
# It should also work on Linux.
conan lock create . \
--profile:all=.github/scripts/conan/apple-clang-17.profile

View File

@@ -22,8 +22,8 @@ fi
TEST_BINARY=$1
if [[ ! -f "$TEST_BINARY" ]]; then
echo "Test binary not found: $TEST_BINARY"
exit 1
echo "Test binary not found: $TEST_BINARY"
exit 1
fi
TESTS=$($TEST_BINARY --gtest_list_tests | awk '/^ / {print suite $1} !/^ / {suite=$1}')
@@ -31,16 +31,15 @@ TESTS=$($TEST_BINARY --gtest_list_tests | awk '/^ / {print suite $1} !/^ / {su
OUTPUT_DIR="./.sanitizer-report"
mkdir -p "$OUTPUT_DIR"
export TSAN_OPTIONS="die_after_fork=0"
export MallocNanoZone='0' # for MacOSX
for TEST in $TESTS; do
OUTPUT_FILE="$OUTPUT_DIR/${TEST//\//_}.log"
$TEST_BINARY --gtest_filter="$TEST" >"$OUTPUT_FILE" 2>&1
OUTPUT_FILE="$OUTPUT_DIR/${TEST//\//_}"
export TSAN_OPTIONS="log_path=\"$OUTPUT_FILE\" die_after_fork=0"
export ASAN_OPTIONS="log_path=\"$OUTPUT_FILE\""
export UBSAN_OPTIONS="log_path=\"$OUTPUT_FILE\""
export MallocNanoZone='0' # for MacOSX
$TEST_BINARY --gtest_filter="$TEST" > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "'$TEST' failed a sanitizer check."
else
rm "$OUTPUT_FILE"
fi
if [ $? -ne 0 ]; then
echo "'$TEST' failed a sanitizer check."
fi
done

View File

@@ -20,5 +20,5 @@ for artifact_name in $(ls); do
rm "${artifact_name}/${BINARY_NAME}"
rm -r "${artifact_name}"
sha256sum "./${artifact_name}.zip" >"./${artifact_name}.zip.sha256sum"
sha256sum "./${artifact_name}.zip" > "./${artifact_name}.zip.sha256sum"
done

View File

@@ -38,37 +38,32 @@ on:
description: Whether to strip clio binary
default: true
defaults:
run:
shell: bash
jobs:
build_and_publish_image:
name: Build and publish image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
- name: Download Clio binary from artifact
if: ${{ inputs.artifact_name != null }}
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@v5
with:
name: ${{ inputs.artifact_name }}
path: ./docker/clio/artifact/
- name: Download Clio binary from url
if: ${{ inputs.clio_server_binary_url != null }}
env:
BINARY_URL: ${{ inputs.clio_server_binary_url }}
BINARY_SHA256: ${{ inputs.binary_sha256 }}
shell: bash
run: |
wget "${BINARY_URL}" -P ./docker/clio/artifact/
if [ "$(sha256sum ./docker/clio/clio_server | awk '{print $1}')" != "${BINARY_SHA256}" ]; then
wget "${{inputs.clio_server_binary_url}}" -P ./docker/clio/artifact/
if [ "$(sha256sum ./docker/clio/clio_server | awk '{print $1}')" != "${{inputs.binary_sha256}}" ]; then
echo "Binary sha256 sum doesn't match"
exit 1
fi
- name: Unpack binary
shell: bash
run: |
sudo apt update && sudo apt install -y tar unzip
cd docker/clio/artifact
@@ -85,6 +80,7 @@ jobs:
- name: Strip binary
if: ${{ inputs.strip_binary }}
shell: bash
run: strip ./docker/clio/clio_server
- name: Set GHCR_REPO

View File

@@ -33,10 +33,6 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.ref == 'refs/heads/develop' && github.run_number || 'branch' }}
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
build-and-test:
name: Build and Test
@@ -49,7 +45,7 @@ jobs:
build_type: [Release, Debug]
container:
[
'{ "image": "ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031" }',
'{ "image": "ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d" }',
]
static: [true]
@@ -79,11 +75,11 @@ jobs:
uses: ./.github/workflows/reusable-build.yml
with:
runs_on: heavy
container: '{ "image": "ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031" }'
container: '{ "image": "ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d" }'
conan_profile: gcc
build_type: Debug
download_ccache: true
upload_ccache: true
upload_ccache: false
code_coverage: true
static: true
upload_clio_server: false
@@ -92,21 +88,40 @@ jobs:
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
package:
name: Build packages
uses: ./.github/workflows/reusable-build.yml
with:
runs_on: heavy
container: '{ "image": "ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d" }'
conan_profile: gcc
build_type: Release
download_ccache: true
upload_ccache: false
code_coverage: false
static: true
upload_clio_server: false
package: true
targets: package
analyze_build_time: false
check_config:
name: Check Config Description
needs: build-and-test
runs-on: heavy
container:
image: ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031
image: ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
- uses: actions/download-artifact@v5
with:
name: clio_server_Linux_Release_gcc
- name: Compare Config Description
shell: bash
run: |
repoConfigFile=docs/config-description.md
configDescriptionFile=config_description_new.md

View File

@@ -12,33 +12,31 @@ concurrency:
env:
CONAN_PROFILE: gcc
defaults:
run:
shell: bash
jobs:
build:
name: Build Clio / `libXRPL ${{ github.event.client_payload.version }}`
runs-on: heavy
container:
image: ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031
image: ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab
uses: XRPLF/actions/.github/actions/prepare-runner@7951b682e5a2973b28b0719a72f01fc4b0d0c34f
with:
enable_ccache: false
disable_ccache: true
- name: Update libXRPL version requirement
shell: bash
run: |
sed -i.bak -E "s|'xrpl/[a-zA-Z0-9\\.\\-]+'|'xrpl/${{ github.event.client_payload.conan_ref }}'|g" conanfile.py
rm -f conanfile.py.bak
- name: Update conan lockfile
shell: bash
run: |
conan lock create . --profile:all ${{ env.CONAN_PROFILE }}
@@ -59,7 +57,7 @@ jobs:
run: strip build/clio_tests
- name: Upload clio_tests
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@v4
with:
name: clio_tests_check_libxrpl
path: build/clio_tests
@@ -69,10 +67,10 @@ jobs:
needs: build
runs-on: heavy
container:
image: ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031
image: ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d
steps:
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
- uses: actions/download-artifact@v5
with:
name: clio_tests_check_libxrpl
@@ -92,7 +90,7 @@ jobs:
issues: write
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
- name: Create an issue
uses: ./.github/actions/create-issue

View File

@@ -5,26 +5,20 @@ on:
types: [opened, edited, reopened, synchronize]
branches: [develop]
defaults:
run:
shell: bash
jobs:
check_title:
runs-on: ubuntu-latest
steps:
- uses: ytanikin/pr-conventional-commits@fda730cb152c05a849d6d84325e50c6182d9d1e9 # 1.5.1
- uses: ytanikin/pr-conventional-commits@b72758283dcbee706975950e96bc4bf323a8d8c0 # v1.4.2
with:
task_types: '["build","feat","fix","docs","test","ci","style","refactor","perf","chore"]'
add_label: false
custom_labels: '{"build":"build", "feat":"enhancement", "fix":"bug", "docs":"documentation", "test":"testability", "ci":"ci", "style":"refactoring", "refactor":"refactoring", "perf":"performance", "chore":"tooling"}'
- name: Check if message starts with upper-case letter
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
if [[ ! "${PR_TITLE}" =~ ^[a-z]+:\ [\[A-Z] ]]; then
if [[ ! "${{ github.event.pull_request.title }}" =~ ^[a-z]+:\ [\[A-Z] ]]; then
echo "Error: PR title must start with an upper-case letter."
exit 1
fi

View File

@@ -22,16 +22,12 @@ env:
CONAN_PROFILE: clang
LLVM_TOOLS_VERSION: 20
defaults:
run:
shell: bash
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:94da8459ddc30e2f0d88a98cba63a57b2cda3031
image: ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d
permissions:
contents: write
@@ -39,14 +35,21 @@ jobs:
pull-requests: write
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab
uses: XRPLF/actions/.github/actions/prepare-runner@7951b682e5a2973b28b0719a72f01fc4b0d0c34f
with:
enable_ccache: false
disable_ccache: true
- name: Restore cache
uses: ./.github/actions/restore-cache
id: restore_cache
with:
conan_profile: ${{ env.CONAN_PROFILE }}
ccache_dir: ${{ env.CCACHE_DIR }}
- name: Run conan
uses: ./.github/actions/conan
@@ -58,34 +61,34 @@ jobs:
with:
conan_profile: ${{ env.CONAN_PROFILE }}
- name: Get number of processors
uses: XRPLF/actions/get-nproc@cf0433aa74563aead044a1e395610c96d65a37cf
id: nproc
- name: Get number of threads
uses: ./.github/actions/get-threads-number
id: number_of_threads
- name: Run clang-tidy (several times)
- name: Run clang-tidy
continue-on-error: true
id: clang_tidy
shell: bash
id: run_clang_tidy
run: |
# We run clang-tidy several times, because some fixes may enable new fixes in subsequent runs.
CLANG_TIDY_COMMAND="run-clang-tidy-${{ env.LLVM_TOOLS_VERSION }} -p build -j ${{ steps.nproc.outputs.nproc }} -fix -quiet"
${CLANG_TIDY_COMMAND} ||
${CLANG_TIDY_COMMAND} ||
${CLANG_TIDY_COMMAND}
- name: Check for changes
id: files_changed
continue-on-error: true
run: |
git diff --exit-code
run-clang-tidy-${{ env.LLVM_TOOLS_VERSION }} -p build -j "${{ steps.number_of_threads.outputs.threads_number }}" -fix -quiet 1>output.txt
- name: Fix local includes and clang-format style
if: ${{ steps.files_changed.outcome != 'success' }}
if: ${{ steps.run_clang_tidy.outcome != 'success' }}
shell: bash
run: |
pre-commit run --all-files fix-local-includes || true
pre-commit run --all-files clang-format || true
- name: Print issues found
if: ${{ steps.run_clang_tidy.outcome != 'success' }}
shell: bash
run: |
sed -i '/error\||/!d' ./output.txt
cat output.txt
rm output.txt
- name: Create an issue
if: ${{ (steps.clang_tidy.outcome != 'success' || steps.files_changed.outcome != 'success') && github.event_name != 'pull_request' }}
if: ${{ steps.run_clang_tidy.outcome != 'success' && github.event_name != 'pull_request' }}
id: create_issue
uses: ./.github/actions/create-issue
env:
@@ -98,7 +101,7 @@ jobs:
List of the issues found: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/
- uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.0
if: ${{ steps.files_changed.outcome != 'success' && github.event_name != 'pull_request' }}
if: ${{ steps.run_clang_tidy.outcome != 'success' && github.event_name != 'pull_request' }}
with:
gpg_private_key: ${{ secrets.ACTIONS_GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.ACTIONS_GPG_PASSPHRASE }}
@@ -106,8 +109,8 @@ jobs:
git_commit_gpgsign: true
- name: Create PR with fixes
if: ${{ steps.files_changed.outcome != 'success' && github.event_name != 'pull_request' }}
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
if: ${{ steps.run_clang_tidy.outcome != 'success' && github.event_name != 'pull_request' }}
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
env:
GH_REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
@@ -122,5 +125,6 @@ jobs:
reviewers: "godexsoft,kuznetsss,PeterChen13579,mathbunnyru"
- name: Fail the job
if: ${{ steps.clang_tidy.outcome != 'success' || steps.files_changed.outcome != 'success' }}
if: ${{ steps.run_clang_tidy.outcome != 'success' }}
shell: bash
run: exit 1

View File

@@ -10,26 +10,22 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
build:
runs-on: ubuntu-latest
container:
image: ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031
image: ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d
steps:
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@v4
with:
lfs: true
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab
uses: XRPLF/actions/.github/actions/prepare-runner@7951b682e5a2973b28b0719a72f01fc4b0d0c34f
with:
enable_ccache: false
disable_ccache: true
- name: Create build directory
run: mkdir build_docs
@@ -43,10 +39,10 @@ jobs:
run: cmake --build . --target docs
- name: Setup Pages
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
uses: actions/upload-pages-artifact@v4
with:
path: build_docs/html
name: docs-develop
@@ -66,6 +62,6 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
uses: actions/deploy-pages@v4
with:
artifact_name: docs-develop

View File

@@ -23,25 +23,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
get_date:
name: Get Date
runs-on: ubuntu-latest
outputs:
date: ${{ steps.get_date.outputs.date }}
steps:
- name: Get current date
id: get_date
run: |
echo "date=$(date +'%Y%m%d')" >> $GITHUB_OUTPUT
build-and-test:
name: Build and Test
needs: get_date
strategy:
fail-fast: false
@@ -55,17 +39,17 @@ jobs:
conan_profile: gcc
build_type: Release
static: true
container: '{ "image": "ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031" }'
container: '{ "image": "ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d" }'
- os: heavy
conan_profile: gcc
build_type: Debug
static: true
container: '{ "image": "ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031" }'
container: '{ "image": "ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d" }'
- os: heavy
conan_profile: gcc.ubsan
build_type: Release
static: false
container: '{ "image": "ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031" }'
container: '{ "image": "ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d" }'
uses: ./.github/workflows/reusable-build-test.yml
with:
@@ -79,31 +63,9 @@ jobs:
upload_clio_server: true
download_ccache: false
upload_ccache: false
version: nightly-${{ needs.get_date.outputs.date }}
package:
name: Build debian package
needs: get_date
uses: ./.github/workflows/reusable-build.yml
with:
runs_on: heavy
container: '{ "image": "ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031" }'
conan_profile: gcc
build_type: Release
download_ccache: false
upload_ccache: false
code_coverage: false
static: true
upload_clio_server: false
package: true
version: nightly-${{ needs.get_date.outputs.date }}
targets: package
analyze_build_time: false
analyze_build_time:
name: Analyze Build Time
needs: get_date
strategy:
fail-fast: false
@@ -111,7 +73,7 @@ jobs:
include:
- os: heavy
conan_profile: clang
container: '{ "image": "ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031" }'
container: '{ "image": "ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d" }'
static: true
- os: macos15
conan_profile: apple-clang
@@ -130,20 +92,19 @@ jobs:
upload_clio_server: false
targets: all
analyze_build_time: true
version: nightly-${{ needs.get_date.outputs.date }}
nightly_release:
needs: [build-and-test, package, get_date]
needs: build-and-test
uses: ./.github/workflows/reusable-release.yml
with:
delete_pattern: "nightly-*"
overwrite_release: true
prerelease: true
title: "Clio development build (nightly-${{ needs.get_date.outputs.date }})"
version: nightly-${{ needs.get_date.outputs.date }}
title: "Clio development (nightly) build"
version: nightly
header: >
> **Note:** Please remember that this is a development release and it is not recommended for production use.
Changelog (including previous releases): <https://github.com/XRPLF/clio/commits/nightly-${{ needs.get_date.outputs.date }}>
Changelog (including previous releases): <https://github.com/XRPLF/clio/commits/nightly>
generate_changelog: false
draft: false
@@ -169,7 +130,7 @@ jobs:
issues: write
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
- name: Create an issue
uses: ./.github/actions/create-issue

View File

@@ -1,8 +1,8 @@
name: Pre-commit auto-update
on:
# every first day of the month
schedule:
# every first day of the month
- cron: "0 0 1 * *"
pull_request:
branches: [release/*, develop]
@@ -12,7 +12,7 @@ on:
jobs:
auto-update:
uses: XRPLF/actions/.github/workflows/pre-commit-autoupdate.yml@ad4ab1ae5a54a4bab0e87294c31fc0729f788b2b
uses: XRPLF/actions/.github/workflows/pre-commit-autoupdate.yml@afbcbdafbe0ce5439492fb87eda6441371086386
with:
sign_commit: true
committer: "Clio CI <skuznetsov@ripple.com>"

View File

@@ -8,7 +8,7 @@ on:
jobs:
run-hooks:
uses: XRPLF/actions/.github/workflows/pre-commit.yml@282890f46d6921249d5659dd38babcb0bd8aef48
uses: XRPLF/actions/.github/workflows/pre-commit.yml@a8d7472b450eb53a1e5228f64552e5974457a21a
with:
runs_on: heavy
container: '{ "image": "ghcr.io/xrplf/clio-pre-commit:94da8459ddc30e2f0d88a98cba63a57b2cda3031" }'
container: '{ "image": "ghcr.io/xrplf/clio-pre-commit:b2be4b51d1d81548ca48e2f2b8f67356b880c96d" }'

View File

@@ -29,7 +29,7 @@ jobs:
conan_profile: gcc
build_type: Release
static: true
container: '{ "image": "ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031" }'
container: '{ "image": "ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d" }'
uses: ./.github/workflows/reusable-build-test.yml
with:
@@ -43,32 +43,13 @@ jobs:
upload_clio_server: true
download_ccache: false
upload_ccache: false
version: ${{ github.event_name == 'push' && github.ref_name || '' }}
package:
name: Build debian package
uses: ./.github/workflows/reusable-build.yml
with:
runs_on: heavy
container: '{ "image": "ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031" }'
conan_profile: gcc
build_type: Release
download_ccache: false
upload_ccache: false
code_coverage: false
static: true
upload_clio_server: false
package: true
version: ${{ github.event_name == 'push' && github.ref_name || '' }}
targets: package
analyze_build_time: false
expected_version: ${{ github.event_name == 'push' && github.ref_name || '' }}
release:
needs: [build-and-test, package]
needs: build-and-test
uses: ./.github/workflows/reusable-release.yml
with:
delete_pattern: ""
overwrite_release: false
prerelease: ${{ contains(github.ref_name, '-') }}
title: "${{ github.ref_name }}"
version: "${{ github.ref_name }}"

View File

@@ -63,18 +63,18 @@ on:
type: string
default: all
expected_version:
description: Expected version of the clio_server binary
required: false
type: string
default: ""
package:
description: Whether to generate Debian package
required: false
type: boolean
default: false
version:
description: Version of the clio_server binary
required: false
type: string
default: ""
jobs:
build:
uses: ./.github/workflows/reusable-build.yml
@@ -90,8 +90,8 @@ jobs:
upload_clio_server: ${{ inputs.upload_clio_server }}
targets: ${{ inputs.targets }}
analyze_build_time: false
expected_version: ${{ inputs.expected_version }}
package: ${{ inputs.package }}
version: ${{ inputs.version }}
test:
needs: build

View File

@@ -60,25 +60,21 @@ on:
required: true
type: boolean
expected_version:
description: Expected version of the clio_server binary
required: false
type: string
default: ""
package:
description: Whether to generate Debian package
required: false
type: boolean
version:
description: Version of the clio_server binary
required: false
type: string
default: ""
secrets:
CODECOV_TOKEN:
required: false
defaults:
run:
shell: bash
jobs:
build:
name: Build
@@ -88,38 +84,36 @@ jobs:
steps:
- name: Cleanup workspace
if: ${{ runner.os == 'macOS' }}
uses: XRPLF/actions/cleanup-workspace@cf0433aa74563aead044a1e395610c96d65a37cf
uses: XRPLF/actions/.github/actions/cleanup-workspace@ea9970b7c211b18f4c8bcdb28c29f5711752029f
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
with:
fetch-depth: 0
# We need to fetch tags to have correct version in the release
# The workaround is based on https://github.com/actions/checkout/issues/1467
fetch-tags: true
ref: ${{ github.ref }}
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab
uses: XRPLF/actions/.github/actions/prepare-runner@7951b682e5a2973b28b0719a72f01fc4b0d0c34f
with:
enable_ccache: ${{ inputs.download_ccache }}
disable_ccache: ${{ !inputs.download_ccache }}
- name: Setup conan on macOS
if: ${{ runner.os == 'macOS' }}
shell: bash
run: ./.github/scripts/conan/init.sh
- name: Generate cache key
uses: ./.github/actions/cache-key
id: cache_key
- name: Restore cache
if: ${{ inputs.download_ccache }}
uses: ./.github/actions/restore-cache
id: restore_cache
with:
conan_profile: ${{ inputs.conan_profile }}
ccache_dir: ${{ env.CCACHE_DIR }}
build_type: ${{ inputs.build_type }}
code_coverage: ${{ inputs.code_coverage }}
- name: Restore ccache cache
if: ${{ inputs.download_ccache && github.ref != 'refs/heads/develop' }}
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ steps.cache_key.outputs.key }}
restore-keys: |
${{ steps.cache_key.outputs.restore_keys }}
- name: Run conan
uses: ./.github/actions/conan
with:
@@ -135,7 +129,6 @@ jobs:
static: ${{ inputs.static }}
time_trace: ${{ inputs.analyze_build_time }}
package: ${{ inputs.package }}
version: ${{ inputs.version }}
- name: Build Clio
uses: ./.github/actions/build-clio
@@ -148,26 +141,24 @@ jobs:
ClangBuildAnalyzer --all build/ build_time_report.bin
ClangBuildAnalyzer --analyze build_time_report.bin > build_time_report.txt
cat build_time_report.txt
shell: bash
- name: Upload build time analyze report
if: ${{ inputs.analyze_build_time }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@v4
with:
name: build_time_report_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }}
path: build_time_report.txt
- name: Show ccache's statistics and zero it
- name: Show ccache's statistics
if: ${{ inputs.download_ccache }}
shell: bash
id: ccache_stats
run: |
ccache --show-stats -vv
ccache --zero-stats
- name: Save ccache cache
if: ${{ inputs.upload_ccache && github.ref == 'refs/heads/develop' }}
uses: actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ steps.cache_key.outputs.key }}
ccache -s > /tmp/ccache.stats
miss_rate=$(cat /tmp/ccache.stats | grep 'Misses' | head -n1 | sed 's/.*(\(.*\)%).*/\1/')
echo "miss_rate=${miss_rate}" >> $GITHUB_OUTPUT
cat /tmp/ccache.stats
- name: Strip unit_tests
if: ${{ !endsWith(inputs.conan_profile, 'san') && !inputs.code_coverage && !inputs.analyze_build_time }}
@@ -179,32 +170,44 @@ jobs:
- name: Upload clio_server
if: ${{ inputs.upload_clio_server && !inputs.code_coverage && !inputs.analyze_build_time }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@v4
with:
name: clio_server_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }}
path: build/clio_server
- name: Upload clio_tests
if: ${{ !inputs.code_coverage && !inputs.analyze_build_time && !inputs.package }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@v4
with:
name: clio_tests_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }}
path: build/clio_tests
- name: Upload clio_integration_tests
if: ${{ !inputs.code_coverage && !inputs.analyze_build_time && !inputs.package }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@v4
with:
name: clio_integration_tests_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }}
path: build/clio_integration_tests
- name: Upload Clio Linux package
if: ${{ inputs.package }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
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' }}
uses: ./.github/actions/save-cache
with:
conan_profile: ${{ inputs.conan_profile }}
ccache_dir: ${{ env.CCACHE_DIR }}
build_type: ${{ inputs.build_type }}
code_coverage: ${{ inputs.code_coverage }}
ccache_cache_hit: ${{ steps.restore_cache.outputs.ccache_cache_hit }}
ccache_cache_miss_rate: ${{ steps.ccache_stats.outputs.miss_rate }}
# This is run as part of the build job, because it requires the following:
# - source code
# - conan packages
@@ -215,21 +218,15 @@ jobs:
if: ${{ inputs.code_coverage }}
uses: ./.github/actions/code-coverage
- name: Verify version is expected
if: ${{ inputs.version != '' }}
env:
INPUT_VERSION: ${{ inputs.version }}
BUILD_TYPE: ${{ inputs.build_type }}
- name: Verify expected version
if: ${{ inputs.expected_version != '' }}
shell: bash
run: |
set -e
EXPECTED_VERSION="clio-${INPUT_VERSION}"
if [[ "${BUILD_TYPE}" == "Debug" ]]; then
EXPECTED_VERSION="${EXPECTED_VERSION}+DEBUG"
fi
actual_version=$(./build/clio_server --version | head -n 1)
if [[ "${actual_version}" != "${EXPECTED_VERSION}" ]]; then
echo "Expected version '${EXPECTED_VERSION}', but got '${actual_version}'"
EXPECTED_VERSION="clio-${{ inputs.expected_version }}"
actual_version=$(./build/clio_server --version)
if [[ "$actual_version" != "$EXPECTED_VERSION" ]]; then
echo "Expected version '$EXPECTED_VERSION', but got '$actual_version'"
exit 1
fi

View File

@@ -3,10 +3,10 @@ name: Make release
on:
workflow_call:
inputs:
delete_pattern:
description: "Pattern to delete previous releases"
overwrite_release:
description: "Overwrite the current release and tag"
required: true
type: string
type: boolean
prerelease:
description: "Create a prerelease"
@@ -38,15 +38,11 @@ on:
required: true
type: boolean
defaults:
run:
shell: bash
jobs:
release:
runs-on: heavy
container:
image: ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031
image: ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d
env:
GH_REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
@@ -55,75 +51,62 @@ jobs:
contents: write
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab
uses: XRPLF/actions/.github/actions/prepare-runner@7951b682e5a2973b28b0719a72f01fc4b0d0c34f
with:
enable_ccache: false
disable_ccache: true
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
- uses: actions/download-artifact@v5
with:
path: release_artifacts
pattern: clio_server_*
- name: Prepare release artifacts
run: .github/scripts/prepare-release-artifacts.sh release_artifacts
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: release_artifacts
pattern: clio_deb_package_*
- name: Create release notes
env:
RELEASE_HEADER: ${{ inputs.header }}
shell: bash
run: |
echo "# Release notes" > "${RUNNER_TEMP}/release_notes.md"
echo "" >> "${RUNNER_TEMP}/release_notes.md"
printf '%s\n' "${RELEASE_HEADER}" >> "${RUNNER_TEMP}/release_notes.md"
printf '%s\n' "${{ inputs.header }}" >> "${RUNNER_TEMP}/release_notes.md"
- name: Generate changelog
shell: bash
if: ${{ inputs.generate_changelog }}
run: |
LAST_TAG="$(gh release view --json tagName -q .tagName --repo XRPLF/clio)"
LAST_TAG_COMMIT="$(git rev-parse $LAST_TAG)"
BASE_COMMIT="$(git merge-base HEAD $LAST_TAG_COMMIT)"
git-cliff "${BASE_COMMIT}..HEAD" --ignore-tags "nightly|-b|-rc" >> "${RUNNER_TEMP}/release_notes.md"
git-cliff "${BASE_COMMIT}..HEAD" --ignore-tags "nightly|-b|-rc"
cat CHANGELOG.md >> "${RUNNER_TEMP}/release_notes.md"
- name: Prepare release artifacts
shell: bash
run: .github/scripts/prepare-release-artifacts.sh release_artifacts
- name: Upload release notes
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@v4
with:
name: release_notes_${{ inputs.version }}
path: "${RUNNER_TEMP}/release_notes.md"
- name: Remove previous release with a pattern
if: ${{ github.event_name != 'pull_request' && inputs.delete_pattern != '' }}
env:
DELETE_PATTERN: ${{ inputs.delete_pattern }}
- name: Remove current release and tag
if: ${{ github.event_name != 'pull_request' && inputs.overwrite_release }}
shell: bash
run: |
RELEASES_TO_DELETE=$(gh release list --limit 50 --repo "${GH_REPO}" | grep -E "${DELETE_PATTERN}" | awk -F'\t' '{print $3}' || true)
if [ -n "$RELEASES_TO_DELETE" ]; then
for RELEASE in $RELEASES_TO_DELETE; do
echo "Deleting release: $RELEASE"
gh release delete "$RELEASE" --repo "${GH_REPO}" --yes --cleanup-tag
done
fi
gh release delete ${{ inputs.version }} --yes || true
git push origin :${{ inputs.version }} || true
- name: Publish release
if: ${{ github.event_name != 'pull_request' }}
env:
RELEASE_VERSION: ${{ inputs.version }}
PRERELEASE_OPTION: ${{ inputs.prerelease && '--prerelease' || '' }}
RELEASE_TITLE: ${{ inputs.title }}
DRAFT_OPTION: ${{ inputs.draft && '--draft' || '' }}
shell: bash
run: |
gh release create "${RELEASE_VERSION}" \
${PRERELEASE_OPTION} \
--title "${RELEASE_TITLE}" \
gh release create "${{ inputs.version }}" \
${{ inputs.prerelease && '--prerelease' || '' }} \
--title "${{ inputs.title }}" \
--target "${GITHUB_SHA}" \
${DRAFT_OPTION} \
${{ inputs.draft && '--draft' || '' }} \
--notes-file "${RUNNER_TEMP}/release_notes.md" \
./release_artifacts/clio_*
./release_artifacts/clio_server*

View File

@@ -33,10 +33,6 @@ on:
required: true
type: boolean
defaults:
run:
shell: bash
jobs:
unit_tests:
name: Unit testing
@@ -47,22 +43,23 @@ jobs:
env:
# TODO: remove completely when we have fixed all currently existing issues with sanitizers
SANITIZER_IGNORE_ERRORS: ${{ endsWith(inputs.conan_profile, '.tsan') }}
SANITIZER_IGNORE_ERRORS: ${{ endsWith(inputs.conan_profile, '.tsan') || inputs.conan_profile == 'clang.asan' || (inputs.conan_profile == 'gcc.asan' && inputs.build_type == 'Release') }}
steps:
- name: Cleanup workspace
if: ${{ runner.os == 'macOS' }}
uses: XRPLF/actions/cleanup-workspace@cf0433aa74563aead044a1e395610c96d65a37cf
uses: XRPLF/actions/.github/actions/cleanup-workspace@ea9970b7c211b18f4c8bcdb28c29f5711752029f
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
- uses: actions/download-artifact@v5
with:
name: clio_tests_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }}
- name: Make clio_tests executable
shell: bash
run: chmod +x ./clio_tests
- name: Run clio_tests (regular)
@@ -71,10 +68,11 @@ jobs:
- name: Run clio_tests (sanitizer errors ignored)
if: ${{ env.SANITIZER_IGNORE_ERRORS == 'true' }}
run: ./.github/scripts/execute-tests-under-sanitizer.sh ./clio_tests
run: ./.github/scripts/execute-tests-under-sanitizer ./clio_tests
- name: Check for sanitizer report
if: ${{ env.SANITIZER_IGNORE_ERRORS == 'true' }}
shell: bash
id: check_report
run: |
if ls .sanitizer-report/* 1> /dev/null 2>&1; then
@@ -85,7 +83,7 @@ jobs:
- name: Upload sanitizer report
if: ${{ env.SANITIZER_IGNORE_ERRORS == 'true' && steps.check_report.outputs.found_report == 'true' }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@v4
with:
name: sanitizer_report_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }}
path: .sanitizer-report/*
@@ -124,13 +122,7 @@ jobs:
steps:
- name: Cleanup workspace
if: ${{ runner.os == 'macOS' }}
uses: XRPLF/actions/cleanup-workspace@cf0433aa74563aead044a1e395610c96d65a37cf
- name: Restart colima
if: ${{ runner.os == 'macOS' }}
run: |
colima delete --force
colima start
uses: XRPLF/actions/.github/actions/cleanup-workspace@ea9970b7c211b18f4c8bcdb28c29f5711752029f
- name: Spin up scylladb
if: ${{ runner.os == 'macOS' }}
@@ -152,7 +144,7 @@ jobs:
sleep 5
done
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
- uses: actions/download-artifact@v5
with:
name: clio_integration_tests_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }}

View File

@@ -6,29 +6,25 @@ on:
CODECOV_TOKEN:
required: true
defaults:
run:
shell: bash
jobs:
upload_report:
name: Upload report
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download report artifact
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@v5
with:
name: coverage-report.xml
path: build
- name: Upload coverage report
if: ${{ hashFiles('build/coverage_report.xml') != '' }}
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
with:
files: build/coverage_report.xml
fail_ci_if_error: true

View File

@@ -15,7 +15,7 @@ on:
- ".github/actions/**"
- "!.github/actions/build-docker-image/**"
- "!.github/actions/create-issue/**"
- .github/scripts/execute-tests-under-sanitizer.sh
- .github/scripts/execute-tests-under-sanitizer
- CMakeLists.txt
- conanfile.py
@@ -44,13 +44,14 @@ jobs:
uses: ./.github/workflows/reusable-build-test.yml
with:
runs_on: heavy
container: '{ "image": "ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031" }'
container: '{ "image": "ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d" }'
download_ccache: false
upload_ccache: false
conan_profile: ${{ matrix.compiler }}${{ matrix.sanitizer_ext }}
build_type: ${{ matrix.build_type }}
static: false
run_unit_tests: true
# Currently, both gcc.tsan and clang.tsan unit tests hang
run_unit_tests: ${{ matrix.sanitizer_ext != '.tsan' }}
run_integration_tests: false
upload_clio_server: false
targets: clio_tests clio_integration_tests

View File

@@ -33,10 +33,6 @@ env:
GCC_MAJOR_VERSION: 15
GCC_VERSION: 15.2.0
defaults:
run:
shell: bash
jobs:
repo:
name: Calculate repo name
@@ -56,11 +52,11 @@ jobs:
needs: repo
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1
uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47.0.0
with:
files: "docker/compilers/gcc/**"
@@ -77,10 +73,10 @@ jobs:
push_image: ${{ github.event_name != 'pull_request' }}
directory: docker/compilers/gcc
tags: |
type=raw,value=latest
type=raw,value=${{ env.GCC_MAJOR_VERSION }}
type=raw,value=${{ env.GCC_VERSION }}
type=raw,value=${{ github.sha }}
type=raw,value=amd64-latest
type=raw,value=amd64-${{ env.GCC_MAJOR_VERSION }}
type=raw,value=amd64-${{ env.GCC_VERSION }}
type=raw,value=amd64-${{ github.sha }}
platforms: linux/amd64
build_args: |
GCC_MAJOR_VERSION=${{ env.GCC_MAJOR_VERSION }}
@@ -89,17 +85,16 @@ jobs:
dockerhub_description: GCC compiler for XRPLF/clio.
gcc-arm64:
if: false
name: Build and push GCC docker image (arm64)
runs-on: heavy-arm64
needs: repo
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1
uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46.0.5
with:
files: "docker/compilers/gcc/**"
@@ -128,26 +123,25 @@ jobs:
dockerhub_description: GCC compiler for XRPLF/clio.
gcc-merge:
if: false
name: Merge and push multi-arch GCC docker image
runs-on: heavy
needs: [repo, gcc-amd64, gcc-arm64]
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1
uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47.0.0
with:
files: "docker/compilers/gcc/**"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
if: ${{ github.event_name != 'pull_request' }}
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -155,7 +149,7 @@ jobs:
- name: Login to DockerHub
if: ${{ github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' }}
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_PW }}
@@ -176,7 +170,7 @@ jobs:
}
push_image ${{ needs.repo.outputs.GHCR_REPO }}/clio-gcc
if [[ ${{ github.repository_owner }} == 'XRPLF' ]]; then
push_image rippleci/clio_gcc
push_image rippleci/clio_clang
fi
clang:
@@ -185,11 +179,11 @@ jobs:
needs: repo
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1
uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47.0.0
with:
files: "docker/compilers/clang/**"
@@ -209,8 +203,7 @@ jobs:
type=raw,value=latest
type=raw,value=${{ env.CLANG_MAJOR_VERSION }}
type=raw,value=${{ github.sha }}
# platforms: linux/amd64,linux/arm64
platforms: linux/amd64
platforms: linux/amd64,linux/arm64
build_args: |
CLANG_MAJOR_VERSION=${{ env.CLANG_MAJOR_VERSION }}
dockerhub_repo: ${{ github.repository_owner == 'XRPLF' && 'rippleci/clio_clang' || '' }}
@@ -219,14 +212,14 @@ jobs:
tools-amd64:
name: Build and push tools docker image (amd64)
runs-on: heavy
needs: [repo, gcc-amd64]
needs: [repo, gcc-merge]
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1
uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47.0.0
with:
files: "docker/tools/**"
@@ -240,25 +233,24 @@ jobs:
push_image: ${{ github.event_name != 'pull_request' }}
directory: docker/tools
tags: |
type=raw,value=latest
type=raw,value=${{ github.sha }}
type=raw,value=amd64-latest
type=raw,value=amd64-${{ github.sha }}
platforms: linux/amd64
build_args: |
GHCR_REPO=${{ needs.repo.outputs.GHCR_REPO }}
GCC_VERSION=${{ env.GCC_VERSION }}
tools-arm64:
if: false
name: Build and push tools docker image (arm64)
runs-on: heavy-arm64
needs: [repo, gcc-amd64]
needs: [repo, gcc-merge]
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1
uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46.0.5
with:
files: "docker/tools/**"
@@ -280,26 +272,25 @@ jobs:
GCC_VERSION=${{ env.GCC_VERSION }}
tools-merge:
if: false
name: Merge and push multi-arch tools docker image
runs-on: heavy
needs: [repo, tools-amd64, tools-arm64]
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1
uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47.0.0
with:
files: "docker/tools/**"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
if: ${{ github.event_name != 'pull_request' }}
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -318,10 +309,10 @@ jobs:
pre-commit:
name: Build and push pre-commit docker image
runs-on: heavy
needs: [repo, tools-amd64]
needs: [repo, tools-merge]
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
- uses: ./.github/actions/build-docker-image
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -333,18 +324,17 @@ jobs:
tags: |
type=raw,value=latest
type=raw,value=${{ github.sha }}
# platforms: linux/amd64,linux/arm64
platforms: linux/amd64
platforms: linux/amd64,linux/arm64
build_args: |
GHCR_REPO=${{ needs.repo.outputs.GHCR_REPO }}
ci:
name: Build and push CI docker image
runs-on: heavy
needs: [repo, gcc-amd64, clang, tools-amd64]
needs: [repo, gcc-merge, clang, tools-merge]
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
- uses: ./.github/actions/build-docker-image
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -360,8 +350,7 @@ jobs:
type=raw,value=latest
type=raw,value=gcc_${{ env.GCC_MAJOR_VERSION }}_clang_${{ env.CLANG_MAJOR_VERSION }}
type=raw,value=${{ github.sha }}
# platforms: linux/amd64,linux/arm64
platforms: linux/amd64
platforms: linux/amd64,linux/arm64
build_args: |
GHCR_REPO=${{ needs.repo.outputs.GHCR_REPO }}
CLANG_MAJOR_VERSION=${{ env.CLANG_MAJOR_VERSION }}

View File

@@ -22,7 +22,6 @@ on:
- .github/actions/conan/action.yml
- ".github/scripts/conan/**"
- "!.github/scripts/conan/regenerate_lockfile.sh"
- conanfile.py
- conan.lock
@@ -33,7 +32,6 @@ on:
- .github/actions/conan/action.yml
- ".github/scripts/conan/**"
- "!.github/scripts/conan/regenerate_lockfile.sh"
- conanfile.py
- conan.lock
@@ -42,17 +40,13 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
generate-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
- name: Calculate conan matrix
id: set-matrix
@@ -75,15 +69,16 @@ jobs:
CONAN_PROFILE: ${{ matrix.compiler }}${{ matrix.sanitizer_ext }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@v4
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab
uses: XRPLF/actions/.github/actions/prepare-runner@7951b682e5a2973b28b0719a72f01fc4b0d0c34f
with:
enable_ccache: false
disable_ccache: true
- name: Setup conan on macOS
if: ${{ runner.os == 'macOS' }}
shell: bash
run: ./.github/scripts/conan/init.sh
- name: Show conan profile
@@ -104,6 +99,4 @@ jobs:
- name: Upload Conan packages
if: ${{ github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' && github.event_name != 'schedule' }}
env:
FORCE_OPTION: ${{ github.event.inputs.force_upload == 'true' && '--force' || '' }}
run: conan upload "*" -r=xrplf --confirm ${FORCE_OPTION}
run: conan upload "*" -r=xrplf --confirm ${{ github.event.inputs.force_upload == 'true' && '--force' || '' }}

View File

@@ -11,10 +11,7 @@
#
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
exclude: |
(?x)^(
docs/doxygen-awesome-theme/.*
)$
exclude: ^(docs/doxygen-awesome-theme/|conan\.lock$)
repos:
# `pre-commit sample-config` default hooks
@@ -29,12 +26,12 @@ repos:
# Autoformat: YAML, JSON, Markdown, etc.
- repo: https://github.com/rbubley/mirrors-prettier
rev: 14abee445aea04b39069c19b4bd54efff6775819 # frozen: v3.7.4
rev: 5ba47274f9b181bce26a5150a725577f3c336011 # frozen: v3.6.2
hooks:
- id: prettier
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: 76b3d32d3f4b965e1d6425253c59407420ae2c43 # frozen: v0.47.0
rev: 192ad822316c3a22fb3d3cc8aa6eafa0b8488360 # frozen: v0.45.0
hooks:
- id: markdownlint-fix
exclude: LICENSE.md
@@ -58,17 +55,6 @@ repos:
--ignore-words=pre-commit-hooks/codespell_ignore.txt,
]
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 831207fd435b47aeffdf6af853097e64322b4d44 # frozen: 25.12.0
hooks:
- id: black
- repo: https://github.com/scop/pre-commit-shfmt
rev: 2a30809d16bc7a60d9b97353c797f42b510d3368 # frozen: v3.12.0-2
hooks:
- id: shfmt
args: ["-i", "4", "--write"]
# Running some C++ hooks before clang-format
# to ensure that the style is consistent.
- repo: local
@@ -94,7 +80,7 @@ repos:
language: script
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: 75ca4ad908dc4a99f57921f29b7e6c1521e10b26 # frozen: v21.1.8
rev: 719856d56a62953b8d2839fb9e851f25c3cfeef8 # frozen: v21.1.2
hooks:
- id: clang-format
args: [--style=file]

View File

@@ -75,6 +75,10 @@ if (san)
endif ()
target_compile_options(clio_options INTERFACE ${SAN_OPTIMIZATION_FLAG} ${SAN_FLAG} -fno-omit-frame-pointer)
target_compile_definitions(
clio_options INTERFACE $<$<STREQUAL:${san},address>:SANITIZER=ASAN> $<$<STREQUAL:${san},thread>:SANITIZER=TSAN>
$<$<STREQUAL:${san},memory>:SANITIZER=MSAN> $<$<STREQUAL:${san},undefined>:SANITIZER=UBSAN>
)
target_link_libraries(clio_options INTERFACE ${SAN_FLAG} ${SAN_LIB})
endif ()

View File

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

View File

@@ -34,6 +34,7 @@ Below are some useful docs to learn more about Clio.
- [How to configure Clio and rippled](./docs/configure-clio.md)
- [How to run Clio](./docs/run-clio.md)
- [Logging](./docs/logging.md)
- [Troubleshooting guide](./docs/trouble_shooting.md)
**General reference material:**

View File

@@ -9,12 +9,10 @@ target_sources(
util/async/ExecutionContextBenchmarks.cpp
# Logger
util/log/LoggerBenchmark.cpp
# WorkQueue
rpc/WorkQueueBenchmarks.cpp
)
include(deps/gbench)
target_include_directories(clio_benchmark PRIVATE .)
target_link_libraries(clio_benchmark PUBLIC clio_util clio_rpc benchmark::benchmark_main spdlog::spdlog)
target_link_libraries(clio_benchmark PUBLIC clio_util benchmark::benchmark_main spdlog::spdlog)
set_target_properties(clio_benchmark PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})

View File

@@ -1,147 +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.
*/
//==============================================================================
#include "rpc/WorkQueue.hpp"
#include "util/Assert.hpp"
#include "util/config/Array.hpp"
#include "util/config/ConfigConstraints.hpp"
#include "util/config/ConfigDefinition.hpp"
#include "util/config/ConfigValue.hpp"
#include "util/config/Types.hpp"
#include "util/log/Logger.hpp"
#include "util/prometheus/Prometheus.hpp"
#include <benchmark/benchmark.h>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/json/object.hpp>
#include <algorithm>
#include <atomic>
#include <cassert>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <mutex>
#include <thread>
#include <vector>
using namespace rpc;
using namespace util::config;
namespace {
auto const kCONFIG = ClioConfigDefinition{
{"prometheus.compress_reply", ConfigValue{ConfigType::Boolean}.defaultValue(true)},
{"prometheus.enabled", ConfigValue{ConfigType::Boolean}.defaultValue(true)},
{"log.channels.[].channel", Array{ConfigValue{ConfigType::String}}},
{"log.channels.[].level", Array{ConfigValue{ConfigType::String}}},
{"log.level", ConfigValue{ConfigType::String}.defaultValue("info")},
{"log.format", ConfigValue{ConfigType::String}.defaultValue(R"(%Y-%m-%d %H:%M:%S.%f %^%3!l:%n%$ - %v)")},
{"log.is_async", ConfigValue{ConfigType::Boolean}.defaultValue(false)},
{"log.enable_console", ConfigValue{ConfigType::Boolean}.defaultValue(false)},
{"log.directory", ConfigValue{ConfigType::String}.optional()},
{"log.rotation_size", ConfigValue{ConfigType::Integer}.defaultValue(2048).withConstraint(gValidateUint32)},
{"log.directory_max_files", ConfigValue{ConfigType::Integer}.defaultValue(25).withConstraint(gValidateUint32)},
{"log.tag_style", ConfigValue{ConfigType::String}.defaultValue("none")},
};
// this should be a fixture but it did not work with Args very well
void
init()
{
static std::once_flag kONCE;
std::call_once(kONCE, [] {
PrometheusService::init(kCONFIG);
(void)util::LogService::init(kCONFIG);
});
}
} // namespace
static void
benchmarkWorkQueue(benchmark::State& state)
{
init();
auto const wqThreads = static_cast<uint32_t>(state.range(0));
auto const maxQueueSize = static_cast<uint32_t>(state.range(1));
auto const clientThreads = static_cast<uint32_t>(state.range(2));
auto const itemsPerClient = static_cast<uint32_t>(state.range(3));
auto const clientProcessingMs = static_cast<uint32_t>(state.range(4));
for (auto _ : state) {
std::atomic_size_t totalExecuted = 0uz;
std::atomic_size_t totalQueued = 0uz;
state.PauseTiming();
WorkQueue queue(wqThreads, maxQueueSize);
state.ResumeTiming();
std::vector<std::thread> threads;
threads.reserve(clientThreads);
for (auto t = 0uz; t < clientThreads; ++t) {
threads.emplace_back([&] {
for (auto i = 0uz; i < itemsPerClient; ++i) {
totalQueued += static_cast<std::size_t>(queue.postCoro(
[&clientProcessingMs, &totalExecuted](auto yield) {
++totalExecuted;
boost::asio::steady_timer timer(
yield.get_executor(), std::chrono::milliseconds{clientProcessingMs}
);
timer.async_wait(yield);
std::this_thread::sleep_for(std::chrono::microseconds{10});
},
/* isWhiteListed = */ false
));
}
});
}
for (auto& t : threads)
t.join();
queue.stop();
ASSERT(totalExecuted == totalQueued, "Totals don't match");
ASSERT(totalQueued <= itemsPerClient * clientThreads, "Queued more than requested");
if (maxQueueSize == 0) {
ASSERT(totalQueued == itemsPerClient * clientThreads, "Queued exactly the expected amount");
} else {
ASSERT(totalQueued >= std::min(maxQueueSize, itemsPerClient * clientThreads), "Queued less than expected");
}
}
}
// Usage example:
/*
./clio_benchmark \
--benchmark_repetitions=10 \
--benchmark_display_aggregates_only=true \
--benchmark_min_time=1x \
--benchmark_filter="WorkQueue"
*/
// TODO: figure out what happens on 1 thread
BENCHMARK(benchmarkWorkQueue)
->ArgsProduct({{2, 4, 8, 16}, {0, 5'000}, {4, 8, 16}, {1'000, 10'000}, {10, 100, 250}})
->Unit(benchmark::kMillisecond);

View File

@@ -49,6 +49,8 @@ postprocessors = [
]
# render body even when there are no releases to process
# render_always = true
# output file path
output = "CHANGELOG.md"
[git]
# parse the commits based on https://www.conventionalcommits.org

View File

@@ -1,42 +1,42 @@
find_package(Git REQUIRED)
if (DEFINED ENV{GITHUB_BRANCH_NAME})
set(GIT_BUILD_BRANCH $ENV{GITHUB_BRANCH_NAME})
set(GIT_COMMIT_HASH $ENV{GITHUB_HEAD_SHA})
else ()
set(GIT_COMMAND branch --show-current)
execute_process(
COMMAND ${GIT_EXECUTABLE} ${GIT_COMMAND} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_BUILD_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY
)
set(GIT_COMMAND rev-parse HEAD)
execute_process(
COMMAND ${GIT_EXECUTABLE} ${GIT_COMMAND} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY
)
endif ()
set(GIT_COMMAND describe --tags --exact-match)
execute_process(
COMMAND date +%Y%m%d%H%M%S WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE BUILD_DATE
OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY
COMMAND ${GIT_EXECUTABLE} ${GIT_COMMAND}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE TAG
RESULT_VARIABLE RC
ERROR_VARIABLE ERR
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE
)
message(STATUS "Git branch: ${GIT_BUILD_BRANCH}")
message(STATUS "Git commit hash: ${GIT_COMMIT_HASH}")
message(STATUS "Build date: ${BUILD_DATE}")
if (DEFINED ENV{FORCE_CLIO_VERSION} AND NOT "$ENV{FORCE_CLIO_VERSION}" STREQUAL "")
message(STATUS "Using explicitly provided '${FORCE_CLIO_VERSION}' as Clio version")
set(CLIO_VERSION "$ENV{FORCE_CLIO_VERSION}")
set(DOC_CLIO_VERSION "$ENV{FORCE_CLIO_VERSION}")
if (RC EQUAL 0)
message(STATUS "Found tag '${TAG}' in git. Will use it as Clio version")
set(CLIO_VERSION "${TAG}")
set(DOC_CLIO_VERSION "${TAG}")
else ()
message(STATUS "Using 'YYYYMMDDHMS-<branch>-<git short rev>' as Clio version")
message(STATUS "Error finding tag in git: ${ERR}")
message(STATUS "Will use 'YYYYMMDDHMS-<branch>-<git-rev>' as Clio version")
string(SUBSTRING ${GIT_COMMIT_HASH} 0 7 GIT_COMMIT_HASH_SHORT)
set(GIT_COMMAND show -s --date=format:%Y%m%d%H%M%S --format=%cd)
execute_process(
COMMAND ${GIT_EXECUTABLE} ${GIT_COMMAND} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE DATE
OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY
)
set(CLIO_VERSION "${BUILD_DATE}-${GIT_BUILD_BRANCH}-${GIT_COMMIT_HASH_SHORT}")
set(GIT_COMMAND branch --show-current)
execute_process(
COMMAND ${GIT_EXECUTABLE} ${GIT_COMMAND} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY
)
set(GIT_COMMAND rev-parse --short HEAD)
execute_process(
COMMAND ${GIT_EXECUTABLE} ${GIT_COMMAND} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE REV
OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY
)
set(CLIO_VERSION "${DATE}-${BRANCH}-${REV}")
set(DOC_CLIO_VERSION "develop")
endif ()

View File

@@ -1,4 +1,4 @@
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_STATIC_RUNTIME ON)
find_package(Boost 1.82 REQUIRED CONFIG COMPONENTS program_options system log log_setup)
find_package(Boost 1.82 REQUIRED CONFIG COMPONENTS program_options coroutine system log log_setup)

View File

@@ -1,3 +1,3 @@
find_package(OpenSSL 3 REQUIRED CONFIG)
find_package(OpenSSL 1.1.1 REQUIRED CONFIG)
set_target_properties(OpenSSL::SSL PROPERTIES INTERFACE_COMPILE_DEFINITIONS OPENSSL_NO_SSL2)

View File

@@ -1,17 +0,0 @@
[Unit]
Description=Clio XRPL API server
Documentation=https://github.com/XRPLF/clio.git
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=@CLIO_INSTALL_DIR@/bin/clio_server @CLIO_INSTALL_DIR@/etc/config.json
Restart=on-failure
User=clio
Group=clio
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target

View File

@@ -11,6 +11,3 @@ file(READ docs/examples/config/example-config.json config)
string(REGEX REPLACE "./clio_log" "/var/log/clio/" config "${config}")
file(WRITE ${CMAKE_BINARY_DIR}/install-config.json "${config}")
install(FILES ${CMAKE_BINARY_DIR}/install-config.json DESTINATION etc RENAME config.json)
configure_file("${CMAKE_SOURCE_DIR}/cmake/install/clio.service.in" "${CMAKE_BINARY_DIR}/clio.service")
install(FILES "${CMAKE_BINARY_DIR}/clio.service" DESTINATION /lib/systemd/system)

View File

@@ -10,36 +10,37 @@ CLIO_BIN="$CLIO_PREFIX/bin/${CLIO_EXECUTABLE}"
CLIO_CONFIG="$CLIO_PREFIX/etc/config.json"
case "$1" in
configure)
if ! id -u "$USER_NAME" >/dev/null 2>&1; then
# Users who should not have a home directory should have their home directory set to /nonexistent
# https://www.debian.org/doc/debian-policy/ch-opersys.html#non-existent-home-directories
useradd \
--system \
--home-dir /nonexistent \
--no-create-home \
--shell /usr/sbin/nologin \
--comment "system user for ${CLIO_EXECUTABLE}" \
--user-group \
${USER_NAME}
fi
configure)
if ! id -u "$USER_NAME" >/dev/null 2>&1; then
# Users who should not have a home directory should have their home directory set to /nonexistent
# https://www.debian.org/doc/debian-policy/ch-opersys.html#non-existent-home-directories
useradd \
--system \
--home-dir /nonexistent \
--no-create-home \
--shell /usr/sbin/nologin \
--comment "system user for ${CLIO_EXECUTABLE}" \
--user-group \
${USER_NAME}
fi
install -d -o "$USER_NAME" -g "$GROUP_NAME" /var/log/clio
install -d -o "$USER_NAME" -g "$GROUP_NAME" /var/log/clio
if [ -f "$CLIO_CONFIG" ]; then
chown "$USER_NAME:$GROUP_NAME" "$CLIO_CONFIG"
fi
if [ -f "$CLIO_CONFIG" ]; then
chown "$USER_NAME:$GROUP_NAME" "$CLIO_CONFIG"
fi
chown -R "$USER_NAME:$GROUP_NAME" "$CLIO_PREFIX"
chown -R "$USER_NAME:$GROUP_NAME" "$CLIO_PREFIX"
ln -sf "$CLIO_BIN" "/usr/bin/${CLIO_EXECUTABLE}"
ln -sf "$CLIO_BIN" "/usr/bin/${CLIO_EXECUTABLE}"
;;
abort-upgrade | abort-remove | abort-deconfigure) ;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

View File

@@ -1,76 +1,57 @@
{
"version": "0.5",
"requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
"xrpl/3.2.0-rc3+e5cf1a0#694aadb2bdc6226effdb3b1920463e37%1780518730.129",
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1778091117.311",
"spdlog/1.17.0#bcbaaf7147bda6ad24ffbd1ac3d7142c%1768312128.781",
"soci/4.0.3#fe32b9ad5eb47e79ab9e45a68f363945%1774450067.231",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
"secp256k1/0.7.1#481881709eb0bdd0185a12b912bbe8ad%1770910500.329",
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1765850186.86",
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1774398111.888",
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1756234269.497",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1756234289.683",
"xrpl/2.6.1#973af2bf9631f239941dd9f5a100bb84%1759275059.342",
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1756234266.869",
"spdlog/1.15.3#3ca0e9e6b83af4d0151e26541d140c86%1754401846.61",
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1756234262.318",
"re2/20230301#dfd6e2bf050eb90ddd8729cfb4c844a4%1756234257.976",
"rapidjson/cci.20220822#1b9d8c2256876a154172dc5cfbe447c6%1754325007.656",
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
"openssl/3.6.2#4789bbf131b77d0515d15e094c8f697f%1778071755.506",
"nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1775040983.408",
"protobuf/3.21.12#d927114e28de9f4691a6bbcdd9a529d1%1756234251.614",
"openssl/1.1.1w#a8f0792d7c5121b954578a7149d23e03%1756223730.729",
"nudb/2.0.9#c62cfd501e57055a7e0d8ee3d5e5427d%1756234237.107",
"minizip/1.2.13#9e87d57804bd372d6d1e32b1871517a3%1754325004.374",
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1756234228.999",
"libuv/1.46.0#dc28c1f653fa197f00db5b577a6f6011%1754325003.592",
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1778091117.848",
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1756223727.64",
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1756230911.03",
"libarchive/3.8.1#5cf685686322e906cb42706ab7e099a8%1756234256.696",
"http_parser/2.9.4#98d91690d6fd021e9e624218a85d9d97%1754325001.385",
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152",
"grpc/1.78.1#b1a9e74b145cc471bed4dc64dc6eb2c1%1774467387.342",
"fmt/12.1.0#50abab23274d56bb8f42c94b3b9a40c7%1763984116.926",
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772",
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
"cassandra-cpp-driver/2.17.0#bd3934138689482102c265d01288a316%1764175359.611",
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1774439234.681",
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778091165.282",
"gtest/1.14.0#f8f0757a574a8dd747d16af62d6eb1b7%1754325000.842",
"grpc/1.50.1#02291451d1e17200293a409410d1c4e1%1756234248.958",
"fmt/11.2.0#579bb2cdf4a7607621beea4eb4651e0f%1754324999.086",
"doctest/2.4.11#a4211dfc329a16ba9f280f9574025659%1756234220.819",
"date/3.0.4#f74bbba5a08fa388256688743136cb6f%1756234217.493",
"cassandra-cpp-driver/2.17.0#e50919efac8418c26be6671fd702540a%1754324997.363",
"c-ares/1.34.5#b78b91e7cfb1f11ce777a285bbf169c6%1756234217.915",
"bzip2/1.0.8#00b4a4658791c1f06914e087f0e792f5%1756234261.716",
"boost/1.83.0#5d975011d65b51abb2d2f6eb8386b368%1754325043.336",
"benchmark/1.9.4#ce4403f7a24d3e1f907cd9da4b678be4%1754578869.672",
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
"abseil/20230802.1#f0f91485b111dc9837a68972cb19ca7b%1756234220.907"
],
"build_requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056",
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
"cmake/4.3.0#b939a42e98f593fb34d3a8c5cc860359%1774439249.183",
"cmake/3.31.11#f325c933f618a1fcebc1e1c0babfd1ba%1774439246.719",
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1774439233.447",
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1756234269.497",
"protobuf/3.21.12#d927114e28de9f4691a6bbcdd9a529d1%1756234251.614",
"cmake/3.31.8#dde3bde00bb843687e55aea5afa0e220%1756234232.89",
"b2/5.3.3#107c15377719889654eb9a162a673975%1756234226.28"
],
"python_requires": [],
"overrides": {
"zlib/[>=1.2.11 <2]": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb"
],
"zlib/1.3.2": [
"boost/1.83.0": [
null,
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb"
"boost/1.83.0#5d975011d65b51abb2d2f6eb8386b368"
],
"boost/1.91.0": [
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c"
],
"protobuf/[>=5.27.0 <7]": [
"protobuf/6.33.5"
"protobuf/3.21.12": [
null,
"protobuf/3.21.12"
],
"lz4/1.9.4": [
"lz4/1.10.0"
],
"boost/[>=1.83.0 <1.91.0]": [
"boost/1.91.0"
],
"sqlite3/[>=3.44 <4]": [
"sqlite3/3.53.0"
],
"boost/1.83.0": [
"boost/1.91.0"
],
"lz4/[>=1.9.4 <2]": [
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504"
"sqlite3/3.44.2": [
"sqlite3/3.49.1"
]
},
"config_requires": []

View File

@@ -3,68 +3,62 @@ from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
class ClioConan(ConanFile):
name = "clio"
license = "ISC"
author = "Alex Kremer <akremer@ripple.com>, John Freeman <jfreeman@ripple.com>, Ayaz Salikhov <asalikhov@ripple.com>"
url = "https://github.com/xrplf/clio"
description = "Clio RPC server"
settings = "os", "compiler", "build_type", "arch"
name = 'clio'
license = 'ISC'
author = 'Alex Kremer <akremer@ripple.com>, John Freeman <jfreeman@ripple.com>, Ayaz Salikhov <asalikhov@ripple.com>'
url = 'https://github.com/xrplf/clio'
description = 'Clio RPC server'
settings = 'os', 'compiler', 'build_type', 'arch'
options = {}
requires = [
"cassandra-cpp-driver/2.17.0",
"fmt/12.1.0",
"libbacktrace/cci.20210118",
"spdlog/1.17.0",
"xrpl/3.2.0-rc3+e5cf1a0",
'boost/1.83.0',
'cassandra-cpp-driver/2.17.0',
'fmt/11.2.0',
'protobuf/3.21.12',
'grpc/1.50.1',
'openssl/1.1.1w',
'xrpl/2.6.1',
'zlib/1.3.1',
'libbacktrace/cci.20210118',
'spdlog/1.15.3',
]
default_options = {
"cassandra-cpp-driver/*:shared": False,
"date/*:header_only": True,
"grpc/*:secure": True,
"grpc/*:shared": False,
"gtest/*:no_main": True,
"libpq/*:shared": False,
"lz4/*:shared": False,
"openssl/*:shared": False,
"protobuf/*:shared": False,
"protobuf/*:with_zlib": True,
"snappy/*:shared": False,
"xrpl/*:rocksdb": True, # TODO: revert to false when includes are fixed in libxrpl
"xrpl/*:tests": False,
'xrpl/*:tests': False,
'xrpl/*:rocksdb': False,
'cassandra-cpp-driver/*:shared': False,
'date/*:header_only': True,
'grpc/*:shared': False,
'grpc/*:secure': True,
'libpq/*:shared': False,
'lz4/*:shared': False,
'openssl/*:shared': False,
'protobuf/*:shared': False,
'protobuf/*:with_zlib': True,
'snappy/*:shared': False,
'gtest/*:no_main': True,
}
exports_sources = ("CMakeLists.txt", "cmake/*", "src/*")
exports_sources = (
'CMakeLists.txt', 'cmake/*', 'src/*'
)
def requirements(self):
self.requires("gtest/1.17.0")
self.requires("benchmark/1.9.4")
# Clio's own code includes grpc (<grpcpp/...>) and openssl (via
# <boost/asio/ssl>) headers directly, but xrpl does not re-export them
# (only boost/date/xxhash are required with transitive_headers=True).
# So they must be direct requirements of clio to get their include dirs;
# the version pins match xrpl's, so this does not change any package_id.
self.requires("grpc/1.78.1")
self.requires("openssl/3.6.2")
# Pin the remaining transitive deps to the exact versions xrpl uses.
# override=True only sets the version when the package appears
# transitively (it does not make them direct deps), and matches xrpl's
# force=True boost pin that overrides nudb's `boost < 1.91.0` cap.
self.requires("boost/1.91.0", override=True)
self.requires("zlib/1.3.2", override=True)
self.requires('gtest/1.14.0')
self.requires('benchmark/1.9.4')
def configure(self):
if self.settings.compiler == "apple-clang":
self.options["boost"].visibility = "global"
if self.settings.compiler == 'apple-clang':
self.options['boost'].visibility = 'global'
def layout(self):
cmake_layout(self)
# Fix this setting to follow the default introduced in Conan 1.48
# to align with our build instructions.
self.folders.generators = "build/generators"
self.folders.generators = 'build/generators'
generators = "CMakeDeps"
generators = 'CMakeDeps'
def generate(self):
tc = CMakeToolchain(self)

View File

@@ -36,6 +36,7 @@ RUN apt-get update \
libmpfr-dev \
libncurses-dev \
make \
ninja-build \
wget \
zip \
&& apt-get clean \
@@ -54,16 +55,13 @@ RUN pip install -q --no-cache-dir \
# lxml 6.0.0 is not compatible with our image
'lxml<6.0.0' \
cmake \
conan==2.24.0 \
gcovr \
# We're adding pre-commit to this image as well,
# because clang-tidy workflow requires it
pre-commit
conan==2.20.1 \
gcovr
# Install LLVM tools
ARG LLVM_TOOLS_VERSION=20
RUN echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-${LLVM_TOOLS_VERSION} main" >> /etc/apt/sources.list \
RUN echo "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${LLVM_TOOLS_VERSION} main" >> /etc/apt/sources.list \
&& wget --progress=dot:giga -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
RUN apt-get update \
@@ -106,7 +104,6 @@ COPY --from=clio-tools \
/usr/local/bin/git-cliff \
/usr/local/bin/gh \
/usr/local/bin/gdb \
/usr/local/bin/ninja \
/usr/local/bin/
WORKDIR /root

View File

@@ -3,20 +3,19 @@
This image contains an environment to build [Clio](https://github.com/XRPLF/clio), check code and documentation.
It is used in [Clio Github Actions](https://github.com/XRPLF/clio/actions) but can also be used to compile Clio locally.
The image is based on Ubuntu 22.04 and contains:
The image is based on Ubuntu 20.04 and contains:
- ccache 4.12.2
- ccache 4.11.3
- Clang 19
- ClangBuildAnalyzer 1.6.0
- Conan 2.24.0
- Doxygen 1.16.1
- Conan 2.20.1
- Doxygen 1.14
- GCC 15.2.0
- GDB 17.1
- gh 2.83.2
- git-cliff 2.11.0
- mold 2.40.4
- Ninja 1.13.2
- Python 3.8
- GDB 16.3
- gh 2.74
- git-cliff 2.9.1
- mold 2.40.1
- Python 3.13
- and some other useful tools
Conan is set up to build Clio without any additional steps.

View File

@@ -10,5 +10,3 @@ os=Linux
[conf]
tools.build:compiler_executables={"c": "/usr/bin/clang-19", "cpp": "/usr/bin/clang++-19"}
grpc/1.50.1:tools.build:cxxflags+=["-Wno-missing-template-arg-list-after-template-kw"]
user.package:libc_version=2.32
tools.info.package_id:confs+=["user.package:libc_version"]

View File

@@ -9,5 +9,3 @@ os=Linux
[conf]
tools.build:compiler_executables={"c": "/usr/bin/gcc-15", "cpp": "/usr/bin/g++-15"}
user.package:libc_version=2.32
tools.info.package_id:confs+=["user.package:libc_version"]

View File

@@ -3,13 +3,6 @@
{% set sanitizer_opt_map = {"asan": "address", "tsan": "thread", "ubsan": "undefined"} %}
{% set sanitizer = sanitizer_opt_map[sani] %}
{% set sanitizer_b2_flags_map = {
"address": "context-impl=ucontext address-sanitizer=norecover",
"thread": "context-impl=ucontext thread-sanitizer=norecover",
"undefined": "undefined-sanitizer=norecover"
} %}
{% set sanitizer_b2_flags_str = sanitizer_b2_flags_map[sanitizer] %}
{% set sanitizer_build_flags_str = "-fsanitize=" ~ sanitizer ~ " -g -O1 -fno-omit-frame-pointer" %}
{% set sanitizer_build_flags = sanitizer_build_flags_str.split(' ') %}
{% set sanitizer_link_flags_str = "-fsanitize=" ~ sanitizer %}
@@ -18,8 +11,7 @@
include({{ compiler }})
[options]
boost/*:extra_b2_flags="{{ sanitizer_b2_flags_str }}"
boost/*:without_context=False
boost/*:extra_b2_flags="cxxflags=\"{{ sanitizer_build_flags_str }}\" linkflags=\"{{ sanitizer_link_flags_str }}\""
boost/*:without_stacktrace=True
[conf]
@@ -28,10 +20,4 @@ tools.build:cxxflags+={{ sanitizer_build_flags }}
tools.build:exelinkflags+={{ sanitizer_link_flags }}
tools.build:sharedlinkflags+={{ sanitizer_link_flags }}
{% if sanitizer == "address" %}
tools.build:defines+=["BOOST_USE_ASAN", "BOOST_USE_UCONTEXT"]
{% elif sanitizer == "thread" %}
tools.build:defines+=["BOOST_USE_TSAN", "BOOST_USE_UCONTEXT"]
{% endif %}
tools.info.package_id:confs+=["tools.build:cflags", "tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags", "tools.build:defines"]
tools.info.package_id:confs+=["tools.build:cflags", "tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags"]

View File

@@ -1,4 +1,4 @@
FROM ubuntu:22.04
FROM ubuntu:20.04
ARG DEBIAN_FRONTEND=noninteractive

View File

@@ -1,4 +1,4 @@
ARG UBUNTU_VERSION=22.04
ARG UBUNTU_VERSION=20.04
ARG GCC_MAJOR_VERSION=invalid
@@ -8,7 +8,7 @@ ARG UBUNTU_VERSION
ARG GCC_MAJOR_VERSION
ARG BUILD_VERSION=0
ARG BUILD_VERSION=1
ARG DEBIAN_FRONTEND=noninteractive
ARG TARGETARCH
@@ -34,7 +34,6 @@ RUN wget --progress=dot:giga https://gcc.gnu.org/pub/gcc/releases/gcc-$GCC_VERSI
WORKDIR /gcc-$GCC_VERSION
RUN ./contrib/download_prerequisites
# hadolint ignore=DL3059
RUN mkdir /gcc-build
WORKDIR /gcc-build
RUN /gcc-$GCC_VERSION/configure \

View File

@@ -1,6 +1,6 @@
services:
clio_develop:
image: ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031
image: ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d
volumes:
- clio_develop_conan_data:/root/.conan2/p
- clio_develop_ccache:/root/.ccache

View File

@@ -2,7 +2,7 @@
script_dir=$(dirname $0)
pushd $script_dir >/dev/null
pushd $script_dir > /dev/null
function start_container {
if [ -z "$(docker ps -q -f name=clio_develop)" ]; then
@@ -41,26 +41,21 @@ EOF
}
case $1 in
-h | --help)
print_help
;;
-h|--help)
print_help ;;
-t | --terminal)
open_terminal
;;
-t|--terminal)
open_terminal ;;
-s | --stop)
stop_container
;;
-s|--stop)
stop_container ;;
-*)
echo "Unknown option: $1"
print_help
;;
-*)
echo "Unknown option: $1"
print_help ;;
*)
run "$@"
;;
*)
run "$@" ;;
esac
popd >/dev/null
popd > /dev/null

View File

@@ -8,10 +8,11 @@ ARG TARGETARCH
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
ARG BUILD_VERSION=0
ARG BUILD_VERSION=2
RUN apt-get update \
&& apt-get install -y --no-install-recommends --no-install-suggests \
ninja-build \
python3 \
python3-pip \
software-properties-common \
@@ -23,16 +24,7 @@ RUN apt-get update \
WORKDIR /tmp
ARG NINJA_VERSION=1.13.2
RUN wget --progress=dot:giga "https://github.com/ninja-build/ninja/archive/refs/tags/v${NINJA_VERSION}.tar.gz" \
&& tar xf "v${NINJA_VERSION}.tar.gz" \
&& cd "ninja-${NINJA_VERSION}" \
&& ./configure.py --bootstrap \
&& mv ninja /usr/local/bin/ninja \
&& rm -rf /tmp/* /var/tmp/*
ARG MOLD_VERSION=2.40.4
ARG MOLD_VERSION=2.40.1
RUN wget --progress=dot:giga "https://github.com/rui314/mold/archive/refs/tags/v${MOLD_VERSION}.tar.gz" \
&& tar xf "v${MOLD_VERSION}.tar.gz" \
&& cd "mold-${MOLD_VERSION}" \
@@ -42,7 +34,7 @@ RUN wget --progress=dot:giga "https://github.com/rui314/mold/archive/refs/tags/v
&& ninja install \
&& rm -rf /tmp/* /var/tmp/*
ARG CCACHE_VERSION=4.12.2
ARG CCACHE_VERSION=4.11.3
RUN wget --progress=dot:giga "https://github.com/ccache/ccache/releases/download/v${CCACHE_VERSION}/ccache-${CCACHE_VERSION}.tar.gz" \
&& tar xf "ccache-${CCACHE_VERSION}.tar.gz" \
&& cd "ccache-${CCACHE_VERSION}" \
@@ -59,7 +51,7 @@ RUN apt-get update \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
ARG DOXYGEN_VERSION=1.16.1
ARG DOXYGEN_VERSION=1.14.0
RUN wget --progress=dot:giga "https://github.com/doxygen/doxygen/releases/download/Release_${DOXYGEN_VERSION//./_}/doxygen-${DOXYGEN_VERSION}.src.tar.gz" \
&& tar xf "doxygen-${DOXYGEN_VERSION}.src.tar.gz" \
&& cd "doxygen-${DOXYGEN_VERSION}" \
@@ -79,13 +71,13 @@ RUN wget --progress=dot:giga "https://github.com/aras-p/ClangBuildAnalyzer/archi
&& ninja install \
&& rm -rf /tmp/* /var/tmp/*
ARG GIT_CLIFF_VERSION=2.11.0
ARG GIT_CLIFF_VERSION=2.9.1
RUN wget --progress=dot:giga "https://github.com/orhun/git-cliff/releases/download/v${GIT_CLIFF_VERSION}/git-cliff-${GIT_CLIFF_VERSION}-x86_64-unknown-linux-musl.tar.gz" \
&& tar xf git-cliff-${GIT_CLIFF_VERSION}-x86_64-unknown-linux-musl.tar.gz \
&& mv git-cliff-${GIT_CLIFF_VERSION}/git-cliff /usr/local/bin/git-cliff \
&& rm -rf /tmp/* /var/tmp/*
ARG GH_VERSION=2.83.2
ARG GH_VERSION=2.74.0
RUN wget --progress=dot:giga "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_${TARGETARCH}.tar.gz" \
&& tar xf gh_${GH_VERSION}_linux_${TARGETARCH}.tar.gz \
&& mv gh_${GH_VERSION}_linux_${TARGETARCH}/bin/gh /usr/local/bin/gh \
@@ -100,7 +92,7 @@ RUN apt-get update \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
ARG GDB_VERSION=17.1
ARG GDB_VERSION=16.3
RUN wget --progress=dot:giga "https://sourceware.org/pub/gdb/releases/gdb-${GDB_VERSION}.tar.gz" \
&& tar xf "gdb-${GDB_VERSION}.tar.gz" \
&& cd "gdb-${GDB_VERSION}" \

View File

@@ -97,14 +97,30 @@ Now you should be able to download the prebuilt dependencies (including `xrpl` p
#### Conan lockfile
To achieve reproducible dependencies, we use a [Conan lockfile](https://docs.conan.io/2/tutorial/versioning/lockfiles.html).
To achieve reproducible dependencies, we use [Conan lockfile](https://docs.conan.io/2/tutorial/versioning/lockfiles.html).
The `conan.lock` file in the repository contains a "snapshot" of the current dependencies.
It is implicitly used when running `conan` commands, you don't need to specify it.
You have to update this file every time you add a new dependency or change a revision or version of an existing dependency.
To update a lockfile, run from the repository root: `./.github/scripts/conan/regenerate_lockfile.sh`
> [!NOTE]
> Conan uses local cache by default when creating a lockfile.
>
> To ensure, that lockfile creation works the same way on all developer machines, you should clear the local cache before creating a new lockfile.
To create a new lockfile, run the following commands in the repository root:
```bash
conan remove '*' --confirm
rm conan.lock
# This ensure that xrplf remote is the first to be consulted
conan remote add --force --index 0 xrplf https://conan.ripplex.io
conan lock create .
```
> [!NOTE]
> If some dependencies are exclusive for some OS, you may need to run the last command for them adding `--profile:all <PROFILE>`.
## Building Clio
@@ -175,7 +191,7 @@ Open the `index.html` file in your browser to see the documentation pages.
It is also possible to build Clio using [Docker](https://www.docker.com/) if you don't want to install all the dependencies on your machine.
```sh
docker run -it ghcr.io/xrplf/clio-ci:94da8459ddc30e2f0d88a98cba63a57b2cda3031
docker run -it ghcr.io/xrplf/clio-ci:b2be4b51d1d81548ca48e2f2b8f67356b880c96d
git clone https://github.com/XRPLF/clio
cd clio
```

View File

@@ -293,7 +293,7 @@ This document provides a list of all available Clio configuration properties in
- **Required**: True
- **Type**: int
- **Default value**: `1000`
- **Default value**: `1`
- **Constraints**: The minimum value is `1`. The maximum value is `4294967295`.
- **Description**: The maximum size of the server's request queue. If set to `0`, this means there is no queue size limit.
@@ -391,7 +391,7 @@ This document provides a list of all available Clio configuration properties in
- **Type**: double
- **Default value**: `10`
- **Constraints**: The value must be a positive double number.
- **Description**: The number of seconds the server waits to shutdown gracefully. If Clio does not shutdown gracefully after the specified value, it will be killed instead.
- **Description**: The number of milliseconds the server waits to shutdown gracefully. If Clio does not shutdown gracefully after the specified value, it will be killed instead.
### cache.num_diffs
@@ -441,30 +441,6 @@ This document provides a list of all available Clio configuration properties in
- **Constraints**: The value must be one of the following: `sync`, `async`, `none`.
- **Description**: The strategy used for Cache loading.
### cache.file.path
- **Required**: False
- **Type**: string
- **Default value**: None
- **Constraints**: None
- **Description**: The path to a file where cache will be saved to on shutdown and loaded from on startup. If the file couldn't be read Clio will load cache as usual (from DB or from rippled).
### cache.file.max_sequence_age
- **Required**: True
- **Type**: int
- **Default value**: `5000`
- **Constraints**: None
- **Description**: Max allowed difference between the latest sequence in DB and in cache file. If the cache file is too old (contains too low latest sequence) Clio will reject using it.
### cache.file.async_save
- **Required**: True
- **Type**: boolean
- **Default value**: `False`
- **Constraints**: None
- **Description**: When false, Clio waits for cache saving to finish before shutting down. When true, cache saving runs in parallel with other shutdown operations.
### log.channels.[].channel
- **Required**: False

View File

@@ -61,7 +61,7 @@
"ip": "0.0.0.0",
"port": 51233,
// Max number of requests to queue up before rejecting further requests.
// Defaults to 1000 (use 0 to make the queue unbound).
// Defaults to 0, which disables the limit.
"max_queue_size": 500,
// If request contains header with authorization, Clio will check if it matches the prefix 'Password ' + this value's sha256 hash
// If matches, the request will be considered as admin request
@@ -137,11 +137,7 @@
// "num_cursors_from_account": 3200, // Read the cursors from the account table until we have enough cursors to partition the ledger to load concurrently.
"num_markers": 48, // The number of markers is the number of coroutines to load the cache concurrently.
"page_fetch_size": 512, // The number of rows to load for each page.
"load": "async", // "sync" to load cache synchronously or "async" to load cache asynchronously or "none"/"no" to turn off the cache.
"file": {
"path": "./cache.bin",
"max_sequence_age": 5000
}
"load": "async" // "sync" to load cache synchronously or "async" to load cache asynchronously or "none"/"no" to turn off the cache.
},
"prometheus": {
"enabled": true,

View File

@@ -45,7 +45,7 @@ if [[ "1.14.0" > "$version" ]]; then
ERROR
-----------------------------------------------------------------------------
A minimum of version 1.14 of $(which doxygen) is required.
A minimum of version 1.14 of `which doxygen` is required.
Your version is $version. Please upgrade it.
Your changes may fail CI checks.
@@ -55,26 +55,26 @@ EOF
exit 0
fi
mkdir -p ${DOCDIR} >/dev/null 2>&1
pushd ${DOCDIR} >/dev/null 2>&1
mkdir -p ${DOCDIR} > /dev/null 2>&1
pushd ${DOCDIR} > /dev/null 2>&1
cat ${ROOT}/docs/Doxyfile |
sed \
-e "s/\${LINT}/YES/" \
-e "s/\${WARN_AS_ERROR}/NO/" \
-e "s!\${SOURCE}!${ROOT}!" \
-e "s/\${USE_DOT}/NO/" \
-e "s/\${EXCLUDES}/impl/" |
${DOXYGEN} - 2>${TMPFILE} 1>/dev/null
cat ${ROOT}/docs/Doxyfile | \
sed \
-e "s/\${LINT}/YES/" \
-e "s/\${WARN_AS_ERROR}/NO/" \
-e "s!\${SOURCE}!${ROOT}!" \
-e "s/\${USE_DOT}/NO/" \
-e "s/\${EXCLUDES}/impl/" \
| ${DOXYGEN} - 2> ${TMPFILE} 1> /dev/null
# We don't want to check for default values and typedefs as well as for member variables
OUT=$(cat ${TMPFILE} |
grep -v "=default" |
grep -v "\(variable\)" |
grep -v "\(typedef\)")
OUT=$(cat ${TMPFILE} \
| grep -v "=default" \
| grep -v "\(variable\)" \
| grep -v "\(typedef\)")
rm -rf ${TMPFILE} >/dev/null 2>&1
popd >/dev/null 2>&1
rm -rf ${TMPFILE} > /dev/null 2>&1
popd > /dev/null 2>&1
if [[ ! -z "$OUT" ]]; then
cat <<EOF

View File

@@ -23,10 +23,10 @@ fix_includes() {
file_path_fixed="${file_path}.tmp.fixed"
# Make all includes to be <...> style
sed -E 's|#include "(.*)"|#include <\1>|g' "$file_path" >"$file_path_all_global"
sed -E 's|#include "(.*)"|#include <\1>|g' "$file_path" > "$file_path_all_global"
# Make local includes to be "..." style
sed -E "s|#include <(($main_src_dirs)/.*)>|#include \"\1\"|g" "$file_path_all_global" >"$file_path_fixed"
sed -E "s|#include <(($main_src_dirs)/.*)>|#include \"\1\"|g" "$file_path_all_global" > "$file_path_fixed"
rm "$file_path_all_global"
# Check if the temporary file is different from the original file

View File

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

View File

@@ -4,7 +4,7 @@
#
set -e -o pipefail
if ! command -v gofmt &>/dev/null; then
if ! command -v gofmt &> /dev/null ; then
echo "gofmt not installed or available in the PATH" >&2
exit 1
fi

View File

@@ -1,4 +1,5 @@
#!/bin/bash
#!/bin/sh
# git for-each-ref refs/tags # see which tags are annotated and which are lightweight. Annotated tags are "tag" objects.
# # Set these so your commits and tags are always signed
@@ -6,7 +7,7 @@
# git config tag.gpgsign true
verify_commit_signed() {
if git verify-commit HEAD &>/dev/null; then
if git verify-commit HEAD &> /dev/null; then
:
# echo "HEAD commit seems signed..."
else
@@ -16,7 +17,7 @@ verify_commit_signed() {
}
verify_tag() {
if git describe --exact-match --tags HEAD &>/dev/null; then
if git describe --exact-match --tags HEAD &> /dev/null; then
: # You might be ok to push
# echo "Tag is annotated."
return 0
@@ -27,7 +28,7 @@ verify_tag() {
}
verify_tag_signed() {
if git verify-tag "$version" &>/dev/null; then
if git verify-tag "$version" &> /dev/null ; then
: # ok, I guess we'll let you push
# echo "Tag appears signed"
return 0
@@ -39,11 +40,11 @@ verify_tag_signed() {
}
# Check some things if we're pushing a branch called "release/"
if echo "$PRE_COMMIT_REMOTE_BRANCH" | grep ^refs\/heads\/release\/ &>/dev/null; then
if echo "$PRE_COMMIT_REMOTE_BRANCH" | grep ^refs\/heads\/release\/ &> /dev/null ; then
version=$(git tag --points-at HEAD)
echo "Looks like you're trying to push a $version release..."
echo "Making sure you've signed and tagged it."
if verify_commit_signed && verify_tag && verify_tag_signed; then
if verify_commit_signed && verify_tag && verify_tag_signed ; then
: # Ok, I guess you can push
else
exit 1

View File

@@ -2,6 +2,7 @@ add_subdirectory(util)
add_subdirectory(data)
add_subdirectory(cluster)
add_subdirectory(etl)
add_subdirectory(etlng)
add_subdirectory(feed)
add_subdirectory(rpc)
add_subdirectory(web)

View File

@@ -5,9 +5,10 @@ target_link_libraries(
clio_app
PUBLIC clio_cluster
clio_etl
clio_etlng
clio_feed
clio_migration
clio_rpc
clio_web
clio_rpc
clio_migration
PRIVATE Boost::program_options
)

View File

@@ -77,10 +77,7 @@ CliArgs::parse(int argc, char const* argv[])
}
if (parsed.contains("version")) {
std::cout << util::build::getClioFullVersionString() << '\n'
<< "Git commit hash: " << util::build::getGitCommitHash() << '\n'
<< "Git build branch: " << util::build::getGitBuildBranch() << '\n'
<< "Build date: " << util::build::getBuildDate() << '\n';
std::cout << util::build::getClioFullVersionString() << '\n';
return Action{Action::Exit{EXIT_SUCCESS}};
}

View File

@@ -25,10 +25,11 @@
#include "data/AmendmentCenter.hpp"
#include "data/BackendFactory.hpp"
#include "data/LedgerCache.hpp"
#include "data/LedgerCacheSaver.hpp"
#include "etl/ETLService.hpp"
#include "etl/LoadBalancer.hpp"
#include "etl/NetworkValidatedLedgers.hpp"
#include "etlng/LoadBalancer.hpp"
#include "etlng/LoadBalancerInterface.hpp"
#include "feed/SubscriptionManager.hpp"
#include "migration/MigrationInspectorFactory.hpp"
#include "rpc/Counters.hpp"
@@ -56,7 +57,6 @@
#include <cstdlib>
#include <memory>
#include <optional>
#include <string>
#include <thread>
#include <utility>
#include <vector>
@@ -91,7 +91,6 @@ ClioApplication::ClioApplication(util::config::ClioConfigDefinition const& confi
{
LOG(util::LogService::info()) << "Clio version: " << util::build::getClioFullVersionString();
signalsHandler_.subscribeToStop([this]() { appStopper_.stop(); });
appStopper_.setOnComplete([this]() { signalsHandler_.notifyGracefulShutdownComplete(); });
}
int
@@ -100,23 +99,20 @@ ClioApplication::run(bool const useNgWebServer)
auto const threads = config_.get<uint16_t>("io_threads");
LOG(util::LogService::info()) << "Number of io threads = " << threads;
// Similarly we need a context to run ETL on
// In the future we can remove the raw ioc and use ctx instead
// This context should be above ioc because its reference is getting into tasks inside ioc
util::async::CoroExecutionContext ctx{threads};
// IO context to handle all incoming requests, as well as other things.
// This is not the only io context in the application.
boost::asio::io_context ioc{threads};
// Similarly we need a context to run ETLng on
// In the future we can remove the raw ioc and use ctx instead
util::async::CoroExecutionContext ctx{threads};
// Rate limiter, to prevent abuse
auto whitelistHandler = web::dosguard::WhitelistHandler{config_};
auto const dosguardWeights = web::dosguard::Weights::make(config_);
auto dosGuard = web::dosguard::DOSGuard{config_, whitelistHandler, dosguardWeights};
auto sweepHandler = web::dosguard::IntervalSweepHandler{config_, ioc, dosGuard};
auto cache = data::LedgerCache{};
auto cacheSaver = data::LedgerCacheSaver{config_, cache};
// Interface to the database
auto backend = data::makeBackend(config_, cache);
@@ -146,12 +142,20 @@ ClioApplication::run(bool const useNgWebServer)
// ETL uses the balancer to extract data.
// The server uses the balancer to forward RPCs to a rippled node.
// The balancer itself publishes to streams (transactions_proposed and accounts_proposed)
auto balancer = etl::LoadBalancer::makeLoadBalancer(
config_, ioc, backend, subscriptions, std::make_unique<util::MTRandomGenerator>(), ledgers
);
auto balancer = [&] -> std::shared_ptr<etlng::LoadBalancerInterface> {
if (config_.get<bool>("__ng_etl")) {
return etlng::LoadBalancer::makeLoadBalancer(
config_, ioc, backend, subscriptions, std::make_unique<util::MTRandomGenerator>(), ledgers
);
}
return etl::LoadBalancer::makeLoadBalancer(
config_, ioc, backend, subscriptions, std::make_unique<util::MTRandomGenerator>(), ledgers
);
}();
// ETL is responsible for writing and publishing to streams. In read-only mode, ETL only publishes
auto etl = etl::ETLService::makeETLService(config_, ctx, backend, subscriptions, balancer, ledgers);
auto etl = etl::ETLService::makeETLService(config_, ioc, ctx, backend, subscriptions, balancer, ledgers);
auto workQueue = rpc::WorkQueue::makeWorkQueue(config_);
auto counters = rpc::Counters::makeCounters(workQueue);
@@ -183,7 +187,7 @@ ClioApplication::run(bool const useNgWebServer)
return EXIT_FAILURE;
}
httpServer->onGet("/metrics", MetricsHandler{adminVerifier, workQueue});
httpServer->onGet("/metrics", MetricsHandler{adminVerifier});
httpServer->onGet("/health", HealthCheckHandler{});
httpServer->onGet("/cache_state", CacheStateHandler{cache});
auto requestHandler = RequestHandler{adminVerifier, handler};
@@ -197,7 +201,7 @@ ClioApplication::run(bool const useNgWebServer)
}
appStopper_.setOnStop(
Stopper::makeOnStopCallback(httpServer.value(), *balancer, *etl, *subscriptions, *backend, cacheSaver, ioc)
Stopper::makeOnStopCallback(httpServer.value(), *balancer, *etl, *subscriptions, *backend, ioc)
);
// Blocks until stopped.
@@ -212,9 +216,6 @@ ClioApplication::run(bool const useNgWebServer)
auto handler = std::make_shared<web::RPCServerHandler<RPCEngineType>>(config_, backend, rpcEngine, etl, dosGuard);
auto const httpServer = web::makeHttpServer(config_, ioc, dosGuard, handler, cache);
appStopper_.setOnStop(
Stopper::makeOnStopCallback(*httpServer, *balancer, *etl, *subscriptions, *backend, cacheSaver, ioc)
);
// Blocks until stopped.
// When stopped, shared_ptrs fall out of scope

View File

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

View File

@@ -20,13 +20,12 @@
#pragma once
#include "data/BackendInterface.hpp"
#include "data/LedgerCacheSaver.hpp"
#include "etl/ETLServiceInterface.hpp"
#include "etl/LoadBalancerInterface.hpp"
#include "etlng/ETLServiceInterface.hpp"
#include "etlng/LoadBalancerInterface.hpp"
#include "feed/SubscriptionManagerInterface.hpp"
#include "util/CoroutineGroup.hpp"
#include "util/log/Logger.hpp"
#include "web/interface/Concepts.hpp"
#include "web/ng/Server.hpp"
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/io_context.hpp>
@@ -43,7 +42,6 @@ namespace app {
class Stopper {
boost::asio::io_context ctx_;
std::thread worker_;
std::function<void()> onCompleteCallback_;
public:
/**
@@ -59,14 +57,6 @@ public:
void
setOnStop(std::function<void(boost::asio::yield_context)> cb);
/**
* @brief Set the callback to be called when graceful shutdown completes.
*
* @param cb The callback to be called when shutdown completes.
*/
void
setOnComplete(std::function<void()> cb);
/**
* @brief Stop the application and run the shutdown tasks.
*/
@@ -81,25 +71,21 @@ public:
* @param etl The ETL service to stop.
* @param subscriptions The subscription manager to stop.
* @param backend The backend to stop.
* @param cacheSaver The ledger cache saver
* @param ioc The io_context to stop.
* @return The callback to be called on application stop.
*/
template <web::SomeServer ServerType, data::SomeLedgerCacheSaver LedgerCacheSaverType>
template <web::ng::SomeServer ServerType>
static std::function<void(boost::asio::yield_context)>
makeOnStopCallback(
ServerType& server,
etl::LoadBalancerInterface& balancer,
etl::ETLServiceInterface& etl,
etlng::LoadBalancerInterface& balancer,
etlng::ETLServiceInterface& etl,
feed::SubscriptionManagerInterface& subscriptions,
data::BackendInterface& backend,
LedgerCacheSaverType& cacheSaver,
boost::asio::io_context& ioc
)
{
return [&](boost::asio::yield_context yield) {
cacheSaver.save();
util::CoroutineGroup coroutineGroup{yield};
coroutineGroup.spawn(yield, [&server](auto innerYield) {
server.stop(innerYield);
@@ -120,8 +106,6 @@ public:
backend.waitForWritesToFinish();
LOG(util::LogService::info()) << "Backend writes finished";
cacheSaver.waitToFinish();
ioc.stop();
LOG(util::LogService::info()) << "io_context stopped";

View File

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

View File

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

View File

@@ -57,7 +57,7 @@ supportedAmendments()
}
bool
lookupAmendment(auto const& allAmendments, std::vector<xrpl::uint256> const& ledgerAmendments, std::string_view name)
lookupAmendment(auto const& allAmendments, std::vector<ripple::uint256> const& ledgerAmendments, std::string_view name)
{
namespace rg = std::ranges;
if (auto const am = rg::find(allAmendments, name, &data::Amendment::name); am != rg::end(allAmendments))
@@ -91,7 +91,7 @@ operator std::string_view() const
}
AmendmentKey::
operator xrpl::uint256() const
operator ripple::uint256() const
{
return Amendment::getAmendmentId(name);
}
@@ -102,14 +102,14 @@ AmendmentCenter::AmendmentCenter(std::shared_ptr<data::BackendInterface> const&
namespace vs = std::views;
rg::copy(
xrpl::allAmendments() | vs::transform([&](auto const& p) {
ripple::allAmendments() | vs::transform([&](auto const& p) {
auto const& [name, support] = p;
return Amendment{
.name = name,
.feature = Amendment::getAmendmentId(name),
.isSupportedByXRPL = support != xrpl::AmendmentSupport::Unsupported,
.isSupportedByXRPL = support != ripple::AmendmentSupport::Unsupported,
.isSupportedByClio = rg::find(supportedAmendments(), name) != rg::end(supportedAmendments()),
.isRetired = support == xrpl::AmendmentSupport::Retired
.isRetired = support == ripple::AmendmentSupport::Retired
};
}),
std::back_inserter(all_)
@@ -146,12 +146,9 @@ AmendmentCenter::isEnabled(AmendmentKey const& key, uint32_t seq) const
bool
AmendmentCenter::isEnabled(boost::asio::yield_context yield, AmendmentKey const& key, uint32_t seq) const
{
try {
if (auto const listAmendments = fetchAmendmentsList(yield, seq); listAmendments)
return lookupAmendment(all_, *listAmendments, key);
} catch (std::runtime_error const&) {
return false; // Some old ledger does not contain Amendments ledger object so do best we can for now
}
if (auto const listAmendments = fetchAmendmentsList(yield, seq); listAmendments)
return lookupAmendment(all_, *listAmendments, key);
return false;
}
@@ -160,19 +157,13 @@ AmendmentCenter::isEnabled(boost::asio::yield_context yield, std::vector<Amendme
{
namespace rg = std::ranges;
try {
if (auto const listAmendments = fetchAmendmentsList(yield, seq); listAmendments) {
std::vector<bool> out;
rg::transform(keys, std::back_inserter(out), [this, &listAmendments](auto const& key) {
return lookupAmendment(all_, *listAmendments, key);
});
if (auto const listAmendments = fetchAmendmentsList(yield, seq); listAmendments) {
std::vector<bool> out;
rg::transform(keys, std::back_inserter(out), [this, &listAmendments](auto const& key) {
return lookupAmendment(all_, *listAmendments, key);
});
return out;
}
} catch (std::runtime_error const&) {
return std::vector<bool>(
keys.size(), false
); // Some old ledger does not contain Amendments ledger object so do best we can for now
return out;
}
return std::vector<bool>(keys.size(), false);
@@ -191,25 +182,25 @@ AmendmentCenter::operator[](AmendmentKey const& key) const
return getAmendment(key);
}
xrpl::uint256
ripple::uint256
Amendment::getAmendmentId(std::string_view name)
{
return xrpl::sha512Half(xrpl::Slice(name.data(), name.size()));
return ripple::sha512Half(ripple::Slice(name.data(), name.size()));
}
std::optional<std::vector<xrpl::uint256>>
std::optional<std::vector<ripple::uint256>>
AmendmentCenter::fetchAmendmentsList(boost::asio::yield_context yield, uint32_t seq) const
{
// the amendments should always be present on the ledger
auto const amendments = backend_->fetchLedgerObject(xrpl::keylet::amendments().key, seq, yield);
auto const amendments = backend_->fetchLedgerObject(ripple::keylet::amendments().key, seq, yield);
if (not amendments.has_value())
throw std::runtime_error("Amendments ledger object must be present in the database");
xrpl::SLE const amendmentsSLE{
xrpl::SerialIter{amendments->data(), amendments->size()}, xrpl::keylet::amendments().key
ripple::SLE const amendmentsSLE{
ripple::SerialIter{amendments->data(), amendments->size()}, ripple::keylet::amendments().key
};
return amendmentsSLE[~xrpl::sfAmendments];
return amendmentsSLE[~ripple::sfAmendments];
}
} // namespace data

View File

@@ -147,17 +147,6 @@ struct Amendments {
REGISTER(fixAMMClawbackRounding);
REGISTER(fixMPTDeliveredAmount);
REGISTER(fixPriceOracleOrder);
REGISTER(DynamicMPT);
REGISTER(fixDelegateV1_1);
REGISTER(fixDirectoryLimit);
REGISTER(fixIncludeKeyletFields);
REGISTER(fixTokenEscrowV1);
REGISTER(LendingProtocol);
REGISTER(MPTokensV2);
REGISTER(PermissionDelegationV1_1);
REGISTER(fixBatchInnerSigs);
REGISTER(fixCleanup3_1_3);
REGISTER(fixCleanup3_2_0);
// Obsolete but supported by libxrpl
REGISTER(CryptoConditionsSuite);
@@ -281,7 +270,7 @@ public:
operator[](AmendmentKey const& key) const final;
private:
[[nodiscard]] std::optional<std::vector<xrpl::uint256>>
[[nodiscard]] std::optional<std::vector<ripple::uint256>>
fetchAmendmentsList(boost::asio::yield_context yield, uint32_t seq) const;
};

View File

@@ -64,7 +64,7 @@ BackendInterface::finishWrites(std::uint32_t const ledgerSequence)
void
BackendInterface::writeLedgerObject(std::string&& key, std::uint32_t const seq, std::string&& blob)
{
ASSERT(key.size() == sizeof(xrpl::uint256), "Key must be 256 bits");
ASSERT(key.size() == sizeof(ripple::uint256), "Key must be 256 bits");
doWriteLedgerObject(std::move(key), seq, std::move(blob));
}
@@ -77,14 +77,14 @@ BackendInterface::hardFetchLedgerRangeNoThrow() const
// *** state data methods
std::optional<Blob>
BackendInterface::fetchLedgerObject(
xrpl::uint256 const& key,
ripple::uint256 const& key,
std::uint32_t const sequence,
boost::asio::yield_context yield
) const
{
auto obj = cache_.get().get(key, sequence);
if (obj) {
LOG(log_.trace()) << "Cache hit - " << xrpl::strHex(key);
LOG(log_.trace()) << "Cache hit - " << ripple::strHex(key);
return obj;
}
@@ -99,7 +99,7 @@ BackendInterface::fetchLedgerObject(
std::optional<std::uint32_t>
BackendInterface::fetchLedgerObjectSeq(
xrpl::uint256 const& key,
ripple::uint256 const& key,
std::uint32_t const sequence,
boost::asio::yield_context yield
) const
@@ -112,14 +112,14 @@ BackendInterface::fetchLedgerObjectSeq(
std::vector<Blob>
BackendInterface::fetchLedgerObjects(
std::vector<xrpl::uint256> const& keys,
std::vector<ripple::uint256> const& keys,
std::uint32_t const sequence,
boost::asio::yield_context yield
) const
{
std::vector<Blob> results;
results.resize(keys.size());
std::vector<xrpl::uint256> misses;
std::vector<ripple::uint256> misses;
for (size_t i = 0; i < keys.size(); ++i) {
auto obj = cache_.get().get(keys[i], sequence);
if (obj) {
@@ -144,25 +144,25 @@ BackendInterface::fetchLedgerObjects(
}
// Fetches the successor to key/index
std::optional<xrpl::uint256>
std::optional<ripple::uint256>
BackendInterface::fetchSuccessorKey(
xrpl::uint256 key,
ripple::uint256 key,
std::uint32_t const ledgerSequence,
boost::asio::yield_context yield
) const
{
auto succ = cache_.get().getSuccessor(key, ledgerSequence);
if (succ) {
LOG(log_.trace()) << "Cache hit - " << xrpl::strHex(key);
LOG(log_.trace()) << "Cache hit - " << ripple::strHex(key);
} else {
LOG(log_.trace()) << "Cache miss - " << xrpl::strHex(key);
LOG(log_.trace()) << "Cache miss - " << ripple::strHex(key);
}
return succ ? succ->key : doFetchSuccessorKey(key, ledgerSequence, yield);
}
std::optional<LedgerObject>
BackendInterface::fetchSuccessorObject(
xrpl::uint256 key,
ripple::uint256 key,
std::uint32_t const ledgerSequence,
boost::asio::yield_context yield
) const
@@ -180,7 +180,7 @@ BackendInterface::fetchSuccessorObject(
BookOffersPage
BackendInterface::fetchBookOffers(
xrpl::uint256 const& book,
ripple::uint256 const& book,
std::uint32_t const ledgerSequence,
std::uint32_t const limit,
boost::asio::yield_context yield
@@ -189,9 +189,9 @@ BackendInterface::fetchBookOffers(
// TODO try to speed this up. This can take a few seconds. The goal is
// to get it down to a few hundred milliseconds.
BookOffersPage page;
xrpl::uint256 const bookEnd = xrpl::getQualityNext(book);
xrpl::uint256 uTipIndex = book;
std::vector<xrpl::uint256> keys;
ripple::uint256 const bookEnd = ripple::getQualityNext(book);
ripple::uint256 uTipIndex = book;
std::vector<ripple::uint256> keys;
auto getMillis = [](auto diff) { return std::chrono::duration_cast<std::chrono::milliseconds>(diff).count(); };
auto begin = std::chrono::system_clock::now();
std::uint32_t numSucc = 0;
@@ -211,17 +211,17 @@ BackendInterface::fetchBookOffers(
uTipIndex = offerDir->key;
while (keys.size() < limit) {
++numPages;
xrpl::STLedgerEntry const sle{
xrpl::SerialIter{offerDir->blob.data(), offerDir->blob.size()}, offerDir->key
ripple::STLedgerEntry const sle{
ripple::SerialIter{offerDir->blob.data(), offerDir->blob.size()}, offerDir->key
};
auto indexes = sle.getFieldV256(xrpl::sfIndexes);
auto indexes = sle.getFieldV256(ripple::sfIndexes);
keys.insert(keys.end(), indexes.begin(), indexes.end());
auto next = sle.getFieldU64(xrpl::sfIndexNext);
auto next = sle.getFieldU64(ripple::sfIndexNext);
if (next == 0u) {
LOG(log_.trace()) << "Next is empty. breaking";
break;
}
auto nextKey = xrpl::keylet::page(uTipIndex, next);
auto nextKey = ripple::keylet::page(uTipIndex, next);
auto nextDir = fetchLedgerObject(nextKey.key, ledgerSequence, yield);
ASSERT(nextDir.has_value(), "Next dir must exist");
offerDir->blob = *nextDir;
@@ -233,7 +233,7 @@ BackendInterface::fetchBookOffers(
auto mid = std::chrono::system_clock::now();
auto objs = fetchLedgerObjects(keys, ledgerSequence, yield);
for (size_t i = 0; i < keys.size() && i < limit; ++i) {
LOG(log_.trace()) << "Key = " << xrpl::strHex(keys[i]) << " blob = " << xrpl::strHex(objs[i])
LOG(log_.trace()) << "Key = " << ripple::strHex(keys[i]) << " blob = " << ripple::strHex(objs[i])
<< " ledgerSequence = " << ledgerSequence;
ASSERT(!objs[i].empty(), "Ledger object can't be empty");
page.offers.push_back({keys[i], objs[i]});
@@ -247,7 +247,7 @@ BackendInterface::fetchBookOffers(
<< ". num pages = " << std::to_string(numPages) << ". Fetching all objects took "
<< std::to_string(getMillis(end - mid))
<< " milliseconds. total time = " << std::to_string(getMillis(end - begin)) << " milliseconds"
<< " book = " << xrpl::strHex(book);
<< " book = " << ripple::strHex(book);
return page;
}
@@ -270,7 +270,7 @@ BackendInterface::updateRange(uint32_t newMax)
{
std::scoped_lock const lck(rngMtx_);
if (range_.has_value() and newMax < range_->maxSequence) {
if (range_.has_value() && newMax < range_->maxSequence) {
ASSERT(
false,
"Range shouldn't exist yet or newMax should be at least range->maxSequence. newMax = {}, "
@@ -280,14 +280,11 @@ BackendInterface::updateRange(uint32_t newMax)
);
}
updateRangeImpl(newMax);
}
void
BackendInterface::forceUpdateRange(uint32_t newMax)
{
std::scoped_lock const lck(rngMtx_);
updateRangeImpl(newMax);
if (!range_.has_value()) {
range_ = {.minSequence = newMax, .maxSequence = newMax};
} else {
range_->maxSequence = newMax;
}
}
void
@@ -305,7 +302,7 @@ BackendInterface::setRange(uint32_t min, uint32_t max, bool force)
LedgerPage
BackendInterface::fetchLedgerPage(
std::optional<xrpl::uint256> const& cursor,
std::optional<ripple::uint256> const& cursor,
std::uint32_t const ledgerSequence,
std::uint32_t const limit,
bool outOfOrder,
@@ -314,11 +311,11 @@ BackendInterface::fetchLedgerPage(
{
LedgerPage page;
std::vector<xrpl::uint256> keys;
std::vector<ripple::uint256> keys;
bool reachedEnd = false;
while (keys.size() < limit && !reachedEnd) {
xrpl::uint256 const& curCursor = [&]() {
ripple::uint256 const& curCursor = [&]() {
if (!keys.empty())
return keys.back();
return (cursor ? *cursor : kFIRST_KEY);
@@ -339,11 +336,11 @@ BackendInterface::fetchLedgerPage(
if (!objects[i].empty()) {
page.objects.push_back({keys[i], std::move(objects[i])});
} else if (!outOfOrder) {
LOG(log_.error()) << "Deleted or non-existent object in successor table. key = " << xrpl::strHex(keys[i])
LOG(log_.error()) << "Deleted or non-existent object in successor table. key = " << ripple::strHex(keys[i])
<< " - seq = " << ledgerSequence;
std::stringstream msg;
for (size_t j = 0; j < objects.size(); ++j) {
msg << " - " << xrpl::strHex(keys[j]);
msg << " - " << ripple::strHex(keys[j]);
}
LOG(log_.error()) << msg.str();
@@ -357,12 +354,12 @@ BackendInterface::fetchLedgerPage(
return page;
}
std::optional<xrpl::Fees>
std::optional<ripple::Fees>
BackendInterface::fetchFees(std::uint32_t const seq, boost::asio::yield_context yield) const
{
xrpl::Fees fees;
ripple::Fees fees;
auto key = xrpl::keylet::fees().key;
auto key = ripple::keylet::fees().key;
auto bytes = fetchLedgerObject(key, seq, yield);
if (!bytes) {
@@ -370,17 +367,17 @@ BackendInterface::fetchFees(std::uint32_t const seq, boost::asio::yield_context
return {};
}
xrpl::SerialIter it(bytes->data(), bytes->size());
xrpl::SLE const sle{it, key};
ripple::SerialIter it(bytes->data(), bytes->size());
ripple::SLE const sle{it, key};
// XRPFees amendment introduced new fields for fees calculations.
// New fields are set and the old fields are removed via `set_fees` tx.
// Fallback to old fields if `set_fees` was not yet used to update the fields on this tx.
auto hasNewFields = false;
{
auto const baseFeeXRP = sle.at(~xrpl::sfBaseFeeDrops);
auto const reserveBaseXRP = sle.at(~xrpl::sfReserveBaseDrops);
auto const reserveIncrementXRP = sle.at(~xrpl::sfReserveIncrementDrops);
auto const baseFeeXRP = sle.at(~ripple::sfBaseFeeDrops);
auto const reserveBaseXRP = sle.at(~ripple::sfReserveBaseDrops);
auto const reserveIncrementXRP = sle.at(~ripple::sfReserveIncrementDrops);
if (baseFeeXRP)
fees.base = baseFeeXRP->xrp();
@@ -396,9 +393,9 @@ BackendInterface::fetchFees(std::uint32_t const seq, boost::asio::yield_context
if (not hasNewFields) {
// Fallback to old fields
auto const baseFee = sle.at(~xrpl::sfBaseFee);
auto const reserveBase = sle.at(~xrpl::sfReserveBase);
auto const reserveIncrement = sle.at(~xrpl::sfReserveIncrement);
auto const baseFee = sle.at(~ripple::sfBaseFee);
auto const reserveBase = sle.at(~ripple::sfReserveBase);
auto const reserveIncrement = sle.at(~ripple::sfReserveIncrement);
if (baseFee)
fees.base = baseFee.value();
@@ -413,14 +410,4 @@ BackendInterface::fetchFees(std::uint32_t const seq, boost::asio::yield_context
return fees;
}
void
BackendInterface::updateRangeImpl(uint32_t newMax)
{
if (!range_.has_value()) {
range_ = {.minSequence = newMax, .maxSequence = newMax};
} else {
range_->maxSequence = newMax;
}
}
} // namespace data

View File

@@ -192,9 +192,9 @@ public:
*
* @param sequence The sequence number to fetch for
* @param yield The coroutine context
* @return The xrpl::LedgerHeader if found; nullopt otherwise
* @return The ripple::LedgerHeader if found; nullopt otherwise
*/
virtual std::optional<xrpl::LedgerHeader>
virtual std::optional<ripple::LedgerHeader>
fetchLedgerBySequence(std::uint32_t sequence, boost::asio::yield_context yield) const = 0;
/**
@@ -202,10 +202,10 @@ public:
*
* @param hash The hash to fetch for
* @param yield The coroutine context
* @return The xrpl::LedgerHeader if found; nullopt otherwise
* @return The ripple::LedgerHeader if found; nullopt otherwise
*/
virtual std::optional<xrpl::LedgerHeader>
fetchLedgerByHash(xrpl::uint256 const& hash, boost::asio::yield_context yield) const = 0;
virtual std::optional<ripple::LedgerHeader>
fetchLedgerByHash(ripple::uint256 const& hash, boost::asio::yield_context yield) const = 0;
/**
* @brief Fetches the latest ledger sequence.
@@ -231,9 +231,9 @@ public:
* @param pageSize The maximum number of accounts per page
* @param seq The accounts need to exist for this sequence
* @param yield The coroutine context
* @return A vector of xrpl::uint256 representing the account roots
* @return A vector of ripple::uint256 representing the account roots
*/
virtual std::vector<xrpl::uint256>
virtual std::vector<ripple::uint256>
fetchAccountRoots(
std::uint32_t number,
std::uint32_t pageSize,
@@ -249,15 +249,6 @@ public:
void
updateRange(uint32_t newMax);
/**
* @brief Updates the range of sequences that are stored in the DB without any checks
* @note In the most cases you should use updateRange() instead
*
* @param newMax The new maximum sequence available
*/
void
forceUpdateRange(uint32_t newMax);
/**
* @brief Sets the range of sequences that are stored in the DB.
*
@@ -275,7 +266,7 @@ public:
* @param yield The coroutine context
* @return Fees if fees are found; nullopt otherwise
*/
std::optional<xrpl::Fees>
std::optional<ripple::Fees>
fetchFees(std::uint32_t seq, boost::asio::yield_context yield) const;
/**
@@ -286,7 +277,7 @@ public:
* @return TransactionAndMetadata if transaction is found; nullopt otherwise
*/
virtual std::optional<TransactionAndMetadata>
fetchTransaction(xrpl::uint256 const& hash, boost::asio::yield_context yield) const = 0;
fetchTransaction(ripple::uint256 const& hash, boost::asio::yield_context yield) const = 0;
/**
* @brief Fetches multiple transactions.
@@ -296,7 +287,7 @@ public:
* @return A vector of TransactionAndMetadata matching the given hashes
*/
virtual std::vector<TransactionAndMetadata>
fetchTransactions(std::vector<xrpl::uint256> const& hashes, boost::asio::yield_context yield) const = 0;
fetchTransactions(std::vector<ripple::uint256> const& hashes, boost::asio::yield_context yield) const = 0;
/**
* @brief Fetches all transactions for a specific account.
@@ -310,7 +301,7 @@ public:
*/
virtual TransactionsAndCursor
fetchAccountTransactions(
xrpl::AccountID const& account,
ripple::AccountID const& account,
std::uint32_t limit,
bool forward,
std::optional<TransactionsCursor> const& txnCursor,
@@ -332,9 +323,9 @@ public:
*
* @param ledgerSequence The ledger sequence to fetch for
* @param yield The coroutine context
* @return Hashes as xrpl::uint256 in a vector
* @return Hashes as ripple::uint256 in a vector
*/
virtual std::vector<xrpl::uint256>
virtual std::vector<ripple::uint256>
fetchAllTransactionHashesInLedger(std::uint32_t ledgerSequence, boost::asio::yield_context yield) const = 0;
/**
@@ -346,7 +337,7 @@ public:
* @return NFT object on success; nullopt otherwise
*/
virtual std::optional<NFT>
fetchNFT(xrpl::uint256 const& tokenID, std::uint32_t ledgerSequence, boost::asio::yield_context yield) const = 0;
fetchNFT(ripple::uint256 const& tokenID, std::uint32_t ledgerSequence, boost::asio::yield_context yield) const = 0;
/**
* @brief Fetches all transactions for a specific NFT.
@@ -360,7 +351,7 @@ public:
*/
virtual TransactionsAndCursor
fetchNFTTransactions(
xrpl::uint256 const& tokenID,
ripple::uint256 const& tokenID,
std::uint32_t limit,
bool forward,
std::optional<TransactionsCursor> const& cursorIn,
@@ -380,11 +371,11 @@ public:
*/
virtual NFTsAndCursor
fetchNFTsByIssuer(
xrpl::AccountID const& issuer,
ripple::AccountID const& issuer,
std::optional<std::uint32_t> const& taxon,
std::uint32_t ledgerSequence,
std::uint32_t limit,
std::optional<xrpl::uint256> const& cursorIn,
std::optional<ripple::uint256> const& cursorIn,
boost::asio::yield_context yield
) const = 0;
@@ -400,9 +391,9 @@ public:
*/
virtual MPTHoldersAndCursor
fetchMPTHolders(
xrpl::uint192 const& mptID,
ripple::uint192 const& mptID,
std::uint32_t const limit,
std::optional<xrpl::AccountID> const& cursorIn,
std::optional<ripple::AccountID> const& cursorIn,
std::uint32_t const ledgerSequence,
boost::asio::yield_context yield
) const = 0;
@@ -419,7 +410,7 @@ public:
* @return The object as a Blob on success; nullopt otherwise
*/
std::optional<Blob>
fetchLedgerObject(xrpl::uint256 const& key, std::uint32_t sequence, boost::asio::yield_context yield) const;
fetchLedgerObject(ripple::uint256 const& key, std::uint32_t sequence, boost::asio::yield_context yield) const;
/**
* @brief Fetches a specific ledger object sequence.
@@ -432,7 +423,7 @@ public:
* @return The sequence in unit32_t on success; nullopt otherwise
*/
std::optional<std::uint32_t>
fetchLedgerObjectSeq(xrpl::uint256 const& key, std::uint32_t sequence, boost::asio::yield_context yield) const;
fetchLedgerObjectSeq(ripple::uint256 const& key, std::uint32_t sequence, boost::asio::yield_context yield) const;
/**
* @brief Fetches all ledger objects by their keys.
@@ -447,7 +438,7 @@ public:
*/
std::vector<Blob>
fetchLedgerObjects(
std::vector<xrpl::uint256> const& keys,
std::vector<ripple::uint256> const& keys,
std::uint32_t sequence,
boost::asio::yield_context yield
) const;
@@ -461,7 +452,7 @@ public:
* @return The object as a Blob on success; nullopt otherwise
*/
virtual std::optional<Blob>
doFetchLedgerObject(xrpl::uint256 const& key, std::uint32_t sequence, boost::asio::yield_context yield) const = 0;
doFetchLedgerObject(ripple::uint256 const& key, std::uint32_t sequence, boost::asio::yield_context yield) const = 0;
/**
* @brief The database-specific implementation for fetching a ledger object sequence.
@@ -473,7 +464,7 @@ public:
*/
virtual std::optional<std::uint32_t>
doFetchLedgerObjectSeq(
xrpl::uint256 const& key,
ripple::uint256 const& key,
std::uint32_t sequence,
boost::asio::yield_context yield
) const = 0;
@@ -488,7 +479,7 @@ public:
*/
virtual std::vector<Blob>
doFetchLedgerObjects(
std::vector<xrpl::uint256> const& keys,
std::vector<ripple::uint256> const& keys,
std::uint32_t sequence,
boost::asio::yield_context yield
) const = 0;
@@ -515,7 +506,7 @@ public:
*/
LedgerPage
fetchLedgerPage(
std::optional<xrpl::uint256> const& cursor,
std::optional<ripple::uint256> const& cursor,
std::uint32_t ledgerSequence,
std::uint32_t limit,
bool outOfOrder,
@@ -531,7 +522,7 @@ public:
* @return The successor on success; nullopt otherwise
*/
std::optional<LedgerObject>
fetchSuccessorObject(xrpl::uint256 key, std::uint32_t ledgerSequence, boost::asio::yield_context yield) const;
fetchSuccessorObject(ripple::uint256 key, std::uint32_t ledgerSequence, boost::asio::yield_context yield) const;
/**
* @brief Fetches the successor key.
@@ -544,8 +535,8 @@ public:
* @param yield The coroutine context
* @return The successor key on success; nullopt otherwise
*/
std::optional<xrpl::uint256>
fetchSuccessorKey(xrpl::uint256 key, std::uint32_t ledgerSequence, boost::asio::yield_context yield) const;
std::optional<ripple::uint256>
fetchSuccessorKey(ripple::uint256 key, std::uint32_t ledgerSequence, boost::asio::yield_context yield) const;
/**
* @brief Database-specific implementation of fetching the successor key
@@ -555,8 +546,8 @@ public:
* @param yield The coroutine context
* @return The successor on success; nullopt otherwise
*/
virtual std::optional<xrpl::uint256>
doFetchSuccessorKey(xrpl::uint256 key, std::uint32_t ledgerSequence, boost::asio::yield_context yield) const = 0;
virtual std::optional<ripple::uint256>
doFetchSuccessorKey(ripple::uint256 key, std::uint32_t ledgerSequence, boost::asio::yield_context yield) const = 0;
/**
* @brief Fetches book offers.
@@ -569,7 +560,7 @@ public:
*/
BookOffersPage
fetchBookOffers(
xrpl::uint256 const& book,
ripple::uint256 const& book,
std::uint32_t ledgerSequence,
std::uint32_t limit,
boost::asio::yield_context yield
@@ -632,7 +623,7 @@ public:
* @param blob r-value string serialization of ledger header.
*/
virtual void
writeLedger(xrpl::LedgerHeader const& ledgerHeader, std::string&& blob) = 0;
writeLedger(ripple::LedgerHeader const& ledgerHeader, std::string&& blob) = 0;
/**
* @brief Writes a new ledger object.
@@ -785,9 +776,6 @@ private:
*/
virtual bool
doFinishWrites() = 0;
void
updateRangeImpl(uint32_t newMax);
};
} // namespace data

View File

@@ -5,7 +5,6 @@ target_sources(
BackendCounters.cpp
BackendInterface.cpp
LedgerCache.cpp
LedgerCacheSaver.cpp
LedgerHeaderCache.cpp
cassandra/impl/Future.cpp
cassandra/impl/Cluster.cpp
@@ -15,9 +14,6 @@ target_sources(
cassandra/impl/SslContext.cpp
cassandra/Handle.cpp
cassandra/SettingsProvider.cpp
impl/InputFile.cpp
impl/LedgerCacheFile.cpp
impl/OutputFile.cpp
)
target_link_libraries(clio_data PUBLIC cassandra-cpp-driver::cassandra-cpp-driver clio_util)

View File

@@ -116,11 +116,11 @@ public:
NFTsAndCursor
fetchNFTsByIssuer(
xrpl::AccountID const& issuer,
ripple::AccountID const& issuer,
std::optional<std::uint32_t> const& taxon,
std::uint32_t const ledgerSequence,
std::uint32_t const limit,
std::optional<xrpl::uint256> const& cursorIn,
std::optional<ripple::uint256> const& cursorIn,
boost::asio::yield_context yield
) const override
{
@@ -130,7 +130,7 @@ public:
if (taxon.has_value()) {
auto r = schema_->selectNFTIDsByIssuerTaxon.bind(issuer);
r.bindAt(1, *taxon);
r.bindAt(2, cursorIn.value_or(xrpl::uint256(0)));
r.bindAt(2, cursorIn.value_or(ripple::uint256(0)));
r.bindAt(3, Limit{limit});
return r;
}
@@ -139,8 +139,8 @@ public:
r.bindAt(
1,
std::make_tuple(
cursorIn.has_value() ? xrpl::nft::toUInt32(xrpl::nft::getTaxon(*cursorIn)) : 0,
cursorIn.value_or(xrpl::uint256(0))
cursorIn.has_value() ? ripple::nft::toUInt32(ripple::nft::getTaxon(*cursorIn)) : 0,
cursorIn.value_or(ripple::uint256(0))
)
);
r.bindAt(2, Limit{limit});
@@ -156,8 +156,8 @@ public:
return {};
}
std::vector<xrpl::uint256> nftIDs;
for (auto const [nftID] : extract<xrpl::uint256>(idQueryResults))
std::vector<ripple::uint256> nftIDs;
for (auto const [nftID] : extract<ripple::uint256>(idQueryResults))
nftIDs.push_back(nftID);
if (nftIDs.empty())
@@ -189,11 +189,11 @@ public:
auto const nftUris = executor_.readEach(yield, selectNFTURIStatements);
for (auto i = 0u; i < nftIDs.size(); i++) {
if (auto const maybeRow = nftInfos[i].template get<uint32_t, xrpl::AccountID, bool>();
if (auto const maybeRow = nftInfos[i].template get<uint32_t, ripple::AccountID, bool>();
maybeRow.has_value()) {
auto [seq, owner, isBurned] = *maybeRow;
NFT nft(nftIDs[i], seq, owner, isBurned);
if (auto const maybeUri = nftUris[i].template get<xrpl::Blob>(); maybeUri.has_value())
if (auto const maybeUri = nftUris[i].template get<ripple::Blob>(); maybeUri.has_value())
nft.uri = *maybeUri;
ret.nfts.push_back(nft);
}
@@ -201,7 +201,7 @@ public:
return ret;
}
std::vector<xrpl::uint256>
std::vector<ripple::uint256>
fetchAccountRoots(
std::uint32_t number,
std::uint32_t pageSize,
@@ -209,8 +209,8 @@ public:
boost::asio::yield_context yield
) const override
{
std::vector<xrpl::uint256> liveAccounts;
std::optional<xrpl::AccountID> lastItem;
std::vector<ripple::uint256> liveAccounts;
std::optional<ripple::AccountID> lastItem;
while (liveAccounts.size() < number) {
Statement const statement = lastItem ? schema_->selectAccountFromToken.bind(*lastItem, Limit{pageSize})
@@ -224,9 +224,9 @@ public:
break;
}
// The results should not contain duplicates, we just filter out deleted accounts
std::vector<xrpl::uint256> fullAccounts;
for (auto [account] : extract<xrpl::AccountID>(results)) {
fullAccounts.push_back(xrpl::keylet::account(account).key);
std::vector<ripple::uint256> fullAccounts;
for (auto [account] : extract<ripple::AccountID>(results)) {
fullAccounts.push_back(ripple::keylet::account(account).key);
lastItem = account;
}
auto const objs = this->doFetchLedgerObjects(fullAccounts, seq, yield);

View File

@@ -43,10 +43,10 @@
* @brief Struct used to keep track of what to write to account_transactions/account_tx tables.
*/
struct AccountTransactionsData {
boost::container::flat_set<xrpl::AccountID> accounts;
boost::container::flat_set<ripple::AccountID> accounts;
std::uint32_t ledgerSequence{};
std::uint32_t transactionIndex{};
xrpl::uint256 txHash;
ripple::uint256 txHash;
/**
* @brief Construct a new AccountTransactionsData object
@@ -54,7 +54,7 @@ struct AccountTransactionsData {
* @param meta The transaction metadata
* @param txHash The transaction hash
*/
AccountTransactionsData(xrpl::TxMeta const& meta, xrpl::uint256 const& txHash)
AccountTransactionsData(ripple::TxMeta const& meta, ripple::uint256 const& txHash)
: accounts(meta.getAffectedAccounts())
, ledgerSequence(meta.getLgrSeq())
, transactionIndex(meta.getIndex())
@@ -71,10 +71,10 @@ struct AccountTransactionsData {
* Gets written to nf_token_transactions table and the like.
*/
struct NFTTransactionsData {
xrpl::uint256 tokenID;
ripple::uint256 tokenID;
std::uint32_t ledgerSequence;
std::uint32_t transactionIndex;
xrpl::uint256 txHash;
ripple::uint256 txHash;
/**
* @brief Construct a new NFTTransactionsData object
@@ -83,7 +83,7 @@ struct NFTTransactionsData {
* @param meta The transaction metadata
* @param txHash The transaction hash
*/
NFTTransactionsData(xrpl::uint256 const& tokenID, xrpl::TxMeta const& meta, xrpl::uint256 const& txHash)
NFTTransactionsData(ripple::uint256 const& tokenID, ripple::TxMeta const& meta, ripple::uint256 const& txHash)
: tokenID(tokenID), ledgerSequence(meta.getLgrSeq()), transactionIndex(meta.getIndex()), txHash(txHash)
{
}
@@ -101,11 +101,11 @@ struct NFTTransactionsData {
* We only set the uri if this is a mint tx, or if we are loading initial state from NFTokenPage objects.
*/
struct NFTsData {
xrpl::uint256 tokenID;
ripple::uint256 tokenID;
std::uint32_t ledgerSequence;
std::optional<std::uint32_t> transactionIndex;
xrpl::AccountID owner;
std::optional<xrpl::Blob> uri;
ripple::AccountID owner;
std::optional<ripple::Blob> uri;
bool isBurned = false;
bool onlyUriChanged = false; // Whether only the URI was changed
@@ -122,10 +122,10 @@ struct NFTsData {
* @param meta The transaction metadata
*/
NFTsData(
xrpl::uint256 const& tokenID,
xrpl::AccountID const& owner,
xrpl::Blob const& uri,
xrpl::TxMeta const& meta
ripple::uint256 const& tokenID,
ripple::AccountID const& owner,
ripple::Blob const& uri,
ripple::TxMeta const& meta
)
: tokenID(tokenID), ledgerSequence(meta.getLgrSeq()), transactionIndex(meta.getIndex()), owner(owner), uri(uri)
{
@@ -141,7 +141,7 @@ struct NFTsData {
* @param meta The transaction metadata
* @param isBurned Whether the NFT is burned
*/
NFTsData(xrpl::uint256 const& tokenID, xrpl::AccountID const& owner, xrpl::TxMeta const& meta, bool isBurned)
NFTsData(ripple::uint256 const& tokenID, ripple::AccountID const& owner, ripple::TxMeta const& meta, bool isBurned)
: tokenID(tokenID)
, ledgerSequence(meta.getLgrSeq())
, transactionIndex(meta.getIndex())
@@ -163,10 +163,10 @@ struct NFTsData {
* @param uri The URI
*/
NFTsData(
xrpl::uint256 const& tokenID,
ripple::uint256 const& tokenID,
std::uint32_t const ledgerSequence,
xrpl::AccountID const& owner,
xrpl::Blob const& uri
ripple::AccountID const& owner,
ripple::Blob const& uri
)
: tokenID(tokenID), ledgerSequence(ledgerSequence), owner(owner), uri(uri)
{
@@ -180,7 +180,7 @@ struct NFTsData {
* @param uri The new URI
*
*/
NFTsData(xrpl::uint256 const& tokenID, xrpl::TxMeta const& meta, xrpl::Blob const& uri)
NFTsData(ripple::uint256 const& tokenID, ripple::TxMeta const& meta, ripple::Blob const& uri)
: tokenID(tokenID)
, ledgerSequence(meta.getLgrSeq())
, transactionIndex(meta.getIndex())
@@ -194,8 +194,8 @@ struct NFTsData {
* @brief Represents an MPT and holder pair
*/
struct MPTHolderData {
xrpl::uint192 mptID;
xrpl::AccountID holder;
ripple::uint192 mptID;
ripple::AccountID holder;
};
/**
@@ -231,25 +231,25 @@ isBookDir(T const& key, R const& object)
if (!isDirNode(object))
return false;
xrpl::STLedgerEntry const sle{xrpl::SerialIter{object.data(), object.size()}, key};
return !sle[~xrpl::sfOwner].has_value();
ripple::STLedgerEntry const sle{ripple::SerialIter{object.data(), object.size()}, key};
return !sle[~ripple::sfOwner].has_value();
}
/**
* @brief Get the book base.
*
* @param key The key to get the book base out of
* @return Book base as xrpl::uint256
* @return Book base as ripple::uint256
*/
template <typename T>
inline xrpl::uint256
inline ripple::uint256
getBookBase(T const& key)
{
static constexpr size_t kEY_SIZE = 24;
ASSERT(key.size() == xrpl::uint256::size(), "Invalid key size {}", key.size());
ASSERT(key.size() == ripple::uint256::size(), "Invalid key size {}", key.size());
xrpl::uint256 ret;
ripple::uint256 ret;
for (size_t i = 0; i < kEY_SIZE; ++i)
ret.data()[i] = key.data()[i];
@@ -257,15 +257,15 @@ getBookBase(T const& key)
}
/**
* @brief Stringify a xrpl::uint256.
* @brief Stringify a ripple::uint256.
*
* @param input The input value
* @return The input value as a string
*/
inline std::string
uint256ToString(xrpl::uint256 const& input)
uint256ToString(ripple::uint256 const& input)
{
return {reinterpret_cast<char const*>(input.data()), xrpl::uint256::size()};
return {reinterpret_cast<char const*>(input.data()), ripple::uint256::size()};
}
/** @brief The ripple epoch start timestamp. Midnight on 1st January 2000. */

View File

@@ -117,22 +117,22 @@ public:
NFTsAndCursor
fetchNFTsByIssuer(
xrpl::AccountID const& issuer,
ripple::AccountID const& issuer,
std::optional<std::uint32_t> const& taxon,
std::uint32_t const ledgerSequence,
std::uint32_t const limit,
std::optional<xrpl::uint256> const& cursorIn,
std::optional<ripple::uint256> const& cursorIn,
boost::asio::yield_context yield
) const override
{
std::vector<xrpl::uint256> nftIDs;
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() ? xrpl::nft::toUInt32(xrpl::nft::getTaxon(*cursorIn)) : 0;
auto const startTokenID = cursorIn.value_or(xrpl::uint256(0));
auto const startTaxon = cursorIn.has_value() ? ripple::nft::toUInt32(ripple::nft::getTaxon(*cursorIn)) : 0;
auto const startTokenID = cursorIn.value_or(ripple::uint256(0));
Statement const firstQuery = schema_->selectNFTIDsByIssuerTaxon.bind(issuer);
firstQuery.bindAt(1, startTaxon);
@@ -141,7 +141,7 @@ public:
auto const firstRes = executor_.read(yield, firstQuery);
if (firstRes.has_value()) {
for (auto const [nftID] : extract<xrpl::uint256>(*firstRes))
for (auto const [nftID] : extract<ripple::uint256>(*firstRes))
nftIDs.push_back(nftID);
}
@@ -153,7 +153,7 @@ public:
auto const secondRes = executor_.read(yield, secondQuery);
if (secondRes.has_value()) {
for (auto const [nftID] : extract<xrpl::uint256>(*secondRes))
for (auto const [nftID] : extract<ripple::uint256>(*secondRes))
nftIDs.push_back(nftID);
}
}
@@ -172,9 +172,9 @@ public:
* @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 xrpl::uint256 representing the account root hashes.
* @return A vector of ripple::uint256 representing the account root hashes.
*/
std::vector<xrpl::uint256>
std::vector<ripple::uint256>
fetchAccountRoots(
[[maybe_unused]] std::uint32_t number,
[[maybe_unused]] std::uint32_t pageSize,
@@ -187,41 +187,41 @@ public:
}
private:
std::vector<xrpl::uint256>
std::vector<ripple::uint256>
fetchNFTIDsByTaxon(
xrpl::AccountID const& issuer,
ripple::AccountID const& issuer,
std::uint32_t const taxon,
std::uint32_t const limit,
std::optional<xrpl::uint256> const& cursorIn,
std::optional<ripple::uint256> const& cursorIn,
boost::asio::yield_context yield
) const
{
std::vector<xrpl::uint256> nftIDs;
std::vector<ripple::uint256> nftIDs;
Statement const statement = schema_->selectNFTIDsByIssuerTaxon.bind(issuer);
statement.bindAt(1, taxon);
statement.bindAt(2, cursorIn.value_or(xrpl::uint256(0)));
statement.bindAt(2, cursorIn.value_or(ripple::uint256(0)));
statement.bindAt(3, Limit{limit});
auto const res = executor_.read(yield, statement);
if (res.has_value() && res->hasRows()) {
for (auto const [nftID] : extract<xrpl::uint256>(*res))
for (auto const [nftID] : extract<ripple::uint256>(*res))
nftIDs.push_back(nftID);
}
return nftIDs;
}
std::vector<xrpl::uint256>
std::vector<ripple::uint256>
fetchNFTIDsWithoutTaxon(
xrpl::AccountID const& issuer,
ripple::AccountID const& issuer,
std::uint32_t const limit,
std::optional<xrpl::uint256> const& cursorIn,
std::optional<ripple::uint256> const& cursorIn,
boost::asio::yield_context yield
) const
{
std::vector<xrpl::uint256> nftIDs;
std::vector<ripple::uint256> nftIDs;
auto const startTaxon = cursorIn.has_value() ? xrpl::nft::toUInt32(xrpl::nft::getTaxon(*cursorIn)) : 0;
auto const startTokenID = cursorIn.value_or(xrpl::uint256(0));
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);
@@ -230,7 +230,7 @@ private:
auto const firstRes = executor_.read(yield, firstQuery);
if (firstRes.has_value()) {
for (auto const [nftID] : extract<xrpl::uint256>(*firstRes))
for (auto const [nftID] : extract<ripple::uint256>(*firstRes))
nftIDs.push_back(nftID);
}
@@ -242,7 +242,7 @@ private:
auto const secondRes = executor_.read(yield, secondQuery);
if (secondRes.has_value()) {
for (auto const [nftID] : extract<xrpl::uint256>(*secondRes))
for (auto const [nftID] : extract<ripple::uint256>(*secondRes))
nftIDs.push_back(nftID);
}
}
@@ -254,7 +254,7 @@ private:
*/
NFTsAndCursor
populateNFTsAndCreateCursor(
std::vector<xrpl::uint256> const& nftIDs,
std::vector<ripple::uint256> const& nftIDs,
std::uint32_t const ledgerSequence,
std::uint32_t const limit,
boost::asio::yield_context yield
@@ -291,11 +291,11 @@ private:
// 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, xrpl::AccountID, bool>();
if (auto const maybeRow = nftInfos[i].template get<uint32_t, ripple::AccountID, bool>();
maybeRow.has_value()) {
auto [seq, owner, isBurned] = *maybeRow;
NFT nft(nftIDs[i], seq, owner, isBurned);
if (auto const maybeUri = nftUris[i].template get<xrpl::Blob>(); maybeUri.has_value())
if (auto const maybeUri = nftUris[i].template get<ripple::Blob>(); maybeUri.has_value())
nft.uri = *maybeUri;
ret.nfts.push_back(nft);
}

View File

@@ -20,22 +20,16 @@
#include "data/LedgerCache.hpp"
#include "data/Types.hpp"
#include "data/impl/LedgerCacheFile.hpp"
#include "etl/Models.hpp"
#include "etlng/Models.hpp"
#include "util/Assert.hpp"
#include <xrpl/basics/base_uint.h>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <map>
#include <mutex>
#include <optional>
#include <shared_mutex>
#include <string>
#include <utility>
#include <vector>
namespace data {
@@ -95,7 +89,7 @@ LedgerCache::update(std::vector<LedgerObject> const& objs, uint32_t seq, bool is
}
void
LedgerCache::update(std::vector<etl::model::Object> const& objs, uint32_t seq)
LedgerCache::update(std::vector<etlng::model::Object> const& objs, uint32_t seq)
{
if (disabled_)
return;
@@ -131,7 +125,7 @@ LedgerCache::update(std::vector<etl::model::Object> const& objs, uint32_t seq)
}
std::optional<LedgerObject>
LedgerCache::getSuccessor(xrpl::uint256 const& key, uint32_t seq) const
LedgerCache::getSuccessor(ripple::uint256 const& key, uint32_t seq) const
{
if (disabled_ or not full_)
return {};
@@ -148,7 +142,7 @@ LedgerCache::getSuccessor(xrpl::uint256 const& key, uint32_t seq) const
}
std::optional<LedgerObject>
LedgerCache::getPredecessor(xrpl::uint256 const& key, uint32_t seq) const
LedgerCache::getPredecessor(ripple::uint256 const& key, uint32_t seq) const
{
if (disabled_ or not full_)
return {};
@@ -164,7 +158,7 @@ LedgerCache::getPredecessor(xrpl::uint256 const& key, uint32_t seq) const
}
std::optional<Blob>
LedgerCache::get(xrpl::uint256 const& key, uint32_t seq) const
LedgerCache::get(ripple::uint256 const& key, uint32_t seq) const
{
if (disabled_)
return {};
@@ -183,7 +177,7 @@ LedgerCache::get(xrpl::uint256 const& key, uint32_t seq) const
}
std::optional<Blob>
LedgerCache::getDeleted(xrpl::uint256 const& key, uint32_t seq) const
LedgerCache::getDeleted(ripple::uint256 const& key, uint32_t seq) const
{
if (disabled_)
return std::nullopt;
@@ -257,34 +251,4 @@ LedgerCache::getSuccessorHitRate() const
return static_cast<float>(successorHitCounter_.get().value()) / successorReqCounter_.get().value();
}
std::expected<void, std::string>
LedgerCache::saveToFile(std::string const& path) const
{
if (not isFull()) {
return std::unexpected{"Ledger cache is not full"};
}
impl::LedgerCacheFile file{path};
std::shared_lock const lock{mtx_};
impl::LedgerCacheFile::DataView const data{.latestSeq = latestSeq_, .map = map_, .deleted = deleted_};
return file.write(data);
}
std::expected<void, std::string>
LedgerCache::loadFromFile(std::string const& path, uint32_t minLatestSequence)
{
impl::LedgerCacheFile file{path};
auto data = file.read(minLatestSequence);
if (not data.has_value()) {
return std::unexpected(std::move(data).error());
}
auto [latestSeq, map, deleted] = std::move(data).value();
std::unique_lock const lock{mtx_};
latestSeq_ = latestSeq;
map_ = std::move(map);
deleted_ = std::move(deleted);
full_ = true;
return {};
}
} // namespace data

View File

@@ -21,7 +21,7 @@
#include "data/LedgerCacheInterface.hpp"
#include "data/Types.hpp"
#include "etl/Models.hpp"
#include "etlng/Models.hpp"
#include "util/prometheus/Bool.hpp"
#include "util/prometheus/Counter.hpp"
#include "util/prometheus/Label.hpp"
@@ -37,7 +37,6 @@
#include <map>
#include <optional>
#include <shared_mutex>
#include <string>
#include <unordered_set>
#include <vector>
@@ -47,16 +46,11 @@ namespace data {
* @brief Cache for an entire ledger.
*/
class LedgerCache : public LedgerCacheInterface {
public:
/** @brief An entry of the cache */
struct CacheEntry {
uint32_t seq = 0;
Blob blob;
};
using CacheMap = std::map<xrpl::uint256, CacheEntry>;
private:
// counters for fetchLedgerObject(s) hit rate
std::reference_wrapper<util::prometheus::CounterInt> objectReqCounter_{PrometheusService::counterInt(
"ledger_cache_counter_total_number",
@@ -79,8 +73,8 @@ private:
util::prometheus::Labels({{"type", "cache_hit"}, {"fetch", "successor_key"}})
)};
CacheMap map_;
CacheMap deleted_;
std::map<ripple::uint256, CacheEntry> map_;
std::map<ripple::uint256, CacheEntry> deleted_;
mutable std::shared_mutex mtx_;
std::condition_variable_any cv_;
@@ -97,26 +91,26 @@ private:
)};
// temporary set to prevent background thread from writing already deleted data. not used when cache is full
std::unordered_set<xrpl::uint256, xrpl::HardenedHash<>> deletes_;
std::unordered_set<ripple::uint256, ripple::hardened_hash<>> deletes_;
public:
void
update(std::vector<LedgerObject> const& objs, uint32_t seq, bool isBackground) override;
void
update(std::vector<etl::model::Object> const& objs, uint32_t seq) override;
update(std::vector<etlng::model::Object> const& objs, uint32_t seq) override;
std::optional<Blob>
get(xrpl::uint256 const& key, uint32_t seq) const override;
get(ripple::uint256 const& key, uint32_t seq) const override;
std::optional<Blob>
getDeleted(xrpl::uint256 const& key, uint32_t seq) const override;
getDeleted(ripple::uint256 const& key, uint32_t seq) const override;
std::optional<LedgerObject>
getSuccessor(xrpl::uint256 const& key, uint32_t seq) const override;
getSuccessor(ripple::uint256 const& key, uint32_t seq) const override;
std::optional<LedgerObject>
getPredecessor(xrpl::uint256 const& key, uint32_t seq) const override;
getPredecessor(ripple::uint256 const& key, uint32_t seq) const override;
void
setDisabled() override;
@@ -144,12 +138,6 @@ public:
void
waitUntilCacheContainsSeq(uint32_t seq) override;
std::expected<void, std::string>
saveToFile(std::string const& path) const override;
std::expected<void, std::string>
loadFromFile(std::string const& path, uint32_t minLatestSequence) override;
};
} // namespace data

View File

@@ -20,16 +20,14 @@
#pragma once
#include "data/Types.hpp"
#include "etl/Models.hpp"
#include "etlng/Models.hpp"
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/hardened_hash.h>
#include <cstddef>
#include <cstdint>
#include <expected>
#include <optional>
#include <string>
#include <vector>
namespace data {
@@ -65,7 +63,7 @@ public:
* @param seq The sequence to update cache for
*/
virtual void
update(std::vector<etl::model::Object> const& objs, uint32_t seq) = 0;
update(std::vector<etlng::model::Object> const& objs, uint32_t seq) = 0;
/**
* @brief Fetch a cached object by its key and sequence number.
@@ -75,7 +73,7 @@ public:
* @return If found in cache, will return the cached Blob; otherwise nullopt is returned
*/
virtual std::optional<Blob>
get(xrpl::uint256 const& key, uint32_t seq) const = 0;
get(ripple::uint256 const& key, uint32_t seq) const = 0;
/**
* @brief Fetch a recently deleted object by its key and sequence number.
@@ -85,7 +83,7 @@ public:
* @return If found in deleted cache, will return the cached Blob; otherwise nullopt is returned
*/
virtual std::optional<Blob>
getDeleted(xrpl::uint256 const& key, uint32_t seq) const = 0;
getDeleted(ripple::uint256 const& key, uint32_t seq) const = 0;
/**
* @brief Gets a cached successor.
@@ -97,7 +95,7 @@ public:
* @return If found in cache, will return the cached successor; otherwise nullopt is returned
*/
virtual std::optional<LedgerObject>
getSuccessor(xrpl::uint256 const& key, uint32_t seq) const = 0;
getSuccessor(ripple::uint256 const& key, uint32_t seq) const = 0;
/**
* @brief Gets a cached predcessor.
@@ -109,7 +107,7 @@ public:
* @return If found in cache, will return the cached predcessor; otherwise nullopt is returned
*/
virtual std::optional<LedgerObject>
getPredecessor(xrpl::uint256 const& key, uint32_t seq) const = 0;
getPredecessor(ripple::uint256 const& key, uint32_t seq) const = 0;
/**
* @brief Disables the cache.
@@ -170,27 +168,6 @@ public:
*/
virtual void
waitUntilCacheContainsSeq(uint32_t seq) = 0;
/**
* @brief Save the cache to file
* @note This operation takes about 7 seconds and it keeps a shared lock of mtx_
*
* @param path The file path to save the cache to
* @return An error as a string if any
*/
[[nodiscard]] virtual std::expected<void, std::string>
saveToFile(std::string const& path) const = 0;
/**
* @brief Load the cache from file
* @note This operation takes about 7 seconds and it keeps mtx_ exclusively locked
*
* @param path The file path to load data from
* @param minLatestSequence The minimum allowed value of the latestLedgerSequence in cache file
* @return An error as a string if any
*/
[[nodiscard]] virtual std::expected<void, std::string>
loadFromFile(std::string const& path, uint32_t minLatestSequence) = 0;
};
} // namespace data

View File

@@ -1,75 +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.
*/
//==============================================================================
#include "data/LedgerCacheSaver.hpp"
#include "data/LedgerCacheInterface.hpp"
#include "util/Assert.hpp"
#include "util/Profiler.hpp"
#include "util/log/Logger.hpp"
#include <string>
#include <thread>
namespace data {
LedgerCacheSaver::LedgerCacheSaver(util::config::ClioConfigDefinition const& config, LedgerCacheInterface const& cache)
: cacheFilePath_(config.maybeValue<std::string>("cache.file.path"))
, cache_(cache)
, isAsync_(config.get<bool>("cache.file.async_save"))
{
}
LedgerCacheSaver::~LedgerCacheSaver()
{
waitToFinish();
}
void
LedgerCacheSaver::save()
{
ASSERT(not savingThread_.has_value(), "Multiple save() calls are not allowed");
savingThread_ = std::thread([this]() {
if (not cacheFilePath_.has_value()) {
return;
}
LOG(util::LogService::info()) << "Saving ledger cache to " << *cacheFilePath_;
if (auto const [success, durationMs] = util::timed([&]() { return cache_.get().saveToFile(*cacheFilePath_); });
success.has_value()) {
LOG(util::LogService::info()) << "Successfully saved ledger cache in " << durationMs << " ms";
} else {
LOG(util::LogService::error()) << "Error saving LedgerCache to file: " << success.error();
}
});
if (not isAsync_) {
waitToFinish();
}
}
void
LedgerCacheSaver::waitToFinish()
{
if (savingThread_.has_value() and savingThread_->joinable()) {
savingThread_->join();
}
savingThread_.reset();
}
} // namespace data

View File

@@ -1,94 +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/LedgerCacheInterface.hpp"
#include "util/config/ConfigDefinition.hpp"
#include <concepts>
#include <functional>
#include <optional>
#include <string>
#include <thread>
namespace data {
/**
* @brief A concept for a class that can save ledger cache asynchronously.
*
* This concept defines the interface requirements for any type that manages
* asynchronous saving of ledger cache to persistent storage.
*/
template <typename T>
concept SomeLedgerCacheSaver = requires(T a) {
{ a.save() } -> std::same_as<void>;
{ a.waitToFinish() } -> std::same_as<void>;
};
/**
* @brief Manages asynchronous saving of ledger cache to a file.
*
* This class provides functionality to save the ledger cache to a file in a separate thread,
* allowing the main application to continue without blocking. The file path is configured
* through the application's configuration system.
*/
class LedgerCacheSaver {
std::optional<std::string> cacheFilePath_;
std::reference_wrapper<LedgerCacheInterface const> cache_;
std::optional<std::thread> savingThread_;
bool isAsync_;
public:
/**
* @brief Constructs a LedgerCacheSaver instance.
*
* @param config The configuration object containing the cache file path setting
* @param cache Reference to the ledger cache interface to be saved
*/
LedgerCacheSaver(util::config::ClioConfigDefinition const& config, LedgerCacheInterface const& cache);
/**
* @brief Destructor that ensures the saving thread is properly joined.
*
* Waits for any ongoing save operation to complete before destruction.
*/
~LedgerCacheSaver();
/**
* @brief Initiates an asynchronous save operation of the ledger cache.
*
* Spawns a new thread that saves the ledger cache to the configured file path.
* If no file path is configured, the operation is skipped. Logs the progress
* and result of the save operation.
*/
void
save();
/**
* @brief Waits for the saving thread to complete.
*
* Blocks until the saving operation finishes if a thread is currently active.
* Safe to call multiple times or when no save operation is in progress.
*/
void
waitToFinish();
};
} // namespace data

View File

@@ -30,7 +30,7 @@
namespace data {
/**
* @brief A simple cache holding one `xrpl::LedgerHeader` to reduce DB lookups.
* @brief A simple cache holding one `ripple::LedgerHeader` to reduce DB lookups.
*
* Used internally by backend implementations. When a ledger header is
* fetched via `FetchLedgerBySeq` (often triggered by RPC commands),
@@ -46,7 +46,7 @@ public:
* @brief Struct to store ledger header cache entry and the sequence it belongs to
*/
struct CacheEntry {
xrpl::LedgerHeader ledger;
ripple::LedgerHeader ledger;
uint32_t seq{};
/**

View File

@@ -39,7 +39,7 @@ using Blob = std::vector<unsigned char>;
* @brief Represents an object in the ledger.
*/
struct LedgerObject {
xrpl::uint256 key;
ripple::uint256 key;
Blob blob;
bool
@@ -51,7 +51,7 @@ struct LedgerObject {
*/
struct LedgerPage {
std::vector<LedgerObject> objects;
std::optional<xrpl::uint256> cursor;
std::optional<ripple::uint256> cursor;
};
/**
@@ -59,7 +59,7 @@ struct LedgerPage {
*/
struct BookOffersPage {
std::vector<LedgerObject> offers;
std::optional<xrpl::uint256> cursor;
std::optional<ripple::uint256> cursor;
};
/**
@@ -170,9 +170,9 @@ struct TransactionsAndCursor {
* @brief Represents a NFToken.
*/
struct NFT {
xrpl::uint256 tokenID;
ripple::uint256 tokenID;
std::uint32_t ledgerSequence{};
xrpl::AccountID owner;
ripple::AccountID owner;
Blob uri;
bool isBurned{};
@@ -187,9 +187,9 @@ struct NFT {
* @param uri The URI
* @param isBurned Whether the token is burned
*/
NFT(xrpl::uint256 const& tokenID,
NFT(ripple::uint256 const& tokenID,
std::uint32_t ledgerSequence,
xrpl::AccountID const& owner,
ripple::AccountID const& owner,
Blob uri,
bool isBurned)
: tokenID{tokenID}, ledgerSequence{ledgerSequence}, owner{owner}, uri{std::move(uri)}, isBurned{isBurned}
@@ -204,7 +204,7 @@ struct NFT {
* @param owner The owner
* @param isBurned Whether the token is burned
*/
NFT(xrpl::uint256 const& tokenID, std::uint32_t ledgerSequence, xrpl::AccountID const& owner, bool isBurned)
NFT(ripple::uint256 const& tokenID, std::uint32_t ledgerSequence, ripple::AccountID const& owner, bool isBurned)
: NFT(tokenID, ledgerSequence, owner, {}, isBurned)
{
}
@@ -230,7 +230,7 @@ struct NFT {
*/
struct NFTsAndCursor {
std::vector<NFT> nfts;
std::optional<xrpl::uint256> cursor;
std::optional<ripple::uint256> cursor;
};
/**
@@ -238,7 +238,7 @@ struct NFTsAndCursor {
*/
struct MPTHoldersAndCursor {
std::vector<Blob> mptokens;
std::optional<xrpl::AccountID> cursor;
std::optional<ripple::AccountID> cursor;
};
/**
@@ -247,9 +247,6 @@ struct MPTHoldersAndCursor {
struct LedgerRange {
std::uint32_t minSequence = 0;
std::uint32_t maxSequence = 0;
bool
operator==(LedgerRange const&) const = default;
};
/**
@@ -257,7 +254,7 @@ struct LedgerRange {
*/
struct Amendment {
std::string name;
xrpl::uint256 feature;
ripple::uint256 feature;
bool isSupportedByXRPL = false;
bool isSupportedByClio = false;
bool isRetired = false;
@@ -268,7 +265,7 @@ struct Amendment {
* @param name The name of the amendment
* @return The amendment Id as uint256
*/
static xrpl::uint256
static ripple::uint256
getAmendmentId(std::string_view const name);
/**
@@ -304,7 +301,7 @@ struct AmendmentKey {
operator std::string_view() const;
/** @brief Conversion to uint256 */
operator xrpl::uint256() const;
operator ripple::uint256() const;
/**
* @brief Comparison operators
@@ -315,8 +312,8 @@ struct AmendmentKey {
operator<=>(AmendmentKey const& other) const = default;
};
constexpr xrpl::uint256 kFIRST_KEY{"0000000000000000000000000000000000000000000000000000000000000000"};
constexpr xrpl::uint256 kLAST_KEY{"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"};
constexpr xrpl::uint256 kHI192{"0000000000000000000000000000000000000000000000001111111111111111"};
constexpr ripple::uint256 kFIRST_KEY{"0000000000000000000000000000000000000000000000000000000000000000"};
constexpr ripple::uint256 kLAST_KEY{"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"};
constexpr ripple::uint256 kHI192{"0000000000000000000000000000000000000000000000001111111111111111"};
} // namespace data

View File

@@ -148,7 +148,7 @@ public:
TransactionsAndCursor
fetchAccountTransactions(
xrpl::AccountID const& account,
ripple::AccountID const& account,
std::uint32_t const limit,
bool forward,
std::optional<TransactionsCursor> const& txnCursor,
@@ -169,14 +169,14 @@ public:
auto cursor = txnCursor;
if (cursor) {
statement.bindAt(1, cursor->asTuple());
LOG(log_.debug()) << "account = " << xrpl::strHex(account) << " tuple = " << cursor->ledgerSequence
LOG(log_.debug()) << "account = " << ripple::strHex(account) << " tuple = " << cursor->ledgerSequence
<< cursor->transactionIndex;
} else {
auto const seq = forward ? rng->minSequence : rng->maxSequence;
auto const placeHolder = forward ? 0u : std::numeric_limits<std::uint32_t>::max();
statement.bindAt(1, std::make_tuple(placeHolder, placeHolder));
LOG(log_.debug()) << "account = " << xrpl::strHex(account) << " idx = " << seq
LOG(log_.debug()) << "account = " << ripple::strHex(account) << " idx = " << seq
<< " tuple = " << placeHolder;
}
@@ -191,11 +191,11 @@ public:
return {};
}
std::vector<xrpl::uint256> hashes = {};
std::vector<ripple::uint256> hashes = {};
auto numRows = results.numRows();
LOG(log_.info()) << "num_rows = " << numRows;
for (auto [hash, data] : extract<xrpl::uint256, std::tuple<uint32_t, uint32_t>>(results)) {
for (auto [hash, data] : extract<ripple::uint256, std::tuple<uint32_t, uint32_t>>(results)) {
hashes.push_back(hash);
if (--numRows == 0) {
LOG(log_.debug()) << "Setting cursor";
@@ -221,7 +221,7 @@ public:
}
void
writeLedger(xrpl::LedgerHeader const& ledgerHeader, std::string&& blob) override
writeLedger(ripple::LedgerHeader const& ledgerHeader, std::string&& blob) override
{
executor_.write(schema_->insertLedgerHeader, ledgerHeader.seq, std::move(blob));
@@ -250,7 +250,7 @@ public:
return std::nullopt;
}
std::optional<xrpl::LedgerHeader>
std::optional<ripple::LedgerHeader>
fetchLedgerBySequence(std::uint32_t const sequence, boost::asio::yield_context yield) const override
{
if (auto const lock = ledgerCache_.get(); lock.has_value() && lock->seq == sequence)
@@ -260,7 +260,7 @@ public:
if (res) {
if (auto const& result = res.value(); result) {
if (auto const maybeValue = result.template get<std::vector<unsigned char>>(); maybeValue) {
auto const header = util::deserializeHeader(xrpl::makeSlice(*maybeValue));
auto const header = util::deserializeHeader(ripple::makeSlice(*maybeValue));
ledgerCache_.put(FetchLedgerCache::CacheEntry{header, sequence});
return header;
}
@@ -277,8 +277,8 @@ public:
return std::nullopt;
}
std::optional<xrpl::LedgerHeader>
fetchLedgerByHash(xrpl::uint256 const& hash, boost::asio::yield_context yield) const override
std::optional<ripple::LedgerHeader>
fetchLedgerByHash(ripple::uint256 const& hash, boost::asio::yield_context yield) const override
{
if (auto const res = executor_.read(yield, schema_->selectLedgerByHash, hash); res) {
if (auto const& result = res.value(); result) {
@@ -342,7 +342,7 @@ public:
return fetchTransactions(hashes, yield);
}
std::vector<xrpl::uint256>
std::vector<ripple::uint256>
fetchAllTransactionHashesInLedger(
std::uint32_t const ledgerSequence,
boost::asio::yield_context yield
@@ -363,8 +363,8 @@ public:
return {};
}
std::vector<xrpl::uint256> hashes;
for (auto [hash] : extract<xrpl::uint256>(result))
std::vector<ripple::uint256> hashes;
for (auto [hash] : extract<ripple::uint256>(result))
hashes.push_back(std::move(hash));
auto end = std::chrono::system_clock::now();
@@ -377,7 +377,7 @@ public:
std::optional<NFT>
fetchNFT(
xrpl::uint256 const& tokenID,
ripple::uint256 const& tokenID,
std::uint32_t const ledgerSequence,
boost::asio::yield_context yield
) const override
@@ -386,7 +386,7 @@ public:
if (not res)
return std::nullopt;
if (auto const maybeRow = res->template get<uint32_t, xrpl::AccountID, bool>(); maybeRow) {
if (auto const maybeRow = res->template get<uint32_t, ripple::AccountID, bool>(); maybeRow) {
auto [seq, owner, isBurned] = *maybeRow;
auto result = std::make_optional<NFT>(tokenID, seq, owner, isBurned);
@@ -403,7 +403,7 @@ public:
// one.
auto uriRes = executor_.read(yield, schema_->selectNFTURI, tokenID, ledgerSequence);
if (uriRes) {
if (auto const maybeUri = uriRes->template get<xrpl::Blob>(); maybeUri)
if (auto const maybeUri = uriRes->template get<ripple::Blob>(); maybeUri)
result->uri = *maybeUri;
}
@@ -416,7 +416,7 @@ public:
TransactionsAndCursor
fetchNFTTransactions(
xrpl::uint256 const& tokenID,
ripple::uint256 const& tokenID,
std::uint32_t const limit,
bool const forward,
std::optional<TransactionsCursor> const& cursorIn,
@@ -437,14 +437,14 @@ public:
auto cursor = cursorIn;
if (cursor) {
statement.bindAt(1, cursor->asTuple());
LOG(log_.debug()) << "token_id = " << xrpl::strHex(tokenID) << " tuple = " << cursor->ledgerSequence
LOG(log_.debug()) << "token_id = " << ripple::strHex(tokenID) << " tuple = " << cursor->ledgerSequence
<< cursor->transactionIndex;
} else {
auto const seq = forward ? rng->minSequence : rng->maxSequence;
auto const placeHolder = forward ? 0 : std::numeric_limits<std::uint32_t>::max();
statement.bindAt(1, std::make_tuple(placeHolder, placeHolder));
LOG(log_.debug()) << "token_id = " << xrpl::strHex(tokenID) << " idx = " << seq
LOG(log_.debug()) << "token_id = " << ripple::strHex(tokenID) << " idx = " << seq
<< " tuple = " << placeHolder;
}
@@ -457,11 +457,11 @@ public:
return {};
}
std::vector<xrpl::uint256> hashes = {};
std::vector<ripple::uint256> hashes = {};
auto numRows = results.numRows();
LOG(log_.info()) << "num_rows = " << numRows;
for (auto [hash, data] : extract<xrpl::uint256, std::tuple<uint32_t, uint32_t>>(results)) {
for (auto [hash, data] : extract<ripple::uint256, std::tuple<uint32_t, uint32_t>>(results)) {
hashes.push_back(hash);
if (--numRows == 0) {
LOG(log_.debug()) << "Setting cursor";
@@ -487,15 +487,15 @@ public:
MPTHoldersAndCursor
fetchMPTHolders(
xrpl::uint192 const& mptID,
ripple::uint192 const& mptID,
std::uint32_t const limit,
std::optional<xrpl::AccountID> const& cursorIn,
std::optional<ripple::AccountID> const& cursorIn,
std::uint32_t const ledgerSequence,
boost::asio::yield_context yield
) const override
{
auto const holderEntries = executor_.read(
yield, schema_->selectMPTHolders, mptID, cursorIn.value_or(xrpl::AccountID(0)), Limit{limit}
yield, schema_->selectMPTHolders, mptID, cursorIn.value_or(ripple::AccountID(0)), Limit{limit}
);
auto const& holderResults = holderEntries.value();
@@ -504,10 +504,10 @@ public:
return {};
}
std::vector<xrpl::uint256> mptKeys;
std::optional<xrpl::AccountID> cursor;
for (auto const [holder] : extract<xrpl::AccountID>(holderResults)) {
mptKeys.push_back(xrpl::keylet::mptoken(mptID, holder).key);
std::vector<ripple::uint256> mptKeys;
std::optional<ripple::AccountID> cursor;
for (auto const [holder] : extract<ripple::AccountID>(holderResults)) {
mptKeys.push_back(ripple::keylet::mptoken(mptID, holder).key);
cursor = holder;
}
@@ -526,12 +526,12 @@ public:
std::optional<Blob>
doFetchLedgerObject(
xrpl::uint256 const& key,
ripple::uint256 const& key,
std::uint32_t const sequence,
boost::asio::yield_context yield
) const override
{
LOG(log_.debug()) << "Fetching ledger object for seq " << sequence << ", key = " << xrpl::to_string(key);
LOG(log_.debug()) << "Fetching ledger object for seq " << sequence << ", key = " << ripple::to_string(key);
if (auto const res = executor_.read(yield, schema_->selectObject, key, sequence); res) {
if (auto const result = res->template get<Blob>(); result) {
if (result->size())
@@ -548,12 +548,12 @@ public:
std::optional<std::uint32_t>
doFetchLedgerObjectSeq(
xrpl::uint256 const& key,
ripple::uint256 const& key,
std::uint32_t const sequence,
boost::asio::yield_context yield
) const override
{
LOG(log_.debug()) << "Fetching ledger object for seq " << sequence << ", key = " << xrpl::to_string(key);
LOG(log_.debug()) << "Fetching ledger object for seq " << sequence << ", key = " << ripple::to_string(key);
if (auto const res = executor_.read(yield, schema_->selectObject, key, sequence); res) {
if (auto const result = res->template get<Blob, std::uint32_t>(); result) {
auto [_, seq] = result.value();
@@ -568,7 +568,7 @@ public:
}
std::optional<TransactionAndMetadata>
fetchTransaction(xrpl::uint256 const& hash, boost::asio::yield_context yield) const override
fetchTransaction(ripple::uint256 const& hash, boost::asio::yield_context yield) const override
{
if (auto const res = executor_.read(yield, schema_->selectTransaction, hash); res) {
if (auto const maybeValue = res->template get<Blob, Blob, uint32_t, uint32_t>(); maybeValue) {
@@ -584,15 +584,15 @@ public:
return std::nullopt;
}
std::optional<xrpl::uint256>
std::optional<ripple::uint256>
doFetchSuccessorKey(
xrpl::uint256 key,
ripple::uint256 key,
std::uint32_t const ledgerSequence,
boost::asio::yield_context yield
) const override
{
if (auto const res = executor_.read(yield, schema_->selectSuccessor, key, ledgerSequence); res) {
if (auto const result = res->template get<xrpl::uint256>(); result) {
if (auto const result = res->template get<ripple::uint256>(); result) {
if (*result == kLAST_KEY)
return std::nullopt;
return result;
@@ -607,7 +607,7 @@ public:
}
std::vector<TransactionAndMetadata>
fetchTransactions(std::vector<xrpl::uint256> const& hashes, boost::asio::yield_context yield) const override
fetchTransactions(std::vector<ripple::uint256> const& hashes, boost::asio::yield_context yield) const override
{
if (hashes.empty())
return {};
@@ -649,7 +649,7 @@ public:
std::vector<Blob>
doFetchLedgerObjects(
std::vector<xrpl::uint256> const& keys,
std::vector<ripple::uint256> const& keys,
std::uint32_t const sequence,
boost::asio::yield_context yield
) const override
@@ -690,7 +690,7 @@ public:
std::vector<LedgerObject>
fetchLedgerDiff(std::uint32_t const ledgerSequence, boost::asio::yield_context yield) const override
{
auto const [keys, timeDiff] = util::timed([this, &ledgerSequence, yield]() -> std::vector<xrpl::uint256> {
auto const [keys, timeDiff] = util::timed([this, &ledgerSequence, yield]() -> std::vector<ripple::uint256> {
auto const res = executor_.read(yield, schema_->selectDiff, ledgerSequence);
if (not res) {
LOG(log_.error()) << "Could not fetch ledger diff: " << res.error() << "; ledger = " << ledgerSequence;
@@ -703,8 +703,8 @@ public:
return {};
}
std::vector<xrpl::uint256> resultKeys;
for (auto [key] : extract<xrpl::uint256>(results))
std::vector<ripple::uint256> resultKeys;
for (auto [key] : extract<ripple::uint256>(results))
resultKeys.push_back(key);
return resultKeys;
@@ -877,8 +877,8 @@ public:
// to record the URI and link to the issuer_nf_tokens table.
if (record.uri) {
statements.push_back(schema_->insertIssuerNFT.bind(
xrpl::nft::getIssuer(record.tokenID),
static_cast<uint32_t>(xrpl::nft::getTaxon(record.tokenID)),
ripple::nft::getIssuer(record.tokenID),
static_cast<uint32_t>(ripple::nft::getTaxon(record.tokenID)),
record.tokenID
));
statements.push_back(

View File

@@ -77,14 +77,14 @@ public:
}
void
append(xrpl::uint256 const& value) const
append(ripple::uint256 const& value) const
{
auto const rc = cass_collection_append_bytes(
*this,
static_cast<cass_byte_t const*>(static_cast<unsigned char const*>(value.data())),
xrpl::uint256::size()
ripple::uint256::size()
);
throwErrorIfNeeded(rc, "Bind xrpl::uint256");
throwErrorIfNeeded(rc, "Bind ripple::uint256");
}
};
} // namespace data::cassandra::impl

View File

@@ -62,18 +62,18 @@ extractColumn(CassRow const* row, std::size_t idx)
using UintTupleType = std::tuple<uint32_t, uint32_t>;
using UCharVectorType = std::vector<unsigned char>;
if constexpr (std::is_same_v<DecayedType, xrpl::uint256>) {
if constexpr (std::is_same_v<DecayedType, ripple::uint256>) {
cass_byte_t const* buf = nullptr;
std::size_t bufSize = 0;
auto const rc = cass_value_get_bytes(cass_row_get_column(row, idx), &buf, &bufSize);
throwErrorIfNeeded(rc, "Extract xrpl::uint256");
output = xrpl::uint256::fromVoid(buf);
} else if constexpr (std::is_same_v<DecayedType, xrpl::AccountID>) {
throwErrorIfNeeded(rc, "Extract ripple::uint256");
output = ripple::uint256::fromVoid(buf);
} else if constexpr (std::is_same_v<DecayedType, ripple::AccountID>) {
cass_byte_t const* buf = nullptr;
std::size_t bufSize = 0;
auto const rc = cass_value_get_bytes(cass_row_get_column(row, idx), &buf, &bufSize);
throwErrorIfNeeded(rc, "Extract xrpl::AccountID");
output = xrpl::AccountID::fromVoid(buf);
throwErrorIfNeeded(rc, "Extract ripple::AccountID");
output = ripple::AccountID::fromVoid(buf);
} else if constexpr (std::is_same_v<DecayedType, UCharVectorType>) {
cass_byte_t const* buf = nullptr;
std::size_t bufSize = 0;

View File

@@ -107,15 +107,15 @@ public:
using DecayedType = std::decay_t<Type>;
using UCharVectorType = std::vector<unsigned char>;
using UintTupleType = std::tuple<uint32_t, uint32_t>;
using UintByteTupleType = std::tuple<uint32_t, xrpl::uint256>;
using ByteVectorType = std::vector<xrpl::uint256>;
using UintByteTupleType = std::tuple<uint32_t, ripple::uint256>;
using ByteVectorType = std::vector<ripple::uint256>;
if constexpr (std::is_same_v<DecayedType, xrpl::uint256> || std::is_same_v<DecayedType, xrpl::uint192>) {
if constexpr (std::is_same_v<DecayedType, ripple::uint256> || std::is_same_v<DecayedType, ripple::uint192>) {
auto const rc = bindBytes(value.data(), value.size());
throwErrorIfNeeded(rc, "Bind xrpl::BaseUInt");
} else if constexpr (std::is_same_v<DecayedType, xrpl::AccountID>) {
throwErrorIfNeeded(rc, "Bind ripple::base_uint");
} else if constexpr (std::is_same_v<DecayedType, ripple::AccountID>) {
auto const rc = bindBytes(value.data(), value.size());
throwErrorIfNeeded(rc, "Bind xrpl::AccountID");
throwErrorIfNeeded(rc, "Bind ripple::AccountID");
} else if constexpr (std::is_same_v<DecayedType, UCharVectorType>) {
auto const rc = bindBytes(value.data(), value.size());
throwErrorIfNeeded(rc, "Bind vector<unsigned char>");
@@ -129,7 +129,7 @@ public:
} else if constexpr (std::is_same_v<DecayedType, UintTupleType> ||
std::is_same_v<DecayedType, UintByteTupleType>) {
auto const rc = cass_statement_bind_tuple(*this, idx, Tuple{std::forward<Type>(value)});
throwErrorIfNeeded(rc, "Bind tuple<uint32, uint32> or <uint32_t, xrpl::uint256>");
throwErrorIfNeeded(rc, "Bind tuple<uint32, uint32> or <uint32_t, ripple::uint256>");
} else if constexpr (std::is_same_v<DecayedType, ByteVectorType>) {
auto const rc = cass_statement_bind_collection(*this, idx, Collection{std::forward<Type>(value)});
throwErrorIfNeeded(rc, "Bind collection");

View File

@@ -79,14 +79,14 @@ public:
else if constexpr (std::is_convertible_v<DecayedType, int64_t>) {
auto const rc = cass_tuple_set_int64(*this, idx, std::forward<Type>(value));
throwErrorIfNeeded(rc, "Bind int64");
} else if constexpr (std::is_same_v<DecayedType, xrpl::uint256>) {
} else if constexpr (std::is_same_v<DecayedType, ripple::uint256>) {
auto const rc = cass_tuple_set_bytes(
*this,
idx,
static_cast<cass_byte_t const*>(static_cast<unsigned char const*>(value.data())),
value.size()
);
throwErrorIfNeeded(rc, "Bind xrpl::uint256");
throwErrorIfNeeded(rc, "Bind ripple::uint256");
} else {
// type not supported for binding
static_assert(util::Unsupported<DecayedType>);

View File

@@ -1,58 +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.
*/
//==============================================================================
#include "data/impl/InputFile.hpp"
#include <xrpl/basics/base_uint.h>
#include <cstddef>
#include <cstring>
#include <ios>
#include <iosfwd>
#include <string>
#include <utility>
namespace data::impl {
InputFile::InputFile(std::string const& path) : file_(path, std::ios::binary | std::ios::in)
{
}
bool
InputFile::isOpen() const
{
return file_.is_open();
}
bool
InputFile::readRaw(char* data, size_t size)
{
file_.read(data, size);
shasum_.update(data, size);
return not file_.fail();
}
xrpl::uint256
InputFile::hash() const
{
auto sum = shasum_;
return std::move(sum).finalize();
}
} // namespace data::impl

View File

@@ -1,210 +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.
*/
//==============================================================================
#include "data/impl/LedgerCacheFile.hpp"
#include "data/LedgerCache.hpp"
#include "data/Types.hpp"
#include <fmt/format.h>
#include <xrpl/basics/base_uint.h>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <exception>
#include <filesystem>
#include <string>
#include <utility>
namespace data::impl {
using Hash = xrpl::uint256;
using Separator = std::array<char, 16>;
static constexpr Separator kSEPARATOR = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
namespace {
std::expected<std::pair<xrpl::uint256, LedgerCache::CacheEntry>, std::string>
readCacheEntry(InputFile& file, size_t i)
{
xrpl::uint256 key;
if (not file.readRaw(reinterpret_cast<char*>(key.data()), xrpl::BaseUInt<256>::kBytes)) {
return std::unexpected(fmt::format("Failed to read key at index {}", i));
}
uint32_t seq{};
if (not file.read(seq)) {
return std::unexpected(fmt::format("Failed to read sequence at index {}", i));
}
size_t blobSize{};
if (not file.read(blobSize)) {
return std::unexpected(fmt::format("Failed to read blob size at index {}", i));
}
Blob blob(blobSize);
if (not file.readRaw(reinterpret_cast<char*>(blob.data()), blobSize)) {
return std::unexpected(fmt::format("Failed to read blob data at index {}", i));
}
return std::make_pair(key, LedgerCache::CacheEntry{.seq = seq, .blob = std::move(blob)});
}
std::expected<void, std::string>
verifySeparator(Separator const& s)
{
if (not std::ranges::all_of(s, [](char c) { return c == 0; })) {
return std::unexpected{"Separator verification failed - data corruption detected"};
}
return {};
}
} // anonymous namespace
LedgerCacheFile::LedgerCacheFile(std::string path) : path_(std::move(path))
{
}
std::expected<void, std::string>
LedgerCacheFile::write(DataView dataView)
{
auto const newFilePath = fmt::format("{}.new", path_);
auto file = OutputFile{newFilePath};
if (not file.isOpen()) {
return std::unexpected{fmt::format("Couldn't open file: {}", newFilePath)};
}
Header const header{
.latestSeq = dataView.latestSeq, .mapSize = dataView.map.size(), .deletedSize = dataView.deleted.size()
};
file.write(header);
file.write(kSEPARATOR);
for (auto const& [k, v] : dataView.map) {
file.write(k.data(), decltype(k)::kBytes);
file.write(v.seq);
file.write(v.blob.size());
file.writeRaw(reinterpret_cast<char const*>(v.blob.data()), v.blob.size());
}
file.write(kSEPARATOR);
for (auto const& [k, v] : dataView.deleted) {
file.write(k.data(), decltype(k)::kBytes);
file.write(v.seq);
file.write(v.blob.size());
file.writeRaw(reinterpret_cast<char const*>(v.blob.data()), v.blob.size());
}
file.write(kSEPARATOR);
auto const hash = file.hash();
file.write(hash.data(), decltype(hash)::kBytes);
try {
std::filesystem::rename(newFilePath, path_);
} catch (std::exception const& e) {
return std::unexpected{fmt::format("Error moving cache file from {} to {}: {}", newFilePath, path_, e.what())};
}
return {};
}
std::expected<LedgerCacheFile::Data, std::string>
LedgerCacheFile::read(uint32_t minLatestSequence)
{
try {
auto file = InputFile{path_};
if (not file.isOpen()) {
return std::unexpected{fmt::format("Couldn't open file: {}", path_)};
}
Data result;
Header header{};
if (not file.read(header)) {
return std::unexpected{"Error reading cache header"};
}
if (header.version != kVERSION) {
return std::unexpected{
fmt::format("Cache has wrong version: expected {} found {}", kVERSION, header.version)
};
}
if (header.latestSeq < minLatestSequence) {
return std::unexpected{fmt::format("Latest sequence ({}) in the cache file is too low.", header.latestSeq)};
}
result.latestSeq = header.latestSeq;
Separator separator{};
if (not file.readRaw(separator.data(), separator.size())) {
return std::unexpected{"Error reading cache header"};
}
if (auto verificationResult = verifySeparator(separator); not verificationResult.has_value()) {
return std::unexpected{std::move(verificationResult).error()};
}
for (size_t i = 0; i < header.mapSize; ++i) {
auto cacheEntryExpected = readCacheEntry(file, i);
if (not cacheEntryExpected.has_value()) {
return std::unexpected{std::move(cacheEntryExpected).error()};
}
// Using insert with hint here to decrease insert operation complexity to the amortized constant instead of
// logN
result.map.insert(result.map.end(), std::move(cacheEntryExpected).value());
}
if (not file.readRaw(separator.data(), separator.size())) {
return std::unexpected{"Error reading separator"};
}
if (auto verificationResult = verifySeparator(separator); not verificationResult.has_value()) {
return std::unexpected{std::move(verificationResult).error()};
}
for (size_t i = 0; i < header.deletedSize; ++i) {
auto cacheEntryExpected = readCacheEntry(file, i);
if (not cacheEntryExpected.has_value()) {
return std::unexpected{std::move(cacheEntryExpected).error()};
}
result.deleted.insert(result.deleted.end(), std::move(cacheEntryExpected).value());
}
if (not file.readRaw(separator.data(), separator.size())) {
return std::unexpected{"Error reading separator"};
}
if (auto verificationResult = verifySeparator(separator); not verificationResult.has_value()) {
return std::unexpected{std::move(verificationResult).error()};
}
auto const dataHash = file.hash();
xrpl::uint256 hashFromFile{};
if (not file.readRaw(reinterpret_cast<char*>(hashFromFile.data()), decltype(hashFromFile)::kBytes)) {
return std::unexpected{"Error reading hash"};
}
if (dataHash != hashFromFile) {
return std::unexpected{"Hash file corruption detected"};
}
return result;
} catch (std::exception const& e) {
return std::unexpected{fmt::format(" Error reading cache file: {}", e.what())};
} catch (...) {
return std::unexpected{fmt::format(" Error reading cache file")};
}
}
} // namespace data::impl

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