mirror of
				https://github.com/XRPLF/clio.git
				synced 2025-11-04 11:55:51 +00:00 
			
		
		
		
	Compare commits
	
		
			164 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					285d4e6e9b | ||
| 
						 | 
					f2a89b095d | ||
| 
						 | 
					7d4e3619b0 | ||
| 
						 | 
					c4b87d2a0a | ||
| 
						 | 
					2d0253bc4a | ||
| 
						 | 
					017cf2adc9 | ||
| 
						 | 
					64b50b419f | ||
| 
						 | 
					fc3e60f17f | ||
| 
						 | 
					8dc7f16ef1 | ||
| 
						 | 
					15a441b084 | ||
| 
						 | 
					3c4903a339 | ||
| 
						 | 
					b53cfd0ec1 | ||
| 
						 | 
					c41399ef8e | ||
| 
						 | 
					7bef13f913 | ||
| 
						 | 
					4ff2953257 | ||
| 
						 | 
					475e309f25 | ||
| 
						 | 
					a7074dbf0f | ||
| 
						 | 
					66691c45a0 | ||
| 
						 | 
					fe4f95dabd | ||
| 
						 | 
					f62fadc9f9 | ||
| 
						 | 
					afb0c7fee2 | ||
| 
						 | 
					fd73b90416 | ||
| 
						 | 
					541bf4395f | ||
| 
						 | 
					63c80f2b7d | ||
| 
						 | 
					385d99c56e | ||
| 
						 | 
					b5da61931f | ||
| 
						 | 
					6af86367fd | ||
| 
						 | 
					9dc322fc7b | ||
| 
						 | 
					c77154a5e6 | ||
| 
						 | 
					fc3ba07f2e | ||
| 
						 | 
					229ba69b5d | ||
| 
						 | 
					524d096777 | ||
| 
						 | 
					815dfd672e | ||
| 
						 | 
					a4b3877cb2 | ||
| 
						 | 
					6bb5804bb8 | ||
| 
						 | 
					67d99457f2 | ||
| 
						 | 
					0e25c0cabc | ||
| 
						 | 
					6b61984e0e | ||
| 
						 | 
					891fd1e7bf | ||
| 
						 | 
					de6c17797f | ||
| 
						 | 
					0add6c6d90 | ||
| 
						 | 
					e6cdb141c5 | ||
| 
						 | 
					c435466fb0 | ||
| 
						 | 
					f8df654d8e | ||
| 
						 | 
					f3e754398e | ||
| 
						 | 
					d04331d244 | ||
| 
						 | 
					1c82d379d9 | ||
| 
						 | 
					f083c82557 | ||
| 
						 | 
					b6d5ec5cf7 | ||
| 
						 | 
					c62e9d56b8 | ||
| 
						 | 
					2a5d73730f | ||
| 
						 | 
					cffda52ba6 | ||
| 
						 | 
					cf081e7e25 | ||
| 
						 | 
					f351a4cc79 | ||
| 
						 | 
					d60654c3dc | ||
| 
						 | 
					9b0b4f5ad7 | ||
| 
						 | 
					a21011799b | ||
| 
						 | 
					2f40cde7f5 | ||
| 
						 | 
					02a75356fb | ||
| 
						 | 
					b761fffa2d | ||
| 
						 | 
					c3be155f8d | ||
| 
						 | 
					11192c362e | ||
| 
						 | 
					2c18fd5465 | ||
| 
						 | 
					da76907279 | ||
| 
						 | 
					1b42466a0d | ||
| 
						 | 
					87f1c06b5b | ||
| 
						 | 
					d0c6b65870 | ||
| 
						 | 
					3343c1fa6b | ||
| 
						 | 
					c8e3da6470 | ||
| 
						 | 
					c409f8b2d6 | ||
| 
						 | 
					13a9aef579 | ||
| 
						 | 
					af4fde9a3a | ||
| 
						 | 
					0282504f18 | ||
| 
						 | 
					bea905adcd | ||
| 
						 | 
					7a9a1656f9 | ||
| 
						 | 
					0ede0ed351 | ||
| 
						 | 
					ee6018186e | ||
| 
						 | 
					293af3f3b0 | ||
| 
						 | 
					9600637edd | ||
| 
						 | 
					7d0753f1da | ||
| 
						 | 
					b04e090cbb | ||
| 
						 | 
					7088ebad97 | ||
| 
						 | 
					1d33b8e88a | ||
| 
						 | 
					44c07e7332 | ||
| 
						 | 
					dbb8d9eedd | ||
| 
						 | 
					bc9ca41bc1 | ||
| 
						 | 
					e4736bf9d8 | ||
| 
						 | 
					7360c4fd0e | ||
| 
						 | 
					9a9de501e4 | ||
| 
						 | 
					fb473f6d28 | ||
| 
						 | 
					4cbd3f5e18 | ||
| 
						 | 
					5332d3e9f0 | ||
| 
						 | 
					5499b892e6 | ||
| 
						 | 
					0ff1edaac8 | ||
| 
						 | 
					b7c8ed7e3a | ||
| 
						 | 
					49e9d5eda0 | ||
| 
						 | 
					d7605d1069 | ||
| 
						 | 
					58045fb0b6 | ||
| 
						 | 
					1b4eed3b2b | ||
| 
						 | 
					27c9e2a530 | ||
| 
						 | 
					00026ebf5a | ||
| 
						 | 
					fa1e9da0de | ||
| 
						 | 
					2bd7ac346c | ||
| 
						 | 
					5abf912b5a | ||
| 
						 | 
					a7f34490b1 | ||
| 
						 | 
					2a74a65b22 | ||
| 
						 | 
					319cd3d67b | ||
| 
						 | 
					2fef03d766 | ||
| 
						 | 
					fb90fb27ae | ||
| 
						 | 
					9607cff8a0 | ||
| 
						 | 
					00c4287b3b | ||
| 
						 | 
					3095f58dbe | ||
| 
						 | 
					8d0e904ecb | ||
| 
						 | 
					3a3d8d46dd | ||
| 
						 | 
					b2f7983609 | ||
| 
						 | 
					501e131061 | ||
| 
						 | 
					27cf62ca63 | ||
| 
						 | 
					638e2e28ab | ||
| 
						 | 
					f9bb62f670 | ||
| 
						 | 
					895f3c0059 | ||
| 
						 | 
					77494245a9 | ||
| 
						 | 
					648cedcba5 | ||
| 
						 | 
					8a613c5de8 | ||
| 
						 | 
					d6ae890f83 | ||
| 
						 | 
					e16a9510f1 | ||
| 
						 | 
					d6598f30f1 | ||
| 
						 | 
					b12d916276 | ||
| 
						 | 
					4f6f717bfb | ||
| 
						 | 
					46a616cdad | ||
| 
						 | 
					f771478da0 | ||
| 
						 | 
					6e606cb7d8 | ||
| 
						 | 
					5bcc11b347 | ||
| 
						 | 
					d227c53ef3 | ||
| 
						 | 
					e85f6cd9e4 | ||
| 
						 | 
					46bd67a9ec | ||
| 
						 | 
					094ed8f299 | ||
| 
						 | 
					d536433d64 | ||
| 
						 | 
					29847caf0e | ||
| 
						 | 
					aa86075159 | ||
| 
						 | 
					4dd3254354 | ||
| 
						 | 
					f55872d496 | ||
| 
						 | 
					6cb63ed9b2 | ||
| 
						 | 
					f77186002a | ||
| 
						 | 
					66849432be | ||
| 
						 | 
					d26c93a711 | ||
| 
						 | 
					54c9a6e7c0 | ||
| 
						 | 
					b24aadc898 | ||
| 
						 | 
					7bd21345a1 | ||
| 
						 | 
					2ff51ff416 | ||
| 
						 | 
					72f9a8fe78 | ||
| 
						 | 
					b2eacf9868 | ||
| 
						 | 
					77cec26cc9 | ||
| 
						 | 
					6a848649b3 | ||
| 
						 | 
					c761b50fa4 | ||
| 
						 | 
					062ef9f0a5 | ||
| 
						 | 
					c7fee023e7 | ||
| 
						 | 
					e65351e9e6 | ||
| 
						 | 
					8cea9ee7e2 | ||
| 
						 | 
					9d299a1948 | ||
| 
						 | 
					132ec743e1 | ||
| 
						 | 
					bdb72f91a2 | ||
| 
						 | 
					0cdc24023f | ||
| 
						 | 
					c795cf371a | ||
| 
						 | 
					e135aa49d5 | 
							
								
								
									
										13
									
								
								.clang-tidy
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								.clang-tidy
									
									
									
									
									
								
							@@ -8,6 +8,7 @@ Checks: '-*,
 | 
			
		||||
  bugprone-chained-comparison,
 | 
			
		||||
  bugprone-compare-pointer-to-member-virtual-function,
 | 
			
		||||
  bugprone-copy-constructor-init,
 | 
			
		||||
  bugprone-crtp-constructor-accessibility,
 | 
			
		||||
  bugprone-dangling-handle,
 | 
			
		||||
  bugprone-dynamic-static-initializers,
 | 
			
		||||
  bugprone-empty-catch,
 | 
			
		||||
@@ -33,9 +34,11 @@ Checks: '-*,
 | 
			
		||||
  bugprone-non-zero-enum-to-bool-conversion,
 | 
			
		||||
  bugprone-optional-value-conversion,
 | 
			
		||||
  bugprone-parent-virtual-call,
 | 
			
		||||
  bugprone-pointer-arithmetic-on-polymorphic-object,
 | 
			
		||||
  bugprone-posix-return,
 | 
			
		||||
  bugprone-redundant-branch-condition,
 | 
			
		||||
  bugprone-reserved-identifier,
 | 
			
		||||
  bugprone-return-const-ref-from-parameter,
 | 
			
		||||
  bugprone-shared-ptr-array-mismatch,
 | 
			
		||||
  bugprone-signal-handler,
 | 
			
		||||
  bugprone-signed-char-misuse,
 | 
			
		||||
@@ -55,6 +58,7 @@ Checks: '-*,
 | 
			
		||||
  bugprone-suspicious-realloc-usage,
 | 
			
		||||
  bugprone-suspicious-semicolon,
 | 
			
		||||
  bugprone-suspicious-string-compare,
 | 
			
		||||
  bugprone-suspicious-stringview-data-usage,
 | 
			
		||||
  bugprone-swapped-arguments,
 | 
			
		||||
  bugprone-switch-missing-default-case,
 | 
			
		||||
  bugprone-terminating-continue,
 | 
			
		||||
@@ -97,10 +101,12 @@ Checks: '-*,
 | 
			
		||||
  modernize-make-unique,
 | 
			
		||||
  modernize-pass-by-value,
 | 
			
		||||
  modernize-type-traits,
 | 
			
		||||
  modernize-use-designated-initializers,
 | 
			
		||||
  modernize-use-emplace,
 | 
			
		||||
  modernize-use-equals-default,
 | 
			
		||||
  modernize-use-equals-delete,
 | 
			
		||||
  modernize-use-override,
 | 
			
		||||
  modernize-use-ranges,
 | 
			
		||||
  modernize-use-starts-ends-with,
 | 
			
		||||
  modernize-use-std-numbers,
 | 
			
		||||
  modernize-use-using,
 | 
			
		||||
@@ -121,9 +127,11 @@ Checks: '-*,
 | 
			
		||||
  readability-convert-member-functions-to-static,
 | 
			
		||||
  readability-duplicate-include,
 | 
			
		||||
  readability-else-after-return,
 | 
			
		||||
  readability-enum-initial-value,
 | 
			
		||||
  readability-implicit-bool-conversion,
 | 
			
		||||
  readability-inconsistent-declaration-parameter-name,
 | 
			
		||||
  readability-make-member-function-const,
 | 
			
		||||
  readability-math-missing-parentheses,
 | 
			
		||||
  readability-misleading-indentation,
 | 
			
		||||
  readability-non-const-parameter,
 | 
			
		||||
  readability-redundant-casting,
 | 
			
		||||
@@ -135,14 +143,15 @@ Checks: '-*,
 | 
			
		||||
  readability-simplify-boolean-expr,
 | 
			
		||||
  readability-static-accessed-through-instance,
 | 
			
		||||
  readability-static-definition-in-anonymous-namespace,
 | 
			
		||||
  readability-suspicious-call-argument
 | 
			
		||||
  readability-suspicious-call-argument,
 | 
			
		||||
  readability-use-std-min-max
 | 
			
		||||
  '
 | 
			
		||||
 | 
			
		||||
CheckOptions:
 | 
			
		||||
  readability-braces-around-statements.ShortStatementLines: 2
 | 
			
		||||
  bugprone-unsafe-functions.ReportMoreUnsafeFunctions: true
 | 
			
		||||
  bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc
 | 
			
		||||
  misc-include-cleaner.IgnoreHeaders: '.*/(detail|impl)/.*;.*(expected|unexpected).*'
 | 
			
		||||
  misc-include-cleaner.IgnoreHeaders: '.*/(detail|impl)/.*;.*(expected|unexpected).*;.*ranges_lower_bound\.h;time.h;stdlib.h'
 | 
			
		||||
 | 
			
		||||
HeaderFilterRegex: '^.*/(src|tests)/.*\.(h|hpp)$'
 | 
			
		||||
WarningsAsErrors: '*'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.clangd
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								.clangd
									
									
									
									
									
								
							@@ -8,3 +8,6 @@ Diagnostics:
 | 
			
		||||
    IgnoreHeader:
 | 
			
		||||
      - ".*/(detail|impl)/.*"
 | 
			
		||||
      - ".*expected.*"
 | 
			
		||||
      - ".*ranges_lower_bound.h"
 | 
			
		||||
      - "time.h"
 | 
			
		||||
      - "stdlib.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -13,14 +13,36 @@ TMPDIR=${ROOT}/.cache/doxygen
 | 
			
		||||
TMPFILE=${TMPDIR}/docs.log
 | 
			
		||||
DOCDIR=${TMPDIR}/out
 | 
			
		||||
 | 
			
		||||
# Check doxygen is at all installed
 | 
			
		||||
if [ -z "$DOXYGEN" ]; then
 | 
			
		||||
    # No hard error if doxygen is not installed yet
 | 
			
		||||
    cat <<EOF
 | 
			
		||||
 | 
			
		||||
                                   WARNING
 | 
			
		||||
-----------------------------------------------------------------------------
 | 
			
		||||
        'doxygen' is required to check documentation.
 | 
			
		||||
        Please install it for next time. For the time being it's on CI.
 | 
			
		||||
        'doxygen' is required to check documentation. 
 | 
			
		||||
        Please install it for next time.
 | 
			
		||||
 | 
			
		||||
        Your changes may fail to pass CI once pushed.
 | 
			
		||||
-----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
EOF
 | 
			
		||||
    exit 0
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Check version of doxygen is at least 1.12
 | 
			
		||||
version=$($DOXYGEN --version | grep -o '[0-9\.]*')
 | 
			
		||||
 | 
			
		||||
if [[ "1.12.0" > "$version" ]]; then
 | 
			
		||||
    # No hard error if doxygen version is not the one we want - let CI deal with it
 | 
			
		||||
    cat <<EOF
 | 
			
		||||
 | 
			
		||||
                                    ERROR
 | 
			
		||||
-----------------------------------------------------------------------------
 | 
			
		||||
        A minimum of version 1.12 of `which doxygen` is required.
 | 
			
		||||
        Your version is $version. Please upgrade it for next time.
 | 
			
		||||
 | 
			
		||||
        Your changes may fail to pass CI once pushed.
 | 
			
		||||
-----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
EOF
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,20 @@
 | 
			
		||||
# This script checks the format of the code and cmake files.
 | 
			
		||||
# In many cases it will automatically fix the issues and abort the commit.
 | 
			
		||||
 | 
			
		||||
no_formatted_directories_staged() {
 | 
			
		||||
    staged_directories=$(git diff-index --cached --name-only HEAD | awk -F/ '{print $1}')
 | 
			
		||||
    for sd in $staged_directories; do
 | 
			
		||||
        if [[ "$sd" =~ ^(benchmark|cmake|src|tests)$ ]]; then
 | 
			
		||||
            return 1
 | 
			
		||||
        fi
 | 
			
		||||
    done
 | 
			
		||||
    return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if no_formatted_directories_staged ; then
 | 
			
		||||
    exit 0
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo "+ Checking code format..."
 | 
			
		||||
 | 
			
		||||
# paths to check and re-format
 | 
			
		||||
@@ -12,12 +26,12 @@ sources="src tests"
 | 
			
		||||
formatter="clang-format -i"
 | 
			
		||||
version=$($formatter --version | grep -o '[0-9\.]*')
 | 
			
		||||
 | 
			
		||||
if [[ "18.0.0" > "$version" ]]; then
 | 
			
		||||
if [[ "19.0.0" > "$version" ]]; then
 | 
			
		||||
    cat <<EOF
 | 
			
		||||
 | 
			
		||||
                                    ERROR
 | 
			
		||||
-----------------------------------------------------------------------------
 | 
			
		||||
            A minimum of version 18 of `which clang-format` is required.
 | 
			
		||||
            A minimum of version 19 of `which clang-format` is required.
 | 
			
		||||
            Your version is $version.
 | 
			
		||||
            Please fix paths and run again.
 | 
			
		||||
-----------------------------------------------------------------------------
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,5 @@
 | 
			
		||||
# This script is intended to be run from the root of the repository.
 | 
			
		||||
 | 
			
		||||
source .githooks/check-format
 | 
			
		||||
#source .githooks/check-docs
 | 
			
		||||
source .githooks/check-docs
 | 
			
		||||
 | 
			
		||||
# TODO: Fix Doxygen issue with reference links. See https://github.com/XRPLF/clio/issues/1431
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,58 @@
 | 
			
		||||
#!/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
 | 
			
		||||
# git config commit.gpgsign true
 | 
			
		||||
# git config tag.gpgsign true
 | 
			
		||||
 | 
			
		||||
verify_commit_signed() {
 | 
			
		||||
    if git verify-commit HEAD &> /dev/null; then
 | 
			
		||||
        :
 | 
			
		||||
        # echo "HEAD commit seems signed..."
 | 
			
		||||
    else
 | 
			
		||||
        echo "HEAD commit isn't signed!"
 | 
			
		||||
        exit 1
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
verify_tag() {
 | 
			
		||||
    if git describe --exact-match --tags HEAD &> /dev/null; then
 | 
			
		||||
        : # You might be ok to push
 | 
			
		||||
        # echo "Tag is annotated."
 | 
			
		||||
        return 0
 | 
			
		||||
    else
 | 
			
		||||
        echo "Tag for [$version] not an annotated tag."
 | 
			
		||||
        exit 1
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
verify_tag_signed() {
 | 
			
		||||
    if git verify-tag "$version" &> /dev/null ; then
 | 
			
		||||
        : # ok, I guess we'll let you push
 | 
			
		||||
        # echo "Tag appears signed"
 | 
			
		||||
        return 0
 | 
			
		||||
    else
 | 
			
		||||
        echo "$version tag isn't signed"
 | 
			
		||||
        echo "Sign it with [git tag -ams\"$version\" $version]"
 | 
			
		||||
        exit 1
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
while read local_ref local_oid remote_ref remote_oid; do
 | 
			
		||||
    # Check some things if we're pushing a branch called "release/"
 | 
			
		||||
    if echo "$remote_ref" | 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
 | 
			
		||||
            : # Ok, I guess you can push
 | 
			
		||||
        else
 | 
			
		||||
            exit 1
 | 
			
		||||
        fi
 | 
			
		||||
    fi
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'pre-push' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; }
 | 
			
		||||
 | 
			
		||||
git lfs pre-push "$@"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										66
									
								
								.github/actions/build_docker_image/action.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								.github/actions/build_docker_image/action.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
name: Build and push Docker image
 | 
			
		||||
description: Build and push Docker image to DockerHub and GitHub Container Registry
 | 
			
		||||
inputs:
 | 
			
		||||
  image_name:
 | 
			
		||||
    description: Name of the image to build
 | 
			
		||||
    required: true
 | 
			
		||||
  push_image:
 | 
			
		||||
    description: Whether to push the image to the registry (true/false)
 | 
			
		||||
    required: true
 | 
			
		||||
  directory:
 | 
			
		||||
    description: The directory containing the Dockerfile
 | 
			
		||||
    required: true
 | 
			
		||||
  tags:
 | 
			
		||||
    description: Comma separated tags to apply to the image
 | 
			
		||||
    required: true
 | 
			
		||||
  platforms:
 | 
			
		||||
    description: Platforms to build the image for (e.g. linux/amd64,linux/arm64)
 | 
			
		||||
    required: true
 | 
			
		||||
  description:
 | 
			
		||||
    description: Short description of the image
 | 
			
		||||
    required: true
 | 
			
		||||
runs:
 | 
			
		||||
  using: composite
 | 
			
		||||
  steps:
 | 
			
		||||
      - name: Login to DockerHub
 | 
			
		||||
        if: ${{ inputs.push_image == 'true' }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ env.DOCKERHUB_USER }}
 | 
			
		||||
          password: ${{ env.DOCKERHUB_PW }}
 | 
			
		||||
 | 
			
		||||
      - name: Login to GitHub Container Registry
 | 
			
		||||
        if: ${{ inputs.push_image == 'true' }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ghcr.io
 | 
			
		||||
          username: ${{ github.repository_owner }}
 | 
			
		||||
          password: ${{ env.GITHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - uses: docker/setup-qemu-action@v3
 | 
			
		||||
      - uses: docker/setup-buildx-action@v3
 | 
			
		||||
 | 
			
		||||
      - uses: docker/metadata-action@v5
 | 
			
		||||
        id: meta
 | 
			
		||||
        with:
 | 
			
		||||
          images: ${{ inputs.image_name }}
 | 
			
		||||
          tags: ${{ inputs.tags }}
 | 
			
		||||
 | 
			
		||||
      - name: Build and push
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
        with:
 | 
			
		||||
          context: ${{ inputs.directory }}
 | 
			
		||||
          platforms: ${{ inputs.platforms }}
 | 
			
		||||
          push: ${{ inputs.push_image == 'true' }}
 | 
			
		||||
          tags: ${{ steps.meta.outputs.tags }}
 | 
			
		||||
 | 
			
		||||
      - name: Update DockerHub description
 | 
			
		||||
        if: ${{ inputs.push_image == 'true' }}
 | 
			
		||||
        uses: peter-evans/dockerhub-description@v4
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ env.DOCKERHUB_USER }}
 | 
			
		||||
          password: ${{ env.DOCKERHUB_PW }}
 | 
			
		||||
          repository: ${{ inputs.image_name }}
 | 
			
		||||
          short-description: ${{ inputs.description }}
 | 
			
		||||
          readme-filepath: ${{ inputs.directory }}/README.md
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
version: 2
 | 
			
		||||
updates:
 | 
			
		||||
  - package-ecosystem: "github-actions"
 | 
			
		||||
    directory: "/"
 | 
			
		||||
    schedule:
 | 
			
		||||
      interval: "weekly"
 | 
			
		||||
      day: "monday"
 | 
			
		||||
      time: "04:00"
 | 
			
		||||
      timezone: "Etc/GMT"
 | 
			
		||||
    reviewers:
 | 
			
		||||
      - "cindyyan317"
 | 
			
		||||
      - "godexsoft"
 | 
			
		||||
      - "kuznetsss"
 | 
			
		||||
    commit-message:
 | 
			
		||||
      prefix: "[CI] "
 | 
			
		||||
    target-branch: "develop"
 | 
			
		||||
							
								
								
									
										3
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							@@ -23,7 +23,7 @@ jobs:
 | 
			
		||||
        run: |
 | 
			
		||||
          ./.githooks/check-format --diff
 | 
			
		||||
        shell: bash
 | 
			
		||||
  
 | 
			
		||||
 
 | 
			
		||||
  check_docs:
 | 
			
		||||
    name: Check documentation
 | 
			
		||||
    runs-on: ubuntu-20.04
 | 
			
		||||
@@ -211,6 +211,7 @@ jobs:
 | 
			
		||||
      - uses: actions/download-artifact@v4
 | 
			
		||||
        with:
 | 
			
		||||
          name: clio_tests_${{ runner.os }}_${{ matrix.build_type }}_${{ matrix.conan_profile }}
 | 
			
		||||
 | 
			
		||||
      - name: Run clio_tests
 | 
			
		||||
        run: |
 | 
			
		||||
          chmod +x ./clio_tests
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										95
									
								
								.github/workflows/build_clio_docker_image.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								.github/workflows/build_clio_docker_image.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
name: Build and publish Clio docker image
 | 
			
		||||
on:
 | 
			
		||||
  workflow_call:
 | 
			
		||||
    inputs:
 | 
			
		||||
      tags:
 | 
			
		||||
        required: true
 | 
			
		||||
        type: string
 | 
			
		||||
        description: Comma separated tags for docker image
 | 
			
		||||
      artifact_name:
 | 
			
		||||
        type: string
 | 
			
		||||
        description: Name of Github artifact to put into docker image
 | 
			
		||||
      strip_binary:
 | 
			
		||||
        type: boolean
 | 
			
		||||
        description: Whether to strip clio binary
 | 
			
		||||
        default: true
 | 
			
		||||
      publish_image:
 | 
			
		||||
        type: boolean
 | 
			
		||||
        description: Whether to publish docker image
 | 
			
		||||
        required: true
 | 
			
		||||
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
    inputs:
 | 
			
		||||
      tags:
 | 
			
		||||
        required: true
 | 
			
		||||
        type: string
 | 
			
		||||
        description: Comma separated tags for docker image
 | 
			
		||||
      clio_server_binary_url:
 | 
			
		||||
        required: true
 | 
			
		||||
        type: string
 | 
			
		||||
        description: Url to download clio_server binary from
 | 
			
		||||
      binary_sha256:
 | 
			
		||||
        required: true
 | 
			
		||||
        type: string
 | 
			
		||||
        description: sha256 hash of the binary
 | 
			
		||||
      strip_binary:
 | 
			
		||||
        type: boolean
 | 
			
		||||
        description: Whether to strip clio binary
 | 
			
		||||
        default: true
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  build_and_publish_image:
 | 
			
		||||
    name: Build and publish image
 | 
			
		||||
    runs-on: ubuntu-20.04
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
      - name: Download Clio binary from artifact
 | 
			
		||||
        if: ${{ inputs.artifact_name != null }}
 | 
			
		||||
        uses: actions/download-artifact@v4
 | 
			
		||||
        with:
 | 
			
		||||
          name: ${{ inputs.artifact_name }}
 | 
			
		||||
          path: ./docker/clio/artifact/
 | 
			
		||||
 | 
			
		||||
      - name: Download Clio binary from url
 | 
			
		||||
        if: ${{ inputs.clio_server_binary_url != null }}
 | 
			
		||||
        shell: bash
 | 
			
		||||
        run: |
 | 
			
		||||
          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
 | 
			
		||||
          artifact=$(find . -type f)
 | 
			
		||||
          if [[ $artifact == *.zip ]]; then
 | 
			
		||||
            unzip $artifact
 | 
			
		||||
          elif [[ $artifact == *.tar.gz ]]; then
 | 
			
		||||
            tar -xvf $artifact
 | 
			
		||||
          fi
 | 
			
		||||
          mv clio_server ../
 | 
			
		||||
          cd ../
 | 
			
		||||
          rm -rf ./artifact
 | 
			
		||||
 | 
			
		||||
      - name: Strip binary
 | 
			
		||||
        if: ${{ inputs.strip_binary }}
 | 
			
		||||
        shell: bash
 | 
			
		||||
        run: strip ./docker/clio/clio_server
 | 
			
		||||
 | 
			
		||||
      - name: Build Docker image
 | 
			
		||||
        uses: ./.github/actions/build_docker_image
 | 
			
		||||
        env:
 | 
			
		||||
          DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }}
 | 
			
		||||
          DOCKERHUB_PW: ${{ secrets.DOCKERHUB_PW }}
 | 
			
		||||
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
        with:
 | 
			
		||||
          image_name: rippleci/clio
 | 
			
		||||
          push_image: ${{ inputs.publish_image }}
 | 
			
		||||
          directory: docker/clio
 | 
			
		||||
          tags: ${{ inputs.tags }}
 | 
			
		||||
          platforms: linux/amd64
 | 
			
		||||
          description: Clio is an XRP Ledger API server.
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/check_libxrpl.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/check_libxrpl.yml
									
									
									
									
										vendored
									
									
								
							@@ -47,7 +47,7 @@ jobs:
 | 
			
		||||
      - name: Upload clio_tests
 | 
			
		||||
        uses: actions/upload-artifact@v4
 | 
			
		||||
        with:
 | 
			
		||||
          name: clio_tests_libxrpl-${{ github.event.client_payload.version }}
 | 
			
		||||
          name: clio_tests_check_libxrpl
 | 
			
		||||
          path: build/clio_tests
 | 
			
		||||
 | 
			
		||||
  run_tests:
 | 
			
		||||
@@ -60,7 +60,7 @@ jobs:
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/download-artifact@v4
 | 
			
		||||
        with:
 | 
			
		||||
          name: clio_tests_libxrpl-${{ github.event.client_payload.version }}
 | 
			
		||||
          name: clio_tests_check_libxrpl
 | 
			
		||||
 | 
			
		||||
      - name: Run clio_tests
 | 
			
		||||
        run: |
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								.github/workflows/check_pr_title.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								.github/workflows/check_pr_title.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
name: Check PR title
 | 
			
		||||
on:
 | 
			
		||||
  pull_request:
 | 
			
		||||
    types: [opened, edited, reopened, synchronize]
 | 
			
		||||
    branches: [develop]
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  check_title:
 | 
			
		||||
    runs-on: ubuntu-20.04
 | 
			
		||||
    # permissions:
 | 
			
		||||
    #   pull-requests: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: ytanikin/PRConventionalCommits@1.3.0
 | 
			
		||||
        with:
 | 
			
		||||
          task_types: '["build","feat","fix","docs","test","ci","style","refactor","perf","chore"]'
 | 
			
		||||
          add_label: false
 | 
			
		||||
          # Turned off labelling because it leads to an error, see https://github.com/ytanikin/PRConventionalCommits/issues/19
 | 
			
		||||
          # custom_labels: '{"build":"build", "feat":"enhancement", "fix":"bug", "docs":"documentation", "test":"testability", "ci":"ci", "style":"refactoring", "refactor":"refactoring", "perf":"performance", "chore":"tooling"}'
 | 
			
		||||
							
								
								
									
										8
									
								
								.github/workflows/clang-tidy.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/clang-tidy.yml
									
									
									
									
										vendored
									
									
								
							@@ -60,7 +60,7 @@ jobs:
 | 
			
		||||
        shell: bash
 | 
			
		||||
        id: run_clang_tidy
 | 
			
		||||
        run: |
 | 
			
		||||
          run-clang-tidy-18 -p build -j ${{ steps.number_of_threads.outputs.threads_number }} -fix -quiet 1>output.txt
 | 
			
		||||
          run-clang-tidy-19 -p build -j ${{ steps.number_of_threads.outputs.threads_number }} -fix -quiet 1>output.txt
 | 
			
		||||
 | 
			
		||||
      - name: Check format
 | 
			
		||||
        if: ${{ steps.run_clang_tidy.outcome != 'success' }}
 | 
			
		||||
@@ -89,7 +89,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
            List of the issues found: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/
 | 
			
		||||
 | 
			
		||||
      - uses: crazy-max/ghaction-import-gpg@v5
 | 
			
		||||
      - uses: crazy-max/ghaction-import-gpg@v6
 | 
			
		||||
        if: ${{ steps.run_clang_tidy.outcome != 'success' }}
 | 
			
		||||
        with:
 | 
			
		||||
          gpg_private_key: ${{ secrets.ACTIONS_GPG_PRIVATE_KEY }}
 | 
			
		||||
@@ -99,7 +99,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
      - name: Create PR with fixes
 | 
			
		||||
        if: ${{ steps.run_clang_tidy.outcome != 'success' }}
 | 
			
		||||
        uses: peter-evans/create-pull-request@v5
 | 
			
		||||
        uses: peter-evans/create-pull-request@v7
 | 
			
		||||
        env:
 | 
			
		||||
          GH_REPO: ${{ github.repository }}
 | 
			
		||||
          GH_TOKEN: ${{ github.token }}
 | 
			
		||||
@@ -109,7 +109,7 @@ jobs:
 | 
			
		||||
          branch: "clang_tidy/autofix"
 | 
			
		||||
          branch-suffix: timestamp
 | 
			
		||||
          delete-branch: true
 | 
			
		||||
          title: "[CI] clang-tidy auto fixes"
 | 
			
		||||
          title: "style: clang-tidy auto fixes"
 | 
			
		||||
          body: "Fixes #${{ steps.create_issue.outputs.created_issue_id }}. Please review and commit clang-tidy fixes."
 | 
			
		||||
          reviewers: "cindyyan317,godexsoft,kuznetsss"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ jobs:
 | 
			
		||||
        id: check
 | 
			
		||||
        shell: bash
 | 
			
		||||
        run: |
 | 
			
		||||
          passed=$(if [[ $(git log -1 --pretty=format:%s | grep '\[CI\] clang-tidy auto fixes') ]]; then echo 'true' ; else echo 'false' ; fi)
 | 
			
		||||
          passed=$(if [[ $(git log -1 --pretty=format:%s | grep 'style: clang-tidy auto fixes') ]]; then echo 'true' ; else echo 'false' ; fi)
 | 
			
		||||
          echo "passed=$passed" >> $GITHUB_OUTPUT
 | 
			
		||||
 | 
			
		||||
      - name: Run clang-tidy workflow
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/docs.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/docs.yml
									
									
									
									
										vendored
									
									
								
							@@ -34,7 +34,7 @@ jobs:
 | 
			
		||||
          cmake ../docs && cmake --build . --target docs
 | 
			
		||||
      
 | 
			
		||||
      - name: Setup Pages
 | 
			
		||||
        uses: actions/configure-pages@v4
 | 
			
		||||
        uses: actions/configure-pages@v5
 | 
			
		||||
 | 
			
		||||
      - name: Upload artifact
 | 
			
		||||
        uses: actions/upload-pages-artifact@v3
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								.github/workflows/nightly.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								.github/workflows/nightly.yml
									
									
									
									
										vendored
									
									
								
							@@ -3,6 +3,10 @@ on:
 | 
			
		||||
  schedule:
 | 
			
		||||
    - cron:  '0 5 * * 1-5'
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
  pull_request:
 | 
			
		||||
    paths:
 | 
			
		||||
      - '.github/workflows/nightly.yml'
 | 
			
		||||
      - '.github/workflows/build_clio_docker_image.yml'
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  build:
 | 
			
		||||
@@ -13,12 +17,15 @@ jobs:
 | 
			
		||||
        include:
 | 
			
		||||
          - os: macos14
 | 
			
		||||
            build_type: Release
 | 
			
		||||
            static: false
 | 
			
		||||
          - os: heavy
 | 
			
		||||
            build_type: Release
 | 
			
		||||
            static: true
 | 
			
		||||
            container:
 | 
			
		||||
              image: rippleci/clio_ci:latest
 | 
			
		||||
          - os: heavy
 | 
			
		||||
            build_type: Debug
 | 
			
		||||
            static: true
 | 
			
		||||
            container:
 | 
			
		||||
              image: rippleci/clio_ci:latest
 | 
			
		||||
    runs-on: [self-hosted, "${{ matrix.os }}"]
 | 
			
		||||
@@ -50,6 +57,7 @@ jobs:
 | 
			
		||||
          conan_profile: ${{ steps.conan.outputs.conan_profile }}
 | 
			
		||||
          conan_cache_hit: ${{ steps.restore_cache.outputs.conan_cache_hit }}
 | 
			
		||||
          build_type: ${{ matrix.build_type }}
 | 
			
		||||
          static: ${{ matrix.static }}
 | 
			
		||||
 | 
			
		||||
      - name: Build Clio
 | 
			
		||||
        uses: ./.github/actions/build_clio
 | 
			
		||||
@@ -130,6 +138,7 @@ jobs:
 | 
			
		||||
          ./clio_integration_tests --backend_host=scylladb
 | 
			
		||||
 | 
			
		||||
  nightly_release:
 | 
			
		||||
    if: ${{ github.event_name != 'pull_request' }}
 | 
			
		||||
    needs: run_tests
 | 
			
		||||
    runs-on: ubuntu-20.04
 | 
			
		||||
    env:
 | 
			
		||||
@@ -143,13 +152,13 @@ jobs:
 | 
			
		||||
      - uses: actions/download-artifact@v4
 | 
			
		||||
        with:
 | 
			
		||||
          path: nightly_release
 | 
			
		||||
          pattern: clio_server_*
 | 
			
		||||
 | 
			
		||||
      - name: Prepare files
 | 
			
		||||
        shell: bash
 | 
			
		||||
        run: |
 | 
			
		||||
          cp ${{ github.workspace }}/.github/workflows/nightly_notes.md "${RUNNER_TEMP}/nightly_notes.md"
 | 
			
		||||
          cd nightly_release
 | 
			
		||||
          rm -r clio_*tests*
 | 
			
		||||
          for d in $(ls); do
 | 
			
		||||
            archive_name=$(ls $d)
 | 
			
		||||
            mv ${d}/${archive_name} ./
 | 
			
		||||
@@ -172,9 +181,21 @@ jobs:
 | 
			
		||||
            --target $GITHUB_SHA --notes-file "${RUNNER_TEMP}/nightly_notes.md" \
 | 
			
		||||
            ./nightly_release/clio_server*
 | 
			
		||||
 | 
			
		||||
  build_and_publish_docker_image:
 | 
			
		||||
    uses: ./.github/workflows/build_clio_docker_image.yml
 | 
			
		||||
    needs: run_tests
 | 
			
		||||
    secrets: inherit
 | 
			
		||||
    with:
 | 
			
		||||
      tags: |
 | 
			
		||||
          type=raw,value=nightly
 | 
			
		||||
          type=raw,value=${{ github.sha }}
 | 
			
		||||
      artifact_name: clio_server_Linux_Release
 | 
			
		||||
      strip_binary: true
 | 
			
		||||
      publish_image: ${{ github.event_name != 'pull_request' }}
 | 
			
		||||
 | 
			
		||||
  create_issue_on_failure:
 | 
			
		||||
    needs: [build, run_tests, nightly_release]
 | 
			
		||||
    if: ${{ always() && contains(needs.*.result, 'failure') }}
 | 
			
		||||
    needs: [build, run_tests, nightly_release, build_and_publish_docker_image]
 | 
			
		||||
    if: ${{ always() && contains(needs.*.result, 'failure') && github.event_name != 'pull_request' }}
 | 
			
		||||
    runs-on: ubuntu-20.04
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: write
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								.github/workflows/update_docker_ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								.github/workflows/update_docker_ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -18,30 +18,19 @@ jobs:
 | 
			
		||||
    name: Build and push docker image
 | 
			
		||||
    runs-on: [self-hosted, heavy]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Login to DockerHub
 | 
			
		||||
        if: ${{ github.event_name != 'pull_request' }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ secrets.DOCKERHUB_USER }}
 | 
			
		||||
          password: ${{ secrets.DOCKERHUB_PW }}
 | 
			
		||||
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - uses: docker/setup-qemu-action@v3
 | 
			
		||||
      - uses: docker/setup-buildx-action@v3
 | 
			
		||||
      - uses: docker/metadata-action@v5
 | 
			
		||||
        id: meta
 | 
			
		||||
      - uses: ./.github/actions/build_docker_image
 | 
			
		||||
        env:
 | 
			
		||||
          DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }}
 | 
			
		||||
          DOCKERHUB_PW: ${{ secrets.DOCKERHUB_PW }}
 | 
			
		||||
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
        with:
 | 
			
		||||
          images: rippleci/clio_ci
 | 
			
		||||
          image_name: rippleci/clio_ci
 | 
			
		||||
          push_image: ${{ github.event_name != 'pull_request' }}
 | 
			
		||||
          directory: docker/ci
 | 
			
		||||
          tags: |
 | 
			
		||||
            type=raw,value=latest
 | 
			
		||||
            type=raw,value=gcc_12_clang_16
 | 
			
		||||
            type=raw,value=${{ env.GITHUB_SHA }}
 | 
			
		||||
 | 
			
		||||
      - name: Build and push
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
        with:
 | 
			
		||||
          context: ${{ github.workspace }}/docker/ci
 | 
			
		||||
            type=raw,value=${{ github.sha }}
 | 
			
		||||
          platforms: linux/amd64,linux/arm64
 | 
			
		||||
          push: ${{ github.event_name != 'pull_request' }}
 | 
			
		||||
          tags: ${{ steps.meta.outputs.tags }}
 | 
			
		||||
 | 
			
		||||
          description: CI image for XRPLF/clio.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/upload_coverage_report.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/upload_coverage_report.yml
									
									
									
									
										vendored
									
									
								
							@@ -23,7 +23,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
      - name: Upload coverage report
 | 
			
		||||
        if: ${{ hashFiles('build/coverage_report.xml') != '' }}
 | 
			
		||||
        uses: wandalen/wretry.action@v1.4.10
 | 
			
		||||
        uses: wandalen/wretry.action@v3.7.3
 | 
			
		||||
        with:
 | 
			
		||||
          action: codecov/codecov-action@v4
 | 
			
		||||
          with: |
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -8,4 +8,4 @@
 | 
			
		||||
