Add clang tidy (#864)

Fixes #863
This commit is contained in:
Sergey Kuznetsov
2023-10-03 10:43:54 +01:00
committed by GitHub
parent 69f5025a29
commit 4b53bef1f5
198 changed files with 2168 additions and 1288 deletions

118
.clang-tidy Normal file
View File

@@ -0,0 +1,118 @@
---
Checks: '-*,
bugprone-argument-comment,
bugprone-assert-side-effect,
bugprone-bad-signal-to-kill-thread,
bugprone-bool-pointer-implicit-conversion,
bugprone-copy-constructor-init,
bugprone-dangling-handle,
bugprone-dynamic-static-initializers,
bugprone-fold-init-type,
bugprone-forward-declaration-namespace,
bugprone-inaccurate-erase,
bugprone-incorrect-roundings,
bugprone-infinite-loop,
bugprone-integer-division,
bugprone-lambda-function-name,
bugprone-macro-parentheses,
bugprone-macro-repeated-side-effects,
bugprone-misplaced-operator-in-strlen-in-alloc,
bugprone-misplaced-pointer-arithmetic-in-alloc,
bugprone-misplaced-widening-cast,
bugprone-move-forwarding-reference,
bugprone-multiple-statement-macro,
bugprone-no-escape,
bugprone-parent-virtual-call,
bugprone-posix-return,
bugprone-redundant-branch-condition,
bugprone-shared-ptr-array-mismatch,
bugprone-signal-handler,
bugprone-signed-char-misuse,
bugprone-sizeof-container,
bugprone-sizeof-expression,
bugprone-spuriously-wake-up-functions,
bugprone-standalone-empty,
bugprone-string-constructor,
bugprone-string-integer-assignment,
bugprone-string-literal-with-embedded-nul,
bugprone-stringview-nullptr,
bugprone-suspicious-enum-usage,
bugprone-suspicious-include,
bugprone-suspicious-memory-comparison,
bugprone-suspicious-memset-usage,
bugprone-suspicious-missing-comma,
bugprone-suspicious-realloc-usage,
bugprone-suspicious-semicolon,
bugprone-suspicious-string-compare,
bugprone-swapped-arguments,
bugprone-terminating-continue,
bugprone-throw-keyword-missing,
bugprone-too-small-loop-variable,
bugprone-undefined-memory-manipulation,
bugprone-undelegated-constructor,
bugprone-unhandled-exception-at-new,
bugprone-unhandled-self-assignment,
bugprone-unused-raii,
bugprone-unused-return-value,
bugprone-use-after-move,
bugprone-virtual-near-miss,
cppcoreguidelines-init-variables,
cppcoreguidelines-prefer-member-initializer,
cppcoreguidelines-pro-type-member-init,
cppcoreguidelines-pro-type-static-cast-downcast,
cppcoreguidelines-virtual-class-destructor,
llvm-namespace-comment,
misc-const-correctness,
misc-definitions-in-headers,
misc-misplaced-const,
misc-redundant-expression,
misc-static-assert,
misc-throw-by-value-catch-by-reference,
misc-unused-alias-decls,
misc-unused-using-decls,
modernize-concat-nested-namespaces,
modernize-deprecated-headers,
modernize-make-shared,
modernize-make-unique,
modernize-pass-by-value,
modernize-use-emplace,
modernize-use-equals-default,
modernize-use-equals-delete,
modernize-use-override,
modernize-use-using,
performance-faster-string-find,
performance-for-range-copy,
performance-implicit-conversion-in-loop,
performance-inefficient-vector-operation,
performance-move-const-arg,
performance-move-constructor-init,
performance-no-automatic-move,
performance-trivially-destructible,
readability-avoid-const-params-in-decls,
readability-braces-around-statements,
readability-const-return-type,
readability-container-contains,
readability-container-size-empty,
readability-convert-member-functions-to-static,
readability-duplicate-include,
readability-else-after-return,
readability-implicit-bool-conversion,
readability-inconsistent-declaration-parameter-name,
readability-make-member-function-const,
readability-misleading-indentation,
readability-non-const-parameter,
readability-redundant-declaration,
readability-redundant-member-init,
readability-redundant-string-init,
readability-simplify-boolean-expr,
readability-static-accessed-through-instance,
readability-static-definition-in-anonymous-namespace,
readability-suspicious-call-argument
'
CheckOptions:
readability-braces-around-statements.ShortStatementLines: 2
HeaderFilterRegex: '^.*/(src|unitests)/.*\.(h|hpp)$'
WarningsAsErrors: '*'

View File

@@ -27,10 +27,11 @@ runs:
shell: bash shell: bash
env: env:
BUILD_OPTION: "${{ inputs.conan_cache_hit == 'true' && 'missing' || '' }}" BUILD_OPTION: "${{ inputs.conan_cache_hit == 'true' && 'missing' || '' }}"
LINT: "${{ runner.os == 'Linux' && 'True' || 'False' }}"
run: | run: |
mkdir -p build mkdir -p build
cd build cd build
threads_num=${{ steps.mac_threads.outputs.num || steps.linux_threads.outputs.num }} threads_num=${{ steps.mac_threads.outputs.num || steps.linux_threads.outputs.num }}
conan install .. -of . -b $BUILD_OPTION -s build_type=Release -o clio:tests=True --profile ${{ inputs.conan_profile }} conan install .. -of . -b $BUILD_OPTION -s build_type=Release -o clio:tests=True -o clio:lint=$LINT --profile ${{ inputs.conan_profile }}
cmake -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release .. -G Ninja cmake -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release .. -G Ninja
cmake --build . --parallel $threads_num cmake --build . --parallel $threads_num

View File

@@ -82,10 +82,15 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Add llvm repo
run: |
echo 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-16 main' >> /etc/apt/sources.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
- name: Install packages - name: Install packages
run: | run: |
apt update -qq apt update -qq
apt install -y jq apt install -y jq clang-tidy-16
- name: Install ccache - name: Install ccache
run: | run: |

31
CMake/ClangTidy.cmake Normal file
View File

@@ -0,0 +1,31 @@
if (lint)
# Find clang-tidy binary
if (DEFINED ENV{CLIO_CLANG_TIDY_BIN})
set (_CLANG_TIDY_BIN $ENV{CLIO_CLANG_TIDY_BIN})
if ((NOT EXISTS ${_CLANG_TIDY_BIN}) OR IS_DIRECTORY ${_CLANG_TIDY_BIN})
message (FATAL_ERROR "$ENV{CLIO_CLANG_TIDY_BIN} no such file. Check CLIO_CLANG_TIDY_BIN env variable")
endif ()
message (STATUS "Using clang-tidy from CLIO_CLANG_TIDY_BIN")
else ()
find_program (_CLANG_TIDY_BIN NAMES "clang-tidy-16" "clang-tidy" REQUIRED)
endif ()
if (NOT _CLANG_TIDY_BIN)
message (FATAL_ERROR
"clang-tidy binary not found. Please set the CLIO_CLANG_TIDY_BIN environment variable or install clang-tidy.")
endif ()
# Support for https://github.com/matus-chochlik/ctcache
find_program (CLANG_TIDY_CACHE_PATH NAMES "clang-tidy-cache")
if (CLANG_TIDY_CACHE_PATH)
set (_CLANG_TIDY_CMD
"${CLANG_TIDY_CACHE_PATH};${_CLANG_TIDY_BIN}"
CACHE STRING "A combined command to run clang-tidy with caching wrapper")
else ()
set(_CLANG_TIDY_CMD "${_CLANG_TIDY_BIN}")
endif ()
set (CMAKE_CXX_CLANG_TIDY "${_CLANG_TIDY_CMD};--quiet")
message (STATUS "Using clang-tidy: ${CMAKE_CXX_CLANG_TIDY}")
endif ()

View File

@@ -19,13 +19,7 @@ set(COMPILER_FLAGS
-Wunused -Wunused
) )
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT lint)
list(APPEND COMPILER_FLAGS
-Wshadow # gcc is to aggressive with shadowing https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78147
)
endif ()
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
list(APPEND COMPILER_FLAGS list(APPEND COMPILER_FLAGS
-Wduplicated-branches -Wduplicated-branches
-Wduplicated-cond -Wduplicated-cond
@@ -34,6 +28,12 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
) )
endif () endif ()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
list(APPEND COMPILER_FLAGS
-Wshadow # gcc is to aggressive with shadowing https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78147
)
endif ()
# See https://github.com/cpp-best-practices/cppbestpractices/blob/master/02-Use_the_Tools_Available.md#gcc--clang for the flags description # See https://github.com/cpp-best-practices/cppbestpractices/blob/master/02-Use_the_Tools_Available.md#gcc--clang for the flags description
target_compile_options (clio PUBLIC ${COMPILER_FLAGS}) target_compile_options (clio PUBLIC ${COMPILER_FLAGS})

View File

