Merge branch 'develop' of github.com:XRPLF/clio into refactor/consteval-specs-move-errors

This commit is contained in:
Alex Kremer
2026-08-26 16:52:52 +01:00
11 changed files with 443 additions and 107 deletions

View File

@@ -75,17 +75,15 @@ include(Settings)
include(PatchNixBinary)
# Clio deps
include(deps/libxrpl)
include(deps/Boost)
include(deps/OpenSSL)
include(deps/Threads)
include(deps/libfmt)
include(deps/cassandra)
include(deps/libbacktrace)
include(deps/spdlog)
include(deps/libfmt)
include(deps/libxrpl)
include(deps/OpenSSL)
include(deps/rpcspec)
target_link_libraries(clio_options INTERFACE rpcspec::rpcspec)
include(deps/spdlog)
include(deps/Threads)
add_subdirectory(src)
add_subdirectory(tests)

View File

@@ -1,5 +1 @@
find_package(xrpl-rpc-spec REQUIRED CONFIG)
if(NOT DEFINED RPCSPEC_IS_CLIO)
add_compile_definitions(RPCSPEC_IS_CLIO=1)
endif()

View File

@@ -3,7 +3,7 @@
"requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1782392402.420688",
"xrpl-rpc-spec/0.1.3#e0552b7fdcb26d9249498fa4273474f1%1786463175.210094",
"xrpl-rpc-spec/0.1.6#0147dce06088874791dca62dc8503b6c%1787751048.16847",
"xrpl/3.3.0#5e356a24ae1f0d6da6bd617b926f92e6%1786467262.262007",
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1782392403.185447",
"spdlog/1.17.0#bcbaaf7147bda6ad24ffbd1ac3d7142c%1782736610.443882",

View File

@@ -17,7 +17,7 @@ class ClioConan(ConanFile):
"fmt/12.1.0",
"libbacktrace/cci.20210118",
"spdlog/1.17.0",
"xrpl-rpc-spec/0.1.3",
"xrpl-rpc-spec/0.1.6",
"xrpl/3.3.0",
]
@@ -33,6 +33,7 @@ class ClioConan(ConanFile):
"protobuf/*:shared": False,
"protobuf/*:with_zlib": True,
"snappy/*:shared": False,
"xrpl-rpc-spec/*:server": "clio",
"xrpl/*:rocksdb": True, # TODO: revert to false when includes are fixed in libxrpl
"xrpl/*:tests": False,
}

View File

@@ -29,4 +29,4 @@ target_sources(
impl/ext/Successor.cpp
)
target_link_libraries(clio_etl PUBLIC clio_data clio_util)
target_link_libraries(clio_etl PUBLIC clio_data clio_util rpcspec::rpcspec)

View File

@@ -9,4 +9,4 @@ target_sources(
impl/SingleFeedBase.cpp
)
target_link_libraries(clio_feed PRIVATE clio_util)
target_link_libraries(clio_feed PRIVATE clio_util rpcspec::rpcspec)

View File