.DS_Store
 | 
			
		||||
CMakeUserPresets.json
 | 
			
		||||
config.json
 | 
			
		||||
src/main/impl/Build.cpp
 | 
			
		||||
src/util/build/Build.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ git config --local core.hooksPath .githooks
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Git hooks dependencies
 | 
			
		||||
The pre-commit hook requires `clang-format >= 18.0.0` and `cmake-format` to be installed on your machine.
 | 
			
		||||
The pre-commit hook requires `clang-format >= 19.0.0` and `cmake-format` to be installed on your machine.
 | 
			
		||||
`clang-format` can be installed using `brew` on macOS and default package manager on Linux.
 | 
			
		||||
`cmake-format` can be installed using `pip`.
 | 
			
		||||
The hook will also attempt to automatically use `doxygen` to verify that everything public in the codebase is covered by doc comments. If `doxygen` is not installed, the hook will raise a warning suggesting to install `doxygen` for future commits.
 | 
			
		||||
@@ -72,6 +72,9 @@ git push --force
 | 
			
		||||
Clio uses `ccache` to speed up compilation. If you want to use it, please make sure it is installed on your machine.
 | 
			
		||||
CMake will automatically detect it and use it if it is available.
 | 
			
		||||
 | 
			
		||||
## Opening a pull request
 | 
			
		||||
When a pull request is open CI will perform checks on the new code.
 | 
			
		||||