@@ -9,6 +9,7 @@ option (tests "Build tests" FALSE)
option (docs "Generate doxygen docs" FALSE) option (docs "Generate doxygen docs" FALSE)
option (coverage "Build test coverage report" FALSE) option (coverage "Build test coverage report" FALSE)
option (packaging "Create distribution packages" FALSE) option (packaging "Create distribution packages" FALSE)
option (lint "Run clang-tidy checks during compilation" FALSE)
# ========================================================================== # # ========================================================================== #
set (san "" CACHE STRING "Add sanitizer instrumentation") set (san "" CACHE STRING "Add sanitizer instrumentation")
set (CMAKE_EXPORT_COMPILE_COMMANDS TRUE) set (CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
@@ -18,6 +19,7 @@ set_property (CACHE san PROPERTY STRINGS ";undefined;memory;address;thread")
# Include required modules # Include required modules
include (CMake/Ccache.cmake) include (CMake/Ccache.cmake)
include (CheckCXXCompilerFlag) include (CheckCXXCompilerFlag)
include (CMake/ClangTidy.cmake)
if (verbose) if (verbose)
set (CMAKE_VERBOSE_MAKEFILE TRUE) set (CMAKE_VERBOSE_MAKEFILE TRUE)

View File

@@ -237,6 +237,21 @@ Clio will fallback to hardcoded defaults when not specified in the config file o
of the minimum and maximum supported versions hardcoded in `src/rpc/common/APIVersion.h`. of the minimum and maximum supported versions hardcoded in `src/rpc/common/APIVersion.h`.
> **Note:** See `example-config.json` for more details. > **Note:** See `example-config.json` for more details.
## Using clang-tidy for static analysis
Minimum clang-tidy version required is 16.0.
Clang-tidy could be run by cmake during building the project.
For that provide the option `-o lint=True` for `conan install` command:
```sh
conan install .. --output-folder . --build missing --settings build_type=Release -o tests=True -o lint=True
```
By default cmake will try to find clang-tidy automatically in your system.
To force cmake use desired binary set `CLIO_CLANG_TIDY_BIN` environment variable as path to clang-tidy binary.
E.g.:
```sh
export CLIO_CLANG_TIDY_BIN=/opt/homebrew/opt/llvm@16/bin/clang-tidy
```
## Developing against `rippled` in standalone mode ## Developing against `rippled` in standalone mode
If you wish you develop against a `rippled` instance running in standalone If you wish you develop against a `rippled` instance running in standalone

View File

@@ -16,6 +16,7 @@ class Clio(ConanFile):
'docs': [True, False], # doxygen API docs; create custom target 'docs' 'docs': [True, False], # doxygen API docs; create custom target 'docs'
'packaging': [True, False], # create distribution packages 'packaging': [True, False], # create distribution packages
'coverage': [True, False], # build for test coverage report; create custom target `clio_tests-ccov` 'coverage': [True, False], # build for test coverage report; create custom target `clio_tests-ccov`
'lint': [True, False], # run clang-tidy checks during compilation
} }
requires = [ requires = [
@@ -33,6 +34,7 @@ class Clio(ConanFile):
'tests': False, 'tests': False,
'packaging': False, 'packaging': False,
'coverage': False, 'coverage': False,
'lint': False,
'docs': False, 'docs': False,
'xrpl/*:tests': False, 'xrpl/*:tests': False,
@@ -73,6 +75,7 @@ class Clio(ConanFile):
tc.variables['verbose'] = self.options.verbose tc.variables['verbose'] = self.options.verbose
tc.variables['tests'] = self.options.tests tc.variables['tests'] = self.options.tests
tc.variables['coverage'] = self.options.coverage tc.variables['coverage'] = self.options.coverage
tc.variables['lint'] = self.options.lint
tc.variables['docs'] = self.options.docs tc.variables['docs'] = self.options.docs
tc.variables['packaging'] = self.options.packaging tc.variables['packaging'] = self.options.packaging
tc.generate() tc.generate()

View File

@@ -34,10 +34,10 @@ namespace data {
* @param config The clio config to use * @param config The clio config to use
* @return A shared_ptr<BackendInterface> with the selected implementation * @return A shared_ptr<BackendInterface> with the selected implementation
*/ */
std::shared_ptr<BackendInterface> inline std::shared_ptr<BackendInterface>
make_Backend(util::Config const& config) make_Backend(util::Config const& config)
{ {
static util::Logger log{"Backend"}; static util::Logger const log{"Backend"};
LOG(log.info()) << "Constructing BackendInterface"; LOG(log.info()) << "Constructing BackendInterface";
auto const readOnly = config.valueOr("read_only", false); auto const readOnly = config.valueOr("read_only", false);

View File

@@ -67,16 +67,18 @@ BackendInterface::fetchLedgerObject(
LOG(gLog.trace()) << "Cache hit - " << ripple::strHex(key); LOG(gLog.trace()) << "Cache hit - " << ripple::strHex(key);
return *obj; 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";
}
else else
{ {
LOG(gLog.trace()) << "Cache miss - " << ripple::strHex(key); LOG(gLog.trace()) << "Missed cache but found in db";
auto dbObj = doFetchLedgerObject(key, sequence, yield);
if (!dbObj)
LOG(gLog.trace()) << "Missed cache and missed in db";
else
LOG(gLog.trace()) << "Missed cache but found in db";
return dbObj;
} }
return dbObj;
} }
std::vector<Blob> std::vector<Blob>
@@ -92,18 +94,22 @@ BackendInterface::fetchLedgerObjects(
{ {
auto obj = cache_.get(keys[i], sequence); auto obj = cache_.get(keys[i], sequence);
if (obj) if (obj)
{
results[i] = *obj; results[i] = *obj;
}
else else
{
misses.push_back(keys[i]); misses.push_back(keys[i]);
}
} }
LOG(gLog.trace()) << "Cache hits = " << keys.size() - misses.size() << " - cache misses = " << misses.size(); LOG(gLog.trace()) << "Cache hits = " << keys.size() - misses.size() << " - cache misses = " << misses.size();
if (misses.size()) if (!misses.empty())
{ {
auto objs = doFetchLedgerObjects(misses, sequence, yield); auto objs = doFetchLedgerObjects(misses, sequence, yield);
for (size_t i = 0, j = 0; i < results.size(); ++i) for (size_t i = 0, j = 0; i < results.size(); ++i)
{ {
if (results[i].size() == 0) if (results[i].empty())
{ {
results[i] = objs[j]; results[i] = objs[j];
++j; ++j;
@@ -122,9 +128,13 @@ BackendInterface::fetchSuccessorKey(
{ {
auto succ = cache_.getSuccessor(key, ledgerSequence); auto succ = cache_.getSuccessor(key, ledgerSequence);
if (succ) if (succ)
{
LOG(gLog.trace()) << "Cache hit - " << ripple::strHex(key); LOG(gLog.trace()) << "Cache hit - " << ripple::strHex(key);
}
else else
{
LOG(gLog.trace()) << "Cache miss - " << ripple::strHex(key); LOG(gLog.trace()) << "Cache miss - " << ripple::strHex(key);
}
return succ ? succ->key : doFetchSuccessorKey(key, ledgerSequence, yield); return succ ? succ->key : doFetchSuccessorKey(key, ledgerSequence, yield);
} }
@@ -181,11 +191,12 @@ BackendInterface::fetchBookOffers(
while (keys.size() < limit) while (keys.size() < limit)
{ {
++numPages; ++numPages;
ripple::STLedgerEntry sle{ripple::SerialIter{offerDir->blob.data(), offerDir->blob.size()}, offerDir->key}; ripple::STLedgerEntry const sle{
ripple::SerialIter{offerDir->blob.data(), offerDir->blob.size()}, offerDir->key};
auto indexes = sle.getFieldV256(ripple::sfIndexes); auto indexes = sle.getFieldV256(ripple::sfIndexes);
keys.insert(keys.end(), indexes.begin(), indexes.end()); keys.insert(keys.end(), indexes.begin(), indexes.end());
auto next = sle.getFieldU64(ripple::sfIndexNext); auto next = sle.getFieldU64(ripple::sfIndexNext);
if (!next) if (next == 0u)
{ {
LOG(gLog.trace()) << "Next is empty. breaking"; LOG(gLog.trace()) << "Next is empty. breaking";
break; break;
@@ -231,19 +242,23 @@ BackendInterface::hardFetchLedgerRange() const
std::optional<LedgerRange> std::optional<LedgerRange>
BackendInterface::fetchLedgerRange() const BackendInterface::fetchLedgerRange() const
{ {
std::shared_lock lck(rngMtx_); std::shared_lock const lck(rngMtx_);
return range; return range;
} }
void void
BackendInterface::updateRange(uint32_t newMax) BackendInterface::updateRange(uint32_t newMax)
{ {
std::scoped_lock lck(rngMtx_); std::scoped_lock const lck(rngMtx_);
assert(!range || newMax >= range->maxSequence); assert(!range || newMax >= range->maxSequence);
if (!range) if (!range)
{
range = {newMax, newMax}; range = {newMax, newMax};
}
else else
{
range->maxSequence = newMax; range->maxSequence = newMax;
}
} }
LedgerPage LedgerPage
@@ -260,20 +275,26 @@ BackendInterface::fetchLedgerPage(
bool reachedEnd = false; bool reachedEnd = false;
while (keys.size() < limit && !reachedEnd) while (keys.size() < limit && !reachedEnd)
{ {
ripple::uint256 const& curCursor = keys.size() ? keys.back() : cursor ? *cursor : firstKey; ripple::uint256 const& curCursor = !keys.empty() ? keys.back() : (cursor ? *cursor : firstKey);
std::uint32_t const seq = outOfOrder ? range->maxSequence : ledgerSequence; std::uint32_t const seq = outOfOrder ? range->maxSequence : ledgerSequence;
auto succ = fetchSuccessorKey(curCursor, seq, yield); auto succ = fetchSuccessorKey(curCursor, seq, yield);
if (!succ) if (!succ)
{
reachedEnd = true; reachedEnd = true;
}
else else
keys.push_back(std::move(*succ)); {
keys.push_back(*succ);
}
} }
auto objects = fetchLedgerObjects(keys, ledgerSequence, yield); auto objects = fetchLedgerObjects(keys, ledgerSequence, yield);
for (size_t i = 0; i < objects.size(); ++i) for (size_t i = 0; i < objects.size(); ++i)
{ {
if (objects[i].size()) if (!objects[i].empty())
page.objects.push_back({std::move(keys[i]), std::move(objects[i])}); {
page.objects.push_back({keys[i], std::move(objects[i])});
}
else if (!outOfOrder) else if (!outOfOrder)
{ {
LOG(gLog.error()) << "Deleted or non-existent object in successor table. key = " << ripple::strHex(keys[i]) LOG(gLog.error()) << "Deleted or non-existent object in successor table. key = " << ripple::strHex(keys[i])
@@ -286,7 +307,7 @@ BackendInterface::fetchLedgerPage(
LOG(gLog.error()) << msg.str(); LOG(gLog.error()) << msg.str();
} }
} }
if (keys.size() && !reachedEnd) if (!keys.empty() && !reachedEnd)
page.cursor = keys.back(); page.cursor = keys.back();
return page; return page;
@@ -307,7 +328,7 @@ BackendInterface::fetchFees(std::uint32_t const seq, boost::asio::yield_context
} }
ripple::SerialIter it(bytes->data(), bytes->size()); ripple::SerialIter it(bytes->data(), bytes->size());
ripple::SLE sle{it, key}; ripple::SLE const sle{it, key};
if (sle.getFieldIndex(ripple::sfBaseFee) != -1) if (sle.getFieldIndex(ripple::sfBaseFee) != -1)
fees.base = sle.getFieldU64(ripple::sfBaseFee); fees.base = sle.getFieldU64(ripple::sfBaseFee);

View File

@@ -48,6 +48,7 @@ public:
} }
}; };
static constexpr std::size_t DEFAULT_WAIT_BETWEEN_RETRY = 500;
/** /**
* @brief A helper function that catches DatabaseTimout exceptions and retries indefinitely. * @brief A helper function that catches DatabaseTimout exceptions and retries indefinitely.
* *
@@ -58,9 +59,9 @@ public:
*/ */
template <class FnType> template <class FnType>
auto auto
retryOnTimeout(FnType func, size_t waitMs = 500) retryOnTimeout(FnType func, size_t waitMs = DEFAULT_WAIT_BETWEEN_RETRY)
{ {
static util::Logger log{"Backend"}; static util::Logger const log{"Backend"};
while (true) while (true)
{ {
@@ -161,7 +162,7 @@ public:
* @return The ripple::LedgerHeader if found; nullopt otherwise * @return The ripple::LedgerHeader if found; nullopt otherwise
*/ */
virtual std::optional<ripple::LedgerHeader> virtual std::optional<ripple::LedgerHeader>
fetchLedgerBySequence(std::uint32_t const sequence, boost::asio::yield_context yield) const = 0; fetchLedgerBySequence(std::uint32_t sequence, boost::asio::yield_context yield) const = 0;
/** /**
* @brief Fetches a specific ledger by hash. * @brief Fetches a specific ledger by hash.
@@ -206,7 +207,7 @@ public:
* @return ripple::Fees if fees are found; nullopt otherwise * @return ripple::Fees if fees are found; nullopt otherwise
*/ */
std::optional<ripple::Fees> std::optional<ripple::Fees>
fetchFees(std::uint32_t const seq, boost::asio::yield_context yield) const; fetchFees(std::uint32_t seq, boost::asio::yield_context yield) const;
/** /**
* @brief Fetches a specific transaction. * @brief Fetches a specific transaction.
@@ -241,7 +242,7 @@ public:
virtual TransactionsAndCursor virtual TransactionsAndCursor
fetchAccountTransactions( fetchAccountTransactions(
ripple::AccountID const& account, ripple::AccountID const& account,
std::uint32_t const limit, std::uint32_t limit,
bool forward, bool forward,
std::optional<TransactionsCursor> const& cursor, std::optional<TransactionsCursor> const& cursor,
boost::asio::yield_context yield) const = 0; boost::asio::yield_context yield) const = 0;
@@ -254,7 +255,7 @@ public:
* @return Results as a vector of TransactionAndMetadata * @return Results as a vector of TransactionAndMetadata
*/ */
virtual std::vector<TransactionAndMetadata> virtual std::vector<TransactionAndMetadata>
fetchAllTransactionsInLedger(std::uint32_t const ledgerSequence, boost::asio::yield_context yield) const = 0; fetchAllTransactionsInLedger(std::uint32_t ledgerSequence, boost::asio::yield_context yield) const = 0;
/** /**
* @brief Fetches all transaction hashes from a specific ledger. * @brief Fetches all transaction hashes from a specific ledger.
@@ -264,7 +265,7 @@ public:
* @return Hashes as ripple::uint256 in a vector * @return Hashes as ripple::uint256 in a vector
*/ */
virtual std::vector<ripple::uint256> virtual std::vector<ripple::uint256>
fetchAllTransactionHashesInLedger(std::uint32_t const ledgerSequence, boost::asio::yield_context yield) const = 0; fetchAllTransactionHashesInLedger(std::uint32_t ledgerSequence, boost::asio::yield_context yield) const = 0;
/** /**
* @brief Fetches a specific NFT. * @brief Fetches a specific NFT.
@@ -275,8 +276,7 @@ public:
* @return NFT object on success; nullopt otherwise * @return NFT object on success; nullopt otherwise
*/ */
virtual std::optional<NFT> virtual std::optional<NFT>
fetchNFT(ripple::uint256 const& tokenID, std::uint32_t const ledgerSequence, boost::asio::yield_context yield) fetchNFT(ripple::uint256 const& tokenID, std::uint32_t ledgerSequence, boost::asio::yield_context yield) const = 0;
const = 0;
/** /**
* @brief Fetches all transactions for a specific NFT. * @brief Fetches all transactions for a specific NFT.
@@ -291,8 +291,8 @@ public:
virtual TransactionsAndCursor virtual TransactionsAndCursor
fetchNFTTransactions( fetchNFTTransactions(
ripple::uint256 const& tokenID, ripple::uint256 const& tokenID,
std::uint32_t const limit, std::uint32_t limit,
bool const forward, bool forward,
std::optional<TransactionsCursor> const& cursorIn, std::optional<TransactionsCursor> const& cursorIn,
boost::asio::yield_context yield) const = 0; boost::asio::yield_context yield) const = 0;
@@ -308,7 +308,7 @@ public:
* @return The object as a Blob on success; nullopt otherwise * @return The object as a Blob on success; nullopt otherwise
*/ */
std::optional<Blob> std::optional<Blob>
fetchLedgerObject(ripple::uint256 const& key, std::uint32_t const sequence, boost::asio::yield_context yield) const; fetchLedgerObject(ripple::uint256 const& key, std::uint32_t sequence, boost::asio::yield_context yield) const;
/** /**
* @brief Fetches all ledger objects by their keys. * @brief Fetches all ledger objects by their keys.
@@ -324,7 +324,7 @@ public:
std::vector<Blob> std::vector<Blob>
fetchLedgerObjects( fetchLedgerObjects(
std::vector<ripple::uint256> const& keys, std::vector<ripple::uint256> const& keys,
std::uint32_t const sequence, std::uint32_t sequence,
boost::asio::yield_context yield) const; boost::asio::yield_context yield) const;
/** /**
@@ -336,8 +336,7 @@ public:
* @return The object as a Blob on success; nullopt otherwise * @return The object as a Blob on success; nullopt otherwise
*/ */
virtual std::optional<Blob> virtual std::optional<Blob>
doFetchLedgerObject(ripple::uint256 const& key, std::uint32_t const sequence, boost::asio::yield_context yield) doFetchLedgerObject(ripple::uint256 const& key, std::uint32_t sequence, boost::asio::yield_context yield) const = 0;
const = 0;
/** /**
* @brief The database-specific implementation for fetching ledger objects. * @brief The database-specific implementation for fetching ledger objects.
@@ -350,7 +349,7 @@ public:
virtual std::vector<Blob> virtual std::vector<Blob>
doFetchLedgerObjects( doFetchLedgerObjects(
std::vector<ripple::uint256> const& keys, std::vector<ripple::uint256> const& keys,
std::uint32_t const sequence, std::uint32_t sequence,
boost::asio::yield_context yield) const = 0; boost::asio::yield_context yield) const = 0;
/** /**
@@ -361,7 +360,7 @@ public:
* @return A vector of LedgerObject representing the diff * @return A vector of LedgerObject representing the diff
*/ */
virtual std::vector<LedgerObject> virtual std::vector<LedgerObject>
fetchLedgerDiff(std::uint32_t const ledgerSequence, boost::asio::yield_context yield) const = 0; fetchLedgerDiff(std::uint32_t ledgerSequence, boost::asio::yield_context yield) const = 0;
/** /**
* @brief Fetches a page of ledger objects, ordered by key/index. * @brief Fetches a page of ledger objects, ordered by key/index.
@@ -376,8 +375,8 @@ public:
LedgerPage LedgerPage
fetchLedgerPage( fetchLedgerPage(
std::optional<ripple::uint256> const& cursor, std::optional<ripple::uint256> const& cursor,
std::uint32_t const ledgerSequence, std::uint32_t ledgerSequence,
std::uint32_t const limit, std::uint32_t limit,
bool outOfOrder, bool outOfOrder,
boost::asio::yield_context yield) const; boost::asio::yield_context yield) const;
@@ -390,8 +389,7 @@ public:
* @return The sucessor on success; nullopt otherwise * @return The sucessor on success; nullopt otherwise
*/ */
std::optional<LedgerObject> std::optional<LedgerObject>
fetchSuccessorObject(ripple::uint256 key, std::uint32_t const ledgerSequence, boost::asio::yield_context yield) fetchSuccessorObject(ripple::uint256 key, std::uint32_t ledgerSequence, boost::asio::yield_context yield) const;
const;
/** /**
* @brief Fetches the successor key. * @brief Fetches the successor key.
@@ -405,7 +403,7 @@ public:
* @return The sucessor key on success; nullopt otherwise * @return The sucessor key on success; nullopt otherwise
*/ */
std::optional<ripple::uint256> std::optional<ripple::uint256>
fetchSuccessorKey(ripple::uint256 key, std::uint32_t const ledgerSequence, boost::asio::yield_context yield) const; fetchSuccessorKey(ripple::uint256 key, std::uint32_t ledgerSequence, boost::asio::yield_context yield) const;
/** /**
* @brief Database-specific implementation of fetching the successor key * @brief Database-specific implementation of fetching the successor key
@@ -416,8 +414,7 @@ public:
* @return The sucessor on success; nullopt otherwise * @return The sucessor on success; nullopt otherwise
*/ */
virtual std::optional<ripple::uint256> virtual std::optional<ripple::uint256>
doFetchSuccessorKey(ripple::uint256 key, std::uint32_t const ledgerSequence, boost::asio::yield_context yield) doFetchSuccessorKey(ripple::uint256 key, std::uint32_t ledgerSequence, boost::asio::yield_context yield) const = 0;
const = 0;
/** /**
* @brief Fetches book offers. * @brief Fetches book offers.
@@ -431,8 +428,8 @@ public:
BookOffersPage BookOffersPage
fetchBookOffers( fetchBookOffers(
ripple::uint256 const& book, ripple::uint256 const& book,
std::uint32_t const ledgerSequence, std::uint32_t ledgerSequence,
std::uint32_t const limit, std::uint32_t limit,
boost::asio::yield_context yield) const; boost::asio::yield_context yield) const;
/** /**
@@ -478,7 +475,7 @@ public:
* @param blob The data to write * @param blob The data to write
*/ */
virtual void virtual void
writeLedgerObject(std::string&& key, std::uint32_t const seq, std::string&& blob); writeLedgerObject(std::string&& key, std::uint32_t seq, std::string&& blob);
/** /**
* @brief Writes a new transaction. * @brief Writes a new transaction.
@@ -492,8 +489,8 @@ public:
virtual void virtual void
writeTransaction( writeTransaction(
std::string&& hash, std::string&& hash,
std::uint32_t const seq, std::uint32_t seq,
std::uint32_t const date, std::uint32_t date,
std::string&& transaction, std::string&& transaction,
std::string&& metadata) = 0; std::string&& metadata) = 0;
@@ -529,7 +526,7 @@ public:
* @param successor The successor data to write * @param successor The successor data to write
*/ */
virtual void virtual void
writeSuccessor(std::string&& key, std::uint32_t const seq, std::string&& successor) = 0; writeSuccessor(std::string&& key, std::uint32_t seq, std::string&& successor) = 0;
/** /**
* @brief Starts a write transaction with the DB. No-op for cassandra. * @brief Starts a write transaction with the DB. No-op for cassandra.
@@ -548,7 +545,7 @@ public:
* @return true on success; false otherwise * @return true on success; false otherwise
*/ */
bool bool
finishWrites(std::uint32_t const ledgerSequence); finishWrites(std::uint32_t ledgerSequence);
/** /**
* @return true if database is overwhelmed; false otherwise * @return true if database is overwhelmed; false otherwise
@@ -558,7 +555,7 @@ public:
private: private:
virtual void virtual void
doWriteLedgerObject(std::string&& key, std::uint32_t const seq, std::string&& blob) = 0; doWriteLedgerObject(std::string&& key, std::uint32_t seq, std::string&& blob) = 0;
virtual bool virtual bool
doFinishWrites() = 0; doFinishWrites() = 0;

View File

@@ -112,11 +112,11 @@ public:
if (!rng) if (!rng)
return {{}, {}}; return {{}, {}};
Statement statement = [this, forward, &account]() { Statement const statement = [this, forward, &account]() {
if (forward) if (forward)
return schema_->selectAccountTxForward.bind(account); return schema_->selectAccountTxForward.bind(account);
else
return schema_->selectAccountTx.bind(account); return schema_->selectAccountTx.bind(account);
}(); }();
auto cursor = cursorIn; auto cursor = cursorIn;
@@ -288,7 +288,8 @@ public:
std::optional<LedgerRange> std::optional<LedgerRange>
hardFetchLedgerRange(boost::asio::yield_context yield) const override hardFetchLedgerRange(boost::asio::yield_context yield) const override
{ {
if (auto const res = executor_.read(yield, schema_->selectLedgerRange); res) auto const res = executor_.read(yield, schema_->selectLedgerRange);
if (res)
{ {
auto const& results = res.value(); auto const& results = res.value();
if (not results.hasRows()) if (not results.hasRows())
@@ -305,9 +306,13 @@ public:
for (auto [seq] : extract<uint32_t>(results)) for (auto [seq] : extract<uint32_t>(results))
{ {
if (idx == 0) if (idx == 0)
{
range.maxSequence = range.minSequence = seq; range.maxSequence = range.minSequence = seq;
}
else if (idx == 1) else if (idx == 1)
{
range.maxSequence = seq; range.maxSequence = seq;
}
++idx; ++idx;
} }
@@ -319,10 +324,7 @@ public:
<< range.maxSequence; << range.maxSequence;
return range; return range;
} }
else LOG(log_.error()) << "Could not fetch ledger range: " << res.error();
{
LOG(log_.error()) << "Could not fetch ledger range: " << res.error();
}
return std::nullopt; return std::nullopt;
} }
@@ -417,11 +419,11 @@ public:
if (!rng) if (!rng)
return {{}, {}}; return {{}, {}};
Statement statement = [this, forward, &tokenID]() { Statement const statement = [this, forward, &tokenID]() {
if (forward) if (forward)
return schema_->selectNFTTxForward.bind(tokenID); return schema_->selectNFTTxForward.bind(tokenID);
else
return schema_->selectNFTTx.bind(tokenID); return schema_->selectNFTTx.bind(tokenID);
}(); }();
auto cursor = cursorIn; auto cursor = cursorIn;
@@ -517,10 +519,8 @@ public:
auto [transaction, meta, seq, date] = *maybeValue; auto [transaction, meta, seq, date] = *maybeValue;
return std::make_optional<TransactionAndMetadata>(transaction, meta, seq, date); return std::make_optional<TransactionAndMetadata>(transaction, meta, seq, date);
} }
else
{ LOG(log_.debug()) << "Could not fetch transaction - no rows";
LOG(log_.debug()) << "Could not fetch transaction - no rows";
}
} }
else else
{ {
@@ -542,10 +542,8 @@ public:
return std::nullopt; return std::nullopt;
return *result; return *result;
} }
else
{ LOG(log_.debug()) << "Could not fetch successor - no rows";
LOG(log_.debug()) << "Could not fetch successor - no rows";
}
} }
else else
{ {
@@ -558,7 +556,7 @@ public:
std::vector<TransactionAndMetadata> std::vector<TransactionAndMetadata>
fetchTransactions(std::vector<ripple::uint256> const& hashes, boost::asio::yield_context yield) const override fetchTransactions(std::vector<ripple::uint256> const& hashes, boost::asio::yield_context yield) const override
{ {
if (hashes.size() == 0) if (hashes.empty())
return {}; return {};
auto const numHashes = hashes.size(); auto const numHashes = hashes.size();
@@ -583,8 +581,8 @@ public:
[](auto const& res) -> TransactionAndMetadata { [](auto const& res) -> TransactionAndMetadata {
if (auto const maybeRow = res.template get<Blob, Blob, uint32_t, uint32_t>(); maybeRow) if (auto const maybeRow = res.template get<Blob, Blob, uint32_t, uint32_t>(); maybeRow)
return *maybeRow; return *maybeRow;
else
return {}; return {};
}); });
}); });
@@ -600,7 +598,7 @@ public:
std::uint32_t const sequence, std::uint32_t const sequence,
boost::asio::yield_context yield) const override boost::asio::yield_context yield) const override
{ {
if (keys.size() == 0) if (keys.empty())
return {}; return {};
auto const numKeys = keys.size(); auto const numKeys = keys.size();
@@ -623,8 +621,8 @@ public:
std::cbegin(entries), std::cend(entries), std::back_inserter(results), [](auto const& res) -> Blob { std::cbegin(entries), std::cend(entries), std::back_inserter(results), [](auto const& res) -> Blob {
if (auto const maybeValue = res.template get<Blob>(); maybeValue) if (auto const maybeValue = res.template get<Blob>(); maybeValue)
return *maybeValue; return *maybeValue;
else
return {}; return {};
}); });
LOG(log_.trace()) << "Fetched " << numKeys << " objects"; LOG(log_.trace()) << "Fetched " << numKeys << " objects";
@@ -715,7 +713,7 @@ public:
std::back_inserter(statements), std::back_inserter(statements),
[this, &record](auto&& account) { [this, &record](auto&& account) {
return schema_->insertAccountTx.bind( return schema_->insertAccountTx.bind(
std::move(account), std::forward<decltype(account)>(account),
std::make_tuple(record.ledgerSequence, record.transactionIndex), std::make_tuple(record.ledgerSequence, record.transactionIndex),
record.txHash); record.txHash);
}); });

View File

@@ -36,8 +36,8 @@
struct AccountTransactionsData struct AccountTransactionsData
{ {
boost::container::flat_set<ripple::AccountID> accounts; boost::container::flat_set<ripple::AccountID> accounts;
std::uint32_t ledgerSequence; std::uint32_t ledgerSequence{};
std::uint32_t transactionIndex; std::uint32_t transactionIndex{};
ripple::uint256 txHash; ripple::uint256 txHash;
AccountTransactionsData(ripple::TxMeta& meta, ripple::uint256 const& txHash) AccountTransactionsData(ripple::TxMeta& meta, ripple::uint256 const& txHash)
@@ -149,8 +149,11 @@ template <class T>
inline bool inline bool
isOffer(T const& object) isOffer(T const& object)
{ {
short offer_bytes = (object[1] << 8) | object[2]; static constexpr short OFFER_OFFSET = 0x006f;
return offer_bytes == 0x006f; static constexpr short SHIFT = 8;
short offer_bytes = (object[1] << SHIFT) | object[2];
return offer_bytes == OFFER_OFFSET;
} }
/** /**
@@ -179,8 +182,9 @@ template <class T>
inline bool inline bool
isDirNode(T const& object) isDirNode(T const& object)
{ {
short spaceKey = (object.data()[1] << 8) | object.data()[2]; static constexpr short DIR_NODE_SPACE_KEY = 0x0064;
return spaceKey == 0x0064; short const spaceKey = (object.data()[1] << 8) | object.data()[2];
return spaceKey == DIR_NODE_SPACE_KEY;
} }
/** /**
@@ -212,7 +216,7 @@ inline ripple::uint256
getBook(T const& offer) getBook(T const& offer)
{ {
ripple::SerialIter it{offer.data(), offer.size()}; ripple::SerialIter it{offer.data(), offer.size()};
ripple::SLE sle{it, {}}; ripple::SLE const sle{it, {}};
ripple::uint256 book = sle.getFieldH256(ripple::sfBookDirectory); ripple::uint256 book = sle.getFieldH256(ripple::sfBookDirectory);
return book; return book;
@@ -228,10 +232,12 @@ template <class T>
inline ripple::uint256 inline ripple::uint256
getBookBase(T const& key) getBookBase(T const& key)
{ {
static constexpr size_t KEY_SIZE = 24;
assert(key.size() == ripple::uint256::size()); assert(key.size() == ripple::uint256::size());
ripple::uint256 ret; ripple::uint256 ret;
for (size_t i = 0; i < 24; ++i) for (size_t i = 0; i < KEY_SIZE; ++i)
ret.data()[i] = key.data()[i]; ret.data()[i] = key.data()[i];
return ret; return ret;
@@ -246,7 +252,7 @@ getBookBase(T const& key)
inline std::string inline std::string
uint256ToString(ripple::uint256 const& input) uint256ToString(ripple::uint256 const& input)
{ {
return {reinterpret_cast<const char*>(input.data()), input.size()}; return {reinterpret_cast<const char*>(input.data()), ripple::uint256::size()};
} }
/** @brief The ripple epoch start timestamp. Midnight on 1st January 2000. */ /** @brief The ripple epoch start timestamp. Midnight on 1st January 2000. */

View File

@@ -24,7 +24,7 @@ namespace data {
uint32_t uint32_t
LedgerCache::latestLedgerSequence() const LedgerCache::latestLedgerSequence() const
{ {
std::shared_lock lck{mtx_}; std::shared_lock const lck{mtx_};
return latestSeq_; return latestSeq_;
} }
@@ -35,7 +35,7 @@ LedgerCache::update(std::vector<LedgerObject> const& objs, uint32_t seq, bool is
return; return;
{ {
std::scoped_lock lck{mtx_}; std::scoped_lock const lck{mtx_};
if (seq > latestSeq_) if (seq > latestSeq_)
{ {
assert(seq == latestSeq_ + 1 || latestSeq_ == 0); assert(seq == latestSeq_ + 1 || latestSeq_ == 0);
@@ -43,9 +43,9 @@ LedgerCache::update(std::vector<LedgerObject> const& objs, uint32_t seq, bool is
} }
for (auto const& obj : objs) for (auto const& obj : objs)
{ {
if (obj.blob.size()) if (!obj.blob.empty())
{ {
if (isBackground && deletes_.count(obj.key)) if (isBackground && deletes_.contains(obj.key))
continue; continue;
auto& e = map_[obj.key]; auto& e = map_[obj.key];
@@ -69,7 +69,7 @@ LedgerCache::getSuccessor(ripple::uint256 const& key, uint32_t seq) const
{ {
if (!full_) if (!full_)
return {}; return {};
std::shared_lock lck{mtx_}; std::shared_lock const lck{mtx_};
successorReqCounter_++; successorReqCounter_++;
if (seq != latestSeq_) if (seq != latestSeq_)
return {}; return {};
@@ -85,7 +85,7 @@ LedgerCache::getPredecessor(ripple::uint256 const& key, uint32_t seq) const
{ {
if (!full_) if (!full_)
return {}; return {};
std::shared_lock lck{mtx_}; std::shared_lock const lck{mtx_};
if (seq != latestSeq_) if (seq != latestSeq_)
return {}; return {};
auto e = map_.lower_bound(key); auto e = map_.lower_bound(key);
@@ -98,7 +98,7 @@ LedgerCache::getPredecessor(ripple::uint256 const& key, uint32_t seq) const
std::optional<Blob> std::optional<Blob>
LedgerCache::get(ripple::uint256 const& key, uint32_t seq) const LedgerCache::get(ripple::uint256 const& key, uint32_t seq) const
{ {
std::shared_lock lck{mtx_}; std::shared_lock const lck{mtx_};
if (seq > latestSeq_) if (seq > latestSeq_)
return {}; return {};
objectReqCounter_++; objectReqCounter_++;
@@ -124,7 +124,7 @@ LedgerCache::setFull()
return; return;
full_ = true; full_ = true;
std::scoped_lock lck{mtx_}; std::scoped_lock const lck{mtx_};
deletes_.clear(); deletes_.clear();
} }
@@ -137,14 +137,14 @@ LedgerCache::isFull() const
size_t size_t
LedgerCache::size() const LedgerCache::size() const
{ {
std::shared_lock lck{mtx_}; std::shared_lock const lck{mtx_};
return map_.size(); return map_.size();
} }
float float
LedgerCache::getObjectHitRate() const LedgerCache::getObjectHitRate() const
{ {
if (!objectReqCounter_) if (objectReqCounter_ == 0u)
return 1; return 1;
return static_cast<float>(objectHitCounter_) / objectReqCounter_; return static_cast<float>(objectHitCounter_) / objectReqCounter_;
} }
@@ -152,7 +152,7 @@ LedgerCache::getObjectHitRate() const
float float
LedgerCache::getSuccessorHitRate() const LedgerCache::getSuccessorHitRate() const
{ {
if (!successorReqCounter_) if (successorReqCounter_ == 0u)
return 1; return 1;
return static_cast<float>(successorHitCounter_) / successorReqCounter_; return static_cast<float>(successorHitCounter_) / successorReqCounter_;
} }

View File

@@ -68,7 +68,7 @@ public:
* @param isBackground Should be set to true when writing old data from a background thread * @param isBackground Should be set to true when writing old data from a background thread
*/ */
void void
update(std::vector<LedgerObject> const& blobs, uint32_t seq, bool isBackground = false); update(std::vector<LedgerObject> const& objs, uint32_t seq, bool isBackground = false);
/** /**
* @brief Fetch a cached object by its key and sequence number. * @brief Fetch a cached object by its key and sequence number.

View File

@@ -24,6 +24,7 @@
#include <optional> #include <optional>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
namespace data { namespace data {
@@ -74,12 +75,8 @@ struct TransactionAndMetadata
std::uint32_t date = 0; std::uint32_t date = 0;
TransactionAndMetadata() = default; TransactionAndMetadata() = default;
TransactionAndMetadata( TransactionAndMetadata(Blob transaction, Blob metadata, std::uint32_t ledgerSequence, std::uint32_t date)
Blob const& transaction, : transaction{std::move(transaction)}, metadata{std::move(metadata)}, ledgerSequence{ledgerSequence}, date{date}
Blob const& metadata,
std::uint32_t ledgerSequence,
std::uint32_t date)
: transaction{transaction}, metadata{metadata}, ledgerSequence{ledgerSequence}, date{date}
{ {
} }
@@ -118,11 +115,6 @@ struct TransactionsCursor
{ {
} }
TransactionsCursor(TransactionsCursor const&) = default;
TransactionsCursor&
operator=(TransactionsCursor const&) = default;
bool bool
operator==(TransactionsCursor const& other) const = default; operator==(TransactionsCursor const& other) const = default;
@@ -148,18 +140,18 @@ struct TransactionsAndCursor
struct NFT struct NFT
{ {
ripple::uint256 tokenID; ripple::uint256 tokenID;
std::uint32_t ledgerSequence; std::uint32_t ledgerSequence{};
ripple::AccountID owner; ripple::AccountID owner;
Blob uri; Blob uri;
bool isBurned; bool isBurned{};
NFT() = default; NFT() = default;
NFT(ripple::uint256 const& tokenID, NFT(ripple::uint256 const& tokenID,
std::uint32_t ledgerSequence, std::uint32_t ledgerSequence,
ripple::AccountID const& owner, ripple::AccountID const& owner,
Blob const& uri, Blob uri,
bool isBurned) bool isBurned)
: tokenID{tokenID}, ledgerSequence{ledgerSequence}, owner{owner}, uri{uri}, isBurned{isBurned} : tokenID{tokenID}, ledgerSequence{ledgerSequence}, owner{owner}, uri{std::move(uri)}, isBurned{isBurned}
{ {
} }

View File

@@ -22,6 +22,7 @@
#include <cassandra.h> #include <cassandra.h>
#include <string> #include <string>
#include <utility>
namespace data::cassandra { namespace data::cassandra {
@@ -31,11 +32,11 @@ namespace data::cassandra {
class CassandraError class CassandraError
{ {
std::string message_; std::string message_;
uint32_t code_; uint32_t code_{};
public: public:
CassandraError() = default; // default constructible required by Expected CassandraError() = default; // default constructible required by Expected
CassandraError(std::string message, uint32_t code) : message_{message}, code_{code} CassandraError(std::string message, uint32_t code) : message_{std::move(message)}, code_{code}
{ {
} }
@@ -91,11 +92,9 @@ public:
bool bool
isTimeout() const isTimeout() const
{ {
if (code_ == CASS_ERROR_LIB_NO_HOSTS_AVAILABLE or code_ == CASS_ERROR_LIB_REQUEST_TIMED_OUT or return code_ == CASS_ERROR_LIB_NO_HOSTS_AVAILABLE or code_ == CASS_ERROR_LIB_REQUEST_TIMED_OUT or
code_ == CASS_ERROR_SERVER_UNAVAILABLE or code_ == CASS_ERROR_SERVER_OVERLOADED or code_ == CASS_ERROR_SERVER_UNAVAILABLE or code_ == CASS_ERROR_SERVER_OVERLOADED or
code_ == CASS_ERROR_SERVER_READ_TIMEOUT) code_ == CASS_ERROR_SERVER_READ_TIMEOUT;
return true;
return false;
} }
/** /**

View File

@@ -88,8 +88,9 @@ std::vector<Handle::FutureType>
Handle::asyncExecuteEach(std::vector<Statement> const& statements) const Handle::asyncExecuteEach(std::vector<Statement> const& statements) const
{ {
std::vector<Handle::FutureType> futures; std::vector<Handle::FutureType> futures;
futures.reserve(statements.size());
for (auto const& statement : statements) for (auto const& statement : statements)
futures.push_back(cass_session_execute(session_, statement)); futures.emplace_back(cass_session_execute(session_, statement));
return futures; return futures;
} }
@@ -98,7 +99,7 @@ Handle::executeEach(std::vector<Statement> const& statements) const
{ {
for (auto futures = asyncExecuteEach(statements); auto const& future : futures) for (auto futures = asyncExecuteEach(statements); auto const& future : futures)
{ {
if (auto const rc = future.await(); not rc) if (auto rc = future.await(); not rc)
return rc; return rc;
} }
@@ -145,11 +146,12 @@ Handle::asyncExecute(std::vector<Statement> const& statements, std::function<voi
Handle::PreparedStatementType Handle::PreparedStatementType
Handle::prepare(std::string_view query) const Handle::prepare(std::string_view query) const
{ {
Handle::FutureType future = cass_session_prepare(session_, query.data()); Handle::FutureType const future = cass_session_prepare(session_, query.data());
if (auto const rc = future.await(); rc) auto const rc = future.await();
if (rc)
return cass_future_get_prepared(future); return cass_future_get_prepared(future);
else
throw std::runtime_error(rc.error().message()); throw std::runtime_error(rc.error().message());
} }
} // namespace data::cassandra } // namespace data::cassandra

View File

@@ -20,6 +20,7 @@
#include <data/cassandra/SettingsProvider.h> #include <data/cassandra/SettingsProvider.h>
#include <data/cassandra/impl/Cluster.h> #include <data/cassandra/impl/Cluster.h>
#include <data/cassandra/impl/Statement.h> #include <data/cassandra/impl/Statement.h>
#include <util/Constants.h>
#include <util/config/Config.h> #include <util/config/Config.h>
#include <boost/json.hpp> #include <boost/json.hpp>
@@ -35,11 +36,13 @@ inline Settings::ContactPoints
tag_invoke(boost::json::value_to_tag<Settings::ContactPoints>, boost::json::value const& value) tag_invoke(boost::json::value_to_tag<Settings::ContactPoints>, boost::json::value const& value)
{ {
if (not value.is_object()) if (not value.is_object())
{
throw std::runtime_error( throw std::runtime_error(
"Feed entire Cassandra section to parse " "Feed entire Cassandra section to parse "
"Settings::ContactPoints instead"); "Settings::ContactPoints instead");
}
util::Config obj{value}; util::Config const obj{value};
Settings::ContactPoints out; Settings::ContactPoints out;
out.contactPoints = obj.valueOrThrow<std::string>("contact_points", "`contact_points` must be a string"); out.contactPoints = obj.valueOrThrow<std::string>("contact_points", "`contact_points` must be a string");
@@ -123,11 +126,11 @@ SettingsProvider::parseSettings() const
auto const connectTimeoutSecond = config_.maybeValue<uint32_t>("connect_timeout"); auto const connectTimeoutSecond = config_.maybeValue<uint32_t>("connect_timeout");
if (connectTimeoutSecond) if (connectTimeoutSecond)
settings.connectionTimeout = std::chrono::milliseconds{*connectTimeoutSecond * 1000}; settings.connectionTimeout = std::chrono::milliseconds{*connectTimeoutSecond * util::MILLISECONDS_PER_SECOND};
auto const requestTimeoutSecond = config_.maybeValue<uint32_t>("request_timeout"); auto const requestTimeoutSecond = config_.maybeValue<uint32_t>("request_timeout");
if (requestTimeoutSecond) if (requestTimeoutSecond)
settings.requestTimeout = std::chrono::milliseconds{*requestTimeoutSecond * 1000}; settings.requestTimeout = std::chrono::milliseconds{*requestTimeoutSecond * util::MILLISECONDS_PER_SECOND};
settings.certificate = parseOptionalCertificate(); settings.certificate = parseOptionalCertificate();
settings.username = config_.maybeValue<std::string>("username"); settings.username = config_.maybeValue<std::string>("username");

View File

@@ -98,20 +98,24 @@ private:
auto handler = [this, &handle, self](auto&& res) mutable { auto handler = [this, &handle, self](auto&& res) mutable {
if (res) if (res)
{ {
onComplete_(std::move(res)); onComplete_(std::forward<decltype(res)>(res));
} }
else else
{ {
if (retryPolicy_.shouldRetry(res.error())) if (retryPolicy_.shouldRetry(res.error()))
{
retryPolicy_.retry([self, &handle]() { self->execute(handle); }); retryPolicy_.retry([self, &handle]() { self->execute(handle); });
}
else else
onComplete_(std::move(res)); // report error {
onComplete_(std::forward<decltype(res)>(res)); // report error
}
} }
self = nullptr; // explicitly decrement refcount self = nullptr; // explicitly decrement refcount
}; };
std::scoped_lock lck{mtx_}; std::scoped_lock const lck{mtx_};
future_.emplace(handle.asyncExecute(data_, std::move(handler))); future_.emplace(handle.asyncExecute(data_, std::move(handler)));
} }
}; };

View File

@@ -26,8 +26,8 @@
#include <vector> #include <vector>
namespace { namespace {
static constexpr auto batchDeleter = [](CassBatch* ptr) { cass_batch_free(ptr); }; constexpr auto batchDeleter = [](CassBatch* ptr) { cass_batch_free(ptr); };
}; } // namespace
namespace data::cassandra::detail { namespace data::cassandra::detail {
@@ -38,8 +38,10 @@ Batch::Batch(std::vector<Statement> const& statements)
cass_batch_set_is_idempotent(*this, cass_true); cass_batch_set_is_idempotent(*this, cass_true);
for (auto const& statement : statements) for (auto const& statement : statements)
{
if (auto const res = add(statement); not res) if (auto const res = add(statement); not res)
throw std::runtime_error("Failed to add statement to batch: " + res.error()); throw std::runtime_error("Failed to add statement to batch: " + res.error());
}
} }
MaybeError MaybeError

View File

@@ -28,7 +28,7 @@
#include <vector> #include <vector>
namespace { namespace {
static constexpr auto clusterDeleter = [](CassCluster* ptr) { cass_cluster_free(ptr); }; constexpr auto clusterDeleter = [](CassCluster* ptr) { cass_cluster_free(ptr); };
template <class... Ts> template <class... Ts>
struct overloadSet : Ts... struct overloadSet : Ts...
@@ -102,8 +102,10 @@ Cluster::setupContactPoints(Settings::ContactPoints const& points)
using std::to_string; using std::to_string;
auto throwErrorIfNeeded = [](CassError rc, std::string const& label, std::string const& value) { auto throwErrorIfNeeded = [](CassError rc, std::string const& label, std::string const& value) {
if (rc != CASS_OK) if (rc != CASS_OK)
{
throw std::runtime_error( throw std::runtime_error(
fmt::format("Cassandra: Error setting {} [{}]: {}", label, value, cass_error_desc(rc))); fmt::format("Cassandra: Error setting {} [{}]: {}", label, value, cass_error_desc(rc)));
}
}; };
{ {
@@ -136,7 +138,7 @@ Cluster::setupCertificate(Settings const& settings)
return; return;
LOG(log_.debug()) << "Configure SSL context"; LOG(log_.debug()) << "Configure SSL context";
SslContext context = SslContext(*settings.certificate); SslContext const context = SslContext(*settings.certificate);
cass_cluster_set_ssl(*this, context); cass_cluster_set_ssl(*this, context);
} }

View File

@@ -40,6 +40,9 @@ namespace data::cassandra::detail {
*/ */
struct Settings struct Settings
{ {
static constexpr std::size_t DEFAULT_CONNECTION_TIMEOUT = 10000;
static constexpr uint32_t DEFAULT_MAX_WRITE_REQUESTS_OUTSTANDING = 10'000;
static constexpr uint32_t DEFAULT_MAX_READ_REQUESTS_OUTSTANDING = 100'000;
/** /**
* @brief Represents the configuration of contact points for cassandra. * @brief Represents the configuration of contact points for cassandra.
*/ */
@@ -61,7 +64,7 @@ struct Settings
bool enableLog = false; bool enableLog = false;
/** @brief Connect timeout specified in milliseconds */ /** @brief Connect timeout specified in milliseconds */
std::chrono::milliseconds connectionTimeout = std::chrono::milliseconds{10000}; std::chrono::milliseconds connectionTimeout = std::chrono::milliseconds{DEFAULT_CONNECTION_TIMEOUT};
/** @brief Request timeout specified in milliseconds */ /** @brief Request timeout specified in milliseconds */
std::chrono::milliseconds requestTimeout = std::chrono::milliseconds{0}; // no timeout at all std::chrono::milliseconds requestTimeout = std::chrono::milliseconds{0}; // no timeout at all
@@ -73,10 +76,10 @@ struct Settings
uint32_t threads = std::thread::hardware_concurrency(); uint32_t threads = std::thread::hardware_concurrency();
/** @brief The maximum number of outstanding write requests at any given moment */ /** @brief The maximum number of outstanding write requests at any given moment */
uint32_t maxWriteRequestsOutstanding = 10'000u; uint32_t maxWriteRequestsOutstanding = DEFAULT_MAX_WRITE_REQUESTS_OUTSTANDING;
/** @brief The maximum number of outstanding read requests at any given moment */ /** @brief The maximum number of outstanding read requests at any given moment */
uint32_t maxReadRequestsOutstanding = 100'000u; uint32_t maxReadRequestsOutstanding = DEFAULT_MAX_READ_REQUESTS_OUTSTANDING;
/** @brief The number of connection per host to always have active */ /** @brief The number of connection per host to always have active */
uint32_t coreConnectionsPerHost = 1u; uint32_t coreConnectionsPerHost = 1u;

View File

@@ -131,15 +131,14 @@ public:
{ {
while (true) while (true)
{ {
if (auto res = handle_.get().execute(statement); res) auto res = handle_.get().execute(statement);
if (res)
{ {
return res; return res;
} }
else
{ LOG(log_.warn()) << "Cassandra sync write error, retrying: " << res.error();
LOG(log_.warn()) << "Cassandra sync write error, retrying: " << res.error(); std::this_thread::sleep_for(std::chrono::milliseconds(5));
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
} }
} }
@@ -242,7 +241,7 @@ public:
future.emplace(handle_.get().asyncExecute(statements, [sself](auto&& res) mutable { future.emplace(handle_.get().asyncExecute(statements, [sself](auto&& res) mutable {
boost::asio::post( boost::asio::post(
boost::asio::get_associated_executor(*sself), boost::asio::get_associated_executor(*sself),
[sself, res = std::move(res)]() mutable { sself->complete(std::move(res)); }); [sself, res = std::forward<decltype(res)>(res)]() mutable { sself->complete(std::move(res)); });
})); }));
}; };
@@ -254,11 +253,9 @@ public:
{ {
return res; return res;
} }
else
{ LOG(log_.error()) << "Failed batch read in coroutine: " << res.error();
LOG(log_.error()) << "Failed batch read in coroutine: " << res.error(); throwErrorIfNeeded(res.error());
throwErrorIfNeeded(res.error());
}
} }
} }
@@ -287,7 +284,7 @@ public:
future.emplace(handle_.get().asyncExecute(statement, [sself](auto&& res) mutable { future.emplace(handle_.get().asyncExecute(statement, [sself](auto&& res) mutable {
boost::asio::post( boost::asio::post(
boost::asio::get_associated_executor(*sself), boost::asio::get_associated_executor(*sself),
[sself, res = std::move(res)]() mutable { sself->complete(std::move(res)); }); [sself, res = std::forward<decltype(res)>(res)]() mutable { sself->complete(std::move(res)); });
})); }));
}; };
@@ -296,14 +293,10 @@ public:
--numReadRequestsOutstanding_; --numReadRequestsOutstanding_;
if (res) if (res)
{
return res; return res;
}
else LOG(log_.error()) << "Failed read in coroutine: " << res.error();
{ throwErrorIfNeeded(res.error());
LOG(log_.error()) << "Failed read in coroutine: " << res.error();
throwErrorIfNeeded(res.error());
}
} }
} }
@@ -336,8 +329,10 @@ public:
// when all async operations complete unblock the result // when all async operations complete unblock the result
if (--numOutstanding == 0) if (--numOutstanding == 0)
{
boost::asio::post( boost::asio::post(
boost::asio::get_associated_executor(*sself), [sself]() mutable { sself->complete(); }); boost::asio::get_associated_executor(*sself), [sself]() mutable { sself->complete(); });
}
}; };
std::transform( std::transform(
@@ -400,18 +395,18 @@ private:
assert(false); assert(false);
throw std::runtime_error("decrementing num outstanding below 0"); throw std::runtime_error("decrementing num outstanding below 0");
} }
size_t cur = (--numWriteRequestsOutstanding_); size_t const cur = (--numWriteRequestsOutstanding_);
{ {
// mutex lock required to prevent race condition around spurious // mutex lock required to prevent race condition around spurious
// wakeup // wakeup
std::lock_guard lck(throttleMutex_); std::lock_guard const lck(throttleMutex_);
throttleCv_.notify_one(); throttleCv_.notify_one();
} }
if (cur == 0) if (cur == 0)
{ {
// mutex lock required to prevent race condition around spurious // mutex lock required to prevent race condition around spurious
// wakeup // wakeup
std::lock_guard lck(syncMutex_); std::lock_guard const lck(syncMutex_);
syncCv_.notify_one(); syncCv_.notify_one();
} }
} }

View File

@@ -25,7 +25,7 @@
#include <vector> #include <vector>
namespace { namespace {
static constexpr auto futureDeleter = [](CassFuture* ptr) { cass_future_free(ptr); }; constexpr auto futureDeleter = [](CassFuture* ptr) { cass_future_free(ptr); };
} // namespace } // namespace
namespace data::cassandra::detail { namespace data::cassandra::detail {
@@ -40,8 +40,8 @@ Future::await() const
if (auto const rc = cass_future_error_code(*this); rc) if (auto const rc = cass_future_error_code(*this); rc)
{ {
auto errMsg = [this](std::string const& label) { auto errMsg = [this](std::string const& label) {
char const* message; char const* message = nullptr;
std::size_t len; std::size_t len = 0;
cass_future_error_message(*this, &message, &len); cass_future_error_message(*this, &message, &len);
return label + ": " + std::string{message, len}; return label + ": " + std::string{message, len};
}(cass_error_desc(rc)); }(cass_error_desc(rc));
@@ -56,17 +56,15 @@ Future::get() const
if (auto const rc = cass_future_error_code(*this); rc) if (auto const rc = cass_future_error_code(*this); rc)
{ {
auto const errMsg = [this](std::string const& label) { auto const errMsg = [this](std::string const& label) {
char const* message; char const* message = nullptr;
std::size_t len; std::size_t len = 0;
cass_future_error_message(*this, &message, &len); cass_future_error_message(*this, &message, &len);
return label + ": " + std::string{message, len}; return label + ": " + std::string{message, len};
}("future::get()"); }("future::get()");
return Error{CassandraError{errMsg, rc}}; return Error{CassandraError{errMsg, rc}};
} }
else
{ return Result{cass_future_get_result(*this)};
return Result{cass_future_get_result(*this)};
}
} }
void void
@@ -80,8 +78,8 @@ invokeHelper(CassFuture* ptr, void* cbPtr)
if (auto const rc = cass_future_error_code(ptr); rc) if (auto const rc = cass_future_error_code(ptr); rc)
{ {
auto const errMsg = [&ptr](std::string const& label) { auto const errMsg = [&ptr](std::string const& label) {
char const* message; char const* message = nullptr;
std::size_t len; std::size_t len = 0;
cass_future_error_message(ptr, &message, &len); cass_future_error_message(ptr, &message, &len);
return label + ": " + std::string{message, len}; return label + ": " + std::string{message, len};
}("invokeHelper"); }("invokeHelper");

View File

@@ -38,7 +38,7 @@ struct Future : public ManagedObject<CassFuture>
}; };
void void
invokeHelper(CassFuture* ptr, void* self); invokeHelper(CassFuture* ptr, void* cbPtr);
class FutureWithCallback : public Future class FutureWithCallback : public Future
{ {

View File

@@ -36,7 +36,6 @@ public:
if (rawPtr == nullptr) if (rawPtr == nullptr)
throw std::runtime_error("Could not create DB object - got nullptr"); throw std::runtime_error("Could not create DB object - got nullptr");
} }
ManagedObject(ManagedObject&&) = default;
operator Managed*() const operator Managed*() const
{ {

View File

@@ -20,8 +20,8 @@
#include <data/cassandra/impl/Result.h> #include <data/cassandra/impl/Result.h>
namespace { namespace {
static constexpr auto resultDeleter = [](CassResult const* ptr) { cass_result_free(ptr); }; constexpr auto resultDeleter = [](CassResult const* ptr) { cass_result_free(ptr); };
static constexpr auto resultIteratorDeleter = [](CassIterator* ptr) { cass_iterator_free(ptr); }; constexpr auto resultIteratorDeleter = [](CassIterator* ptr) { cass_iterator_free(ptr); };
} // namespace } // namespace
namespace data::cassandra::detail { namespace data::cassandra::detail {
@@ -43,7 +43,7 @@ Result::hasRows() const
} }
/* implicit */ ResultIterator::ResultIterator(CassIterator* ptr) /* implicit */ ResultIterator::ResultIterator(CassIterator* ptr)
: ManagedObject{ptr, resultIteratorDeleter}, hasMore_{cass_iterator_next(ptr)} : ManagedObject{ptr, resultIteratorDeleter}, hasMore_{cass_iterator_next(ptr) != 0u}
{ {
} }
@@ -56,7 +56,7 @@ ResultIterator::fromResult(Result const& result)
[[maybe_unused]] bool [[maybe_unused]] bool
ResultIterator::moveForward() ResultIterator::moveForward()
{ {
hasMore_ = cass_iterator_next(*this); hasMore_ = (cass_iterator_next(*this) != 0u);
return hasMore_; return hasMore_;
} }

View File

@@ -57,24 +57,24 @@ extractColumn(CassRow const* row, std::size_t idx)
if constexpr (std::is_same_v<DecayedType, ripple::uint256>) if constexpr (std::is_same_v<DecayedType, ripple::uint256>)
{ {
cass_byte_t const* buf; cass_byte_t const* buf = nullptr;
std::size_t bufSize; std::size_t bufSize = 0;
auto const rc = cass_value_get_bytes(cass_row_get_column(row, idx), &buf, &bufSize); auto const rc = cass_value_get_bytes(cass_row_get_column(row, idx), &buf, &bufSize);
throwErrorIfNeeded(rc, "Extract ripple::uint256"); throwErrorIfNeeded(rc, "Extract ripple::uint256");
output = ripple::uint256::fromVoid(buf); output = ripple::uint256::fromVoid(buf);
} }
else if constexpr (std::is_same_v<DecayedType, ripple::AccountID>) else if constexpr (std::is_same_v<DecayedType, ripple::AccountID>)
{ {
cass_byte_t const* buf; cass_byte_t const* buf = nullptr;
std::size_t bufSize; std::size_t bufSize = 0;
auto const rc = cass_value_get_bytes(cass_row_get_column(row, idx), &buf, &bufSize); auto const rc = cass_value_get_bytes(cass_row_get_column(row, idx), &buf, &bufSize);
throwErrorIfNeeded(rc, "Extract ripple::AccountID"); throwErrorIfNeeded(rc, "Extract ripple::AccountID");
output = ripple::AccountID::fromVoid(buf); output = ripple::AccountID::fromVoid(buf);
} }
else if constexpr (std::is_same_v<DecayedType, UCharVectorType>) else if constexpr (std::is_same_v<DecayedType, UCharVectorType>)
{ {
cass_byte_t const* buf; cass_byte_t const* buf = nullptr;
std::size_t bufSize; std::size_t bufSize = 0;
auto const rc = cass_value_get_bytes(cass_row_get_column(row, idx), &buf, &bufSize); auto const rc = cass_value_get_bytes(cass_row_get_column(row, idx), &buf, &bufSize);
throwErrorIfNeeded(rc, "Extract vector<unsigned char>"); throwErrorIfNeeded(rc, "Extract vector<unsigned char>");
output = UCharVectorType{buf, buf + bufSize}; output = UCharVectorType{buf, buf + bufSize};
@@ -86,23 +86,23 @@ extractColumn(CassRow const* row, std::size_t idx)
} }
else if constexpr (std::is_convertible_v<DecayedType, std::string>) else if constexpr (std::is_convertible_v<DecayedType, std::string>)
{ {
char const* value; char const* value = nullptr;
std::size_t len; std::size_t len = 0;
auto const rc = cass_value_get_string(cass_row_get_column(row, idx), &value, &len); auto const rc = cass_value_get_string(cass_row_get_column(row, idx), &value, &len);
throwErrorIfNeeded(rc, "Extract string"); throwErrorIfNeeded(rc, "Extract string");
output = std::string{value, len}; output = std::string{value, len};
} }
else if constexpr (std::is_same_v<DecayedType, bool>) else if constexpr (std::is_same_v<DecayedType, bool>)
{ {
cass_bool_t flag; cass_bool_t flag = cass_bool_t::cass_false;
auto const rc = cass_value_get_bool(cass_row_get_column(row, idx), &flag); auto const rc = cass_value_get_bool(cass_row_get_column(row, idx), &flag);
throwErrorIfNeeded(rc, "Extract bool"); throwErrorIfNeeded(rc, "Extract bool");
output = flag ? true : false; output = flag != cass_bool_t::cass_false;
} }
// clio only uses bigint (int64_t) so we convert any incoming type // clio only uses bigint (int64_t) so we convert any incoming type
else if constexpr (std::is_convertible_v<DecayedType, int64_t>) else if constexpr (std::is_convertible_v<DecayedType, int64_t>)
{ {
int64_t out; int64_t out = 0;
auto const rc = cass_value_get_int64(cass_row_get_column(row, idx), &out); auto const rc = cass_value_get_int64(cass_row_get_column(row, idx), &out);
throwErrorIfNeeded(rc, "Extract int64"); throwErrorIfNeeded(rc, "Extract int64");
output = static_cast<DecayedType>(out); output = static_cast<DecayedType>(out);

View File

@@ -84,7 +84,7 @@ public:
/** /**
* @brief Calculates the wait time before attempting another retry * @brief Calculates the wait time before attempting another retry
*/ */
std::chrono::milliseconds static std::chrono::milliseconds
calculateDelay(uint32_t attempt) calculateDelay(uint32_t attempt)
{ {
return std::chrono::milliseconds{lround(std::pow(2, std::min(10u, attempt)))}; return std::chrono::milliseconds{lround(std::pow(2, std::min(10u, attempt)))};

View File

@@ -20,7 +20,7 @@
#include <data/cassandra/impl/SslContext.h> #include <data/cassandra/impl/SslContext.h>
namespace { namespace {
static constexpr auto contextDeleter = [](CassSsl* ptr) { cass_ssl_free(ptr); }; constexpr auto contextDeleter = [](CassSsl* ptr) { cass_ssl_free(ptr); };
} // namespace } // namespace
namespace data::cassandra::detail { namespace data::cassandra::detail {

View File

@@ -64,8 +64,6 @@ public:
cass_statement_set_is_idempotent(*this, cass_true); cass_statement_set_is_idempotent(*this, cass_true);
} }
Statement(Statement&&) = default;
/** /**
* @brief Binds the given arguments to the statement. * @brief Binds the given arguments to the statement.
* *
@@ -75,7 +73,7 @@ public:
void void
bind(Args&&... args) const bind(Args&&... args) const
{ {
std::size_t idx = 0; std::size_t idx = 0; // NOLINT(misc-const-correctness)
(this->bindAt<Args>(idx++, std::forward<Args>(args)), ...); (this->bindAt<Args>(idx++, std::forward<Args>(args)), ...);
} }
@@ -126,7 +124,7 @@ public:
} }
else if constexpr (std::is_same_v<DecayedType, UintTupleType>) else if constexpr (std::is_same_v<DecayedType, UintTupleType>)
{ {
auto const rc = cass_statement_bind_tuple(*this, idx, Tuple{std::move(value)}); auto const rc = cass_statement_bind_tuple(*this, idx, Tuple{std::forward<Type>(value)});
throwErrorIfNeeded(rc, "Bind tuple<uint32, uint32>"); throwErrorIfNeeded(rc, "Bind tuple<uint32, uint32>");
} }
else if constexpr (std::is_same_v<DecayedType, bool>) else if constexpr (std::is_same_v<DecayedType, bool>)

View File

@@ -20,8 +20,8 @@
#include <data/cassandra/impl/Tuple.h> #include <data/cassandra/impl/Tuple.h>
namespace { namespace {
static constexpr auto tupleDeleter = [](CassTuple* ptr) { cass_tuple_free(ptr); }; constexpr auto tupleDeleter = [](CassTuple* ptr) { cass_tuple_free(ptr); };
static constexpr auto tupleIteratorDeleter = [](CassIterator* ptr) { cass_iterator_free(ptr); }; constexpr auto tupleIteratorDeleter = [](CassIterator* ptr) { cass_iterator_free(ptr); };
} // namespace } // namespace
namespace data::cassandra::detail { namespace data::cassandra::detail {

View File

@@ -131,7 +131,7 @@ private:
// clio only uses bigint (int64_t) so we convert any incoming type // clio only uses bigint (int64_t) so we convert any incoming type
if constexpr (std::is_convertible_v<DecayedType, int64_t>) if constexpr (std::is_convertible_v<DecayedType, int64_t>)
{ {
int64_t out; int64_t out = 0;
auto const rc = cass_value_get_int64(cass_iterator_get_value(*this), &out); auto const rc = cass_value_get_int64(cass_iterator_get_value(*this), &out);
throwErrorIfNeeded(rc, "Extract int64 from tuple"); throwErrorIfNeeded(rc, "Extract int64 from tuple");
output = static_cast<DecayedType>(out); output = static_cast<DecayedType>(out);

View File

@@ -62,7 +62,7 @@ public:
void void
push(uint32_t idx) push(uint32_t idx)
{ {
std::lock_guard lck(m_); std::lock_guard const lck(m_);
if (!max_ || idx > *max_) if (!max_ || idx > *max_)
max_ = idx; max_ = idx;
cv_.notify_all(); cv_.notify_all();
@@ -96,9 +96,13 @@ public:
std::unique_lock lck(m_); std::unique_lock lck(m_);
auto pred = [sequence, this]() -> bool { return (max_ && sequence <= *max_); }; auto pred = [sequence, this]() -> bool { return (max_ && sequence <= *max_); };
if (maxWaitMs) if (maxWaitMs)
{
cv_.wait_for(lck, std::chrono::milliseconds(*maxWaitMs)); cv_.wait_for(lck, std::chrono::milliseconds(*maxWaitMs));
}
else else
{
cv_.wait(lck, pred); cv_.wait(lck, pred);
}
return pred(); return pred();
} }
}; };
@@ -190,7 +194,7 @@ public:
std::optional<T> std::optional<T>
tryPop() tryPop()
{ {
std::scoped_lock lck(m_); std::scoped_lock const lck(m_);
if (queue_.empty()) if (queue_.empty())
return {}; return {};
@@ -212,7 +216,7 @@ getMarkers(size_t numMarkers)
{ {
assert(numMarkers <= 256); assert(numMarkers <= 256);
unsigned char incr = 256 / numMarkers; unsigned char const incr = 256 / numMarkers;
std::vector<ripple::uint256> markers; std::vector<ripple::uint256> markers;
markers.reserve(numMarkers); markers.reserve(numMarkers);

View File

@@ -18,9 +18,12 @@
//============================================================================== //==============================================================================
#include <etl/ETLService.h> #include <etl/ETLService.h>
#include <util/Constants.h>
#include <ripple/protocol/LedgerHeader.h> #include <ripple/protocol/LedgerHeader.h>
#include <utility>
namespace etl { namespace etl {
// Database must be populated when this starts // Database must be populated when this starts
std::optional<uint32_t> std::optional<uint32_t>
@@ -44,8 +47,10 @@ ETLService::runETLPipeline(uint32_t startSequence, uint32_t numExtractors)
auto pipe = DataPipeType{numExtractors, startSequence}; auto pipe = DataPipeType{numExtractors, startSequence};
for (auto i = 0u; i < numExtractors; ++i) for (auto i = 0u; i < numExtractors; ++i)
{
extractors.push_back(std::make_unique<ExtractorType>( extractors.push_back(std::make_unique<ExtractorType>(
pipe, networkValidatedLedgers_, ledgerFetcher_, startSequence + i, finishSequence_, state_)); pipe, networkValidatedLedgers_, ledgerFetcher_, startSequence + i, finishSequence_, state_));
}
auto transformer = auto transformer =
TransformerType{pipe, backend_, ledgerLoader_, ledgerPublisher_, amendmentBlockHandler_, startSequence, state_}; TransformerType{pipe, backend_, ledgerLoader_, ledgerPublisher_, amendmentBlockHandler_, startSequence, state_};
@@ -58,8 +63,9 @@ ETLService::runETLPipeline(uint32_t startSequence, uint32_t numExtractors)
auto const end = std::chrono::system_clock::now(); auto const end = std::chrono::system_clock::now();
auto const lastPublishedSeq = ledgerPublisher_.getLastPublishedSequence(); auto const lastPublishedSeq = ledgerPublisher_.getLastPublishedSequence();
static constexpr auto NANOSECONDS_PER_SECOND = 1'000'000'000.0;
LOG(log_.debug()) << "Extracted and wrote " << lastPublishedSeq.value_or(startSequence) - startSequence << " in " LOG(log_.debug()) << "Extracted and wrote " << lastPublishedSeq.value_or(startSequence) - startSequence << " in "
<< ((end - begin).count()) / 1000000000.0; << ((end - begin).count()) / NANOSECONDS_PER_SECOND;
state_.isWriting = false; state_.isWriting = false;
@@ -154,7 +160,7 @@ ETLService::publishNextSequence(uint32_t nextSequence)
ledgerPublisher_.publish(nextSequence, {}); ledgerPublisher_.publish(nextSequence, {});
++nextSequence; ++nextSequence;
} }
else if (networkValidatedLedgers_->waitUntilValidatedByNetwork(nextSequence, 1000)) else if (networkValidatedLedgers_->waitUntilValidatedByNetwork(nextSequence, util::MILLISECONDS_PER_SECOND))
{ {
LOG(log_.info()) << "Ledger with sequence = " << nextSequence << " has been validated by the network. " LOG(log_.info()) << "Ledger with sequence = " << nextSequence << " has been validated by the network. "
<< "Attempting to find in database and publish"; << "Attempting to find in database and publish";
@@ -166,7 +172,7 @@ ETLService::publishNextSequence(uint32_t nextSequence)
// waits one second between each attempt to read the ledger from the // waits one second between each attempt to read the ledger from the
// database // database
constexpr size_t timeoutSeconds = 10; constexpr size_t timeoutSeconds = 10;
bool success = ledgerPublisher_.publish(nextSequence, timeoutSeconds); bool const success = ledgerPublisher_.publish(nextSequence, timeoutSeconds);
if (!success) if (!success)
{ {
@@ -199,14 +205,13 @@ ETLService::monitorReadOnly()
if (!rng) if (!rng)
{ {
if (auto net = networkValidatedLedgers_->getMostRecent()) if (auto net = networkValidatedLedgers_->getMostRecent())
{
return *net; return *net;
else }
return std::nullopt; return std::nullopt;
}
else
{
return rng->maxSequence;
} }
return rng->maxSequence;
}(); }();
if (!latestSequenceOpt.has_value()) if (!latestSequenceOpt.has_value())
@@ -230,7 +235,7 @@ ETLService::monitorReadOnly()
{ {
// if we can't, wait until it's validated by the network, or 1 second passes, whichever occurs first. // if we can't, wait until it's validated by the network, or 1 second passes, whichever occurs first.
// Even if we don't hear from rippled, if ledgers are being written to the db, we publish them. // Even if we don't hear from rippled, if ledgers are being written to the db, we publish them.
networkValidatedLedgers_->waitUntilValidatedByNetwork(latestSequence, 1000); networkValidatedLedgers_->waitUntilValidatedByNetwork(latestSequence, util::MILLISECONDS_PER_SECOND);
} }
} }
} }
@@ -251,9 +256,13 @@ ETLService::doWork()
beast::setCurrentThreadName("ETLService worker"); beast::setCurrentThreadName("ETLService worker");
if (state_.isReadOnly) if (state_.isReadOnly)
{
monitorReadOnly(); monitorReadOnly();
}
else else
{
monitor(); monitor();
}
}); });
} }
@@ -266,7 +275,7 @@ ETLService::ETLService(
std::shared_ptr<NetworkValidatedLedgersType> ledgers) std::shared_ptr<NetworkValidatedLedgersType> ledgers)
: backend_(backend) : backend_(backend)
, loadBalancer_(balancer) , loadBalancer_(balancer)
, networkValidatedLedgers_(ledgers) , networkValidatedLedgers_(std::move(ledgers))
, cacheLoader_(config, ioc, backend, backend->cache()) , cacheLoader_(config, ioc, backend, backend->cache())
, ledgerFetcher_(backend, balancer) , ledgerFetcher_(backend, balancer)
, ledgerLoader_(backend, balancer, ledgerFetcher_, state_) , ledgerLoader_(backend, balancer, ledgerFetcher_, state_)

View File

@@ -46,7 +46,7 @@ struct NFTTransactionsData;
struct NFTsData; struct NFTsData;
namespace feed { namespace feed {
class SubscriptionManager; class SubscriptionManager;
} } // namespace feed
/** /**
* @brief This namespace contains everything to do with the ETL and ETL sources. * @brief This namespace contains everything to do with the ETL and ETL sources.
@@ -252,7 +252,7 @@ private:
* @return true if stopping; false otherwise * @return true if stopping; false otherwise
*/ */
bool bool
isStopping() isStopping() const
{ {
return state_.isStopping; return state_.isStopping;
} }
@@ -265,7 +265,7 @@ private:
* @return the number of markers * @return the number of markers
*/ */
std::uint32_t std::uint32_t
getNumMarkers() getNumMarkers() const
{ {
return numMarkers_; return numMarkers_;
} }

View File

@@ -73,10 +73,15 @@ LoadBalancer::LoadBalancer(
std::shared_ptr<feed::SubscriptionManager> subscriptions, std::shared_ptr<feed::SubscriptionManager> subscriptions,
std::shared_ptr<NetworkValidatedLedgers> validatedLedgers) std::shared_ptr<NetworkValidatedLedgers> validatedLedgers)
{ {
static constexpr std::uint32_t MAX_DOWNLOAD = 256;
if (auto value = config.maybeValue<uint32_t>("num_markers"); value) if (auto value = config.maybeValue<uint32_t>("num_markers"); value)
downloadRanges_ = std::clamp(*value, 1u, 256u); {
downloadRanges_ = std::clamp(*value, 1u, MAX_DOWNLOAD);
}
else if (backend->fetchLedgerRange()) else if (backend->fetchLedgerRange())
{
downloadRanges_ = 4; downloadRanges_ = 4;
}
for (auto const& entry : config.array("etl_sources")) for (auto const& entry : config.array("etl_sources"))
{ {
@@ -101,10 +106,14 @@ LoadBalancer::loadInitialLedger(uint32_t sequence, bool cacheOnly)
auto [data, res] = source->loadInitialLedger(sequence, downloadRanges_, cacheOnly); auto [data, res] = source->loadInitialLedger(sequence, downloadRanges_, cacheOnly);
if (!res) if (!res)
{
LOG(log_.error()) << "Failed to download initial ledger." LOG(log_.error()) << "Failed to download initial ledger."
<< " Sequence = " << sequence << " source = " << source->toString(); << " Sequence = " << sequence << " source = " << source->toString();
}
else else
{
response = std::move(data); response = std::move(data);
}
return res; return res;
}, },
@@ -116,7 +125,7 @@ LoadBalancer::OptionalGetLedgerResponseType
LoadBalancer::fetchLedger(uint32_t ledgerSequence, bool getObjects, bool getObjectNeighbors) LoadBalancer::fetchLedger(uint32_t ledgerSequence, bool getObjects, bool getObjectNeighbors)
{ {
GetLedgerResponseType response; GetLedgerResponseType response;
bool success = execute( bool const success = execute(
[&response, ledgerSequence, getObjects, getObjectNeighbors, log = log_](auto& source) { [&response, ledgerSequence, getObjects, getObjectNeighbors, log = log_](auto& source) {
auto [status, data] = source->fetchLedger(ledgerSequence, getObjects, getObjectNeighbors); auto [status, data] = source->fetchLedger(ledgerSequence, getObjects, getObjectNeighbors);
response = std::move(data); response = std::move(data);
@@ -126,19 +135,18 @@ LoadBalancer::fetchLedger(uint32_t ledgerSequence, bool getObjects, bool getObje
<< " from source = " << source->toString(); << " from source = " << source->toString();
return true; return true;
} }
else
{ LOG(log.warn()) << "Could not fetch ledger " << ledgerSequence << ", Reply: " << response.DebugString()
LOG(log.warn()) << "Could not fetch ledger " << ledgerSequence << ", Reply: " << response.DebugString() << ", error_code: " << status.error_code() << ", error_msg: " << status.error_message()
<< ", error_code: " << status.error_code() << ", error_msg: " << status.error_message() << ", source = " << source->toString();
<< ", source = " << source->toString(); return false;
return false;
}
}, },
ledgerSequence); ledgerSequence);
if (success) if (success)
{
return response; return response;
else }
return {}; return {};
} }
std::optional<boost::json::object> std::optional<boost::json::object>
@@ -209,18 +217,16 @@ LoadBalancer::execute(Func f, uint32_t ledgerSequence)
This || true is only needed when loading full history standalone */ This || true is only needed when loading full history standalone */
if (source->hasLedger(ledgerSequence)) if (source->hasLedger(ledgerSequence))
{ {
bool res = f(source); bool const res = f(source);
if (res) if (res)
{ {
LOG(log_.debug()) << "Successfully executed func at source = " << source->toString() LOG(log_.debug()) << "Successfully executed func at source = " << source->toString()
<< " - ledger sequence = " << ledgerSequence; << " - ledger sequence = " << ledgerSequence;
break; break;
} }
else
{ LOG(log_.warn()) << "Failed to execute func at source = " << source->toString()
LOG(log_.warn()) << "Failed to execute func at source = " << source->toString() << " - ledger sequence = " << ledgerSequence;
<< " - ledger sequence = " << ledgerSequence;
}
} }
else else
{ {

View File

@@ -55,9 +55,12 @@ public:
using OptionalGetLedgerResponseType = std::optional<GetLedgerResponseType>; using OptionalGetLedgerResponseType = std::optional<GetLedgerResponseType>;
private: private:
static constexpr std::uint32_t DEFAULT_DOWNLOAD_RANGES = 16;
util::Logger log_{"ETL"}; util::Logger log_{"ETL"};
std::vector<std::unique_ptr<Source>> sources_; std::vector<std::unique_ptr<Source>> sources_;
std::uint32_t downloadRanges_ = 16; /*< The number of markers to use when downloading intial ledger */ std::uint32_t downloadRanges_ =
DEFAULT_DOWNLOAD_RANGES; /*< The number of markers to use when downloading intial ledger */
public: public:
/** /**

View File

@@ -106,8 +106,10 @@ getNFTokenMintData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx)
// There should always be a difference so the returned finalIDs // There should always be a difference so the returned finalIDs
// iterator should never be end(). But better safe than sorry. // iterator should never be end(). But better safe than sorry.
if (finalIDs.size() != prevIDs.size() + 1 || diff.first == finalIDs.end() || !owner) if (finalIDs.size() != prevIDs.size() + 1 || diff.first == finalIDs.end() || !owner)
{
throw std::runtime_error( throw std::runtime_error(
fmt::format(" - unexpected NFTokenMint data in tx {}", strHex(sttx.getTransactionID()))); fmt::format(" - unexpected NFTokenMint data in tx {}", strHex(sttx.getTransactionID())));
}
return { return {
{NFTTransactionsData(*diff.first, txMeta, sttx.getTransactionID())}, {NFTTransactionsData(*diff.first, txMeta, sttx.getTransactionID())},
@@ -147,8 +149,10 @@ getNFTokenBurnData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx)
prevNFTs = previousFields.getFieldArray(ripple::sfNFTokens); prevNFTs = previousFields.getFieldArray(ripple::sfNFTokens);
} }
else if (!prevNFTs && node.getFName() == ripple::sfDeletedNode) else if (!prevNFTs && node.getFName() == ripple::sfDeletedNode)
{
prevNFTs = prevNFTs =
node.peekAtField(ripple::sfFinalFields).downcast<ripple::STObject>().getFieldArray(ripple::sfNFTokens); node.peekAtField(ripple::sfFinalFields).downcast<ripple::STObject>().getFieldArray(ripple::sfNFTokens);
}
if (!prevNFTs) if (!prevNFTs)
continue; continue;
@@ -158,6 +162,7 @@ getNFTokenBurnData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx)
return candidate.getFieldH256(ripple::sfNFTokenID) == tokenID; return candidate.getFieldH256(ripple::sfNFTokenID) == tokenID;
}); });
if (nft != prevNFTs->end()) if (nft != prevNFTs->end())
{
return std::make_pair( return std::make_pair(
txs, txs,
NFTsData( NFTsData(
@@ -165,6 +170,7 @@ getNFTokenBurnData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx)
ripple::AccountID::fromVoid(node.getFieldH256(ripple::sfLedgerIndex).data()), ripple::AccountID::fromVoid(node.getFieldH256(ripple::sfLedgerIndex).data()),
txMeta, txMeta,
true)); true));
}
} }
std::stringstream msg; std::stringstream msg;
@@ -235,9 +241,11 @@ getNFTokenAcceptOfferData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx
ripple::STArray const& nfts = [&node] { ripple::STArray const& nfts = [&node] {
if (node.getFName() == ripple::sfCreatedNode) if (node.getFName() == ripple::sfCreatedNode)
{
return node.peekAtField(ripple::sfNewFields) return node.peekAtField(ripple::sfNewFields)
.downcast<ripple::STObject>() .downcast<ripple::STObject>()
.getFieldArray(ripple::sfNFTokens); .getFieldArray(ripple::sfNFTokens);
}
return node.peekAtField(ripple::sfFinalFields) return node.peekAtField(ripple::sfFinalFields)
.downcast<ripple::STObject>() .downcast<ripple::STObject>()
.getFieldArray(ripple::sfNFTokens); .getFieldArray(ripple::sfNFTokens);
@@ -247,9 +255,11 @@ getNFTokenAcceptOfferData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx
return candidate.getFieldH256(ripple::sfNFTokenID) == tokenID; return candidate.getFieldH256(ripple::sfNFTokenID) == tokenID;
}); });
if (nft != nfts.end()) if (nft != nfts.end())
{
return { return {
{NFTTransactionsData(tokenID, txMeta, sttx.getTransactionID())}, {NFTTransactionsData(tokenID, txMeta, sttx.getTransactionID())},
NFTsData(tokenID, nodeOwner, txMeta, false)}; NFTsData(tokenID, nodeOwner, txMeta, false)};
}
} }
std::stringstream msg; std::stringstream msg;

View File

@@ -46,6 +46,6 @@ getNFTDataFromTx(ripple::TxMeta const& txMeta, ripple::STTx const& sttx);
* @return The NFT data as a vector * @return The NFT data as a vector
*/ */
std::vector<NFTsData> std::vector<NFTsData>
getNFTDataFromObj(std::uint32_t const seq, std::string const& key, std::string const& blob); getNFTDataFromObj(std::uint32_t seq, std::string const& key, std::string const& blob);
} // namespace etl } // namespace etl

View File

@@ -147,7 +147,7 @@ ProbingSource::make_SSLHooks() noexcept
{ {
return {// onConnected return {// onConnected
[this](auto ec) { [this](auto ec) {
std::lock_guard lck(mtx_); std::lock_guard const lck(mtx_);
if (currentSrc_) if (currentSrc_)
return SourceHooks::Action::STOP; return SourceHooks::Action::STOP;
@@ -161,7 +161,7 @@ ProbingSource::make_SSLHooks() noexcept
}, },
// onDisconnected // onDisconnected
[this](auto /* ec */) { [this](auto /* ec */) {
std::lock_guard lck(mtx_); std::lock_guard const lck(mtx_);
if (currentSrc_) if (currentSrc_)
{ {
currentSrc_ = nullptr; currentSrc_ = nullptr;
@@ -176,7 +176,7 @@ ProbingSource::make_PlainHooks() noexcept
{ {
return {// onConnected return {// onConnected
[this](auto ec) { [this](auto ec) {
std::lock_guard lck(mtx_); std::lock_guard const lck(mtx_);
if (currentSrc_) if (currentSrc_)
return SourceHooks::Action::STOP; return SourceHooks::Action::STOP;
@@ -190,7 +190,7 @@ ProbingSource::make_PlainHooks() noexcept
}, },
// onDisconnected // onDisconnected
[this](auto /* ec */) { [this](auto /* ec */) {
std::lock_guard lck(mtx_); std::lock_guard const lck(mtx_);
if (currentSrc_) if (currentSrc_)
{ {
currentSrc_ = nullptr; currentSrc_ = nullptr;

View File

@@ -75,7 +75,7 @@ public:
LoadBalancer& balancer, LoadBalancer& balancer,
boost::asio::ssl::context sslCtx = boost::asio::ssl::context{boost::asio::ssl::context::tlsv12}); boost::asio::ssl::context sslCtx = boost::asio::ssl::context{boost::asio::ssl::context::tlsv12});
~ProbingSource() = default; ~ProbingSource() override = default;
void void
run() override; run() override;

View File

@@ -38,11 +38,11 @@
#include <boost/uuid/uuid.hpp> #include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp> #include <boost/uuid/uuid_generators.hpp>
#include <grpcpp/grpcpp.h> #include <grpcpp/grpcpp.h>
#include <utility>
class ProbingSource;
namespace feed { namespace feed {
class SubscriptionManager; class SubscriptionManager;
} } // namespace feed
// TODO: we use Source so that we can store a vector of Sources // TODO: we use Source so that we can store a vector of Sources
// but we also use CRTP for implementation of the common logic - this is a bit strange because CRTP as used here is // but we also use CRTP for implementation of the common logic - this is a bit strange because CRTP as used here is
@@ -51,6 +51,7 @@ class SubscriptionManager;
// things into the base class instead. // things into the base class instead.
namespace etl { namespace etl {
class ProbingSource;
/** /**
* @brief Base class for all ETL sources. * @brief Base class for all ETL sources.
@@ -206,7 +207,7 @@ class SourceImpl : public Source
LoadBalancer& balancer_; LoadBalancer& balancer_;
etl::detail::ForwardCache forwardCache_; etl::detail::ForwardCache forwardCache_;
boost::uuids::uuid uuid_; boost::uuids::uuid uuid_{};
protected: protected:
std::string ip_; std::string ip_;
@@ -245,15 +246,15 @@ public:
std::shared_ptr<NetworkValidatedLedgers> validatedLedgers, std::shared_ptr<NetworkValidatedLedgers> validatedLedgers,
LoadBalancer& balancer, LoadBalancer& balancer,
SourceHooks hooks) SourceHooks hooks)
: networkValidatedLedgers_(validatedLedgers) : networkValidatedLedgers_(std::move(validatedLedgers))
, backend_(backend) , backend_(std::move(backend))
, subscriptions_(subscriptions) , subscriptions_(std::move(subscriptions))
, balancer_(balancer) , balancer_(balancer)
, forwardCache_(config, ioc, *this) , forwardCache_(config, ioc, *this)
, strand_(boost::asio::make_strand(ioc)) , strand_(boost::asio::make_strand(ioc))
, timer_(strand_) , timer_(strand_)
, resolver_(strand_) , resolver_(strand_)
, hooks_(hooks) , hooks_(std::move(hooks))
{ {
static boost::uuids::random_generator uuidGenerator; static boost::uuids::random_generator uuidGenerator;
uuid_ = uuidGenerator(); uuid_ = uuidGenerator();
@@ -266,7 +267,7 @@ public:
grpcPort_ = *value; grpcPort_ = *value;
try try
{ {
boost::asio::ip::tcp::endpoint endpoint{boost::asio::ip::make_address(ip_), std::stoi(grpcPort_)}; boost::asio::ip::tcp::endpoint const endpoint{boost::asio::ip::make_address(ip_), std::stoi(grpcPort_)};
std::stringstream ss; std::stringstream ss;
ss << endpoint; ss << endpoint;
grpc::ChannelArguments chArgs; grpc::ChannelArguments chArgs;
@@ -282,7 +283,7 @@ public:
} }
} }
~SourceImpl() ~SourceImpl() override
{ {
derived().close(false); derived().close(false);
} }
@@ -316,7 +317,7 @@ public:
} }
namespace beast = boost::beast; namespace beast = boost::beast;
namespace http = beast::http; namespace http = boost::beast::http;
namespace websocket = beast::websocket; namespace websocket = beast::websocket;
namespace net = boost::asio; namespace net = boost::asio;
using tcp = boost::asio::ip::tcp; using tcp = boost::asio::ip::tcp;
@@ -324,7 +325,7 @@ public:
try try
{ {
auto executor = boost::asio::get_associated_executor(yield); auto executor = boost::asio::get_associated_executor(yield);
boost::beast::error_code ec; beast::error_code ec;
tcp::resolver resolver{executor}; tcp::resolver resolver{executor};
auto ws = std::make_unique<websocket::stream<beast::tcp_stream>>(executor); auto ws = std::make_unique<websocket::stream<beast::tcp_stream>>(executor);
@@ -384,14 +385,14 @@ public:
bool bool
hasLedger(uint32_t sequence) const override hasLedger(uint32_t sequence) const override
{ {
std::lock_guard lck(mtx_); std::lock_guard const lck(mtx_);
for (auto& pair : validatedLedgers_) for (auto& pair : validatedLedgers_)
{ {
if (sequence >= pair.first && sequence <= pair.second) if (sequence >= pair.first && sequence <= pair.second)
{ {
return true; return true;
} }
else if (sequence < pair.first) if (sequence < pair.first)
{ {
// validatedLedgers_ is a sorted list of disjoint ranges // validatedLedgers_ is a sorted list of disjoint ranges
// if the sequence comes before this range, the sequence will // if the sequence comes before this range, the sequence will
@@ -420,7 +421,7 @@ public:
request.set_get_object_neighbors(getObjectNeighbors); request.set_get_object_neighbors(getObjectNeighbors);
request.set_user("ETL"); request.set_user("ETL");
grpc::Status status = stub_->GetLedger(&context, request, &response); grpc::Status const status = stub_->GetLedger(&context, request, &response);
if (status.ok() && !response.is_unlimited()) if (status.ok() && !response.is_unlimited())
{ {
@@ -452,9 +453,11 @@ public:
auto last = getLastMsgTime(); auto last = getLastMsgTime();
if (last.time_since_epoch().count() != 0) if (last.time_since_epoch().count() != 0)
{
res["last_msg_age_seconds"] = std::to_string( res["last_msg_age_seconds"] = std::to_string(
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - getLastMsgTime()) std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - getLastMsgTime())
.count()); .count());
}
return res; return res;
} }
@@ -466,7 +469,7 @@ public:
return {{}, false}; return {{}, false};
grpc::CompletionQueue cq; grpc::CompletionQueue cq;
void* tag; void* tag = nullptr;
bool ok = false; bool ok = false;
std::vector<etl::detail::AsyncCallData> calls; std::vector<etl::detail::AsyncCallData> calls;
auto markers = getMarkers(numMarkers); auto markers = getMarkers(numMarkers);
@@ -488,7 +491,7 @@ public:
size_t numFinished = 0; size_t numFinished = 0;
bool abort = false; bool abort = false;
size_t incr = 500000; size_t const incr = 500000;
size_t progress = incr; size_t progress = incr;
std::vector<std::string> edgeKeys; std::vector<std::string> edgeKeys;
@@ -502,31 +505,29 @@ public:
LOG(log_.error()) << "loadInitialLedger - ok is false"; LOG(log_.error()) << "loadInitialLedger - ok is false";
return {{}, false}; // handle cancelled return {{}, false}; // handle cancelled
} }
else
LOG(log_.trace()) << "Marker prefix = " << ptr->getMarkerPrefix();
auto result = ptr->process(stub_, cq, *backend_, abort, cacheOnly);
if (result != etl::detail::AsyncCallData::CallStatus::MORE)
{ {
LOG(log_.trace()) << "Marker prefix = " << ptr->getMarkerPrefix(); ++numFinished;
LOG(log_.debug()) << "Finished a marker. "
<< "Current number of finished = " << numFinished;
auto result = ptr->process(stub_, cq, *backend_, abort, cacheOnly); std::string const lastKey = ptr->getLastKey();
if (result != etl::detail::AsyncCallData::CallStatus::MORE)
{
++numFinished;
LOG(log_.debug()) << "Finished a marker. "
<< "Current number of finished = " << numFinished;
std::string lastKey = ptr->getLastKey(); if (!lastKey.empty())
edgeKeys.push_back(ptr->getLastKey());
}
if (lastKey.size()) if (result == etl::detail::AsyncCallData::CallStatus::ERRORED)
edgeKeys.push_back(ptr->getLastKey()); abort = true;
}
if (result == etl::detail::AsyncCallData::CallStatus::ERRORED) if (backend_->cache().size() > progress)
abort = true; {
LOG(log_.info()) << "Downloaded " << backend_->cache().size() << " records from rippled";
if (backend_->cache().size() > progress) progress += incr;
{
LOG(log_.info()) << "Downloaded " << backend_->cache().size() << " records from rippled";
progress += incr;
}
} }
} }
@@ -577,7 +578,9 @@ public:
} }
else else
{ {
boost::beast::get_lowest_layer(derived().ws()).expires_after(std::chrono::seconds(30)); static constexpr std::size_t LOWEST_LAYER_TIMEOUT_SECONDS = 30;
boost::beast::get_lowest_layer(derived().ws())
.expires_after(std::chrono::seconds(LOWEST_LAYER_TIMEOUT_SECONDS));
boost::beast::get_lowest_layer(derived().ws()).async_connect(results, [this](auto ec, auto ep) { boost::beast::get_lowest_layer(derived().ws()).async_connect(results, [this](auto ec, auto ep) {
derived().onConnect(ec, ep); derived().onConnect(ec, ep);
}); });
@@ -602,7 +605,7 @@ public:
} }
else else
{ {
boost::json::object jv{ boost::json::object const jv{
{"command", "subscribe"}, {"command", "subscribe"},
{"streams", {"ledger", "manifests", "validations", "transactions_proposed"}}, {"streams", {"ledger", "manifests", "validations", "transactions_proposed"}},
}; };
@@ -632,9 +635,13 @@ public:
onWrite(boost::beast::error_code ec, [[maybe_unused]] size_t size) onWrite(boost::beast::error_code ec, [[maybe_unused]] size_t size)
{ {
if (ec) if (ec)
{
reconnect(ec); reconnect(ec);
}
else else
{
derived().ws().async_read(readBuffer_, [this](auto ec, size_t size) { onRead(ec, size); }); derived().ws().async_read(readBuffer_, [this](auto ec, size_t size) { onRead(ec, size); });
}
} }
/** /**
@@ -757,6 +764,7 @@ protected:
void void
reconnect(boost::beast::error_code ec) reconnect(boost::beast::error_code ec)
{ {
static constexpr std::size_t BUFFER_SIZE = 128;
if (paused_) if (paused_)
return; return;
@@ -776,7 +784,7 @@ protected:
boost::lexical_cast<std::string>(ERR_GET_REASON(ec.value())) + ") "; boost::lexical_cast<std::string>(ERR_GET_REASON(ec.value())) + ") ";
// ERR_PACK /* crypto/err/err.h */ // ERR_PACK /* crypto/err/err.h */
char buf[128]; char buf[BUFFER_SIZE];
::ERR_error_string_n(ec.value(), buf, sizeof(buf)); ::ERR_error_string_n(ec.value(), buf, sizeof(buf));
err += buf; err += buf;
@@ -793,11 +801,11 @@ protected:
} }
// exponentially increasing timeouts, with a max of 30 seconds // exponentially increasing timeouts, with a max of 30 seconds
size_t waitTime = std::min(pow(2, numFailures_), 30.0); size_t const waitTime = std::min(pow(2, numFailures_), 30.0);
numFailures_++; numFailures_++;
timer_.expires_after(boost::asio::chrono::seconds(waitTime)); timer_.expires_after(boost::asio::chrono::seconds(waitTime));
timer_.async_wait([this](auto ec) { timer_.async_wait([this](auto ec) {
bool startAgain = (ec != boost::asio::error::operation_aborted); bool const startAgain = (ec != boost::asio::error::operation_aborted);
derived().close(startAgain); derived().close(startAgain);
}); });
} }
@@ -806,14 +814,14 @@ private:
void void
setLastMsgTime() setLastMsgTime()
{ {
std::lock_guard lck(lastMsgTimeMtx_); std::lock_guard const lck(lastMsgTimeMtx_);
lastMsgTime_ = std::chrono::system_clock::now(); lastMsgTime_ = std::chrono::system_clock::now();
} }
std::chrono::system_clock::time_point std::chrono::system_clock::time_point
getLastMsgTime() const getLastMsgTime() const
{ {
std::lock_guard lck(lastMsgTimeMtx_); std::lock_guard const lck(lastMsgTimeMtx_);
return lastMsgTime_; return lastMsgTime_;
} }
@@ -831,21 +839,21 @@ private:
if (minAndMax.size() == 1) if (minAndMax.size() == 1)
{ {
uint32_t sequence = std::stoll(minAndMax[0]); uint32_t const sequence = std::stoll(minAndMax[0]);
pairs.push_back(std::make_pair(sequence, sequence)); pairs.emplace_back(sequence, sequence);
} }
else else
{ {
assert(minAndMax.size() == 2); assert(minAndMax.size() == 2);
uint32_t min = std::stoll(minAndMax[0]); uint32_t const min = std::stoll(minAndMax[0]);
uint32_t max = std::stoll(minAndMax[1]); uint32_t const max = std::stoll(minAndMax[1]);
pairs.push_back(std::make_pair(min, max)); pairs.emplace_back(min, max);
} }
} }
std::sort(pairs.begin(), pairs.end(), [](auto left, auto right) { return left.first < right.first; }); std::sort(pairs.begin(), pairs.end(), [](auto left, auto right) { return left.first < right.first; });
// we only hold the lock here, to avoid blocking while string processing // we only hold the lock here, to avoid blocking while string processing
std::lock_guard lck(mtx_); std::lock_guard const lck(mtx_);
validatedLedgers_ = std::move(pairs); validatedLedgers_ = std::move(pairs);
validatedLedgersRaw_ = range; validatedLedgersRaw_ = range;
} }
@@ -853,7 +861,7 @@ private:
std::string std::string
getValidatedRange() const getValidatedRange() const
{ {
std::lock_guard lck(mtx_); std::lock_guard const lck(mtx_);
return validatedLedgersRaw_; return validatedLedgersRaw_;
} }
}; };

View File

@@ -35,7 +35,7 @@ struct AmendmentBlockAction
void void
operator()() operator()()
{ {
static util::Logger log{"ETL"}; static util::Logger const log{"ETL"};
LOG(log.fatal()) LOG(log.fatal())
<< "Can't process new ledgers: The current ETL source is not compatible with the version of the " << "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."; "libxrpl Clio is currently using. Please upgrade Clio to a newer version.";

View File

@@ -19,6 +19,7 @@
#pragma once #pragma once
#include <data/BackendInterface.h>
#include <etl/NFTHelpers.h> #include <etl/NFTHelpers.h>
#include <util/log/Logger.h> #include <util/log/Logger.h>
@@ -48,14 +49,14 @@ public:
request_.mutable_ledger()->set_sequence(seq); request_.mutable_ledger()->set_sequence(seq);
if (marker.isNonZero()) if (marker.isNonZero())
{ {
request_.set_marker(marker.data(), marker.size()); request_.set_marker(marker.data(), ripple::uint256::size());
} }
request_.set_user("ETL"); request_.set_user("ETL");
nextPrefix_ = 0x00; nextPrefix_ = 0x00;
if (nextMarker) if (nextMarker)
nextPrefix_ = nextMarker->data()[0]; nextPrefix_ = nextMarker->data()[0];
unsigned char prefix = marker.data()[0]; unsigned char const prefix = marker.data()[0];
LOG(log_.debug()) << "Setting up AsyncCallData. marker = " << ripple::strHex(marker) LOG(log_.debug()) << "Setting up AsyncCallData. marker = " << ripple::strHex(marker)
<< " . prefix = " << ripple::strHex(std::string(1, prefix)) << " . prefix = " << ripple::strHex(std::string(1, prefix))
@@ -102,18 +103,18 @@ public:
bool more = true; bool more = true;
// if no marker returned, we are done // if no marker returned, we are done
if (cur_->marker().size() == 0) if (cur_->marker().empty())
more = false; more = false;
// if returned marker is greater than our end, we are done // if returned marker is greater than our end, we are done
unsigned char prefix = cur_->marker()[0]; unsigned char const prefix = cur_->marker()[0];
if (nextPrefix_ != 0x00 && prefix >= nextPrefix_) if (nextPrefix_ != 0x00 && prefix >= nextPrefix_)
more = false; more = false;
// if we are not done, make the next async call // if we are not done, make the next async call
if (more) if (more)
{ {
request_.set_marker(std::move(cur_->marker())); request_.set_marker(cur_->marker());
call(stub, cq); call(stub, cq);
} }
@@ -136,7 +137,7 @@ public:
{obj.mutable_data()->begin(), obj.mutable_data()->end()}}); {obj.mutable_data()->begin(), obj.mutable_data()->end()}});
if (!cacheOnly) if (!cacheOnly)
{ {
if (lastKey_.size()) if (!lastKey_.empty())
backend.writeSuccessor(std::move(lastKey_), request_.ledger().sequence(), std::string{obj.key()}); backend.writeSuccessor(std::move(lastKey_), request_.ledger().sequence(), std::string{obj.key()});
lastKey_ = obj.key(); lastKey_ = obj.key();
backend.writeNFTs(getNFTDataFromObj(request_.ledger().sequence(), obj.key(), obj.data())); backend.writeNFTs(getNFTDataFromObj(request_.ledger().sequence(), obj.key(), obj.data()));
@@ -166,10 +167,11 @@ public:
std::string std::string
getMarkerPrefix() getMarkerPrefix()
{ {
if (next_->marker().size() == 0) if (next_->marker().empty())
{
return ""; return "";
else }
return ripple::strHex(std::string{next_->marker().data()[0]}); return ripple::strHex(std::string{next_->marker().data()[0]});
} }
std::string std::string

View File

@@ -42,6 +42,10 @@ namespace etl::detail {
template <typename CacheType> template <typename CacheType>
class CacheLoader class CacheLoader
{ {
static constexpr size_t DEFAULT_NUM_CACHE_DIFFS = 32;
static constexpr size_t DEFAULT_NUM_CACHE_MARKERS = 48;
static constexpr size_t DEFAULT_CACHE_PAGE_FETCH_SIZE = 512;
enum class LoadStyle { ASYNC, SYNC, NOT_AT_ALL }; enum class LoadStyle { ASYNC, SYNC, NOT_AT_ALL };
util::Logger log_{"ETL"}; util::Logger log_{"ETL"};
@@ -52,18 +56,18 @@ class CacheLoader
LoadStyle cacheLoadStyle_ = LoadStyle::ASYNC; LoadStyle cacheLoadStyle_ = LoadStyle::ASYNC;
// number of diffs to use to generate cursors to traverse the ledger in parallel during initial cache download // number of diffs to use to generate cursors to traverse the ledger in parallel during initial cache download
size_t numCacheDiffs_ = 32; size_t numCacheDiffs_ = DEFAULT_NUM_CACHE_DIFFS;
// number of markers to use at one time to traverse the ledger in parallel during initial cache download // number of markers to use at one time to traverse the ledger in parallel during initial cache download
size_t numCacheMarkers_ = 48; size_t numCacheMarkers_ = DEFAULT_NUM_CACHE_MARKERS;
// number of ledger objects to fetch concurrently per marker during cache download // number of ledger objects to fetch concurrently per marker during cache download
size_t cachePageFetchSize_ = 512; size_t cachePageFetchSize_ = DEFAULT_CACHE_PAGE_FETCH_SIZE;
struct ClioPeer struct ClioPeer
{ {
std::string ip; std::string ip;
int port; int port{};
}; };
std::vector<ClioPeer> clioPeers_; std::vector<ClioPeer> clioPeers_;
@@ -107,7 +111,7 @@ public:
clioPeers_.push_back({ip, port}); clioPeers_.push_back({ip, port});
} }
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); unsigned const seed = std::chrono::system_clock::now().time_since_epoch().count();
std::shuffle(std::begin(clioPeers_), std::end(clioPeers_), std::default_random_engine(seed)); std::shuffle(std::begin(clioPeers_), std::end(clioPeers_), std::default_random_engine(seed));
} }
} }
@@ -142,7 +146,7 @@ public:
return; return;
} }
if (clioPeers_.size() > 0) if (!clioPeers_.empty())
{ {
boost::asio::spawn(ioContext_.get(), [this, seq](boost::asio::yield_context yield) { boost::asio::spawn(ioContext_.get(), [this, seq](boost::asio::yield_context yield) {
for (auto const& peer : clioPeers_) for (auto const& peer : clioPeers_)
@@ -157,16 +161,15 @@ public:
}); });
return; return;
} }
else
{ loadCacheFromDb(seq);
loadCacheFromDb(seq);
}
// If loading synchronously, poll cache until full // If loading synchronously, poll cache until full
static constexpr size_t SLEEP_TIME_SECONDS = 10;
while (cacheLoadStyle_ == LoadStyle::SYNC && not cache_.get().isFull()) while (cacheLoadStyle_ == LoadStyle::SYNC && not cache_.get().isFull())
{ {
LOG(log_.debug()) << "Cache not full. Cache size = " << cache_.get().size() << ". Sleeping ..."; LOG(log_.debug()) << "Cache not full. Cache size = " << cache_.get().size() << ". Sleeping ...";
std::this_thread::sleep_for(std::chrono::seconds(10)); std::this_thread::sleep_for(std::chrono::seconds(SLEEP_TIME_SECONDS));
if (cache_.get().isFull()) if (cache_.get().isFull())
LOG(log_.info()) << "Cache is full. Cache size = " << cache_.get().size(); LOG(log_.info()) << "Cache is full. Cache size = " << cache_.get().size();
} }
@@ -188,13 +191,12 @@ private:
{ {
LOG(log_.info()) << "Loading cache from peer. ip = " << ip << " . port = " << port; LOG(log_.info()) << "Loading cache from peer. ip = " << ip << " . port = " << port;
namespace beast = boost::beast; // from <boost/beast.hpp> namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace websocket = beast::websocket; // from namespace websocket = beast::websocket; // from
namespace net = boost::asio; // from namespace net = boost::asio; // from
using tcp = boost::asio::ip::tcp; // from using tcp = boost::asio::ip::tcp; // from
try try
{ {
boost::beast::error_code ec; beast::error_code ec;
// These objects perform our I/O // These objects perform our I/O
tcp::resolver resolver{ioContext_.get()}; tcp::resolver resolver{ioContext_.get()};
@@ -221,13 +223,14 @@ private:
std::optional<boost::json::value> marker; std::optional<boost::json::value> marker;
LOG(log_.trace()) << "Sending request"; LOG(log_.trace()) << "Sending request";
static constexpr int LIMIT = 2048;
auto getRequest = [&](auto marker) { auto getRequest = [&](auto marker) {
boost::json::object request = { boost::json::object request = {
{"command", "ledger_data"}, {"command", "ledger_data"},
{"ledger_index", ledgerIndex}, {"ledger_index", ledgerIndex},
{"binary", true}, {"binary", true},
{"out_of_order", true}, {"out_of_order", true},
{"limit", 2048}}; {"limit", LIMIT}};
if (marker) if (marker)
request["marker"] = *marker; request["marker"] = *marker;
@@ -270,8 +273,9 @@ private:
auto const& err = response.at("error"); auto const& err = response.at("error");
if (err.is_string() && err.as_string() == "lgrNotFound") if (err.is_string() && err.as_string() == "lgrNotFound")
{ {
static constexpr size_t MAX_ATTEMPTS = 5;
++numAttempts; ++numAttempts;
if (numAttempts >= 5) if (numAttempts >= MAX_ATTEMPTS)
{ {
LOG(log_.error()) << " ledger not found at peer after 5 attempts. " LOG(log_.error()) << " ledger not found at peer after 5 attempts. "
"peer = " "peer = "
@@ -295,9 +299,13 @@ private:
return false; return false;
} }
if (response.contains("marker")) if (response.contains("marker"))
{
marker = response.at("marker"); marker = response.at("marker");
}
else else
{
marker = {}; marker = {};
}
auto const& state = response.at("state").as_array(); auto const& state = response.at("state").as_array();
@@ -356,16 +364,20 @@ private:
diff.erase(std::unique(diff.begin(), diff.end(), [](auto a, auto b) { return a.key == b.key; }), diff.end()); diff.erase(std::unique(diff.begin(), diff.end(), [](auto a, auto b) { return a.key == b.key; }), diff.end());
cursors.push_back({}); cursors.emplace_back();
for (auto const& obj : diff) for (auto const& obj : diff)
if (obj.blob.size()) {
cursors.push_back({obj.key}); if (!obj.blob.empty())
cursors.push_back({}); cursors.emplace_back(obj.key);
}
cursors.emplace_back();
std::stringstream cursorStr; std::stringstream cursorStr;
for (auto const& c : cursors) for (auto const& c : cursors)
{
if (c) if (c)
cursorStr << ripple::strHex(*c) << ", "; cursorStr << ripple::strHex(*c) << ", ";
}
LOG(log_.info()) << "Loading cache. num cursors = " << cursors.size() - 1; LOG(log_.info()) << "Loading cache. num cursors = " << cursors.size() - 1;
LOG(log_.trace()) << "cursors = " << cursorStr.str(); LOG(log_.trace()) << "cursors = " << cursorStr.str();

View File

@@ -28,6 +28,7 @@
#include <chrono> #include <chrono>
#include <mutex> #include <mutex>
#include <thread> #include <thread>
#include <utility>
namespace etl::detail { namespace etl::detail {
@@ -57,7 +58,7 @@ public:
std::optional<uint32_t> finishSequence, std::optional<uint32_t> finishSequence,
SystemState const& state) SystemState const& state)
: pipe_(std::ref(pipe)) : pipe_(std::ref(pipe))
, networkValidatedLedgers_{networkValidatedLedgers} , networkValidatedLedgers_{std::move(networkValidatedLedgers)}
, ledgerFetcher_{std::ref(ledgerFetcher)} , ledgerFetcher_{std::ref(ledgerFetcher)}
, startSequence_{startSequence} , startSequence_{startSequence}
, finishSequence_{finishSequence} , finishSequence_{finishSequence}

View File

@@ -37,14 +37,14 @@ ForwardCache::freshen()
{ {
boost::asio::spawn( boost::asio::spawn(
strand_, [this, numOutstanding, command = cacheEntry.first](boost::asio::yield_context yield) { strand_, [this, numOutstanding, command = cacheEntry.first](boost::asio::yield_context yield) {
boost::json::object request = {{"command", command}}; boost::json::object const request = {{"command", command}};
auto resp = source_.requestFromRippled(request, {}, yield); auto resp = source_.requestFromRippled(request, {}, yield);
if (!resp || resp->contains("error")) if (!resp || resp->contains("error"))
resp = {}; resp = {};
{ {
std::scoped_lock lk(mtx_); std::scoped_lock const lk(mtx_);
latestForwarded_[command] = resp; latestForwarded_[command] = resp;
} }
}); });
@@ -54,7 +54,7 @@ ForwardCache::freshen()
void void
ForwardCache::clear() ForwardCache::clear()
{ {
std::scoped_lock lk(mtx_); std::scoped_lock const lk(mtx_);
for (auto& cacheEntry : latestForwarded_) for (auto& cacheEntry : latestForwarded_)
latestForwarded_[cacheEntry.first] = {}; latestForwarded_[cacheEntry.first] = {};
} }
@@ -64,16 +64,20 @@ ForwardCache::get(boost::json::object const& request) const
{ {
std::optional<std::string> command = {}; std::optional<std::string> command = {};
if (request.contains("command") && !request.contains("method") && request.at("command").is_string()) if (request.contains("command") && !request.contains("method") && request.at("command").is_string())
{
command = request.at("command").as_string().c_str(); command = request.at("command").as_string().c_str();
}
else if (request.contains("method") && !request.contains("command") && request.at("method").is_string()) else if (request.contains("method") && !request.contains("command") && request.at("method").is_string())
{
command = request.at("method").as_string().c_str(); command = request.at("method").as_string().c_str();
}
if (!command) if (!command)
return {}; return {};
if (rpc::specifiesCurrentOrClosedLedger(request)) if (rpc::specifiesCurrentOrClosedLedger(request))
return {}; return {};
std::shared_lock lk(mtx_); std::shared_lock const lk(mtx_);
if (!latestForwarded_.contains(*command)) if (!latestForwarded_.contains(*command))
return {}; return {};

View File

@@ -31,7 +31,9 @@
#include <mutex> #include <mutex>
#include <unordered_map> #include <unordered_map>
namespace etl {
class Source; class Source;
} // namespace etl
namespace etl::detail { namespace etl::detail {
@@ -41,6 +43,7 @@ namespace etl::detail {
class ForwardCache class ForwardCache
{ {
using ResponseType = std::optional<boost::json::object>; using ResponseType = std::optional<boost::json::object>;
static constexpr std::uint32_t DEFAULT_DURATION = 10;
util::Logger log_{"ETL"}; util::Logger log_{"ETL"};
@@ -48,7 +51,7 @@ class ForwardCache
std::unordered_map<std::string, ResponseType> latestForwarded_; std::unordered_map<std::string, ResponseType> latestForwarded_;
boost::asio::strand<boost::asio::io_context::executor_type> strand_; boost::asio::strand<boost::asio::io_context::executor_type> strand_;
etl::Source const& source_; etl::Source const& source_;
std::uint32_t duration_ = 10; std::uint32_t duration_ = DEFAULT_DURATION;
void void
clear(); clear();
@@ -76,7 +79,7 @@ public:
freshen(); freshen();
std::optional<boost::json::object> std::optional<boost::json::object>
get(boost::json::object const& command) const; get(boost::json::object const& request) const;
}; };
} // namespace etl::detail } // namespace etl::detail

View File

@@ -27,6 +27,7 @@
#include <grpcpp/grpcpp.h> #include <grpcpp/grpcpp.h>
#include <optional> #include <optional>
#include <utility>
namespace etl::detail { namespace etl::detail {
@@ -50,7 +51,7 @@ public:
* @brief Create an instance of the fetcher * @brief Create an instance of the fetcher
*/ */
LedgerFetcher(std::shared_ptr<BackendInterface> backend, std::shared_ptr<LoadBalancerType> balancer) LedgerFetcher(std::shared_ptr<BackendInterface> backend, std::shared_ptr<LoadBalancerType> balancer)
: backend_(backend), loadBalancer_(balancer) : backend_(std::move(backend)), loadBalancer_(std::move(balancer))
{ {
} }

View File

@@ -30,6 +30,7 @@
#include <ripple/beast/core/CurrentThreadName.h> #include <ripple/beast/core/CurrentThreadName.h>
#include <memory> #include <memory>
#include <utility>
/** /**
* @brief Account transactions, NFT transactions and NFT data bundled togeher. * @brief Account transactions, NFT transactions and NFT data bundled togeher.
@@ -71,7 +72,10 @@ public:
std::shared_ptr<LoadBalancerType> balancer, std::shared_ptr<LoadBalancerType> balancer,
LedgerFetcherType& fetcher, LedgerFetcherType& fetcher,
SystemState const& state) SystemState const& state)
: backend_{backend}, loadBalancer_{balancer}, fetcher_{std::ref(fetcher)}, state_{std::cref(state)} : backend_{std::move(backend)}
, loadBalancer_{std::move(balancer)}
, fetcher_{std::ref(fetcher)}
, state_{std::cref(state)}
{ {
} }
@@ -96,7 +100,7 @@ public:
std::string* raw = txn.mutable_transaction_blob(); std::string* raw = txn.mutable_transaction_blob();
ripple::SerialIter it{raw->data(), raw->size()}; ripple::SerialIter it{raw->data(), raw->size()};
ripple::STTx sttx{it}; ripple::STTx const sttx{it};
LOG(log_.trace()) << "Inserting transaction = " << sttx.getTransactionID(); LOG(log_.trace()) << "Inserting transaction = " << sttx.getTransactionID();
@@ -108,7 +112,8 @@ public:
result.nfTokensData.push_back(*maybeNFT); result.nfTokensData.push_back(*maybeNFT);
result.accountTxData.emplace_back(txMeta, sttx.getTransactionID()); result.accountTxData.emplace_back(txMeta, sttx.getTransactionID());
std::string keyStr{reinterpret_cast<const char*>(sttx.getTransactionID().data()), 32}; static constexpr std::size_t KEY_SIZE = 32;
std::string keyStr{reinterpret_cast<const char*>(sttx.getTransactionID().data()), KEY_SIZE};
backend_->writeTransaction( backend_->writeTransaction(
std::move(keyStr), std::move(keyStr),
ledger.seq, ledger.seq,
@@ -225,8 +230,9 @@ public:
++numWrites; ++numWrites;
} }
prev = std::move(cur->key); prev = cur->key;
if (numWrites % 100000 == 0 && numWrites != 0) static constexpr std::size_t LOG_INTERVAL = 100000;
if (numWrites % LOG_INTERVAL == 0 && numWrites != 0)
LOG(log_.info()) << "Wrote " << numWrites << " book successors"; LOG(log_.info()) << "Wrote " << numWrites << " book successors";
} }

View File

@@ -21,6 +21,7 @@
#include <data/BackendInterface.h> #include <data/BackendInterface.h>
#include <etl/SystemState.h> #include <etl/SystemState.h>
#include <feed/SubscriptionManager.h>
#include <util/LedgerUtils.h> #include <util/LedgerUtils.h>
#include <util/Profiler.h> #include <util/Profiler.h>
#include <util/log/Logger.h> #include <util/log/Logger.h>
@@ -28,6 +29,7 @@
#include <ripple/protocol/LedgerHeader.h> #include <ripple/protocol/LedgerHeader.h>
#include <chrono> #include <chrono>
#include <utility>
namespace etl::detail { namespace etl::detail {
@@ -72,8 +74,8 @@ public:
std::shared_ptr<feed::SubscriptionManager> subscriptions, std::shared_ptr<feed::SubscriptionManager> subscriptions,
SystemState const& state) SystemState const& state)
: publishStrand_{boost::asio::make_strand(ioc)} : publishStrand_{boost::asio::make_strand(ioc)}
, backend_{backend} , backend_{std::move(backend)}
, subscriptions_{subscriptions} , subscriptions_{std::move(subscriptions)}
, state_{std::cref(state)} , state_{std::cref(state)}
{ {
} }
@@ -111,16 +113,14 @@ public:
++numAttempts; ++numAttempts;
continue; continue;
} }
else
{
auto lgr = data::synchronousAndRetryOnTimeout(
[&](auto yield) { return backend_->fetchLedgerBySequence(ledgerSequence, yield); });
assert(lgr); auto lgr = data::synchronousAndRetryOnTimeout(
publish(*lgr); [&](auto yield) { return backend_->fetchLedgerBySequence(ledgerSequence, yield); });
return true; assert(lgr);
} publish(*lgr);
return true;
} }
return false; return false;
} }
@@ -142,7 +142,7 @@ public:
{ {
LOG(log_.info()) << "Updating cache"; LOG(log_.info()) << "Updating cache";
std::vector<data::LedgerObject> diff = data::synchronousAndRetryOnTimeout( std::vector<data::LedgerObject> const diff = data::synchronousAndRetryOnTimeout(
[&](auto yield) { return backend_->fetchLedgerDiff(lgrInfo.seq, yield); }); [&](auto yield) { return backend_->fetchLedgerDiff(lgrInfo.seq, yield); });
backend_->cache().update(diff, lgrInfo.seq); // todo: inject cache to update, don't use backend cache backend_->cache().update(diff, lgrInfo.seq); // todo: inject cache to update, don't use backend cache
@@ -154,19 +154,20 @@ public:
// if the ledger closed over 10 minutes ago, assume we are still catching up and don't publish // if the ledger closed over 10 minutes ago, assume we are still catching up and don't publish
// TODO: this probably should be a strategy // TODO: this probably should be a strategy
if (age < 600) static constexpr std::uint32_t MAX_LEDGER_AGE_SECONDS = 600;
if (age < MAX_LEDGER_AGE_SECONDS)
{ {
std::optional<ripple::Fees> fees = data::synchronousAndRetryOnTimeout( std::optional<ripple::Fees> fees = data::synchronousAndRetryOnTimeout(
[&](auto yield) { return backend_->fetchFees(lgrInfo.seq, yield); }); [&](auto yield) { return backend_->fetchFees(lgrInfo.seq, yield); });
std::vector<data::TransactionAndMetadata> transactions = data::synchronousAndRetryOnTimeout( std::vector<data::TransactionAndMetadata> const transactions = data::synchronousAndRetryOnTimeout(
[&](auto yield) { return backend_->fetchAllTransactionsInLedger(lgrInfo.seq, yield); }); [&](auto yield) { return backend_->fetchAllTransactionsInLedger(lgrInfo.seq, yield); });
auto ledgerRange = backend_->fetchLedgerRange(); auto ledgerRange = backend_->fetchLedgerRange();
assert(ledgerRange); assert(ledgerRange);
assert(fees); assert(fees);
std::string range = std::string const range =
std::to_string(ledgerRange->minSequence) + "-" + std::to_string(ledgerRange->maxSequence); std::to_string(ledgerRange->minSequence) + "-" + std::to_string(ledgerRange->maxSequence);
subscriptions_->pubLedger(lgrInfo, *fees, range, transactions.size()); subscriptions_->pubLedger(lgrInfo, *fees, range, transactions.size());
@@ -203,7 +204,7 @@ public:
std::chrono::time_point<std::chrono::system_clock> std::chrono::time_point<std::chrono::system_clock>
getLastPublish() const getLastPublish() const
{ {
std::shared_lock lck(publishTimeMtx_); std::shared_lock const lck(publishTimeMtx_);
return lastPublish_; return lastPublish_;
} }
@@ -213,7 +214,7 @@ public:
std::uint32_t std::uint32_t
lastCloseAgeSeconds() const lastCloseAgeSeconds() const
{ {
std::shared_lock lck(closeTimeMtx_); std::shared_lock const lck(closeTimeMtx_);
auto now = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()) auto now = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch())
.count(); .count();
auto closeTime = lastCloseTime_.time_since_epoch().count(); auto closeTime = lastCloseTime_.time_since_epoch().count();
@@ -225,7 +226,7 @@ public:
std::optional<uint32_t> std::optional<uint32_t>
getLastPublishedSequence() const getLastPublishedSequence() const
{ {
std::scoped_lock lck(lastPublishedSeqMtx_); std::scoped_lock const lck(lastPublishedSeqMtx_);
return lastPublishedSequence_; return lastPublishedSequence_;
} }
@@ -233,21 +234,21 @@ private:
void void
setLastClose(std::chrono::time_point<ripple::NetClock> lastCloseTime) setLastClose(std::chrono::time_point<ripple::NetClock> lastCloseTime)
{ {
std::scoped_lock lck(closeTimeMtx_); std::scoped_lock const lck(closeTimeMtx_);
lastCloseTime_ = lastCloseTime; lastCloseTime_ = lastCloseTime;
} }
void void
setLastPublishTime() setLastPublishTime()
{ {
std::scoped_lock lck(publishTimeMtx_); std::scoped_lock const lck(publishTimeMtx_);
lastPublish_ = std::chrono::system_clock::now(); lastPublish_ = std::chrono::system_clock::now();
} }
void void
setLastPublishedSequence(std::optional<uint32_t> lastPublishedSequence) setLastPublishedSequence(std::optional<uint32_t> lastPublishedSequence)
{ {
std::scoped_lock lck(lastPublishedSeqMtx_); std::scoped_lock const lck(lastPublishedSeqMtx_);
lastPublishedSequence_ = lastPublishedSequence; lastPublishedSequence_ = lastPublishedSequence;
} }
}; };

View File

@@ -34,6 +34,7 @@
#include <chrono> #include <chrono>
#include <memory> #include <memory>
#include <thread> #include <thread>
#include <utility>
namespace etl::detail { namespace etl::detail {
@@ -87,7 +88,7 @@ public:
uint32_t startSequence, uint32_t startSequence,
SystemState& state) SystemState& state)
: pipe_{std::ref(pipe)} : pipe_{std::ref(pipe)}
, backend_{backend} , backend_{std::move(backend)}
, loader_{std::ref(loader)} , loader_{std::ref(loader)}
, publisher_{std::ref(publisher)} , publisher_{std::ref(publisher)}
, amendmentBlockHandler_{std::ref(amendmentBlockHandler)} , amendmentBlockHandler_{std::ref(amendmentBlockHandler)}
@@ -299,7 +300,7 @@ private:
for (auto const& obj : cacheUpdates) for (auto const& obj : cacheUpdates)
{ {
if (modified.count(obj.key)) if (modified.contains(obj.key))
continue; continue;
auto lb = backend_->cache().getPredecessor(obj.key, lgrInfo.seq); auto lb = backend_->cache().getPredecessor(obj.key, lgrInfo.seq);
@@ -310,7 +311,7 @@ private:
if (!ub) if (!ub)
ub = {data::lastKey, {}}; ub = {data::lastKey, {}};
if (obj.blob.size() == 0) if (obj.blob.empty())
{ {
LOG(log_.debug()) << "writing successor for deleted object " << ripple::strHex(obj.key) << " - " LOG(log_.debug()) << "writing successor for deleted object " << ripple::strHex(obj.key) << " - "
<< ripple::strHex(lb->key) << " - " << ripple::strHex(ub->key); << ripple::strHex(lb->key) << " - " << ripple::strHex(ub->key);
@@ -378,10 +379,10 @@ private:
if (obj.mod_type() != RawLedgerObjectType::MODIFIED) if (obj.mod_type() != RawLedgerObjectType::MODIFIED)
{ {
std::string* predPtr = obj.mutable_predecessor(); std::string* predPtr = obj.mutable_predecessor();
if (!predPtr->size()) if (predPtr->empty())
*predPtr = uint256ToString(data::firstKey); *predPtr = uint256ToString(data::firstKey);
std::string* succPtr = obj.mutable_successor(); std::string* succPtr = obj.mutable_successor();
if (!succPtr->size()) if (succPtr->empty())
*succPtr = uint256ToString(data::lastKey); *succPtr = uint256ToString(data::lastKey);
if (obj.mod_type() == RawLedgerObjectType::DELETED) if (obj.mod_type() == RawLedgerObjectType::DELETED)

View File

@@ -78,7 +78,7 @@ SubscriptionManager::subLedger(boost::asio::yield_context yield, SessionPtrType
fees = backend_->fetchFees(lgrInfo->seq, yield); fees = backend_->fetchFees(lgrInfo->seq, yield);
assert(fees); assert(fees);
std::string range = std::to_string(ledgerRange->minSequence) + "-" + std::to_string(ledgerRange->maxSequence); std::string const range = std::to_string(ledgerRange->minSequence) + "-" + std::to_string(ledgerRange->maxSequence);
auto pubMsg = getLedgerPubMessage(*lgrInfo, *fees, range, 0); auto pubMsg = getLedgerPubMessage(*lgrInfo, *fees, range, 0);
pubMsg.erase("txn_count"); pubMsg.erase("txn_count");
@@ -216,20 +216,27 @@ SubscriptionManager::pubTransaction(data::TransactionAndMetadata const& blobs, r
// We need a field that contains the TakerGets and TakerPays // We need a field that contains the TakerGets and TakerPays
// parameters. // parameters.
if (node.getFName() == ripple::sfModifiedNode) if (node.getFName() == ripple::sfModifiedNode)
{
field = &ripple::sfPreviousFields; field = &ripple::sfPreviousFields;
}
else if (node.getFName() == ripple::sfCreatedNode) else if (node.getFName() == ripple::sfCreatedNode)
{
field = &ripple::sfNewFields; field = &ripple::sfNewFields;
}
else if (node.getFName() == ripple::sfDeletedNode) else if (node.getFName() == ripple::sfDeletedNode)
{
field = &ripple::sfFinalFields; field = &ripple::sfFinalFields;
}
if (field) if (field != nullptr)
{ {
auto data = dynamic_cast<const ripple::STObject*>(node.peekAtPField(*field)); auto data = dynamic_cast<const ripple::STObject*>(node.peekAtPField(*field));
if (data && data->isFieldPresent(ripple::sfTakerPays) && data->isFieldPresent(ripple::sfTakerGets)) if ((data != nullptr) && data->isFieldPresent(ripple::sfTakerPays) &&
data->isFieldPresent(ripple::sfTakerGets))
{ {
// determine the OrderBook // determine the OrderBook
ripple::Book book{ ripple::Book const book{
data->getFieldAmount(ripple::sfTakerGets).issue(), data->getFieldAmount(ripple::sfTakerGets).issue(),
data->getFieldAmount(ripple::sfTakerPays).issue()}; data->getFieldAmount(ripple::sfTakerPays).issue()};
if (alreadySent.find(book) == alreadySent.end()) if (alreadySent.find(book) == alreadySent.end())
@@ -335,7 +342,7 @@ void
SubscriptionManager::subscribeHelper(SessionPtrType const& session, Subscription& subs, CleanupFunction&& func) SubscriptionManager::subscribeHelper(SessionPtrType const& session, Subscription& subs, CleanupFunction&& func)
{ {
subs.subscribe(session); subs.subscribe(session);
std::scoped_lock lk(cleanupMtx_); std::scoped_lock const lk(cleanupMtx_);
cleanupFuncs_[session].push_back(std::move(func)); cleanupFuncs_[session].push_back(std::move(func));
} }
@@ -348,18 +355,18 @@ SubscriptionManager::subscribeHelper(
CleanupFunction&& func) CleanupFunction&& func)
{ {
subs.subscribe(session, k); subs.subscribe(session, k);
std::scoped_lock lk(cleanupMtx_); std::scoped_lock const lk(cleanupMtx_);
cleanupFuncs_[session].push_back(std::move(func)); cleanupFuncs_[session].push_back(std::move(func));
} }
void void
SubscriptionManager::cleanup(SessionPtrType session) SubscriptionManager::cleanup(SessionPtrType session)
{ {
std::scoped_lock lk(cleanupMtx_); std::scoped_lock const lk(cleanupMtx_);
if (!cleanupFuncs_.contains(session)) if (!cleanupFuncs_.contains(session))
return; return;
for (auto f : cleanupFuncs_[session]) for (auto const& f : cleanupFuncs_[session])
{ {
f(session); f(session);
} }

View File

@@ -79,13 +79,13 @@ parseCli(int argc, char* argv[])
po::store(po::command_line_parser(argc, argv).options(description).positional(positional).run(), parsed); po::store(po::command_line_parser(argc, argv).options(description).positional(positional).run(), parsed);
po::notify(parsed); po::notify(parsed);
if (parsed.count("version")) if (parsed.count("version") != 0u)
{ {
std::cout << Build::getClioFullVersionString() << '\n'; std::cout << Build::getClioFullVersionString() << '\n';
std::exit(EXIT_SUCCESS); std::exit(EXIT_SUCCESS);
} }
if (parsed.count("help")) if (parsed.count("help") != 0u)
{ {
std::cout << "Clio server " << Build::getClioFullVersionString() << "\n\n" << description; std::cout << "Clio server " << Build::getClioFullVersionString() << "\n\n" << description;
std::exit(EXIT_SUCCESS); std::exit(EXIT_SUCCESS);
@@ -109,7 +109,7 @@ parseCerts(Config const& config)
auto certFilename = config.value<std::string>("ssl_cert_file"); auto certFilename = config.value<std::string>("ssl_cert_file");
auto keyFilename = config.value<std::string>("ssl_key_file"); auto keyFilename = config.value<std::string>("ssl_key_file");
std::ifstream readCert(certFilename, std::ios::in | std::ios::binary); std::ifstream const readCert(certFilename, std::ios::in | std::ios::binary);
if (!readCert) if (!readCert)
return {}; return {};

View File

@@ -36,7 +36,7 @@ struct Amendments
* @param name The name of the amendment * @param name The name of the amendment
* @return The corresponding amendment Id * @return The corresponding amendment Id
*/ */
static ripple::uint256 const static ripple::uint256
GetAmendmentId(std::string_view const name) GetAmendmentId(std::string_view const name)
{ {
return ripple::sha512Half(ripple::Slice(name.data(), name.size())); return ripple::sha512Half(ripple::Slice(name.data(), name.size()));

View File

@@ -188,8 +188,8 @@ private:
handleAffectedNode(node); handleAffectedNode(node);
} }
std::optional<uint32_t> static std::optional<uint32_t>
shouldCancelOffer(std::shared_ptr<ripple::STTx const> const& tx) const shouldCancelOffer(std::shared_ptr<ripple::STTx const> const& tx)
{ {
switch (tx->getFieldU16(ripple::sfTransactionType)) switch (tx->getFieldU16(ripple::sfTransactionType))
{ {
@@ -242,7 +242,7 @@ tag_invoke(boost::json::value_from_tag, boost::json::value& jv, BookChange const
* @param lgrInfo The ledger header * @param lgrInfo The ledger header
* @param transactions The vector of transactions with heir metadata * @param transactions The vector of transactions with heir metadata
*/ */
[[nodiscard]] boost::json::object const [[nodiscard]] boost::json::object
computeBookChanges(ripple::LedgerHeader const& lgrInfo, std::vector<data::TransactionAndMetadata> const& transactions); computeBookChanges(ripple::LedgerHeader const& lgrInfo, std::vector<data::TransactionAndMetadata> const& transactions);
} // namespace rpc } // namespace rpc

View File

@@ -26,7 +26,7 @@ namespace rpc {
void void
Counters::rpcFailed(std::string const& method) Counters::rpcFailed(std::string const& method)
{ {
std::scoped_lock lk(mutex_); std::scoped_lock const lk(mutex_);
MethodInfo& counters = methodInfo_[method]; MethodInfo& counters = methodInfo_[method];
++counters.started; ++counters.started;
++counters.failed; ++counters.failed;
@@ -35,7 +35,7 @@ Counters::rpcFailed(std::string const& method)
void void
Counters::rpcErrored(std::string const& method) Counters::rpcErrored(std::string const& method)
{ {
std::scoped_lock lk(mutex_); std::scoped_lock const lk(mutex_);
MethodInfo& counters = methodInfo_[method]; MethodInfo& counters = methodInfo_[method];
++counters.started; ++counters.started;
++counters.errored; ++counters.errored;
@@ -44,7 +44,7 @@ Counters::rpcErrored(std::string const& method)
void void
Counters::rpcComplete(std::string const& method, std::chrono::microseconds const& rpcDuration) Counters::rpcComplete(std::string const& method, std::chrono::microseconds const& rpcDuration)
{ {
std::scoped_lock lk(mutex_); std::scoped_lock const lk(mutex_);
MethodInfo& counters = methodInfo_[method]; MethodInfo& counters = methodInfo_[method];
++counters.started; ++counters.started;
++counters.finished; ++counters.finished;
@@ -54,7 +54,7 @@ Counters::rpcComplete(std::string const& method, std::chrono::microseconds const
void void
Counters::rpcForwarded(std::string const& method) Counters::rpcForwarded(std::string const& method)
{ {
std::scoped_lock lk(mutex_); std::scoped_lock const lk(mutex_);
MethodInfo& counters = methodInfo_[method]; MethodInfo& counters = methodInfo_[method];
++counters.forwarded; ++counters.forwarded;
} }
@@ -62,7 +62,7 @@ Counters::rpcForwarded(std::string const& method)
void void
Counters::rpcFailedToForward(std::string const& method) Counters::rpcFailedToForward(std::string const& method)
{ {
std::scoped_lock lk(mutex_); std::scoped_lock const lk(mutex_);
MethodInfo& counters = methodInfo_[method]; MethodInfo& counters = methodInfo_[method];
++counters.failedForward; ++counters.failedForward;
} }
@@ -106,7 +106,7 @@ Counters::uptime() const
boost::json::object boost::json::object
Counters::report() const Counters::report() const
{ {
std::scoped_lock lk(mutex_); std::scoped_lock const lk(mutex_);
auto obj = boost::json::object{}; auto obj = boost::json::object{};
obj[JS(rpc)] = boost::json::object{}; obj[JS(rpc)] = boost::json::object{};

View File

@@ -146,8 +146,10 @@ makeError(Status const& status)
status.code); status.code);
if (status.extraInfo) if (status.extraInfo)
{
for (auto& [key, value] : status.extraInfo.value()) for (auto& [key, value] : status.extraInfo.value())
res[key] = value; res[key] = value;
}
return res; return res;
} }

View File

@@ -27,6 +27,7 @@
#include <optional> #include <optional>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <utility>
#include <variant> #include <variant>
namespace rpc { namespace rpc {
@@ -73,8 +74,8 @@ using CombinedError = std::variant<RippledError, ClioError>;
struct Status struct Status
{ {
CombinedError code = RippledError::rpcSUCCESS; CombinedError code = RippledError::rpcSUCCESS;
std::string error = ""; std::string error;
std::string message = ""; std::string message;
std::optional<boost::json::object> extraInfo; std::optional<boost::json::object> extraInfo;
Status() = default; Status() = default;
@@ -83,15 +84,16 @@ struct Status
// HACK. Some rippled handlers explicitly specify errors. // HACK. Some rippled handlers explicitly specify errors.
// This means that we have to be able to duplicate this functionality. // This means that we have to be able to duplicate this functionality.
explicit Status(std::string const& message) : code(ripple::rpcUNKNOWN), message(message) explicit Status(std::string message) : code(ripple::rpcUNKNOWN), message(std::move(message))
{ {
} }
Status(CombinedError code, std::string message) : code(code), message(message) Status(CombinedError code, std::string message) : code(code), message(std::move(message))
{ {
} }
Status(CombinedError code, std::string error, std::string message) : code(code), error(error), message(message) Status(CombinedError code, std::string error, std::string message)
: code(code), error(std::move(error)), message(std::move(message))
{ {
} }
@@ -156,7 +158,7 @@ class InvalidParamsError : public std::exception
std::string msg; std::string msg;
public: public:
explicit InvalidParamsError(std::string const& msg) : msg(msg) explicit InvalidParamsError(std::string msg) : msg(std::move(msg))
{ {
} }
@@ -173,7 +175,7 @@ class AccountNotFoundError : public std::exception
std::string account; std::string account;
public: public:
explicit AccountNotFoundError(std::string const& acct) : account(acct) explicit AccountNotFoundError(std::string acct) : account(std::move(acct))
{ {
} }

View File

@@ -37,9 +37,13 @@ make_WsContext(
{ {
boost::json::value commandValue = nullptr; boost::json::value commandValue = nullptr;
if (!request.contains("command") && request.contains("method")) if (!request.contains("command") && request.contains("method"))
{
commandValue = request.at("method"); commandValue = request.at("method");
}
else if (request.contains("command") && !request.contains("method")) else if (request.contains("command") && !request.contains("method"))
{
commandValue = request.at("command"); commandValue = request.at("command");
}
if (!commandValue.is_string()) if (!commandValue.is_string())
return Error{{ClioError::rpcCOMMAND_IS_MISSING, "Method/Command is not specified or is not a string."}}; return Error{{ClioError::rpcCOMMAND_IS_MISSING, "Method/Command is not specified or is not a string."}};
@@ -48,7 +52,7 @@ make_WsContext(
if (!apiVersion) if (!apiVersion)
return Error{{ClioError::rpcINVALID_API_VERSION, apiVersion.error()}}; return Error{{ClioError::rpcINVALID_API_VERSION, apiVersion.error()}};
string command = commandValue.as_string().c_str(); string const command = commandValue.as_string().c_str();
return web::Context(yc, command, *apiVersion, request, session, tagFactory, range, clientIp); return web::Context(yc, command, *apiVersion, request, session, tagFactory, range, clientIp);
} }
@@ -70,7 +74,7 @@ make_HttpContext(
if (request.at("method").as_string().empty()) if (request.at("method").as_string().empty())
return Error{{ClioError::rpcCOMMAND_IS_EMPTY}}; return Error{{ClioError::rpcCOMMAND_IS_EMPTY}};
string command = request.at("method").as_string().c_str(); string const command = request.at("method").as_string().c_str();
if (command == "subscribe" || command == "unsubscribe") if (command == "subscribe" || command == "unsubscribe")
return Error{{RippledError::rpcBAD_SYNTAX, "Subscribe and unsubscribe are only allowed or websocket."}}; return Error{{RippledError::rpcBAD_SYNTAX, "Subscribe and unsubscribe are only allowed or websocket."}};

View File

@@ -46,7 +46,7 @@
// forward declarations // forward declarations
namespace feed { namespace feed {
class SubscriptionManager; class SubscriptionManager;
} } // namespace feed
namespace etl { namespace etl {
class LoadBalancer; class LoadBalancer;
class ETLService; class ETLService;
@@ -149,12 +149,11 @@ public:
LOG(perfLog_.debug()) << ctx.tag() << " finish executing rpc `" << ctx.method << '`'; LOG(perfLog_.debug()) << ctx.tag() << " finish executing rpc `" << ctx.method << '`';
if (v) if (v)
return v->as_object();
else
{ {
notifyErrored(ctx.method); return v->as_object();
return Status{v.error()};
} }
notifyErrored(ctx.method);
return Status{v.error()};
} }
catch (data::DatabaseTimeout const& t) catch (data::DatabaseTimeout const& t)
{ {

View File

@@ -93,7 +93,9 @@ getDeliveredAmount(
// then its absence indicates that the amount delivered is listed in the // then its absence indicates that the amount delivered is listed in the
// Amount field. DeliveredAmount went live January 24, 2014. // Amount field. DeliveredAmount went live January 24, 2014.
// 446000000 is in Feb 2014, well after DeliveredAmount went live // 446000000 is in Feb 2014, well after DeliveredAmount went live
if (ledgerSequence >= 4594095 || date > 446000000) static constexpr std::uint32_t FIRST_LEDGER_WITH_DELIVERED_AMOUNT = 4594095;
static constexpr std::uint32_t DELIVERED_AMOUNT_LIVE_DATE = 446000000;
if (ledgerSequence >= FIRST_LEDGER_WITH_DELIVERED_AMOUNT || date > DELIVERED_AMOUNT_LIVE_DATE)
{ {
return txn->getFieldAmount(ripple::sfAmount); return txn->getFieldAmount(ripple::sfAmount);
} }
@@ -133,14 +135,19 @@ accountFromStringStrict(std::string const& account)
std::optional<ripple::AccountID> result; std::optional<ripple::AccountID> result;
if (publicKey) if (publicKey)
{
result = ripple::calcAccountID(*publicKey); result = ripple::calcAccountID(*publicKey);
}
else else
{
result = ripple::parseBase58<ripple::AccountID>(account); result = ripple::parseBase58<ripple::AccountID>(account);
}
if (result) if (result)
{
return result.value(); return result.value();
else }
return {}; return {};
} }
std::pair<std::shared_ptr<ripple::STTx const>, std::shared_ptr<ripple::STObject const>> std::pair<std::shared_ptr<ripple::STTx const>, std::shared_ptr<ripple::STObject const>>
@@ -177,7 +184,7 @@ deserializeTxPlusMeta(data::TransactionAndMetadata const& blobs, std::uint32_t s
{ {
auto [tx, meta] = deserializeTxPlusMeta(blobs); auto [tx, meta] = deserializeTxPlusMeta(blobs);
std::shared_ptr<ripple::TxMeta> m = std::make_shared<ripple::TxMeta>(tx->getTransactionID(), seq, *meta); std::shared_ptr<ripple::TxMeta> const m = std::make_shared<ripple::TxMeta>(tx->getTransactionID(), seq, *meta);
return {tx, m}; return {tx, m};
} }
@@ -224,9 +231,13 @@ insertDeliveredAmount(
if (canHaveDeliveredAmount(txn, meta)) if (canHaveDeliveredAmount(txn, meta))
{ {
if (auto amt = getDeliveredAmount(txn, meta, meta->getLgrSeq(), date)) if (auto amt = getDeliveredAmount(txn, meta, meta->getLgrSeq(), date))
{
metaJson["delivered_amount"] = toBoostJson(amt->getJson(ripple::JsonOptions::include_date)); metaJson["delivered_amount"] = toBoostJson(amt->getJson(ripple::JsonOptions::include_date));
}
else else
{
metaJson["delivered_amount"] = "unavailable"; metaJson["delivered_amount"] = "unavailable";
}
return true; return true;
} }
return false; return false;
@@ -330,9 +341,13 @@ ledgerInfoFromRequest(std::shared_ptr<data::BackendInterface const> const& backe
{ {
boost::json::string const& stringIndex = indexValue.as_string(); boost::json::string const& stringIndex = indexValue.as_string();
if (stringIndex == "validated") if (stringIndex == "validated")
{
ledgerSequence = ctx.range.maxSequence; ledgerSequence = ctx.range.maxSequence;
}
else else
{
ledgerSequence = parseStringAsUInt(stringIndex.c_str()); ledgerSequence = parseStringAsUInt(stringIndex.c_str());
}
} }
else if (indexValue.is_int64()) else if (indexValue.is_int64())
ledgerSequence = indexValue.as_int64(); ledgerSequence = indexValue.as_int64();
@@ -368,7 +383,7 @@ getLedgerInfoFromHashOrSeq(
{ {
// invoke uint256's constructor to parse the hex string , instead of // invoke uint256's constructor to parse the hex string , instead of
// copying buffer // copying buffer
ripple::uint256 ledgerHash256{std::string_view(*ledgerHash)}; ripple::uint256 const ledgerHash256{std::string_view(*ledgerHash)};
lgrInfo = backend.fetchLedgerByHash(ledgerHash256, yield); lgrInfo = backend.fetchLedgerByHash(ledgerHash256, yield);
if (!lgrInfo || lgrInfo->seq > maxSeq) if (!lgrInfo || lgrInfo->seq > maxSeq)
return err; return err;
@@ -411,8 +426,10 @@ getStartHint(ripple::SLE const& sle, ripple::AccountID const& accountID)
if (sle.getType() == ripple::ltRIPPLE_STATE) if (sle.getType() == ripple::ltRIPPLE_STATE)
{ {
if (sle.getFieldAmount(ripple::sfLowLimit).getIssuer() == accountID) if (sle.getFieldAmount(ripple::sfLowLimit).getIssuer() == accountID)
{
return sle.getFieldU64(ripple::sfLowNode); return sle.getFieldU64(ripple::sfLowNode);
else if (sle.getFieldAmount(ripple::sfHighLimit).getIssuer() == accountID) }
if (sle.getFieldAmount(ripple::sfHighLimit).getIssuer() == accountID)
return sle.getFieldU64(ripple::sfHighNode); return sle.getFieldU64(ripple::sfHighNode);
} }
@@ -443,17 +460,18 @@ traverseNFTObjects(
return Status{RippledError::rpcINVALID_PARAMS, "Invalid marker."}; return Status{RippledError::rpcINVALID_PARAMS, "Invalid marker."};
// no marker, start from the last page // no marker, start from the last page
ripple::uint256 currentPage = nextPage == beast::zero ? lastNFTPage.key : nextPage; ripple::uint256 const currentPage = nextPage == beast::zero ? lastNFTPage.key : nextPage;
// read the current page // read the current page
auto page = backend.fetchLedgerObject(currentPage, sequence, yield); auto page = backend.fetchLedgerObject(currentPage, sequence, yield);
if (!page) if (!page)
{ {
if (nextPage == beast::zero) // no nft objects in lastNFTPage if (nextPage == beast::zero)
{ // no nft objects in lastNFTPage
return AccountCursor{beast::zero, 0}; return AccountCursor{beast::zero, 0};
else // marker is in the right range, but still invalid } // marker is in the right range, but still invalid
return Status{RippledError::rpcINVALID_PARAMS, "Invalid marker."}; return Status{RippledError::rpcINVALID_PARAMS, "Invalid marker."};
} }
// the object exists and the key is in right range, must be nft page // the object exists and the key is in right range, must be nft page
@@ -553,7 +571,8 @@ traverseOwnedNodes(
// Only reserve 2048 nodes when fetching all owned ledger objects. If there // Only reserve 2048 nodes when fetching all owned ledger objects. If there
// are more, then keys will allocate more memory, which is suboptimal, but // are more, then keys will allocate more memory, which is suboptimal, but
// should only occur occasionally. // should only occur occasionally.
keys.reserve(std::min(std::uint32_t{2048}, limit)); static constexpr std::uint32_t MIN_NODES = 2048;
keys.reserve(std::min(MIN_NODES, limit));
auto start = std::chrono::system_clock::now(); auto start = std::chrono::system_clock::now();
@@ -567,7 +586,7 @@ traverseOwnedNodes(
return Status(ripple::rpcINVALID_PARAMS, "Invalid marker."); return Status(ripple::rpcINVALID_PARAMS, "Invalid marker.");
ripple::SerialIter hintDirIt{hintDir->data(), hintDir->size()}; ripple::SerialIter hintDirIt{hintDir->data(), hintDir->size()};
ripple::SLE hintDirSle{hintDirIt, hintIndex.key}; ripple::SLE const hintDirSle{hintDirIt, hintIndex.key};
if (auto const& indexes = hintDirSle.getFieldV256(ripple::sfIndexes); if (auto const& indexes = hintDirSle.getFieldV256(ripple::sfIndexes);
std::find(std::begin(indexes), std::end(indexes), hexMarker) == std::end(indexes)) std::find(std::begin(indexes), std::end(indexes), hexMarker) == std::end(indexes))
@@ -586,7 +605,7 @@ traverseOwnedNodes(
return Status(ripple::rpcINVALID_PARAMS, "Owner directory not found."); return Status(ripple::rpcINVALID_PARAMS, "Owner directory not found.");
ripple::SerialIter ownedDirIt{ownerDir->data(), ownerDir->size()}; ripple::SerialIter ownedDirIt{ownerDir->data(), ownerDir->size()};
ripple::SLE ownedDirSle{ownedDirIt, currentIndex.key}; ripple::SLE const ownedDirSle{ownedDirIt, currentIndex.key};
for (auto const& key : ownedDirSle.getFieldV256(ripple::sfIndexes)) for (auto const& key : ownedDirSle.getFieldV256(ripple::sfIndexes))
{ {
@@ -630,7 +649,7 @@ traverseOwnedNodes(
break; break;
ripple::SerialIter ownedDirIt{ownerDir->data(), ownerDir->size()}; ripple::SerialIter ownedDirIt{ownerDir->data(), ownerDir->size()};
ripple::SLE ownedDirSle{ownedDirIt, currentIndex.key}; ripple::SLE const ownedDirSle{ownedDirIt, currentIndex.key};
for (auto const& key : ownedDirSle.getFieldV256(ripple::sfIndexes)) for (auto const& key : ownedDirSle.getFieldV256(ripple::sfIndexes))
{ {
@@ -703,8 +722,10 @@ parseRippleLibSeed(boost::json::value const& value)
auto const result = ripple::decodeBase58Token(value.as_string().c_str(), ripple::TokenType::None); auto const result = ripple::decodeBase58Token(value.as_string().c_str(), ripple::TokenType::None);
if (result.size() == 18 && static_cast<std::uint8_t>(result[0]) == std::uint8_t(0xE1) && static constexpr std::size_t SEED_SIZE = 18;
static_cast<std::uint8_t>(result[1]) == std::uint8_t(0x4B)) static constexpr std::array<std::uint8_t, 2> SEED_PREFIX = {0xE1, 0x4B};
if (result.size() == SEED_SIZE && static_cast<std::uint8_t>(result[0]) == SEED_PREFIX[0] &&
static_cast<std::uint8_t>(result[1]) == SEED_PREFIX[1])
return ripple::Seed(ripple::makeSlice(result.substr(2))); return ripple::Seed(ripple::makeSlice(result.substr(2)));
return {}; return {};
@@ -720,9 +741,9 @@ keypairFromRequst(boost::json::object const& request)
static std::string const secretTypes[]{"passphrase", "secret", "seed", "seed_hex"}; static std::string const secretTypes[]{"passphrase", "secret", "seed", "seed_hex"};
// Identify which secret type is in use. // Identify which secret type is in use.
std::string secretType = ""; std::string secretType;
int count = 0; int count = 0;
for (auto t : secretTypes) for (const auto& t : secretTypes)
{ {
if (request.contains(t)) if (request.contains(t))
{ {
@@ -750,7 +771,7 @@ keypairFromRequst(boost::json::object const& request)
if (!request.at("key_type").is_string()) if (!request.at("key_type").is_string())
return Status{RippledError::rpcINVALID_PARAMS, "keyTypeNotString"}; return Status{RippledError::rpcINVALID_PARAMS, "keyTypeNotString"};
std::string key_type = request.at("key_type").as_string().c_str(); std::string const key_type = request.at("key_type").as_string().c_str();
keyType = ripple::keyTypeFromString(key_type); keyType = ripple::keyTypeFromString(key_type);
if (!keyType) if (!keyType)
@@ -788,17 +809,21 @@ keypairFromRequst(boost::json::object const& request)
if (!request.at(secretType).is_string()) if (!request.at(secretType).is_string())
return Status{RippledError::rpcINVALID_PARAMS, "secret value must be string"}; return Status{RippledError::rpcINVALID_PARAMS, "secret value must be string"};
std::string key = request.at(secretType).as_string().c_str(); std::string const key = request.at(secretType).as_string().c_str();
if (secretType == "seed") if (secretType == "seed")
{
seed = ripple::parseBase58<ripple::Seed>(key); seed = ripple::parseBase58<ripple::Seed>(key);
}
else if (secretType == "passphrase") else if (secretType == "passphrase")
{
seed = ripple::parseGenericSeed(key); seed = ripple::parseGenericSeed(key);
}
else if (secretType == "seed_hex") else if (secretType == "seed_hex")
{ {
ripple::uint128 s; ripple::uint128 s;
if (s.parseHex(key)) if (s.parseHex(key))
seed.emplace(ripple::Slice(s.data(), s.size())); seed.emplace(ripple::Slice(s.data(), ripple::uint128::size()));
} }
} }
else else
@@ -806,7 +831,7 @@ keypairFromRequst(boost::json::object const& request)
if (!request.at("secret").is_string()) if (!request.at("secret").is_string())
return Status{RippledError::rpcINVALID_PARAMS, "field secret should be a string"}; return Status{RippledError::rpcINVALID_PARAMS, "field secret should be a string"};
std::string secret = request.at("secret").as_string().c_str(); std::string const secret = request.at("secret").as_string().c_str();
seed = ripple::parseGenericSeed(secret); seed = ripple::parseGenericSeed(secret);
} }
} }
@@ -861,7 +886,7 @@ isGlobalFrozen(
return false; return false;
ripple::SerialIter it{blob->data(), blob->size()}; ripple::SerialIter it{blob->data(), blob->size()};
ripple::SLE sle{it, key}; ripple::SLE const sle{it, key};
return sle.isFlag(ripple::lsfGlobalFreeze); return sle.isFlag(ripple::lsfGlobalFreeze);
} }
@@ -885,7 +910,7 @@ isFrozen(
return false; return false;
ripple::SerialIter it{blob->data(), blob->size()}; ripple::SerialIter it{blob->data(), blob->size()};
ripple::SLE sle{it, key}; ripple::SLE const sle{it, key};
if (sle.isFlag(ripple::lsfGlobalFreeze)) if (sle.isFlag(ripple::lsfGlobalFreeze))
return true; return true;
@@ -899,7 +924,7 @@ isFrozen(
return false; return false;
ripple::SerialIter issuerIt{blob->data(), blob->size()}; ripple::SerialIter issuerIt{blob->data(), blob->size()};
ripple::SLE issuerLine{issuerIt, key}; ripple::SLE const issuerLine{issuerIt, key};
auto frozen = (issuer > account) ? ripple::lsfHighFreeze : ripple::lsfLowFreeze; auto frozen = (issuer > account) ? ripple::lsfHighFreeze : ripple::lsfLowFreeze;
@@ -924,7 +949,7 @@ xrpLiquid(
return beast::zero; return beast::zero;
ripple::SerialIter it{blob->data(), blob->size()}; ripple::SerialIter it{blob->data(), blob->size()};
ripple::SLE sle{it, key}; ripple::SLE const sle{it, key};
std::uint32_t const ownerCount = sle.getFieldU32(ripple::sfOwnerCount); std::uint32_t const ownerCount = sle.getFieldU32(ripple::sfOwnerCount);
@@ -951,10 +976,8 @@ accountFunds(
{ {
return amount; return amount;
} }
else
{ return accountHolds(backend, sequence, id, amount.getCurrency(), amount.getIssuer(), true, yield);
return accountHolds(backend, sequence, id, amount.getCurrency(), amount.getIssuer(), true, yield);
}
} }
ripple::STAmount ripple::STAmount
@@ -983,7 +1006,7 @@ accountHolds(
} }
ripple::SerialIter it{blob->data(), blob->size()}; ripple::SerialIter it{blob->data(), blob->size()};
ripple::SLE sle{it, key}; ripple::SLE const sle{it, key};
if (zeroIfFrozen && isFrozen(backend, sequence, account, currency, issuer, yield)) if (zeroIfFrozen && isFrozen(backend, sequence, account, currency, issuer, yield))
{ {
@@ -1016,7 +1039,7 @@ transferRate(
if (blob) if (blob)
{ {
ripple::SerialIter it{blob->data(), blob->size()}; ripple::SerialIter it{blob->data(), blob->size()};
ripple::SLE sle{it, key}; ripple::SLE const sle{it, key};
if (sle.isFieldPresent(ripple::sfTransferRate)) if (sle.isFieldPresent(ripple::sfTransferRate))
return ripple::Rate{sle.getFieldU32(ripple::sfTransferRate)}; return ripple::Rate{sle.getFieldU32(ripple::sfTransferRate)};
@@ -1038,7 +1061,7 @@ postProcessOrderBook(
std::map<ripple::AccountID, ripple::STAmount> umBalance; std::map<ripple::AccountID, ripple::STAmount> umBalance;
bool globalFreeze = isGlobalFrozen(backend, ledgerSequence, book.out.account, yield) || bool const globalFreeze = isGlobalFrozen(backend, ledgerSequence, book.out.account, yield) ||
isGlobalFrozen(backend, ledgerSequence, book.in.account, yield); isGlobalFrozen(backend, ledgerSequence, book.in.account, yield);
auto rate = transferRate(backend, ledgerSequence, book.out.account, yield); auto rate = transferRate(backend, ledgerSequence, book.out.account, yield);
@@ -1048,8 +1071,8 @@ postProcessOrderBook(
try try
{ {
ripple::SerialIter it{obj.blob.data(), obj.blob.size()}; ripple::SerialIter it{obj.blob.data(), obj.blob.size()};
ripple::SLE offer{it, obj.key}; ripple::SLE const offer{it, obj.key};
ripple::uint256 bookDir = offer.getFieldH256(ripple::sfBookDirectory); ripple::uint256 const bookDir = offer.getFieldH256(ripple::sfBookDirectory);
auto const uOfferOwnerID = offer.getAccountID(ripple::sfAccount); auto const uOfferOwnerID = offer.getAccountID(ripple::sfAccount);
auto const& saTakerGets = offer.getFieldAmount(ripple::sfTakerGets); auto const& saTakerGets = offer.getFieldAmount(ripple::sfTakerGets);
@@ -1094,7 +1117,7 @@ postProcessOrderBook(
ripple::STAmount saTakerGetsFunded; ripple::STAmount saTakerGetsFunded;
ripple::STAmount saOwnerFundsLimit = saOwnerFunds; ripple::STAmount saOwnerFundsLimit = saOwnerFunds;
ripple::Rate offerRate = ripple::parityRate; ripple::Rate offerRate = ripple::parityRate;
ripple::STAmount dirRate = ripple::amountFromQuality(getQuality(bookDir)); ripple::STAmount const dirRate = ripple::amountFromQuality(getQuality(bookDir));
if (rate != ripple::parityRate if (rate != ripple::parityRate
// Have a tranfer fee. // Have a tranfer fee.
@@ -1122,7 +1145,7 @@ postProcessOrderBook(
.getJson(ripple::JsonOptions::none)); .getJson(ripple::JsonOptions::none));
} }
ripple::STAmount saOwnerPays = (ripple::parityRate == offerRate) ripple::STAmount const saOwnerPays = (ripple::parityRate == offerRate)
? saTakerGetsFunded ? saTakerGetsFunded
: std::min(saOwnerFunds, ripple::multiply(saTakerGetsFunded, offerRate)); : std::min(saOwnerFunds, ripple::multiply(saTakerGetsFunded, offerRate));
@@ -1148,26 +1171,34 @@ std::variant<Status, ripple::Book>
parseBook(ripple::Currency pays, ripple::AccountID payIssuer, ripple::Currency gets, ripple::AccountID getIssuer) parseBook(ripple::Currency pays, ripple::AccountID payIssuer, ripple::Currency gets, ripple::AccountID getIssuer)
{ {
if (isXRP(pays) && !isXRP(payIssuer)) if (isXRP(pays) && !isXRP(payIssuer))
{
return Status{ return Status{
RippledError::rpcSRC_ISR_MALFORMED, RippledError::rpcSRC_ISR_MALFORMED,
"Unneeded field 'taker_pays.issuer' for XRP currency " "Unneeded field 'taker_pays.issuer' for XRP currency "
"specification."}; "specification."};
}
if (!isXRP(pays) && isXRP(payIssuer)) if (!isXRP(pays) && isXRP(payIssuer))
{
return Status{ return Status{
RippledError::rpcSRC_ISR_MALFORMED, RippledError::rpcSRC_ISR_MALFORMED,
"Invalid field 'taker_pays.issuer', expected non-XRP " "Invalid field 'taker_pays.issuer', expected non-XRP "
"issuer."}; "issuer."};
}
if (ripple::isXRP(gets) && !ripple::isXRP(getIssuer)) if (ripple::isXRP(gets) && !ripple::isXRP(getIssuer))
{
return Status{ return Status{
RippledError::rpcDST_ISR_MALFORMED, RippledError::rpcDST_ISR_MALFORMED,
"Unneeded field 'taker_gets.issuer' for XRP currency " "Unneeded field 'taker_gets.issuer' for XRP currency "
"specification."}; "specification."};
}
if (!ripple::isXRP(gets) && ripple::isXRP(getIssuer)) if (!ripple::isXRP(gets) && ripple::isXRP(getIssuer))
{
return Status{ return Status{
RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', expected non-XRP issuer."}; RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', expected non-XRP issuer."};
}
if (pays == gets && payIssuer == getIssuer) if (pays == gets && payIssuer == getIssuer)
return Status{RippledError::rpcBAD_MARKET, "badMarket"}; return Status{RippledError::rpcBAD_MARKET, "badMarket"};
@@ -1202,9 +1233,11 @@ parseBook(boost::json::object const& request)
return Status{RippledError::rpcDST_AMT_MALFORMED}; return Status{RippledError::rpcDST_AMT_MALFORMED};
if (!taker_gets.at("currency").is_string()) if (!taker_gets.at("currency").is_string())
{
return Status{ return Status{
RippledError::rpcDST_AMT_MALFORMED, RippledError::rpcDST_AMT_MALFORMED,
}; };
}
ripple::Currency pay_currency; ripple::Currency pay_currency;
if (!ripple::to_currency(pay_currency, taker_pays.at("currency").as_string().c_str())) if (!ripple::to_currency(pay_currency, taker_pays.at("currency").as_string().c_str()))
@@ -1232,16 +1265,20 @@ parseBook(boost::json::object const& request)
} }
if (isXRP(pay_currency) && !isXRP(pay_issuer)) if (isXRP(pay_currency) && !isXRP(pay_issuer))
{
return Status{ return Status{
RippledError::rpcSRC_ISR_MALFORMED, RippledError::rpcSRC_ISR_MALFORMED,
"Unneeded field 'taker_pays.issuer' for XRP currency " "Unneeded field 'taker_pays.issuer' for XRP currency "
"specification."}; "specification."};
}
if (!isXRP(pay_currency) && isXRP(pay_issuer)) if (!isXRP(pay_currency) && isXRP(pay_issuer))
{
return Status{ return Status{
RippledError::rpcSRC_ISR_MALFORMED, RippledError::rpcSRC_ISR_MALFORMED,
"Invalid field 'taker_pays.issuer', expected non-XRP " "Invalid field 'taker_pays.issuer', expected non-XRP "
"issuer."}; "issuer."};
}
if ((!isXRP(pay_currency)) && (!taker_pays.contains("issuer"))) if ((!isXRP(pay_currency)) && (!taker_pays.contains("issuer")))
return Status{RippledError::rpcSRC_ISR_MALFORMED, "Missing non-XRP issuer."}; return Status{RippledError::rpcSRC_ISR_MALFORMED, "Missing non-XRP issuer."};
@@ -1257,10 +1294,12 @@ parseBook(boost::json::object const& request)
return Status{RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer."}; return Status{RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer."};
if (get_issuer == ripple::noAccount()) if (get_issuer == ripple::noAccount())
{
return Status{ return Status{
RippledError::rpcDST_ISR_MALFORMED, RippledError::rpcDST_ISR_MALFORMED,
"Invalid field 'taker_gets.issuer', bad issuer account " "Invalid field 'taker_gets.issuer', bad issuer account "
"one."}; "one."};
}
} }
else else
{ {
@@ -1268,14 +1307,18 @@ parseBook(boost::json::object const& request)
} }
if (ripple::isXRP(get_currency) && !ripple::isXRP(get_issuer)) if (ripple::isXRP(get_currency) && !ripple::isXRP(get_issuer))
{
return Status{ return Status{
RippledError::rpcDST_ISR_MALFORMED, RippledError::rpcDST_ISR_MALFORMED,
"Unneeded field 'taker_gets.issuer' for XRP currency " "Unneeded field 'taker_gets.issuer' for XRP currency "
"specification."}; "specification."};
}
if (!ripple::isXRP(get_currency) && ripple::isXRP(get_issuer)) if (!ripple::isXRP(get_currency) && ripple::isXRP(get_issuer))
{
return Status{ return Status{
RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', expected non-XRP issuer."}; RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', expected non-XRP issuer."};
}
if (pay_currency == get_currency && pay_issuer == get_issuer) if (pay_currency == get_currency && pay_issuer == get_issuer)
return Status{RippledError::rpcBAD_MARKET, "badMarket"}; return Status{RippledError::rpcBAD_MARKET, "badMarket"};
@@ -1304,7 +1347,7 @@ specifiesCurrentOrClosedLedger(boost::json::object const& request)
auto indexValue = request.at("ledger_index"); auto indexValue = request.at("ledger_index");
if (indexValue.is_string()) if (indexValue.is_string())
{ {
std::string index = indexValue.as_string().c_str(); std::string const index = indexValue.as_string().c_str();
return index == "current" || index == "closed"; return index == "current" || index == "closed";
} }
} }
@@ -1337,7 +1380,7 @@ isAmendmentEnabled(
// the amendments should always be present in ledger // the amendments should always be present in ledger
auto const& amendments = backend->fetchLedgerObject(ripple::keylet::amendments().key, seq, yield); auto const& amendments = backend->fetchLedgerObject(ripple::keylet::amendments().key, seq, yield);
ripple::SLE amendmentsSLE{ ripple::SLE const amendmentsSLE{
ripple::SerialIter{amendments->data(), amendments->size()}, ripple::keylet::amendments().key}; ripple::SerialIter{amendments->data(), amendments->size()}, ripple::keylet::amendments().key};
auto const listAmendments = amendmentsSLE.getFieldV256(ripple::sfAmendments); auto const listAmendments = amendmentsSLE.getFieldV256(ripple::sfAmendments);

View File

@@ -63,7 +63,7 @@ std::pair<std::shared_ptr<ripple::STTx const>, std::shared_ptr<ripple::TxMeta co
deserializeTxPlusMeta(data::TransactionAndMetadata const& blobs, std::uint32_t seq); deserializeTxPlusMeta(data::TransactionAndMetadata const& blobs, std::uint32_t seq);
std::pair<boost::json::object, boost::json::object> std::pair<boost::json::object, boost::json::object>
toExpandedJson(data::TransactionAndMetadata const& blobs, NFTokenjson includeNFTIDs = NFTokenjson::DISABLE); toExpandedJson(data::TransactionAndMetadata const& blobs, NFTokenjson nftEnabled = NFTokenjson::DISABLE);
bool bool
insertDeliveredAmount( insertDeliveredAmount(
@@ -111,7 +111,7 @@ traverseOwnedNodes(
BackendInterface const& backend, BackendInterface const& backend,
ripple::Keylet const& owner, ripple::Keylet const& owner,
ripple::uint256 const& hexMarker, ripple::uint256 const& hexMarker,
std::uint32_t const startHint, std::uint32_t startHint,
std::uint32_t sequence, std::uint32_t sequence,
std::uint32_t limit, std::uint32_t limit,
boost::asio::yield_context yield, boost::asio::yield_context yield,
@@ -210,7 +210,7 @@ std::variant<Status, ripple::Book>
parseBook(boost::json::object const& request); parseBook(boost::json::object const& request);
std::variant<Status, ripple::AccountID> std::variant<Status, ripple::AccountID>
parseTaker(boost::json::value const& request); parseTaker(boost::json::value const& taker);
bool bool
specifiesCurrentOrClosedLedger(boost::json::object const& request); specifiesCurrentOrClosedLedger(boost::json::object const& request);
@@ -231,7 +231,9 @@ logDuration(web::Context const& ctx, T const& dur)
{ {
using boost::json::serialize; using boost::json::serialize;
static util::Logger log{"RPC"}; static util::Logger const log{"RPC"};
static constexpr std::int64_t DURATION_ERROR_THRESHOLD_SECONDS = 10;
auto const millis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count(); auto const millis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
auto const seconds = std::chrono::duration_cast<std::chrono::seconds>(dur).count(); auto const seconds = std::chrono::duration_cast<std::chrono::seconds>(dur).count();
auto const msg = fmt::format( auto const msg = fmt::format(
@@ -239,10 +241,14 @@ logDuration(web::Context const& ctx, T const& dur)
millis, millis,
serialize(util::removeSecret(ctx.params))); serialize(util::removeSecret(ctx.params)));
if (seconds > 10) if (seconds > DURATION_ERROR_THRESHOLD_SECONDS)
{
LOG(log.error()) << ctx.tag() << msg; LOG(log.error()) << ctx.tag() << msg;
}
else if (seconds > 1) else if (seconds > 1)
{
LOG(log.warn()) << ctx.tag() << msg; LOG(log.warn()) << ctx.tag() << msg;
}
else else
LOG(log.info()) << ctx.tag() << msg; LOG(log.info()) << ctx.tag() << msg;
} }

View File

@@ -67,7 +67,7 @@ public:
static WorkQueue static WorkQueue
make_WorkQueue(util::Config const& config) make_WorkQueue(util::Config const& config)
{ {
static util::Logger log{"RPC"}; static util::Logger const log{"RPC"};
auto const serverConfig = config.section("server"); auto const serverConfig = config.section("server");
auto const numThreads = config.valueOr<uint32_t>("workers", std::thread::hardware_concurrency()); auto const numThreads = config.valueOr<uint32_t>("workers", std::thread::hardware_concurrency());
auto const maxQueueSize = serverConfig.valueOr<uint32_t>("max_queue_size", 0); // 0 is no limit auto const maxQueueSize = serverConfig.valueOr<uint32_t>("max_queue_size", 0); // 0 is no limit

View File

@@ -62,8 +62,10 @@ ValidateArrayAt::verify(boost::json::value& value, std::string_view key) const
auto& res = arr.at(idx_); auto& res = arr.at(idx_);
for (auto const& spec : specs_) for (auto const& spec : specs_)
{
if (auto const ret = spec.process(res); not ret) if (auto const ret = spec.process(res); not ret)
return Error{ret.error()}; return Error{ret.error()};
}
return {}; return {};
} }

View File

@@ -26,6 +26,8 @@
#include <fmt/core.h> #include <fmt/core.h>
#include <utility>
namespace rpc::meta { namespace rpc::meta {
/** /**
@@ -100,13 +102,13 @@ public:
*/ */
template <SomeRequirement... Requirements> template <SomeRequirement... Requirements>
IfType(Requirements&&... requirements) IfType(Requirements&&... requirements)
{ : processor_(
processor_ = [... r = std::forward<Requirements>(requirements)]( [... r = std::forward<Requirements>(
boost::json::value& j, std::string_view key) -> MaybeError { requirements)](boost::json::value& j, std::string_view key) -> MaybeError {
std::optional<Status> firstFailure = std::nullopt; std::optional<Status> firstFailure = std::nullopt;
// the check logic is the same as fieldspec // the check logic is the same as fieldspec
// clang-format off // clang-format off
([&j, &key, &firstFailure, req = &r]() { ([&j, &key, &firstFailure, req = &r]() {
if (firstFailure) if (firstFailure)
return; return;
@@ -114,13 +116,14 @@ public:
if (auto const res = req->verify(j, key); not res) if (auto const res = req->verify(j, key); not res)
firstFailure = res.error(); firstFailure = res.error();
}(), ...); }(), ...);
// clang-format on // clang-format on
if (firstFailure) if (firstFailure)
return Error{firstFailure.value()}; return Error{firstFailure.value()};
return {}; return {};
}; })
{
} }
/** /**
@@ -160,7 +163,7 @@ public:
* @brief Constructs a validator that calls the given validator `req` and returns a custom error `err` in case `req` * @brief Constructs a validator that calls the given validator `req` and returns a custom error `err` in case `req`
* fails. * fails.
*/ */
WithCustomError(SomeRequirement req, Status err) : requirement{std::move(req)}, error{err} WithCustomError(SomeRequirement req, Status err) : requirement{std::move(req)}, error{std::move(err)}
{ {
} }

View File

@@ -85,8 +85,8 @@ struct ToLower final
* @param key The key used to retrieve the modified value from the outer object * @param key The key used to retrieve the modified value from the outer object
* @return Possibly an error * @return Possibly an error
*/ */
[[nodiscard]] MaybeError [[nodiscard]] static MaybeError
modify(boost::json::value& value, std::string_view key) const modify(boost::json::value& value, std::string_view key)
{ {
if (not value.is_object() or not value.as_object().contains(key.data())) if (not value.is_object() or not value.as_object().contains(key.data()))
return {}; // ignore. field does not exist, let 'required' fail instead return {}; // ignore. field does not exist, let 'required' fail instead

View File

@@ -33,8 +33,10 @@ FieldSpec::process(boost::json::value& value) const
RpcSpec::process(boost::json::value& value) const RpcSpec::process(boost::json::value& value) const
{ {
for (auto const& field : fields_) for (auto const& field : fields_)
{
if (auto ret = field.process(value); not ret) if (auto ret = field.process(value); not ret)
return Error{ret.error()}; return Error{ret.error()};
}
return {}; return {};
} }

View File

@@ -85,7 +85,7 @@ struct RpcSpec final
RpcSpec(const RpcSpec& other, std::initializer_list<FieldSpec> additionalFields) : fields_{other.fields_} RpcSpec(const RpcSpec& other, std::initializer_list<FieldSpec> additionalFields) : fields_{other.fields_}
{ {
for (auto& f : additionalFields) for (auto& f : additionalFields)
fields_.push_back(std::move(f)); fields_.push_back(f);
} }
/** /**

View File

@@ -28,13 +28,15 @@
#include <boost/json/value.hpp> #include <boost/json/value.hpp>
#include <boost/json/value_from.hpp> #include <boost/json/value_from.hpp>
namespace etl {
class LoadBalancer; class LoadBalancer;
} // namespace etl
namespace web { namespace web {
struct ConnectionBase; struct ConnectionBase;
} } // namespace web
namespace feed { namespace feed {
class SubscriptionManager; class SubscriptionManager;
} } // namespace feed
namespace rpc { namespace rpc {
@@ -100,7 +102,7 @@ using Result = std::variant<Status, boost::json::object>;
struct AccountCursor struct AccountCursor
{ {
ripple::uint256 index; ripple::uint256 index;
std::uint32_t hint; std::uint32_t hint{};
std::string std::string
toString() const toString() const

View File

@@ -29,7 +29,7 @@
namespace rpc::validation { namespace rpc::validation {
[[nodiscard]] MaybeError [[nodiscard]] MaybeError
Required::verify(boost::json::value const& value, std::string_view key) const Required::verify(boost::json::value const& value, std::string_view key)
{ {
if (not value.is_object() or not value.as_object().contains(key.data())) if (not value.is_object() or not value.as_object().contains(key.data()))
return Error{Status{RippledError::rpcINVALID_PARAMS, "Required field '" + std::string{key} + "' missing"}}; return Error{Status{RippledError::rpcINVALID_PARAMS, "Required field '" + std::string{key} + "' missing"}};
@@ -49,7 +49,7 @@ CustomValidator::verify(boost::json::value const& value, std::string_view key) c
[[nodiscard]] bool [[nodiscard]] bool
checkIsU32Numeric(std::string_view sv) checkIsU32Numeric(std::string_view sv)
{ {
uint32_t unused; uint32_t unused = 0;
auto [_, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), unused); auto [_, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), unused);
return ec == std::errc(); return ec == std::errc();
@@ -145,12 +145,14 @@ CustomValidator IssuerValidator =
return Error{Status{RippledError::rpcINVALID_PARAMS, fmt::format("Invalid field '{}', bad issuer.", key)}}; return Error{Status{RippledError::rpcINVALID_PARAMS, fmt::format("Invalid field '{}', bad issuer.", key)}};
if (issuer == ripple::noAccount()) if (issuer == ripple::noAccount())
{
return Error{Status{ return Error{Status{
RippledError::rpcINVALID_PARAMS, RippledError::rpcINVALID_PARAMS,
fmt::format( fmt::format(
"Invalid field '{}', bad issuer account " "Invalid field '{}', bad issuer account "
"one.", "one.",
key)}}; key)}};
}
return MaybeError{}; return MaybeError{};
}}; }};
@@ -185,7 +187,7 @@ CustomValidator SubscribeAccountsValidator =
if (!value.is_array()) if (!value.is_array())
return Error{Status{RippledError::rpcINVALID_PARAMS, std::string(key) + "NotArray"}}; return Error{Status{RippledError::rpcINVALID_PARAMS, std::string(key) + "NotArray"}};
if (value.as_array().size() == 0) if (value.as_array().empty())
return Error{Status{RippledError::rpcACT_MALFORMED, std::string(key) + " malformed."}}; return Error{Status{RippledError::rpcACT_MALFORMED, std::string(key) + " malformed."}};
for (auto const& v : value.as_array()) for (auto const& v : value.as_array())
@@ -195,7 +197,7 @@ CustomValidator SubscribeAccountsValidator =
obj[keyItem] = v; obj[keyItem] = v;
if (auto const err = AccountValidator.verify(obj, keyItem); !err) if (auto err = AccountValidator.verify(obj, keyItem); !err)
return err; return err;
} }

View File

@@ -25,6 +25,8 @@
#include <fmt/core.h> #include <fmt/core.h>
#include <utility>
namespace rpc::validation { namespace rpc::validation {
/** /**
@@ -83,8 +85,8 @@ template <typename Expected>
*/ */
struct Required final struct Required final
{ {
[[nodiscard]] MaybeError [[nodiscard]] static MaybeError
verify(boost::json::value const& value, std::string_view key) const; verify(boost::json::value const& value, std::string_view key);
}; };
/** /**
@@ -129,9 +131,11 @@ public:
using boost::json::value_to; using boost::json::value_to;
auto const res = value_to<T>(value.as_object().at(key.data())); auto const res = value_to<T>(value.as_object().at(key.data()));
if (value_ == res) if (value_ == res)
{
return Error{Status{ return Error{Status{
RippledError::rpcNOT_SUPPORTED, RippledError::rpcNOT_SUPPORTED,
fmt::format("Not supported field '{}'s value '{}'", std::string{key}, res)}}; fmt::format("Not supported field '{}'s value '{}'", std::string{key}, res)}};
}
} }
return {}; return {};
} }
@@ -151,8 +155,8 @@ public:
* @param key The key used to retrieve the tested value from the outer object * @param key The key used to retrieve the tested value from the outer object
* @return `RippledError::rpcNOT_SUPPORTED` if the field is found; otherwise no error is returned * @return `RippledError::rpcNOT_SUPPORTED` if the field is found; otherwise no error is returned
*/ */
[[nodiscard]] MaybeError [[nodiscard]] static MaybeError
verify(boost::json::value const& value, std::string_view key) const verify(boost::json::value const& value, std::string_view key)
{ {
if (value.is_object() and value.as_object().contains(key.data())) if (value.is_object() and value.as_object().contains(key.data()))
return Error{Status{RippledError::rpcNOT_SUPPORTED, "Not supported field '" + std::string{key}}}; return Error{Status{RippledError::rpcNOT_SUPPORTED, "Not supported field '" + std::string{key}}};
@@ -340,7 +344,7 @@ public:
* *
* @param original The original value to store * @param original The original value to store
*/ */
explicit EqualTo(Type original) : original_{original} explicit EqualTo(Type original) : original_{std::move(original)}
{ {
} }

View File

@@ -32,8 +32,8 @@ public:
* @param ip The ip addr of the client * @param ip The ip addr of the client
* @return true if authorized; false otherwise * @return true if authorized; false otherwise
*/ */
bool static bool
isAdmin(std::string_view ip) const isAdmin(std::string_view ip)
{ {
return ip == "127.0.0.1"; return ip == "127.0.0.1";
} }

View File

@@ -80,10 +80,7 @@ public:
request.at("accounts").as_bool())); request.at("accounts").as_bool()));
}; };
if (checkAccountInfoForward() or checkLedgerForward()) return static_cast<bool>(checkAccountInfoForward() or checkLedgerForward());
return true;
return false;
} }
Result Result
@@ -92,16 +89,15 @@ public:
auto toForward = ctx.params; auto toForward = ctx.params;
toForward["command"] = ctx.method; toForward["command"] = ctx.method;
if (auto const res = balancer_->forwardToRippled(toForward, ctx.clientIp, ctx.yield); not res) auto const res = balancer_->forwardToRippled(toForward, ctx.clientIp, ctx.yield);
if (not res)
{ {
notifyFailedToForward(ctx.method); notifyFailedToForward(ctx.method);
return Status{RippledError::rpcFAILED_TO_FORWARD}; return Status{RippledError::rpcFAILED_TO_FORWARD};
} }
else
{ notifyForwarded(ctx.method);
notifyForwarded(ctx.method); return *res;
return *res;
}
} }
bool bool

View File

@@ -34,10 +34,10 @@ class LoadBalancer;
} // namespace etl } // namespace etl
namespace rpc { namespace rpc {
class Counters; class Counters;
} } // namespace rpc
namespace feed { namespace feed {
class SubscriptionManager; class SubscriptionManager;
} } // namespace feed
namespace rpc::detail { namespace rpc::detail {

View File

@@ -50,17 +50,20 @@ struct DefaultProcessor final
// real handler is given expected Input, not json // real handler is given expected Input, not json
if (!ret) if (!ret)
{
return Error{ret.error()}; // forward Status return Error{ret.error()}; // forward Status
else }
return value_from(ret.value()); return value_from(ret.value());
} }
else if constexpr (SomeHandlerWithoutInput<HandlerType>) else if constexpr (SomeHandlerWithoutInput<HandlerType>)
{ {
// no input to pass, ignore the value // no input to pass, ignore the value
if (auto const ret = handler.process(ctx); not ret) auto const ret = handler.process(ctx);
if (not ret)
{
return Error{ret.error()}; // forward Status return Error{ret.error()}; // forward Status
else }
return value_from(ret.value()); return value_from(ret.value());
} }
else else
{ {

View File

@@ -23,7 +23,7 @@
namespace rpc { namespace rpc {
void void
AccountChannelsHandler::addChannel(std::vector<ChannelResponse>& jsonChannels, ripple::SLE const& channelSle) const AccountChannelsHandler::addChannel(std::vector<ChannelResponse>& jsonChannels, ripple::SLE const& channelSle)
{ {
ChannelResponse channel; ChannelResponse channel;
channel.channelID = ripple::to_string(channelSle.key()); channel.channelID = ripple::to_string(channelSle.key());
@@ -128,9 +128,13 @@ tag_invoke(boost::json::value_to_tag<AccountChannelsHandler::Input>, boost::json
if (jsonObject.contains(JS(ledger_index))) if (jsonObject.contains(JS(ledger_index)))
{ {
if (!jsonObject.at(JS(ledger_index)).is_string()) if (!jsonObject.at(JS(ledger_index)).is_string())
{
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64(); input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
}
else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") else if (jsonObject.at(JS(ledger_index)).as_string() != "validated")
{
input.ledgerIndex = std::stoi(jv.at(JS(ledger_index)).as_string().c_str()); input.ledgerIndex = std::stoi(jv.at(JS(ledger_index)).as_string().c_str());
}
} }
return input; return input;

View File

@@ -56,7 +56,7 @@ public:
std::string balance; std::string balance;
std::optional<std::string> publicKey; std::optional<std::string> publicKey;
std::optional<std::string> publicKeyHex; std::optional<std::string> publicKeyHex;
uint32_t settleDelay; uint32_t settleDelay{};
std::optional<uint32_t> expiration; std::optional<uint32_t> expiration;
std::optional<uint32_t> cancelAfter; std::optional<uint32_t> cancelAfter;
std::optional<uint32_t> sourceTag; std::optional<uint32_t> sourceTag;
@@ -68,10 +68,10 @@ public:
std::vector<ChannelResponse> channels; std::vector<ChannelResponse> channels;
std::string account; std::string account;
std::string ledgerHash; std::string ledgerHash;
uint32_t ledgerIndex; uint32_t ledgerIndex{};
// validated should be sent via framework // validated should be sent via framework
bool validated = true; bool validated = true;
uint32_t limit; uint32_t limit{};
std::optional<std::string> marker; std::optional<std::string> marker;
}; };
@@ -92,8 +92,8 @@ public:
{ {
} }
RpcSpecConstRef static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion) const spec([[maybe_unused]] uint32_t apiVersion)
{ {
static auto const rpcSpec = RpcSpec{ static auto const rpcSpec = RpcSpec{
{JS(account), validation::Required{}, validation::AccountValidator}, {JS(account), validation::Required{}, validation::AccountValidator},
@@ -114,8 +114,8 @@ public:
process(Input input, Context const& ctx) const; process(Input input, Context const& ctx) const;
private: private:
void static void
addChannel(std::vector<ChannelResponse>& jsonLines, ripple::SLE const& line) const; addChannel(std::vector<ChannelResponse>& jsonChannels, ripple::SLE const& channelSle);
friend void friend void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output const& output); tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output const& output);

View File

@@ -106,9 +106,13 @@ tag_invoke(boost::json::value_to_tag<AccountCurrenciesHandler::Input>, boost::js
if (jsonObject.contains(JS(ledger_index))) if (jsonObject.contains(JS(ledger_index)))
{ {
if (!jsonObject.at(JS(ledger_index)).is_string()) if (!jsonObject.at(JS(ledger_index)).is_string())
{
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64(); input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
}
else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") else if (jsonObject.at(JS(ledger_index)).as_string() != "validated")
{
input.ledgerIndex = std::stoi(jv.at(JS(ledger_index)).as_string().c_str()); input.ledgerIndex = std::stoi(jv.at(JS(ledger_index)).as_string().c_str());
}
} }
return input; return input;

View File

@@ -44,7 +44,7 @@ public:
struct Output struct Output
{ {
std::string ledgerHash; std::string ledgerHash;
uint32_t ledgerIndex; uint32_t ledgerIndex{};
std::set<std::string> receiveCurrencies; std::set<std::string> receiveCurrencies;
std::set<std::string> sendCurrencies; std::set<std::string> sendCurrencies;
// validated should be sent via framework // validated should be sent via framework
@@ -65,8 +65,8 @@ public:
{ {
} }
RpcSpecConstRef static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion) const spec([[maybe_unused]] uint32_t apiVersion)
{ {
static auto const rpcSpec = RpcSpec{ static auto const rpcSpec = RpcSpec{
{JS(account), validation::Required{}, validation::AccountValidator}, {JS(account), validation::Required{}, validation::AccountValidator},

View File

@@ -128,7 +128,7 @@ tag_invoke(boost::json::value_from_tag, boost::json::value& jv, AccountInfoHandl
if (output.isClawbackEnabled) if (output.isClawbackEnabled)
{ {
lsFlags.push_back({"allowTrustLineClawback", ripple::lsfAllowTrustLineClawback}); lsFlags.emplace_back("allowTrustLineClawback", ripple::lsfAllowTrustLineClawback);
} }
boost::json::object acctFlags; boost::json::object acctFlags;
@@ -146,9 +146,13 @@ tag_invoke(boost::json::value_from_tag, boost::json::value& jv, AccountInfoHandl
std::back_inserter(signers), std::back_inserter(signers),
[](auto const& signerList) { return toJson(signerList); }); [](auto const& signerList) { return toJson(signerList); });
if (output.apiVersion == 1) if (output.apiVersion == 1)
{
jv.as_object()[JS(account_data)].as_object()[JS(signer_lists)] = std::move(signers); jv.as_object()[JS(account_data)].as_object()[JS(signer_lists)] = std::move(signers);
}
else else
{
jv.as_object()[JS(signer_lists)] = signers; jv.as_object()[JS(signer_lists)] = signers;
}
} }
} }
@@ -170,9 +174,13 @@ tag_invoke(boost::json::value_to_tag<AccountInfoHandler::Input>, boost::json::va
if (jsonObject.contains(JS(ledger_index))) if (jsonObject.contains(JS(ledger_index)))
{ {
if (!jsonObject.at(JS(ledger_index)).is_string()) if (!jsonObject.at(JS(ledger_index)).is_string())
{
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64(); input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
}
else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") else if (jsonObject.at(JS(ledger_index)).as_string() != "validated")
{
input.ledgerIndex = std::stoi(jsonObject.at(JS(ledger_index)).as_string().c_str()); input.ledgerIndex = std::stoi(jsonObject.at(JS(ledger_index)).as_string().c_str());
}
} }
if (jsonObject.contains(JS(signer_lists))) if (jsonObject.contains(JS(signer_lists)))

View File

@@ -86,8 +86,8 @@ public:
{ {
} }
RpcSpecConstRef static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion) const spec([[maybe_unused]] uint32_t apiVersion)
{ {
static auto const rpcSpecV1 = RpcSpec{ static auto const rpcSpecV1 = RpcSpec{
{JS(account), validation::AccountValidator}, {JS(account), validation::AccountValidator},

View File

@@ -26,7 +26,7 @@ AccountLinesHandler::addLine(
std::vector<LineResponse>& lines, std::vector<LineResponse>& lines,
ripple::SLE const& lineSle, ripple::SLE const& lineSle,
ripple::AccountID const& account, ripple::AccountID const& account,
std::optional<ripple::AccountID> const& peerAccount) const std::optional<ripple::AccountID> const& peerAccount)
{ {
auto const flags = lineSle.getFieldU32(ripple::sfFlags); auto const flags = lineSle.getFieldU32(ripple::sfFlags);
auto const lowLimit = lineSle.getFieldAmount(ripple::sfLowLimit); auto const lowLimit = lineSle.getFieldAmount(ripple::sfLowLimit);
@@ -52,12 +52,12 @@ AccountLinesHandler::addLine(
if (not viewLowest) if (not viewLowest)
balance.negate(); balance.negate();
bool const lineAuth = flags & (viewLowest ? ripple::lsfLowAuth : ripple::lsfHighAuth); bool const lineAuth = (flags & (viewLowest ? ripple::lsfLowAuth : ripple::lsfHighAuth)) != 0u;
bool const lineAuthPeer = flags & (not viewLowest ? ripple::lsfLowAuth : ripple::lsfHighAuth); bool const lineAuthPeer = (flags & (not viewLowest ? ripple::lsfLowAuth : ripple::lsfHighAuth)) != 0u;
bool const lineNoRipple = flags & (viewLowest ? ripple::lsfLowNoRipple : ripple::lsfHighNoRipple); bool const lineNoRipple = (flags & (viewLowest ? ripple::lsfLowNoRipple : ripple::lsfHighNoRipple)) != 0u;
bool const lineNoRipplePeer = flags & (not viewLowest ? ripple::lsfLowNoRipple : ripple::lsfHighNoRipple); bool const lineNoRipplePeer = (flags & (not viewLowest ? ripple::lsfLowNoRipple : ripple::lsfHighNoRipple)) != 0u;
bool const lineFreeze = flags & (viewLowest ? ripple::lsfLowFreeze : ripple::lsfHighFreeze); bool const lineFreeze = (flags & (viewLowest ? ripple::lsfLowFreeze : ripple::lsfHighFreeze)) != 0u;
bool const lineFreezePeer = flags & (not viewLowest ? ripple::lsfLowFreeze : ripple::lsfHighFreeze); bool const lineFreezePeer = (flags & (not viewLowest ? ripple::lsfLowFreeze : ripple::lsfHighFreeze)) != 0u;
ripple::STAmount const& saBalance = balance; ripple::STAmount const& saBalance = balance;
ripple::STAmount const& saLimit = lineLimit; ripple::STAmount const& saLimit = lineLimit;
@@ -119,9 +119,13 @@ AccountLinesHandler::process(AccountLinesHandler::Input input, Context const& ct
if (input.ignoreDefault) if (input.ignoreDefault)
{ {
if (sle.getFieldAmount(ripple::sfLowLimit).getIssuer() == accountID) if (sle.getFieldAmount(ripple::sfLowLimit).getIssuer() == accountID)
ignore = !(sle.getFieldU32(ripple::sfFlags) & ripple::lsfLowReserve); {
ignore = ((sle.getFieldU32(ripple::sfFlags) & ripple::lsfLowReserve) == 0u);
}
else else
ignore = !(sle.getFieldU32(ripple::sfFlags) & ripple::lsfHighReserve); {
ignore = ((sle.getFieldU32(ripple::sfFlags) & ripple::lsfHighReserve) == 0u);
}
} }
if (not ignore) if (not ignore)
@@ -174,9 +178,13 @@ tag_invoke(boost::json::value_to_tag<AccountLinesHandler::Input>, boost::json::v
if (jsonObject.contains(JS(ledger_index))) if (jsonObject.contains(JS(ledger_index)))
{ {
if (!jsonObject.at(JS(ledger_index)).is_string()) if (!jsonObject.at(JS(ledger_index)).is_string())
{
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64(); input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
}
else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") else if (jsonObject.at(JS(ledger_index)).as_string() != "validated")
{
input.ledgerIndex = std::stoi(jv.at(JS(ledger_index)).as_string().c_str()); input.ledgerIndex = std::stoi(jv.at(JS(ledger_index)).as_string().c_str());
}
} }
return input; return input;

View File

@@ -53,10 +53,10 @@ public:
std::string currency; std::string currency;
std::string limit; std::string limit;
std::string limitPeer; std::string limitPeer;
uint32_t qualityIn; uint32_t qualityIn{};
uint32_t qualityOut; uint32_t qualityOut{};
bool noRipple; bool noRipple{};
bool noRipplePeer; bool noRipplePeer{};
std::optional<bool> authorized; std::optional<bool> authorized;
std::optional<bool> peerAuthorized; std::optional<bool> peerAuthorized;
std::optional<bool> freeze; std::optional<bool> freeze;
@@ -68,10 +68,10 @@ public:
std::string account; std::string account;
std::vector<LineResponse> lines; std::vector<LineResponse> lines;
std::string ledgerHash; std::string ledgerHash;
uint32_t ledgerIndex; uint32_t ledgerIndex{};
bool validated = true; // should be sent via framework bool validated = true; // should be sent via framework
std::optional<std::string> marker; std::optional<std::string> marker;
uint32_t limit; uint32_t limit{};
}; };
struct Input struct Input
@@ -92,8 +92,8 @@ public:
{ {
} }
RpcSpecConstRef static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion) const spec([[maybe_unused]] uint32_t apiVersion)
{ {
static auto const rpcSpec = RpcSpec{ static auto const rpcSpec = RpcSpec{
{JS(account), {JS(account),
@@ -117,12 +117,12 @@ public:
process(Input input, Context const& ctx) const; process(Input input, Context const& ctx) const;
private: private:
void static void
addLine( addLine(
std::vector<LineResponse>& lines, std::vector<LineResponse>& lines,
ripple::SLE const& lineSle, ripple::SLE const& lineSle,
ripple::AccountID const& account, ripple::AccountID const& account,
std::optional<ripple::AccountID> const& peerAccount) const; std::optional<ripple::AccountID> const& peerAccount);
private: private:
friend void friend void

View File

@@ -76,7 +76,7 @@ AccountNFTsHandler::process(AccountNFTsHandler::Input input, Context const& ctx)
obj[SFS(sfNFTokenTaxon)] = ripple::nft::toUInt32(ripple::nft::getTaxon(nftokenID)); obj[SFS(sfNFTokenTaxon)] = ripple::nft::toUInt32(ripple::nft::getTaxon(nftokenID));
obj[JS(nft_serial)] = ripple::nft::getSerial(nftokenID); obj[JS(nft_serial)] = ripple::nft::getSerial(nftokenID);
if (std::uint16_t xferFee = {ripple::nft::getTransferFee(nftokenID)}) if (std::uint16_t const xferFee = {ripple::nft::getTransferFee(nftokenID)})
obj[SFS(sfTransferFee)] = xferFee; obj[SFS(sfTransferFee)] = xferFee;
} }
@@ -132,9 +132,13 @@ tag_invoke(boost::json::value_to_tag<AccountNFTsHandler::Input>, boost::json::va
if (jsonObject.contains(JS(ledger_index))) if (jsonObject.contains(JS(ledger_index)))
{ {
if (!jsonObject.at(JS(ledger_index)).is_string()) if (!jsonObject.at(JS(ledger_index)).is_string())
{
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64(); input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
}
else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") else if (jsonObject.at(JS(ledger_index)).as_string() != "validated")
{
input.ledgerIndex = std::stoi(jsonObject.at(JS(ledger_index)).as_string().c_str()); input.ledgerIndex = std::stoi(jsonObject.at(JS(ledger_index)).as_string().c_str());
}
} }
if (jsonObject.contains(JS(limit))) if (jsonObject.contains(JS(limit)))

View File

@@ -68,8 +68,8 @@ public:
{ {
} }
RpcSpecConstRef static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion) const spec([[maybe_unused]] uint32_t apiVersion)
{ {
static auto const rpcSpec = RpcSpec{ static auto const rpcSpec = RpcSpec{
{JS(account), validation::Required{}, validation::AccountValidator}, {JS(account), validation::Required{}, validation::AccountValidator},

View File

@@ -149,9 +149,13 @@ tag_invoke(boost::json::value_to_tag<AccountObjectsHandler::Input>, boost::json:
if (jsonObject.contains(JS(ledger_index))) if (jsonObject.contains(JS(ledger_index)))
{ {
if (!jsonObject.at(JS(ledger_index)).is_string()) if (!jsonObject.at(JS(ledger_index)).is_string())
{
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64(); input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
}
else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") else if (jsonObject.at(JS(ledger_index)).as_string() != "validated")
{
input.ledgerIndex = std::stoi(jv.at(JS(ledger_index)).as_string().c_str()); input.ledgerIndex = std::stoi(jv.at(JS(ledger_index)).as_string().c_str());
}
} }
if (jsonObject.contains(JS(type))) if (jsonObject.contains(JS(type)))

View File

@@ -54,9 +54,9 @@ public:
{ {
std::string account; std::string account;
std::string ledgerHash; std::string ledgerHash;
uint32_t ledgerIndex; uint32_t ledgerIndex{};
std::optional<std::string> marker; std::optional<std::string> marker;
uint32_t limit; uint32_t limit{};
std::vector<ripple::SLE> accountObjects; std::vector<ripple::SLE> accountObjects;
bool validated = true; bool validated = true;
}; };
@@ -79,8 +79,8 @@ public:
{ {
} }
RpcSpecConstRef static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion) const spec([[maybe_unused]] uint32_t apiVersion)
{ {
static auto const rpcSpec = RpcSpec{ static auto const rpcSpec = RpcSpec{
{JS(account), validation::Required{}, validation::AccountValidator}, {JS(account), validation::Required{}, validation::AccountValidator},

View File

@@ -22,7 +22,7 @@
namespace rpc { namespace rpc {
void void
AccountOffersHandler::addOffer(std::vector<Offer>& offers, ripple::SLE const& offerSle) const AccountOffersHandler::addOffer(std::vector<Offer>& offers, ripple::SLE const& offerSle)
{ {
auto offer = AccountOffersHandler::Offer(); auto offer = AccountOffersHandler::Offer();
offer.takerPays = offerSle.getFieldAmount(ripple::sfTakerPays); offer.takerPays = offerSle.getFieldAmount(ripple::sfTakerPays);
@@ -115,13 +115,17 @@ tag_invoke(boost::json::value_from_tag, boost::json::value& jv, AccountOffersHan
auto const convertAmount = [&](const char* field, ripple::STAmount const& amount) { auto const convertAmount = [&](const char* field, ripple::STAmount const& amount) {
if (amount.native()) if (amount.native())
{
jsonObject[field] = amount.getText(); jsonObject[field] = amount.getText();
}
else else
{
jsonObject[field] = { jsonObject[field] = {
{JS(currency), ripple::to_string(amount.getCurrency())}, {JS(currency), ripple::to_string(amount.getCurrency())},
{JS(issuer), ripple::to_string(amount.getIssuer())}, {JS(issuer), ripple::to_string(amount.getIssuer())},
{JS(value), amount.getText()}, {JS(value), amount.getText()},
}; };
}
}; };
convertAmount(JS(taker_pays), offer.takerPays); convertAmount(JS(taker_pays), offer.takerPays);
@@ -143,9 +147,13 @@ tag_invoke(boost::json::value_to_tag<AccountOffersHandler::Input>, boost::json::
if (jsonObject.contains(JS(ledger_index))) if (jsonObject.contains(JS(ledger_index)))
{ {
if (!jsonObject.at(JS(ledger_index)).is_string()) if (!jsonObject.at(JS(ledger_index)).is_string())
{
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64(); input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
}
else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") else if (jsonObject.at(JS(ledger_index)).as_string() != "validated")
{
input.ledgerIndex = std::stoi(jsonObject.at(JS(ledger_index)).as_string().c_str()); input.ledgerIndex = std::stoi(jsonObject.at(JS(ledger_index)).as_string().c_str());
}
} }
if (jsonObject.contains(JS(limit))) if (jsonObject.contains(JS(limit)))

View File

@@ -44,8 +44,8 @@ public:
struct Offer struct Offer
{ {
uint32_t flags; uint32_t flags{};
uint32_t seq; uint32_t seq{};
ripple::STAmount takerGets; ripple::STAmount takerGets;
ripple::STAmount takerPays; ripple::STAmount takerPays;
std::string quality; std::string quality;
@@ -56,7 +56,7 @@ public:
{ {
std::string account; std::string account;
std::string ledgerHash; std::string ledgerHash;
uint32_t ledgerIndex; uint32_t ledgerIndex{};
std::vector<Offer> offers; std::vector<Offer> offers;
std::optional<std::string> marker; std::optional<std::string> marker;
// validated should be sent via framework // validated should be sent via framework
@@ -79,8 +79,8 @@ public:
{ {
} }
RpcSpecConstRef static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion) const spec([[maybe_unused]] uint32_t apiVersion)
{ {
static auto const rpcSpec = RpcSpec{ static auto const rpcSpec = RpcSpec{
{JS(account), validation::Required{}, validation::AccountValidator}, {JS(account), validation::Required{}, validation::AccountValidator},
@@ -99,8 +99,8 @@ public:
process(Input input, Context const& ctx) const; process(Input input, Context const& ctx) const;
private: private:
void static void
addOffer(std::vector<Offer>& offers, ripple::SLE const& offerSle) const; addOffer(std::vector<Offer>& offers, ripple::SLE const& offerSle);
friend void friend void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output const& output); tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output const& output);

View File

@@ -128,9 +128,13 @@ AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx) con
// if forward, start at minIndex - 1, because the SQL query is exclusive, we need to include the 0 transaction // if forward, start at minIndex - 1, because the SQL query is exclusive, we need to include the 0 transaction
// index of minIndex // index of minIndex
if (input.forward) if (input.forward)
{
cursor = {minIndex - 1, std::numeric_limits<int32_t>::max()}; cursor = {minIndex - 1, std::numeric_limits<int32_t>::max()};
}
else else
{
cursor = {maxIndex, std::numeric_limits<int32_t>::max()}; cursor = {maxIndex, std::numeric_limits<int32_t>::max()};
}
} }
auto const limit = input.limit.value_or(LIMIT_DEFAULT); auto const limit = input.limit.value_or(LIMIT_DEFAULT);
@@ -156,7 +160,7 @@ AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx) con
response.marker = std::nullopt; response.marker = std::nullopt;
break; break;
} }
else if (txnPlusMeta.ledgerSequence > maxIndex && !input.forward) if (txnPlusMeta.ledgerSequence > maxIndex && !input.forward)
{ {
LOG(log_.debug()) << "Skipping over transactions from incomplete ledger"; LOG(log_.debug()) << "Skipping over transactions from incomplete ledger";
continue; continue;
@@ -250,12 +254,18 @@ tag_invoke(boost::json::value_to_tag<AccountTxHandler::Input>, boost::json::valu
if (jsonObject.contains(JS(ledger_index))) if (jsonObject.contains(JS(ledger_index)))
{ {
if (!jsonObject.at(JS(ledger_index)).is_string()) if (!jsonObject.at(JS(ledger_index)).is_string())
{
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64(); input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
}
else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") else if (jsonObject.at(JS(ledger_index)).as_string() != "validated")
{
input.ledgerIndex = std::stoi(jsonObject.at(JS(ledger_index)).as_string().c_str()); input.ledgerIndex = std::stoi(jsonObject.at(JS(ledger_index)).as_string().c_str());
}
else else
{
// could not get the latest validated ledger seq here, using this flag to indicate that // could not get the latest validated ledger seq here, using this flag to indicate that
input.usingValidatedLedger = true; input.usingValidatedLedger = true;
}
} }
if (jsonObject.contains(JS(binary))) if (jsonObject.contains(JS(binary)))
@@ -268,9 +278,11 @@ tag_invoke(boost::json::value_to_tag<AccountTxHandler::Input>, boost::json::valu
input.limit = jsonObject.at(JS(limit)).as_int64(); input.limit = jsonObject.at(JS(limit)).as_int64();
if (jsonObject.contains(JS(marker))) if (jsonObject.contains(JS(marker)))
{
input.marker = AccountTxHandler::Marker{ input.marker = AccountTxHandler::Marker{
jsonObject.at(JS(marker)).as_object().at(JS(ledger)).as_int64(), jsonObject.at(JS(marker)).as_object().at(JS(ledger)).as_int64(),
jsonObject.at(JS(marker)).as_object().at(JS(seq)).as_int64()}; jsonObject.at(JS(marker)).as_object().at(JS(seq)).as_int64()};
}
if (jsonObject.contains("tx_type")) if (jsonObject.contains("tx_type"))
{ {

View File

@@ -57,8 +57,8 @@ public:
struct Output struct Output
{ {
std::string account; std::string account;
uint32_t ledgerIndexMin; uint32_t ledgerIndexMin{0};
uint32_t ledgerIndexMax; uint32_t ledgerIndexMax{0};
std::optional<uint32_t> limit; std::optional<uint32_t> limit;
std::optional<Marker> marker; std::optional<Marker> marker;
// TODO: use a better type than json // TODO: use a better type than json
@@ -90,8 +90,8 @@ public:
{ {
} }
RpcSpecConstRef static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion) const spec([[maybe_unused]] uint32_t apiVersion)
{ {
static auto const rpcSpecForV1 = RpcSpec{ static auto const rpcSpecForV1 = RpcSpec{
{JS(account), validation::Required{}, validation::AccountValidator}, {JS(account), validation::Required{}, validation::AccountValidator},

View File

@@ -70,15 +70,19 @@ tag_invoke(boost::json::value_to_tag<BookChangesHandler::Input>, boost::json::va
if (jsonObject.contains(JS(ledger_index))) if (jsonObject.contains(JS(ledger_index)))
{ {
if (!jsonObject.at(JS(ledger_index)).is_string()) if (!jsonObject.at(JS(ledger_index)).is_string())
{
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64(); input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
}
else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") else if (jsonObject.at(JS(ledger_index)).as_string() != "validated")
{
input.ledgerIndex = std::stoi(jv.at(JS(ledger_index)).as_string().c_str()); input.ledgerIndex = std::stoi(jv.at(JS(ledger_index)).as_string().c_str());
}
} }
return input; return input;
} }
[[nodiscard]] boost::json::object const [[nodiscard]] boost::json::object
computeBookChanges(ripple::LedgerHeader const& lgrInfo, std::vector<data::TransactionAndMetadata> const& transactions) computeBookChanges(ripple::LedgerHeader const& lgrInfo, std::vector<data::TransactionAndMetadata> const& transactions)
{ {
using boost::json::value_from; using boost::json::value_from;

View File

@@ -40,8 +40,8 @@ public:
struct Output struct Output
{ {
std::string ledgerHash; std::string ledgerHash;
uint32_t ledgerIndex; uint32_t ledgerIndex{};
uint32_t ledgerTime; uint32_t ledgerTime{};
std::vector<BookChange> bookChanges; std::vector<BookChange> bookChanges;
bool validated = true; bool validated = true;
}; };
@@ -59,8 +59,8 @@ public:
{ {
} }
RpcSpecConstRef static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion) const spec([[maybe_unused]] uint32_t apiVersion)
{ {
static auto const rpcSpec = RpcSpec{ static auto const rpcSpec = RpcSpec{
{JS(ledger_hash), validation::Uint256HexStringValidator}, {JS(ledger_hash), validation::Uint256HexStringValidator},

View File

@@ -84,9 +84,13 @@ tag_invoke(boost::json::value_to_tag<BookOffersHandler::Input>, boost::json::val
if (jsonObject.contains(JS(ledger_index))) if (jsonObject.contains(JS(ledger_index)))
{ {
if (!jsonObject.at(JS(ledger_index)).is_string()) if (!jsonObject.at(JS(ledger_index)).is_string())
{
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64(); input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
}
else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") else if (jsonObject.at(JS(ledger_index)).as_string() != "validated")
{
input.ledgerIndex = std::stoi(jv.at(JS(ledger_index)).as_string().c_str()); input.ledgerIndex = std::stoi(jv.at(JS(ledger_index)).as_string().c_str());
}
} }
if (jsonObject.contains(JS(taker))) if (jsonObject.contains(JS(taker)))

View File

@@ -70,8 +70,8 @@ public:
{ {
} }
RpcSpecConstRef static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion) const spec([[maybe_unused]] uint32_t apiVersion)
{ {
static auto const rpcSpec = RpcSpec{ static auto const rpcSpec = RpcSpec{
{JS(taker_gets), {JS(taker_gets),

View File

@@ -63,7 +63,7 @@ DepositAuthorizedHandler::process(DepositAuthorizedHandler::Input input, Context
// Check destination for the DepositAuth flag. // Check destination for the DepositAuth flag.
// If that flag is not set then a deposit should be just fine. // If that flag is not set then a deposit should be just fine.
if (sle.getFieldU32(ripple::sfFlags) & ripple::lsfDepositAuth) if ((sle.getFieldU32(ripple::sfFlags) & ripple::lsfDepositAuth) != 0u)
{ {
// See if a preauthorization entry is in the ledger. // See if a preauthorization entry is in the ledger.
auto const depositPreauthKeylet = ripple::keylet::depositPreauth(*destinationAccountID, *sourceAccountID); auto const depositPreauthKeylet = ripple::keylet::depositPreauth(*destinationAccountID, *sourceAccountID);
@@ -91,9 +91,13 @@ tag_invoke(boost::json::value_to_tag<DepositAuthorizedHandler::Input>, boost::js
if (jsonObject.contains(JS(ledger_index))) if (jsonObject.contains(JS(ledger_index)))
{ {
if (!jsonObject.at(JS(ledger_index)).is_string()) if (!jsonObject.at(JS(ledger_index)).is_string())
{
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64(); input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
}
else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") else if (jsonObject.at(JS(ledger_index)).as_string() != "validated")
{
input.ledgerIndex = std::stoi(jv.at(JS(ledger_index)).as_string().c_str()); input.ledgerIndex = std::stoi(jv.at(JS(ledger_index)).as_string().c_str());
}
} }
return input; return input;

View File

@@ -47,7 +47,7 @@ public:
std::string sourceAccount; std::string sourceAccount;
std::string destinationAccount; std::string destinationAccount;
std::string ledgerHash; std::string ledgerHash;
uint32_t ledgerIndex; uint32_t ledgerIndex{};
// validated should be sent via framework // validated should be sent via framework
bool validated = true; bool validated = true;
}; };
@@ -67,8 +67,8 @@ public:
{ {
} }
RpcSpecConstRef static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion) const spec([[maybe_unused]] uint32_t apiVersion)
{ {
static auto const rpcSpec = RpcSpec{ static auto const rpcSpec = RpcSpec{
{JS(source_account), validation::Required{}, validation::AccountValidator}, {JS(source_account), validation::Required{}, validation::AccountValidator},

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