@@ -21,6 +21,7 @@ target_sources(
common/MetaProcessors.cpp
common/impl/APIVersionParser.cpp
common/impl/HandlerProvider.cpp
common/impl/HandlerRegistry.cpp
filters/impl/DelegateTransactionsFilter.cpp
handlers/AccountChannels.cpp
handlers/AccountCurrencies.cpp
@@ -60,4 +61,4 @@ target_sources(
handlers/VaultInfo.cpp
)
target_link_libraries(clio_rpc PUBLIC clio_util clio_data)
target_link_libraries(clio_rpc PUBLIC clio_util clio_data rpcspec::rpcspec)

View File

@@ -5,47 +5,8 @@
#include "etl/ETLServiceInterface.hpp"
#include "etl/LoadBalancerInterface.hpp"
#include "feed/SubscriptionManagerInterface.hpp"
#include "rpc/Counters.hpp"
#include "rpc/common/AnyHandler.hpp"
#include "rpc/handlers/AMMInfo.hpp"
#include "rpc/handlers/AccountChannels.hpp"
#include "rpc/handlers/AccountCurrencies.hpp"
#include "rpc/handlers/AccountInfo.hpp"
#include "rpc/handlers/AccountLines.hpp"
#include "rpc/handlers/AccountMPTokenIssuances.hpp"
#include "rpc/handlers/AccountMPTokens.hpp"
#include "rpc/handlers/AccountNFTs.hpp"
#include "rpc/handlers/AccountObjects.hpp"
#include "rpc/handlers/AccountOffers.hpp"
#include "rpc/handlers/AccountTx.hpp"
#include "rpc/handlers/BookChanges.hpp"
#include "rpc/handlers/BookOffers.hpp"
#include "rpc/handlers/DepositAuthorized.hpp"
#include "rpc/handlers/Feature.hpp"
#include "rpc/handlers/GatewayBalances.hpp"
#include "rpc/handlers/GetAggregatePrice.hpp"
#include "rpc/handlers/Ledger.hpp"
#include "rpc/handlers/LedgerData.hpp"
#include "rpc/handlers/LedgerEntry.hpp"
#include "rpc/handlers/LedgerIndex.hpp"
#include "rpc/handlers/LedgerRange.hpp"
#include "rpc/handlers/MPTHolders.hpp"
#include "rpc/handlers/MPTokenIssuanceHistory.hpp"
#include "rpc/handlers/NFTBuyOffers.hpp"
#include "rpc/handlers/NFTHistory.hpp"
#include "rpc/handlers/NFTInfo.hpp"
#include "rpc/handlers/NFTSellOffers.hpp"
#include "rpc/handlers/NFTsByIssuer.hpp"
#include "rpc/handlers/NoRippleCheck.hpp"
#include "rpc/handlers/Ping.hpp"
#include "rpc/handlers/Random.hpp"
#include "rpc/handlers/ServerInfo.hpp"
#include "rpc/handlers/Subscribe.hpp"
#include "rpc/handlers/TransactionEntry.hpp"
#include "rpc/handlers/Tx.hpp"
#include "rpc/handlers/Unsubscribe.hpp"
#include "rpc/handlers/VaultInfo.hpp"
#include "rpc/handlers/VersionHandler.hpp"
#include "rpc/common/impl/HandlerRegistry.hpp"
#include "util/config/ConfigDefinition.hpp"
#include <memory>
@@ -64,57 +25,29 @@ ProductionHandlerProvider::ProductionHandlerProvider(
std::shared_ptr<data::AmendmentCenterInterface const> const& amendmentCenter,
Counters const& counters
)
: handlerMap_{
{"account_channels", {.handler = AccountChannelsHandler{backend}}},
{"account_currencies", {.handler = AccountCurrenciesHandler{backend}}},
{"account_info", {.handler = AccountInfoHandler{backend, amendmentCenter}}},
{"account_lines", {.handler = AccountLinesHandler{backend}}},
{"account_mptoken_issuances",
{.handler = AccountMPTokenIssuancesHandler{backend}, .isClioOnly = true}}, // clio only
{"account_mptokens",
{.handler = AccountMPTokensHandler{backend}, .isClioOnly = true}}, // clio only
{"account_nfts", {.handler = AccountNFTsHandler{backend}}},
{"account_objects", {.handler = AccountObjectsHandler{backend}}},
{"account_offers", {.handler = AccountOffersHandler{backend}}},
{"account_tx", {.handler = AccountTxHandler{backend, etl}}},
{"amm_info", {.handler = AMMInfoHandler{backend, amendmentCenter}}},
{"book_changes", {.handler = BookChangesHandler{backend}}},
{"book_offers", {.handler = BookOffersHandler{backend, amendmentCenter}}},
{"deposit_authorized", {.handler = DepositAuthorizedHandler{backend}}},
{"feature", {.handler = FeatureHandler{backend, amendmentCenter}}},
{"gateway_balances", {.handler = GatewayBalancesHandler{backend}}},
{"get_aggregate_price", {.handler = GetAggregatePriceHandler{backend}}},
{"ledger", {.handler = LedgerHandler{backend, amendmentCenter}}},
{"ledger_data", {.handler = LedgerDataHandler{backend}}},
{"ledger_entry", {.handler = LedgerEntryHandler{backend}}},
{"ledger_index",
{.handler = LedgerIndexHandler{backend}, .isClioOnly = true}}, // clio only
{"ledger_range", {.handler = LedgerRangeHandler{backend}}},
{"mpt_holders",
{.handler = MPTHoldersHandler{backend}, .isClioOnly = true}}, // clio only
{"mptoken_issuance_history",
{.handler = MPTokenIssuanceHistoryHandler{backend}, .isClioOnly = true}}, // clio only
{"nfts_by_issuer",
{.handler = NFTsByIssuerHandler{backend}, .isClioOnly = true}}, // clio only
{"nft_history",
{.handler = NFTHistoryHandler{backend}, .isClioOnly = true}}, // clio only
{"nft_buy_offers", {.handler = NFTBuyOffersHandler{backend}}},
{"nft_info", {.handler = NFTInfoHandler{backend}, .isClioOnly = true}}, // clio only
{"nft_sell_offers", {.handler = NFTSellOffersHandler{backend}}},
{"noripple_check", {.handler = NoRippleCheckHandler{backend}}},
{"ping", {.handler = PingHandler{}}},
{"random", {.handler = RandomHandler{}}},
{"server_info",
{.handler = ServerInfoHandler{backend, subscriptionManager, balancer, etl, counters}}},
{"transaction_entry", {.handler = TransactionEntryHandler{backend}}},
{"tx", {.handler = TxHandler{backend, etl}}},
{"subscribe",
{.handler = SubscribeHandler{backend, amendmentCenter, subscriptionManager}}},
{"unsubscribe", {.handler = UnsubscribeHandler{subscriptionManager}}},
{"vault_info", {.handler = VaultInfoHandler{backend}}},
{"version", {.handler = VersionHandler{config}}},
}
{
HandlerDeps const deps{
.config = config,
.backend = backend,
.subscriptionManager = subscriptionManager,
.balancer = balancer,
.etl = etl,
.amendmentCenter = amendmentCenter,
.counters = counters
};
auto const registry = handlerRegistry();
handlerMap_.reserve(registry.size());
for (auto const& entry : registry) {
handlerMap_.emplace(
entry.name,
Handler{
.handler = entry.factory(deps),
.isClioOnly = entry.isClioOnly,
}
);
}
}
bool

View File

@@ -0,0 +1,328 @@
#include "rpc/common/impl/HandlerRegistry.hpp"
#include "rpc/Counters.hpp" // IWYU pragma: keep
#include "rpc/common/AnyHandler.hpp"
#include "rpc/handlers/AMMInfo.hpp"
#include "rpc/handlers/AccountChannels.hpp"
#include "rpc/handlers/AccountCurrencies.hpp"
#include "rpc/handlers/AccountInfo.hpp"
#include "rpc/handlers/AccountLines.hpp"
#include "rpc/handlers/AccountMPTokenIssuances.hpp"
#include "rpc/handlers/AccountMPTokens.hpp"
#include "rpc/handlers/AccountNFTs.hpp"
#include "rpc/handlers/AccountObjects.hpp"
#include "rpc/handlers/AccountOffers.hpp"
#include "rpc/handlers/AccountTx.hpp"
#include "rpc/handlers/BookChanges.hpp"
#include "rpc/handlers/BookOffers.hpp"
#include "rpc/handlers/DepositAuthorized.hpp"
#include "rpc/handlers/Feature.hpp"
#include "rpc/handlers/GatewayBalances.hpp"
#include "rpc/handlers/GetAggregatePrice.hpp"
#include "rpc/handlers/Ledger.hpp"
#include "rpc/handlers/LedgerData.hpp"
#include "rpc/handlers/LedgerEntry.hpp"
#include "rpc/handlers/LedgerIndex.hpp"
#include "rpc/handlers/LedgerRange.hpp"
#include "rpc/handlers/MPTHolders.hpp"
#include "rpc/handlers/MPTokenIssuanceHistory.hpp"
#include "rpc/handlers/NFTBuyOffers.hpp"
#include "rpc/handlers/NFTHistory.hpp"
#include "rpc/handlers/NFTInfo.hpp"
#include "rpc/handlers/NFTSellOffers.hpp"
#include "rpc/handlers/NFTsByIssuer.hpp"
#include "rpc/handlers/NoRippleCheck.hpp"
#include "rpc/handlers/Ping.hpp"
#include "rpc/handlers/Random.hpp"
#include "rpc/handlers/ServerInfo.hpp"
#include "rpc/handlers/Subscribe.hpp"
#include "rpc/handlers/TransactionEntry.hpp"
#include "rpc/handlers/Tx.hpp"
#include "rpc/handlers/Unsubscribe.hpp"
#include "rpc/handlers/VaultInfo.hpp"
#include "rpc/handlers/VersionHandler.hpp"
#include "util/Concepts.hpp"
#include <array>
#include <span>
#include <tuple>
namespace rpc::impl {
namespace {
constexpr auto kHandlers = std::to_array<HandlerEntry>({
{
.name = "account_channels",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return AccountChannelsHandler{d.backend};
},
},
{
.name = "account_currencies",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return AccountCurrenciesHandler{d.backend};
},
},
{
.name = "account_info",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return AccountInfoHandler{d.backend, d.amendmentCenter};
},
},
{
.name = "account_lines",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return AccountLinesHandler{d.backend};
},
},
{
.name = "account_mptoken_issuances",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return AccountMPTokenIssuancesHandler{d.backend};
},
.isClioOnly = true,
},
{
.name = "account_mptokens",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return AccountMPTokensHandler{d.backend};
},
.isClioOnly = true,
},
{
.name = "account_nfts",
.factory = [](HandlerDeps const& d) -> AnyHandler { return AccountNFTsHandler{d.backend}; },
},
{
.name = "account_objects",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return AccountObjectsHandler{d.backend};
},
},
{
.name = "account_offers",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return AccountOffersHandler{d.backend};
},
},
{
.name = "account_tx",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return AccountTxHandler{d.backend, d.etl};
},
},
{
.name = "amm_info",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return AMMInfoHandler{d.backend, d.amendmentCenter};
},
},
{
.name = "book_changes",
.factory = [](HandlerDeps const& d) -> AnyHandler { return BookChangesHandler{d.backend}; },
},
{
.name = "book_offers",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return BookOffersHandler{d.backend, d.amendmentCenter};
},
},
{
.name = "deposit_authorized",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return DepositAuthorizedHandler{d.backend};
},
},
{
.name = "feature",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return FeatureHandler{d.backend, d.amendmentCenter};
},
},
{
.name = "gateway_balances",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return GatewayBalancesHandler{d.backend};
},
},
{
.name = "get_aggregate_price",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return GetAggregatePriceHandler{d.backend};
},
},
{
.name = "ledger",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return LedgerHandler{d.backend, d.amendmentCenter};
},
},
{
.name = "ledger_data",
.factory = [](HandlerDeps const& d) -> AnyHandler { return LedgerDataHandler{d.backend}; },
},
{
.name = "ledger_entry",
.factory = [](HandlerDeps const& d) -> AnyHandler { return LedgerEntryHandler{d.backend}; },
},
{
.name = "ledger_index",
.factory = [](HandlerDeps const& d) -> AnyHandler { return LedgerIndexHandler{d.backend}; },
.isClioOnly = true,
},
{
.name = "ledger_range",
.factory = [](HandlerDeps const& d) -> AnyHandler { return LedgerRangeHandler{d.backend}; },
},
{
.name = "mpt_holders",
.factory = [](HandlerDeps const& d) -> AnyHandler { return MPTHoldersHandler{d.backend}; },
.isClioOnly = true,
},
{
.name = "mptoken_issuance_history",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return MPTokenIssuanceHistoryHandler{d.backend};
},
.isClioOnly = true,
},
{
.name = "nfts_by_issuer",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return NFTsByIssuerHandler{d.backend};
},
.isClioOnly = true,
},
{
.name = "nft_history",
.factory = [](HandlerDeps const& d) -> AnyHandler { return NFTHistoryHandler{d.backend}; },
.isClioOnly = true,
},
{
.name = "nft_buy_offers",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return NFTBuyOffersHandler{d.backend};
},
},
{
.name = "nft_info",
.factory = [](HandlerDeps const& d) -> AnyHandler { return NFTInfoHandler{d.backend}; },
.isClioOnly = true,
},
{
.name = "nft_sell_offers",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return NFTSellOffersHandler{d.backend};
},
},
{
.name = "noripple_check",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return NoRippleCheckHandler{d.backend};
},
},
{
.name = "ping",
.factory = [](HandlerDeps const&) -> AnyHandler { return PingHandler{}; },
},
{
.name = "random",
.factory = [](HandlerDeps const&) -> AnyHandler { return RandomHandler{}; },
},
{
.name = "server_info",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return ServerInfoHandler{
d.backend, d.subscriptionManager, d.balancer, d.etl, d.counters
};
},
},
{
.name = "transaction_entry",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return TransactionEntryHandler{d.backend};
},
},
{
.name = "tx",
.factory = [](HandlerDeps const& d) -> AnyHandler { return TxHandler{d.backend, d.etl}; },
},
{
.name = "subscribe",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return SubscribeHandler{d.backend, d.amendmentCenter, d.subscriptionManager};
},
},
{
.name = "unsubscribe",
.factory = [](HandlerDeps const& d) -> AnyHandler {
return UnsubscribeHandler{d.subscriptionManager};
},
},
{
.name = "vault_info",
.factory = [](HandlerDeps const& d) -> AnyHandler { return VaultInfoHandler{d.backend}; },
},
{
.name = "version",
.factory = [](HandlerDeps const& d) -> AnyHandler { return VersionHandler{d.config}; },
},
});
// A duplicate name would silently shadow a handler so we check at compile time.
static_assert(
std::apply(
[](auto const&... entry) { return util::hasNoDuplicates(entry.name...); },
kHandlers
),
"RPC handler names must be unique"
);
} // namespace
std::span<HandlerEntry const>
handlerRegistry() noexcept
{
return kHandlers;
}
} // namespace rpc::impl