Title of the pull request and squashed commit should follow [conventional commits specification](https://www.conventionalcommits.org/en/v1.0.0/).
 | 
			
		||||
 | 
			
		||||
## Fixing issues found during code review
 | 
			
		||||
While your code is in review, it's possible that some changes will be requested by reviewer(s).
 | 
			
		||||
@@ -102,7 +105,7 @@ The button for that is near the bottom of the PR's page on GitHub.
 | 
			
		||||
This is a non-exhaustive list of recommended style guidelines. These are not always strictly enforced and serve as a way to keep the codebase coherent.
 | 
			
		||||
 | 
			
		||||
## Formatting
 | 
			
		||||
Code must conform to `clang-format` version 18, unless the result would be unreasonably difficult to read or maintain.
 | 
			
		||||
Code must conform to `clang-format` version 19, unless the result would be unreasonably difficult to read or maintain.
 | 
			
		||||
In most cases the pre-commit hook will take care of formatting and will fix any issues automatically.
 | 
			
		||||
To manually format your code, use `clang-format -i <your changed files>` for C++ files and `cmake-format -i <your changed files>` for CMake files.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,5 +12,5 @@ target_sources(
 | 
			
		||||
include(deps/gbench)
 | 
			
		||||
 | 
			
		||||
target_include_directories(clio_benchmark PRIVATE .)
 | 
			
		||||
target_link_libraries(clio_benchmark PUBLIC clio benchmark::benchmark_main)
 | 
			
		||||
target_link_libraries(clio_benchmark PUBLIC clio_etl benchmark::benchmark_main)
 | 
			
		||||
set_target_properties(clio_benchmark PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,6 @@
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <latch>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
@@ -209,7 +208,7 @@ benchmarkThreads(benchmark::State& state)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename CtxType>
 | 
			
		||||
void
 | 
			
		||||
static void
 | 
			
		||||
benchmarkExecutionContextBatched(benchmark::State& state)
 | 
			
		||||
{
 | 
			
		||||
    auto data = generateData();
 | 
			
		||||
@@ -220,7 +219,7 @@ benchmarkExecutionContextBatched(benchmark::State& state)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename CtxType>
 | 
			
		||||
void
 | 
			
		||||
static void
 | 
			
		||||
benchmarkAnyExecutionContextBatched(benchmark::State& state)
 | 
			
		||||
{
 | 
			
		||||
    auto data = generateData();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,11 +17,12 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include "main/Build.hpp"
 | 
			
		||||
#include "util/build/Build.hpp"
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace Build {
 | 
			
		||||
namespace util::build {
 | 
			
		||||
 | 
			
		||||
static constexpr char versionString[] = "@CLIO_VERSION@";
 | 
			
		||||
 | 
			
		||||
std::string const&
 | 
			
		||||
@@ -38,4 +39,4 @@ getClioFullVersionString()
 | 
			
		||||
    return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace Build
 | 
			
		||||
}  // namespace util::build
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ if (lint)
 | 
			
		||||
    endif ()
 | 
			
		||||
    message(STATUS "Using clang-tidy from CLIO_CLANG_TIDY_BIN")
 | 
			
		||||
  else ()
 | 
			
		||||
    find_program(_CLANG_TIDY_BIN NAMES "clang-tidy-18" "clang-tidy" REQUIRED)
 | 
			
		||||
    find_program(_CLANG_TIDY_BIN NAMES "clang-tidy-19" "clang-tidy" REQUIRED)
 | 
			
		||||
  endif ()
 | 
			
		||||
 | 
			
		||||
  if (NOT _CLANG_TIDY_BIN)
 | 
			
		||||
 
 | 
			
		||||
@@ -45,4 +45,4 @@ endif ()
 | 
			
		||||
 | 
			
		||||
message(STATUS "Build version: ${CLIO_VERSION}")
 | 
			
		||||
 | 
			
		||||
configure_file(${CMAKE_CURRENT_LIST_DIR}/Build.cpp.in ${CMAKE_CURRENT_LIST_DIR}/../src/main/impl/Build.cpp)
 | 
			
		||||
configure_file(${CMAKE_CURRENT_LIST_DIR}/Build.cpp.in ${CMAKE_CURRENT_LIST_DIR}/../src/util/build/Build.cpp)
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,8 @@ class Clio(ConanFile):
 | 
			
		||||
        'protobuf/3.21.9',
 | 
			
		||||
        'grpc/1.50.1',
 | 
			
		||||
        'openssl/1.1.1u',
 | 
			
		||||
        'xrpl/2.2.0',
 | 
			
		||||
        'xrpl/2.4.0-b1',
 | 
			
		||||
        'zlib/1.3.1',
 | 
			
		||||
        'libbacktrace/cci.20210118'
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								docker/ci/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								docker/ci/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
# CI image for XRPLF/clio
 | 
			
		||||
 | 
			
		||||
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 20.04 and contains:
 | 
			
		||||
- clang 16
 | 
			
		||||
- gcc 12.3
 | 
			
		||||
- doxygen 1.10
 | 
			
		||||
- gh 2.40
 | 
			
		||||
- ccache 4.8.3
 | 
			
		||||
- conan
 | 
			
		||||
- and some other useful tools
 | 
			
		||||
 | 
			
		||||
Conan is set up to build Clio without any additional steps. There are two preset conan profiles: `clang` and `gcc` to use corresponding compiler.
 | 
			
		||||
@@ -6,10 +6,10 @@ SHELL ["/bin/bash", "-c"]
 | 
			
		||||
USER root
 | 
			
		||||
WORKDIR /root
 | 
			
		||||
 | 
			
		||||
ENV CCACHE_VERSION=4.8.3 \
 | 
			
		||||
    LLVM_TOOLS_VERSION=18 \
 | 
			
		||||
ENV CCACHE_VERSION=4.10.2 \
 | 
			
		||||
    LLVM_TOOLS_VERSION=19 \
 | 
			
		||||
    GH_VERSION=2.40.0 \
 | 
			
		||||
    DOXYGEN_VERSION=1.10.0
 | 
			
		||||
    DOXYGEN_VERSION=1.12.0
 | 
			
		||||
 
 | 
			
		||||
# Add repositories
 | 
			
		||||
RUN apt-get -qq update \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								docker/clio/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								docker/clio/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
# Clio official docker image
 | 
			
		||||
 | 
			
		||||
[Clio](https://github.com/XRPLF/clio) is an XRP Ledger API server optimized for RPC calls over WebSocket or JSON-RPC.
 | 
			
		||||
It stores validated historical ledger and transaction data in a space efficient format.
 | 
			
		||||
 | 
			
		||||
This image contains `clio_server` binary allowing users to run Clio easily.
 | 
			
		||||
 | 
			
		||||
## Clio configuration file
 | 
			
		||||
 | 
			
		||||
Please note that while Clio requires a configuration file, this image doesn't include any default config.
 | 
			
		||||
Your configuration file should be mounted under the path `/opt/clio/etc/config.json`.
 | 
			
		||||
Clio repository provides an [example](https://github.com/XRPLF/clio/blob/develop/docs/examples/config/example-config.json) of the configuration file.
 | 
			
		||||
 | 
			
		||||
Config file recommendations:
 | 
			
		||||
- Set `log_to_console` to `false` if you want to avoid logs being written to `stdout`.
 | 
			
		||||
- Set `log_directory` to `/opt/clio/log` to store logs in a volume.
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
The following command can be used to run Clio in docker (assuming server's port is `51233` in your config):
 | 
			
		||||
```bash
 | 
			
		||||
docker run -d -v <path to your config.json>:/opt/clio/etc/config.json -v <path to store logs>:/opt/clio/log -p 51233:51233 rippleci/clio
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										16
									
								
								docker/clio/dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								docker/clio/dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
FROM ubuntu:22.04
 | 
			
		||||
 | 
			
		||||
COPY ./clio_server /opt/clio/bin/clio_server
 | 
			
		||||
 | 
			
		||||
RUN ln -s /opt/clio/bin/clio_server /usr/local/bin/clio_server && \
 | 
			
		||||
    mkdir -p /opt/clio/etc/ && \
 | 
			
		||||
    mkdir -p /opt/clio/log/ && \
 | 
			
		||||
    groupadd -g 10001 clio && \
 | 
			
		||||
    useradd -u 10000 -g 10001 -s /bin/bash clio && \
 | 
			
		||||
    chown clio:clio /opt/clio/log && \
 | 
			
		||||
    apt update && \
 | 
			
		||||
    apt install -y libatomic1
 | 
			
		||||
 | 
			
		||||
USER clio
 | 
			
		||||
ENTRYPOINT ["/opt/clio/bin/clio_server"]
 | 
			
		||||
CMD ["--conf", "/opt/clio/etc/config.json"]
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
version: '3.7'
 | 
			
		||||
services:
 | 
			
		||||
  clio_develop:
 | 
			
		||||
    image: rippleci/clio_ci:latest
 | 
			
		||||
 
 | 
			
		||||
@@ -141,3 +141,43 @@ If you wish to develop against a `rippled` instance running in standalone mode t
 | 
			
		||||
 | 
			
		||||
1. Advance the `rippled` ledger to at least ledger 256.
 | 
			
		||||
2. Wait 10 minutes before first starting Clio against this standalone node.
 | 
			
		||||
 | 
			
		||||
## Building with a Custom `libxrpl`
 | 
			
		||||
 | 
			
		||||
Sometimes, during development, you need to build against a custom version of `libxrpl`. (For example, you may be developing compatibility for a proposed amendment that is not yet merged to the main `rippled` codebase.) To build Clio with compatibility for a custom fork or branch of `rippled`, follow these steps:
 | 
			
		||||
 | 
			
		||||
1. First, pull/clone the appropriate `rippled` fork and switch to the branch you want to build. For example, the following example uses an in-development build with [XLS-33d Multi-Purpose Tokens](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0033d-multi-purpose-tokens):
 | 
			
		||||
 | 
			
		||||
    ```sh
 | 
			
		||||
    git clone https://github.com/shawnxie999/rippled/
 | 
			
		||||
    cd rippled
 | 
			
		||||
    git switch mpt-1.1
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
2. Export a custom package to your local Conan store using a user/channel:
 | 
			
		||||
 | 
			
		||||
    ```sh
 | 
			
		||||
    conan export . my/feature
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
3. Patch your local Clio build to use the right package.
 | 
			
		||||
 | 
			
		||||
    Edit `conanfile.py` (from the Clio repository root). Replace the `xrpl` requirement with the custom package version from the previous step. This must also include the current version number from your `rippled` branch. For example:
 | 
			
		||||
 | 
			
		||||
    ```py
 | 
			
		||||
    # ... (excerpt from conanfile.py)
 | 
			
		||||
        requires = [
 | 
			
		||||
        'boost/1.82.0',
 | 
			
		||||
        'cassandra-cpp-driver/2.17.0',
 | 
			
		||||
        'fmt/10.1.1',
 | 
			
		||||
        'protobuf/3.21.9',
 | 
			
		||||
        'grpc/1.50.1',
 | 
			
		||||
        'openssl/1.1.1u',
 | 
			
		||||
        'xrpl/2.3.0-b1@my/feature', # Update this line
 | 
			
		||||
        'libbacktrace/cci.20210118'
 | 
			
		||||
    ]
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
4. Build Clio as you would have before.
 | 
			
		||||
 | 
			
		||||
    See [Building Clio](#building-clio) for details.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This is an example configuration file. Please do not use without modifying to suit your needs.
 | 
			
		||||
 */
 | 
			
		||||
{
 | 
			
		||||
    "database": {
 | 
			
		||||
        "type": "cassandra",
 | 
			
		||||
        "cassandra": {
 | 
			
		||||
            // This option can be used to setup a secure connect bundle connection
 | 
			
		||||
            "secure_connect_bundle": "[path/to/zip. ignore if using contact_points]",
 | 
			
		||||
            // The following options are used only if using contact_points
 | 
			
		||||
            "contact_points": "[ip. ignore if using secure_connect_bundle]",
 | 
			
		||||
            "port": "[port. ignore if using_secure_connect_bundle]",
 | 
			
		||||
            // Authentication settings
 | 
			
		||||
            "username": "[username, if any]",
 | 
			
		||||
            "password": "[password, if any]",
 | 
			
		||||
            // Other common settings
 | 
			
		||||
            "keyspace": "clio",
 | 
			
		||||
            "max_write_requests_outstanding": 25000,
 | 
			
		||||
            "max_read_requests_outstanding": 30000,
 | 
			
		||||
            "threads": 8
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    "etl_sources": [
 | 
			
		||||
        {
 | 
			
		||||
            "ip": "[rippled ip]",
 | 
			
		||||
            "ws_port": "6006",
 | 
			
		||||
            "grpc_port": "50051"
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "dos_guard": {
 | 
			
		||||
        "whitelist": [
 | 
			
		||||
            "127.0.0.1"
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    "server": {
 | 
			
		||||
        "ip": "0.0.0.0",
 | 
			
		||||
        "port": 8080
 | 
			
		||||
    },
 | 
			
		||||
    "log_level": "debug",
 | 
			
		||||
    "log_file": "./clio.log",
 | 
			
		||||
    "extractor_threads": 8,
 | 
			
		||||
    "read_only": false
 | 
			
		||||
}
 | 
			
		||||
@@ -31,7 +31,7 @@
 | 
			
		||||
    "etl_sources": [
 | 
			
		||||
        {
 | 
			
		||||
            "ip": "127.0.0.1",
 | 
			
		||||
            "ws_port": "6006",
 | 
			
		||||
            "ws_port": "6005",
 | 
			
		||||
            "grpc_port": "50051"
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
@@ -39,6 +39,9 @@
 | 
			
		||||
        "cache_timeout": 0.250, // in seconds, could be 0, which means no cache
 | 
			
		||||
        "request_timeout": 10.0 // time for Clio to wait for rippled to reply on a forwarded request (default is 10 seconds)
 | 
			
		||||
    },
 | 
			
		||||
    "rpc": {
 | 
			
		||||
        "cache_timeout": 0.5 // in seconds, could be 0, which means no cache for rpc
 | 
			
		||||
    },
 | 
			
		||||
    "dos_guard": {
 | 
			
		||||
        // Comma-separated list of IPs to exclude from rate limiting
 | 
			
		||||
        "whitelist": [
 | 
			
		||||
@@ -67,7 +70,15 @@
 | 
			
		||||
        "admin_password": "xrp",
 | 
			
		||||
        // If local_admin is true, Clio will consider requests come from 127.0.0.1 as admin requests
 | 
			
		||||
        // It's true by default unless admin_password is set,'local_admin' : true and 'admin_password' can not be set at the same time
 | 
			
		||||
        "local_admin": false
 | 
			
		||||
        "local_admin": false,
 | 
			
		||||
        "processing_policy": "parallel", // Could be "sequent" or "parallel".
 | 
			
		||||
        // For sequent policy request from one client connection will be processed one by one and the next one will not be read before
 | 
			
		||||
        // the previous one is processed. For parallel policy Clio will take all requests and process them in parallel and
 | 
			
		||||
        // send a reply for each request whenever it is ready.
 | 
			
		||||
        "parallel_requests_limit": 10, // Optional parameter, used only if "processing_strategy" is "parallel". It limits the number of requests for one client connection processed in parallel. Infinite if not specified.
 | 
			
		||||
        // Max number of responses to queue up before sent successfully. If a client's waiting queue is too long, the server will close the connection.
 | 
			
		||||
        "ws_max_sending_queue_size": 1500,
 | 
			
		||||
        "__ng_web_server": false // Use ng web server. This is a temporary setting which will be deleted after switching to ng web server
 | 
			
		||||
    },
 | 
			
		||||
    // Time in seconds for graceful shutdown. Defaults to 10 seconds. Not fully implemented yet.
 | 
			
		||||
    "graceful_period": 10.0,
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ You can find an example docker-compose file, with Prometheus and Grafana configs
 | 
			
		||||
 | 
			
		||||
## Using `clang-tidy` for static analysis
 | 
			
		||||
 | 
			
		||||
The minimum [clang-tidy](https://clang.llvm.org/extra/clang-tidy/) version required is 17.0.
 | 
			
		||||
The minimum [clang-tidy](https://clang.llvm.org/extra/clang-tidy/) version required is 19.0.
 | 
			
		||||
 | 
			
		||||
Clang-tidy can be run by Cmake when building the project. To achieve this, you just need to provide the option `-o lint=True` for the `conan install` command:
 | 
			
		||||
 | 
			
		||||
@@ -26,5 +26,5 @@ By default Cmake will try to find `clang-tidy` automatically in your system.
 | 
			
		||||
To force Cmake to use your desired binary, set the `CLIO_CLANG_TIDY_BIN` environment variable to the path of the `clang-tidy` binary. For example:
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
export CLIO_CLANG_TIDY_BIN=/opt/homebrew/opt/llvm@17/bin/clang-tidy
 | 
			
		||||
export CLIO_CLANG_TIDY_BIN=/opt/homebrew/opt/llvm@19/bin/clang-tidy
 | 
			
		||||
```
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,10 @@
 | 
			
		||||
add_subdirectory(util)
 | 
			
		||||
add_subdirectory(data)
 | 
			
		||||
add_subdirectory(etl)
 | 
			
		||||
add_subdirectory(etlng)
 | 
			
		||||
add_subdirectory(feed)
 | 
			
		||||
add_subdirectory(rpc)
 | 
			
		||||
add_subdirectory(web)
 | 
			
		||||
add_subdirectory(migration)
 | 
			
		||||
add_subdirectory(app)
 | 
			
		||||
add_subdirectory(main)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								src/app/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/app/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
add_library(clio_app)
 | 
			
		||||
target_sources(clio_app PRIVATE CliArgs.cpp ClioApplication.cpp WebHandlers.cpp)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(clio_app PUBLIC clio_etl clio_etlng clio_feed clio_web clio_rpc clio_migration)
 | 
			
		||||
							
								
								
									
										82
									
								
								src/app/CliArgs.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/app/CliArgs.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2024, 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 "app/CliArgs.hpp"
 | 
			
		||||
 | 
			
		||||
#include "migration/MigrationApplication.hpp"
 | 
			
		||||
#include "util/build/Build.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/program_options/options_description.hpp>
 | 
			
		||||
#include <boost/program_options/parsers.hpp>
 | 
			
		||||
#include <boost/program_options/positional_options.hpp>
 | 
			
		||||
#include <boost/program_options/value_semantic.hpp>
 | 
			
		||||
#include <boost/program_options/variables_map.hpp>
 | 
			
		||||
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
namespace app {
 | 
			
		||||
 | 
			
		||||
CliArgs::Action
 | 
			
		||||
CliArgs::parse(int argc, char const* argv[])
 | 
			
		||||
{
 | 
			
		||||
    namespace po = boost::program_options;
 | 
			
		||||
    // clang-format off
 | 
			
		||||
    po::options_description description("Options");
 | 
			
		||||
    description.add_options()
 | 
			
		||||
        ("help,h", "print help message and exit")
 | 
			
		||||
        ("version,v", "print version and exit")
 | 
			
		||||
        ("conf,c", po::value<std::string>()->default_value(defaultConfigPath), "configuration file")
 | 
			
		||||
        ("ng-web-server,w", "Use ng-web-server")
 | 
			
		||||
        ("migrate", po::value<std::string>(), "start migration helper")
 | 
			
		||||
    ;
 | 
			
		||||
    // clang-format on
 | 
			
		||||
    po::positional_options_description positional;
 | 
			
		||||
    positional.add("conf", 1);
 | 
			
		||||
 | 
			
		||||
    po::variables_map parsed;
 | 
			
		||||
    po::store(po::command_line_parser(argc, argv).options(description).positional(positional).run(), parsed);
 | 
			
		||||
    po::notify(parsed);
 | 
			
		||||
 | 
			
		||||
    if (parsed.count("help") != 0u) {
 | 
			
		||||
        std::cout << "Clio server " << util::build::getClioFullVersionString() << "\n\n" << description;
 | 
			
		||||
        return Action{Action::Exit{EXIT_SUCCESS}};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (parsed.count("version") != 0u) {
 | 
			
		||||
        std::cout << util::build::getClioFullVersionString() << '\n';
 | 
			
		||||
        return Action{Action::Exit{EXIT_SUCCESS}};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto configPath = parsed["conf"].as<std::string>();
 | 
			
		||||
 | 
			
		||||
    if (parsed.count("migrate") != 0u) {
 | 
			
		||||
        auto const opt = parsed["migrate"].as<std::string>();
 | 
			
		||||
        if (opt == "status")
 | 
			
		||||
            return Action{Action::Migrate{.configPath = std::move(configPath), .subCmd = MigrateSubCmd::status()}};
 | 
			
		||||
        return Action{Action::Migrate{.configPath = std::move(configPath), .subCmd = MigrateSubCmd::migration(opt)}};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Action{Action::Run{.configPath = std::move(configPath), .useNgWebServer = parsed.count("ng-web-server") != 0}
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace app
 | 
			
		||||
							
								
								
									
										103
									
								
								src/app/CliArgs.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								src/app/CliArgs.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2024, 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 "migration/MigrationApplication.hpp"
 | 
			
		||||
#include "util/OverloadSet.hpp"
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <variant>
 | 
			
		||||
 | 
			
		||||
namespace app {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Parsed command line arguments representation.
 | 
			
		||||
 */
 | 
			
		||||
class CliArgs {
 | 
			
		||||
public:
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Default configuration path.
 | 
			
		||||
     */
 | 
			
		||||
    static constexpr char defaultConfigPath[] = "/etc/opt/clio/config.json";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief An action parsed from the command line.
 | 
			
		||||
     */
 | 
			
		||||
    class Action {
 | 
			
		||||
    public:
 | 
			
		||||
        /** @brief Run action. */
 | 
			
		||||
        struct Run {
 | 
			
		||||
            std::string configPath;  ///< Configuration file path.
 | 
			
		||||
            bool useNgWebServer;     ///< Whether to use a ng web server
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /** @brief Exit action. */
 | 
			
		||||
        struct Exit {
 | 
			
		||||
            int exitCode;  ///< Exit code.
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /** @brief Migration action. */
 | 
			
		||||
        struct Migrate {
 | 
			
		||||
            std::string configPath;
 | 
			
		||||
            MigrateSubCmd subCmd;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @brief Construct an action from a Run.
 | 
			
		||||
         *
 | 
			
		||||
         * @param action Run action.
 | 
			
		||||
         */
 | 
			
		||||
        template <typename ActionType>
 | 
			
		||||
            requires std::is_same_v<ActionType, Run> or std::is_same_v<ActionType, Exit> or
 | 
			
		||||
            std::is_same_v<ActionType, Migrate>
 | 
			
		||||
        explicit Action(ActionType&& action) : action_(std::forward<ActionType>(action))
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @brief Apply a function to the action.
 | 
			
		||||
         *
 | 
			
		||||
         * @tparam Processors Action processors types. Must be callable with the action type and return int.
 | 
			
		||||
         * @param processors Action processors.
 | 
			
		||||
         * @return Exit code.
 | 
			
		||||
         */
 | 
			
		||||
        template <typename... Processors>
 | 
			
		||||
        int
 | 
			
		||||
        apply(Processors&&... processors) const
 | 
			
		||||
        {
 | 
			
		||||
            return std::visit(util::OverloadSet{std::forward<Processors>(processors)...}, action_);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        std::variant<Run, Exit, Migrate> action_;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Parse command line arguments.
 | 
			
		||||
     *
 | 
			
		||||
     * @param argc Number of arguments.
 | 
			
		||||
     * @param argv Array of arguments.
 | 
			
		||||
     * @return Parsed command line arguments.
 | 
			
		||||
     */
 | 
			
		||||
    static Action
 | 
			
		||||
    parse(int argc, char const* argv[]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace app
 | 
			
		||||
							
								
								
									
										183
									
								
								src/app/ClioApplication.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								src/app/ClioApplication.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,183 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2024, 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 "app/ClioApplication.hpp"
 | 
			
		||||
 | 
			
		||||
#include "app/WebHandlers.hpp"
 | 
			
		||||
#include "data/AmendmentCenter.hpp"
 | 
			
		||||
#include "data/BackendFactory.hpp"
 | 
			
		||||
#include "etl/ETLService.hpp"
 | 
			
		||||
#include "etl/LoadBalancer.hpp"
 | 
			
		||||
#include "etl/NetworkValidatedLedgers.hpp"
 | 
			
		||||
#include "feed/SubscriptionManager.hpp"
 | 
			
		||||
#include "rpc/Counters.hpp"
 | 
			
		||||
#include "rpc/RPCEngine.hpp"
 | 
			
		||||
#include "rpc/WorkQueue.hpp"
 | 
			
		||||
#include "rpc/common/impl/HandlerProvider.hpp"
 | 
			
		||||
#include "util/build/Build.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
#include "util/newconfig/ConfigDefinition.hpp"
 | 
			
		||||
#include "util/prometheus/Prometheus.hpp"
 | 
			
		||||
#include "web/AdminVerificationStrategy.hpp"
 | 
			
		||||
#include "web/RPCServerHandler.hpp"
 | 
			
		||||
#include "web/Server.hpp"
 | 
			
		||||
#include "web/dosguard/DOSGuard.hpp"
 | 
			
		||||
#include "web/dosguard/IntervalSweepHandler.hpp"
 | 
			
		||||
#include "web/dosguard/WhitelistHandler.hpp"
 | 
			
		||||
#include "web/ng/RPCServerHandler.hpp"
 | 
			
		||||
#include "web/ng/Server.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/io_context.hpp>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace app {
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Start context threads
 | 
			
		||||
 *
 | 
			
		||||
 * @param ioc Context
 | 
			
		||||
 * @param numThreads Number of worker threads to start
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
start(boost::asio::io_context& ioc, std::uint32_t numThreads)
 | 
			
		||||
{
 | 
			
		||||
    std::vector<std::thread> v;
 | 
			
		||||
    v.reserve(numThreads - 1);
 | 
			
		||||
    for (auto i = numThreads - 1; i > 0; --i)
 | 
			
		||||
        v.emplace_back([&ioc] { ioc.run(); });
 | 
			
		||||
 | 
			
		||||
    ioc.run();
 | 
			
		||||
    for (auto& t : v)
 | 
			
		||||
        t.join();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
 | 
			
		||||
ClioApplication::ClioApplication(util::config::ClioConfigDefinition const& config)
 | 
			
		||||
    : config_(config), signalsHandler_{config_}
 | 
			
		||||
{
 | 
			
		||||
    LOG(util::LogService::info()) << "Clio version: " << util::build::getClioFullVersionString();
 | 
			
		||||
    PrometheusService::init(config);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
ClioApplication::run(bool const useNgWebServer)
 | 
			
		||||
{
 | 
			
		||||
    auto const threads = config_.get<uint16_t>("io_threads");
 | 
			
		||||
    LOG(util::LogService::info()) << "Number of io threads = " << 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};
 | 
			
		||||
 | 
			
		||||
    // Rate limiter, to prevent abuse
 | 
			
		||||
    auto whitelistHandler = web::dosguard::WhitelistHandler{config_};
 | 
			
		||||
    auto dosGuard = web::dosguard::DOSGuard{config_, whitelistHandler};
 | 
			
		||||
    auto sweepHandler = web::dosguard::IntervalSweepHandler{config_, ioc, dosGuard};
 | 
			
		||||
 | 
			
		||||
    // Interface to the database
 | 
			
		||||
    auto backend = data::make_Backend(config_);
 | 
			
		||||
 | 
			
		||||
    // Manages clients subscribed to streams
 | 
			
		||||
    auto subscriptions = feed::SubscriptionManager::make_SubscriptionManager(config_, backend);
 | 
			
		||||
 | 
			
		||||
    // Tracks which ledgers have been validated by the network
 | 
			
		||||
    auto ledgers = etl::NetworkValidatedLedgers::make_ValidatedLedgers();
 | 
			
		||||
 | 
			
		||||
    // Handles the connection to one or more rippled nodes.
 | 
			
		||||
    // 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::make_LoadBalancer(config_, ioc, backend, subscriptions, ledgers);
 | 
			
		||||
 | 
			
		||||
    // ETL is responsible for writing and publishing to streams. In read-only mode, ETL only publishes
 | 
			
		||||
    auto etl = etl::ETLService::make_ETLService(config_, ioc, backend, subscriptions, balancer, ledgers);
 | 
			
		||||
 | 
			
		||||
    auto workQueue = rpc::WorkQueue::make_WorkQueue(config_);
 | 
			
		||||
    auto counters = rpc::Counters::make_Counters(workQueue);
 | 
			
		||||
    auto const amendmentCenter = std::make_shared<data::AmendmentCenter const>(backend);
 | 
			
		||||
    auto const handlerProvider = std::make_shared<rpc::impl::ProductionHandlerProvider const>(
 | 
			
		||||
        config_, backend, subscriptions, balancer, etl, amendmentCenter, counters
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    using RPCEngineType = rpc::RPCEngine<etl::LoadBalancer, rpc::Counters>;
 | 
			
		||||
    auto const rpcEngine =
 | 
			
		||||
        RPCEngineType::make_RPCEngine(config_, backend, balancer, dosGuard, workQueue, counters, handlerProvider);
 | 
			
		||||
 | 
			
		||||
    if (useNgWebServer or config_.get<bool>("server.__ng_web_server")) {
 | 
			
		||||
        web::ng::RPCServerHandler<RPCEngineType, etl::ETLService> handler{config_, backend, rpcEngine, etl};
 | 
			
		||||
 | 
			
		||||
        auto expectedAdminVerifier = web::make_AdminVerificationStrategy(config_);
 | 
			
		||||
        if (not expectedAdminVerifier.has_value()) {
 | 
			
		||||
            LOG(util::LogService::error()) << "Error creating admin verifier: " << expectedAdminVerifier.error();
 | 
			
		||||
            return EXIT_FAILURE;
 | 
			
		||||
        }
 | 
			
		||||
        auto const adminVerifier = std::move(expectedAdminVerifier).value();
 | 
			
		||||
 | 
			
		||||
        auto httpServer = web::ng::make_Server(config_, OnConnectCheck{dosGuard}, DisconnectHook{dosGuard}, ioc);
 | 
			
		||||
 | 
			
		||||
        if (not httpServer.has_value()) {
 | 
			
		||||
            LOG(util::LogService::error()) << "Error creating web server: " << httpServer.error();
 | 
			
		||||
            return EXIT_FAILURE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        httpServer->onGet("/metrics", MetricsHandler{adminVerifier});
 | 
			
		||||
        httpServer->onGet("/health", HealthCheckHandler{});
 | 
			
		||||
        auto requestHandler = RequestHandler{adminVerifier, handler, dosGuard};
 | 
			
		||||
        httpServer->onPost("/", requestHandler);
 | 
			
		||||
        httpServer->onWs(std::move(requestHandler));
 | 
			
		||||
 | 
			
		||||
        auto const maybeError = httpServer->run();
 | 
			
		||||
        if (maybeError.has_value()) {
 | 
			
		||||
            LOG(util::LogService::error()) << "Error starting web server: " << *maybeError;
 | 
			
		||||
            return EXIT_FAILURE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Blocks until stopped.
 | 
			
		||||
        // When stopped, shared_ptrs fall out of scope
 | 
			
		||||
        // Calls destructors on all resources, and destructs in order
 | 
			
		||||
        start(ioc, threads);
 | 
			
		||||
 | 
			
		||||
        return EXIT_SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Init the web server
 | 
			
		||||
    auto handler =
 | 
			
		||||
        std::make_shared<web::RPCServerHandler<RPCEngineType, etl::ETLService>>(config_, backend, rpcEngine, etl);
 | 
			
		||||
 | 
			
		||||
    auto const httpServer = web::make_HttpServer(config_, ioc, dosGuard, handler);
 | 
			
		||||
 | 
			
		||||
    // Blocks until stopped.
 | 
			
		||||
    // When stopped, shared_ptrs fall out of scope
 | 
			
		||||
    // Calls destructors on all resources, and destructs in order
 | 
			
		||||
    start(ioc, threads);
 | 
			
		||||
 | 
			
		||||
    return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace app
 | 
			
		||||
							
								
								
									
										52
									
								
								src/app/ClioApplication.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/app/ClioApplication.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2024, 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 "util/SignalsHandler.hpp"
 | 
			
		||||
#include "util/newconfig/ConfigDefinition.hpp"
 | 
			
		||||
 | 
			
		||||
namespace app {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief The main application class
 | 
			
		||||
 */
 | 
			
		||||
class ClioApplication {
 | 
			
		||||
    util::config::ClioConfigDefinition const& config_;
 | 
			
		||||
    util::SignalsHandler signalsHandler_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Construct a new ClioApplication object
 | 
			
		||||
     *
 | 
			
		||||
     * @param config The configuration of the application
 | 
			
		||||
     */
 | 
			
		||||
    ClioApplication(util::config::ClioConfigDefinition const& config);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Run the application
 | 
			
		||||
     *
 | 
			
		||||
     * @param useNgWebServer Whether to use the new web server
 | 
			
		||||
     *
 | 
			
		||||
     * @return exit code
 | 
			
		||||
     */
 | 
			
		||||
    int
 | 
			
		||||
    run(bool useNgWebServer);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace app
 | 
			
		||||
							
								
								
									
										111
									
								
								src/app/WebHandlers.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/app/WebHandlers.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,111 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2024, 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 "app/WebHandlers.hpp"
 | 
			
		||||
 | 
			
		||||
#include "util/Assert.hpp"
 | 
			
		||||
#include "util/prometheus/Http.hpp"
 | 
			
		||||
#include "web/AdminVerificationStrategy.hpp"
 | 
			
		||||
#include "web/SubscriptionContextInterface.hpp"
 | 
			
		||||
#include "web/dosguard/DOSGuardInterface.hpp"
 | 
			
		||||
#include "web/ng/Connection.hpp"
 | 
			
		||||
#include "web/ng/Request.hpp"
 | 
			
		||||
#include "web/ng/Response.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
#include <boost/beast/http/status.hpp>
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
namespace app {
 | 
			
		||||
 | 
			
		||||
OnConnectCheck::OnConnectCheck(web::dosguard::DOSGuardInterface& dosguard) : dosguard_{dosguard}
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::expected<void, web::ng::Response>
 | 
			
		||||
OnConnectCheck::operator()(web::ng::Connection const& connection)
 | 
			
		||||
{
 | 
			
		||||
    dosguard_.get().increment(connection.ip());
 | 
			
		||||
    if (not dosguard_.get().isOk(connection.ip())) {
 | 
			
		||||
        return std::unexpected{
 | 
			
		||||
            web::ng::Response{boost::beast::http::status::too_many_requests, "Too many requests", connection}
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DisconnectHook::DisconnectHook(web::dosguard::DOSGuardInterface& dosguard) : dosguard_{dosguard}
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
DisconnectHook::operator()(web::ng::Connection const& connection)
 | 
			
		||||
{
 | 
			
		||||
    dosguard_.get().decrement(connection.ip());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MetricsHandler::MetricsHandler(std::shared_ptr<web::AdminVerificationStrategy> adminVerifier)
 | 
			
		||||
    : adminVerifier_{std::move(adminVerifier)}
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
web::ng::Response
 | 
			
		||||
MetricsHandler::operator()(
 | 
			
		||||
    web::ng::Request const& request,
 | 
			
		||||
    web::ng::ConnectionMetadata& connectionMetadata,
 | 
			
		||||
    web::SubscriptionContextPtr,
 | 
			
		||||
    boost::asio::yield_context
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
    auto const maybeHttpRequest = request.asHttpRequest();
 | 
			
		||||
    ASSERT(maybeHttpRequest.has_value(), "Got not a http request in Get");
 | 
			
		||||
    auto const& httpRequest = maybeHttpRequest->get();
 | 
			
		||||
 | 
			
		||||
    // 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())
 | 
			
		||||
    );
 | 
			
		||||
    ASSERT(maybeResponse.has_value(), "Got unexpected request for Prometheus");
 | 
			
		||||
    return web::ng::Response{std::move(maybeResponse).value(), request};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
web::ng::Response
 | 
			
		||||
HealthCheckHandler::operator()(
 | 
			
		||||
    web::ng::Request const& request,
 | 
			
		||||
    web::ng::ConnectionMetadata&,
 | 
			
		||||
    web::SubscriptionContextPtr,
 | 
			
		||||
    boost::asio::yield_context
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
    static auto constexpr HealthCheckHTML = R"html(
 | 
			
		||||
    <!DOCTYPE html>
 | 
			
		||||
    <html>
 | 
			
		||||
        <head><title>Test page for Clio</title></head>
 | 
			
		||||
        <body><h1>Clio Test</h1><p>This page shows Clio http(s) connectivity is working.</p></body>
 | 
			
		||||
    </html>
 | 
			
		||||
)html";
 | 
			
		||||
 | 
			
		||||
    return web::ng::Response{boost::beast::http::status::ok, HealthCheckHTML, request};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace app
 | 
			
		||||
							
								
								
									
										234
									
								
								src/app/WebHandlers.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								src/app/WebHandlers.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,234 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2024, 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 "rpc/Errors.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
#include "web/AdminVerificationStrategy.hpp"
 | 
			
		||||
#include "web/SubscriptionContextInterface.hpp"
 | 
			
		||||
#include "web/dosguard/DOSGuardInterface.hpp"
 | 
			
		||||
#include "web/ng/Connection.hpp"
 | 
			
		||||
#include "web/ng/Request.hpp"
 | 
			
		||||
#include "web/ng/Response.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
#include <boost/beast/http/status.hpp>
 | 
			
		||||
#include <boost/json/array.hpp>
 | 
			
		||||
#include <boost/json/parse.hpp>
 | 
			
		||||
 | 
			
		||||
#include <exception>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
namespace app {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief A function object that checks if the connection is allowed to proceed.
 | 
			
		||||
 */
 | 
			
		||||
class OnConnectCheck {
 | 
			
		||||
    std::reference_wrapper<web::dosguard::DOSGuardInterface> dosguard_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Construct a new OnConnectCheck object
 | 
			
		||||
     *
 | 
			
		||||
     * @param dosguard The DOSGuardInterface to use for checking the connection.
 | 
			
		||||
     */
 | 
			
		||||
    OnConnectCheck(web::dosguard::DOSGuardInterface& dosguard);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Check if the connection is allowed to proceed.
 | 
			
		||||
     *
 | 
			
		||||
     * @param connection The connection to check.
 | 
			
		||||
     * @return A response if the connection is not allowed to proceed or void otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    std::expected<void, web::ng::Response>
 | 
			
		||||
    operator()(web::ng::Connection const& connection);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief A function object to be called when a connection is disconnected.
 | 
			
		||||
 */
 | 
			
		||||
class DisconnectHook {
 | 
			
		||||
    std::reference_wrapper<web::dosguard::DOSGuardInterface> dosguard_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Construct a new DisconnectHook object
 | 
			
		||||
     *
 | 
			
		||||
     * @param dosguard The DOSGuardInterface to use for disconnecting the connection.
 | 
			
		||||
     */
 | 
			
		||||
    DisconnectHook(web::dosguard::DOSGuardInterface& dosguard);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief The call of the function object.
 | 
			
		||||
     *
 | 
			
		||||
     * @param connection The connection which has disconnected.
 | 
			
		||||
     */
 | 
			
		||||
    void
 | 
			
		||||
    operator()(web::ng::Connection const& connection);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief A function object that handles the metrics endpoint.
 | 
			
		||||
 */
 | 
			
		||||
class MetricsHandler {
 | 
			
		||||
    std::shared_ptr<web::AdminVerificationStrategy> adminVerifier_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Construct a new MetricsHandler object
 | 
			
		||||
     *
 | 
			
		||||
     * @param adminVerifier The AdminVerificationStrategy to use for verifying the connection for admin access.
 | 
			
		||||
     */
 | 
			
		||||
    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.
 | 
			
		||||
     * @return The response to the request.
 | 
			
		||||
     */
 | 
			
		||||
    web::ng::Response
 | 
			
		||||
    operator()(
 | 
			
		||||
        web::ng::Request const& request,
 | 
			
		||||
        web::ng::ConnectionMetadata& connectionMetadata,
 | 
			
		||||
        web::SubscriptionContextPtr,
 | 
			
		||||
        boost::asio::yield_context
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief A function object that handles the health check endpoint.
 | 
			
		||||
 */
 | 
			
		||||
class HealthCheckHandler {
 | 
			
		||||
public:
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief The call of the function object.
 | 
			
		||||
     *
 | 
			
		||||
     * @param request The request to handle.
 | 
			
		||||
     * @return The response to the request
 | 
			
		||||
     */
 | 
			
		||||
    web::ng::Response
 | 
			
		||||
    operator()(
 | 
			
		||||
        web::ng::Request const& request,
 | 
			
		||||
        web::ng::ConnectionMetadata&,
 | 
			
		||||
        web::SubscriptionContextPtr,
 | 
			
		||||
        boost::asio::yield_context
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief A function object that handles the websocket endpoint.
 | 
			
		||||
 *
 | 
			
		||||
 * @tparam RpcHandlerType The type of the RPC handler.
 | 
			
		||||
 */
 | 
			
		||||
template <typename RpcHandlerType>
 | 
			
		||||
class RequestHandler {
 | 
			
		||||
    util::Logger webServerLog_{"WebServer"};
 | 
			
		||||
    std::shared_ptr<web::AdminVerificationStrategy> adminVerifier_;
 | 
			
		||||
    std::reference_wrapper<RpcHandlerType> rpcHandler_;
 | 
			
		||||
    std::reference_wrapper<web::dosguard::DOSGuardInterface> dosguard_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Construct a new RequestHandler object
 | 
			
		||||
     *
 | 
			
		||||
     * @param adminVerifier The AdminVerificationStrategy to use for verifying the connection for admin access.
 | 
			
		||||
     * @param rpcHandler The RPC handler to use for handling the request.
 | 
			
		||||
     * @param dosguard The DOSGuardInterface to use for checking the connection.
 | 
			
		||||
     */
 | 
			
		||||
    RequestHandler(
 | 
			
		||||
        std::shared_ptr<web::AdminVerificationStrategy> adminVerifier,
 | 
			
		||||
        RpcHandlerType& rpcHandler,
 | 
			
		||||
        web::dosguard::DOSGuardInterface& dosguard
 | 
			
		||||
    )
 | 
			
		||||
        : adminVerifier_(std::move(adminVerifier)), rpcHandler_(rpcHandler), dosguard_(dosguard)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief The call of the function object.
 | 
			
		||||
     *
 | 
			
		||||
     * @param request The request to handle.
 | 
			
		||||
     * @param connectionMetadata The connection metadata.
 | 
			
		||||
     * @param subscriptionContext The subscription context.
 | 
			
		||||
     * @param yield The yield context.
 | 
			
		||||
     * @return The response to the request.
 | 
			
		||||
     */
 | 
			
		||||
    web::ng::Response
 | 
			
		||||
    operator()(
 | 
			
		||||
        web::ng::Request const& request,
 | 
			
		||||
        web::ng::ConnectionMetadata& connectionMetadata,
 | 
			
		||||
        web::SubscriptionContextPtr subscriptionContext,
 | 
			
		||||
        boost::asio::yield_context yield
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
        if (not dosguard_.get().request(connectionMetadata.ip())) {
 | 
			
		||||
            auto error = rpc::makeError(rpc::RippledError::rpcSLOW_DOWN);
 | 
			
		||||
 | 
			
		||||
            if (not request.isHttp()) {
 | 
			
		||||
                try {
 | 
			
		||||
                    auto requestJson = boost::json::parse(request.message());
 | 
			
		||||
                    if (requestJson.is_object() && requestJson.as_object().contains("id"))
 | 
			
		||||
                        error["id"] = requestJson.as_object().at("id");
 | 
			
		||||
                    error["request"] = request.message();
 | 
			
		||||
                } catch (std::exception const&) {
 | 
			
		||||
                    error["request"] = request.message();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return web::ng::Response{boost::beast::http::status::service_unavailable, error, request};
 | 
			
		||||
        }
 | 
			
		||||
        LOG(webServerLog_.info()) << connectionMetadata.tag()
 | 
			
		||||
                                  << "Received request from ip = " << connectionMetadata.ip()
 | 
			
		||||
                                  << " - posting to WorkQueue";
 | 
			
		||||
 | 
			
		||||
        connectionMetadata.setIsAdmin([this, &request, &connectionMetadata]() {
 | 
			
		||||
            return adminVerifier_->isAdmin(request.httpHeaders(), connectionMetadata.ip());
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            auto response = rpcHandler_(request, connectionMetadata, std::move(subscriptionContext), yield);
 | 
			
		||||
 | 
			
		||||
            if (not dosguard_.get().add(connectionMetadata.ip(), response.message().size())) {
 | 
			
		||||
                auto jsonResponse = boost::json::parse(response.message()).as_object();
 | 
			
		||||
                jsonResponse["warning"] = "load";
 | 
			
		||||
                if (jsonResponse.contains("warnings") && jsonResponse["warnings"].is_array()) {
 | 
			
		||||
                    jsonResponse["warnings"].as_array().push_back(rpc::makeWarning(rpc::warnRPC_RATE_LIMIT));
 | 
			
		||||
                } else {
 | 
			
		||||
                    jsonResponse["warnings"] = boost::json::array{rpc::makeWarning(rpc::warnRPC_RATE_LIMIT)};
 | 
			
		||||
                }
 | 
			
		||||
                response.setMessage(jsonResponse);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return response;
 | 
			
		||||
        } catch (std::exception const&) {
 | 
			
		||||
            return web::ng::Response{
 | 
			
		||||
                boost::beast::http::status::internal_server_error,
 | 
			
		||||
                rpc::makeError(rpc::RippledError::rpcINTERNAL),
 | 
			
		||||
                request
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace app
 | 
			
		||||
							
								
								
									
										203
									
								
								src/data/AmendmentCenter.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								src/data/AmendmentCenter.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,203 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2024, 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/AmendmentCenter.hpp"
 | 
			
		||||
 | 
			
		||||
#include "data/BackendInterface.hpp"
 | 
			
		||||
#include "data/Types.hpp"
 | 
			
		||||
#include "util/Assert.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
#include <xrpl/basics/Slice.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/protocol/Feature.h>
 | 
			
		||||
#include <xrpl/protocol/Indexes.h>
 | 
			
		||||
#include <xrpl/protocol/SField.h>
 | 
			
		||||
#include <xrpl/protocol/STLedgerEntry.h>
 | 
			
		||||
#include <xrpl/protocol/Serializer.h>
 | 
			
		||||
#include <xrpl/protocol/digest.h>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <iterator>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <ranges>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <unordered_set>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
std::unordered_set<std::string>&
 | 
			
		||||
SUPPORTED_AMENDMENTS()
 | 
			
		||||
{
 | 
			
		||||
    static std::unordered_set<std::string> amendments = {};
 | 
			
		||||
    return amendments;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
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))
 | 
			
		||||
        return rg::find(ledgerAmendments, am->feature) != rg::end(ledgerAmendments);
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
 | 
			
		||||
namespace data {
 | 
			
		||||
namespace impl {
 | 
			
		||||
 | 
			
		||||
WritingAmendmentKey::WritingAmendmentKey(std::string amendmentName) : AmendmentKey{std::move(amendmentName)}
 | 
			
		||||
{
 | 
			
		||||
    ASSERT(not SUPPORTED_AMENDMENTS().contains(name), "Attempt to register the same amendment twice");
 | 
			
		||||
    SUPPORTED_AMENDMENTS().insert(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace impl
 | 
			
		||||
 | 
			
		||||
AmendmentKey::operator std::string const&() const
 | 
			
		||||
{
 | 
			
		||||
    return name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AmendmentKey::operator std::string_view() const
 | 
			
		||||
{
 | 
			
		||||
    return name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AmendmentKey::operator ripple::uint256() const
 | 
			
		||||
{
 | 
			
		||||
    return Amendment::GetAmendmentId(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AmendmentCenter::AmendmentCenter(std::shared_ptr<data::BackendInterface> const& backend) : backend_{backend}
 | 
			
		||||
{
 | 
			
		||||
    namespace rg = std::ranges;
 | 
			
		||||
    namespace vs = std::views;
 | 
			
		||||
 | 
			
		||||
    rg::copy(
 | 
			
		||||
        ripple::allAmendments() | vs::transform([&](auto const& p) {
 | 
			
		||||
            auto const& [name, support] = p;
 | 
			
		||||
            return Amendment{
 | 
			
		||||
                .name = name,
 | 
			
		||||
                .feature = Amendment::GetAmendmentId(name),
 | 
			
		||||
                .isSupportedByXRPL = support != ripple::AmendmentSupport::Unsupported,
 | 
			
		||||
                .isSupportedByClio = rg::find(SUPPORTED_AMENDMENTS(), name) != rg::end(SUPPORTED_AMENDMENTS()),
 | 
			
		||||
                .isRetired = support == ripple::AmendmentSupport::Retired
 | 
			
		||||
            };
 | 
			
		||||
        }),
 | 
			
		||||
        std::back_inserter(all_)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    for (auto const& am : all_ | vs::filter([](auto const& am) { return am.isSupportedByClio; }))
 | 
			
		||||
        supported_.insert_or_assign(am.name, am);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
AmendmentCenter::isSupported(AmendmentKey const& key) const
 | 
			
		||||
{
 | 
			
		||||
    return supported_.contains(key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::map<std::string, Amendment> const&
 | 
			
		||||
AmendmentCenter::getSupported() const
 | 
			
		||||
{
 | 
			
		||||
    return supported_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<Amendment> const&
 | 
			
		||||
AmendmentCenter::getAll() const
 | 
			
		||||
{
 | 
			
		||||
    return all_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
AmendmentCenter::isEnabled(AmendmentKey const& key, uint32_t seq) const
 | 
			
		||||
{
 | 
			
		||||
    return data::synchronous([this, &key, seq](auto yield) { return isEnabled(yield, key, seq); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
AmendmentCenter::isEnabled(boost::asio::yield_context yield, AmendmentKey const& key, uint32_t seq) const
 | 
			
		||||
{
 | 
			
		||||
    if (auto const listAmendments = fetchAmendmentsList(yield, seq); listAmendments)
 | 
			
		||||
        return lookupAmendment(all_, *listAmendments, key);
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<bool>
 | 
			
		||||
AmendmentCenter::isEnabled(boost::asio::yield_context yield, std::vector<AmendmentKey> const& keys, uint32_t seq) const
 | 
			
		||||
{
 | 
			
		||||
    namespace rg = std::ranges;
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return std::vector<bool>(keys.size(), false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Amendment const&
 | 
			
		||||
AmendmentCenter::getAmendment(AmendmentKey const& key) const
 | 
			
		||||
{
 | 
			
		||||
    ASSERT(supported_.contains(key), "The amendment '{}' must be present in supported amendments list", key.name);
 | 
			
		||||
    return supported_.at(key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Amendment const&
 | 
			
		||||
AmendmentCenter::operator[](AmendmentKey const& key) const
 | 
			
		||||
{
 | 
			
		||||
    return getAmendment(key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ripple::uint256
 | 
			
		||||
Amendment::GetAmendmentId(std::string_view name)
 | 
			
		||||
{
 | 
			
		||||
    return ripple::sha512Half(ripple::Slice(name.data(), name.size()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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(ripple::keylet::amendments().key, seq, yield);
 | 
			
		||||
    if (not amendments.has_value())
 | 
			
		||||
        throw std::runtime_error("Amendments ledger object must be present in the database");
 | 
			
		||||
 | 
			
		||||
    ripple::SLE const amendmentsSLE{
 | 
			
		||||
        ripple::SerialIter{amendments->data(), amendments->size()}, ripple::keylet::amendments().key
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return amendmentsSLE[~ripple::sfAmendments];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace data
 | 
			
		||||
							
								
								
									
										259
									
								
								src/data/AmendmentCenter.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								src/data/AmendmentCenter.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,259 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2024, 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/AmendmentCenterInterface.hpp"
 | 
			
		||||
#include "data/BackendInterface.hpp"
 | 
			
		||||
#include "data/Types.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
#include <boost/preprocessor.hpp>
 | 
			
		||||
#include <boost/preprocessor/seq/for_each.hpp>
 | 
			
		||||
#include <boost/preprocessor/stringize.hpp>
 | 
			
		||||
#include <boost/preprocessor/variadic/to_seq.hpp>
 | 
			
		||||
#include <xrpl/basics/Slice.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/protocol/Feature.h>
 | 
			
		||||
#include <xrpl/protocol/Indexes.h>
 | 
			
		||||
#include <xrpl/protocol/SField.h>
 | 
			
		||||
#include <xrpl/protocol/STLedgerEntry.h>
 | 
			
		||||
#include <xrpl/protocol/Serializer.h>
 | 
			
		||||
#include <xrpl/protocol/digest.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#define REGISTER(name)                                   \
 | 
			
		||||
    inline static impl::WritingAmendmentKey const name = \
 | 
			
		||||
        impl::WritingAmendmentKey(std::string(BOOST_PP_STRINGIZE(name)))
 | 
			
		||||
 | 
			
		||||
namespace data {
 | 
			
		||||
namespace impl {
 | 
			
		||||
 | 
			
		||||
struct WritingAmendmentKey : AmendmentKey {
 | 
			
		||||
    explicit WritingAmendmentKey(std::string amendmentName);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace impl
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief List of supported amendments
 | 
			
		||||
 */
 | 
			
		||||
struct Amendments {
 | 
			
		||||
    // NOTE: if Clio wants to report it supports an Amendment it should be listed here.
 | 
			
		||||
    // Whether an amendment is obsolete and/or supported by libxrpl is extracted directly from libxrpl.
 | 
			
		||||
    // If an amendment is in the list below it just means Clio did whatever changes needed to support it.
 | 
			
		||||
    // Most of the time it's going to be no changes at all.
 | 
			
		||||
 | 
			
		||||
    /** @cond */
 | 
			
		||||
    REGISTER(OwnerPaysFee);
 | 
			
		||||
    REGISTER(Flow);
 | 
			
		||||
    REGISTER(FlowCross);
 | 
			
		||||
    REGISTER(fix1513);
 | 
			
		||||
    REGISTER(DepositAuth);
 | 
			
		||||
    REGISTER(Checks);
 | 
			
		||||
    REGISTER(fix1571);
 | 
			
		||||
    REGISTER(fix1543);
 | 
			
		||||
    REGISTER(fix1623);
 | 
			
		||||
    REGISTER(DepositPreauth);
 | 
			
		||||
    REGISTER(fix1515);
 | 
			
		||||
    REGISTER(fix1578);
 | 
			
		||||
    REGISTER(MultiSignReserve);
 | 
			
		||||
    REGISTER(fixTakerDryOfferRemoval);
 | 
			
		||||
    REGISTER(fixMasterKeyAsRegularKey);
 | 
			
		||||
    REGISTER(fixCheckThreading);
 | 
			
		||||
    REGISTER(fixPayChanRecipientOwnerDir);
 | 
			
		||||
    REGISTER(DeletableAccounts);
 | 
			
		||||
    REGISTER(fixQualityUpperBound);
 | 
			
		||||
    REGISTER(RequireFullyCanonicalSig);
 | 
			
		||||
    REGISTER(fix1781);
 | 
			
		||||
    REGISTER(HardenedValidations);
 | 
			
		||||
    REGISTER(fixAmendmentMajorityCalc);
 | 
			
		||||
    REGISTER(NegativeUNL);
 | 
			
		||||
    REGISTER(TicketBatch);
 | 
			
		||||
    REGISTER(FlowSortStrands);
 | 
			
		||||
    REGISTER(fixSTAmountCanonicalize);
 | 
			
		||||
    REGISTER(fixRmSmallIncreasedQOffers);
 | 
			
		||||
    REGISTER(CheckCashMakesTrustLine);
 | 
			
		||||
    REGISTER(ExpandedSignerList);
 | 
			
		||||
    REGISTER(NonFungibleTokensV1_1);
 | 
			
		||||
    REGISTER(fixTrustLinesToSelf);
 | 
			
		||||
    REGISTER(fixRemoveNFTokenAutoTrustLine);
 | 
			
		||||
    REGISTER(ImmediateOfferKilled);
 | 
			
		||||
    REGISTER(DisallowIncoming);
 | 
			
		||||
    REGISTER(XRPFees);
 | 
			
		||||
    REGISTER(fixUniversalNumber);
 | 
			
		||||
    REGISTER(fixNonFungibleTokensV1_2);
 | 
			
		||||
    REGISTER(fixNFTokenRemint);
 | 
			
		||||
    REGISTER(fixReducedOffersV1);
 | 
			
		||||
    REGISTER(Clawback);
 | 
			
		||||
    REGISTER(AMM);
 | 
			
		||||
    REGISTER(XChainBridge);
 | 
			
		||||
    REGISTER(fixDisallowIncomingV1);
 | 
			
		||||
    REGISTER(DID);
 | 
			
		||||
    REGISTER(fixFillOrKill);
 | 
			
		||||
    REGISTER(fixNFTokenReserve);
 | 
			
		||||
    REGISTER(fixInnerObjTemplate);
 | 
			
		||||
    REGISTER(fixAMMOverflowOffer);
 | 
			
		||||
    REGISTER(PriceOracle);
 | 
			
		||||
    REGISTER(fixEmptyDID);
 | 
			
		||||
    REGISTER(fixXChainRewardRounding);
 | 
			
		||||
    REGISTER(fixPreviousTxnID);
 | 
			
		||||
    REGISTER(fixAMMv1_1);
 | 
			
		||||
    REGISTER(NFTokenMintOffer);
 | 
			
		||||
    REGISTER(fixReducedOffersV2);
 | 
			
		||||
    REGISTER(fixEnforceNFTokenTrustline);
 | 
			
		||||
    REGISTER(fixInnerObjTemplate2);
 | 
			
		||||
    REGISTER(fixNFTokenPageLinks);
 | 
			
		||||
    REGISTER(InvariantsV1_1);
 | 
			
		||||
    REGISTER(MPTokensV1);
 | 
			
		||||
    REGISTER(fixAMMv1_2);
 | 
			
		||||
    REGISTER(AMMClawback);
 | 
			
		||||
    REGISTER(Credentials);
 | 
			
		||||
 | 
			
		||||
    // Obsolete but supported by libxrpl
 | 
			
		||||
    REGISTER(CryptoConditionsSuite);
 | 
			
		||||
    REGISTER(NonFungibleTokensV1);
 | 
			
		||||
    REGISTER(fixNFTokenDirV1);
 | 
			
		||||
    REGISTER(fixNFTokenNegOffer);
 | 
			
		||||
 | 
			
		||||
    // Retired amendments
 | 
			
		||||
    REGISTER(MultiSign);
 | 
			
		||||
    REGISTER(TrustSetAuth);
 | 
			
		||||
    REGISTER(FeeEscalation);
 | 
			
		||||
    REGISTER(PayChan);
 | 
			
		||||
    REGISTER(fix1368);
 | 
			
		||||
    REGISTER(CryptoConditions);
 | 
			
		||||
    REGISTER(Escrow);
 | 
			
		||||
    REGISTER(TickSize);
 | 
			
		||||
    REGISTER(fix1373);
 | 
			
		||||
    REGISTER(EnforceInvariants);
 | 
			
		||||
    REGISTER(SortedDirectories);
 | 
			
		||||
    REGISTER(fix1201);
 | 
			
		||||
    REGISTER(fix1512);
 | 
			
		||||
    REGISTER(fix1523);
 | 
			
		||||
    REGISTER(fix1528);
 | 
			
		||||
    /** @endcond */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#undef REGISTER
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Knowledge center for amendments within XRPL
 | 
			
		||||
 */
 | 
			
		||||
class AmendmentCenter : public AmendmentCenterInterface {
 | 
			
		||||
    std::shared_ptr<data::BackendInterface> backend_;
 | 
			
		||||
 | 
			
		||||
    std::map<std::string, Amendment> supported_;
 | 
			
		||||
    std::vector<Amendment> all_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Construct a new AmendmentCenter instance
 | 
			
		||||
     *
 | 
			
		||||
     * @param backend The backend
 | 
			
		||||
     */
 | 
			
		||||
    explicit AmendmentCenter(std::shared_ptr<data::BackendInterface> const& backend);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Check whether an amendment is supported by Clio
 | 
			
		||||
     *
 | 
			
		||||
     * @param key The key of the amendment to check
 | 
			
		||||
     * @return true if supported; false otherwise
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] bool
 | 
			
		||||
    isSupported(AmendmentKey const& key) const final;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Get all supported amendments as a map
 | 
			
		||||
     *
 | 
			
		||||
     * @return The amendments supported by Clio
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] std::map<std::string, Amendment> const&
 | 
			
		||||
    getSupported() const final;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Get all known amendments
 | 
			
		||||
     *
 | 
			
		||||
     * @return All known amendments as a vector
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] std::vector<Amendment> const&
 | 
			
		||||
    getAll() const final;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Check whether an amendment was/is enabled for a given sequence
 | 
			
		||||
     *
 | 
			
		||||
     * @param key The key of the amendment to check
 | 
			
		||||
     * @param seq The sequence to check for
 | 
			
		||||
     * @return true if enabled; false otherwise
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] bool
 | 
			
		||||
    isEnabled(AmendmentKey const& key, uint32_t seq) const final;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Check whether an amendment was/is enabled for a given sequence
 | 
			
		||||
     *
 | 
			
		||||
     * @param yield The coroutine context to use
 | 
			
		||||
     * @param key The key of the amendment to check
 | 
			
		||||
     * @param seq The sequence to check for
 | 
			
		||||
     * @return true if enabled; false otherwise
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] bool
 | 
			
		||||
    isEnabled(boost::asio::yield_context yield, AmendmentKey const& key, uint32_t seq) const final;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Check whether an amendment was/is enabled for a given sequence
 | 
			
		||||
     *
 | 
			
		||||
     * @param yield The coroutine context to use
 | 
			
		||||
     * @param keys The keys of the amendments to check
 | 
			
		||||
     * @param seq The sequence to check for
 | 
			
		||||
     * @return A vector of bools representing enabled state for each of the given keys
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] std::vector<bool>
 | 
			
		||||
    isEnabled(boost::asio::yield_context yield, std::vector<AmendmentKey> const& keys, uint32_t seq) const final;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Get an amendment
 | 
			
		||||
     *
 | 
			
		||||
     * @param key The key of the amendment to get
 | 
			
		||||
     * @return The amendment as a const ref; asserts if the amendment is unknown
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] Amendment const&
 | 
			
		||||
    getAmendment(AmendmentKey const& key) const final;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Get an amendment by its key
 | 
			
		||||
 | 
			
		||||
     * @param key The amendment key from @see Amendments
 | 
			
		||||
     * @return The amendment as a const ref; asserts if the amendment is unknown
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] Amendment const&
 | 
			
		||||
    operator[](AmendmentKey const& key) const final;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    [[nodiscard]] std::optional<std::vector<ripple::uint256>>
 | 
			
		||||
    fetchAmendmentsList(boost::asio::yield_context yield, uint32_t seq) const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace data
 | 
			
		||||
							
								
								
									
										116
									
								
								src/data/AmendmentCenterInterface.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/data/AmendmentCenterInterface.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2024, 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/Types.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace data {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief The interface of an amendment center
 | 
			
		||||
 */
 | 
			
		||||
class AmendmentCenterInterface {
 | 
			
		||||
public:
 | 
			
		||||
    virtual ~AmendmentCenterInterface() = default;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Check whether an amendment is supported by Clio
 | 
			
		||||
     *
 | 
			
		||||
     * @param key The key of the amendment to check
 | 
			
		||||
     * @return true if supported; false otherwise
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] virtual bool
 | 
			
		||||
    isSupported(AmendmentKey const& key) const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Get all supported amendments as a map
 | 
			
		||||
     *
 | 
			
		||||
     * @return The amendments supported by Clio
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] virtual std::map<std::string, Amendment> const&
 | 
			
		||||
    getSupported() const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Get all known amendments
 | 
			
		||||
     *
 | 
			
		||||
     * @return All known amendments as a vector
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] virtual std::vector<Amendment> const&
 | 
			
		||||
    getAll() const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Check whether an amendment was/is enabled for a given sequence
 | 
			
		||||
     *
 | 
			
		||||
     * @param key The key of the amendment to check
 | 
			
		||||
     * @param seq The sequence to check for
 | 
			
		||||
     * @return true if enabled; false otherwise
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] virtual bool
 | 
			
		||||
    isEnabled(AmendmentKey const& key, uint32_t seq) const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Check whether an amendment was/is enabled for a given sequence
 | 
			
		||||
     *
 | 
			
		||||
     * @param yield The coroutine context to use
 | 
			
		||||
     * @param key The key of the amendment to check
 | 
			
		||||
     * @param seq The sequence to check for
 | 
			
		||||
     * @return true if enabled; false otherwise
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] virtual bool
 | 
			
		||||
    isEnabled(boost::asio::yield_context yield, AmendmentKey const& key, uint32_t seq) const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Check whether an amendment was/is enabled for a given sequence
 | 
			
		||||
     *
 | 
			
		||||
     * @param yield The coroutine context to use
 | 
			
		||||
     * @param keys The keys of the amendments to check
 | 
			
		||||
     * @param seq The sequence to check for
 | 
			
		||||
     * @return A vector of bools representing enabled state for each of the given keys
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] virtual std::vector<bool>
 | 
			
		||||
    isEnabled(boost::asio::yield_context yield, std::vector<AmendmentKey> const& keys, uint32_t seq) const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Get an amendment
 | 
			
		||||
     *
 | 
			
		||||
     * @param key The key of the amendment to get
 | 
			
		||||
     * @return The amendment as a const ref; asserts if the amendment is unknown
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] virtual Amendment const&
 | 
			
		||||
    getAmendment(AmendmentKey const& key) const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Get an amendment by its key
 | 
			
		||||
     *
 | 
			
		||||
     * @param key The amendment key from @see Amendments
 | 
			
		||||
     * @return The amendment as a const ref; asserts if the amendment is unknown
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] virtual Amendment const&
 | 
			
		||||
    operator[](AmendmentKey const& key) const = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace data
 | 
			
		||||
@@ -22,8 +22,8 @@
 | 
			
		||||
#include "data/BackendInterface.hpp"
 | 
			
		||||
#include "data/CassandraBackend.hpp"
 | 
			
		||||
#include "data/cassandra/SettingsProvider.hpp"
 | 
			
		||||
#include "util/config/Config.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
#include "util/newconfig/ConfigDefinition.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/algorithm/string.hpp>
 | 
			
		||||
#include <boost/algorithm/string/predicate.hpp>
 | 
			
		||||
@@ -41,19 +41,18 @@ namespace data {
 | 
			
		||||
 * @return A shared_ptr<BackendInterface> with the selected implementation
 | 
			
		||||
 */
 | 
			
		||||
inline std::shared_ptr<BackendInterface>
 | 
			
		||||
make_Backend(util::Config const& config)
 | 
			
		||||
make_Backend(util::config::ClioConfigDefinition const& config)
 | 
			
		||||
{
 | 
			
		||||
    static util::Logger const log{"Backend"};
 | 
			
		||||
    LOG(log.info()) << "Constructing BackendInterface";
 | 
			
		||||
 | 
			
		||||
    auto const readOnly = config.valueOr("read_only", false);
 | 
			
		||||
    auto const readOnly = config.get<bool>("read_only");
 | 
			
		||||
 | 
			
		||||
    auto const type = config.value<std::string>("database.type");
 | 
			
		||||
    auto const type = config.get<std::string>("database.type");
 | 
			
		||||
    std::shared_ptr<BackendInterface> backend = nullptr;
 | 
			
		||||
 | 
			
		||||
    // TODO: retire `cassandra-new` by next release after 2.0
 | 
			
		||||
    if (boost::iequals(type, "cassandra") or boost::iequals(type, "cassandra-new")) {
 | 
			
		||||
        auto cfg = config.section("database." + type);
 | 
			
		||||
    if (boost::iequals(type, "cassandra")) {
 | 
			
		||||
        auto const cfg = config.getObject("database." + type);
 | 
			
		||||
        backend = std::make_shared<data::cassandra::CassandraBackend>(data::cassandra::SettingsProvider{cfg}, readOnly);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,13 +24,13 @@
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <ripple/basics/strHex.h>
 | 
			
		||||
#include <ripple/protocol/Fees.h>
 | 
			
		||||
#include <ripple/protocol/Indexes.h>
 | 
			
		||||
#include <ripple/protocol/SField.h>
 | 
			
		||||
#include <ripple/protocol/STLedgerEntry.h>
 | 
			
		||||
#include <ripple/protocol/Serializer.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/basics/strHex.h>
 | 
			
		||||
#include <xrpl/protocol/Fees.h>
 | 
			
		||||
#include <xrpl/protocol/Indexes.h>
 | 
			
		||||
#include <xrpl/protocol/SField.h>
 | 
			
		||||
#include <xrpl/protocol/STLedgerEntry.h>
 | 
			
		||||
#include <xrpl/protocol/Serializer.h>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
@@ -93,7 +93,6 @@ BackendInterface::fetchLedgerObject(
 | 
			
		||||
        return obj;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LOG(gLog.trace()) << "Cache miss - " << ripple::strHex(key);
 | 
			
		||||
    auto dbObj = doFetchLedgerObject(key, sequence, yield);
 | 
			
		||||
    if (!dbObj) {
 | 
			
		||||
        LOG(gLog.trace()) << "Missed cache and missed in db";
 | 
			
		||||
@@ -103,6 +102,19 @@ BackendInterface::fetchLedgerObject(
 | 
			
		||||
    return dbObj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<std::uint32_t>
 | 
			
		||||
BackendInterface::fetchLedgerObjectSeq(
 | 
			
		||||
    ripple::uint256 const& key,
 | 
			
		||||
    std::uint32_t const sequence,
 | 
			
		||||
    boost::asio::yield_context yield
 | 
			
		||||
) const
 | 
			
		||||
{
 | 
			
		||||
    auto seq = doFetchLedgerObjectSeq(key, sequence, yield);
 | 
			
		||||
    if (!seq)
 | 
			
		||||
        LOG(gLog.trace()) << "Missed in db";
 | 
			
		||||
    return seq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<Blob>
 | 
			
		||||
BackendInterface::fetchLedgerObjects(
 | 
			
		||||
    std::vector<ripple::uint256> const& keys,
 | 
			
		||||
@@ -164,9 +176,9 @@ BackendInterface::fetchSuccessorObject(
 | 
			
		||||
    if (succ) {
 | 
			
		||||
        auto obj = fetchLedgerObject(*succ, ledgerSequence, yield);
 | 
			
		||||
        if (!obj)
 | 
			
		||||
            return {{*succ, {}}};
 | 
			
		||||
            return {{.key = *succ, .blob = {}}};
 | 
			
		||||
 | 
			
		||||
        return {{*succ, *obj}};
 | 
			
		||||
        return {{.key = *succ, .blob = *obj}};
 | 
			
		||||
    }
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
@@ -271,7 +283,7 @@ BackendInterface::updateRange(uint32_t newMax)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (!range) {
 | 
			
		||||
        range = {newMax, newMax};
 | 
			
		||||
        range = {.minSequence = newMax, .maxSequence = newMax};
 | 
			
		||||
    } else {
 | 
			
		||||
        range->maxSequence = newMax;
 | 
			
		||||
    }
 | 
			
		||||
@@ -287,7 +299,7 @@ BackendInterface::setRange(uint32_t min, uint32_t max, bool force)
 | 
			
		||||
        ASSERT(not range.has_value(), "Range was already set");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    range = {min, max};
 | 
			
		||||
    range = {.minSequence = min, .maxSequence = max};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LedgerPage
 | 
			
		||||
 
 | 
			
		||||
@@ -31,10 +31,10 @@
 | 
			
		||||
#include <boost/json.hpp>
 | 
			
		||||
#include <boost/json/object.hpp>
 | 
			
		||||
#include <boost/utility/result_of.hpp>
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <ripple/protocol/AccountID.h>
 | 
			
		||||
#include <ripple/protocol/Fees.h>
 | 
			
		||||
#include <ripple/protocol/LedgerHeader.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/protocol/AccountID.h>
 | 
			
		||||
#include <xrpl/protocol/Fees.h>
 | 
			
		||||
#include <xrpl/protocol/LedgerHeader.h>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
@@ -364,6 +364,25 @@ public:
 | 
			
		||||
        boost::asio::yield_context yield
 | 
			
		||||
    ) const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Fetches all holders' balances for a MPTIssuanceID
 | 
			
		||||
     *
 | 
			
		||||
     * @param mptID MPTIssuanceID you wish you query.
 | 
			
		||||
     * @param limit Paging limit.
 | 
			
		||||
     * @param cursorIn Optional cursor to allow us to pick up from where we last left off.
 | 
			
		||||
     * @param ledgerSequence The ledger sequence to fetch for
 | 
			
		||||
     * @param yield Currently executing coroutine.
 | 
			
		||||
     * @return std::vector<Blob> of MPToken balances and an optional marker
 | 
			
		||||
     */
 | 
			
		||||
    virtual MPTHoldersAndCursor
 | 
			
		||||
    fetchMPTHolders(
 | 
			
		||||
        ripple::uint192 const& mptID,
 | 
			
		||||
        std::uint32_t const limit,
 | 
			
		||||
        std::optional<ripple::AccountID> const& cursorIn,
 | 
			
		||||
        std::uint32_t const ledgerSequence,
 | 
			
		||||
        boost::asio::yield_context yield
 | 
			
		||||
    ) const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Fetches a specific ledger object.
 | 
			
		||||
     *
 | 
			
		||||
@@ -378,6 +397,19 @@ public:
 | 
			
		||||
    std::optional<Blob>
 | 
			
		||||
    fetchLedgerObject(ripple::uint256 const& key, std::uint32_t sequence, boost::asio::yield_context yield) const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Fetches a specific ledger object sequence.
 | 
			
		||||
     *
 | 
			
		||||
     * Currently the real fetch happens in doFetchLedgerObjectSeq
 | 
			
		||||
     *
 | 
			
		||||
     * @param key The key of the object
 | 
			
		||||
     * @param sequence The ledger sequence to fetch for
 | 
			
		||||
     * @param yield The coroutine context
 | 
			
		||||
     * @return The sequence in unit32_t on success; nullopt otherwise
 | 
			
		||||
     */
 | 
			
		||||
    std::optional<std::uint32_t>
 | 
			
		||||
    fetchLedgerObjectSeq(ripple::uint256 const& key, std::uint32_t sequence, boost::asio::yield_context yield) const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Fetches all ledger objects by their keys.
 | 
			
		||||
     *
 | 
			
		||||
@@ -407,6 +439,18 @@ public:
 | 
			
		||||
    virtual std::optional<Blob>
 | 
			
		||||
    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.
 | 
			
		||||
     *
 | 
			
		||||
     * @param key The key to fetch for
 | 
			
		||||
     * @param sequence The ledger sequence to fetch for
 | 
			
		||||
     * @param yield The coroutine context
 | 
			
		||||
     * @return The sequence in unit32_t on success; nullopt otherwise
 | 
			
		||||
     */
 | 
			
		||||
    virtual std::optional<std::uint32_t>
 | 
			
		||||
    doFetchLedgerObjectSeq(ripple::uint256 const& key, std::uint32_t sequence, boost::asio::yield_context yield)
 | 
			
		||||
        const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief The database-specific implementation for fetching ledger objects.
 | 
			
		||||
     *
 | 
			
		||||
@@ -504,6 +548,16 @@ public:
 | 
			
		||||
        boost::asio::yield_context yield
 | 
			
		||||
    ) const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Fetches the status of migrator by name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param migratorName The name of the migrator
 | 
			
		||||
     * @param yield The coroutine context
 | 
			
		||||
     * @return The status of the migrator if found; nullopt otherwise
 | 
			
		||||
     */
 | 
			
		||||
    virtual std::optional<std::string>
 | 
			
		||||
    fetchMigratorStatus(std::string const& migratorName, boost::asio::yield_context yield) const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Synchronously fetches the ledger range from DB.
 | 
			
		||||
     *
 | 
			
		||||
@@ -592,6 +646,14 @@ public:
 | 
			
		||||
    virtual void
 | 
			
		||||
    writeNFTTransactions(std::vector<NFTTransactionsData> const& data) = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Write accounts that started holding onto a MPT.
 | 
			
		||||
     *
 | 
			
		||||
     * @param data A vector of MPT ID and account pairs
 | 
			
		||||
     */
 | 
			
		||||
    virtual void
 | 
			
		||||
    writeMPTHolders(std::vector<MPTHolderData> const& data) = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Write a new successor.
 | 
			
		||||
     *
 | 
			
		||||
@@ -621,6 +683,15 @@ public:
 | 
			
		||||
    bool
 | 
			
		||||
    finishWrites(std::uint32_t ledgerSequence);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Mark the migration status of a migrator as Migrated in the database
 | 
			
		||||
     *
 | 
			
		||||
     * @param migratorName The name of the migrator
 | 
			
		||||
     * @param status The status to set
 | 
			
		||||
     */
 | 
			
		||||
    virtual void
 | 
			
		||||
    writeMigratorStatus(std::string const& migratorName, std::string const& status) = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return true if database is overwhelmed; false otherwise
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,8 @@
 | 
			
		||||
add_library(clio_data)
 | 
			
		||||
target_sources(
 | 
			
		||||
  clio_data
 | 
			
		||||
  PRIVATE BackendCounters.cpp
 | 
			
		||||
  PRIVATE AmendmentCenter.cpp
 | 
			
		||||
          BackendCounters.cpp
 | 
			
		||||
          BackendInterface.cpp
 | 
			
		||||
          LedgerCache.cpp
 | 
			
		||||
          cassandra/impl/Future.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@
 | 
			
		||||
#include "data/BackendInterface.hpp"
 | 
			
		||||
#include "data/DBHelpers.hpp"
 | 
			
		||||
#include "data/Types.hpp"
 | 
			
		||||
#include "data/cassandra/Concepts.hpp"
 | 
			
		||||
#include "data/cassandra/Handle.hpp"
 | 
			
		||||
#include "data/cassandra/Schema.hpp"
 | 
			
		||||
#include "data/cassandra/SettingsProvider.hpp"
 | 
			
		||||
@@ -36,13 +35,13 @@
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
#include <boost/json/object.hpp>
 | 
			
		||||
#include <cassandra.h>
 | 
			
		||||
#include <ripple/basics/Blob.h>
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <ripple/basics/strHex.h>
 | 
			
		||||
#include <ripple/protocol/AccountID.h>
 | 
			
		||||
#include <ripple/protocol/Indexes.h>
 | 
			
		||||
#include <ripple/protocol/LedgerHeader.h>
 | 
			
		||||
#include <ripple/protocol/nft.h>
 | 
			
		||||
#include <xrpl/basics/Blob.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/basics/strHex.h>
 | 
			
		||||
#include <xrpl/protocol/AccountID.h>
 | 
			
		||||
#include <xrpl/protocol/Indexes.h>
 | 
			
		||||
#include <xrpl/protocol/LedgerHeader.h>
 | 
			
		||||
#include <xrpl/protocol/nft.h>
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
@@ -73,13 +72,15 @@ class BasicCassandraBackend : public BackendInterface {
 | 
			
		||||
 | 
			
		||||
    SettingsProviderType settingsProvider_;
 | 
			
		||||
    Schema<SettingsProviderType> schema_;
 | 
			
		||||
 | 
			
		||||
    std::atomic_uint32_t ledgerSequence_ = 0u;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    Handle handle_;
 | 
			
		||||
 | 
			
		||||
    // have to be mutable because BackendInterface constness :(
 | 
			
		||||
    mutable ExecutionStrategyType executor_;
 | 
			
		||||
 | 
			
		||||
    std::atomic_uint32_t ledgerSequence_ = 0u;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Create a new cassandra/scylla backend instance.
 | 
			
		||||
@@ -94,7 +95,7 @@ public:
 | 
			
		||||
        , executor_{settingsProvider_.getSettings(), handle_}
 | 
			
		||||
    {
 | 
			
		||||
        if (auto const res = handle_.connect(); not res)
 | 
			
		||||
            throw std::runtime_error("Could not connect to Cassandra: " + res.error());
 | 
			
		||||
            throw std::runtime_error("Could not connect to database: " + res.error());
 | 
			
		||||
 | 
			
		||||
        if (not readOnly) {
 | 
			
		||||
            if (auto const res = handle_.execute(schema_.createKeyspace); not res) {
 | 
			
		||||
@@ -129,7 +130,7 @@ public:
 | 
			
		||||
    {
 | 
			
		||||
        auto rng = fetchLedgerRange();
 | 
			
		||||
        if (!rng)
 | 
			
		||||
            return {{}, {}};
 | 
			
		||||
            return {.txns = {}, .cursor = {}};
 | 
			
		||||
 | 
			
		||||
        Statement const statement = [this, forward, &account]() {
 | 
			
		||||
            if (forward)
 | 
			
		||||
@@ -336,8 +337,8 @@ public:
 | 
			
		||||
 | 
			
		||||
        auto const& result = res.value();
 | 
			
		||||
        if (not result.hasRows()) {
 | 
			
		||||
            LOG(log_.error()) << "Could not fetch all transaction hashes - no rows; ledger = "
 | 
			
		||||
                              << std::to_string(ledgerSequence);
 | 
			
		||||
            LOG(log_.warn()) << "Could not fetch all transaction hashes - no rows; ledger = "
 | 
			
		||||
                             << std::to_string(ledgerSequence);
 | 
			
		||||
            return {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -346,7 +347,7 @@ public:
 | 
			
		||||
            hashes.push_back(std::move(hash));
 | 
			
		||||
 | 
			
		||||
        auto end = std::chrono::system_clock::now();
 | 
			
		||||
        LOG(log_.debug()) << "Fetched " << hashes.size() << " transaction hashes from Cassandra in "
 | 
			
		||||
        LOG(log_.debug()) << "Fetched " << hashes.size() << " transaction hashes from database in "
 | 
			
		||||
                          << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
 | 
			
		||||
                          << " milliseconds";
 | 
			
		||||
 | 
			
		||||
@@ -400,7 +401,7 @@ public:
 | 
			
		||||
    {
 | 
			
		||||
        auto rng = fetchLedgerRange();
 | 
			
		||||
        if (!rng)
 | 
			
		||||
            return {{}, {}};
 | 
			
		||||
            return {.txns = {}, .cursor = {}};
 | 
			
		||||
 | 
			
		||||
        Statement const statement = [this, forward, &tokenID]() {
 | 
			
		||||
            if (forward)
 | 
			
		||||
@@ -548,6 +549,45 @@ public:
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MPTHoldersAndCursor
 | 
			
		||||
    fetchMPTHolders(
 | 
			
		||||
        ripple::uint192 const& mptID,
 | 
			
		||||
        std::uint32_t const limit,
 | 
			
		||||
        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(ripple::AccountID(0)), Limit{limit}
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        auto const& holderResults = holderEntries.value();
 | 
			
		||||
        if (not holderResults.hasRows()) {
 | 
			
		||||
            LOG(log_.debug()) << "No rows returned";
 | 
			
		||||
            return {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto mptObjects = doFetchLedgerObjects(mptKeys, ledgerSequence, yield);
 | 
			
		||||
 | 
			
		||||
        auto it = std::remove_if(mptObjects.begin(), mptObjects.end(), [](Blob const& mpt) { return mpt.empty(); });
 | 
			
		||||
 | 
			
		||||
        mptObjects.erase(it, mptObjects.end());
 | 
			
		||||
 | 
			
		||||
        ASSERT(mptKeys.size() <= limit, "Number of keys can't exceed the limit");
 | 
			
		||||
        if (mptKeys.size() == limit)
 | 
			
		||||
            return {mptObjects, cursor};
 | 
			
		||||
 | 
			
		||||
        return {mptObjects, {}};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::optional<Blob>
 | 
			
		||||
    doFetchLedgerObject(ripple::uint256 const& key, std::uint32_t const sequence, boost::asio::yield_context yield)
 | 
			
		||||
        const override
 | 
			
		||||
@@ -567,6 +607,25 @@ public:
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::optional<std::uint32_t>
 | 
			
		||||
    doFetchLedgerObjectSeq(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 = " << 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();
 | 
			
		||||
                return seq;
 | 
			
		||||
            }
 | 
			
		||||
            LOG(log_.debug()) << "Could not fetch ledger object sequence - no rows";
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            LOG(log_.error()) << "Could not fetch ledger object sequence: " << res.error();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::optional<TransactionAndMetadata>
 | 
			
		||||
    fetchTransaction(ripple::uint256 const& hash, boost::asio::yield_context yield) const override
 | 
			
		||||
    {
 | 
			
		||||
@@ -640,7 +699,7 @@ public:
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        ASSERT(numHashes == results.size(), "Number of hashes and results must match");
 | 
			
		||||
        LOG(log_.debug()) << "Fetched " << numHashes << " transactions from Cassandra in " << timeDiff
 | 
			
		||||
        LOG(log_.debug()) << "Fetched " << numHashes << " transactions from database in " << timeDiff
 | 
			
		||||
                          << " milliseconds";
 | 
			
		||||
        return results;
 | 
			
		||||
    }
 | 
			
		||||
@@ -760,7 +819,7 @@ public:
 | 
			
		||||
        if (keys.empty())
 | 
			
		||||
            return {};
 | 
			
		||||
 | 
			
		||||
        LOG(log_.debug()) << "Fetched " << keys.size() << " diff hashes from Cassandra in " << timeDiff
 | 
			
		||||
        LOG(log_.debug()) << "Fetched " << keys.size() << " diff hashes from database in " << timeDiff
 | 
			
		||||
                          << " milliseconds";
 | 
			
		||||
 | 
			
		||||
        auto const objs = fetchLedgerObjects(keys, ledgerSequence, yield);
 | 
			
		||||
@@ -778,6 +837,26 @@ public:
 | 
			
		||||
        return results;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::optional<std::string>
 | 
			
		||||
    fetchMigratorStatus(std::string const& migratorName, boost::asio::yield_context yield) const override
 | 
			
		||||
    {
 | 
			
		||||
        auto const res = executor_.read(yield, schema_->selectMigratorStatus, Text(migratorName));
 | 
			
		||||
        if (not res) {
 | 
			
		||||
            LOG(log_.error()) << "Could not fetch migrator status: " << res.error();
 | 
			
		||||
            return {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto const& results = res.value();
 | 
			
		||||
        if (not results) {
 | 
			
		||||
            return {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (auto [statusString] : extract<std::string>(results))
 | 
			
		||||
            return statusString;
 | 
			
		||||
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    doWriteLedgerObject(std::string&& key, std::uint32_t const seq, std::string&& blob) override
 | 
			
		||||
    {
 | 
			
		||||
@@ -848,7 +927,7 @@ public:
 | 
			
		||||
        std::string&& metadata
 | 
			
		||||
    ) override
 | 
			
		||||
    {
 | 
			
		||||
        LOG(log_.trace()) << "Writing txn to cassandra";
 | 
			
		||||
        LOG(log_.trace()) << "Writing txn to database";
 | 
			
		||||
 | 
			
		||||
        executor_.write(schema_->insertLedgerTransaction, seq, hash);
 | 
			
		||||
        executor_.write(
 | 
			
		||||
@@ -887,6 +966,17 @@ public:
 | 
			
		||||
        executor_.write(std::move(statements));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    writeMPTHolders(std::vector<MPTHolderData> const& data) override
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<Statement> statements;
 | 
			
		||||
        statements.reserve(data.size());
 | 
			
		||||
        for (auto [mptId, holder] : data)
 | 
			
		||||
            statements.push_back(schema_->insertMPTHolder.bind(std::move(mptId), std::move(holder)));
 | 
			
		||||
 | 
			
		||||
        executor_.write(std::move(statements));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    startWrites() const override
 | 
			
		||||
    {
 | 
			
		||||
@@ -894,6 +984,14 @@ public:
 | 
			
		||||
        // probably was used in PG to start a transaction or smth.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    writeMigratorStatus(std::string const& migratorName, std::string const& status) override
 | 
			
		||||
    {
 | 
			
		||||
        executor_.writeSync(
 | 
			
		||||
            schema_->insertMigratorStatus, data::cassandra::Text{migratorName}, data::cassandra::Text(status)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool
 | 
			
		||||
    isTooBusy() const override
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -23,16 +23,16 @@
 | 
			
		||||
#include "util/Assert.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/container/flat_set.hpp>
 | 
			
		||||
#include <ripple/basics/Blob.h>
 | 
			
		||||
#include <ripple/basics/Log.h>
 | 
			
		||||
#include <ripple/basics/StringUtilities.h>
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <ripple/protocol/AccountID.h>
 | 
			
		||||
#include <ripple/protocol/SField.h>
 | 
			
		||||
#include <ripple/protocol/STAccount.h>
 | 
			
		||||
#include <ripple/protocol/STLedgerEntry.h>
 | 
			
		||||
#include <ripple/protocol/Serializer.h>
 | 
			
		||||
#include <ripple/protocol/TxMeta.h>
 | 
			
		||||
#include <xrpl/basics/Blob.h>
 | 
			
		||||
#include <xrpl/basics/Log.h>
 | 
			
		||||
#include <xrpl/basics/StringUtilities.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/protocol/AccountID.h>
 | 
			
		||||
#include <xrpl/protocol/SField.h>
 | 
			
		||||
#include <xrpl/protocol/STAccount.h>
 | 
			
		||||
#include <xrpl/protocol/STLedgerEntry.h>
 | 
			
		||||
#include <xrpl/protocol/Serializer.h>
 | 
			
		||||
#include <xrpl/protocol/TxMeta.h>
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
@@ -172,6 +172,14 @@ struct NFTsData {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Represents an MPT and holder pair
 | 
			
		||||
 */
 | 
			
		||||
struct MPTHolderData {
 | 
			
		||||
    ripple::uint192 mptID;
 | 
			
		||||
    ripple::AccountID holder;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Check whether the supplied object is an offer.
 | 
			
		||||
 *
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
#include "data/Types.hpp"
 | 
			
		||||
#include "util/Assert.hpp"
 | 
			
		||||
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
@@ -75,7 +75,7 @@ LedgerCache::update(std::vector<LedgerObject> const& objs, uint32_t seq, bool is
 | 
			
		||||
 | 
			
		||||
                auto& e = map_[obj.key];
 | 
			
		||||
                if (seq > e.seq) {
 | 
			
		||||
                    e = {seq, obj.blob};
 | 
			
		||||
                    e = {.seq = seq, .blob = obj.blob};
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                map_.erase(obj.key);
 | 
			
		||||
@@ -101,7 +101,7 @@ LedgerCache::getSuccessor(ripple::uint256 const& key, uint32_t seq) const
 | 
			
		||||
    if (e == map_.end())
 | 
			
		||||
        return {};
 | 
			
		||||
    ++successorHitCounter_.get();
 | 
			
		||||
    return {{e->first, e->second.blob}};
 | 
			
		||||
    return {{.key = e->first, .blob = e->second.blob}};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<LedgerObject>
 | 
			
		||||
@@ -117,7 +117,7 @@ LedgerCache::getPredecessor(ripple::uint256 const& key, uint32_t seq) const
 | 
			
		||||
    if (e == map_.begin())
 | 
			
		||||
        return {};
 | 
			
		||||
    --e;
 | 
			
		||||
    return {{e->first, e->second.blob}};
 | 
			
		||||
    return {{.key = e->first, .blob = e->second.blob}};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<Blob>
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,8 @@
 | 
			
		||||
#include "util/prometheus/Label.hpp"
 | 
			
		||||
#include "util/prometheus/Prometheus.hpp"
 | 
			
		||||
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <ripple/basics/hardened_hash.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/basics/hardened_hash.h>
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
 
 | 
			
		||||
@@ -262,3 +262,15 @@ CREATE TABLE clio.nf_token_transactions (
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `nf_token_transactions` table serves as the NFT counterpart to `account_tx`, inspired by the same motivations and fulfilling a similar role within this context. It drives the `nft_history` API.
 | 
			
		||||
 | 
			
		||||
### migrator_status
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
CREATE TABLE clio.migrator_status (
 | 
			
		||||
    migrator_name TEXT,     # The name of the migrator
 | 
			
		||||
    status TEXT,            # The status of the migrator
 | 
			
		||||
    PRIMARY KEY (migrator_name)
 | 
			
		||||
) 
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `migrator_status` table stores the status of the migratior in this database. If a migrator's status is `migrated`, it means this database has finished data migration for this migrator.
 | 
			
		||||
 
 | 
			
		||||
@@ -19,11 +19,13 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <ripple/protocol/AccountID.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/protocol/AccountID.h>
 | 
			
		||||
 | 
			
		||||
#include <concepts>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <tuple>
 | 
			
		||||
#include <utility>
 | 
			
		||||
@@ -231,6 +233,14 @@ struct NFTsAndCursor {
 | 
			
		||||
    std::optional<ripple::uint256> cursor;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Represents an array of MPTokens
 | 
			
		||||
 */
 | 
			
		||||
struct MPTHoldersAndCursor {
 | 
			
		||||
    std::vector<Blob> mptokens;
 | 
			
		||||
    std::optional<ripple::AccountID> cursor;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Stores a range of sequences as a min and max pair.
 | 
			
		||||
 */
 | 
			
		||||
@@ -239,6 +249,69 @@ struct LedgerRange {
 | 
			
		||||
    std::uint32_t maxSequence = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Represents an amendment in the XRPL
 | 
			
		||||
 */
 | 
			
		||||
struct Amendment {
 | 
			
		||||
    std::string name;
 | 
			
		||||
    ripple::uint256 feature;
 | 
			
		||||
    bool isSupportedByXRPL = false;
 | 
			
		||||
    bool isSupportedByClio = false;
 | 
			
		||||
    bool isRetired = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Get the amendment Id from its name
 | 
			
		||||
     *
 | 
			
		||||
     * @param name The name of the amendment
 | 
			
		||||
     * @return The amendment Id as uint256
 | 
			
		||||
     */
 | 
			
		||||
    static ripple::uint256
 | 
			
		||||
    GetAmendmentId(std::string_view const name);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Equality comparison operator
 | 
			
		||||
     * @param other The object to compare to
 | 
			
		||||
     * @return Whether the objects are equal
 | 
			
		||||
     */
 | 
			
		||||
    bool
 | 
			
		||||
    operator==(Amendment const& other) const
 | 
			
		||||
    {
 | 
			
		||||
        return name == other.name;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief A helper for amendment name to feature conversions
 | 
			
		||||
 */
 | 
			
		||||
struct AmendmentKey {
 | 
			
		||||
    std::string name;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Construct a new AmendmentKey
 | 
			
		||||
     * @param val Anything convertible to a string
 | 
			
		||||
     */
 | 
			
		||||
    AmendmentKey(std::convertible_to<std::string> auto&& val) : name{std::forward<decltype(val)>(val)}
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @brief Conversion to string */
 | 
			
		||||
    operator std::string const&() const;
 | 
			
		||||
 | 
			
		||||
    /** @brief Conversion to string_view */
 | 
			
		||||
    operator std::string_view() const;
 | 
			
		||||
 | 
			
		||||
    /** @brief Conversion to uint256 */
 | 
			
		||||
    operator ripple::uint256() const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Comparison operators
 | 
			
		||||
     * @param other The object to compare to
 | 
			
		||||
     * @return Whether the objects are equal, greater or less
 | 
			
		||||
     */
 | 
			
		||||
    auto
 | 
			
		||||
    operator<=>(AmendmentKey const& other) const = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
constexpr ripple::uint256 firstKey{"0000000000000000000000000000000000000000000000000000000000000000"};
 | 
			
		||||
constexpr ripple::uint256 lastKey{"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"};
 | 
			
		||||
constexpr ripple::uint256 hi192{"0000000000000000000000000000000000000000000000001111111111111111"};
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@ Handle::connect() const
 | 
			
		||||
Handle::FutureType
 | 
			
		||||
Handle::asyncConnect(std::string_view keyspace) const
 | 
			
		||||
{
 | 
			
		||||
    return cass_session_connect_keyspace(session_, cluster_, keyspace.data());
 | 
			
		||||
    return cass_session_connect_keyspace_n(session_, cluster_, keyspace.data(), keyspace.size());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Handle::MaybeErrorType
 | 
			
		||||
@@ -155,7 +155,7 @@ Handle::asyncExecute(std::vector<StatementType> const& statements, std::function
 | 
			
		||||
Handle::PreparedStatementType
 | 
			
		||||
Handle::prepare(std::string_view query) const
 | 
			
		||||
{
 | 
			
		||||
    Handle::FutureType const future = cass_session_prepare(session_, query.data());
 | 
			
		||||
    Handle::FutureType const future = cass_session_prepare_n(session_, query.data(), query.size());
 | 
			
		||||
    auto const rc = future.await();
 | 
			
		||||
    if (rc)
 | 
			
		||||
        return cass_future_get_prepared(future);
 | 
			
		||||
 
 | 
			
		||||
@@ -257,6 +257,31 @@ public:
 | 
			
		||||
            qualifiedTableName(settingsProvider_.get(), "nf_token_transactions")
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        statements.emplace_back(fmt::format(
 | 
			
		||||
            R"(
 | 
			
		||||
           CREATE TABLE IF NOT EXISTS {}
 | 
			
		||||
                  ( 
 | 
			
		||||
                    mpt_id blob,
 | 
			
		||||
                    holder blob,
 | 
			
		||||
                   PRIMARY KEY (mpt_id, holder)
 | 
			
		||||
                  ) 
 | 
			
		||||
             WITH CLUSTERING ORDER BY (holder ASC)
 | 
			
		||||
            )",
 | 
			
		||||
            qualifiedTableName(settingsProvider_.get(), "mp_token_holders")
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        statements.emplace_back(fmt::format(
 | 
			
		||||
            R"(
 | 
			
		||||
           CREATE TABLE IF NOT EXISTS {}
 | 
			
		||||
                  ( 
 | 
			
		||||
                   migrator_name TEXT,
 | 
			
		||||
                          status TEXT,
 | 
			
		||||
                         PRIMARY KEY (migrator_name)
 | 
			
		||||
                  ) 
 | 
			
		||||
            )",
 | 
			
		||||
            qualifiedTableName(settingsProvider_.get(), "migrator_status")
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        return statements;
 | 
			
		||||
    }();
 | 
			
		||||
 | 
			
		||||
@@ -393,6 +418,17 @@ public:
 | 
			
		||||
            ));
 | 
			
		||||
        }();
 | 
			
		||||
 | 
			
		||||
        PreparedStatement insertMPTHolder = [this]() {
 | 
			
		||||
            return handle_.get().prepare(fmt::format(
 | 
			
		||||
                R"(
 | 
			
		||||
                INSERT INTO {} 
 | 
			
		||||
                       (mpt_id, holder)
 | 
			
		||||
                VALUES (?, ?)
 | 
			
		||||
                )",
 | 
			
		||||
                qualifiedTableName(settingsProvider_.get(), "mp_token_holders")
 | 
			
		||||
            ));
 | 
			
		||||
        }();
 | 
			
		||||
 | 
			
		||||
        PreparedStatement insertLedgerHeader = [this]() {
 | 
			
		||||
            return handle_.get().prepare(fmt::format(
 | 
			
		||||
                R"(
 | 
			
		||||
@@ -442,6 +478,17 @@ public:
 | 
			
		||||
            ));
 | 
			
		||||
        }();
 | 
			
		||||
 | 
			
		||||
        PreparedStatement insertMigratorStatus = [this]() {
 | 
			
		||||
            return handle_.get().prepare(fmt::format(
 | 
			
		||||
                R"(
 | 
			
		||||
                INSERT INTO {}
 | 
			
		||||
                       (migrator_name, status)
 | 
			
		||||
                VALUES (?, ?)
 | 
			
		||||
                )",
 | 
			
		||||
                qualifiedTableName(settingsProvider_.get(), "migrator_status")
 | 
			
		||||
            ));
 | 
			
		||||
        }();
 | 
			
		||||
 | 
			
		||||
        //
 | 
			
		||||
        // Select queries
 | 
			
		||||
        //
 | 
			
		||||
@@ -687,6 +734,20 @@ public:
 | 
			
		||||
            ));
 | 
			
		||||
        }();
 | 
			
		||||
 | 
			
		||||
        PreparedStatement selectMPTHolders = [this]() {
 | 
			
		||||
            return handle_.get().prepare(fmt::format(
 | 
			
		||||
                R"(
 | 
			
		||||
                SELECT holder
 | 
			
		||||
                  FROM {}    
 | 
			
		||||
                 WHERE mpt_id = ?
 | 
			
		||||
                   AND holder > ?
 | 
			
		||||
              ORDER BY holder ASC
 | 
			
		||||
                 LIMIT ?
 | 
			
		||||
                )",
 | 
			
		||||
                qualifiedTableName(settingsProvider_.get(), "mp_token_holders")
 | 
			
		||||
            ));
 | 
			
		||||
        }();
 | 
			
		||||
 | 
			
		||||
        PreparedStatement selectLedgerByHash = [this]() {
 | 
			
		||||
            return handle_.get().prepare(fmt::format(
 | 
			
		||||
                R"(
 | 
			
		||||
@@ -730,6 +791,17 @@ public:
 | 
			
		||||
                qualifiedTableName(settingsProvider_.get(), "ledger_range")
 | 
			
		||||
            ));
 | 
			
		||||
        }();
 | 
			
		||||
 | 
			
		||||
        PreparedStatement selectMigratorStatus = [this]() {
 | 
			
		||||
            return handle_.get().prepare(fmt::format(
 | 
			
		||||
                R"(
 | 
			
		||||
                SELECT status
 | 
			
		||||
                  FROM {}
 | 
			
		||||
                 WHERE migrator_name = ?
 | 
			
		||||
                )",
 | 
			
		||||
                qualifiedTableName(settingsProvider_.get(), "migrator_status")
 | 
			
		||||
            ));
 | 
			
		||||
        }();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -22,10 +22,7 @@
 | 
			
		||||
#include "data/cassandra/Types.hpp"
 | 
			
		||||
#include "data/cassandra/impl/Cluster.hpp"
 | 
			
		||||
#include "util/Constants.hpp"
 | 
			
		||||
#include "util/config/Config.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/json/conversion.hpp>
 | 
			
		||||
#include <boost/json/value.hpp>
 | 
			
		||||
#include "util/newconfig/ObjectView.hpp"
 | 
			
		||||
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
@@ -36,43 +33,17 @@
 | 
			
		||||
#include <ios>
 | 
			
		||||
#include <iterator>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <system_error>
 | 
			
		||||
 | 
			
		||||
namespace data::cassandra {
 | 
			
		||||
 | 
			
		||||
namespace impl {
 | 
			
		||||
inline Settings::ContactPoints
 | 
			
		||||
tag_invoke(boost::json::value_to_tag<Settings::ContactPoints>, boost::json::value const& value)
 | 
			
		||||
{
 | 
			
		||||
    if (not value.is_object()) {
 | 
			
		||||
        throw std::runtime_error("Feed entire Cassandra section to parse Settings::ContactPoints instead");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    util::Config const obj{value};
 | 
			
		||||
    Settings::ContactPoints out;
 | 
			
		||||
 | 
			
		||||
    out.contactPoints = obj.valueOrThrow<std::string>("contact_points", "`contact_points` must be a string");
 | 
			
		||||
    out.port = obj.maybeValue<uint16_t>("port");
 | 
			
		||||
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline Settings::SecureConnectionBundle
 | 
			
		||||
tag_invoke(boost::json::value_to_tag<Settings::SecureConnectionBundle>, boost::json::value const& value)
 | 
			
		||||
{
 | 
			
		||||
    if (not value.is_string())
 | 
			
		||||
        throw std::runtime_error("`secure_connect_bundle` must be a string");
 | 
			
		||||
    return Settings::SecureConnectionBundle{value.as_string().data()};
 | 
			
		||||
}
 | 
			
		||||
}  // namespace impl
 | 
			
		||||
 | 
			
		||||
SettingsProvider::SettingsProvider(util::Config const& cfg)
 | 
			
		||||
SettingsProvider::SettingsProvider(util::config::ObjectView const& cfg)
 | 
			
		||||
    : config_{cfg}
 | 
			
		||||
    , keyspace_{cfg.valueOr<std::string>("keyspace", "clio")}
 | 
			
		||||
    , keyspace_{cfg.get<std::string>("keyspace")}
 | 
			
		||||
    , tablePrefix_{cfg.maybeValue<std::string>("table_prefix")}
 | 
			
		||||
    , replicationFactor_{cfg.valueOr<uint16_t>("replication_factor", 3)}
 | 
			
		||||
    , replicationFactor_{cfg.get<uint16_t>("replication_factor")}
 | 
			
		||||
    , settings_{parseSettings()}
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@@ -86,8 +57,8 @@ SettingsProvider::getSettings() const
 | 
			
		||||
std::optional<std::string>
 | 
			
		||||
SettingsProvider::parseOptionalCertificate() const
 | 
			
		||||
{
 | 
			
		||||
    if (auto const certPath = config_.maybeValue<std::string>("certfile"); certPath) {
 | 
			
		||||
        auto const path = std::filesystem::path(*certPath);
 | 
			
		||||
    if (auto const certPath = config_.getValueView("certfile"); certPath.hasValue()) {
 | 
			
		||||
        auto const path = std::filesystem::path(certPath.asString());
 | 
			
		||||
        std::ifstream fileStream(path.string(), std::ios::in);
 | 
			
		||||
        if (!fileStream) {
 | 
			
		||||
            throw std::system_error(errno, std::generic_category(), "Opening certificate " + path.string());
 | 
			
		||||
@@ -108,30 +79,34 @@ Settings
 | 
			
		||||
SettingsProvider::parseSettings() const
 | 
			
		||||
{
 | 
			
		||||
    auto settings = Settings::defaultSettings();
 | 
			
		||||
    if (auto const bundle = config_.maybeValue<Settings::SecureConnectionBundle>("secure_connect_bundle"); bundle) {
 | 
			
		||||
        settings.connectionInfo = *bundle;
 | 
			
		||||
 | 
			
		||||
    // all config values used in settings is under "database.cassandra" prefix
 | 
			
		||||
    if (config_.getValueView("secure_connect_bundle").hasValue()) {
 | 
			
		||||
        auto const bundle = Settings::SecureConnectionBundle{(config_.get<std::string>("secure_connect_bundle"))};
 | 
			
		||||
        settings.connectionInfo = bundle;
 | 
			
		||||
    } else {
 | 
			
		||||
        settings.connectionInfo =
 | 
			
		||||
            config_.valueOrThrow<Settings::ContactPoints>("Missing contact_points in Cassandra config");
 | 
			
		||||
        Settings::ContactPoints out;
 | 
			
		||||
        out.contactPoints = config_.get<std::string>("contact_points");
 | 
			
		||||
        out.port = config_.maybeValue<uint32_t>("port");
 | 
			
		||||
        settings.connectionInfo = out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    settings.threads = config_.valueOr<uint32_t>("threads", settings.threads);
 | 
			
		||||
    settings.maxWriteRequestsOutstanding =
 | 
			
		||||
        config_.valueOr<uint32_t>("max_write_requests_outstanding", settings.maxWriteRequestsOutstanding);
 | 
			
		||||
    settings.maxReadRequestsOutstanding =
 | 
			
		||||
        config_.valueOr<uint32_t>("max_read_requests_outstanding", settings.maxReadRequestsOutstanding);
 | 
			
		||||
    settings.coreConnectionsPerHost =
 | 
			
		||||
        config_.valueOr<uint32_t>("core_connections_per_host", settings.coreConnectionsPerHost);
 | 
			
		||||
    settings.threads = config_.get<uint32_t>("threads");
 | 
			
		||||
    settings.maxWriteRequestsOutstanding = config_.get<uint32_t>("max_write_requests_outstanding");
 | 
			
		||||
    settings.maxReadRequestsOutstanding = config_.get<uint32_t>("max_read_requests_outstanding");
 | 
			
		||||
    settings.coreConnectionsPerHost = config_.get<uint32_t>("core_connections_per_host");
 | 
			
		||||
    settings.queueSizeIO = config_.maybeValue<uint32_t>("queue_size_io");
 | 
			
		||||
    settings.writeBatchSize = config_.valueOr<std::size_t>("write_batch_size", settings.writeBatchSize);
 | 
			
		||||
    settings.writeBatchSize = config_.get<std::size_t>("write_batch_size");
 | 
			
		||||
 | 
			
		||||
    auto const connectTimeoutSecond = config_.maybeValue<uint32_t>("connect_timeout");
 | 
			
		||||
    if (connectTimeoutSecond)
 | 
			
		||||
        settings.connectionTimeout = std::chrono::milliseconds{*connectTimeoutSecond * util::MILLISECONDS_PER_SECOND};
 | 
			
		||||
    if (config_.getValueView("connect_timeout").hasValue()) {
 | 
			
		||||
        auto const connectTimeoutSecond = config_.get<uint32_t>("connect_timeout");
 | 
			
		||||
        settings.connectionTimeout = std::chrono::milliseconds{connectTimeoutSecond * util::MILLISECONDS_PER_SECOND};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto const requestTimeoutSecond = config_.maybeValue<uint32_t>("request_timeout");
 | 
			
		||||
    if (requestTimeoutSecond)
 | 
			
		||||
        settings.requestTimeout = std::chrono::milliseconds{*requestTimeoutSecond * util::MILLISECONDS_PER_SECOND};
 | 
			
		||||
    if (config_.getValueView("request_timeout").hasValue()) {
 | 
			
		||||
        auto const requestTimeoutSecond = config_.get<uint32_t>("request_timeout");
 | 
			
		||||
        settings.requestTimeout = std::chrono::milliseconds{requestTimeoutSecond * util::MILLISECONDS_PER_SECOND};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    settings.certificate = parseOptionalCertificate();
 | 
			
		||||
    settings.username = config_.maybeValue<std::string>("username");
 | 
			
		||||
 
 | 
			
		||||
@@ -19,10 +19,9 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "data/cassandra/Handle.hpp"
 | 
			
		||||
#include "data/cassandra/Types.hpp"
 | 
			
		||||
#include "util/config/Config.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
#include "data/cassandra/impl/Cluster.hpp"
 | 
			
		||||
#include "util/newconfig/ObjectView.hpp"
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <optional>
 | 
			
		||||
@@ -34,7 +33,7 @@ namespace data::cassandra {
 | 
			
		||||
 * @brief Provides settings for @ref BasicCassandraBackend.
 | 
			
		||||
 */
 | 
			
		||||
class SettingsProvider {
 | 
			
		||||
    util::Config config_;
 | 
			
		||||
    util::config::ObjectView config_;
 | 
			
		||||
 | 
			
		||||
    std::string keyspace_;
 | 
			
		||||
    std::optional<std::string> tablePrefix_;
 | 
			
		||||
@@ -47,7 +46,7 @@ public:
 | 
			
		||||
     *
 | 
			
		||||
     * @param cfg The config of Clio to use
 | 
			
		||||
     */
 | 
			
		||||
    explicit SettingsProvider(util::Config const& cfg);
 | 
			
		||||
    explicit SettingsProvider(util::config::ObjectView const& cfg);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return The cluster settings
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,8 @@
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <expected>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
namespace data::cassandra {
 | 
			
		||||
 | 
			
		||||
@@ -55,6 +57,26 @@ struct Limit {
 | 
			
		||||
    int32_t limit;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief A strong type wrapper for string
 | 
			
		||||
 *
 | 
			
		||||
 * This is unfortunately needed right now to support TEXT properly
 | 
			
		||||
 * because clio uses string to represent BLOB
 | 
			
		||||
 * If we want to bind TEXT with string, we need to use this type
 | 
			
		||||
 */
 | 
			
		||||
struct Text {
 | 
			
		||||
    std::string text;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Construct a new Text object from string type
 | 
			
		||||
     *
 | 
			
		||||
     * @param text The text to wrap
 | 
			
		||||
     */
 | 
			
		||||
    explicit Text(std::string text) : text{std::move(text)}
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Handle;
 | 
			
		||||
class CassandraError;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@
 | 
			
		||||
 | 
			
		||||
#include "data/cassandra/impl/ManagedObject.hpp"
 | 
			
		||||
#include "data/cassandra/impl/SslContext.hpp"
 | 
			
		||||
#include "util/OverloadSet.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
 | 
			
		||||
#include <cassandra.h>
 | 
			
		||||
@@ -31,16 +32,9 @@
 | 
			
		||||
#include <variant>
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
constexpr auto clusterDeleter = [](CassCluster* ptr) { cass_cluster_free(ptr); };
 | 
			
		||||
 | 
			
		||||
template <typename... Ts>
 | 
			
		||||
struct overloadSet : Ts... {
 | 
			
		||||
    using Ts::operator()...;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// explicit deduction guide (not needed as of C++20, but clang be clang)
 | 
			
		||||
template <typename... Ts>
 | 
			
		||||
overloadSet(Ts...) -> overloadSet<Ts...>;
 | 
			
		||||
};  // namespace
 | 
			
		||||
 | 
			
		||||
namespace data::cassandra::impl {
 | 
			
		||||
@@ -90,7 +84,7 @@ void
 | 
			
		||||
Cluster::setupConnection(Settings const& settings)
 | 
			
		||||
{
 | 
			
		||||
    std::visit(
 | 
			
		||||
        overloadSet{
 | 
			
		||||
        util::OverloadSet{
 | 
			
		||||
            [this](Settings::ContactPoints const& points) { setupContactPoints(points); },
 | 
			
		||||
            [this](Settings::SecureConnectionBundle const& bundle) { setupSecureBundle(bundle); }
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
#include "data/cassandra/impl/ManagedObject.hpp"
 | 
			
		||||
 | 
			
		||||
#include <cassandra.h>
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
 
 | 
			
		||||
@@ -21,10 +21,11 @@
 | 
			
		||||
 | 
			
		||||
#include "data/cassandra/impl/ManagedObject.hpp"
 | 
			
		||||
#include "data/cassandra/impl/Tuple.hpp"
 | 
			
		||||
#include "util/UnsupportedType.hpp"
 | 
			
		||||
 | 
			
		||||
#include <cassandra.h>
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <ripple/protocol/AccountID.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/protocol/AccountID.h>
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
@@ -41,9 +42,6 @@
 | 
			
		||||
 | 
			
		||||
namespace data::cassandra::impl {
 | 
			
		||||
 | 
			
		||||
template <typename>
 | 
			
		||||
static constexpr bool unsupported_v = false;
 | 
			
		||||
 | 
			
		||||
template <typename Type>
 | 
			
		||||
inline Type
 | 
			
		||||
extractColumn(CassRow const* row, std::size_t idx)
 | 
			
		||||
@@ -103,7 +101,7 @@ extractColumn(CassRow const* row, std::size_t idx)
 | 
			
		||||
        output = static_cast<DecayedType>(out);
 | 
			
		||||
    } else {
 | 
			
		||||
        // type not supported for extraction
 | 
			
		||||
        static_assert(unsupported_v<DecayedType>);
 | 
			
		||||
        static_assert(util::Unsupported<DecayedType>);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return output;
 | 
			
		||||
 
 | 
			
		||||
@@ -23,12 +23,13 @@
 | 
			
		||||
#include "data/cassandra/impl/Collection.hpp"
 | 
			
		||||
#include "data/cassandra/impl/ManagedObject.hpp"
 | 
			
		||||
#include "data/cassandra/impl/Tuple.hpp"
 | 
			
		||||
#include "util/UnsupportedType.hpp"
 | 
			
		||||
 | 
			
		||||
#include <cassandra.h>
 | 
			
		||||
#include <fmt/core.h>
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <ripple/protocol/AccountID.h>
 | 
			
		||||
#include <ripple/protocol/STAccount.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/protocol/AccountID.h>
 | 
			
		||||
#include <xrpl/protocol/STAccount.h>
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
@@ -44,9 +45,6 @@ namespace data::cassandra::impl {
 | 
			
		||||
class Statement : public ManagedObject<CassStatement> {
 | 
			
		||||
    static constexpr auto deleter = [](CassStatement* ptr) { cass_statement_free(ptr); };
 | 
			
		||||
 | 
			
		||||
    template <typename>
 | 
			
		||||
    static constexpr bool unsupported_v = false;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Construct a new statement with optionally provided arguments.
 | 
			
		||||
@@ -56,7 +54,7 @@ public:
 | 
			
		||||
     */
 | 
			
		||||
    template <typename... Args>
 | 
			
		||||
    explicit Statement(std::string_view query, Args&&... args)
 | 
			
		||||
        : ManagedObject{cass_statement_new(query.data(), sizeof...(args)), deleter}
 | 
			
		||||
        : ManagedObject{cass_statement_new_n(query.data(), query.size(), sizeof...(args)), deleter}
 | 
			
		||||
    {
 | 
			
		||||
        cass_statement_set_consistency(*this, CASS_CONSISTENCY_QUORUM);
 | 
			
		||||
        cass_statement_set_is_idempotent(*this, cass_true);
 | 
			
		||||
@@ -108,9 +106,9 @@ public:
 | 
			
		||||
        using UintByteTupleType = std::tuple<uint32_t, ripple::uint256>;
 | 
			
		||||
        using ByteVectorType = std::vector<ripple::uint256>;
 | 
			
		||||
 | 
			
		||||
        if constexpr (std::is_same_v<DecayedType, ripple::uint256>) {
 | 
			
		||||
        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 ripple::uint256");
 | 
			
		||||
            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 ripple::AccountID");
 | 
			
		||||
@@ -121,6 +119,9 @@ public:
 | 
			
		||||
            // reinterpret_cast is needed here :'(
 | 
			
		||||
            auto const rc = bindBytes(reinterpret_cast<unsigned char const*>(value.data()), value.size());
 | 
			
		||||
            throwErrorIfNeeded(rc, "Bind string (as bytes)");
 | 
			
		||||
        } else if constexpr (std::is_convertible_v<DecayedType, Text>) {
 | 
			
		||||
            auto const rc = cass_statement_bind_string_n(*this, idx, value.text.c_str(), value.text.size());
 | 
			
		||||
            throwErrorIfNeeded(rc, "Bind string (as TEXT)");
 | 
			
		||||
        } 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)});
 | 
			
		||||
@@ -141,7 +142,7 @@ public:
 | 
			
		||||
            throwErrorIfNeeded(rc, "Bind int64");
 | 
			
		||||
        } else {
 | 
			
		||||
            // type not supported for binding
 | 
			
		||||
            static_assert(unsupported_v<DecayedType>);
 | 
			
		||||
            static_assert(util::Unsupported<DecayedType>);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -20,9 +20,10 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "data/cassandra/impl/ManagedObject.hpp"
 | 
			
		||||
#include "util/UnsupportedType.hpp"
 | 
			
		||||
 | 
			
		||||
#include <cassandra.h>
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
@@ -38,9 +39,6 @@ namespace data::cassandra::impl {
 | 
			
		||||
class Tuple : public ManagedObject<CassTuple> {
 | 
			
		||||
    static constexpr auto deleter = [](CassTuple* ptr) { cass_tuple_free(ptr); };
 | 
			
		||||
 | 
			
		||||
    template <typename>
 | 
			
		||||
    static constexpr bool unsupported_v = false;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /* implicit */ Tuple(CassTuple* ptr);
 | 
			
		||||
 | 
			
		||||
@@ -91,15 +89,12 @@ public:
 | 
			
		||||
            throwErrorIfNeeded(rc, "Bind ripple::uint256");
 | 
			
		||||
        } else {
 | 
			
		||||
            // type not supported for binding
 | 
			
		||||
            static_assert(unsupported_v<DecayedType>);
 | 
			
		||||
            static_assert(util::Unsupported<DecayedType>);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class TupleIterator : public ManagedObject<CassIterator> {
 | 
			
		||||
    template <typename>
 | 
			
		||||
    static constexpr bool unsupported_v = false;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /* implicit */ TupleIterator(CassIterator* ptr);
 | 
			
		||||
 | 
			
		||||
@@ -141,7 +136,7 @@ private:
 | 
			
		||||
            output = static_cast<DecayedType>(out);
 | 
			
		||||
        } else {
 | 
			
		||||
            // type not supported for extraction
 | 
			
		||||
            static_assert(unsupported_v<DecayedType>);
 | 
			
		||||
            static_assert(util::Unsupported<DecayedType>);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return output;
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,8 @@ target_sources(
 | 
			
		||||
          NetworkValidatedLedgers.cpp
 | 
			
		||||
          NFTHelpers.cpp
 | 
			
		||||
          Source.cpp
 | 
			
		||||
          impl/ForwardingCache.cpp
 | 
			
		||||
          MPTHelpers.cpp
 | 
			
		||||
          impl/AmendmentBlockHandler.cpp
 | 
			
		||||
          impl/ForwardingSource.cpp
 | 
			
		||||
          impl/GrpcSource.cpp
 | 
			
		||||
          impl/SubscriptionSource.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,11 @@ public:
 | 
			
		||||
     * @param backend The backend to use
 | 
			
		||||
     * @param cache The cache to load into
 | 
			
		||||
     */
 | 
			
		||||
    CacheLoader(util::Config const& config, std::shared_ptr<BackendInterface> const& backend, CacheType& cache)
 | 
			
		||||
    CacheLoader(
 | 
			
		||||
        util::config::ClioConfigDefinition const& config,
 | 
			
		||||
        std::shared_ptr<BackendInterface> const& backend,
 | 
			
		||||
        CacheType& cache
 | 
			
		||||
    )
 | 
			
		||||
        : backend_{backend}, cache_{cache}, settings_{make_CacheLoaderSettings(config)}, ctx_{settings_.numThreads}
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -19,11 +19,12 @@
 | 
			
		||||
 | 
			
		||||
#include "etl/CacheLoaderSettings.hpp"
 | 
			
		||||
 | 
			
		||||
#include "util/config/Config.hpp"
 | 
			
		||||
#include "util/newconfig/ConfigDefinition.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/algorithm/string/predicate.hpp>
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace etl {
 | 
			
		||||
@@ -47,31 +48,29 @@ CacheLoaderSettings::isDisabled() const
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] CacheLoaderSettings
 | 
			
		||||
make_CacheLoaderSettings(util::Config const& config)
 | 
			
		||||
make_CacheLoaderSettings(util::config::ClioConfigDefinition const& config)
 | 
			
		||||
{
 | 
			
		||||
    CacheLoaderSettings settings;
 | 
			
		||||
    settings.numThreads = config.valueOr("io_threads", settings.numThreads);
 | 
			
		||||
    if (config.contains("cache")) {
 | 
			
		||||
        auto const cache = config.section("cache");
 | 
			
		||||
        // Given diff number to generate cursors
 | 
			
		||||
        settings.numCacheDiffs = cache.valueOr<size_t>("num_diffs", settings.numCacheDiffs);
 | 
			
		||||
        // Given cursors number fetching from diff
 | 
			
		||||
        settings.numCacheCursorsFromDiff = cache.valueOr<size_t>("num_cursors_from_diff", 0);
 | 
			
		||||
        // Given cursors number fetching from account
 | 
			
		||||
        settings.numCacheCursorsFromAccount = cache.valueOr<size_t>("num_cursors_from_account", 0);
 | 
			
		||||
    settings.numThreads = config.get<uint16_t>("io_threads");
 | 
			
		||||
    auto const cache = config.getObject("cache");
 | 
			
		||||
    // Given diff number to generate cursors
 | 
			
		||||
    settings.numCacheDiffs = cache.get<std::size_t>("num_diffs");
 | 
			
		||||
    // Given cursors number fetching from diff
 | 
			
		||||
    settings.numCacheCursorsFromDiff = cache.get<std::size_t>("num_cursors_from_diff");
 | 
			
		||||
    // Given cursors number fetching from account
 | 
			
		||||
    settings.numCacheCursorsFromAccount = cache.get<std::size_t>("num_cursors_from_account");
 | 
			
		||||
 | 
			
		||||
        settings.numCacheMarkers = cache.valueOr<size_t>("num_markers", settings.numCacheMarkers);
 | 
			
		||||
        settings.cachePageFetchSize = cache.valueOr<size_t>("page_fetch_size", settings.cachePageFetchSize);
 | 
			
		||||
    settings.numCacheMarkers = cache.get<std::size_t>("num_markers");
 | 
			
		||||
    settings.cachePageFetchSize = cache.get<std::size_t>("page_fetch_size");
 | 
			
		||||
 | 
			
		||||
    auto const entry = cache.get<std::string>("load");
 | 
			
		||||
    if (boost::iequals(entry, "sync"))
 | 
			
		||||
        settings.loadStyle = CacheLoaderSettings::LoadStyle::SYNC;
 | 
			
		||||
    if (boost::iequals(entry, "async"))
 | 
			
		||||
        settings.loadStyle = CacheLoaderSettings::LoadStyle::ASYNC;
 | 
			
		||||
    if (boost::iequals(entry, "none") or boost::iequals(entry, "no"))
 | 
			
		||||
        settings.loadStyle = CacheLoaderSettings::LoadStyle::NONE;
 | 
			
		||||
 | 
			
		||||
        if (auto entry = cache.maybeValue<std::string>("load"); entry) {
 | 
			
		||||
            if (boost::iequals(*entry, "sync"))
 | 
			
		||||
                settings.loadStyle = CacheLoaderSettings::LoadStyle::SYNC;
 | 
			
		||||
            if (boost::iequals(*entry, "async"))
 | 
			
		||||
                settings.loadStyle = CacheLoaderSettings::LoadStyle::ASYNC;
 | 
			
		||||
            if (boost::iequals(*entry, "none") or boost::iequals(*entry, "no"))
 | 
			
		||||
                settings.loadStyle = CacheLoaderSettings::LoadStyle::NONE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return settings;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "util/config/Config.hpp"
 | 
			
		||||
#include "util/newconfig/ConfigDefinition.hpp"
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
 | 
			
		||||
@@ -64,6 +64,6 @@ struct CacheLoaderSettings {
 | 
			
		||||
 * @returns The CacheLoaderSettings object
 | 
			
		||||
 */
 | 
			
		||||
[[nodiscard]] CacheLoaderSettings
 | 
			
		||||
make_CacheLoaderSettings(util::Config const& config);
 | 
			
		||||
make_CacheLoaderSettings(util::config::ClioConfigDefinition const& config);
 | 
			
		||||
 | 
			
		||||
}  // namespace etl
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
 | 
			
		||||
#include "util/Assert.hpp"
 | 
			
		||||
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
/** @file */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
 
 | 
			
		||||
@@ -26,12 +26,12 @@
 | 
			
		||||
#include "feed/SubscriptionManagerInterface.hpp"
 | 
			
		||||
#include "util/Assert.hpp"
 | 
			
		||||
#include "util/Constants.hpp"
 | 
			
		||||
#include "util/config/Config.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
#include "util/newconfig/ConfigDefinition.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/io_context.hpp>
 | 
			
		||||
#include <ripple/beast/core/CurrentThreadName.h>
 | 
			
		||||
#include <ripple/protocol/LedgerHeader.h>
 | 
			
		||||
#include <xrpl/beast/core/CurrentThreadName.h>
 | 
			
		||||
#include <xrpl/protocol/LedgerHeader.h>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
@@ -262,7 +262,7 @@ ETLService::doWork()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ETLService::ETLService(
 | 
			
		||||
    util::Config const& config,
 | 
			
		||||
    util::config::ClioConfigDefinition const& config,
 | 
			
		||||
    boost::asio::io_context& ioc,
 | 
			
		||||
    std::shared_ptr<BackendInterface> backend,
 | 
			
		||||
    std::shared_ptr<feed::SubscriptionManagerInterface> subscriptions,
 | 
			
		||||
@@ -280,9 +280,9 @@ ETLService::ETLService(
 | 
			
		||||
{
 | 
			
		||||
    startSequence_ = config.maybeValue<uint32_t>("start_sequence");
 | 
			
		||||
    finishSequence_ = config.maybeValue<uint32_t>("finish_sequence");
 | 
			
		||||
    state_.isReadOnly = config.valueOr("read_only", static_cast<bool>(state_.isReadOnly));
 | 
			
		||||
    extractorThreads_ = config.valueOr<uint32_t>("extractor_threads", extractorThreads_);
 | 
			
		||||
    txnThreshold_ = config.valueOr<size_t>("txn_threshold", txnThreshold_);
 | 
			
		||||
    state_.isReadOnly = config.get<bool>("read_only");
 | 
			
		||||
    extractorThreads_ = config.get<uint32_t>("extractor_threads");
 | 
			
		||||
    txnThreshold_ = config.get<std::size_t>("txn_threshold");
 | 
			
		||||
 | 
			
		||||
    // This should probably be done in the backend factory but we don't have state available until here
 | 
			
		||||
    backend_->setCorruptionDetector(CorruptionDetector<data::LedgerCache>{state_, backend->cache()});
 | 
			
		||||
 
 | 
			
		||||
@@ -22,11 +22,11 @@
 | 
			
		||||
#include "data/BackendInterface.hpp"
 | 
			
		||||
#include "data/LedgerCache.hpp"
 | 
			
		||||
#include "etl/CacheLoader.hpp"
 | 
			
		||||
#include "etl/ETLHelpers.hpp"
 | 
			
		||||
#include "etl/ETLState.hpp"
 | 
			
		||||
#include "etl/LoadBalancer.hpp"
 | 
			
		||||
#include "etl/NetworkValidatedLedgersInterface.hpp"
 | 
			
		||||
#include "etl/SystemState.hpp"
 | 
			
		||||
#include "etl/impl/AmendmentBlock.hpp"
 | 
			
		||||
#include "etl/impl/AmendmentBlockHandler.hpp"
 | 
			
		||||
#include "etl/impl/ExtractionDataPipe.hpp"
 | 
			
		||||
#include "etl/impl/Extractor.hpp"
 | 
			
		||||
#include "etl/impl/LedgerFetcher.hpp"
 | 
			
		||||
@@ -37,11 +37,10 @@
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/io_context.hpp>
 | 
			
		||||
#include <boost/asio/steady_timer.hpp>
 | 
			
		||||
#include <boost/json/object.hpp>
 | 
			
		||||
#include <grpcpp/grpcpp.h>
 | 
			
		||||
#include <org/xrpl/rpc/v1/get_ledger.pb.h>
 | 
			
		||||
#include <ripple/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
 | 
			
		||||
#include <xrpl/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
@@ -53,9 +52,6 @@
 | 
			
		||||
struct AccountTransactionsData;
 | 
			
		||||
struct NFTTransactionsData;
 | 
			
		||||
struct NFTsData;
 | 
			
		||||
namespace feed {
 | 
			
		||||
class SubscriptionManager;
 | 
			
		||||
}  // namespace feed
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief This namespace contains everything to do with the ETL and ETL sources.
 | 
			
		||||
@@ -85,7 +81,7 @@ class ETLService {
 | 
			
		||||
    using ExtractorType = etl::impl::Extractor<DataPipeType, LedgerFetcherType>;
 | 
			
		||||
    using LedgerLoaderType = etl::impl::LedgerLoader<LoadBalancerType, LedgerFetcherType>;
 | 
			
		||||
    using LedgerPublisherType = etl::impl::LedgerPublisher<CacheType>;
 | 
			
		||||
    using AmendmentBlockHandlerType = etl::impl::AmendmentBlockHandler<>;
 | 
			
		||||
    using AmendmentBlockHandlerType = etl::impl::AmendmentBlockHandler;
 | 
			
		||||
    using TransformerType =
 | 
			
		||||
        etl::impl::Transformer<DataPipeType, LedgerLoaderType, LedgerPublisherType, AmendmentBlockHandlerType>;
 | 
			
		||||
 | 
			
		||||
@@ -123,7 +119,7 @@ public:
 | 
			
		||||
     * @param ledgers The network validated ledgers datastructure
 | 
			
		||||
     */
 | 
			
		||||
    ETLService(
 | 
			
		||||
        util::Config const& config,
 | 
			
		||||
        util::config::ClioConfigDefinition const& config,
 | 
			
		||||
        boost::asio::io_context& ioc,
 | 
			
		||||
        std::shared_ptr<BackendInterface> backend,
 | 
			
		||||
        std::shared_ptr<feed::SubscriptionManagerInterface> subscriptions,
 | 
			
		||||
@@ -146,7 +142,7 @@ public:
 | 
			
		||||
     */
 | 
			
		||||
    static std::shared_ptr<ETLService>
 | 
			
		||||
    make_ETLService(
 | 
			
		||||
        util::Config const& config,
 | 
			
		||||
        util::config::ClioConfigDefinition const& config,
 | 
			
		||||
        boost::asio::io_context& ioc,
 | 
			
		||||
        std::shared_ptr<BackendInterface> backend,
 | 
			
		||||
        std::shared_ptr<feed::SubscriptionManagerInterface> subscriptions,
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@
 | 
			
		||||
#include <boost/json/conversion.hpp>
 | 
			
		||||
#include <boost/json/value.hpp>
 | 
			
		||||
#include <boost/json/value_to.hpp>
 | 
			
		||||
#include <ripple/protocol/jss.h>
 | 
			
		||||
#include <xrpl/protocol/jss.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <optional>
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@
 | 
			
		||||
 | 
			
		||||
#include <boost/json.hpp>
 | 
			
		||||
#include <boost/json/conversion.hpp>
 | 
			
		||||
#include <boost/json/object.hpp>
 | 
			
		||||
#include <boost/json/value.hpp>
 | 
			
		||||
#include <boost/json/value_to.hpp>
 | 
			
		||||
 | 
			
		||||
@@ -46,8 +47,11 @@ struct ETLState {
 | 
			
		||||
    static std::optional<ETLState>
 | 
			
		||||
    fetchETLStateFromSource(Forward& source) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        auto const serverInfoRippled = data::synchronous([&source](auto yield) {
 | 
			
		||||
            return source.forwardToRippled({{"command", "server_info"}}, std::nullopt, {}, yield);
 | 
			
		||||
        auto const serverInfoRippled = data::synchronous([&source](auto yield) -> std::optional<boost::json::object> {
 | 
			
		||||
            if (auto result = source.forwardToRippled({{"command", "server_info"}}, std::nullopt, {}, yield)) {
 | 
			
		||||
                return std::move(result).value();
 | 
			
		||||
            }
 | 
			
		||||
            return std::nullopt;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (serverInfoRippled)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										65
									
								
								src/etl/LedgerFetcherInterface.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/etl/LedgerFetcherInterface.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2024, 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.
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
/** @file */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <xrpl/proto/org/xrpl/rpc/v1/get_ledger.pb.h>
 | 
			
		||||
#include <xrpl/proto/org/xrpl/rpc/v1/ledger.pb.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <optional>
 | 
			
		||||
 | 
			
		||||
namespace etl {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief An interface for LedgerFetcher
 | 
			
		||||
 */
 | 
			
		||||
struct LedgerFetcherInterface {
 | 
			
		||||
    using GetLedgerResponseType = org::xrpl::rpc::v1::GetLedgerResponse;
 | 
			
		||||
    using OptionalGetLedgerResponseType = std::optional<GetLedgerResponseType>;
 | 
			
		||||
 | 
			
		||||
    virtual ~LedgerFetcherInterface() = default;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Extract data for a particular ledger from an ETL source
 | 
			
		||||
     *
 | 
			
		||||
     * This function continously tries to extract the specified ledger (using all available ETL sources) until the
 | 
			
		||||
     * extraction succeeds, or the server shuts down.
 | 
			
		||||
     *
 | 
			
		||||
     * @param seq sequence of the ledger to extract
 | 
			
		||||
     * @return Ledger header and transaction+metadata blobs; Empty optional if the server is shutting down
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] virtual OptionalGetLedgerResponseType
 | 
			
		||||
    fetchData(uint32_t seq) = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Extract diff data for a particular ledger from an ETL source.
 | 
			
		||||
     *
 | 
			
		||||
     * This function continously tries to extract the specified ledger (using all available ETL sources) until the
 | 
			
		||||
     * extraction succeeds, or the server shuts down.
 | 
			
		||||
     *
 | 
			
		||||
     * @param seq sequence of the ledger to extract
 | 
			
		||||
     * @return Ledger data diff between sequance and parent; Empty optional if the server is shutting down
 | 
			
		||||
     */
 | 
			
		||||
    [[nodiscard]] virtual OptionalGetLedgerResponseType
 | 
			
		||||
    fetchDataAndDiff(uint32_t seq) = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace etl
 | 
			
		||||
@@ -24,21 +24,28 @@
 | 
			
		||||
#include "etl/NetworkValidatedLedgersInterface.hpp"
 | 
			
		||||
#include "etl/Source.hpp"
 | 
			
		||||
#include "feed/SubscriptionManagerInterface.hpp"
 | 
			
		||||
#include "rpc/Errors.hpp"
 | 
			
		||||
#include "util/Assert.hpp"
 | 
			
		||||
#include "util/Random.hpp"
 | 
			
		||||
#include "util/ResponseExpirationCache.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
#include "util/newconfig/ArrayView.hpp"
 | 
			
		||||
#include "util/newconfig/ConfigDefinition.hpp"
 | 
			
		||||
#include "util/newconfig/ObjectView.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/io_context.hpp>
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
#include <boost/json/array.hpp>
 | 
			
		||||
#include <boost/json/object.hpp>
 | 
			
		||||
#include <boost/json/value.hpp>
 | 
			
		||||
#include <boost/json/value_to.hpp>
 | 
			
		||||
#include <fmt/core.h>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <expected>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
@@ -47,13 +54,13 @@
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
using namespace util;
 | 
			
		||||
using namespace util::config;
 | 
			
		||||
 | 
			
		||||
namespace etl {
 | 
			
		||||
 | 
			
		||||
std::shared_ptr<LoadBalancer>
 | 
			
		||||
LoadBalancer::make_LoadBalancer(
 | 
			
		||||
    Config const& config,
 | 
			
		||||
    ClioConfigDefinition const& config,
 | 
			
		||||
    boost::asio::io_context& ioc,
 | 
			
		||||
    std::shared_ptr<BackendInterface> backend,
 | 
			
		||||
    std::shared_ptr<feed::SubscriptionManagerInterface> subscriptions,
 | 
			
		||||
@@ -67,7 +74,7 @@ LoadBalancer::make_LoadBalancer(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LoadBalancer::LoadBalancer(
 | 
			
		||||
    Config const& config,
 | 
			
		||||
    ClioConfigDefinition const& config,
 | 
			
		||||
    boost::asio::io_context& ioc,
 | 
			
		||||
    std::shared_ptr<BackendInterface> backend,
 | 
			
		||||
    std::shared_ptr<feed::SubscriptionManagerInterface> subscriptions,
 | 
			
		||||
@@ -75,20 +82,23 @@ LoadBalancer::LoadBalancer(
 | 
			
		||||
    SourceFactory sourceFactory
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
    auto const forwardingCacheTimeout = config.valueOr<float>("forwarding.cache_timeout", 0.f);
 | 
			
		||||
    auto const forwardingCacheTimeout = config.get<float>("forwarding.cache_timeout");
 | 
			
		||||
    if (forwardingCacheTimeout > 0.f) {
 | 
			
		||||
        forwardingCache_ = impl::ForwardingCache{Config::toMilliseconds(forwardingCacheTimeout)};
 | 
			
		||||
        forwardingCache_ = util::ResponseExpirationCache{
 | 
			
		||||
            util::config::ClioConfigDefinition::toMilliseconds(forwardingCacheTimeout),
 | 
			
		||||
            {"server_info", "server_state", "server_definitions", "fee", "ledger_closed"}
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static constexpr std::uint32_t MAX_DOWNLOAD = 256;
 | 
			
		||||
    if (auto value = config.maybeValue<uint32_t>("num_markers"); value) {
 | 
			
		||||
        ASSERT(*value > 0 and *value <= MAX_DOWNLOAD, "'num_markers' value in config must be in range 1-256");
 | 
			
		||||
        downloadRanges_ = *value;
 | 
			
		||||
    auto const numMarkers = config.getValueView("num_markers");
 | 
			
		||||
    if (numMarkers.hasValue()) {
 | 
			
		||||
        auto const value = numMarkers.asIntType<uint32_t>();
 | 
			
		||||
        downloadRanges_ = value;
 | 
			
		||||
    } else if (backend->fetchLedgerRange()) {
 | 
			
		||||
        downloadRanges_ = 4;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto const allowNoEtl = config.valueOr("allow_no_etl", false);
 | 
			
		||||
    auto const allowNoEtl = config.get<bool>("allow_no_etl");
 | 
			
		||||
 | 
			
		||||
    auto const checkOnETLFailure = [this, allowNoEtl](std::string const& log) {
 | 
			
		||||
        LOG(log_.warn()) << log;
 | 
			
		||||
@@ -99,20 +109,25 @@ LoadBalancer::LoadBalancer(
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    auto const forwardingTimeout = Config::toMilliseconds(config.valueOr<float>("forwarding.request_timeout", 10.));
 | 
			
		||||
    for (auto const& entry : config.array("etl_sources")) {
 | 
			
		||||
    auto const forwardingTimeout =
 | 
			
		||||
        ClioConfigDefinition::toMilliseconds(config.get<float>("forwarding.request_timeout"));
 | 
			
		||||
    auto const etlArray = config.getArray("etl_sources");
 | 
			
		||||
    for (auto it = etlArray.begin<ObjectView>(); it != etlArray.end<ObjectView>(); ++it) {
 | 
			
		||||
        auto source = sourceFactory(
 | 
			
		||||
            entry,
 | 
			
		||||
            *it,
 | 
			
		||||
            ioc,
 | 
			
		||||
            backend,
 | 
			
		||||
            subscriptions,
 | 
			
		||||
            validatedLedgers,
 | 
			
		||||
            forwardingTimeout,
 | 
			
		||||
            [this]() {
 | 
			
		||||
                if (not hasForwardingSource_)
 | 
			
		||||
                if (not hasForwardingSource_.lock().get())
 | 
			
		||||
                    chooseForwardingSource();
 | 
			
		||||
            },
 | 
			
		||||
            [this](bool wasForwarding) {
 | 
			
		||||
                if (wasForwarding)
 | 
			
		||||
                    chooseForwardingSource();
 | 
			
		||||
            },
 | 
			
		||||
            [this]() { chooseForwardingSource(); },
 | 
			
		||||
            [this]() {
 | 
			
		||||
                if (forwardingCache_.has_value())
 | 
			
		||||
                    forwardingCache_->invalidate();
 | 
			
		||||
@@ -211,7 +226,7 @@ LoadBalancer::fetchLedger(
 | 
			
		||||
    return response;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<boost::json::object>
 | 
			
		||||
std::expected<boost::json::object, rpc::ClioError>
 | 
			
		||||
LoadBalancer::forwardToRippled(
 | 
			
		||||
    boost::json::object const& request,
 | 
			
		||||
    std::optional<std::string> const& clientIp,
 | 
			
		||||
@@ -219,9 +234,13 @@ LoadBalancer::forwardToRippled(
 | 
			
		||||
    boost::asio::yield_context yield
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
    if (not request.contains("command"))
 | 
			
		||||
        return std::unexpected{rpc::ClioError::rpcCOMMAND_IS_MISSING};
 | 
			
		||||
 | 
			
		||||
    auto const cmd = boost::json::value_to<std::string>(request.at("command"));
 | 
			
		||||
    if (forwardingCache_) {
 | 
			
		||||
        if (auto cachedResponse = forwardingCache_->get(request); cachedResponse) {
 | 
			
		||||
            return cachedResponse;
 | 
			
		||||
        if (auto cachedResponse = forwardingCache_->get(cmd); cachedResponse) {
 | 
			
		||||
            return std::move(cachedResponse).value();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -233,20 +252,26 @@ LoadBalancer::forwardToRippled(
 | 
			
		||||
    auto xUserValue = isAdmin ? ADMIN_FORWARDING_X_USER_VALUE : USER_FORWARDING_X_USER_VALUE;
 | 
			
		||||
 | 
			
		||||
    std::optional<boost::json::object> response;
 | 
			
		||||
    rpc::ClioError error = rpc::ClioError::etlCONNECTION_ERROR;
 | 
			
		||||
    while (numAttempts < sources_.size()) {
 | 
			
		||||
        if (auto res = sources_[sourceIdx]->forwardToRippled(request, clientIp, xUserValue, yield)) {
 | 
			
		||||
            response = std::move(res);
 | 
			
		||||
        auto res = sources_[sourceIdx]->forwardToRippled(request, clientIp, xUserValue, yield);
 | 
			
		||||
        if (res) {
 | 
			
		||||
            response = std::move(res).value();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        error = std::max(error, res.error());  // Choose the best result between all sources
 | 
			
		||||
 | 
			
		||||
        sourceIdx = (sourceIdx + 1) % sources_.size();
 | 
			
		||||
        ++numAttempts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (response and forwardingCache_ and not response->contains("error"))
 | 
			
		||||
        forwardingCache_->put(request, *response);
 | 
			
		||||
    if (response) {
 | 
			
		||||
        if (forwardingCache_ and not response->contains("error"))
 | 
			
		||||
            forwardingCache_->put(cmd, *response);
 | 
			
		||||
        return std::move(response).value();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return response;
 | 
			
		||||
    return std::unexpected{error};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
boost::json::value
 | 
			
		||||
@@ -314,11 +339,13 @@ LoadBalancer::getETLState() noexcept
 | 
			
		||||
void
 | 
			
		||||
LoadBalancer::chooseForwardingSource()
 | 
			
		||||
{
 | 
			
		||||
    hasForwardingSource_ = false;
 | 
			
		||||
    LOG(log_.info()) << "Choosing a new source to forward subscriptions";
 | 
			
		||||
    auto hasForwardingSourceLock = hasForwardingSource_.lock();
 | 
			
		||||
    hasForwardingSourceLock.get() = false;
 | 
			
		||||
    for (auto& source : sources_) {
 | 
			
		||||
        if (not hasForwardingSource_ and source->isConnected()) {
 | 
			
		||||
        if (not hasForwardingSourceLock.get() and source->isConnected()) {
 | 
			
		||||
            source->setForwarding(true);
 | 
			
		||||
            hasForwardingSource_ = true;
 | 
			
		||||
            hasForwardingSourceLock.get() = true;
 | 
			
		||||
        } else {
 | 
			
		||||
            source->setForwarding(false);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -23,10 +23,12 @@
 | 
			
		||||
#include "etl/ETLState.hpp"
 | 
			
		||||
#include "etl/NetworkValidatedLedgersInterface.hpp"
 | 
			
		||||
#include "etl/Source.hpp"
 | 
			
		||||
#include "etl/impl/ForwardingCache.hpp"
 | 
			
		||||
#include "feed/SubscriptionManagerInterface.hpp"
 | 
			
		||||
#include "util/config/Config.hpp"
 | 
			
		||||
#include "rpc/Errors.hpp"
 | 
			
		||||
#include "util/Mutex.hpp"
 | 
			
		||||
#include "util/ResponseExpirationCache.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
#include "util/newconfig/ConfigDefinition.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include <boost/asio/io_context.hpp>
 | 
			
		||||
@@ -36,11 +38,11 @@
 | 
			
		||||
#include <grpcpp/grpcpp.h>
 | 
			
		||||
#include <org/xrpl/rpc/v1/get_ledger.pb.h>
 | 
			
		||||
#include <org/xrpl/rpc/v1/ledger.pb.h>
 | 
			
		||||
#include <ripple/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
 | 
			
		||||
#include <xrpl/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <expected>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <string>
 | 
			
		||||
@@ -67,14 +69,17 @@ private:
 | 
			
		||||
 | 
			
		||||
    util::Logger log_{"ETL"};
 | 
			
		||||
    // Forwarding cache must be destroyed after sources because sources have a callback to invalidate cache
 | 
			
		||||
    std::optional<impl::ForwardingCache> forwardingCache_;
 | 
			
		||||
    std::optional<util::ResponseExpirationCache> forwardingCache_;
 | 
			
		||||
    std::optional<std::string> forwardingXUserValue_;
 | 
			
		||||
 | 
			
		||||
    std::vector<SourcePtr> sources_;
 | 
			
		||||
    std::optional<ETLState> etlState_;
 | 
			
		||||
    std::uint32_t downloadRanges_ =
 | 
			
		||||
        DEFAULT_DOWNLOAD_RANGES; /*< The number of markers to use when downloading initial ledger */
 | 
			
		||||
    std::atomic_bool hasForwardingSource_{false};
 | 
			
		||||
 | 
			
		||||
    // Using mutext instead of atomic_bool because choosing a new source to
 | 
			
		||||
    // forward messages should be done with a mutual exclusion otherwise there will be a race condition
 | 
			
		||||
    util::Mutex<bool> hasForwardingSource_{false};
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /**
 | 
			
		||||
@@ -98,7 +103,7 @@ public:
 | 
			
		||||
     * @param sourceFactory A factory function to create a source
 | 
			
		||||
     */
 | 
			
		||||
    LoadBalancer(
 | 
			
		||||
        util::Config const& config,
 | 
			
		||||
        util::config::ClioConfigDefinition const& config,
 | 
			
		||||
        boost::asio::io_context& ioc,
 | 
			
		||||
        std::shared_ptr<BackendInterface> backend,
 | 
			
		||||
        std::shared_ptr<feed::SubscriptionManagerInterface> subscriptions,
 | 
			
		||||
@@ -119,7 +124,7 @@ public:
 | 
			
		||||
     */
 | 
			
		||||
    static std::shared_ptr<LoadBalancer>
 | 
			
		||||
    make_LoadBalancer(
 | 
			
		||||
        util::Config const& config,
 | 
			
		||||
        util::config::ClioConfigDefinition const& config,
 | 
			
		||||
        boost::asio::io_context& ioc,
 | 
			
		||||
        std::shared_ptr<BackendInterface> backend,
 | 
			
		||||
        std::shared_ptr<feed::SubscriptionManagerInterface> subscriptions,
 | 
			
		||||
@@ -181,9 +186,9 @@ public:
 | 
			
		||||
     * @param clientIp The IP address of the peer, if known
 | 
			
		||||
     * @param isAdmin Whether the request is from an admin
 | 
			
		||||
     * @param yield The coroutine context
 | 
			
		||||
     * @return Response received from rippled node as JSON object on success; nullopt on failure
 | 
			
		||||
     * @return Response received from rippled node as JSON object on success or error on failure
 | 
			
		||||
     */
 | 
			
		||||
    std::optional<boost::json::object>
 | 
			
		||||
    std::expected<boost::json::object, rpc::ClioError>
 | 
			
		||||
    forwardToRippled(
 | 
			
		||||
        boost::json::object const& request,
 | 
			
		||||
        std::optional<std::string> const& clientIp,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										86
									
								
								src/etl/MPTHelpers.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/etl/MPTHelpers.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2024, 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/DBHelpers.hpp"
 | 
			
		||||
 | 
			
		||||
#include <ripple/protocol/STBase.h>
 | 
			
		||||
#include <ripple/protocol/STTx.h>
 | 
			
		||||
#include <ripple/protocol/TxMeta.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/protocol/LedgerFormats.h>
 | 
			
		||||
#include <xrpl/protocol/SField.h>
 | 
			
		||||
#include <xrpl/protocol/STLedgerEntry.h>
 | 
			
		||||
#include <xrpl/protocol/STObject.h>
 | 
			
		||||
#include <xrpl/protocol/Serializer.h>
 | 
			
		||||
#include <xrpl/protocol/TER.h>
 | 
			
		||||
#include <xrpl/protocol/TxFormats.h>
 | 
			
		||||
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace etl {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Get the MPToken created from a transaction
 | 
			
		||||
 *
 | 
			
		||||
 * @param txMeta Transaction metadata
 | 
			
		||||
 * @return MPT and holder account pair
 | 
			
		||||
 */
 | 
			
		||||
std::optional<MPTHolderData>
 | 
			
		||||
getMPTokenAuthorize(ripple::TxMeta const& txMeta)
 | 
			
		||||
{
 | 
			
		||||
    for (ripple::STObject const& node : txMeta.getNodes()) {
 | 
			
		||||
        if (node.getFieldU16(ripple::sfLedgerEntryType) != ripple::ltMPTOKEN)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        if (node.getFName() == ripple::sfCreatedNode) {
 | 
			
		||||
            auto const& newMPT = node.peekAtField(ripple::sfNewFields).downcast<ripple::STObject>();
 | 
			
		||||
            return MPTHolderData{
 | 
			
		||||
                .mptID = newMPT[ripple::sfMPTokenIssuanceID], .holder = newMPT.getAccountID(ripple::sfAccount)
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<MPTHolderData>
 | 
			
		||||
getMPTHolderFromTx(ripple::TxMeta const& txMeta, ripple::STTx const& sttx)
 | 
			
		||||
{
 | 
			
		||||
    if (txMeta.getResultTER() != ripple::tesSUCCESS || sttx.getTxnType() != ripple::TxType::ttMPTOKEN_AUTHORIZE)
 | 
			
		||||
        return {};
 | 
			
		||||
 | 
			
		||||
    return getMPTokenAuthorize(txMeta);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<MPTHolderData>
 | 
			
		||||
getMPTHolderFromObj(std::string const& key, std::string const& blob)
 | 
			
		||||
{
 | 
			
		||||
    ripple::STLedgerEntry const sle =
 | 
			
		||||
        ripple::STLedgerEntry(ripple::SerialIter{blob.data(), blob.size()}, ripple::uint256::fromVoid(key.data()));
 | 
			
		||||
 | 
			
		||||
    if (sle.getFieldU16(ripple::sfLedgerEntryType) != ripple::ltMPTOKEN)
 | 
			
		||||
        return {};
 | 
			
		||||
 | 
			
		||||
    auto const mptIssuanceID = sle[ripple::sfMPTokenIssuanceID];
 | 
			
		||||
    auto const holder = sle.getAccountID(ripple::sfAccount);
 | 
			
		||||
 | 
			
		||||
    return MPTHolderData{.mptID = mptIssuanceID, .holder = holder};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace etl
 | 
			
		||||
							
								
								
									
										50
									
								
								src/etl/MPTHelpers.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/etl/MPTHelpers.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2024, 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.
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
/** @file */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "data/DBHelpers.hpp"
 | 
			
		||||
 | 
			
		||||
#include <ripple/protocol/STTx.h>
 | 
			
		||||
#include <ripple/protocol/TxMeta.h>
 | 
			
		||||
 | 
			
		||||
namespace etl {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Pull MPT data from TX via ETLService.
 | 
			
		||||
 *
 | 
			
		||||
 * @param txMeta Transaction metadata
 | 
			
		||||
 * @param sttx The transaction
 | 
			
		||||
 * @return The MPTIssuanceID and holder pair as a optional
 | 
			
		||||
 */
 | 
			
		||||
std::optional<MPTHolderData>
 | 
			
		||||
getMPTHolderFromTx(ripple::TxMeta const& txMeta, ripple::STTx const& sttx);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Pull MPT data from ledger object via loadInitialLedger.
 | 
			
		||||
 *
 | 
			
		||||
 * @param key The owner key
 | 
			
		||||
 * @param blob Object data as blob
 | 
			
		||||
 * @return The MPTIssuanceID and holder pair as a optional
 | 
			
		||||
 */
 | 
			
		||||
std::optional<MPTHolderData>
 | 
			
		||||
getMPTHolderFromObj(std::string const& key, std::string const& blob);
 | 
			
		||||
 | 
			
		||||
}  // namespace etl
 | 
			
		||||
@@ -20,20 +20,20 @@
 | 
			
		||||
#include "data/DBHelpers.hpp"
 | 
			
		||||
 | 
			
		||||
#include <fmt/core.h>
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <ripple/basics/strHex.h>
 | 
			
		||||
#include <ripple/protocol/AccountID.h>
 | 
			
		||||
#include <ripple/protocol/LedgerFormats.h>
 | 
			
		||||
#include <ripple/protocol/SField.h>
 | 
			
		||||
#include <ripple/protocol/STArray.h>
 | 
			
		||||
#include <ripple/protocol/STBase.h>
 | 
			
		||||
#include <ripple/protocol/STLedgerEntry.h>
 | 
			
		||||
#include <ripple/protocol/STObject.h>
 | 
			
		||||
#include <ripple/protocol/STTx.h>
 | 
			
		||||
#include <ripple/protocol/Serializer.h>
 | 
			
		||||
#include <ripple/protocol/TER.h>
 | 
			
		||||
#include <ripple/protocol/TxFormats.h>
 | 
			
		||||
#include <ripple/protocol/TxMeta.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/basics/strHex.h>
 | 
			
		||||
#include <xrpl/protocol/AccountID.h>
 | 
			
		||||
#include <xrpl/protocol/LedgerFormats.h>
 | 
			
		||||
#include <xrpl/protocol/SField.h>
 | 
			
		||||
#include <xrpl/protocol/STArray.h>
 | 
			
		||||
#include <xrpl/protocol/STBase.h>
 | 
			
		||||
#include <xrpl/protocol/STLedgerEntry.h>
 | 
			
		||||
#include <xrpl/protocol/STObject.h>
 | 
			
		||||
#include <xrpl/protocol/STTx.h>
 | 
			
		||||
#include <xrpl/protocol/Serializer.h>
 | 
			
		||||
#include <xrpl/protocol/TER.h>
 | 
			
		||||
#include <xrpl/protocol/TxFormats.h>
 | 
			
		||||
#include <xrpl/protocol/TxMeta.h>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
@@ -73,9 +73,9 @@ getNFTokenMintData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx)
 | 
			
		||||
        if (node.getFName() == ripple::sfCreatedNode) {
 | 
			
		||||
            ripple::STArray const& toAddNFTs =
 | 
			
		||||
                node.peekAtField(ripple::sfNewFields).downcast<ripple::STObject>().getFieldArray(ripple::sfNFTokens);
 | 
			
		||||
            std::transform(
 | 
			
		||||
                toAddNFTs.begin(),
 | 
			
		||||
                toAddNFTs.end(),
 | 
			
		||||
            std::ranges::transform(
 | 
			
		||||
                toAddNFTs,
 | 
			
		||||
 | 
			
		||||
                std::back_inserter(finalIDs),
 | 
			
		||||
                [](ripple::STObject const& nft) { return nft.getFieldH256(ripple::sfNFTokenID); }
 | 
			
		||||
            );
 | 
			
		||||
@@ -98,29 +98,30 @@ getNFTokenMintData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            ripple::STArray const& toAddNFTs = previousFields.getFieldArray(ripple::sfNFTokens);
 | 
			
		||||
            std::transform(
 | 
			
		||||
                toAddNFTs.begin(),
 | 
			
		||||
                toAddNFTs.end(),
 | 
			
		||||
            std::ranges::transform(
 | 
			
		||||
                toAddNFTs,
 | 
			
		||||
 | 
			
		||||
                std::back_inserter(prevIDs),
 | 
			
		||||
                [](ripple::STObject const& nft) { return nft.getFieldH256(ripple::sfNFTokenID); }
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            ripple::STArray const& toAddFinalNFTs =
 | 
			
		||||
                node.peekAtField(ripple::sfFinalFields).downcast<ripple::STObject>().getFieldArray(ripple::sfNFTokens);
 | 
			
		||||
            std::transform(
 | 
			
		||||
                toAddFinalNFTs.begin(),
 | 
			
		||||
                toAddFinalNFTs.end(),
 | 
			
		||||
            std::ranges::transform(
 | 
			
		||||
                toAddFinalNFTs,
 | 
			
		||||
 | 
			
		||||
                std::back_inserter(finalIDs),
 | 
			
		||||
                [](ripple::STObject const& nft) { return nft.getFieldH256(ripple::sfNFTokenID); }
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::sort(finalIDs.begin(), finalIDs.end());
 | 
			
		||||
    std::sort(prevIDs.begin(), prevIDs.end());
 | 
			
		||||
    std::ranges::sort(finalIDs);
 | 
			
		||||
    std::ranges::sort(prevIDs);
 | 
			
		||||
 | 
			
		||||
    // Find the first NFT ID that doesn't match.  We're looking for an
 | 
			
		||||
    // added NFT, so the one we want will be the mismatch in finalIDs.
 | 
			
		||||
    // NOLINTNEXTLINE(modernize-use-ranges)
 | 
			
		||||
    auto const diff = std::mismatch(finalIDs.begin(), finalIDs.end(), prevIDs.begin(), prevIDs.end());
 | 
			
		||||
 | 
			
		||||
    // There should always be a difference so the returned finalIDs
 | 
			
		||||
@@ -261,7 +262,7 @@ getNFTokenAcceptOfferData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx
 | 
			
		||||
                .getFieldArray(ripple::sfNFTokens);
 | 
			
		||||
        }();
 | 
			
		||||
 | 
			
		||||
        auto const nft = std::find_if(nfts.begin(), nfts.end(), [&tokenID](ripple::STObject const& candidate) {
 | 
			
		||||
        auto const nft = std::ranges::find_if(nfts, [&tokenID](ripple::STObject const& candidate) {
 | 
			
		||||
            return candidate.getFieldH256(ripple::sfNFTokenID) == tokenID;
 | 
			
		||||
        });
 | 
			
		||||
        if (nft != nfts.end()) {
 | 
			
		||||
@@ -294,16 +295,14 @@ getNFTokenCancelOfferData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx
 | 
			
		||||
        txs.emplace_back(tokenID, txMeta, sttx.getTransactionID());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Deduplicate any transactions based on tokenID/txIdx combo. Can't just
 | 
			
		||||
    // use txIdx because in this case one tx can cancel offers for several
 | 
			
		||||
    // NFTs.
 | 
			
		||||
    std::sort(txs.begin(), txs.end(), [](NFTTransactionsData const& a, NFTTransactionsData const& b) {
 | 
			
		||||
        return a.tokenID < b.tokenID && a.transactionIndex < b.transactionIndex;
 | 
			
		||||
    // Deduplicate any transactions based on tokenID
 | 
			
		||||
    std::ranges::sort(txs, [](NFTTransactionsData const& a, NFTTransactionsData const& b) {
 | 
			
		||||
        return a.tokenID < b.tokenID;
 | 
			
		||||
    });
 | 
			
		||||
    auto last = std::unique(txs.begin(), txs.end(), [](NFTTransactionsData const& a, NFTTransactionsData const& b) {
 | 
			
		||||
        return a.tokenID == b.tokenID && a.transactionIndex == b.transactionIndex;
 | 
			
		||||
    auto [last, end] = std::ranges::unique(txs, [](NFTTransactionsData const& a, NFTTransactionsData const& b) {
 | 
			
		||||
        return a.tokenID == b.tokenID;
 | 
			
		||||
    });
 | 
			
		||||
    txs.erase(last, txs.end());
 | 
			
		||||
    txs.erase(last, end);
 | 
			
		||||
    return {txs, {}};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -358,4 +357,20 @@ getNFTDataFromObj(std::uint32_t const seq, std::string const& key, std::string c
 | 
			
		||||
 | 
			
		||||
    return nfts;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<NFTsData>
 | 
			
		||||
getUniqueNFTsDatas(std::vector<NFTsData> const& nfts)
 | 
			
		||||
{
 | 
			
		||||
    std::vector<NFTsData> results = nfts;
 | 
			
		||||
 | 
			
		||||
    std::ranges::sort(results, [](NFTsData const& a, NFTsData const& b) {
 | 
			
		||||
        return a.tokenID == b.tokenID ? a.transactionIndex > b.transactionIndex : a.tokenID > b.tokenID;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    auto const [last, end] =
 | 
			
		||||
        std::ranges::unique(results, [](NFTsData const& a, NFTsData const& b) { return a.tokenID == b.tokenID; });
 | 
			
		||||
    results.erase(last, end);
 | 
			
		||||
    return results;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace etl
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,8 @@
 | 
			
		||||
 | 
			
		||||
#include "data/DBHelpers.hpp"
 | 
			
		||||
 | 
			
		||||
#include <ripple/protocol/STTx.h>
 | 
			
		||||
#include <ripple/protocol/TxMeta.h>
 | 
			
		||||
#include <xrpl/protocol/STTx.h>
 | 
			
		||||
#include <xrpl/protocol/TxMeta.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <optional>
 | 
			
		||||
@@ -104,4 +104,14 @@ getNFTDataFromTx(ripple::TxMeta const& txMeta, ripple::STTx const& sttx);
 | 
			
		||||
std::vector<NFTsData>
 | 
			
		||||
getNFTDataFromObj(std::uint32_t seq, std::string const& key, std::string const& blob);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Get the unique NFTs data from a vector of NFTsData happening in the same ledger. For example, if a NFT has
 | 
			
		||||
 * both accept offer and burn happening in the same ledger,we only keep the final state of the NFT.
 | 
			
		||||
 | 
			
		||||
 * @param nfts The NFTs data to filter, happening in the same ledger
 | 
			
		||||
 * @return The unique NFTs data
 | 
			
		||||
 */
 | 
			
		||||
std::vector<NFTsData>
 | 
			
		||||
getUniqueNFTsDatas(std::vector<NFTsData> const& nfts);
 | 
			
		||||
 | 
			
		||||
}  // namespace etl
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@
 | 
			
		||||
#include "etl/impl/SourceImpl.hpp"
 | 
			
		||||
#include "etl/impl/SubscriptionSource.hpp"
 | 
			
		||||
#include "feed/SubscriptionManagerInterface.hpp"
 | 
			
		||||
#include "util/config/Config.hpp"
 | 
			
		||||
#include "util/newconfig/ObjectView.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/io_context.hpp>
 | 
			
		||||
 | 
			
		||||
@@ -39,7 +39,7 @@ namespace etl {
 | 
			
		||||
 | 
			
		||||
SourcePtr
 | 
			
		||||
make_Source(
 | 
			
		||||
    util::Config const& config,
 | 
			
		||||
    util::config::ObjectView const& config,
 | 
			
		||||
    boost::asio::io_context& ioc,
 | 
			
		||||
    std::shared_ptr<BackendInterface> backend,
 | 
			
		||||
    std::shared_ptr<feed::SubscriptionManagerInterface> subscriptions,
 | 
			
		||||
@@ -50,9 +50,9 @@ make_Source(
 | 
			
		||||
    SourceBase::OnLedgerClosedHook onLedgerClosed
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
    auto const ip = config.valueOr<std::string>("ip", {});
 | 
			
		||||
    auto const wsPort = config.valueOr<std::string>("ws_port", {});
 | 
			
		||||
    auto const grpcPort = config.valueOr<std::string>("grpc_port", {});
 | 
			
		||||
    auto const ip = config.get<std::string>("ip");
 | 
			
		||||
    auto const wsPort = config.get<std::string>("ws_port");
 | 
			
		||||
    auto const grpcPort = config.get<std::string>("grpc_port");
 | 
			
		||||
 | 
			
		||||
    impl::ForwardingSource forwardingSource{ip, wsPort, forwardingTimeout};
 | 
			
		||||
    impl::GrpcSource grpcSource{ip, grpcPort, std::move(backend)};
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,10 @@
 | 
			
		||||
#include "data/BackendInterface.hpp"
 | 
			
		||||
#include "etl/NetworkValidatedLedgersInterface.hpp"
 | 
			
		||||
#include "feed/SubscriptionManagerInterface.hpp"
 | 
			
		||||
#include "util/config/Config.hpp"
 | 
			
		||||
#include "rpc/Errors.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
#include "util/newconfig/ConfigDefinition.hpp"
 | 
			
		||||
#include "util/newconfig/ObjectView.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/io_context.hpp>
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
@@ -34,6 +36,7 @@
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <expected>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <optional>
 | 
			
		||||
@@ -51,7 +54,7 @@ namespace etl {
 | 
			
		||||
class SourceBase {
 | 
			
		||||
public:
 | 
			
		||||
    using OnConnectHook = std::function<void()>;
 | 
			
		||||
    using OnDisconnectHook = std::function<void()>;
 | 
			
		||||
    using OnDisconnectHook = std::function<void(bool)>;
 | 
			
		||||
    using OnLedgerClosedHook = std::function<void()>;
 | 
			
		||||
 | 
			
		||||
    virtual ~SourceBase() = default;
 | 
			
		||||
@@ -131,9 +134,9 @@ public:
 | 
			
		||||
     * @param forwardToRippledClientIp IP of the client forwarding this request if known
 | 
			
		||||
     * @param xUserValue Value of the X-User header
 | 
			
		||||
     * @param yield The coroutine context
 | 
			
		||||
     * @return Response wrapped in an optional on success; nullopt otherwise
 | 
			
		||||
     * @return Response on success or error on failure
 | 
			
		||||
     */
 | 
			
		||||
    virtual std::optional<boost::json::object>
 | 
			
		||||
    virtual std::expected<boost::json::object, rpc::ClioError>
 | 
			
		||||
    forwardToRippled(
 | 
			
		||||
        boost::json::object const& request,
 | 
			
		||||
        std::optional<std::string> const& forwardToRippledClientIp,
 | 
			
		||||
@@ -145,7 +148,7 @@ public:
 | 
			
		||||
using SourcePtr = std::unique_ptr<SourceBase>;
 | 
			
		||||
 | 
			
		||||
using SourceFactory = std::function<SourcePtr(
 | 
			
		||||
    util::Config const& config,
 | 
			
		||||
    util::config::ObjectView const& config,
 | 
			
		||||
    boost::asio::io_context& ioc,
 | 
			
		||||
    std::shared_ptr<BackendInterface> backend,
 | 
			
		||||
    std::shared_ptr<feed::SubscriptionManagerInterface> subscriptions,
 | 
			
		||||
@@ -173,7 +176,7 @@ using SourceFactory = std::function<SourcePtr(
 | 
			
		||||
 */
 | 
			
		||||
SourcePtr
 | 
			
		||||
make_Source(
 | 
			
		||||
    util::Config const& config,
 | 
			
		||||
    util::config::ObjectView const& config,
 | 
			
		||||
    boost::asio::io_context& ioc,
 | 
			
		||||
    std::shared_ptr<BackendInterface> backend,
 | 
			
		||||
    std::shared_ptr<feed::SubscriptionManagerInterface> subscriptions,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,95 +0,0 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2023, 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 "etl/SystemState.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/io_context.hpp>
 | 
			
		||||
#include <boost/asio/post.hpp>
 | 
			
		||||
#include <boost/asio/steady_timer.hpp>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <functional>
 | 
			
		||||
 | 
			
		||||
namespace etl::impl {
 | 
			
		||||
 | 
			
		||||
struct AmendmentBlockAction {
 | 
			
		||||
    void
 | 
			
		||||
    operator()()
 | 
			
		||||
    {
 | 
			
		||||
        static util::Logger const log{"ETL"};
 | 
			
		||||
        LOG(log.fatal()) << "Can't process new ledgers: The current ETL source is not compatible with the version of "
 | 
			
		||||
                         << "the libxrpl Clio is currently using. Please upgrade Clio to a newer version.";
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename ActionCallableType = AmendmentBlockAction>
 | 
			
		||||
class AmendmentBlockHandler {
 | 
			
		||||
    std::reference_wrapper<boost::asio::io_context> ctx_;
 | 
			
		||||
    std::reference_wrapper<SystemState> state_;
 | 
			
		||||
    boost::asio::steady_timer timer_;
 | 
			
		||||
    std::chrono::milliseconds interval_;
 | 
			
		||||
 | 
			
		||||
    ActionCallableType action_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    template <typename DurationType = std::chrono::seconds>
 | 
			
		||||
    AmendmentBlockHandler(
 | 
			
		||||
        boost::asio::io_context& ioc,
 | 
			
		||||
        SystemState& state,
 | 
			
		||||
        DurationType interval = DurationType{1},
 | 
			
		||||
        ActionCallableType&& action = ActionCallableType()
 | 
			
		||||
    )
 | 
			
		||||
        : ctx_{std::ref(ioc)}
 | 
			
		||||
        , state_{std::ref(state)}
 | 
			
		||||
        , timer_{ioc}
 | 
			
		||||
        , interval_{std::chrono::duration_cast<std::chrono::milliseconds>(interval)}
 | 
			
		||||
        , action_{std::move(action)}
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~AmendmentBlockHandler()
 | 
			
		||||
    {
 | 
			
		||||
        boost::asio::post(ctx_.get(), [this]() { timer_.cancel(); });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    onAmendmentBlock()
 | 
			
		||||
    {
 | 
			
		||||
        state_.get().isAmendmentBlocked = true;
 | 
			
		||||
        startReportingTimer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void
 | 
			
		||||
    startReportingTimer()
 | 
			
		||||
    {
 | 
			
		||||
        action_();
 | 
			
		||||
 | 
			
		||||
        timer_.expires_after(interval_);
 | 
			
		||||
        timer_.async_wait([this](auto ec) {
 | 
			
		||||
            if (!ec)
 | 
			
		||||
                boost::asio::post(ctx_.get(), [this] { startReportingTimer(); });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace etl::impl
 | 
			
		||||
							
								
								
									
										56
									
								
								src/etl/impl/AmendmentBlockHandler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/etl/impl/AmendmentBlockHandler.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2024, 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 "etl/impl/AmendmentBlockHandler.hpp"
 | 
			
		||||
 | 
			
		||||
#include "etl/SystemState.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/io_context.hpp>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
namespace etl::impl {
 | 
			
		||||
 | 
			
		||||
AmendmentBlockHandler::ActionType const AmendmentBlockHandler::defaultAmendmentBlockAction = []() {
 | 
			
		||||
    static util::Logger const log{"ETL"};
 | 
			
		||||
    LOG(log.fatal()) << "Can't process new ledgers: The current ETL source is not compatible with the version of "
 | 
			
		||||
                     << "the libxrpl Clio is currently using. Please upgrade Clio to a newer version.";
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
AmendmentBlockHandler::AmendmentBlockHandler(
 | 
			
		||||
    boost::asio::io_context& ioc,
 | 
			
		||||
    SystemState& state,
 | 
			
		||||
    std::chrono::steady_clock::duration interval,
 | 
			
		||||
    ActionType action
 | 
			
		||||
)
 | 
			
		||||
    : state_{std::ref(state)}, repeat_{ioc}, interval_{interval}, action_{std::move(action)}
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
AmendmentBlockHandler::onAmendmentBlock()
 | 
			
		||||
{
 | 
			
		||||
    state_.get().isAmendmentBlocked = true;
 | 
			
		||||
    repeat_.start(interval_, action_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace etl::impl
 | 
			
		||||
							
								
								
									
										59
									
								
								src/etl/impl/AmendmentBlockHandler.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/etl/impl/AmendmentBlockHandler.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2023, 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 "etl/SystemState.hpp"
 | 
			
		||||
#include "util/Repeat.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/io_context.hpp>
 | 
			
		||||
#include <boost/asio/post.hpp>
 | 
			
		||||
#include <boost/asio/steady_timer.hpp>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <functional>
 | 
			
		||||
 | 
			
		||||
namespace etl::impl {
 | 
			
		||||
 | 
			
		||||
class AmendmentBlockHandler {
 | 
			
		||||
public:
 | 
			
		||||
    using ActionType = std::function<void()>;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::reference_wrapper<SystemState> state_;
 | 
			
		||||
    util::Repeat repeat_;
 | 
			
		||||
    std::chrono::steady_clock::duration interval_;
 | 
			
		||||
 | 
			
		||||
    ActionType action_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    static ActionType const defaultAmendmentBlockAction;
 | 
			
		||||
 | 
			
		||||
    AmendmentBlockHandler(
 | 
			
		||||
        boost::asio::io_context& ioc,
 | 
			
		||||
        SystemState& state,
 | 
			
		||||
        std::chrono::steady_clock::duration interval = std::chrono::seconds{1},
 | 
			
		||||
        ActionType action = defaultAmendmentBlockAction
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    onAmendmentBlock();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace etl::impl
 | 
			
		||||
@@ -22,6 +22,7 @@
 | 
			
		||||
#include "data/BackendInterface.hpp"
 | 
			
		||||
#include "data/Types.hpp"
 | 
			
		||||
#include "etl/ETLHelpers.hpp"
 | 
			
		||||
#include "etl/MPTHelpers.hpp"
 | 
			
		||||
#include "etl/NFTHelpers.hpp"
 | 
			
		||||
#include "util/Assert.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
@@ -30,9 +31,9 @@
 | 
			
		||||
#include <grpcpp/grpcpp.h>
 | 
			
		||||
#include <grpcpp/support/status.h>
 | 
			
		||||
#include <org/xrpl/rpc/v1/get_ledger_data.pb.h>
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <ripple/basics/strHex.h>
 | 
			
		||||
#include <ripple/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/basics/strHex.h>
 | 
			
		||||
#include <xrpl/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
@@ -154,6 +155,11 @@ public:
 | 
			
		||||
                    backend.writeSuccessor(std::move(lastKey_), request_.ledger().sequence(), std::string{obj.key()});
 | 
			
		||||
                lastKey_ = obj.key();
 | 
			
		||||
                backend.writeNFTs(getNFTDataFromObj(request_.ledger().sequence(), obj.key(), obj.data()));
 | 
			
		||||
 | 
			
		||||
                auto const maybeMPTHolder = getMPTHolderFromObj(obj.key(), obj.data());
 | 
			
		||||
                if (maybeMPTHolder)
 | 
			
		||||
                    backend.writeMPTHolders({*maybeMPTHolder});
 | 
			
		||||
 | 
			
		||||
                backend.writeLedgerObject(
 | 
			
		||||
                    std::move(*obj.mutable_key()), request_.ledger().sequence(), std::move(*obj.mutable_data())
 | 
			
		||||
                );
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 
 | 
			
		||||
@@ -28,9 +28,9 @@
 | 
			
		||||
 | 
			
		||||
#include <boost/algorithm/string/predicate.hpp>
 | 
			
		||||
#include <boost/context/detail/config.hpp>
 | 
			
		||||
#include <ripple/basics/Blob.h>
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <ripple/basics/strHex.h>
 | 
			
		||||
#include <xrpl/basics/Blob.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/basics/strHex.h>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <atomic>
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@
 | 
			
		||||
#include "data/Types.hpp"
 | 
			
		||||
#include "etl/impl/BaseCursorProvider.hpp"
 | 
			
		||||
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@
 | 
			
		||||
#include "etl/impl/BaseCursorProvider.hpp"
 | 
			
		||||
#include "util/Assert.hpp"
 | 
			
		||||
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
 
 | 
			
		||||
@@ -25,8 +25,8 @@
 | 
			
		||||
 | 
			
		||||
#include <boost/algorithm/string/predicate.hpp>
 | 
			
		||||
#include <boost/asio/io_context.hpp>
 | 
			
		||||
#include <ripple/basics/base_uint.h>
 | 
			
		||||
#include <ripple/basics/strHex.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/basics/strHex.h>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
@@ -70,12 +70,9 @@ public:
 | 
			
		||||
            return a.key < b.key or (a.key == b.key and std::size(a.blob) < std::size(b.blob));
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        diffs.erase(
 | 
			
		||||
            std::unique(
 | 
			
		||||
                std::begin(diffs), std::end(diffs), [](auto const& a, auto const& b) { return a.key == b.key; }
 | 
			
		||||
            ),
 | 
			
		||||
            std::end(diffs)
 | 
			
		||||
        );
 | 
			
		||||
        auto const [removalCursor, last] =
 | 
			
		||||
            rg::unique(diffs, [](auto const& a, auto const& b) { return a.key == b.key; });
 | 
			
		||||
        diffs.erase(removalCursor, last);
 | 
			
		||||
 | 
			
		||||
        std::vector<ripple::uint256> cursors{data::firstKey};
 | 
			
		||||
        rg::copy(
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@
 | 
			
		||||
#include "util/Profiler.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
 | 
			
		||||
#include <ripple/beast/core/CurrentThreadName.h>
 | 
			
		||||
#include <xrpl/beast/core/CurrentThreadName.h>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
@@ -121,19 +121,19 @@ private:
 | 
			
		||||
        pipe_.get().finish(startSequence_);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool
 | 
			
		||||
    [[nodiscard]] bool
 | 
			
		||||
    isStopping() const
 | 
			
		||||
    {
 | 
			
		||||
        return state_.get().isStopping;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool
 | 
			
		||||
    [[nodiscard]] bool
 | 
			
		||||
    hasWriteConflict() const
 | 
			
		||||
    {
 | 
			
		||||
        return state_.get().writeConflict;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool
 | 
			
		||||
    [[nodiscard]] bool
 | 
			
		||||
    shouldFinish(uint32_t seq) const
 | 
			
		||||
    {
 | 
			
		||||
        // Stopping conditions:
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@
 | 
			
		||||
 | 
			
		||||
#include "etl/impl/ForwardingSource.hpp"
 | 
			
		||||
 | 
			
		||||
#include "rpc/Errors.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
@@ -55,7 +56,7 @@ ForwardingSource::ForwardingSource(
 | 
			
		||||
        );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<boost::json::object>
 | 
			
		||||
std::expected<boost::json::object, rpc::ClioError>
 | 
			
		||||
ForwardingSource::forwardToRippled(
 | 
			
		||||
    boost::json::object const& request,
 | 
			
		||||
    std::optional<std::string> const& forwardToRippledClientIp,
 | 
			
		||||
@@ -74,18 +75,26 @@ ForwardingSource::forwardToRippled(
 | 
			
		||||
 | 
			
		||||
    auto expectedConnection = connectionBuilder.connect(yield);
 | 
			
		||||
    if (not expectedConnection) {
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
        LOG(log_.debug()) << "Couldn't connect to rippled to forward request.";
 | 
			
		||||
        return std::unexpected{rpc::ClioError::etlCONNECTION_ERROR};
 | 
			
		||||
    }
 | 
			
		||||
    auto& connection = expectedConnection.value();
 | 
			
		||||
 | 
			
		||||
    auto writeError = connection->write(boost::json::serialize(request), yield, forwardingTimeout_);
 | 
			
		||||
    if (writeError) {
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
        LOG(log_.debug()) << "Error sending request to rippled to forward request.";
 | 
			
		||||
        return std::unexpected{rpc::ClioError::etlREQUEST_ERROR};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto response = connection->read(yield, forwardingTimeout_);
 | 
			
		||||
    if (not response) {
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
        if (auto errorCode = response.error().errorCode();
 | 
			
		||||
            errorCode.has_value() and errorCode->value() == boost::system::errc::timed_out) {
 | 
			
		||||
            LOG(log_.debug()) << "Request to rippled timed out";
 | 
			
		||||
            return std::unexpected{rpc::ClioError::etlREQUEST_TIMEOUT};
 | 
			
		||||
        }
 | 
			
		||||
        LOG(log_.debug()) << "Error sending request to rippled to forward request.";
 | 
			
		||||
        return std::unexpected{rpc::ClioError::etlREQUEST_ERROR};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    boost::json::value parsedResponse;
 | 
			
		||||
@@ -94,8 +103,8 @@ ForwardingSource::forwardToRippled(
 | 
			
		||||
        if (not parsedResponse.is_object())
 | 
			
		||||
            throw std::runtime_error("response is not an object");
 | 
			
		||||
    } catch (std::exception const& e) {
 | 
			
		||||
        LOG(log_.error()) << "Error parsing response from rippled: " << e.what() << ". Response: " << *response;
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
        LOG(log_.debug()) << "Error parsing response from rippled: " << e.what() << ". Response: " << *response;
 | 
			
		||||
        return std::unexpected{rpc::ClioError::etlINVALID_RESPONSE};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto responseObject = parsedResponse.as_object();
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "rpc/Errors.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
#include "util/requests/WsConnection.hpp"
 | 
			
		||||
 | 
			
		||||
@@ -26,6 +27,7 @@
 | 
			
		||||
#include <boost/json/object.hpp>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <expected>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
@@ -54,9 +56,9 @@ public:
 | 
			
		||||
     * @param forwardToRippledClientIp IP of the client forwarding this request if known
 | 
			
		||||
     * @param xUserValue Optional value for X-User header
 | 
			
		||||
     * @param yield The coroutine context
 | 
			
		||||
     * @return Response wrapped in an optional on success; nullopt otherwise
 | 
			
		||||
     * @return Response on success or error on failure
 | 
			
		||||
     */
 | 
			
		||||
    std::optional<boost::json::object>
 | 
			
		||||
    std::expected<boost::json::object, rpc::ClioError>
 | 
			
		||||
    forwardToRippled(
 | 
			
		||||
        boost::json::object const& request,
 | 
			
		||||
        std::optional<std::string> const& forwardToRippledClientIp,
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@
 | 
			
		||||
#include "util/Assert.hpp"
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/ip/address.hpp>
 | 
			
		||||
#include <boost/asio/io_context.hpp>
 | 
			
		||||
#include <boost/asio/ip/tcp.hpp>
 | 
			
		||||
#include <fmt/core.h>
 | 
			
		||||
#include <grpcpp/client_context.h>
 | 
			
		||||
@@ -39,6 +39,7 @@
 | 
			
		||||
#include <exception>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
@@ -46,12 +47,17 @@
 | 
			
		||||
namespace etl::impl {
 | 
			
		||||
 | 
			
		||||
GrpcSource::GrpcSource(std::string const& ip, std::string const& grpcPort, std::shared_ptr<BackendInterface> backend)
 | 
			
		||||
    : log_(fmt::format("ETL_Grpc[{}:{}]", ip, grpcPort)), backend_(std::move(backend))
 | 
			
		||||
    : log_(fmt::format("GrpcSource[{}:{}]", ip, grpcPort)), backend_(std::move(backend))
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        boost::asio::ip::tcp::endpoint const endpoint{boost::asio::ip::make_address(ip), std::stoi(grpcPort)};
 | 
			
		||||
        boost::asio::io_context ctx;
 | 
			
		||||
        boost::asio::ip::tcp::resolver resolver{ctx};
 | 
			
		||||
        auto const resolverResult = resolver.resolve(ip, grpcPort);
 | 
			
		||||
        if (resolverResult.empty()) {
 | 
			
		||||
            throw std::runtime_error("Failed to resolve " + ip + ":" + grpcPort);
 | 
			
		||||
        }
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << endpoint;
 | 
			
		||||
        ss << resolverResult.begin()->endpoint();
 | 
			
		||||
        grpc::ChannelArguments chArgs;
 | 
			
		||||
        chArgs.SetMaxReceiveMessageSize(-1);
 | 
			
		||||
        stub_ = org::xrpl::rpc::v1::XRPLedgerAPIService::NewStub(
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@
 | 
			
		||||
 | 
			
		||||
#include <grpcpp/support/status.h>
 | 
			
		||||
#include <org/xrpl/rpc/v1/get_ledger.pb.h>
 | 
			
		||||
#include <ripple/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
 | 
			
		||||
#include <xrpl/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <memory>
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@
 | 
			
		||||
#include "util/log/Logger.hpp"
 | 
			
		||||
 | 
			
		||||
#include <grpcpp/grpcpp.h>
 | 
			
		||||
#include <ripple/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
 | 
			
		||||
#include <xrpl/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <memory>
 | 
			
		||||
@@ -63,7 +63,7 @@ public:
 | 
			
		||||
     * @param sequence sequence of the ledger to extract
 | 
			
		||||
     * @return Ledger header and transaction+metadata blobs; Empty optional if the server is shutting down
 | 
			
		||||
     */
 | 
			
		||||
    OptionalGetLedgerResponseType
 | 
			
		||||
    [[nodiscard]] OptionalGetLedgerResponseType
 | 
			
		||||
    fetchData(uint32_t sequence)
 | 
			
		||||
    {
 | 
			
		||||
        LOG(log_.debug()) << "Attempting to fetch ledger with sequence = " << sequence;
 | 
			
		||||
@@ -83,7 +83,7 @@ public:
 | 
			
		||||
     * @param sequence sequence of the ledger to extract
 | 
			
		||||
     * @return Ledger data diff between sequance and parent; Empty optional if the server is shutting down
 | 
			
		||||
     */
 | 
			
		||||
    OptionalGetLedgerResponseType
 | 
			
		||||
    [[nodiscard]] OptionalGetLedgerResponseType
 | 
			
		||||
    fetchDataAndDiff(uint32_t sequence)
 | 
			
		||||
    {
 | 
			
		||||
        LOG(log_.debug()) << "Attempting to fetch ledger with sequence = " << sequence;
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user