Add initial Conan integration (#712)

Fixes #645
This commit is contained in:
Alex Kremer
2023-07-24 18:43:02 +01:00
committed by GitHub
parent 2336148d0d
commit c6ca650767
76 changed files with 626 additions and 572 deletions

2
.gitignore vendored
View File

@@ -1,6 +1,8 @@
*clio*.log
build*/
.build
.vscode
.python-version
CMakeUserPresets.json
config.json
src/main/impl/Build.cpp

15
CMake/CheckCompiler.cmake Normal file
View File

@@ -0,0 +1,15 @@
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12)
message(FATAL_ERROR "Clang 12+ required for building clio")
endif()
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12)
message(FATAL_ERROR "AppleClang 12+ required for building clio")
endif()
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11)
message(FATAL_ERROR "GCC 11+ required for building clio")
endif()
else()
message(FATAL_ERROR "Supported compilers: AppleClang 12+, Clang 12+, GCC 11+")
endif()

View File

@@ -1,5 +1,5 @@
# call add_converage(module_name) to add coverage targets for the given module
function(add_converage module)
# call add_coverage(module_name) to add coverage targets for the given module
function(add_coverage module)
if("${CMAKE_C_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang"
OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
message("[Coverage] Building with llvm Code Coverage Tools")
@@ -10,15 +10,21 @@ function(add_converage module)
endif()
# set Flags
target_compile_options(${module} PRIVATE -fprofile-instr-generate
-fcoverage-mapping)
target_link_options(${module} PUBLIC -fprofile-instr-generate
-fcoverage-mapping)
target_compile_options(${module} PRIVATE
-fprofile-instr-generate
-fcoverage-mapping)
target_link_options(${module} PUBLIC
-fprofile-instr-generate
-fcoverage-mapping)
target_compile_options(clio PRIVATE -fprofile-instr-generate
-fcoverage-mapping)
target_link_options(clio PUBLIC -fprofile-instr-generate
-fcoverage-mapping)
target_compile_options(clio PRIVATE
-fprofile-instr-generate
-fcoverage-mapping)
target_link_options(clio PUBLIC
-fprofile-instr-generate
-fcoverage-mapping)
# llvm-cov
add_custom_target(
@@ -60,8 +66,7 @@ function(add_converage module)
COMMENT
"Open ${module}-llvm-cov/index.html in your browser to view the coverage report."
)
elseif("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR "${CMAKE_CXX_COMPILER_ID}"
MATCHES "GNU")
elseif("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
message("[Coverage] Building with Gcc Code Coverage Tools")
find_program(GCOV_PATH gcov)

View File

@@ -1,6 +1,11 @@
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_STATIC_RUNTIME ON)
find_package(Boost 1.75 COMPONENTS filesystem log_setup log thread system REQUIRED)
target_link_libraries(clio PUBLIC ${Boost_LIBRARIES})
find_package(Boost 1.82 REQUIRED
COMPONENTS
program_options
coroutine
system
log
log_setup
)

5
CMake/deps/OpenSSL.cmake Normal file
View File

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

2
CMake/deps/Threads.cmake Normal file
View File

@@ -0,0 +1,2 @@
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads)

View File

@@ -1,153 +1 @@
find_package(ZLIB REQUIRED)
find_library(cassandra NAMES cassandra)
if(NOT cassandra)
message("System installed Cassandra cpp driver not found. Will build")
find_library(zlib NAMES zlib1g-dev zlib-devel zlib z)
if(NOT zlib)
message("zlib not found. will build")
add_library(zlib STATIC IMPORTED GLOBAL)
ExternalProject_Add(zlib_src
PREFIX ${nih_cache_path}
GIT_REPOSITORY https://github.com/madler/zlib.git
GIT_TAG v1.2.12
INSTALL_COMMAND ""
BUILD_BYPRODUCTS <BINARY_DIR>/${CMAKE_STATIC_LIBRARY_PREFIX}z.a
)
ExternalProject_Get_Property (zlib_src SOURCE_DIR)
ExternalProject_Get_Property (zlib_src BINARY_DIR)
set (zlib_src_SOURCE_DIR "${SOURCE_DIR}")
file (MAKE_DIRECTORY ${zlib_src_SOURCE_DIR}/include)
set_target_properties (zlib PROPERTIES
IMPORTED_LOCATION
${BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}z.a
INTERFACE_INCLUDE_DIRECTORIES
${SOURCE_DIR}/include)
add_dependencies(zlib zlib_src)
file(TO_CMAKE_PATH "${zlib_src_SOURCE_DIR}" zlib_src_SOURCE_DIR)
endif()
find_library(krb5 NAMES krb5-dev libkrb5-dev)
if(NOT krb5)
message("krb5 not found. will build")
add_library(krb5 STATIC IMPORTED GLOBAL)
ExternalProject_Add(krb5_src
PREFIX ${nih_cache_path}
GIT_REPOSITORY https://github.com/krb5/krb5.git
GIT_TAG krb5-1.20
UPDATE_COMMAND ""
CONFIGURE_COMMAND autoreconf src && CFLAGS=-fcommon ./src/configure --enable-static --disable-shared
BUILD_IN_SOURCE 1
BUILD_COMMAND make
INSTALL_COMMAND ""
BUILD_BYPRODUCTS <SOURCE_DIR>/lib/${CMAKE_STATIC_LIBRARY_PREFIX}krb5.a
)
message(${ep_lib_prefix}/krb5.a)
message(${CMAKE_STATIC_LIBRARY_PREFIX}krb5.a)
ExternalProject_Get_Property (krb5_src SOURCE_DIR)
ExternalProject_Get_Property (krb5_src BINARY_DIR)
set (krb5_src_SOURCE_DIR "${SOURCE_DIR}")
file (MAKE_DIRECTORY ${krb5_src_SOURCE_DIR}/include)
set_target_properties (krb5 PROPERTIES
IMPORTED_LOCATION
${SOURCE_DIR}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}krb5.a
INTERFACE_INCLUDE_DIRECTORIES
${SOURCE_DIR}/include)
add_dependencies(krb5 krb5_src)
file(TO_CMAKE_PATH "${krb5_src_SOURCE_DIR}" krb5_src_SOURCE_DIR)
endif()
find_library(libuv1 NAMES uv1 libuv1 liubuv1-dev libuv1:amd64)
if(NOT libuv1)
message("libuv1 not found, will build")
add_library(libuv1 STATIC IMPORTED GLOBAL)
ExternalProject_Add(libuv_src
PREFIX ${nih_cache_path}
GIT_REPOSITORY https://github.com/libuv/libuv.git
GIT_TAG v1.44.1
INSTALL_COMMAND ""
BUILD_BYPRODUCTS <BINARY_DIR>/${CMAKE_STATIC_LIBRARY_PREFIX}uv_a.a
)
ExternalProject_Get_Property (libuv_src SOURCE_DIR)
ExternalProject_Get_Property (libuv_src BINARY_DIR)
set (libuv_src_SOURCE_DIR "${SOURCE_DIR}")
file (MAKE_DIRECTORY ${libuv_src_SOURCE_DIR}/include)
set_target_properties (libuv1 PROPERTIES
IMPORTED_LOCATION
${BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}uv_a.a
INTERFACE_INCLUDE_DIRECTORIES
${SOURCE_DIR}/include)
add_dependencies(libuv1 libuv_src)
file(TO_CMAKE_PATH "${libuv_src_SOURCE_DIR}" libuv_src_SOURCE_DIR)
endif()
add_library (cassandra STATIC IMPORTED GLOBAL)
ExternalProject_Add(cassandra_src
PREFIX ${nih_cache_path}
GIT_REPOSITORY https://github.com/datastax/cpp-driver.git
GIT_TAG 2.16.2
CMAKE_ARGS
-DLIBUV_ROOT_DIR=${BINARY_DIR}
-DLIBUV_INCLUDE_DIR=${SOURCE_DIR}/include
-DCASS_BUILD_STATIC=ON
-DCASS_BUILD_SHARED=OFF
INSTALL_COMMAND ""
BUILD_BYPRODUCTS <BINARY_DIR>/${CMAKE_STATIC_LIBRARY_PREFIX}cassandra_static.a
)
ExternalProject_Get_Property (cassandra_src SOURCE_DIR)
ExternalProject_Get_Property (cassandra_src BINARY_DIR)
set (cassandra_src_SOURCE_DIR "${SOURCE_DIR}")
file (MAKE_DIRECTORY ${cassandra_src_SOURCE_DIR}/include)
set_target_properties (cassandra PROPERTIES
IMPORTED_LOCATION
${BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}cassandra_static.a
INTERFACE_INCLUDE_DIRECTORIES
${SOURCE_DIR}/include)
message("cass dirs")
message(${BINARY_DIR})
message(${SOURCE_DIR})
message(${BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}cassandra_static.a)
add_dependencies(cassandra cassandra_src)
if(NOT libuv1)
ExternalProject_Add_StepDependencies(cassandra_src build libuv1)
target_link_libraries(cassandra INTERFACE libuv1)
else()
target_link_libraries(cassandra INTERFACE ${libuv1})
endif()
if(NOT krb5)
ExternalProject_Add_StepDependencies(cassandra_src build krb5)
target_link_libraries(cassandra INTERFACE krb5)
else()
target_link_libraries(cassandra INTERFACE ${krb5})
endif()
if(NOT zlib)
ExternalProject_Add_StepDependencies(cassandra_src build zlib)
target_link_libraries(cassandra INTERFACE zlib)
else()
target_link_libraries(cassandra INTERFACE ${zlib})
endif()
set(OPENSSL_USE_STATIC_LIBS TRUE)
find_package(OpenSSL REQUIRED)
target_link_libraries(cassandra INTERFACE OpenSSL::SSL)
file(TO_CMAKE_PATH "${cassandra_src_SOURCE_DIR}" cassandra_src_SOURCE_DIR)
target_link_libraries(clio PUBLIC cassandra)
else()
message("Found system installed cassandra cpp driver")
message(${cassandra})
find_path(cassandra_includes NAMES cassandra.h REQUIRED)
message(${cassandra_includes})
get_filename_component(CASSANDRA_HEADER ${cassandra_includes}/cassandra.h REALPATH)
get_filename_component(CASSANDRA_HEADER_DIR ${CASSANDRA_HEADER} DIRECTORY)
target_link_libraries (clio PUBLIC ${cassandra})
target_include_directories(clio PUBLIC ${CASSANDRA_HEADER_DIR})
endif()
find_package(cassandra-cpp-driver REQUIRED)

View File

@@ -1,22 +1,4 @@
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
)
FetchContent_GetProperties(googletest)
if(NOT googletest_POPULATED)
FetchContent_Populate(googletest)
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
target_link_libraries(clio_tests PUBLIC clio gmock_main)
target_include_directories(clio_tests PRIVATE unittests)
find_package(gtest REQUIRED)
enable_testing()
include(GoogleTest)
#increase timeout for tests discovery to 10 seconds, by default it is 5s. As more unittests added, we start to hit this issue
#https://github.com/google/googletest/issues/3475
gtest_discover_tests(clio_tests DISCOVERY_TIMEOUT 10)

View File