View File

@@ -0,0 +1,79 @@
/** @file */
#pragma once
#include "rpc/common/AnyHandler.hpp"
#include <memory>
#include <span>
#include <string_view>
namespace data {
class AmendmentCenterInterface;
class BackendInterface;
} // namespace data
namespace etl {
struct ETLServiceInterface;
class LoadBalancerInterface;
} // namespace etl
namespace feed {
class SubscriptionManagerInterface;
} // namespace feed
namespace util::config {
class ClioConfigDefinition;
} // namespace util::config
namespace rpc {
class Counters;
} // namespace rpc
namespace rpc::impl {
/**
* @brief The bundle of runtime dependencies a handler factory may consume.
*
* A short-lived view over the caller's objects: it holds references only, so it must
* not outlive the call it is passed to.
*/
struct HandlerDeps {
util::config::ClioConfigDefinition const& config;
std::shared_ptr<data::BackendInterface> const& backend;
std::shared_ptr<feed::SubscriptionManagerInterface> const& subscriptionManager;
std::shared_ptr<etl::LoadBalancerInterface> const& balancer;
std::shared_ptr<etl::ETLServiceInterface const> const& etl;
std::shared_ptr<data::AmendmentCenterInterface const> const& amendmentCenter;
Counters const& counters;
};
/**
* @brief Constructs one type-erased handler from the available dependencies.
*
* A plain function pointer rather than @c std::function so that the registry stays a
* literal type and is constant-initialised.
*/
using HandlerFactory = AnyHandler (*)(HandlerDeps const&);
/**
* @brief One row of the handler registry: the method name, how to construct the
* handler, and its static metadata.
*/
struct HandlerEntry {
std::string_view name;
HandlerFactory factory;
bool isClioOnly = false;
};
/**
* @brief The full set of registered RPC handlers.
*
* Static data: reading the registry constructs no handler and requires no runtime
* dependencies.
*
* @return A span over the registry, valid for the lifetime of the program
*/
[[nodiscard]] std::span<HandlerEntry const>
handlerRegistry() noexcept;
} // namespace rpc::impl

View File

@@ -21,4 +21,4 @@ target_sources(
SubscriptionContext.cpp
)
target_link_libraries(clio_web PUBLIC clio_util)
target_link_libraries(clio_web PUBLIC clio_util rpcspec::rpcspec)