@@ -1,14 +1 @@
FetchContent_Declare(
libfmt
URL https://github.com/fmtlib/fmt/releases/download/9.1.0/fmt-9.1.0.zip
)
FetchContent_GetProperties(libfmt)
if(NOT libfmt_POPULATED)
FetchContent_Populate(libfmt)
add_subdirectory(${libfmt_SOURCE_DIR} ${libfmt_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
target_link_libraries(clio PUBLIC fmt)
find_package(fmt REQUIRED)

1
CMake/deps/libxrpl.cmake Normal file
View File

@@ -0,0 +1 @@
find_package(xrpl REQUIRED)

View File

@@ -1,20 +0,0 @@
set(RIPPLED_REPO "https://github.com/ripple/rippled.git")
set(RIPPLED_BRANCH "1.9.2")
set(NIH_CACHE_ROOT "${CMAKE_CURRENT_BINARY_DIR}" CACHE INTERNAL "")
set(patch_command ! grep operator!= src/ripple/protocol/Feature.h || git apply < ${CMAKE_CURRENT_SOURCE_DIR}/CMake/deps/Remove-bitset-operator.patch)
message(STATUS "Cloning ${RIPPLED_REPO} branch ${RIPPLED_BRANCH}")
FetchContent_Declare(rippled
GIT_REPOSITORY "${RIPPLED_REPO}"
GIT_TAG "${RIPPLED_BRANCH}"
GIT_SHALLOW ON
PATCH_COMMAND "${patch_command}"
)
FetchContent_GetProperties(rippled)
if(NOT rippled_POPULATED)
FetchContent_Populate(rippled)
add_subdirectory(${rippled_SOURCE_DIR} ${rippled_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
target_link_libraries(clio PUBLIC xrpl_core grpc_pbufs)
target_include_directories(clio PUBLIC ${rippled_SOURCE_DIR}/src ) # TODO: Seems like this shouldn't be needed?

View File

@@ -1,6 +1,7 @@
target_compile_options(clio
PUBLIC -Wall
-Werror
-Wno-narrowing
-Wno-deprecated-declarations
-Wno-dangling-else)
target_compile_options(clio PUBLIC
-Wall
-Werror
-Wno-narrowing
-Wno-deprecated-declarations
-Wno-dangling-else
-Wno-unused-but-set-variable)

View File

@@ -1,21 +1,20 @@
cmake_minimum_required(VERSION 3.16.3)
project(clio)
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11)
message(FATAL_ERROR "GCC 11+ required for building clio")
endif()
# ==================================================== #
# Options #
# ==================================================== #
option(verbose "Verbose build" FALSE)
option(tests "Build tests" FALSE)
option(coverage "Build test coverage report" FALSE)
option(packaging "Create distribution packages" FALSE)
# ==================================================== #
option(BUILD_TESTS "Build tests" TRUE)
option(VERBOSE "Verbose build" TRUE)
if(VERBOSE)
if(verbose)
set(CMAKE_VERBOSE_MAKEFILE TRUE)
set(FETCHCONTENT_QUIET FALSE CACHE STRING "Verbose FetchContent()")
endif()
if(PACKAGING)
if(packaging)
add_definitions(-DPKG=1)
endif()
@@ -23,18 +22,48 @@ endif()
add_definitions(-DBOOST_ASIO_HAS_STD_INVOKE_RESULT=1)
add_library(clio)
target_compile_features(clio PUBLIC cxx_std_20)
target_include_directories(clio PUBLIC src)
include(FetchContent)
include(ExternalProject)
include(CMake/settings.cmake)
# Clio tweaks and checks
include(CMake/CheckCompiler.cmake)
include(CMake/Settings.cmake)
include(CMake/ClioVersion.cmake)
include(CMake/deps/rippled.cmake)
include(CMake/deps/libfmt.cmake)
include(CMake/SourceLocation.cmake)
# Clio deps
include(CMake/deps/libxrpl.cmake)
include(CMake/deps/Boost.cmake)
include(CMake/deps/OpenSSL.cmake)
include(CMake/deps/Threads.cmake)
include(CMake/deps/libfmt.cmake)
include(CMake/deps/cassandra.cmake)
include(CMake/deps/SourceLocation.cmake)
# TODO: Include directory will be wrong when installed.
target_include_directories(clio PUBLIC src)
target_compile_features(clio PUBLIC cxx_std_20)
target_link_libraries(clio
PUBLIC Boost::boost
PUBLIC Boost::coroutine
PUBLIC Boost::program_options
PUBLIC Boost::system
PUBLIC Boost::log
PUBLIC Boost::log_setup
PUBLIC cassandra-cpp-driver::cassandra-cpp-driver
PUBLIC fmt::fmt
PUBLIC OpenSSL::Crypto
PUBLIC OpenSSL::SSL
PUBLIC xrpl::libxrpl
INTERFACE Threads::Threads
)
target_compile_definitions(clio PUBLIC
BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS
BOOST_ASIO_HAS_STD_INVOKE_RESULT
BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT
BOOST_BEAST_ALLOW_DEPRECATED
BOOST_CONTAINER_FWD_BAD_DEQUE
BOOST_COROUTINES_NO_DEPRECATION_WARNING)
target_sources(clio PRIVATE
## Main
@@ -42,7 +71,6 @@ target_sources(clio PRIVATE
## Backend
src/backend/BackendInterface.cpp
src/backend/LedgerCache.cpp
## NextGen Backend
src/backend/cassandra/impl/Future.cpp
src/backend/cassandra/impl/Cluster.cpp
src/backend/cassandra/impl/Batch.cpp
@@ -70,9 +98,8 @@ target_sources(clio PRIVATE
src/rpc/common/Validators.cpp
src/rpc/common/MetaProcessors.cpp
src/rpc/common/impl/APIVersionParser.cpp
# RPC impl
src/rpc/common/impl/HandlerProvider.cpp
## RPC handler
## RPC handlers
src/rpc/handlers/AccountChannels.cpp
src/rpc/handlers/AccountCurrencies.cpp
src/rpc/handlers/AccountInfo.cpp
@@ -103,12 +130,16 @@ target_sources(clio PRIVATE
src/log/Logger.cpp
src/util/Taggable.cpp)
add_executable(clio_server src/main/main.cpp)
# Clio server
add_executable(clio_server src/main/Main.cpp)
target_link_libraries(clio_server PUBLIC clio)
if(BUILD_TESTS)
# Unittesting
if(tests)
set(TEST_TARGET clio_tests)
add_executable(${TEST_TARGET}
# Common
unittests/Main.cpp
unittests/Playground.cpp
unittests/Logger.cpp
unittests/Config.cpp
@@ -170,23 +201,30 @@ if(BUILD_TESTS)
unittests/backend/cassandra/SettingsProviderTests.cpp
unittests/backend/cassandra/ExecutionStrategyTests.cpp
unittests/backend/cassandra/AsyncExecutorTests.cpp
# Webserver
unittests/webserver/ServerTest.cpp
unittests/webserver/RPCServerHandlerTest.cpp)
include(CMake/deps/gtest.cmake)
# fix for dwarf5 bug on ci
# See https://github.com/google/googletest/issues/3475
gtest_discover_tests(clio_tests DISCOVERY_TIMEOUT 10)
# Fix for dwarf5 bug on ci
target_compile_options(clio PUBLIC -gdwarf-4)
target_compile_definitions(${TEST_TARGET} PUBLIC UNITTEST_BUILD)
target_include_directories(${TEST_TARGET} PRIVATE unittests)
target_link_libraries(${TEST_TARGET} PUBLIC clio gtest::gtest)
# if CODE_COVERAGE enable, add clio_test-ccov
if(CODE_COVERAGE)
include(CMake/coverage.cmake)
add_converage(${TEST_TARGET})
# Generate `clio_test-ccov` if coverage is enabled
if(coverage)
include(CMake/Coverage.cmake)
add_coverage(${TEST_TARGET})
endif()
endif()
include(CMake/install/install.cmake)
if(PACKAGING)
include(CMake/packaging.cmake)
if(packaging)
include(CMake/packaging.cmake) # TODO: file is actually missing?
endif()

View File

@@ -1,3 +1,78 @@
# Temporary build instructions
## Prerequisites
1. Make sure you have conan 1.x installed and active (use brew on mac). Conan may have been upgraded to v2 and that may not work.
2. You should already have a Conan profile after building rippled. Example:
```
[settings]
os=Macos
os_build=Macos
arch=armv8
arch_build=armv8
compiler=apple-clang
compiler.version=14
compiler.libcxx=libc++
build_type=Release
compiler.cppstd=20
[options]
boost:extra_b2_flags=define=BOOST_ASIO_HAS_STD_INVOKE_RESULT
[build_requires]
[env]
CFLAGS=-DBOOST_ASIO_HAS_STD_INVOKE_RESULT
CXXFLAGS=-DBOOST_ASIO_HAS_STD_INVOKE_RESULT
[conf]
tools.build:cxxflags=['-DBOOST_ASIO_HAS_STD_INVOKE_RESULT']
tools.build:cflags=['-DBOOST_ASIO_HAS_STD_INVOKE_RESULT']
```
## Using artifactory (temporary packages)
```sh
conan remote add conan-non-prod http://18.143.149.228:8081/artifactory/api/conan/conan-non-prod
```
Now you should be able to download prebuilt `xrpl` package on some platforms. At the very least you should be able to skip the local package step for `rippled` (as described below) and conan should be able to fetch it from artifactory instead.
## Preparing local packages (alternative to artifactory)
1. Get rippled from [this branch](https://github.com/thejohnfreeman/clio/tree/conan)
2. Remove old packages you may have cached:
```sh
conan remove -f xrpl/1.12.0-b2
conan remove -f cassandra-cpp-driver/2.16.2
```
3. In a clone of rippled from step 1, build rippled as per their instructions. pay attention to these commands:
```sh
conan export external/snappy snappy/1.1.9@
conan export external/soci soci/4.0.3@
mkdir build && cd build
conan install .. --output-folder . --build missing --settings build_type=Release
cmake -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build . --parallel 8 # or without the number if you feel extra adventurous
cd - # to go back to root of rippled
```
4. Perform this command at the root directory of rippled
```sh
conan export .
```
this will export a local package `xrpl/1.12.0-b2`.
5. Navigate to clio's root directory and perform
```sh
conan export external/cassandra # export our "custom" cassandra driver package
mkdir build && cd build
conan install .. --output-folder . --build missing --settings build_type=Release -o tests=True
cmake -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build . --parallel 8 # or without the number if you feel extra adventurous
```
If all goes well, `conan install` will find required packages and `cmake` will do the rest. you should end up with `clio_server` and `clio_unittests` in the `build` directory (the current directory).
Please note that a few unittests are currently failing. See below.
## Things to fix
1. Figure out what to do with `ripple::Fees` that is now missing the `units` member. It was used in a few places and couple unittests are broken because of it.
2. Fix build on CI (currently using old CMake. need to use conan instead).
3. Fix code coverage support (see 'coverage' option in conanfile).
4. See if we can contribute/push our cassandra-cpp-driver to conan center so we don't need to export it before we able to use it.
5. Try to improve the new asio code that is using `async_compose` and potentially the `FutureWithCallback` way of accepting the callback.
# Clio
Clio is an XRP Ledger API server. Clio is optimized for RPC calls, over WebSocket or JSON-RPC. Validated
historical ledger and transaction data are stored in a more space-efficient format,

79
conanfile.py Normal file
View File

@@ -0,0 +1,79 @@
from conan import ConanFile
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
import re
class Clio(ConanFile):
name = 'clio'
license = 'ISC'
author = 'Alex Kremer <akremer@ripple.com>, John Freeman <jfreeman@ripple.com>'
url = 'https://github.com/xrplf/clio'
description = 'Clio RPC server'
settings = 'os', 'compiler', 'build_type', 'arch'
options = {
'fPIC': [True, False],
'verbose': [True, False],
'tests': [True, False], # build unit tests
'packaging': [True, False], # create distribution packages
'coverage': [True, False], # build for test coverage report
}
requires = [
'boost/1.82.0',
'cassandra-cpp-driver/2.16.2',
'fmt/10.0.0',
'grpc/1.50.1',
'gtest/1.13.0',
'openssl/1.1.1u',
'xrpl/1.12.0-b2',
]
default_options = {
'fPIC': True,
'verbose': False,
'tests': False,
'packaging': False,
'coverage': False,
'cassandra-driver/*:shared': False,
'date/*:header_only': True,
'grpc/*:shared': False,
'grpc/*:secure': True,
'libpq/*:shared': False,
'lz4/*:shared': False,
'openssl/*:shared': False,
'protobuf/*:shared': False,
'protobuf/*:with_zlib': True,
'snappy/*:shared': False,
'gtest/*:no_main': True,
}
exports_sources = (
'CMakeLists.txt', 'CMake/*', 'src/*'
)
def configure(self):
if self.settings.compiler == 'apple-clang':
self.options['boost'].visibility = 'global'
def layout(self):
cmake_layout(self)
# Fix this setting to follow the default introduced in Conan 1.48
# to align with our build instructions.
self.folders.generators = 'build/generators'
generators = 'CMakeDeps'
def generate(self):
tc = CMakeToolchain(self)
tc.variables['verbose'] = self.options.verbose
tc.variables['tests'] = self.options.tests
tc.variables['coverage'] = self.options.coverage
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
cmake = CMake(self)
cmake.install()

87
external/cassandra/conanfile.py vendored Normal file
View File

@@ -0,0 +1,87 @@
from conan import ConanFile, tools
from conan.tools.cmake import CMake, CMakeToolchain
class Cassandra(ConanFile):
name = 'cassandra-cpp-driver'
version = '2.16.2'
license = 'Apache-2.0'
url = 'https://github.com/conan-io/conan-center-index'
homepage = 'https://docs.datastax.com/en/developer/cpp-driver/'
description = 'Cassandra C++ Driver'
topics = ('conan', 'cassandra', 'driver')
settings = 'os', 'arch', 'compiler', 'build_type'
options = {
'shared': [True, False],
'fPIC': [True, False],
'install_header_in_subdir': [True, False],
'use_atomic': [None, 'boost', 'std'],
'with_openssl': [True, False],
'with_zlib': [True, False],
'with_kerberos': [True, False],
'use_timerfd': [True, False],
}
default_options = {
'shared': False,
'fPIC': True,
'install_header_in_subdir': False,
'use_atomic': None,
'with_openssl': True,
'with_zlib': True,
'with_kerberos': False,
'use_timerfd': True,
}
def requirements(self):
self.requires('libuv/1.44.1')
self.requires('http_parser/2.9.4')
if self.options.with_openssl:
self.requires('openssl/1.1.1q')
if self.options.with_zlib:
self.requires('minizip/1.2.12')
self.requires('zlib/1.2.13')
if self.options.use_atomic == 'boost':
self.requires('boost/1.79.0')
exports_sources = ['CMakeLists.txt']
def config_options(self):
if self.settings.os == 'Windows':
del self.options.fPIC
def configure(self):
self.options['libuv'].shared = self.options.shared
def generate(self):
tc = CMakeToolchain(self)
if self.settings.get_safe('compiler.cppstd') == '20':
tc.blocks['cppstd'].values['cppstd'] = 17
tc.variables['CASS_BUILD_STATIC'] = not self.options.shared
tc.variables['CASS_USE_STATIC_LIBS'] = not self.options.shared
tc.variables['CASS_BUILD_SHARED'] = self.options.shared
tc.variables['LIBUV_ROOT_DIR'] = self.deps_cpp_info['libuv'].rootpath
if self.options.with_openssl:
tc.variables['OPENSSL_ROOT_DIR'] = self.deps_cpp_info['openssl'].rootpath
tc.generate()
def source(self):
tools.files.get(self, f'https://github.com/datastax/cpp-driver/archive/refs/tags/{self.version}.tar.gz', strip_root=True)
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
cmake = CMake(self)
cmake.install()
def package_info(self):
if self.options.shared:
self.cpp_info.libs = ['cassandra']
else:
self.cpp_info.libs = ['cassandra_static']
if self.settings.os == 'Windows':
self.cpp_info.libs.extend(['iphlpapi', 'psapi', 'wsock32', 'crypt32', 'ws2_32', 'userenv'])
if not self.options.shared:
self.cpp_info.defines = ['CASS_STATIC']

View File

@@ -308,8 +308,9 @@ BackendInterface::fetchFees(std::uint32_t const seq, boost::asio::yield_context&
if (sle.getFieldIndex(ripple::sfBaseFee) != -1)
fees.base = sle.getFieldU64(ripple::sfBaseFee);
if (sle.getFieldIndex(ripple::sfReferenceFeeUnits) != -1)
fees.units = sle.getFieldU32(ripple::sfReferenceFeeUnits);
// deprecated?
// if (sle.getFieldIndex(ripple::sfReferenceFeeUnits) != -1)
// fees.units = sle.getFieldU32(ripple::sfReferenceFeeUnits);
if (sle.getFieldIndex(ripple::sfReserveBase) != -1)
fees.reserve = sle.getFieldU32(ripple::sfReserveBase);

View File

@@ -19,13 +19,14 @@
#pragma once
#include <ripple/ledger/ReadView.h>
#include <backend/DBHelpers.h>
#include <backend/LedgerCache.h>
#include <backend/Types.h>
#include <config/Config.h>
#include <log/Logger.h>
#include <ripple/protocol/Fees.h>
#include <ripple/protocol/LedgerHeader.h>
#include <boost/asio/spawn.hpp>
#include <boost/json.hpp>
@@ -98,7 +99,7 @@ synchronous(F&& f)
* ctx, and is used to pass a stream of data into the method.
*/
boost::asio::io_context ctx;
boost::asio::io_context::strand strand(ctx);
boost::asio::strand<boost::asio::io_context::executor_type> strand(ctx.get_executor());
std::optional<boost::asio::io_context::work> work;
/*! @brief Place the ctx within the vector of serialized handlers. */
@@ -203,11 +204,11 @@ public:
}
/*! @brief Fetches a specific ledger by sequence number. */
virtual std::optional<ripple::LedgerInfo>
virtual std::optional<ripple::LedgerHeader>
fetchLedgerBySequence(std::uint32_t const sequence, boost::asio::yield_context& yield) const = 0;
/*! @brief Fetches a specific ledger by hash. */
virtual std::optional<ripple::LedgerInfo>
virtual std::optional<ripple::LedgerHeader>
fetchLedgerByHash(ripple::uint256 const& hash, boost::asio::yield_context& yield) const = 0;
/*! @brief Fetches the latest ledger sequence. */
@@ -475,11 +476,11 @@ public:
/**
* @brief Writes to a specific ledger.
*
* @param ledgerInfo Const on ledger information.
* @param ledgerHeader r-value string representing ledger header.
* @param ledgerHeader Ledger header.
* @param blob r-value string serialization of ledger header.
*/
virtual void
writeLedger(ripple::LedgerInfo const& ledgerInfo, std::string&& ledgerHeader) = 0;
writeLedger(ripple::LedgerHeader const& ledgerHeader, std::string&& blob) = 0;
/**
* @brief Writes a new ledger object.

View File

@@ -29,7 +29,8 @@
#include <util/LedgerUtils.h>
#include <util/Profiler.h>
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
#include <ripple/protocol/LedgerHeader.h>
#include <ripple/protocol/nft.h>
#include <boost/asio/spawn.hpp>
namespace Backend::Cassandra {
@@ -199,9 +200,9 @@ public:
}
void
writeLedger(ripple::LedgerInfo const& ledgerInfo, std::string&& header) override
writeLedger(ripple::LedgerHeader const& ledgerInfo, std::string&& blob) override
{
executor_.write(schema_->insertLedgerHeader, ledgerInfo.seq, std::move(header));
executor_.write(schema_->insertLedgerHeader, ledgerInfo.seq, std::move(blob));
executor_.write(schema_->insertLedgerHash, ledgerInfo.hash, ledgerInfo.seq);
@@ -232,7 +233,7 @@ public:
return std::nullopt;
}
std::optional<ripple::LedgerInfo>
std::optional<ripple::LedgerHeader>
fetchLedgerBySequence(std::uint32_t const sequence, boost::asio::yield_context& yield) const override
{
log_.trace() << __func__ << " call for seq " << sequence;
@@ -261,7 +262,7 @@ public:
return std::nullopt;
}
std::optional<ripple::LedgerInfo>
std::optional<ripple::LedgerHeader>
fetchLedgerByHash(ripple::uint256 const& hash, boost::asio::yield_context& yield) const override
{
log_.trace() << __func__ << " call";

View File

@@ -21,7 +21,6 @@
#include <ripple/basics/Log.h>
#include <ripple/basics/StringUtilities.h>
#include <ripple/ledger/ReadView.h>
#include <ripple/protocol/SField.h>
#include <ripple/protocol/STAccount.h>
#include <ripple/protocol/TxMeta.h>

View File

@@ -24,6 +24,7 @@
#include <boost/json.hpp>
#include <fstream>
#include <string>
#include <thread>

View File

@@ -25,7 +25,7 @@
#include <log/Logger.h>
#include <util/Expected.h>
#include <boost/asio/async_result.hpp>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <atomic>
@@ -75,11 +75,7 @@ public:
using FutureType = typename HandleType::FutureType;
using FutureWithCallbackType = typename HandleType::FutureWithCallbackType;
using ResultType = typename HandleType::ResultType;
using CompletionTokenType = boost::asio::yield_context;
using FunctionType = void(boost::system::error_code);
using AsyncResultType = boost::asio::async_result<CompletionTokenType, FunctionType>;
using HandlerType = typename AsyncResultType::completion_handler_type;
DefaultExecutionStrategy(Settings settings, HandleType const& handle)
: maxWriteRequestsOutstanding_{settings.maxWriteRequestsOutstanding}
@@ -224,29 +220,29 @@ public:
[[maybe_unused]] ResultOrErrorType
read(CompletionTokenType token, std::vector<StatementType> const& statements)
{
auto handler = HandlerType{token};
auto result = AsyncResultType{handler};
auto const numStatements = statements.size();
std::optional<FutureWithCallbackType> future;
// todo: perhaps use policy instead
while (true)
{
numReadRequestsOutstanding_ += numStatements;
// TODO: see if we can avoid using shared_ptr for self here
auto init = [this, &statements, &future]<typename Self>(Self& self) {
future.emplace(handle_.get().asyncExecute(
statements, [sself = std::make_shared<Self>(std::move(self))](auto&& res) mutable {
boost::asio::post(
sself->get_io_executor(), [sself = std::move(sself), res = std::move(res)]() mutable {
sself->complete(std::move(res));
sself.reset();
});
}));
};
auto const future = handle_.get().asyncExecute(statements, [handler](auto&&) mutable {
boost::asio::post(boost::asio::get_associated_executor(handler), [handler]() mutable {
handler(boost::system::error_code{});
});
});
// suspend coroutine until completion handler is called
result.get();
auto res = boost::asio::async_compose<CompletionTokenType, void(ResultOrErrorType)>(init, token);
numReadRequestsOutstanding_ -= numStatements;
// it's safe to call blocking get on future here as we already
// waited for the coroutine to resume above.
if (auto res = future.get(); res)
if (res)
{
return res;
}
@@ -271,28 +267,28 @@ public:
[[maybe_unused]] ResultOrErrorType
read(CompletionTokenType token, StatementType const& statement)
{
auto handler = HandlerType{token};
auto result = AsyncResultType{handler};
std::optional<FutureWithCallbackType> future;
// todo: perhaps use policy instead
while (true)
{
++numReadRequestsOutstanding_;
// TODO: see if we can avoid using shared_ptr for self here
auto init = [this, &statement, &future]<typename Self>(Self& self) {
future.emplace(handle_.get().asyncExecute(
statement, [sself = std::make_shared<Self>(std::move(self))](auto&& res) mutable {
boost::asio::post(
sself->get_io_executor(), [sself = std::move(sself), res = std::move(res)]() mutable {
sself->complete(std::move(res));
sself.reset();
});
}));
};
auto const future = handle_.get().asyncExecute(statement, [handler](auto const&) mutable {
boost::asio::post(boost::asio::get_associated_executor(handler), [handler]() mutable {
handler(boost::system::error_code{});
});
});
// suspend coroutine until completion handler is called
result.get();
auto res = boost::asio::async_compose<CompletionTokenType, void(ResultOrErrorType)>(init, token);
--numReadRequestsOutstanding_;
// it's safe to call blocking get on future here as we already
// waited for the coroutine to resume above.
if (auto res = future.get(); res)
if (res)
{
return res;
}
@@ -318,9 +314,6 @@ public:
std::vector<ResultType>
readEach(CompletionTokenType token, std::vector<StatementType> const& statements)
{
auto handler = HandlerType{token};
auto result = AsyncResultType{handler};
std::atomic_bool hadError = false;
std::atomic_int numOutstanding = statements.size();
numReadRequestsOutstanding_ += statements.size();
@@ -328,29 +321,30 @@ public:
auto futures = std::vector<FutureWithCallbackType>{};
futures.reserve(numOutstanding);
// used as the handler for each async statement individually
auto executionHandler = [handler, &hadError, &numOutstanding](auto const& res) mutable {
if (not res)
hadError = true;
auto init = [this, &statements, &futures, &hadError, &numOutstanding]<typename Self>(Self& self) {
auto sself = std::make_shared<Self>(std::move(self)); // TODO: see if we can avoid this
auto executionHandler = [&hadError, &numOutstanding, sself = std::move(sself)](auto const& res) mutable {
if (not res)
hadError = true;
// when all async operations complete unblock the result
if (--numOutstanding == 0)
boost::asio::post(boost::asio::get_associated_executor(handler), [handler]() mutable {
handler(boost::system::error_code{});
// when all async operations complete unblock the result
if (--numOutstanding == 0)
boost::asio::post(sself->get_io_executor(), [sself = std::move(sself)]() mutable {
sself->complete();
sself.reset();
});
};
std::transform(
std::cbegin(statements),
std::cend(statements),
std::back_inserter(futures),
[this, &executionHandler](auto const& statement) {
return handle_.get().asyncExecute(statement, executionHandler);
});
};
std::transform(
std::cbegin(statements),
std::cend(statements),
std::back_inserter(futures),
[this, &executionHandler](auto const& statement) {
return handle_.get().asyncExecute(statement, executionHandler);
});
// suspend coroutine until completion handler is called
result.get();
boost::asio::async_compose<CompletionTokenType, void()>(init, token);
numReadRequestsOutstanding_ -= statements.size();
if (hadError)
@@ -359,8 +353,7 @@ public:
std::vector<ResultType> results;
results.reserve(futures.size());
// it's safe to call blocking get on futures here as we already
// waited for the coroutine to resume above.
// it's safe to call blocking get on futures here as we already waited for the coroutine to resume above.
std::transform(
std::make_move_iterator(std::begin(futures)),
std::make_move_iterator(std::end(futures)),

View File

@@ -90,6 +90,7 @@ invokeHelper(CassFuture* ptr, void* cbPtr)
}
}
// TODO: cb_ can be deleted before cassandra-driver calls it if the user fails to hold onto the future object
/* implicit */ FutureWithCallback::FutureWithCallback(CassFuture* ptr, fn_t&& cb)
: Future{ptr}, cb_{std::make_unique<fn_t>(std::move(cb))}
{

View File

@@ -19,6 +19,8 @@
#include <etl/ETLService.h>
#include <ripple/protocol/LedgerHeader.h>
using namespace clio;
// Database must be populated when this starts
@@ -80,7 +82,7 @@ ETLService::monitor()
if (!rng)
{
log_.info() << "Database is empty. Will download a ledger from the network.";
std::optional<ripple::LedgerInfo> ledger;
std::optional<ripple::LedgerHeader> ledger;
try
{

View File

@@ -34,7 +34,7 @@
#include <log/Logger.h>
#include <subscriptions/SubscriptionManager.h>
#include "org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h"
#include <ripple/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
#include <grpcpp/grpcpp.h>
#include <memory>

View File

@@ -25,8 +25,8 @@
#include <log/Logger.h>
#include <subscriptions/SubscriptionManager.h>
#include <ripple/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
#include <boost/asio.hpp>
#include "org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h"
#include <grpcpp/grpcpp.h>
class Source;

View File

@@ -26,9 +26,7 @@
#include <log/Logger.h>
#include <subscriptions/SubscriptionManager.h>
#include "org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h"
#include <grpcpp/grpcpp.h>
#include <ripple/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
#include <boost/algorithm/string.hpp>
#include <boost/asio.hpp>
#include <boost/beast/core.hpp>
@@ -37,6 +35,7 @@
#include <boost/beast/websocket.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <grpcpp/grpcpp.h>
class LoadBalancer;
class Source;

View File

@@ -21,13 +21,12 @@
#include <log/Logger.h>
#include <ripple/ledger/ReadView.h>
#include <ripple/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
#include <boost/algorithm/string.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/core/string.hpp>
#include <boost/beast/websocket.hpp>
#include "org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h"
#include <grpcpp/grpcpp.h>
#include <chrono>

View File

@@ -46,7 +46,7 @@ class ForwardCache
mutable std::shared_mutex mtx_;
std::unordered_map<std::string, ResponseType> latestForwarded_;
boost::asio::io_context::strand strand_;
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
Source const& source_;
std::uint32_t duration_ = 10;
@@ -55,7 +55,7 @@ class ForwardCache
public:
ForwardCache(clio::Config const& config, boost::asio::io_context& ioc, Source const& source)
: strand_(ioc), source_(source)
: strand_(ioc.get_executor()), source_(source)
{
if (config.contains("cache"))
{

View File

@@ -23,8 +23,7 @@
#include <etl/Source.h>
#include <log/Logger.h>
#include <ripple/ledger/ReadView.h>
#include "org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h"
#include <ripple/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
#include <grpcpp/grpcpp.h>
#include <optional>

View File

@@ -28,7 +28,6 @@
#include <util/Profiler.h>
#include <ripple/beast/core/CurrentThreadName.h>
#include <ripple/ledger/ReadView.h>
#include <memory>
@@ -85,7 +84,7 @@ public:
* nft_token_transactions tables (mostly transaction hashes, corresponding nodestore hashes and affected accounts)
*/
FormattedTransactionsData
insertTransactions(ripple::LedgerInfo const& ledger, GetLedgerResponseType& data)
insertTransactions(ripple::LedgerHeader const& ledger, GetLedgerResponseType& data)
{
FormattedTransactionsData result;
@@ -140,7 +139,7 @@ public:
* @param sequence the sequence of the ledger to download
* @return The ledger downloaded, with a full transaction and account state map
*/
std::optional<ripple::LedgerInfo>
std::optional<ripple::LedgerHeader>
loadInitialLedger(uint32_t sequence)
{
// check that database is actually empty
@@ -158,7 +157,7 @@ public:
if (!ledgerData)
return {};
ripple::LedgerInfo lgrInfo = util::deserializeHeader(ripple::makeSlice(ledgerData->ledger_header()));
ripple::LedgerHeader lgrInfo = util::deserializeHeader(ripple::makeSlice(ledgerData->ledger_header()));
log_.debug() << "Deserialized ledger header. " << util::toString(lgrInfo);

View File

@@ -25,7 +25,7 @@
#include <util/LedgerUtils.h>
#include <util/Profiler.h>
#include <ripple/ledger/ReadView.h>
#include <ripple/protocol/LedgerHeader.h>
#include <chrono>
@@ -47,7 +47,7 @@ class LedgerPublisher
{
clio::Logger log_{"ETL"};
boost::asio::io_context::strand publishStrand_;
boost::asio::strand<boost::asio::io_context::executor_type> publishStrand_;
std::shared_ptr<BackendInterface> backend_;
std::shared_ptr<SubscriptionManagerType> subscriptions_;
@@ -71,7 +71,7 @@ public:
std::shared_ptr<BackendInterface> backend,
std::shared_ptr<SubscriptionManager> subscriptions,
SystemState const& state)
: publishStrand_{ioc}, backend_{backend}, subscriptions_{subscriptions}, state_{std::cref(state)}
: publishStrand_{ioc.get_executor()}, backend_{backend}, subscriptions_{subscriptions}, state_{std::cref(state)}
{
}
@@ -130,7 +130,7 @@ public:
* @param lgrInfo the ledger to publish
*/
void
publish(ripple::LedgerInfo const& lgrInfo)
publish(ripple::LedgerHeader const& lgrInfo)
{
boost::asio::post(publishStrand_, [this, lgrInfo = lgrInfo]() {
log_.info() << "Publishing ledger " << std::to_string(lgrInfo.seq);

View File

@@ -27,7 +27,7 @@
#include <util/Profiler.h>
#include <ripple/beast/core/CurrentThreadName.h>
#include "org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h"
#include <ripple/proto/org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
#include <grpcpp/grpcpp.h>
#include <chrono>
@@ -163,11 +163,11 @@ private:
* @param rawData data extracted from an ETL source
* @return the newly built ledger and data to write to the database
*/
std::pair<ripple::LedgerInfo, bool>
std::pair<ripple::LedgerHeader, bool>
buildNextLedger(GetLedgerResponseType& rawData)
{
log_.debug() << "Beginning ledger update";
ripple::LedgerInfo lgrInfo = util::deserializeHeader(ripple::makeSlice(rawData.ledger_header()));
ripple::LedgerHeader lgrInfo = util::deserializeHeader(ripple::makeSlice(rawData.ledger_header()));
log_.debug() << "Deserialized ledger header. " << util::toString(lgrInfo);
backend_->startWrites();
@@ -191,7 +191,7 @@ private:
log_.fatal()
<< "Failed to build next ledger: " << e.what()
<< " Possible cause: The ETL node is not compatible with the version of the rippled lib Clio is using.";
return {ripple::LedgerInfo{}, false};
return {ripple::LedgerHeader{}, false};
}
log_.debug() << "Inserted all transactions. Number of transactions = "
@@ -217,7 +217,7 @@ private:
* @param rawData Ledger data from GRPC
*/
void
updateCache(ripple::LedgerInfo const& lgrInfo, GetLedgerResponseType& rawData)
updateCache(ripple::LedgerHeader const& lgrInfo, GetLedgerResponseType& rawData)
{
std::vector<Backend::LedgerObject> cacheUpdates;
cacheUpdates.reserve(rawData.ledger_objects().objects_size());
@@ -348,7 +348,7 @@ private:
* @param rawData Ledger data from GRPC
*/
void
writeSuccessors(ripple::LedgerInfo const& lgrInfo, GetLedgerResponseType& rawData)
writeSuccessors(ripple::LedgerHeader const& lgrInfo, GetLedgerResponseType& rawData)
{
// Write successor info, if included from rippled
if (rawData.object_neighbors_included())

View File

@@ -39,6 +39,7 @@
#include <boost/json.hpp>
#include <boost/program_options.hpp>
#include <fstream>
#include <main/Build.h>
#include <memory>
#include <sstream>

View File

@@ -230,6 +230,8 @@ tag_invoke(boost::json::value_from_tag, boost::json::value& jv, BookChange const
}
[[nodiscard]] boost::json::object const
computeBookChanges(ripple::LedgerInfo const& lgrInfo, std::vector<Backend::TransactionAndMetadata> const& transactions);
computeBookChanges(
ripple::LedgerHeader const& lgrInfo,
std::vector<Backend::TransactionAndMetadata> const& transactions);
} // namespace RPC

View File

@@ -251,7 +251,7 @@ toJson(ripple::SLE const& sle)
}
boost::json::object
toJson(ripple::LedgerInfo const& lgrInfo)
toJson(ripple::LedgerHeader const& lgrInfo)
{
boost::json::object header;
header["ledger_sequence"] = lgrInfo.seq;
@@ -284,7 +284,7 @@ parseStringAsUInt(std::string const& value)
return index;
}
std::variant<Status, ripple::LedgerInfo>
std::variant<Status, ripple::LedgerHeader>
ledgerInfoFromRequest(std::shared_ptr<Backend::BackendInterface const> const& backend, Web::Context const& ctx)
{
auto hashValue = ctx.params.contains("ledger_hash") ? ctx.params.at("ledger_hash") : nullptr;
@@ -339,7 +339,7 @@ ledgerInfoFromRequest(std::shared_ptr<Backend::BackendInterface const> const& ba
}
// extract ledgerInfoFromRequest's parameter from context
std::variant<Status, ripple::LedgerInfo>
std::variant<Status, ripple::LedgerHeader>
getLedgerInfoFromHashOrSeq(
BackendInterface const& backend,
boost::asio::yield_context& yield,
@@ -347,7 +347,7 @@ getLedgerInfoFromHashOrSeq(
std::optional<uint32_t> ledgerIndex,
uint32_t maxSeq)
{
std::optional<ripple::LedgerInfo> lgrInfo;
std::optional<ripple::LedgerHeader> lgrInfo;
auto const err = Status{RippledError::rpcLGR_NOT_FOUND, "ledgerNotFound"};
if (ledgerHash)
{
@@ -373,7 +373,7 @@ getLedgerInfoFromHashOrSeq(
}
std::vector<unsigned char>
ledgerInfoToBlob(ripple::LedgerInfo const& info, bool includeHash)
ledgerInfoToBlob(ripple::LedgerHeader const& info, bool includeHash)
{
ripple::Serializer s;
s.add32(info.seq);
@@ -664,7 +664,7 @@ std::shared_ptr<ripple::SLE const>
read(
std::shared_ptr<Backend::BackendInterface const> const& backend,
ripple::Keylet const& keylet,
ripple::LedgerInfo const& lgrInfo,
ripple::LedgerHeader const& lgrInfo,
Web::Context const& context)
{
if (auto const blob = backend->fetchLedgerObject(keylet.key, lgrInfo.seq, context.yield); blob)

View File

@@ -23,17 +23,18 @@
* This file contains a variety of utility functions used when executing the handlers.
*/
#include <ripple/app/ledger/Ledger.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/STLedgerEntry.h>
#include <ripple/protocol/STTx.h>
#include <backend/BackendInterface.h>
#include <rpc/JS.h>
#include <rpc/common/Types.h>
#include <util/JsonUtils.h>
#include <webserver/Context.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/Rate.h>
#include <ripple/protocol/STLedgerEntry.h>
#include <ripple/protocol/STTx.h>
#include <fmt/core.h>
#include <util/JsonUtils.h>
namespace RPC {
@@ -74,7 +75,7 @@ boost::json::object
toJson(ripple::SLE const& sle);
boost::json::object
toJson(ripple::LedgerInfo const& info);
toJson(ripple::LedgerHeader const& info);
boost::json::object
toJson(ripple::TxMeta const& meta);
@@ -85,15 +86,15 @@ toBoostJson(RippledJson const& value);
boost::json::object
generatePubLedgerMessage(
ripple::LedgerInfo const& lgrInfo,
ripple::LedgerHeader const& lgrInfo,
ripple::Fees const& fees,
std::string const& ledgerRange,
std::uint32_t txnCount);
std::variant<Status, ripple::LedgerInfo>
std::variant<Status, ripple::LedgerHeader>
ledgerInfoFromRequest(std::shared_ptr<Backend::BackendInterface const> const& backend, Web::Context const& ctx);
std::variant<Status, ripple::LedgerInfo>
std::variant<Status, ripple::LedgerHeader>
getLedgerInfoFromHashOrSeq(
BackendInterface const& backend,
boost::asio::yield_context& yield,
@@ -129,7 +130,7 @@ std::shared_ptr<ripple::SLE const>
read(
std::shared_ptr<Backend::BackendInterface const> const& backend,
ripple::Keylet const& keylet,
ripple::LedgerInfo const& lgrInfo,
ripple::LedgerHeader const& lgrInfo,
Web::Context const& context);
std::variant<Status, std::pair<ripple::PublicKey, ripple::SecretKey>>
@@ -139,7 +140,7 @@ std::vector<ripple::AccountID>
getAccountsFromTransaction(boost::json::object const& transaction);
std::vector<unsigned char>
ledgerInfoToBlob(ripple::LedgerInfo const& info, bool includeHash = false);
ledgerInfoToBlob(ripple::LedgerHeader const& info, bool includeHash = false);
bool
isGlobalFrozen(

View File

@@ -26,6 +26,7 @@
#include <boost/asio/spawn.hpp>
#include <boost/json/value.hpp>
#include <boost/json/value_from.hpp>
namespace Server {
struct ConnectionBase;

View File

@@ -56,8 +56,6 @@ struct DefaultProcessor final
}
else if constexpr (HandlerWithoutInput<HandlerType>)
{
using OutType = HandlerReturnType<typename HandlerType::Output>;
// no input to pass, ignore the value
if (auto const ret = handler.process(ctx); not ret)
return Error{ret.error()}; // forward Status

View File

@@ -65,7 +65,7 @@ AccountChannelsHandler::process(AccountChannelsHandler::Input input, Context con
if (auto status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
auto const accountID = accountFromStringStrict(input.account);
auto const accountLedgerObject =
sharedPtrBackend_->fetchLedgerObject(ripple::keylet::account(*accountID).key, lgrInfo.seq, ctx.yield);
@@ -139,13 +139,15 @@ tag_invoke(boost::json::value_to_tag<AccountChannelsHandler::Input>, boost::json
void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, AccountChannelsHandler::Output const& output)
{
using boost::json::value_from;
auto obj = boost::json::object{
{JS(account), output.account},
{JS(ledger_hash), output.ledgerHash},
{JS(ledger_index), output.ledgerIndex},
{JS(validated), output.validated},
{JS(limit), output.limit},
{JS(channels), output.channels},
{JS(channels), value_from(output.channels)},
};
if (output.marker)

View File

@@ -30,7 +30,7 @@ AccountCurrenciesHandler::process(AccountCurrenciesHandler::Input input, Context
if (auto const status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
auto const accountID = accountFromStringStrict(input.account);
auto const accountLedgerObject =
@@ -81,12 +81,14 @@ AccountCurrenciesHandler::process(AccountCurrenciesHandler::Input input, Context
void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, AccountCurrenciesHandler::Output const& output)
{
using boost::json::value_from;
jv = {
{JS(ledger_hash), output.ledgerHash},
{JS(ledger_index), output.ledgerIndex},
{JS(validated), output.validated},
{JS(receive_currencies), output.receiveCurrencies},
{JS(send_currencies), output.sendCurrencies},
{JS(receive_currencies), value_from(output.receiveCurrencies)},
{JS(send_currencies), value_from(output.sendCurrencies)},
};
}

View File

@@ -35,7 +35,7 @@ AccountInfoHandler::process(AccountInfoHandler::Input input, Context const& ctx)
if (auto const status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
auto const accountStr = input.account.value_or(input.ident.value_or(""));
auto const accountID = accountFromStringStrict(accountStr);
auto const accountKeylet = ripple::keylet::account(*accountID);

View File

@@ -99,7 +99,7 @@ AccountLinesHandler::process(AccountLinesHandler::Input input, Context const& ct
if (auto status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
auto const accountID = accountFromStringStrict(input.account);
auto const accountLedgerObject =
sharedPtrBackend_->fetchLedgerObject(ripple::keylet::account(*accountID).key, lgrInfo.seq, ctx.yield);
@@ -185,13 +185,15 @@ tag_invoke(boost::json::value_to_tag<AccountLinesHandler::Input>, boost::json::v
void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, AccountLinesHandler::Output const& output)
{
using boost::json::value_from;
auto obj = boost::json::object{
{JS(account), output.account},
{JS(ledger_hash), output.ledgerHash},
{JS(ledger_index), output.ledgerIndex},
{JS(validated), output.validated},
{JS(limit), output.limit},
{JS(lines), output.lines},
{JS(lines), value_from(output.lines)},
};
if (output.marker)

View File

@@ -19,7 +19,8 @@
#include <rpc/handlers/AccountNFTs.h>
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
#include <ripple/protocol/Serializer.h>
#include <ripple/protocol/nft.h>
namespace RPC {
@@ -33,7 +34,7 @@ AccountNFTsHandler::process(AccountNFTsHandler::Input input, Context const& ctx)
if (auto const status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
auto const accountID = accountFromStringStrict(input.account);
auto const accountLedgerObject =
sharedPtrBackend_->fetchLedgerObject(ripple::keylet::account(*accountID).key, lgrInfo.seq, ctx.yield);

View File

@@ -44,7 +44,7 @@ AccountObjectsHandler::process(AccountObjectsHandler::Input input, Context const
if (auto const status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
auto const accountID = accountFromStringStrict(input.account);
auto const accountLedgerObject =
sharedPtrBackend_->fetchLedgerObject(ripple::keylet::account(*accountID).key, lgrInfo.seq, ctx.yield);

View File

@@ -50,7 +50,7 @@ AccountOffersHandler::process(AccountOffersHandler::Input input, Context const&
if (auto const status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
auto const accountID = accountFromStringStrict(input.account);
auto const accountLedgerObject =
sharedPtrBackend_->fetchLedgerObject(ripple::keylet::account(*accountID).key, lgrInfo.seq, ctx.yield);

View File

@@ -61,7 +61,7 @@ AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx) con
if (auto status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
maxIndex = minIndex = std::get<ripple::LedgerInfo>(lgrInfoOrStatus).seq;
maxIndex = minIndex = std::get<ripple::LedgerHeader>(lgrInfoOrStatus).seq;
}
std::optional<Backend::TransactionsCursor> cursor;

View File

@@ -31,7 +31,7 @@ BookChangesHandler::process(BookChangesHandler::Input input, Context const& ctx)
if (auto const status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
auto const transactions = sharedPtrBackend_->fetchAllTransactionsInLedger(lgrInfo.seq, ctx.yield);
Output response;
@@ -79,7 +79,9 @@ tag_invoke(boost::json::value_to_tag<BookChangesHandler::Input>, boost::json::va
}
[[nodiscard]] boost::json::object const
computeBookChanges(ripple::LedgerInfo const& lgrInfo, std::vector<Backend::TransactionAndMetadata> const& transactions)
computeBookChanges(
ripple::LedgerHeader const& lgrInfo,
std::vector<Backend::TransactionAndMetadata> const& transactions)
{
using boost::json::value_from;

View File

@@ -37,7 +37,7 @@ BookOffersHandler::process(Input input, Context const& ctx) const
if (auto const status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
auto const book = std::get<ripple::Book>(bookMaybe);
auto const bookKey = getBookBase(book);

View File

@@ -32,7 +32,7 @@ DepositAuthorizedHandler::process(DepositAuthorizedHandler::Input input, Context
if (auto status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
auto const sourceAccountID = accountFromStringStrict(input.sourceAccount);
auto const destinationAccountID = accountFromStringStrict(input.destinationAccount);

View File

@@ -33,7 +33,7 @@ GatewayBalancesHandler::process(GatewayBalancesHandler::Input input, Context con
return Error{*status};
// check account
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
auto const accountID = accountFromStringStrict(input.account);
auto const accountLedgerObject =
sharedPtrBackend_->fetchLedgerObject(ripple::keylet::account(*accountID).key, lgrInfo.seq, ctx.yield);

View File

@@ -30,7 +30,7 @@ LedgerHandler::process(LedgerHandler::Input input, Context const& ctx) const
if (auto const status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
Output output;
if (input.binary)
@@ -51,8 +51,6 @@ LedgerHandler::process(LedgerHandler::Input input, Context const& ctx) const
output.header[JS(ledger_index)] = std::to_string(lgrInfo.seq);
output.header[JS(parent_close_time)] = lgrInfo.parentCloseTime.time_since_epoch().count();
output.header[JS(parent_hash)] = ripple::strHex(lgrInfo.parentHash);
output.header[JS(seqNum)] = std::to_string(lgrInfo.seq);
output.header[JS(totalCoins)] = ripple::to_string(lgrInfo.drops);
output.header[JS(total_coins)] = ripple::to_string(lgrInfo.drops);
output.header[JS(transaction_hash)] = ripple::strHex(lgrInfo.txHash);
}

View File

@@ -19,7 +19,7 @@
#include <rpc/handlers/LedgerData.h>
#include <ripple/app/ledger/LedgerToJson.h>
#include <ripple/protocol/serialize.h>
#include <algorithm>
@@ -68,7 +68,7 @@ LedgerDataHandler::process(Input input, Context const& ctx) const
if (auto const status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
// no marker -> first call, return header information
auto header = boost::json::object();
@@ -93,8 +93,6 @@ LedgerDataHandler::process(Input input, Context const& ctx) const
header[JS(ledger_index)] = std::to_string(lgrInfo.seq);
header[JS(parent_close_time)] = lgrInfo.parentCloseTime.time_since_epoch().count();
header[JS(parent_hash)] = ripple::strHex(lgrInfo.parentHash);
header[JS(seqNum)] = std::to_string(lgrInfo.seq);
header[JS(totalCoins)] = ripple::to_string(lgrInfo.drops);
header[JS(total_coins)] = ripple::to_string(lgrInfo.drops);
header[JS(transaction_hash)] = ripple::strHex(lgrInfo.txHash);
}

View File

@@ -93,7 +93,7 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx)
if (auto const status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
auto const ledgerObject = sharedPtrBackend_->fetchLedgerObject(key, lgrInfo.seq, ctx.yield);
if (!ledgerObject || ledgerObject->size() == 0)

View File

@@ -20,7 +20,6 @@
#include <rpc/RPCHelpers.h>
#include <rpc/handlers/NFTBuyOffers.h>
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
#include <ripple/protocol/Indexes.h>
using namespace ripple;

View File

@@ -63,7 +63,7 @@ NFTHistoryHandler::process(NFTHistoryHandler::Input input, Context const& ctx) c
if (auto status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
maxIndex = minIndex = std::get<ripple::LedgerInfo>(lgrInfoOrStatus).seq;
maxIndex = minIndex = std::get<ripple::LedgerHeader>(lgrInfoOrStatus).seq;
}
std::optional<Backend::TransactionsCursor> cursor;

View File

@@ -20,8 +20,8 @@
#include <rpc/RPCHelpers.h>
#include <rpc/handlers/NFTInfo.h>
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/nft.h>
using namespace ripple;
using namespace ::RPC;
@@ -39,7 +39,7 @@ NFTInfoHandler::process(NFTInfoHandler::Input input, Context const& ctx) const
if (auto const status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<LedgerHeader>(lgrInfoOrStatus);
auto const maybeNft = sharedPtrBackend_->fetchNFT(tokenID, lgrInfo.seq, ctx.yield);
if (not maybeNft.has_value())

View File

@@ -20,7 +20,6 @@
#include <rpc/RPCHelpers.h>
#include <rpc/handlers/NFTOffersCommon.h>
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
#include <ripple/protocol/Indexes.h>
using namespace ripple;
@@ -68,7 +67,7 @@ NFTOffersHandlerBase::iterateOfferDirectory(
if (auto const status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<LedgerHeader>(lgrInfoOrStatus);
// TODO: just check for existence without pulling
if (not sharedPtrBackend_->fetchLedgerObject(directory.key, lgrInfo.seq, yield))
@@ -139,10 +138,12 @@ NFTOffersHandlerBase::iterateOfferDirectory(
void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, NFTOffersHandlerBase::Output const& output)
{
using boost::json::value_from;
auto object = boost::json::object{
{JS(nft_id), output.nftID},
{JS(validated), output.validated},
{JS(offers), output.offers},
{JS(offers), value_from(output.offers)},
};
if (output.marker)

View File

@@ -20,7 +20,6 @@
#include <rpc/RPCHelpers.h>
#include <rpc/handlers/NFTSellOffers.h>
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
#include <ripple/protocol/Indexes.h>
using namespace ripple;

View File

@@ -34,7 +34,7 @@ NoRippleCheckHandler::process(NoRippleCheckHandler::Input input, Context const&
if (auto status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
auto const accountID = accountFromStringStrict(input.account);
auto const keylet = ripple::keylet::account(*accountID).key;
auto const accountObj = sharedPtrBackend_->fetchLedgerObject(keylet, lgrInfo.seq, ctx.yield);
@@ -46,7 +46,8 @@ NoRippleCheckHandler::process(NoRippleCheckHandler::Input input, Context const&
auto sle = ripple::SLE{it, keylet};
auto accountSeq = sle.getFieldU32(ripple::sfSequence);
bool const bDefaultRipple = sle.getFieldU32(ripple::sfFlags) & ripple::lsfDefaultRipple;
auto const fees = input.transactions ? sharedPtrBackend_->fetchFees(lgrInfo.seq, ctx.yield) : std::nullopt;
// TODO: remove if no longer needed
// auto const fees = input.transactions ? sharedPtrBackend_->fetchFees(lgrInfo.seq, ctx.yield) : std::nullopt;
auto output = NoRippleCheckHandler::Output();
@@ -57,7 +58,8 @@ NoRippleCheckHandler::process(NoRippleCheckHandler::Input input, Context const&
boost::json::object tx;
tx[JS(Sequence)] = accountSeq;
tx[JS(Account)] = ripple::toBase58(accountID);
tx[JS(Fee)] = toBoostJson(fees->units.jsonClipped());
// TODO: deprecated?
// tx[JS(Fee)] = toBoostJson(fees->units.jsonClipped());
return tx;
};
@@ -186,10 +188,12 @@ tag_invoke(boost::json::value_to_tag<NoRippleCheckHandler::Input>, boost::json::
void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, NoRippleCheckHandler::Output const& output)
{
using boost::json::value_from;
auto obj = boost::json::object{
{JS(ledger_hash), output.ledgerHash},
{JS(ledger_index), output.ledgerIndex},
{"problems", output.problems},
{"problems", value_from(output.problems)},
{JS(validated), output.validated},
};

View File

@@ -171,8 +171,10 @@ private:
friend void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output const& output)
{
using boost::json::value_from;
jv = {
{JS(info), output.info},
{JS(info), value_from(output.info)},
{JS(validated), output.validated},
};
}
@@ -180,6 +182,7 @@ private:
friend void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, InfoSection const& info)
{
using boost::json::value_from;
using ripple::to_string;
jv = {
@@ -188,8 +191,8 @@ private:
{JS(time), to_string(std::chrono::floor<std::chrono::microseconds>(info.time))},
{JS(uptime), info.uptime.count()},
{"clio_version", info.clioVersion},
{JS(validated_ledger), info.validatedLedger},
{"cache", info.cache},
{JS(validated_ledger), value_from(info.validatedLedger)},
{"cache", value_from(info.cache)},
};
if (info.isAmendmentBlocked)

View File

@@ -31,7 +31,7 @@ TransactionEntryHandler::process(TransactionEntryHandler::Input input, Context c
if (auto status = std::get_if<Status>(&lgrInfoOrStatus))
return Error{*status};
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
auto const lgrInfo = std::get<ripple::LedgerHeader>(lgrInfoOrStatus);
auto const dbRet = sharedPtrBackend_->fetchTransaction(ripple::uint256{input.txHash.c_str()}, ctx.yield);
// Note: transaction_entry is meant to only search a specified ledger for
// the specified transaction. tx searches the entire range of history. For

View File

@@ -41,7 +41,7 @@ Subscription::publish(std::shared_ptr<std::string> const& message)
boost::json::object
getLedgerPubMessage(
ripple::LedgerInfo const& lgrInfo,
ripple::LedgerHeader const& lgrInfo,
ripple::Fees const& fees,
std::string const& ledgerRange,
std::uint32_t txnCount)
@@ -53,7 +53,8 @@ getLedgerPubMessage(
pubMsg["ledger_hash"] = to_string(lgrInfo.hash);
pubMsg["ledger_time"] = lgrInfo.closeTime.time_since_epoch().count();
pubMsg["fee_ref"] = RPC::toBoostJson(fees.units.jsonClipped());
// deprecated?
// pubMsg["fee_ref"] = RPC::toBoostJson(fees.units.jsonClipped());
pubMsg["fee_base"] = RPC::toBoostJson(fees.base.jsonClipped());
pubMsg["reserve_base"] = RPC::toBoostJson(fees.reserve.jsonClipped());
pubMsg["reserve_inc"] = RPC::toBoostJson(fees.increment.jsonClipped());
@@ -144,7 +145,7 @@ SubscriptionManager::unsubBookChanges(SessionPtrType session)
void
SubscriptionManager::pubLedger(
ripple::LedgerInfo const& lgrInfo,
ripple::LedgerHeader const& lgrInfo,
ripple::Fees const& fees,
std::string const& ledgerRange,
std::uint32_t txnCount)
@@ -156,7 +157,7 @@ SubscriptionManager::pubLedger(
}
void
SubscriptionManager::pubTransaction(Backend::TransactionAndMetadata const& blobs, ripple::LedgerInfo const& lgrInfo)
SubscriptionManager::pubTransaction(Backend::TransactionAndMetadata const& blobs, ripple::LedgerHeader const& lgrInfo)
{
auto [tx, meta] = RPC::deserializeTxPlusMeta(blobs, lgrInfo.seq);
boost::json::object pubObj;
@@ -244,7 +245,7 @@ SubscriptionManager::pubTransaction(Backend::TransactionAndMetadata const& blobs
void
SubscriptionManager::pubBookChanges(
ripple::LedgerInfo const& lgrInfo,
ripple::LedgerHeader const& lgrInfo,
std::vector<Backend::TransactionAndMetadata> const& transactions)
{
auto const json = RPC::computeBookChanges(lgrInfo, transactions);

View File

@@ -24,13 +24,15 @@
#include <log/Logger.h>
#include <webserver/interface/ConnectionBase.h>
#include <ripple/protocol/LedgerHeader.h>
#include <memory>
using SessionPtrType = std::shared_ptr<Server::ConnectionBase>;
class Subscription
{
boost::asio::io_context::strand strand_;
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
std::unordered_set<SessionPtrType> subscribers_ = {};
std::atomic_uint64_t subCount_ = 0;
@@ -39,7 +41,7 @@ public:
Subscription(Subscription&) = delete;
Subscription(Subscription&&) = delete;
explicit Subscription(boost::asio::io_context& ioc) : strand_(ioc)
explicit Subscription(boost::asio::io_context& ioc) : strand_(ioc.get_executor())
{
}
@@ -72,7 +74,7 @@ class SubscriptionMap
{
using subscribers = std::set<SessionPtrType>;
boost::asio::io_context::strand strand_;
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
std::unordered_map<Key, subscribers> subscribers_ = {};
std::atomic_uint64_t subCount_ = 0;
@@ -81,7 +83,7 @@ public:
SubscriptionMap(SubscriptionMap&) = delete;
SubscriptionMap(SubscriptionMap&&) = delete;
explicit SubscriptionMap(boost::asio::io_context& ioc) : strand_(ioc)
explicit SubscriptionMap(boost::asio::io_context& ioc) : strand_(ioc.get_executor())
{
}
@@ -253,13 +255,15 @@ public:
void
pubLedger(
ripple::LedgerInfo const& lgrInfo,
ripple::LedgerHeader const& lgrInfo,
ripple::Fees const& fees,
std::string const& ledgerRange,
std::uint32_t txnCount);
void
pubBookChanges(ripple::LedgerInfo const& lgrInfo, std::vector<Backend::TransactionAndMetadata> const& transactions);
pubBookChanges(
ripple::LedgerHeader const& lgrInfo,
std::vector<Backend::TransactionAndMetadata> const& transactions);
void
unsubLedger(SessionPtrType session);
@@ -271,7 +275,7 @@ public:
unsubTransactions(SessionPtrType session);
void
pubTransaction(Backend::TransactionAndMetadata const& blobs, ripple::LedgerInfo const& lgrInfo);
pubTransaction(Backend::TransactionAndMetadata const& blobs, ripple::LedgerHeader const& lgrInfo);
void
subAccount(ripple::AccountID const& account, SessionPtrType const& session);

View File

@@ -19,38 +19,26 @@
#pragma once
#include <ripple/basics/Slice.h>
#include <ripple/basics/StringUtilities.h>
#include <ripple/protocol/LedgerHeader.h>
#include <sstream>
#include <string>
namespace util {
inline ripple::LedgerInfo
inline ripple::LedgerHeader
deserializeHeader(ripple::Slice data)
{
ripple::SerialIter sit(data.data(), data.size());
ripple::LedgerInfo info;
info.seq = sit.get32();
info.drops = sit.get64();
info.parentHash = sit.get256();
info.txHash = sit.get256();
info.accountHash = sit.get256();
info.parentCloseTime = ripple::NetClock::time_point{ripple::NetClock::duration{sit.get32()}};
info.closeTime = ripple::NetClock::time_point{ripple::NetClock::duration{sit.get32()}};
info.closeTimeResolution = ripple::NetClock::duration{sit.get8()};
info.closeFlags = sit.get8();
info.hash = sit.get256();
return info;
return ripple::deserializeHeader(data, /*hasHash=*/true);
}
inline std::string
toString(ripple::LedgerInfo const& info)
toString(ripple::LedgerHeader const& info)
{
std::stringstream ss;
ss << "LedgerInfo { Sequence : " << info.seq << " Hash : " << ripple::strHex(info.hash)
ss << "LedgerHeader { Sequence : " << info.seq << " Hash : " << ripple::strHex(info.hash)
<< " TxHash : " << strHex(info.txHash) << " AccountHash : " << ripple::strHex(info.accountHash)
<< " ParentHash : " << strHex(info.parentHash) << " }";
return ss.str();

27
unittests/Main.cpp Normal file
View File

@@ -0,0 +1,27 @@
//------------------------------------------------------------------------------
/*
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.
*/
//==============================================================================
#include <gtest/gtest.h>
int
main(int argc, char** argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -103,8 +103,8 @@ TEST_F(BackendCassandraFactoryTestWithDB, CreateCassandraBackend)
R"({{
"database":
{{
"type" : "cassandra",
"cassandra" : {{
"type": "cassandra",
"cassandra": {{
"contact_points": "{}",
"keyspace": "{}",
"replication_factor": 1
@@ -113,19 +113,22 @@ TEST_F(BackendCassandraFactoryTestWithDB, CreateCassandraBackend)
}})",
contactPoints,
keyspace))};
// FIXME: this currently throws runtime_error (Could not create keyspace). no idea why
auto backend = make_Backend(ctx, cfg);
EXPECT_TRUE(backend);
// empty db does not have ledger range
EXPECT_FALSE(backend->fetchLedgerRange());
// insert range table
Backend::Cassandra::Handle handle{contactPoints};
EXPECT_TRUE(handle.connect());
handle.execute(fmt::format("INSERT INTO {}.ledger_range (is_latest, sequence) VALUES (False, 100)", keyspace));
handle.execute(fmt::format("INSERT INTO {}.ledger_range (is_latest, sequence) VALUES (False, 100)", keyspace));
handle.execute(fmt::format("INSERT INTO {}.ledger_range (is_latest, sequence) VALUES (True, 500)", keyspace));
backend = make_Backend(ctx, cfg);
EXPECT_TRUE(backend);
auto const range = backend->fetchLedgerRange();
EXPECT_EQ(range->minSequence, 100);
EXPECT_EQ(range->maxSequence, 500);

View File

@@ -45,19 +45,10 @@ TEST_F(BackendCassandraExecutionStrategyTest, ReadOneInCoroutineSuccessful)
EXPECT_CALL(handle, asyncExecute(An<FakeStatement const&>(), An<std::function<void(FakeResultOrError)>&&>()))
.Times(1);
auto called = std::atomic_bool{false};
auto work = std::optional<boost::asio::io_context::work>{ctx};
boost::asio::spawn(ctx, [&work, &called, &strat](boost::asio::yield_context yield) {
runSpawn([&strat](boost::asio::yield_context yield) {
auto statement = FakeStatement{};
strat.read(yield, statement);
called = true;
work.reset();
});
ctx.run();
ASSERT_TRUE(called);
}
TEST_F(BackendCassandraExecutionStrategyTest, ReadOneInCoroutineThrowsOnTimeoutFailure)
@@ -67,26 +58,17 @@ TEST_F(BackendCassandraExecutionStrategyTest, ReadOneInCoroutineThrowsOnTimeoutF
ON_CALL(handle, asyncExecute(An<FakeStatement const&>(), An<std::function<void(FakeResultOrError)>&&>()))
.WillByDefault([](auto const&, auto&& cb) {
cb({}); // notify that item is ready
return FakeFutureWithCallback{
FakeResultOrError{CassandraError{"timeout", CASS_ERROR_LIB_REQUEST_TIMED_OUT}}};
auto res = FakeResultOrError{CassandraError{"timeout", CASS_ERROR_LIB_REQUEST_TIMED_OUT}};
cb(res); // notify that item is ready
return FakeFutureWithCallback{res};
});
EXPECT_CALL(handle, asyncExecute(An<FakeStatement const&>(), An<std::function<void(FakeResultOrError)>&&>()))
.Times(1);
auto called = std::atomic_bool{false};
auto work = std::optional<boost::asio::io_context::work>{ctx};
boost::asio::spawn(ctx, [&work, &called, &strat](boost::asio::yield_context yield) {
runSpawn([&strat](boost::asio::yield_context yield) {
auto statement = FakeStatement{};
EXPECT_THROW(strat.read(yield, statement), DatabaseTimeout);
called = true;
work.reset();
});
ctx.run();
ASSERT_TRUE(called);
}
TEST_F(BackendCassandraExecutionStrategyTest, ReadOneInCoroutineThrowsOnInvalidQueryFailure)
@@ -96,26 +78,17 @@ TEST_F(BackendCassandraExecutionStrategyTest, ReadOneInCoroutineThrowsOnInvalidQ
ON_CALL(handle, asyncExecute(An<FakeStatement const&>(), An<std::function<void(FakeResultOrError)>&&>()))
.WillByDefault([](auto const&, auto&& cb) {
cb({}); // notify that item is ready
return FakeFutureWithCallback{
FakeResultOrError{CassandraError{"invalid", CASS_ERROR_SERVER_INVALID_QUERY}}};
auto res = FakeResultOrError{CassandraError{"invalid", CASS_ERROR_SERVER_INVALID_QUERY}};
cb(res); // notify that item is ready
return FakeFutureWithCallback{res};
});
EXPECT_CALL(handle, asyncExecute(An<FakeStatement const&>(), An<std::function<void(FakeResultOrError)>&&>()))
.Times(1);
auto called = std::atomic_bool{false};
auto work = std::optional<boost::asio::io_context::work>{ctx};
boost::asio::spawn(ctx, [&work, &called, &strat](boost::asio::yield_context yield) {
runSpawn([&strat](boost::asio::yield_context yield) {
auto statement = FakeStatement{};
EXPECT_THROW(strat.read(yield, statement), std::runtime_error);
called = true;
work.reset();
});
ctx.run();
ASSERT_TRUE(called);
}
TEST_F(BackendCassandraExecutionStrategyTest, ReadBatchInCoroutineSuccessful)
@@ -134,19 +107,10 @@ TEST_F(BackendCassandraExecutionStrategyTest, ReadBatchInCoroutineSuccessful)
handle, asyncExecute(An<std::vector<FakeStatement> const&>(), An<std::function<void(FakeResultOrError)>&&>()))
.Times(1);
auto called = std::atomic_bool{false};
auto work = std::optional<boost::asio::io_context::work>{ctx};
boost::asio::spawn(ctx, [&work, &called, &strat](boost::asio::yield_context yield) {
runSpawn([&strat](boost::asio::yield_context yield) {
auto statements = std::vector<FakeStatement>(3);
strat.read(yield, statements);
called = true;
work.reset();
});
ctx.run();
ASSERT_TRUE(called);
}
TEST_F(BackendCassandraExecutionStrategyTest, ReadBatchInCoroutineThrowsOnTimeoutFailure)
@@ -158,27 +122,18 @@ TEST_F(BackendCassandraExecutionStrategyTest, ReadBatchInCoroutineThrowsOnTimeou
handle, asyncExecute(An<std::vector<FakeStatement> const&>(), An<std::function<void(FakeResultOrError)>&&>()))
.WillByDefault([](auto const& statements, auto&& cb) {
EXPECT_EQ(statements.size(), 3);
cb({}); // notify that item is ready
return FakeFutureWithCallback{
FakeResultOrError{CassandraError{"timeout", CASS_ERROR_LIB_REQUEST_TIMED_OUT}}};
auto res = FakeResultOrError{CassandraError{"timeout", CASS_ERROR_LIB_REQUEST_TIMED_OUT}};
cb(res); // notify that item is ready
return FakeFutureWithCallback{res};
});
EXPECT_CALL(
handle, asyncExecute(An<std::vector<FakeStatement> const&>(), An<std::function<void(FakeResultOrError)>&&>()))
.Times(1);
auto called = std::atomic_bool{false};
auto work = std::optional<boost::asio::io_context::work>{ctx};
boost::asio::spawn(ctx, [&work, &called, &strat](boost::asio::yield_context yield) {
runSpawn([&strat](boost::asio::yield_context yield) {
auto statements = std::vector<FakeStatement>(3);
EXPECT_THROW(strat.read(yield, statements), DatabaseTimeout);
called = true;
work.reset();
});
ctx.run();
ASSERT_TRUE(called);
}
TEST_F(BackendCassandraExecutionStrategyTest, ReadBatchInCoroutineThrowsOnInvalidQueryFailure)
@@ -190,27 +145,18 @@ TEST_F(BackendCassandraExecutionStrategyTest, ReadBatchInCoroutineThrowsOnInvali
handle, asyncExecute(An<std::vector<FakeStatement> const&>(), An<std::function<void(FakeResultOrError)>&&>()))
.WillByDefault([](auto const& statements, auto&& cb) {
EXPECT_EQ(statements.size(), 3);
cb({}); // notify that item is ready
return FakeFutureWithCallback{
FakeResultOrError{CassandraError{"invalid", CASS_ERROR_SERVER_INVALID_QUERY}}};
auto res = FakeResultOrError{CassandraError{"invalid", CASS_ERROR_SERVER_INVALID_QUERY}};
cb(res); // notify that item is ready
return FakeFutureWithCallback{res};
});
EXPECT_CALL(
handle, asyncExecute(An<std::vector<FakeStatement> const&>(), An<std::function<void(FakeResultOrError)>&&>()))
.Times(1);
auto called = std::atomic_bool{false};
auto work = std::optional<boost::asio::io_context::work>{ctx};
boost::asio::spawn(ctx, [&work, &called, &strat](boost::asio::yield_context yield) {
runSpawn([&strat](boost::asio::yield_context yield) {
auto statements = std::vector<FakeStatement>(3);
EXPECT_THROW(strat.read(yield, statements), std::runtime_error);
called = true;
work.reset();
});
ctx.run();
ASSERT_TRUE(called);
}
TEST_F(BackendCassandraExecutionStrategyTest, ReadBatchInCoroutineMarksBusyIfRequestsOutstandingExceeded)
@@ -233,21 +179,12 @@ TEST_F(BackendCassandraExecutionStrategyTest, ReadBatchInCoroutineMarksBusyIfReq
handle, asyncExecute(An<std::vector<FakeStatement> const&>(), An<std::function<void(FakeResultOrError)>&&>()))
.Times(1);
auto called = std::atomic_bool{false};
auto work = std::optional<boost::asio::io_context::work>{ctx};
boost::asio::spawn(ctx, [&work, &called, &strat](boost::asio::yield_context yield) {
runSpawn([&strat](boost::asio::yield_context yield) {
EXPECT_FALSE(strat.isTooBusy()); // 2 was the limit, 0 atm
auto statements = std::vector<FakeStatement>(3);
strat.read(yield, statements);
EXPECT_FALSE(strat.isTooBusy()); // after read completes it's 0 again
called = true;
work.reset();
});
ctx.run();
ASSERT_TRUE(called);
}
TEST_F(BackendCassandraExecutionStrategyTest, ReadEachInCoroutineSuccessful)
@@ -267,20 +204,11 @@ TEST_F(BackendCassandraExecutionStrategyTest, ReadEachInCoroutineSuccessful)
An<std::function<void(FakeResultOrError)>&&>()))
.Times(3); // once per statement
auto called = std::atomic_bool{false};
auto work = std::optional<boost::asio::io_context::work>{ctx};
boost::asio::spawn(ctx, [&work, &called, &strat](boost::asio::yield_context yield) {
runSpawn([&strat](boost::asio::yield_context yield) {
auto statements = std::vector<FakeStatement>(3);
auto res = strat.readEach(yield, statements);
EXPECT_EQ(res.size(), statements.size());
called = true;
work.reset();
});
ctx.run();
ASSERT_TRUE(called);
}
TEST_F(BackendCassandraExecutionStrategyTest, ReadEachInCoroutineThrowsOnFailure)
@@ -305,19 +233,10 @@ TEST_F(BackendCassandraExecutionStrategyTest, ReadEachInCoroutineThrowsOnFailure
An<std::function<void(FakeResultOrError)>&&>()))
.Times(3); // once per statement
auto called = std::atomic_bool{false};
auto work = std::optional<boost::asio::io_context::work>{ctx};
boost::asio::spawn(ctx, [&work, &called, &strat](boost::asio::yield_context yield) {
runSpawn([&strat](boost::asio::yield_context yield) {
auto statements = std::vector<FakeStatement>(3);
EXPECT_THROW(strat.readEach(yield, statements), DatabaseTimeout);
called = true;
work.reset();
});
ctx.run();
ASSERT_TRUE(called);
}
TEST_F(BackendCassandraExecutionStrategyTest, WriteSyncFirstTrySuccessful)

View File

@@ -229,8 +229,6 @@ TEST_F(RPCLedgerDataHandlerTest, NoMarker)
"ledger_index":"30",
"parent_close_time":0,
"parent_hash":"0000000000000000000000000000000000000000000000000000000000000000",
"seqNum":"30",
"totalCoins":"0",
"total_coins":"0",
"transaction_hash":"0000000000000000000000000000000000000000000000000000000000000000",
"closed":true
@@ -297,8 +295,6 @@ TEST_F(RPCLedgerDataHandlerTest, TypeFilter)
"ledger_index":"30",
"parent_close_time":0,
"parent_hash":"0000000000000000000000000000000000000000000000000000000000000000",
"seqNum":"30",
"totalCoins":"0",
"total_coins":"0",
"transaction_hash":"0000000000000000000000000000000000000000000000000000000000000000",
"closed":true
@@ -368,8 +364,6 @@ TEST_F(RPCLedgerDataHandlerTest, OutOfOrder)
"ledger_index":"30",
"parent_close_time":0,
"parent_hash":"0000000000000000000000000000000000000000000000000000000000000000",
"seqNum":"30",
"totalCoins":"0",
"total_coins":"0",
"transaction_hash":"0000000000000000000000000000000000000000000000000000000000000000",
"closed":true

View File

@@ -267,8 +267,6 @@ TEST_F(RPCLedgerHandlerTest, Default)
"ledger_index":"30",
"parent_close_time":0,
"parent_hash":"0000000000000000000000000000000000000000000000000000000000000000",
"seqNum":"30",
"totalCoins":"0",
"total_coins":"0",
"transaction_hash":"0000000000000000000000000000000000000000000000000000000000000000"
}
@@ -457,8 +455,6 @@ TEST_F(RPCLedgerHandlerTest, TransactionsExpandNotBinary)
"ledger_index":"30",
"parent_close_time":0,
"parent_hash":"0000000000000000000000000000000000000000000000000000000000000000",
"seqNum":"30",
"totalCoins":"0",
"total_coins":"0",
"transaction_hash":"0000000000000000000000000000000000000000000000000000000000000000",
"transactions":[
@@ -679,8 +675,6 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsEmtpy)
"ledger_index":"30",
"parent_close_time":0,
"parent_hash":"0000000000000000000000000000000000000000000000000000000000000000",
"seqNum":"30",
"totalCoins":"0",
"total_coins":"0",
"transaction_hash":"0000000000000000000000000000000000000000000000000000000000000000",
"transactions":[
@@ -771,9 +765,7 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsTrueBinaryFalse)
"ledger_index": "30",
"parent_close_time": 0,
"parent_hash": "0000000000000000000000000000000000000000000000000000000000000000",
"seqNum": "30",
"total_coins": "0",
"totalCoins": "0",
"transaction_hash": "0000000000000000000000000000000000000000000000000000000000000000",
"transactions": [
{

View File

@@ -615,6 +615,7 @@ TEST_F(RPCSubscribeHandlerTest, StreamsLedger)
auto const handler = AnyHandler{SubscribeHandler{mockBackendPtr, subManager_}};
auto const output = handler.process(input, Context{std::ref(yield), session_});
ASSERT_TRUE(output);
// FIXME: fee_ref is missing now. this is possibly correct. need to confirm:
EXPECT_EQ(output->as_object(), json::parse(expectedOutput));
std::this_thread::sleep_for(20ms);
auto const report = subManager_->report();

View File

@@ -19,7 +19,6 @@
#pragma once
#include <ripple/ledger/ReadView.h>
#include <webserver/interface/ConnectionBase.h>
#include <boost/asio/spawn.hpp>

View File

@@ -20,7 +20,8 @@
#pragma once
#include <ripple/basics/base_uint.h>
#include <ripple/ledger/ReadView.h>
#include <ripple/protocol/LedgerHeader.h>
#include <ripple/protocol/Protocol.h>
#include <string>

View File

@@ -21,7 +21,11 @@
#include <backend/Types.h>
#include <ripple/ledger/ReadView.h>
#include <ripple/protocol/LedgerHeader.h>
#include <ripple/protocol/Protocol.h>
#include <ripple/protocol/STBase.h>
#include <ripple/protocol/STTx.h>
#include <ripple/protocol/TxMeta.h>
#include <optional>
#include <string_view>

View File

@@ -788,12 +788,12 @@ TEST_F(WebRPCServerHandlerTest, HTTPTooBusy)
TEST_F(WebRPCServerHandlerTest, HTTPRequestNotJson)
{
static auto constexpr request = "not json";
static auto constexpr response = "Unable to parse request: syntax error";
static auto constexpr responsePrefix = "Unable to parse request: syntax error";
EXPECT_CALL(*rpcEngine, notifyBadSyntax).Times(1);
(*handler)(std::move(request), session);
EXPECT_EQ(session->message, response);
EXPECT_THAT(session->message, testing::StartsWith(responsePrefix));
EXPECT_EQ(session->lastStatus, boost::beast::http::status::bad_request);
}