diff --git a/conan.lock b/conan.lock index c0a1fcd70c..a599de0815 100644 --- a/conan.lock +++ b/conan.lock @@ -3,7 +3,7 @@ "requires": [ "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708", "xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1782392402.420688", - "xrpl-rpc-spec/0.1.6#0147dce06088874791dca62dc8503b6c%1787751048.16847", + "xrpl-rpc-spec/0.1.7#774d2f93c4b48a1523d8d5a94a2b082a%1787768955.574105", "xrpl/3.3.0#5e356a24ae1f0d6da6bd617b926f92e6%1786467262.262007", "sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1782392403.185447", "spdlog/1.17.0#bcbaaf7147bda6ad24ffbd1ac3d7142c%1782736610.443882", diff --git a/conanfile.py b/conanfile.py index 3ef0797cf7..056f092b27 100644 --- a/conanfile.py +++ b/conanfile.py @@ -17,7 +17,7 @@ class ClioConan(ConanFile): "fmt/12.1.0", "libbacktrace/cci.20210118", "spdlog/1.17.0", - "xrpl-rpc-spec/0.1.6", + "xrpl-rpc-spec/0.1.7", "xrpl/3.3.0", ] diff --git a/src/app/WebHandlers.cpp b/src/app/WebHandlers.cpp index dce06c4120..a320927b5a 100644 --- a/src/app/WebHandlers.cpp +++ b/src/app/WebHandlers.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include diff --git a/src/etl/CMakeLists.txt b/src/etl/CMakeLists.txt index 17cabe312d..89ff928949 100644 --- a/src/etl/CMakeLists.txt +++ b/src/etl/CMakeLists.txt @@ -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) diff --git a/src/etl/LoadBalancer.cpp b/src/etl/LoadBalancer.cpp index 9bb14b7776..e2c42cf8f8 100644 --- a/src/etl/LoadBalancer.cpp +++ b/src/etl/LoadBalancer.cpp @@ -7,7 +7,6 @@ #include "etl/NetworkValidatedLedgersInterface.hpp" #include "etl/Source.hpp" #include "feed/SubscriptionManagerInterface.hpp" -#include "rpc/Errors.hpp" #include "util/Assert.hpp" #include "util/CoroutineGroup.hpp" #include "util/Profiler.hpp" @@ -27,6 +26,7 @@ #include #include #include +#include #include #include @@ -292,7 +292,7 @@ LoadBalancer::forwardToRippled( auto xUserValue = isAdmin ? kAdminForwardingXUserValue : kUserForwardingXUserValue; std::optional response; - rpc::ClioError error = rpc::ClioError::EtlConnectionError; + rpc::ClioError error = rpc::ClioError::RpcForwardingConnectionError; while (numAttempts < sources_.size()) { auto [res, duration] = util::timed([&]() { return sources_[sourceIdx]->forwardToRippled(request, clientIp, xUserValue, yield); diff --git a/src/etl/Source.hpp b/src/etl/Source.hpp index ca7acb9b80..7118e31fc0 100644 --- a/src/etl/Source.hpp +++ b/src/etl/Source.hpp @@ -4,7 +4,6 @@ #include "etl/LoadBalancerInterface.hpp" #include "etl/NetworkValidatedLedgersInterface.hpp" #include "feed/SubscriptionManagerInterface.hpp" -#include "rpc/Errors.hpp" #include "util/config/ObjectView.hpp" #include @@ -13,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/src/etl/impl/ForwardingSource.cpp b/src/etl/impl/ForwardingSource.cpp index 1ea8512efb..2b41154713 100644 --- a/src/etl/impl/ForwardingSource.cpp +++ b/src/etl/impl/ForwardingSource.cpp @@ -1,6 +1,5 @@ #include "etl/impl/ForwardingSource.hpp" -#include "rpc/Errors.hpp" #include "util/log/Logger.hpp" #include @@ -10,6 +9,7 @@ #include #include #include +#include #include #include @@ -58,14 +58,14 @@ ForwardingSource::forwardToRippled( auto expectedConnection = connectionBuilder.connect(yield); if (not expectedConnection) { LOG(log_.debug()) << "Couldn't connect to rippled to forward request."; - return std::unexpected{rpc::ClioError::EtlConnectionError}; + return std::unexpected{rpc::ClioError::RpcForwardingConnectionError}; } auto& connection = expectedConnection.value(); auto writeError = connection->write(boost::json::serialize(request), yield, forwardingTimeout_); if (writeError) { LOG(log_.debug()) << "Error sending request to rippled to forward request."; - return std::unexpected{rpc::ClioError::EtlRequestError}; + return std::unexpected{rpc::ClioError::RpcForwardingRequestError}; } auto response = connection->read(yield, forwardingTimeout_); @@ -73,10 +73,10 @@ ForwardingSource::forwardToRippled( if (auto errorCode = response.error().errorCode(); errorCode.has_value() and errorCode->value() == boost::system::errc::timed_out) { LOG(log_.debug()) << "Request to rippled timed out"; - return std::unexpected{rpc::ClioError::EtlRequestTimeout}; + return std::unexpected{rpc::ClioError::RpcForwardingTimeout}; } LOG(log_.debug()) << "Error sending request to rippled to forward request."; - return std::unexpected{rpc::ClioError::EtlRequestError}; + return std::unexpected{rpc::ClioError::RpcForwardingRequestError}; } boost::json::value parsedResponse; @@ -87,7 +87,7 @@ ForwardingSource::forwardToRippled( } catch (std::exception const& e) { LOG(log_.debug()) << "Error parsing response from rippled: " << e.what() << ". Response: " << *response; - return std::unexpected{rpc::ClioError::EtlInvalidResponse}; + return std::unexpected{rpc::ClioError::RpcForwardingInvalidResponse}; } auto responseObject = parsedResponse.as_object(); diff --git a/src/etl/impl/ForwardingSource.hpp b/src/etl/impl/ForwardingSource.hpp index 3806b9b71c..d86e3a3127 100644 --- a/src/etl/impl/ForwardingSource.hpp +++ b/src/etl/impl/ForwardingSource.hpp @@ -1,11 +1,11 @@ #pragma once -#include "rpc/Errors.hpp" #include "util/log/Logger.hpp" #include "util/requests/WsConnection.hpp" #include #include +#include #include #include diff --git a/src/etl/impl/SourceImpl.hpp b/src/etl/impl/SourceImpl.hpp index 211c65d118..790c2fba63 100644 --- a/src/etl/impl/SourceImpl.hpp +++ b/src/etl/impl/SourceImpl.hpp @@ -6,12 +6,12 @@ #include "etl/impl/ForwardingSource.hpp" #include "etl/impl/GrpcSource.hpp" #include "etl/impl/SubscriptionSource.hpp" -#include "rpc/Errors.hpp" #include #include #include #include +#include #include #include diff --git a/src/feed/CMakeLists.txt b/src/feed/CMakeLists.txt index e136caca87..acfd1361ce 100644 --- a/src/feed/CMakeLists.txt +++ b/src/feed/CMakeLists.txt @@ -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) diff --git a/src/rpc/CredentialHelpers.cpp b/src/rpc/CredentialHelpers.cpp index 2601cb03e6..f89f904f14 100644 --- a/src/rpc/CredentialHelpers.cpp +++ b/src/rpc/CredentialHelpers.cpp @@ -1,5 +1,4 @@ #include "data/BackendInterface.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/common/Types.hpp" #include "util/Assert.hpp" @@ -7,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/Errors.cpp b/src/rpc/Errors.cpp index f8ec37cfa4..c0c3b0ea51 100644 --- a/src/rpc/Errors.cpp +++ b/src/rpc/Errors.cpp @@ -4,10 +4,12 @@ #include "util/OverloadSet.hpp" #include +#include #include #include #include +#include #include #include #include @@ -22,6 +24,16 @@ using namespace std; namespace rpc { +/** + * @brief Stream a Status in human readable form. + * + * Declared in rpcspec but implemented here: rendering a code needs Clio's + * getErrorInfo table and xrpl::RPC::getErrorInfo. + * + * @param stream The stream to write to + * @param status The status to write + * @return The same stream + */ std::ostream& operator<<(std::ostream& stream, Status const& status) { @@ -57,100 +69,100 @@ operator<<(std::ostream& stream, Status const& status) return stream; } -WarningInfo const& -getWarningInfo(WarningCode code) -{ - static constexpr WarningInfo kInfos[]{ - {WarningCode::WarnUnknown, "Unknown warning"}, - {WarningCode::WarnRpcClio, - "This is a clio server. clio only serves validated data. If you want to talk to rippled, " - "include " - "'ledger_index':'current' in your request"}, - {WarningCode::WarnRpcOutdated, "This server may be out of date"}, - {WarningCode::WarnRpcRateLimit, "You are about to be rate limited"}, - {WarningCode::WarnRpcDeprecated, - "Some fields from your request are deprecated. Please check the documentation at " - "https://xrpl.org/docs/references/http-websocket-apis/ and update your request."} - }; - - auto matchByCode = [code](auto const& info) { return info.code == code; }; - if (auto it = ranges::find_if(kInfos, matchByCode); it != end(kInfos)) - return *it; - - throw(out_of_range("Invalid WarningCode")); -} - -boost::json::object -makeWarning(WarningCode code) -{ - auto json = boost::json::object{}; - auto const& info = getWarningInfo(code); - json["id"] = static_cast(code); - json["message"] = info.message; - return json; -} - ClioErrorInfo const& getErrorInfo(ClioError code) { - static constexpr ClioErrorInfo kInfos[]{ - {.code = ClioError::RpcMalformedCurrency, - .error = "malformedCurrency", - .message = "Malformed currency."}, - {.code = ClioError::RpcMalformedRequest, - .error = "malformedRequest", - .message = "Malformed request."}, - {.code = ClioError::RpcMalformedOwner, - .error = "malformedOwner", - .message = "Malformed owner."}, - {.code = ClioError::RpcMalformedAddress, - .error = "malformedAddress", - .message = "Malformed address."}, - {.code = ClioError::RpcUnknownOption, - .error = "unknownOption", - .message = "Unknown option."}, - {.code = ClioError::RpcFieldNotFoundTransaction, - .error = "fieldNotFoundTransaction", - .message = "Missing field."}, - {.code = ClioError::RpcMalformedOracleDocumentId, - .error = "malformedDocumentID", - .message = "Malformed oracle_document_id."}, - {.code = ClioError::RpcMalformedAuthorizedCredentials, - .error = "malformedAuthorizedCredentials", - .message = "Malformed authorized credentials."}, + static constexpr auto kInfos = std::to_array({ + { + .code = ClioError::RpcMalformedCurrency, + .error = "malformedCurrency", + .message = "Malformed currency.", + }, + { + .code = ClioError::RpcMalformedRequest, + .error = "malformedRequest", + .message = "Malformed request.", + }, + { + .code = ClioError::RpcMalformedOwner, + .error = "malformedOwner", + .message = "Malformed owner.", + }, + { + .code = ClioError::RpcMalformedAddress, + .error = "malformedAddress", + .message = "Malformed address.", + }, + { + .code = ClioError::RpcUnknownOption, + .error = "unknownOption", + .message = "Unknown option.", + }, + { + .code = ClioError::RpcFieldNotFoundTransaction, + .error = "fieldNotFoundTransaction", + .message = "Missing field.", + }, + { + .code = ClioError::RpcMalformedOracleDocumentId, + .error = "malformedDocumentID", + .message = "Malformed oracle_document_id.", + }, + { + .code = ClioError::RpcMalformedAuthorizedCredentials, + .error = "malformedAuthorizedCredentials", + .message = "Malformed authorized credentials.", + }, // special system errors - {.code = ClioError::RpcInvalidApiVersion, - .error = JS(invalid_API_version), - .message = "Invalid API version."}, - {.code = ClioError::RpcCommandIsMissing, - .error = JS(missingCommand), - .message = "Method is not specified or is not a string."}, - {.code = ClioError::RpcCommandNotString, - .error = "commandNotString", - .message = "Method is not a string."}, - {.code = ClioError::RpcCommandIsEmpty, - .error = "emptyCommand", - .message = "Method is an empty string."}, - {.code = ClioError::RpcParamsUnparsable, - .error = "paramsUnparsable", - .message = "Params must be an array holding exactly one object."}, - // etl related errors - {.code = ClioError::EtlConnectionError, - .error = "connectionError", - .message = "Couldn't connect to rippled."}, - {.code = ClioError::EtlRequestError, - .error = "requestError", - .message = "Error sending request to rippled."}, - {.code = ClioError::EtlRequestTimeout, - .error = "timeout", - .message = "Request to rippled timed out."}, - {.code = ClioError::EtlInvalidResponse, - .error = "invalidResponse", - .message = "Rippled returned an invalid response."} - }; + { + .code = ClioError::RpcInvalidApiVersion, + .error = JS(invalid_API_version), + .message = "Invalid API version.", + }, + { + .code = ClioError::RpcCommandIsMissing, + .error = JS(missingCommand), + .message = "Method is not specified or is not a string.", + }, + { + .code = ClioError::RpcCommandNotString, + .error = "commandNotString", + .message = "Method is not a string.", + }, + { + .code = ClioError::RpcCommandIsEmpty, + .error = "emptyCommand", + .message = "Method is an empty string.", + }, + { + .code = ClioError::RpcParamsUnparsable, + .error = "paramsUnparsable", + .message = "Params must be an array holding exactly one object.", + }, + // errors from forwarding to an upstream rippled source + { + .code = ClioError::RpcForwardingConnectionError, + .error = "connectionError", + .message = "Couldn't connect to rippled.", + }, + { + .code = ClioError::RpcForwardingRequestError, + .error = "requestError", + .message = "Error sending request to rippled.", + }, + { + .code = ClioError::RpcForwardingTimeout, + .error = "timeout", + .message = "Request to rippled timed out.", + }, + { + .code = ClioError::RpcForwardingInvalidResponse, + .error = "invalidResponse", + .message = "Rippled returned an invalid response.", + }, + }); - auto matchByCode = [code](auto const& info) { return info.code == code; }; - if (auto it = ranges::find_if(kInfos, matchByCode); it != end(kInfos)) + if (auto it = ranges::find(kInfos, code, &ClioErrorInfo::code); it != end(kInfos)) return *it; throw(out_of_range("Invalid error code")); diff --git a/src/rpc/Errors.hpp b/src/rpc/Errors.hpp index 5b06c365a4..c5bb2e3f99 100644 --- a/src/rpc/Errors.hpp +++ b/src/rpc/Errors.hpp @@ -2,51 +2,15 @@ #pragma once #include -#include +#include -#include #include -#include #include -#include -#include namespace rpc { /** - * @brief Custom clio RPC Errors. - */ -enum class ClioError { - // normal clio errors start with 5000 - RpcMalformedCurrency = 5000, - RpcMalformedRequest = 5001, - RpcMalformedOwner = 5002, - RpcMalformedAddress = 5003, - RpcUnknownOption = 5005, - RpcFieldNotFoundTransaction = 5006, - RpcMalformedOracleDocumentId = 5007, - RpcMalformedAuthorizedCredentials = 5008, - // NOTE: RpcEntryNotFound is replaced with RippledError::RpcEntryNotFound - // RpcEntryNotFound = 5009, - - // special system errors start with 6000 - RpcInvalidApiVersion = 6000, - RpcCommandIsMissing = 6001, - RpcCommandNotString = 6002, - RpcCommandIsEmpty = 6003, - RpcParamsUnparsable = 6004, - - // TODO: Since it is not only rpc errors here now, we should move it to util - // etl related errors start with 7000 - // Higher value in this errors means better progress in the forwarding - EtlConnectionError = 7000, - EtlRequestError = 7001, - EtlRequestTimeout = 7002, - EtlInvalidResponse = 7003, -}; - -/** - * @brief Holds info about a particular @ref ClioError. + * @brief Holds info about a particular ClioError. */ struct ClioErrorInfo { ClioError const code; @@ -55,238 +19,10 @@ struct ClioErrorInfo { }; /** - * @brief Clio uses compatible Rippled error codes for most RPC errors. - */ -using RippledError = xrpl::ErrorCodeI; - -/** - * @brief Clio operates on a combination of Rippled and Custom Clio error codes. - * - * @see RippledError For rippled error codes - * @see ClioError For custom clio error codes - */ -using CombinedError = std::variant; - -/** - * @brief A status returned from any RPC handler. - */ -struct Status { - CombinedError code = RippledError::RpcSuccess; - std::string error; - std::string message; - std::optional extraInfo; - - Status() = default; - - /** - * @brief Construct a new Status object - * - * @param code The error code - */ - /* implicit */ Status(CombinedError code) : code(code) {}; - - /** - * @brief Construct a new Status object - * - * @param code The error code - * @param extraInfo The extra info - */ - Status(CombinedError code, boost::json::object&& extraInfo) - : code(code), extraInfo(std::move(extraInfo)) {}; - - /** - * @brief Construct a new Status object with a custom message - * - * @note HACK. Some rippled handlers explicitly specify errors. This means that we have to be - * able to duplicate this functionality. - * - * @param message The message - */ - explicit Status(std::string message) : code(xrpl::RpcUnknown), message(std::move(message)) - { - } - - /** - * @brief Construct a new Status object - * - * @param code The error code - * @param message The message - */ - Status(CombinedError code, std::string message) : code(code), message(std::move(message)) - { - } - - /** - * @brief Construct a new Status object - * - * @param code The error code - * @param error The error - * @param message The message - */ - Status(CombinedError code, std::string error, std::string message) - : code(code), error(std::move(error)), message(std::move(message)) - { - } - - bool - operator==(Status const& other) const = default; - - /** - * @brief Check if the status is not OK - * - * @return true if the status is not OK; false otherwise - */ - operator bool() const - { - if (auto err = std::get_if(&code)) - return *err != RippledError::RpcSuccess; - - return true; - } - - /** - * @brief Returns true if the @ref rpc::Status contains the desired @ref rpc::RippledError - * - * @param other The @ref rpc::RippledError to match - * @return true if status matches given error; false otherwise - */ - bool - operator==(RippledError other) const - { - if (auto err = std::get_if(&code)) - return *err == other; - - return false; - } - - /** - * @brief Returns true if the Status contains the desired @ref ClioError - * - * @param other The RippledError to match - * @return true if status matches given error; false otherwise - */ - bool - operator==(ClioError other) const - { - if (auto err = std::get_if(&code)) - return *err == other; - - return false; - } - - /** - * @brief Custom output stream for Status - * - * @param stream The output stream - * @param status The Status - * @return The same ostream we were given - */ - friend std::ostream& - operator<<(std::ostream& stream, Status const& status); -}; - -/** - * @brief Warning codes that can be returned by clio. - */ -// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) -enum WarningCode { - WarnUnknown = -1, - WarnRpcClio = 2001, - WarnRpcOutdated = 2002, - WarnRpcRateLimit = 2003, - WarnRpcDeprecated = 2004 -}; - -/** - * @brief Holds information about a clio warning. - */ -struct WarningInfo { - constexpr WarningInfo() = default; - - /** - * @brief Construct a new Warning Info object - * - * @param code The warning code - * @param message The warning message - */ - constexpr WarningInfo(WarningCode code, char const* message) : code(code), message(message) - { - } - - WarningCode code = WarningCode::WarnUnknown; - std::string_view const message = "unknown warning"; -}; - -/** - * @brief Invalid parameters error. - */ -class InvalidParamsError : public std::exception { - std::string msg_; - -public: - /** - * @brief Construct a new Invalid Params Error object - * - * @param msg The error message - */ - explicit InvalidParamsError(std::string msg) : msg_(std::move(msg)) - { - } - - /** - * @brief Get the error message as a C string - * - * @return The error message - */ - [[nodiscard]] char const* - what() const noexcept override - { - return msg_.c_str(); - } -}; - -/** - * @brief Account not found error. - */ -class AccountNotFoundError : public std::exception { - std::string account_; - -public: - /** - * @brief Construct a new Account Not Found Error object - * - * @param acct The account - */ - explicit AccountNotFoundError(std::string acct) : account_(std::move(acct)) - { - } - - /** - * @brief Get the error message as a C string - * - * @return The error message - */ - [[nodiscard]] char const* - what() const noexcept override - { - return account_.c_str(); - } -}; - -/** - * @brief A globally available @ref rpc::Status that represents a successful state. + * @brief A globally available rpc::Status that represents a successful state. */ static Status gOk; -/** - * @brief Get the warning info object from a warning code. - * - * @param code The warning code - * @return A reference to the static warning info - */ -WarningInfo const& -getWarningInfo(WarningCode code); - /** * @brief Get the error info object from an clio-specific error code. * @@ -297,16 +33,7 @@ ClioErrorInfo const& getErrorInfo(ClioError code); /** - * @brief Generate JSON from a @ref rpc::WarningCode. - * - * @param code The warning code - * @return The JSON output - */ -boost::json::object -makeWarning(WarningCode code); - -/** - * @brief Generate JSON from a @ref rpc::Status. + * @brief Generate JSON from a rpc::Status. * * @param status The status object * @return The JSON output @@ -315,7 +42,7 @@ boost::json::object makeError(Status const& status); /** - * @brief Generate JSON from a @ref rpc::RippledError. + * @brief Generate JSON from a rpc::RippledError. * * @param err The rippled error * @param customError A custom error @@ -330,7 +57,7 @@ makeError( ); /** - * @brief Generate JSON from a @ref rpc::ClioError. + * @brief Generate JSON from a rpc::ClioError. * * @param err The clio's custom error * @param customError A custom error diff --git a/src/rpc/Factories.cpp b/src/rpc/Factories.cpp index 6badd01479..22c4aa9964 100644 --- a/src/rpc/Factories.cpp +++ b/src/rpc/Factories.cpp @@ -1,7 +1,6 @@ #include "rpc/Factories.hpp" #include "data/Types.hpp" -#include "rpc/Errors.hpp" #include "rpc/common/APIVersion.hpp" #include "rpc/common/Types.hpp" #include "util/Taggable.hpp" @@ -13,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/src/rpc/RPCHelpers.cpp b/src/rpc/RPCHelpers.cpp index c75e1fef3b..ab20a22ed6 100644 --- a/src/rpc/RPCHelpers.cpp +++ b/src/rpc/RPCHelpers.cpp @@ -4,7 +4,6 @@ #include "data/AmendmentCenterInterface.hpp" #include "data/BackendInterface.hpp" #include "data/Types.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/common/Types.hpp" #include "util/AccountUtils.hpp" @@ -27,6 +26,8 @@ #include #include #include +#include +#include #include #include #include @@ -547,6 +548,47 @@ getLedgerHeaderFromHashOrSeq( return *lgrInfo; } +std::expected +getLedgerHeaderFromLedgerSpecifier( + BackendInterface const& backend, + boost::asio::yield_context yield, + spec::LedgerSpecifier const& ledger, + uint32_t maxSeq +) +{ + auto const err = std::unexpected{Status{RippledError::RpcLgrNotFound, "ledgerNotFound"}}; + auto const resolved = ledger.resolved(); + + if (resolved.isHash()) { + auto const lgrInfo = + backend.fetchLedgerByHash(std::get(resolved.value), yield); + if (!lgrInfo || lgrInfo->seq > maxSeq) + return err; + + return *lgrInfo; + } + + if (resolved.isShortcut()) { + auto const shortcut = std::get(resolved.value); + ASSERT( + shortcut == spec::LedgerShortcut::Validated, + "current/closed ledgers must be forwarded before dispatch" + ); + } + + auto const ledgerSequence = resolved.isSequence() ? std::get(resolved.value) : maxSeq; + + // return without hitting the db + if (ledgerSequence > maxSeq) + return err; + + auto const lgrInfo = backend.fetchLedgerBySequence(ledgerSequence, yield); + if (!lgrInfo) + return err; + + return *lgrInfo; +} + std::vector ledgerHeaderToBlob(xrpl::LedgerHeader const& info, bool includeHash) { diff --git a/src/rpc/RPCHelpers.hpp b/src/rpc/RPCHelpers.hpp index e649809b92..823e45b1f6 100644 --- a/src/rpc/RPCHelpers.hpp +++ b/src/rpc/RPCHelpers.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,7 @@ #include #include #include +#include #include #include #include @@ -299,6 +301,35 @@ getLedgerHeaderFromHashOrSeq( uint32_t maxSeq ); +/** + * @brief Get ledger header from a spec-library ledger specifier. + * + * The strong-typed counterpart of @ref getLedgerHeaderFromHashOrSeq, for handlers whose + * spec produces a @c LedgerSpecifier instead of a ledger_hash / ledger_index pair. + * Behaviour matches that overload: a hash or sequence beyond @p maxSeq, or one absent from + * the backend, yields @c ledgerNotFound. + * + * A @c validated shortcut resolves to @p maxSeq, which is what it means for a server that + * only serves validated data. An unspecified ledger resolves via + * @c LedgerSpecifier::resolved(), which the spec library fixes to @c validated for Clio. + * + * @c current and @c closed cannot reach here: @ref specifiesCurrentOrClosedLedger forwards + * those upstream before dispatch. + * + * @param backend The backend to use + * @param yield The coroutine context + * @param ledger The ledger the request selected + * @param maxSeq The maximum sequence to search + * @return The ledger header or an error status + */ +std::expected +getLedgerHeaderFromLedgerSpecifier( + BackendInterface const& backend, + boost::asio::yield_context yield, + spec::LedgerSpecifier const& ledger, + uint32_t maxSeq +); + /** * @brief Traverse nodes owned by an account * diff --git a/src/rpc/common/AnyHandler.hpp b/src/rpc/common/AnyHandler.hpp index 3c305d0806..c818a2e2c4 100644 --- a/src/rpc/common/AnyHandler.hpp +++ b/src/rpc/common/AnyHandler.hpp @@ -60,7 +60,7 @@ class AnyHandler final { * * @param value The JSON to process * @param ctx Request context - * @return JSON result or @ref Status on error + * @return JSON result or Status on error */ [[nodiscard]] ReturnType process(boost::json::value const& value, Context const& ctx) const diff --git a/src/rpc/common/Concepts.hpp b/src/rpc/common/Concepts.hpp index c455b2ded6..bf289335c2 100644 --- a/src/rpc/common/Concepts.hpp +++ b/src/rpc/common/Concepts.hpp @@ -7,8 +7,12 @@ #include #include #include +#include +#include +#include #include +#include #include #include @@ -71,17 +75,46 @@ concept SomeHandlerWithInput = requires(T a, uint32_t version) { { a.spec(version) } -> std::same_as; } and SomeContextProcessWithInput and boost::json::has_value_to::value; +/** + * @brief Specifies what a Handler validated by the shared consteval spec must provide. + * + * Such a handler inherits @c rpc::spec::HandlerFor from the spec library, which + * supplies a static @c parseInput (validate and deserialise in one pass) and a static + * @c spec returning a type-erased @c RpcSpecView. Presence of @c parseInput is what + * selects this path over @c SomeHandlerWithInput. + * + * The two input paths are mutually exclusive by construction: a legacy handler returns + * @c RpcSpec @c const& from a non-static @c spec and needs a @c value_to for its Input, + * neither of which holds here. @c kIsSingleInputPath asserts that below. + */ +template +concept SomeHandlerWithTypedInput = requires(uint32_t version, boost::json::value jv) { + typename T::Input; + { T::parseInput(jv, version) } -> std::same_as>; + { T::spec(version) } -> std::same_as; +} and SomeContextProcessWithInput; + /** * @brief Specifies what a Handler without Input must provide. */ template concept SomeHandlerWithoutInput = SomeContextProcessWithoutInput; +/** + * @brief True when @p T does not straddle the legacy and typed input paths. + * + * Guards the @c if @c constexpr chain in @c DefaultProcessor - were a handler to satisfy + * both, the dispatch order alone would silently decide which spec ran. + */ +template +constexpr bool kIsSingleInputPath = not(SomeHandlerWithInput and SomeHandlerWithTypedInput); + /** * @brief Specifies what a Handler type must provide. */ template -concept SomeHandler = (SomeHandlerWithInput or SomeHandlerWithoutInput) and +concept SomeHandler = + (SomeHandlerWithInput or SomeHandlerWithTypedInput or SomeHandlerWithoutInput) and boost::json::has_value_from::value; } // namespace rpc diff --git a/src/rpc/common/MetaProcessors.cpp b/src/rpc/common/MetaProcessors.cpp index a5ab8cc7cf..6b6155d25c 100644 --- a/src/rpc/common/MetaProcessors.cpp +++ b/src/rpc/common/MetaProcessors.cpp @@ -1,9 +1,9 @@ #include "rpc/common/MetaProcessors.hpp" -#include "rpc/Errors.hpp" #include "rpc/common/Types.hpp" #include +#include #include diff --git a/src/rpc/common/Specs.cpp b/src/rpc/common/Specs.cpp index c837efeadf..ab0919265d 100644 --- a/src/rpc/common/Specs.cpp +++ b/src/rpc/common/Specs.cpp @@ -1,11 +1,11 @@ #include "rpc/common/Specs.hpp" -#include "rpc/Errors.hpp" #include "rpc/common/Checkers.hpp" #include "rpc/common/Types.hpp" #include #include +#include #include #include diff --git a/src/rpc/common/Specs.hpp b/src/rpc/common/Specs.hpp index f3996da37d..5a5e6eaa7a 100644 --- a/src/rpc/common/Specs.hpp +++ b/src/rpc/common/Specs.hpp @@ -54,7 +54,7 @@ struct FieldSpec final { * @brief Processes the passed JSON value using the stored processors. * * @param value The JSON value to validate and/or modify - * @return Nothing on success; @ref Status on error + * @return Nothing on success; Status on error */ [[nodiscard]] MaybeError process(boost::json::value& value) const; @@ -106,7 +106,7 @@ struct RpcSpec final { * @brief Processes the passed JSON value using the stored field specs. * * @param value The JSON value to validate and/or modify - * @return Nothing on success; @ref Status on error + * @return Nothing on success; Status on error */ [[nodiscard]] MaybeError process(boost::json::value& value) const; diff --git a/src/rpc/common/Validators.cpp b/src/rpc/common/Validators.cpp index 1cd86e8e6f..1a0f743743 100644 --- a/src/rpc/common/Validators.cpp +++ b/src/rpc/common/Validators.cpp @@ -1,6 +1,5 @@ #include "rpc/common/Validators.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -12,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/common/impl/Processors.hpp b/src/rpc/common/impl/Processors.hpp index 1242ded035..dc56157ec9 100644 --- a/src/rpc/common/impl/Processors.hpp +++ b/src/rpc/common/impl/Processors.hpp @@ -5,6 +5,9 @@ #include "util/UnsupportedType.hpp" #include +#include + +#include namespace rpc::impl { @@ -19,7 +22,30 @@ struct DefaultProcessor final { { using boost::json::value_from; using boost::json::value_to; - if constexpr (SomeHandlerWithInput) { + + static_assert( + kIsSingleInputPath, + "handler satisfies both the legacy and the typed input path; dispatch would be " + "decided by the order of the branches below rather than by the handler" + ); + + if constexpr (SomeHandlerWithTypedInput) { + // The shared consteval spec validates and deserializes in a single pass, so there + // is no separate process() step here: RpcSpecView::process() is a no-op for a + // TypedSpec. check() still runs separately because warnings are collected against + // the request as sent, and must be forwarded even when parsing then fails. + auto warnings = spec::toJsonArray(HandlerType::spec(ctx.apiVersion).check(value)); + + auto input = HandlerType::parseInput(value, ctx.apiVersion); + if (not input) + return ReturnType{Error{std::move(input).error()}, std::move(warnings)}; + + auto ret = handler.process(*input, ctx); + if (not ret) + return ReturnType{Error{std::move(ret).error()}, std::move(warnings)}; + + return ReturnType{value_from(std::move(ret).value()), std::move(warnings)}; + } else if constexpr (SomeHandlerWithInput) { // first we run validation against specified API version auto const spec = handler.spec(ctx.apiVersion); diff --git a/src/rpc/handlers/AMMInfo.cpp b/src/rpc/handlers/AMMInfo.cpp index 3eb8f30020..685c253c30 100644 --- a/src/rpc/handlers/AMMInfo.cpp +++ b/src/rpc/handlers/AMMInfo.cpp @@ -2,7 +2,6 @@ #include "data/DBHelpers.hpp" #include "rpc/AMMHelpers.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/MetaProcessors.hpp" @@ -18,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/AccountChannels.cpp b/src/rpc/handlers/AccountChannels.cpp index c977be2483..9a2ae4e092 100644 --- a/src/rpc/handlers/AccountChannels.cpp +++ b/src/rpc/handlers/AccountChannels.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/AccountChannels.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -11,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/AccountCurrencies.cpp b/src/rpc/handlers/AccountCurrencies.cpp index beceda7306..a72ebc3e91 100644 --- a/src/rpc/handlers/AccountCurrencies.cpp +++ b/src/rpc/handlers/AccountCurrencies.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/AccountCurrencies.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -10,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/AccountInfo.cpp b/src/rpc/handlers/AccountInfo.cpp index 83e3d67ae2..870e223937 100644 --- a/src/rpc/handlers/AccountInfo.cpp +++ b/src/rpc/handlers/AccountInfo.cpp @@ -1,7 +1,6 @@ #include "rpc/handlers/AccountInfo.hpp" #include "data/AmendmentCenter.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/JsonBool.hpp" @@ -14,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/AccountLines.cpp b/src/rpc/handlers/AccountLines.cpp index 8d0bacee66..982d2deefa 100644 --- a/src/rpc/handlers/AccountLines.cpp +++ b/src/rpc/handlers/AccountLines.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/AccountLines.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -11,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/AccountMPTokenIssuances.cpp b/src/rpc/handlers/AccountMPTokenIssuances.cpp index bd9309e30f..22f0c261fe 100644 --- a/src/rpc/handlers/AccountMPTokenIssuances.cpp +++ b/src/rpc/handlers/AccountMPTokenIssuances.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/AccountMPTokenIssuances.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -11,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/AccountMPTokens.cpp b/src/rpc/handlers/AccountMPTokens.cpp index f5381cee2c..bede8e56c3 100644 --- a/src/rpc/handlers/AccountMPTokens.cpp +++ b/src/rpc/handlers/AccountMPTokens.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/AccountMPTokens.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -12,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/AccountNFTs.cpp b/src/rpc/handlers/AccountNFTs.cpp index 96b7f95829..2e65fc6cb9 100644 --- a/src/rpc/handlers/AccountNFTs.cpp +++ b/src/rpc/handlers/AccountNFTs.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/AccountNFTs.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -10,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/AccountObjects.cpp b/src/rpc/handlers/AccountObjects.cpp index 183ebd4e12..cdde3372b4 100644 --- a/src/rpc/handlers/AccountObjects.cpp +++ b/src/rpc/handlers/AccountObjects.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/AccountObjects.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -12,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/AccountOffers.cpp b/src/rpc/handlers/AccountOffers.cpp index 09abb815d1..c733ead9f8 100644 --- a/src/rpc/handlers/AccountOffers.cpp +++ b/src/rpc/handlers/AccountOffers.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/AccountOffers.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -11,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/AccountTx.cpp b/src/rpc/handlers/AccountTx.cpp index 70464c34f2..afacae3922 100644 --- a/src/rpc/handlers/AccountTx.cpp +++ b/src/rpc/handlers/AccountTx.cpp @@ -1,7 +1,6 @@ #include "rpc/handlers/AccountTx.hpp" #include "data/Types.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/JsonBool.hpp" @@ -19,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/DepositAuthorized.cpp b/src/rpc/handlers/DepositAuthorized.cpp index e07fb503da..5ba93019d2 100644 --- a/src/rpc/handlers/DepositAuthorized.cpp +++ b/src/rpc/handlers/DepositAuthorized.cpp @@ -1,7 +1,6 @@ #include "rpc/handlers/DepositAuthorized.hpp" #include "rpc/CredentialHelpers.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -13,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/Feature.cpp b/src/rpc/handlers/Feature.cpp index f9821e5e92..a0b69c490f 100644 --- a/src/rpc/handlers/Feature.cpp +++ b/src/rpc/handlers/Feature.cpp @@ -1,7 +1,6 @@ #include "rpc/handlers/Feature.hpp" #include "data/Types.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/MetaProcessors.hpp" @@ -14,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/GatewayBalances.cpp b/src/rpc/handlers/GatewayBalances.cpp index e9585d2bea..294bdd74c9 100644 --- a/src/rpc/handlers/GatewayBalances.cpp +++ b/src/rpc/handlers/GatewayBalances.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/GatewayBalances.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -12,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/GetAggregatePrice.cpp b/src/rpc/handlers/GetAggregatePrice.cpp index 0b8cc519ae..4d464a1f82 100644 --- a/src/rpc/handlers/GetAggregatePrice.cpp +++ b/src/rpc/handlers/GetAggregatePrice.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/GetAggregatePrice.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" diff --git a/src/rpc/handlers/LedgerData.cpp b/src/rpc/handlers/LedgerData.cpp index 9a05caa40e..74724980c2 100644 --- a/src/rpc/handlers/LedgerData.cpp +++ b/src/rpc/handlers/LedgerData.cpp @@ -1,7 +1,6 @@ #include "rpc/handlers/LedgerData.hpp" #include "data/Types.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -14,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/LedgerEntry.cpp b/src/rpc/handlers/LedgerEntry.cpp index bef9c8666b..e577d5c0fe 100644 --- a/src/rpc/handlers/LedgerEntry.cpp +++ b/src/rpc/handlers/LedgerEntry.cpp @@ -1,7 +1,6 @@ #include "rpc/handlers/LedgerEntry.hpp" #include "rpc/CredentialHelpers.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -13,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/LedgerIndex.cpp b/src/rpc/handlers/LedgerIndex.cpp index 0315b242b7..350f96b095 100644 --- a/src/rpc/handlers/LedgerIndex.cpp +++ b/src/rpc/handlers/LedgerIndex.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/LedgerIndex.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/common/Types.hpp" #include "util/Assert.hpp" @@ -9,6 +8,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/MPTHolders.cpp b/src/rpc/handlers/MPTHolders.cpp index 247f7a8154..e84181ccd7 100644 --- a/src/rpc/handlers/MPTHolders.cpp +++ b/src/rpc/handlers/MPTHolders.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/MPTHolders.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -11,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/MPTokenIssuanceHistory.cpp b/src/rpc/handlers/MPTokenIssuanceHistory.cpp index 813528ed3f..b03d6b78cf 100644 --- a/src/rpc/handlers/MPTokenIssuanceHistory.cpp +++ b/src/rpc/handlers/MPTokenIssuanceHistory.cpp @@ -1,7 +1,6 @@ #include "rpc/handlers/MPTokenIssuanceHistory.hpp" #include "data/Types.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -15,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/NFTHistory.cpp b/src/rpc/handlers/NFTHistory.cpp index 51a1f17914..f2229f7210 100644 --- a/src/rpc/handlers/NFTHistory.cpp +++ b/src/rpc/handlers/NFTHistory.cpp @@ -1,7 +1,6 @@ #include "rpc/handlers/NFTHistory.hpp" #include "data/Types.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -15,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/NFTInfo.cpp b/src/rpc/handlers/NFTInfo.cpp index acaad62b94..6506d78ce8 100644 --- a/src/rpc/handlers/NFTInfo.cpp +++ b/src/rpc/handlers/NFTInfo.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/NFTInfo.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -11,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/NFTOffersCommon.cpp b/src/rpc/handlers/NFTOffersCommon.cpp index ce4efe203e..b3797b14e0 100644 --- a/src/rpc/handlers/NFTOffersCommon.cpp +++ b/src/rpc/handlers/NFTOffersCommon.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/NFTOffersCommon.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -12,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/NFTsByIssuer.cpp b/src/rpc/handlers/NFTsByIssuer.cpp index 471b50d9a4..ca5a2eed8d 100644 --- a/src/rpc/handlers/NFTsByIssuer.cpp +++ b/src/rpc/handlers/NFTsByIssuer.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/NFTsByIssuer.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -11,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/NoRippleCheck.cpp b/src/rpc/handlers/NoRippleCheck.cpp index 10d3dd091b..2a5ff16018 100644 --- a/src/rpc/handlers/NoRippleCheck.cpp +++ b/src/rpc/handlers/NoRippleCheck.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/NoRippleCheck.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/JsonBool.hpp" @@ -14,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/Subscribe.cpp b/src/rpc/handlers/Subscribe.cpp index 0782154571..d0f2a40740 100644 --- a/src/rpc/handlers/Subscribe.cpp +++ b/src/rpc/handlers/Subscribe.cpp @@ -5,7 +5,6 @@ #include "data/Types.hpp" #include "feed/SubscriptionManagerInterface.hpp" #include "feed/Types.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Checkers.hpp" @@ -21,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/TransactionEntry.cpp b/src/rpc/handlers/TransactionEntry.cpp index 9d21467568..083023bfb6 100644 --- a/src/rpc/handlers/TransactionEntry.cpp +++ b/src/rpc/handlers/TransactionEntry.cpp @@ -1,6 +1,5 @@ #include "rpc/handlers/TransactionEntry.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -10,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc/handlers/Unsubscribe.cpp b/src/rpc/handlers/Unsubscribe.cpp index d0923cd4ee..f7c521adf3 100644 --- a/src/rpc/handlers/Unsubscribe.cpp +++ b/src/rpc/handlers/Unsubscribe.cpp @@ -2,7 +2,6 @@ #include "feed/SubscriptionManagerInterface.hpp" #include "feed/Types.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Checkers.hpp" @@ -14,6 +13,7 @@ #include #include #include +#include #include #include diff --git a/src/rpc/handlers/VaultInfo.cpp b/src/rpc/handlers/VaultInfo.cpp index 967f19a038..7fee9f2ae4 100644 --- a/src/rpc/handlers/VaultInfo.cpp +++ b/src/rpc/handlers/VaultInfo.cpp @@ -1,7 +1,6 @@ #include "rpc/handlers/VaultInfo.hpp" #include "data/BackendInterface.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -11,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/src/web/CMakeLists.txt b/src/web/CMakeLists.txt index facc5e258f..2a0db40a6e 100644 --- a/src/web/CMakeLists.txt +++ b/src/web/CMakeLists.txt @@ -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) diff --git a/src/web/impl/ErrorHandling.hpp b/src/web/impl/ErrorHandling.hpp index 2f631b4485..f14e6131c2 100644 --- a/src/web/impl/ErrorHandling.hpp +++ b/src/web/impl/ErrorHandling.hpp @@ -80,10 +80,10 @@ class ErrorHelper { case rpc::ClioError::RpcFieldNotFoundTransaction: case rpc::ClioError::RpcMalformedOracleDocumentId: case rpc::ClioError::RpcMalformedAuthorizedCredentials: - case rpc::ClioError::EtlConnectionError: - case rpc::ClioError::EtlRequestError: - case rpc::ClioError::EtlRequestTimeout: - case rpc::ClioError::EtlInvalidResponse: + case rpc::ClioError::RpcForwardingConnectionError: + case rpc::ClioError::RpcForwardingRequestError: + case rpc::ClioError::RpcForwardingTimeout: + case rpc::ClioError::RpcForwardingInvalidResponse: ASSERT( false, "Unknown rpc error code {}", static_cast(*clioCode) ); // this should never happen diff --git a/src/web/ng/impl/ErrorHandling.cpp b/src/web/ng/impl/ErrorHandling.cpp index c86ce259a1..a1b5bebe36 100644 --- a/src/web/ng/impl/ErrorHandling.cpp +++ b/src/web/ng/impl/ErrorHandling.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -92,10 +93,10 @@ ErrorHelper::makeError(rpc::Status const& err) const case rpc::ClioError::RpcFieldNotFoundTransaction: case rpc::ClioError::RpcMalformedOracleDocumentId: case rpc::ClioError::RpcMalformedAuthorizedCredentials: - case rpc::ClioError::EtlConnectionError: - case rpc::ClioError::EtlRequestError: - case rpc::ClioError::EtlRequestTimeout: - case rpc::ClioError::EtlInvalidResponse: + case rpc::ClioError::RpcForwardingConnectionError: + case rpc::ClioError::RpcForwardingRequestError: + case rpc::ClioError::RpcForwardingTimeout: + case rpc::ClioError::RpcForwardingInvalidResponse: ASSERT( false, "Unknown rpc error code {}", static_cast(*clioCode) ); // this should never happen diff --git a/tests/common/CMakeLists.txt b/tests/common/CMakeLists.txt index 51b41e6a6b..6c093112a8 100644 --- a/tests/common/CMakeLists.txt +++ b/tests/common/CMakeLists.txt @@ -3,6 +3,7 @@ add_library(clio_testing_common) target_sources( clio_testing_common PRIVATE + rpc/FakesAndMocks.cpp util/AssignRandomPort.cpp util/BinaryTestObject.cpp util/CallWithTimeout.cpp diff --git a/tests/common/rpc/FakesAndMocks.cpp b/tests/common/rpc/FakesAndMocks.cpp new file mode 100644 index 0000000000..62a8245274 --- /dev/null +++ b/tests/common/rpc/FakesAndMocks.cpp @@ -0,0 +1,6 @@ +#include "rpc/FakesAndMocks.hpp" + +#include +#include // IWYU pragma: keep + +template struct rpc::spec::HandlerFor; diff --git a/tests/common/rpc/FakesAndMocks.hpp b/tests/common/rpc/FakesAndMocks.hpp index e277b68d60..56d66fa025 100644 --- a/tests/common/rpc/FakesAndMocks.hpp +++ b/tests/common/rpc/FakesAndMocks.hpp @@ -10,6 +10,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -153,4 +160,58 @@ struct HandlerWithoutInputMock { MOCK_METHOD(Result, process, (rpc::Context const&), (const)); }; +// The shared consteval spec resolves a handler's spec from its Input type via an ADL +// `specFor` hook, so the fake Input below needs its own namespace to host that hook. +namespace typed_fake { + +// input data for TypedHandlerFake; mirrors TestInput so the two paths stay comparable +struct TypedInput { + std::string hello; + std::optional limit; +}; + +inline constexpr auto kInputSpec = rpc::spec::spec( + rpc::spec::field("hello", &TypedInput::hello, rpc::spec::required, rpc::spec::asString), + rpc::spec::field("limit", &TypedInput::limit, rpc::spec::asUint32), + rpc::spec::field("old_field", rpc::spec::deprecated) +); + +inline constexpr auto kSpec = rpc::spec::versioned(kInputSpec); + +/** @brief ADL hook: resolve the versioned spec from the Input type. */ +[[nodiscard]] constexpr auto const& +specFor(TypedInput const*) noexcept +{ + return kSpec; +} + +} // namespace typed_fake + +// example handler validated by the shared consteval spec rather than by rpc::RpcSpec. +// Note it declares no spec() and no Input of its own: both come from HandlerFor, and there +// is no tag_invoke for TypedInput, which is what keeps it off the legacy path. +class TypedHandlerFake : public rpc::spec::HandlerFor { +public: + using Output = TestOutput; + using Result = rpc::HandlerReturnType; + + static Result + process(Input const& input, [[maybe_unused]] rpc::Context const& ctx) + { + return Output{input.hello + '_' + std::to_string(input.limit.value_or(0))}; + } +}; + +class FailingTypedHandlerFake : public rpc::spec::HandlerFor { +public: + using Output = TestOutput; + using Result = rpc::HandlerReturnType; + + static Result + process([[maybe_unused]] Input const& input, [[maybe_unused]] rpc::Context const& ctx) + { + return rpc::Error{rpc::Status{"Very custom error"}}; + } +}; + } // namespace tests::common diff --git a/tests/common/util/MockSource.hpp b/tests/common/util/MockSource.hpp index fbc4c4bbc1..31db732dad 100644 --- a/tests/common/util/MockSource.hpp +++ b/tests/common/util/MockSource.hpp @@ -5,7 +5,6 @@ #include "etl/NetworkValidatedLedgersInterface.hpp" #include "etl/Source.hpp" #include "feed/SubscriptionManagerInterface.hpp" -#include "rpc/Errors.hpp" #include "util/config/ObjectView.hpp" #include @@ -16,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/tests/unit/app/WebHandlersTests.cpp b/tests/unit/app/WebHandlersTests.cpp index cf86f38f42..5377ef4795 100644 --- a/tests/unit/app/WebHandlersTests.cpp +++ b/tests/unit/app/WebHandlersTests.cpp @@ -1,5 +1,4 @@ #include "app/WebHandlers.hpp" -#include "rpc/Errors.hpp" #include "rpc/WorkQueue.hpp" #include "util/AsioContextTestFixture.hpp" #include "util/MockLedgerCache.hpp" @@ -24,6 +23,7 @@ #include #include #include +#include #include #include diff --git a/tests/unit/etl/ETLStateTests.cpp b/tests/unit/etl/ETLStateTests.cpp index 3db5702f63..09dbf259f9 100644 --- a/tests/unit/etl/ETLStateTests.cpp +++ b/tests/unit/etl/ETLStateTests.cpp @@ -1,10 +1,10 @@ #include "etl/ETLState.hpp" -#include "rpc/Errors.hpp" #include "util/MockSource.hpp" #include #include #include +#include #include @@ -18,7 +18,7 @@ struct ETLStateTest : public virtual ::testing::Test { TEST_F(ETLStateTest, Error) { EXPECT_CALL(source, forwardToRippled) - .WillOnce(Return(std::unexpected{rpc::ClioError::EtlInvalidResponse})); + .WillOnce(Return(std::unexpected{rpc::ClioError::RpcForwardingInvalidResponse})); auto const state = etl::ETLState::fetchETLStateFromSource(source); EXPECT_FALSE(state); } diff --git a/tests/unit/etl/ForwardingSourceTests.cpp b/tests/unit/etl/ForwardingSourceTests.cpp index f85d7ba930..e7f9e08226 100644 --- a/tests/unit/etl/ForwardingSourceTests.cpp +++ b/tests/unit/etl/ForwardingSourceTests.cpp @@ -1,5 +1,4 @@ #include "etl/impl/ForwardingSource.hpp" -#include "rpc/Errors.hpp" #include "util/AsioContextTestFixture.hpp" #include "util/Spawn.hpp" #include "util/TestWsServer.hpp" @@ -9,6 +8,7 @@ #include #include #include +#include #include #include @@ -35,7 +35,7 @@ TEST_F(ForwardingSourceTests, ConnectionFailed) runSpawn([&](boost::asio::yield_context yield) { auto result = forwardingSource_.forwardToRippled({}, {}, {}, yield); ASSERT_FALSE(result); - EXPECT_EQ(result.error(), rpc::ClioError::EtlConnectionError); + EXPECT_EQ(result.error(), rpc::ClioError::RpcForwardingConnectionError); }); } @@ -79,7 +79,7 @@ TEST_F(ForwardingSourceOperationsTests, XUserHeader) boost::json::parse(message_).as_object(), {}, xUserValue, yield ); ASSERT_FALSE(result); - EXPECT_EQ(result.error(), rpc::ClioError::EtlRequestError); + EXPECT_EQ(result.error(), rpc::ClioError::RpcForwardingRequestError); }); } @@ -95,7 +95,7 @@ TEST_F(ForwardingSourceOperationsTests, ReadFailed) boost::json::parse(message_).as_object(), {}, {}, yield ); ASSERT_FALSE(result); - EXPECT_EQ(result.error(), rpc::ClioError::EtlRequestError); + EXPECT_EQ(result.error(), rpc::ClioError::RpcForwardingRequestError); }); } @@ -111,7 +111,7 @@ TEST_F(ForwardingSourceOperationsTests, ReadTimeout) boost::json::parse(message_).as_object(), {}, {}, yield ); ASSERT_FALSE(result); - EXPECT_EQ(result.error(), rpc::ClioError::EtlRequestTimeout); + EXPECT_EQ(result.error(), rpc::ClioError::RpcForwardingTimeout); }); } @@ -136,7 +136,7 @@ TEST_F(ForwardingSourceOperationsTests, ParseFailed) boost::json::parse(message_).as_object(), {}, {}, yield ); ASSERT_FALSE(result); - EXPECT_EQ(result.error(), rpc::ClioError::EtlInvalidResponse); + EXPECT_EQ(result.error(), rpc::ClioError::RpcForwardingInvalidResponse); }); } @@ -162,7 +162,7 @@ TEST_F(ForwardingSourceOperationsTests, GotNotAnObject) boost::json::parse(message_).as_object(), {}, {}, yield ); ASSERT_FALSE(result); - EXPECT_EQ(result.error(), rpc::ClioError::EtlInvalidResponse); + EXPECT_EQ(result.error(), rpc::ClioError::RpcForwardingInvalidResponse); }); } diff --git a/tests/unit/etl/LoadBalancerTests.cpp b/tests/unit/etl/LoadBalancerTests.cpp index 243c276164..35da1f7131 100644 --- a/tests/unit/etl/LoadBalancerTests.cpp +++ b/tests/unit/etl/LoadBalancerTests.cpp @@ -3,7 +3,6 @@ #include "etl/LoadBalancerInterface.hpp" #include "etl/Models.hpp" #include "etl/Source.hpp" -#include "rpc/Errors.hpp" #include "util/AsioContextTestFixture.hpp" #include "util/MockBackendTestFixture.hpp" #include "util/MockNetworkValidatedLedgers.hpp" @@ -30,6 +29,7 @@ #include #include #include +#include #include #include @@ -203,9 +203,9 @@ TEST_F(LoadBalancerConstructorTests, fetchETLState_AllSourcesFail) { EXPECT_CALL(sourceFactory_, makeSource).Times(2); EXPECT_CALL(sourceFactory_.sourceAt(0), forwardToRippled) - .WillOnce(Return(std::unexpected{rpc::ClioError::EtlConnectionError})); + .WillOnce(Return(std::unexpected{rpc::ClioError::RpcForwardingConnectionError})); EXPECT_CALL(sourceFactory_.sourceAt(1), forwardToRippled) - .WillOnce(Return(std::unexpected{rpc::ClioError::EtlConnectionError})); + .WillOnce(Return(std::unexpected{rpc::ClioError::RpcForwardingConnectionError})); EXPECT_THROW({ makeLoadBalancer(); }, std::logic_error); } @@ -225,7 +225,7 @@ TEST_F(LoadBalancerConstructorTests, fetchETLState_Source1Fails0OK) EXPECT_CALL(sourceFactory_.sourceAt(0), forwardToRippled) .WillOnce(Return(boost::json::object{})); EXPECT_CALL(sourceFactory_.sourceAt(1), forwardToRippled) - .WillOnce(Return(std::unexpected{rpc::ClioError::EtlConnectionError})); + .WillOnce(Return(std::unexpected{rpc::ClioError::RpcForwardingConnectionError})); EXPECT_CALL(sourceFactory_.sourceAt(0), run); EXPECT_CALL(sourceFactory_.sourceAt(1), run); makeLoadBalancer(); @@ -235,7 +235,7 @@ TEST_F(LoadBalancerConstructorTests, fetchETLState_Source0Fails1OK) { EXPECT_CALL(sourceFactory_, makeSource).Times(2); EXPECT_CALL(sourceFactory_.sourceAt(0), forwardToRippled) - .WillOnce(Return(std::unexpected{rpc::ClioError::EtlConnectionError})); + .WillOnce(Return(std::unexpected{rpc::ClioError::RpcForwardingConnectionError})); EXPECT_CALL(sourceFactory_.sourceAt(1), forwardToRippled) .WillOnce(Return(boost::json::object{})); EXPECT_CALL(sourceFactory_.sourceAt(0), run); @@ -265,7 +265,7 @@ TEST_F(LoadBalancerConstructorTests, fetchETLState_AllSourcesFailButAllowNoEtlIs .WillOnce(Return(boost::json::object{})); EXPECT_CALL(sourceFactory_.sourceAt(0), run); EXPECT_CALL(sourceFactory_.sourceAt(1), forwardToRippled) - .WillOnce(Return(std::unexpected{rpc::ClioError::EtlConnectionError})); + .WillOnce(Return(std::unexpected{rpc::ClioError::RpcForwardingConnectionError})); EXPECT_CALL(sourceFactory_.sourceAt(1), run); configJson_.as_object()["allow_no_etl"] = true; @@ -716,7 +716,7 @@ TEST_F(LoadBalancerForwardToRippledTests, source0Fails) sourceFactory_.sourceAt(0), forwardToRippled(request_, clientIP_, LoadBalancer::kUserForwardingXUserValue, testing::_) ) - .WillOnce(Return(std::unexpected{rpc::ClioError::EtlConnectionError})); + .WillOnce(Return(std::unexpected{rpc::ClioError::RpcForwardingConnectionError})); EXPECT_CALL( sourceFactory_.sourceAt(1), forwardToRippled(request_, clientIP_, LoadBalancer::kUserForwardingXUserValue, testing::_) @@ -819,7 +819,7 @@ TEST_F(LoadBalancerForwardToRippledPrometheusTests, source0Fails) sourceFactory_.sourceAt(0), forwardToRippled(request_, clientIP_, LoadBalancer::kUserForwardingXUserValue, testing::_) ) - .WillOnce(Return(std::unexpected{rpc::ClioError::EtlConnectionError})); + .WillOnce(Return(std::unexpected{rpc::ClioError::RpcForwardingConnectionError})); EXPECT_CALL( sourceFactory_.sourceAt(1), forwardToRippled(request_, clientIP_, LoadBalancer::kUserForwardingXUserValue, testing::_) @@ -876,33 +876,33 @@ INSTANTIATE_TEST_SUITE_P( testing::Values( LoadBalancerForwardToRippledErrorTestBundle{ "ConnectionError_RequestError", - rpc::ClioError::EtlConnectionError, - rpc::ClioError::EtlRequestError, - rpc::ClioError::EtlRequestError + rpc::ClioError::RpcForwardingConnectionError, + rpc::ClioError::RpcForwardingRequestError, + rpc::ClioError::RpcForwardingRequestError }, LoadBalancerForwardToRippledErrorTestBundle{ "RequestError_RequestTimeout", - rpc::ClioError::EtlRequestError, - rpc::ClioError::EtlRequestTimeout, - rpc::ClioError::EtlRequestTimeout + rpc::ClioError::RpcForwardingRequestError, + rpc::ClioError::RpcForwardingTimeout, + rpc::ClioError::RpcForwardingTimeout }, LoadBalancerForwardToRippledErrorTestBundle{ "RequestTimeout_InvalidResponse", - rpc::ClioError::EtlRequestTimeout, - rpc::ClioError::EtlInvalidResponse, - rpc::ClioError::EtlInvalidResponse + rpc::ClioError::RpcForwardingTimeout, + rpc::ClioError::RpcForwardingInvalidResponse, + rpc::ClioError::RpcForwardingInvalidResponse }, LoadBalancerForwardToRippledErrorTestBundle{ "BothRequestTimeout", - rpc::ClioError::EtlRequestTimeout, - rpc::ClioError::EtlRequestTimeout, - rpc::ClioError::EtlRequestTimeout + rpc::ClioError::RpcForwardingTimeout, + rpc::ClioError::RpcForwardingTimeout, + rpc::ClioError::RpcForwardingTimeout }, LoadBalancerForwardToRippledErrorTestBundle{ "InvalidResponse_RequestError", - rpc::ClioError::EtlInvalidResponse, - rpc::ClioError::EtlRequestError, - rpc::ClioError::EtlInvalidResponse + rpc::ClioError::RpcForwardingInvalidResponse, + rpc::ClioError::RpcForwardingRequestError, + rpc::ClioError::RpcForwardingInvalidResponse } ), tests::util::kNameGenerator diff --git a/tests/unit/etl/SourceImplTests.cpp b/tests/unit/etl/SourceImplTests.cpp index 999f01cbd2..31acac3f7d 100644 --- a/tests/unit/etl/SourceImplTests.cpp +++ b/tests/unit/etl/SourceImplTests.cpp @@ -2,7 +2,6 @@ #include "etl/LoadBalancerInterface.hpp" #include "etl/Models.hpp" #include "etl/impl/SourceImpl.hpp" -#include "rpc/Errors.hpp" #include "util/Spawn.hpp" #include @@ -13,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/tests/unit/rpc/BaseTests.cpp b/tests/unit/rpc/BaseTests.cpp index bbff83ed6f..70f49b0113 100644 --- a/tests/unit/rpc/BaseTests.cpp +++ b/tests/unit/rpc/BaseTests.cpp @@ -1,4 +1,3 @@ -#include "rpc/Errors.hpp" #include "rpc/common/MetaProcessors.hpp" #include "rpc/common/Modifiers.hpp" #include "rpc/common/Specs.hpp" @@ -13,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/tests/unit/rpc/ErrorTests.cpp b/tests/unit/rpc/ErrorTests.cpp index 2f78662545..5952069aa9 100644 --- a/tests/unit/rpc/ErrorTests.cpp +++ b/tests/unit/rpc/ErrorTests.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -150,7 +151,7 @@ INSTANTIATE_TEST_SUITE_P( "Clio", WarningCode::WarnRpcClio, "This is a clio server. clio only serves validated data. If you want to talk to " - "rippled, include " + "xrpld, include " "'ledger_index':'current' in your request" }, WarningCodeTestBundle{ @@ -234,7 +235,7 @@ INSTANTIATE_TEST_SUITE_P( }, StatusStreamTestBundle{ .testName = "StatusWithCodeAndExtraInfo", - .status = Status{ClioError::EtlConnectionError, boost::json::object{}}, + .status = Status{ClioError::RpcForwardingConnectionError, boost::json::object{}}, .expectedOutput = "Code: 7000, Message: Couldn't connect to rippled., Extra Info: {}" }, StatusStreamTestBundle{ @@ -256,7 +257,7 @@ INSTANTIATE_TEST_SUITE_P( .testName = "StatusWithCodeErrorMessage", .status = Status{ - ClioError::EtlInvalidResponse, + ClioError::RpcForwardingInvalidResponse, "invalidResponse", "Rippled returned an invalid response." }, diff --git a/tests/unit/rpc/ForwardingProxyTests.cpp b/tests/unit/rpc/ForwardingProxyTests.cpp index 396bfb5eb6..a8cac87ea9 100644 --- a/tests/unit/rpc/ForwardingProxyTests.cpp +++ b/tests/unit/rpc/ForwardingProxyTests.cpp @@ -1,5 +1,4 @@ #include "data/Types.hpp" -#include "rpc/Errors.hpp" #include "rpc/common/impl/ForwardingProxy.hpp" #include "util/HandlerBaseTestFixture.hpp" #include "util/MockCounters.hpp" @@ -16,6 +15,7 @@ #include #include #include +#include #include #include @@ -338,7 +338,7 @@ TEST_F(RPCForwardingProxyTest, ForwardingFailYieldsErrorStatus) *rawBalancerPtr, forwardToRippled(forwarded.as_object(), std::make_optional(kClientIp), true, _) ) - .WillOnce(Return(std::unexpected{rpc::ClioError::EtlInvalidResponse})); + .WillOnce(Return(std::unexpected{rpc::ClioError::RpcForwardingInvalidResponse})); EXPECT_CALL(*rawHandlerProviderPtr, contains(method)).WillOnce(Return(true)); @@ -360,6 +360,6 @@ TEST_F(RPCForwardingProxyTest, ForwardingFailYieldsErrorStatus) auto const res = proxy_.forward(ctx); EXPECT_FALSE(res.response.has_value()); - EXPECT_EQ(res.response.error(), rpc::ClioError::EtlInvalidResponse); + EXPECT_EQ(res.response.error(), rpc::ClioError::RpcForwardingInvalidResponse); }); } diff --git a/tests/unit/rpc/RPCEngineTests.cpp b/tests/unit/rpc/RPCEngineTests.cpp index 48820e48b6..840e05c5cb 100644 --- a/tests/unit/rpc/RPCEngineTests.cpp +++ b/tests/unit/rpc/RPCEngineTests.cpp @@ -1,6 +1,5 @@ #include "data/BackendInterface.hpp" #include "data/Types.hpp" -#include "rpc/Errors.hpp" #include "rpc/FakesAndMocks.hpp" #include "rpc/RPCEngine.hpp" #include "rpc/WorkQueue.hpp" @@ -29,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/tests/unit/rpc/RPCHelpersTests.cpp b/tests/unit/rpc/RPCHelpersTests.cpp index 651682eaf1..eb73aed083 100644 --- a/tests/unit/rpc/RPCHelpersTests.cpp +++ b/tests/unit/rpc/RPCHelpersTests.cpp @@ -7,6 +7,7 @@ #include "util/AsioContextTestFixture.hpp" #include "util/LoggerFixtures.hpp" #include "util/MockAmendmentCenter.hpp" +#include "util/MockAssert.hpp" #include "util/MockBackendTestFixture.hpp" #include "util/MockPrometheus.hpp" #include "util/NameGenerator.hpp" @@ -25,6 +26,8 @@ #include #include #include +#include +#include #include #include #include @@ -2057,3 +2060,156 @@ INSTANTIATE_TEST_SUITE_P( ), tests::util::kNameGenerator ); + +// getLedgerHeaderFromLedgerSpecifier — the strong-typed counterpart of +// getLedgerHeaderFromHashOrSeq. The fixture's range is [10, 300], so kRangeMax below is 300. + +namespace { +constexpr auto kSpecifierRangeMax = 300u; +} // namespace + +TEST_F(RPCHelpersTest, LedgerHeaderFromSpecifierByHash) +{ + auto const expected = createLedgerHeader(kIndex1, 30); + EXPECT_CALL(*backend_, fetchLedgerByHash(xrpl::uint256{kIndex1}, _)).WillOnce(Return(expected)); + + runSpawn([&, this](auto yield) { + auto const res = getLedgerHeaderFromLedgerSpecifier( + *backend_, yield, spec::LedgerSpecifier{xrpl::uint256{kIndex1}}, kSpecifierRangeMax + ); + ASSERT_TRUE(res.has_value()); + EXPECT_EQ(res->seq, 30); + }); +} + +TEST_F(RPCHelpersTest, LedgerHeaderFromSpecifierByHashNotFound) +{ + EXPECT_CALL(*backend_, fetchLedgerByHash(xrpl::uint256{kIndex1}, _)) + .WillOnce(Return(std::nullopt)); + + runSpawn([&, this](auto yield) { + auto const res = getLedgerHeaderFromLedgerSpecifier( + *backend_, yield, spec::LedgerSpecifier{xrpl::uint256{kIndex1}}, kSpecifierRangeMax + ); + ASSERT_FALSE(res.has_value()); + EXPECT_EQ(res.error().message, "ledgerNotFound"); + }); +} + +TEST_F(RPCHelpersTest, LedgerHeaderFromSpecifierByHashBeyondMaxSeq) +{ + // present in the backend, but newer than the range the caller may serve + EXPECT_CALL(*backend_, fetchLedgerByHash(xrpl::uint256{kIndex1}, _)) + .WillOnce(Return(createLedgerHeader(kIndex1, kSpecifierRangeMax + 1))); + + runSpawn([&, this](auto yield) { + auto const res = getLedgerHeaderFromLedgerSpecifier( + *backend_, yield, spec::LedgerSpecifier{xrpl::uint256{kIndex1}}, kSpecifierRangeMax + ); + ASSERT_FALSE(res.has_value()); + EXPECT_EQ(res.error().message, "ledgerNotFound"); + }); +} + +TEST_F(RPCHelpersTest, LedgerHeaderFromSpecifierBySequence) +{ + EXPECT_CALL(*backend_, fetchLedgerBySequence(30, _)) + .WillOnce(Return(createLedgerHeader(kIndex1, 30))); + + runSpawn([&, this](auto yield) { + auto const res = getLedgerHeaderFromLedgerSpecifier( + *backend_, yield, spec::LedgerSpecifier{uint32_t{30}}, kSpecifierRangeMax + ); + ASSERT_TRUE(res.has_value()); + EXPECT_EQ(res->seq, 30); + }); +} + +TEST_F(RPCHelpersTest, LedgerHeaderFromSpecifierBySequenceBeyondMaxSeqSkipsBackend) +{ + EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(0); + + runSpawn([&, this](auto yield) { + auto const res = getLedgerHeaderFromLedgerSpecifier( + *backend_, + yield, + spec::LedgerSpecifier{uint32_t{kSpecifierRangeMax + 1}}, + kSpecifierRangeMax + ); + ASSERT_FALSE(res.has_value()); + EXPECT_EQ(res.error().message, "ledgerNotFound"); + }); +} + +TEST_F(RPCHelpersTest, LedgerHeaderFromSpecifierValidatedUsesMaxSeq) +{ + EXPECT_CALL(*backend_, fetchLedgerBySequence(kSpecifierRangeMax, _)) + .WillOnce(Return(createLedgerHeader(kIndex1, kSpecifierRangeMax))); + + runSpawn([&, this](auto yield) { + auto const res = getLedgerHeaderFromLedgerSpecifier( + *backend_, + yield, + spec::LedgerSpecifier{spec::LedgerShortcut::Validated}, + kSpecifierRangeMax + ); + ASSERT_TRUE(res.has_value()); + EXPECT_EQ(res->seq, kSpecifierRangeMax); + }); +} + +struct RPCHelpersAssertTest : RPCHelpersTest, common::util::WithMockAssert {}; + +TEST_F(RPCHelpersAssertTest, LedgerHeaderFromSpecifierCurrentAsserts) +{ + EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(0); + + runSpawn([&, this](auto yield) { + EXPECT_CLIO_ASSERT_FAIL_WITH_MESSAGE( + { + [[maybe_unused]] auto const res = getLedgerHeaderFromLedgerSpecifier( + *backend_, + yield, + spec::LedgerSpecifier{spec::LedgerShortcut::Current}, + kSpecifierRangeMax + ); + }, + "must be forwarded before dispatch" + ); + }); +} + +TEST_F(RPCHelpersAssertTest, LedgerHeaderFromSpecifierClosedAsserts) +{ + EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(0); + + runSpawn([&, this](auto yield) { + EXPECT_CLIO_ASSERT_FAIL_WITH_MESSAGE( + { + [[maybe_unused]] auto const res = getLedgerHeaderFromLedgerSpecifier( + *backend_, + yield, + spec::LedgerSpecifier{spec::LedgerShortcut::Closed}, + kSpecifierRangeMax + ); + }, + "must be forwarded before dispatch" + ); + }); +} + +TEST_F(RPCHelpersTest, LedgerHeaderFromSpecifierUnspecifiedResolvesToMaxSeq) +{ + // an unspecified ledger resolves via LedgerSpecifier::resolved(), which the spec library + // fixes to `validated` under RPCSPEC_IS_CLIO + EXPECT_CALL(*backend_, fetchLedgerBySequence(kSpecifierRangeMax, _)) + .WillOnce(Return(createLedgerHeader(kIndex1, kSpecifierRangeMax))); + + runSpawn([&, this](auto yield) { + auto const res = getLedgerHeaderFromLedgerSpecifier( + *backend_, yield, spec::LedgerSpecifier{}, kSpecifierRangeMax + ); + ASSERT_TRUE(res.has_value()); + EXPECT_EQ(res->seq, kSpecifierRangeMax); + }); +} diff --git a/tests/unit/rpc/common/CheckersTests.cpp b/tests/unit/rpc/common/CheckersTests.cpp index 2c34322cba..a9c99d8999 100644 --- a/tests/unit/rpc/common/CheckersTests.cpp +++ b/tests/unit/rpc/common/CheckersTests.cpp @@ -1,8 +1,8 @@ -#include "rpc/Errors.hpp" #include "rpc/common/Checkers.hpp" #include #include +#include #include diff --git a/tests/unit/rpc/common/SpecsTests.cpp b/tests/unit/rpc/common/SpecsTests.cpp index 8e48db857c..620a22b7c1 100644 --- a/tests/unit/rpc/common/SpecsTests.cpp +++ b/tests/unit/rpc/common/SpecsTests.cpp @@ -1,4 +1,3 @@ -#include "rpc/Errors.hpp" #include "rpc/common/Checkers.hpp" #include "rpc/common/Specs.hpp" #include "rpc/common/Types.hpp" @@ -7,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/tests/unit/rpc/common/TypesTests.cpp b/tests/unit/rpc/common/TypesTests.cpp index 3ee5eb037a..eec172d5a2 100644 --- a/tests/unit/rpc/common/TypesTests.cpp +++ b/tests/unit/rpc/common/TypesTests.cpp @@ -1,4 +1,3 @@ -#include "rpc/Errors.hpp" #include "rpc/common/Types.hpp" #include diff --git a/tests/unit/rpc/handlers/AccountCurrenciesTests.cpp b/tests/unit/rpc/handlers/AccountCurrenciesTests.cpp index 8220331d50..909a752794 100644 --- a/tests/unit/rpc/handlers/AccountCurrenciesTests.cpp +++ b/tests/unit/rpc/handlers/AccountCurrenciesTests.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/unit/rpc/handlers/AccountInfoTests.cpp b/tests/unit/rpc/handlers/AccountInfoTests.cpp index 85a1b3c341..e28bceb968 100644 --- a/tests/unit/rpc/handlers/AccountInfoTests.cpp +++ b/tests/unit/rpc/handlers/AccountInfoTests.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/unit/rpc/handlers/AccountLinesTests.cpp b/tests/unit/rpc/handlers/AccountLinesTests.cpp index d473349cfa..e9f42ba931 100644 --- a/tests/unit/rpc/handlers/AccountLinesTests.cpp +++ b/tests/unit/rpc/handlers/AccountLinesTests.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/unit/rpc/handlers/AccountOffersTests.cpp b/tests/unit/rpc/handlers/AccountOffersTests.cpp index 447968bd8a..e9d70b1113 100644 --- a/tests/unit/rpc/handlers/AccountOffersTests.cpp +++ b/tests/unit/rpc/handlers/AccountOffersTests.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/unit/rpc/handlers/CredentialHelpersTests.cpp b/tests/unit/rpc/handlers/CredentialHelpersTests.cpp index 07cf381b08..b11935bb57 100644 --- a/tests/unit/rpc/handlers/CredentialHelpersTests.cpp +++ b/tests/unit/rpc/handlers/CredentialHelpersTests.cpp @@ -1,5 +1,4 @@ #include "rpc/CredentialHelpers.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "util/AsioContextTestFixture.hpp" #include "util/MockBackendTestFixture.hpp" @@ -12,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/unit/rpc/handlers/DefaultProcessorTests.cpp b/tests/unit/rpc/handlers/DefaultProcessorTests.cpp index cb4066ec07..22afa8cb6f 100644 --- a/tests/unit/rpc/handlers/DefaultProcessorTests.cpp +++ b/tests/unit/rpc/handlers/DefaultProcessorTests.cpp @@ -1,4 +1,6 @@ +#include "rpc/Errors.hpp" #include "rpc/FakesAndMocks.hpp" +#include "rpc/common/Concepts.hpp" #include "rpc/common/Specs.hpp" #include "rpc/common/Types.hpp" #include "rpc/common/Validators.hpp" @@ -67,3 +69,104 @@ TEST_F(RPCDefaultProcessorTest, InvalidInput) EXPECT_TRUE(ret.warnings.empty()); }); } + +// Pin which path each fake takes. Without this, a change that made a typed handler also +// satisfy SomeHandlerWithInput would silently reroute it through the legacy validators and +// every test below would still pass. +static_assert(SomeHandlerWithTypedInput); +static_assert(not SomeHandlerWithInput); +static_assert(SomeHandlerWithTypedInput); +static_assert(SomeHandlerWithInput); +static_assert(not SomeHandlerWithTypedInput); + +// The four tests below exercise the typed path — a handler whose spec, validation and +// deserialization all come from the shared consteval spec via HandlerFor. They run +// against the same DefaultProcessor as the legacy tests above, which is the point: the +// dual path is a dispatch detail, not a second processor. + +TEST_F(RPCDefaultProcessorTest, NewSpecHandler_HappyPath) +{ + runSpawn([](auto yield) { + TypedHandlerFake const handler; + rpc::impl::DefaultProcessor const processor; + + auto const input = boost::json::parse(R"JSON({ "hello": "world", "limit": 42 })JSON"); + + auto const ret = processor(handler, input, Context{yield}); + ASSERT_TRUE(ret); + EXPECT_TRUE(ret.warnings.empty()); + EXPECT_EQ(ret.result.value().at("computed").as_string(), "world_42"); + }); +} + +TEST_F(RPCDefaultProcessorTest, NewSpecHandler_MissingRequiredField_ReturnsError) +{ + runSpawn([](auto yield) { + TypedHandlerFake const handler; + rpc::impl::DefaultProcessor const processor; + + auto const input = boost::json::parse(R"JSON({ "limit": 42 })JSON"); + + auto const ret = processor(handler, input, Context{yield}); + ASSERT_FALSE(ret); + EXPECT_TRUE(ret.warnings.empty()); + }); +} + +TEST_F(RPCDefaultProcessorTest, NewSpecHandler_DeprecatedField_WarningsForwarded) +{ + runSpawn([](auto yield) { + TypedHandlerFake const handler; + rpc::impl::DefaultProcessor const processor; + + auto const input = boost::json::parse(R"JSON({ "hello": "world", "old_field": true })JSON"); + + auto const ret = processor(handler, input, Context{yield}); + ASSERT_TRUE(ret); + EXPECT_EQ(ret.warnings.size(), 1); + }); +} + +TEST_F(RPCDefaultProcessorTest, NewSpecHandler_HandlerReturnsError_ForwardsError) +{ + runSpawn([](auto yield) { + FailingTypedHandlerFake const handler; + rpc::impl::DefaultProcessor const processor; + + auto const input = boost::json::parse(R"JSON({ "hello": "world", "limit": 42 })JSON"); + auto const ret = processor(handler, input, Context{yield}); + + ASSERT_FALSE(ret); + EXPECT_EQ(rpc::makeError(ret.result.error()).at("error").as_string(), "Very custom error"); + EXPECT_TRUE(ret.warnings.empty()); + }); +} + +TEST_F(RPCDefaultProcessorTest, NewSpecHandler_HandlerReturnsError_StillForwardsWarnings) +{ + runSpawn([](auto yield) { + FailingTypedHandlerFake const handler; + rpc::impl::DefaultProcessor const processor; + + auto const input = boost::json::parse(R"JSON({ "hello": "world", "old_field": true })JSON"); + auto const ret = processor(handler, input, Context{yield}); + + ASSERT_FALSE(ret); + EXPECT_EQ(rpc::makeError(ret.result.error()).at("error").as_string(), "Very custom error"); + EXPECT_EQ(ret.warnings.size(), 1); + }); +} + +TEST_F(RPCDefaultProcessorTest, NewSpecHandler_DeprecatedFieldAbsent_NoWarnings) +{ + runSpawn([](auto yield) { + TypedHandlerFake const handler; + rpc::impl::DefaultProcessor const processor; + + auto const input = boost::json::parse(R"JSON({ "hello": "world" })JSON"); + + auto const ret = processor(handler, input, Context{yield}); + ASSERT_TRUE(ret); + EXPECT_TRUE(ret.warnings.empty()); + }); +} diff --git a/tests/unit/rpc/handlers/LedgerDataTests.cpp b/tests/unit/rpc/handlers/LedgerDataTests.cpp index 5e3255ee7d..3bf7ed676c 100644 --- a/tests/unit/rpc/handlers/LedgerDataTests.cpp +++ b/tests/unit/rpc/handlers/LedgerDataTests.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/unit/rpc/handlers/LedgerEntryTests.cpp b/tests/unit/rpc/handlers/LedgerEntryTests.cpp index 8f968fbce7..e9276e6c61 100644 --- a/tests/unit/rpc/handlers/LedgerEntryTests.cpp +++ b/tests/unit/rpc/handlers/LedgerEntryTests.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/unit/rpc/handlers/LedgerTests.cpp b/tests/unit/rpc/handlers/LedgerTests.cpp index 1b04f60afb..6ce57bb94a 100644 --- a/tests/unit/rpc/handlers/LedgerTests.cpp +++ b/tests/unit/rpc/handlers/LedgerTests.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/unit/rpc/handlers/ServerInfoTests.cpp b/tests/unit/rpc/handlers/ServerInfoTests.cpp index c1e7cbdfcb..84b7427e89 100644 --- a/tests/unit/rpc/handlers/ServerInfoTests.cpp +++ b/tests/unit/rpc/handlers/ServerInfoTests.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -177,7 +178,7 @@ TEST_F(RPCServerInfoHandlerTest, DefaultOutputIsPresent) EXPECT_CALL( *rawBalancerPtr, forwardToRippled(testing::_, testing::Eq(kClientIp), false, testing::_) ) - .WillOnce(Return(std::unexpected{rpc::ClioError::EtlInvalidResponse})); + .WillOnce(Return(std::unexpected{rpc::ClioError::RpcForwardingInvalidResponse})); EXPECT_CALL(*rawCountersPtr, uptime).WillOnce(Return(std::chrono::seconds{1234})); @@ -220,7 +221,7 @@ TEST_F(RPCServerInfoHandlerTest, AmendmentBlockedIsPresentIfSet) EXPECT_CALL( *rawBalancerPtr, forwardToRippled(testing::_, testing::Eq(kClientIp), false, testing::_) ) - .WillOnce(Return(std::unexpected{rpc::ClioError::EtlInvalidResponse})); + .WillOnce(Return(std::unexpected{rpc::ClioError::RpcForwardingInvalidResponse})); EXPECT_CALL(*rawCountersPtr, uptime).WillOnce(Return(std::chrono::seconds{1234})); @@ -261,7 +262,7 @@ TEST_F(RPCServerInfoHandlerTest, CorruptionDetectedIsPresentIfSet) EXPECT_CALL( *rawBalancerPtr, forwardToRippled(testing::_, testing::Eq(kClientIp), false, testing::_) ) - .WillOnce(Return(std::unexpected{rpc::ClioError::EtlInvalidResponse})); + .WillOnce(Return(std::unexpected{rpc::ClioError::RpcForwardingInvalidResponse})); EXPECT_CALL(*rawCountersPtr, uptime).WillOnce(Return(std::chrono::seconds{1234})); @@ -302,7 +303,7 @@ TEST_F(RPCServerInfoHandlerTest, CacheReportsEnabledFlagCorrectly) *rawBalancerPtr, forwardToRippled(testing::_, testing::Eq(kClientIp), false, testing::_) ) .Times(2) - .WillRepeatedly(Return(std::unexpected{rpc::ClioError::EtlInvalidResponse})); + .WillRepeatedly(Return(std::unexpected{rpc::ClioError::RpcForwardingInvalidResponse})); EXPECT_CALL(*rawCountersPtr, uptime) .Times(2) diff --git a/tests/unit/rpc/handlers/SubscribeTests.cpp b/tests/unit/rpc/handlers/SubscribeTests.cpp index 28e77e7290..83d8af7063 100644 --- a/tests/unit/rpc/handlers/SubscribeTests.cpp +++ b/tests/unit/rpc/handlers/SubscribeTests.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/unit/rpc/handlers/TestHandlerTests.cpp b/tests/unit/rpc/handlers/TestHandlerTests.cpp index c30c39f15b..84a07769fe 100644 --- a/tests/unit/rpc/handlers/TestHandlerTests.cpp +++ b/tests/unit/rpc/handlers/TestHandlerTests.cpp @@ -6,6 +6,7 @@ #include #include +#include using namespace std; using namespace rpc; diff --git a/tests/unit/rpc/handlers/TxTests.cpp b/tests/unit/rpc/handlers/TxTests.cpp index b8acacc69a..e7e4f63fcd 100644 --- a/tests/unit/rpc/handlers/TxTests.cpp +++ b/tests/unit/rpc/handlers/TxTests.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/tests/unit/rpc/handlers/UnsubscribeTests.cpp b/tests/unit/rpc/handlers/UnsubscribeTests.cpp index c515bf7753..04c23300e6 100644 --- a/tests/unit/rpc/handlers/UnsubscribeTests.cpp +++ b/tests/unit/rpc/handlers/UnsubscribeTests.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/tests/unit/rpc/handlers/VaultInfoTests.cpp b/tests/unit/rpc/handlers/VaultInfoTests.cpp index f1fc0afb01..73e9dc5804 100644 --- a/tests/unit/rpc/handlers/VaultInfoTests.cpp +++ b/tests/unit/rpc/handlers/VaultInfoTests.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/unit/web/LoadWarningTests.cpp b/tests/unit/web/LoadWarningTests.cpp index c4adaf34af..d174981bb1 100644 --- a/tests/unit/web/LoadWarningTests.cpp +++ b/tests/unit/web/LoadWarningTests.cpp @@ -1,7 +1,7 @@ -#include "rpc/Errors.hpp" #include "web/LoadWarning.hpp" #include +#include #include #include diff --git a/tests/unit/web/RPCServerHandlerTests.cpp b/tests/unit/web/RPCServerHandlerTests.cpp index 4b5fb0fddc..fb98796547 100644 --- a/tests/unit/web/RPCServerHandlerTests.cpp +++ b/tests/unit/web/RPCServerHandlerTests.cpp @@ -1,4 +1,3 @@ -#include "rpc/Errors.hpp" #include "rpc/common/APIVersion.hpp" #include "rpc/common/Types.hpp" #include "util/AsioContextTestFixture.hpp" @@ -21,6 +20,7 @@ #include #include #include +#include #include #include @@ -122,7 +122,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPDefaultPath) "warnings": [ { "id": 2001, - "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" + "message": "This is a clio server. clio only serves validated data. If you want to talk to xrpld, include 'ledger_index':'current' in your request" } ] })JSON"; @@ -190,7 +190,7 @@ TEST_F(WebRPCServerHandlerTest, WsNormalPath) "warnings": [ { "id": 2001, - "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" + "message": "This is a clio server. clio only serves validated data. If you want to talk to xrpld, include 'ledger_index':'current' in your request" } ] })JSON"; @@ -265,7 +265,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPForwardedPath) "warnings": [ { "id": 2001, - "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" + "message": "This is a clio server. clio only serves validated data. If you want to talk to xrpld, include 'ledger_index':'current' in your request" } ] })JSON"; @@ -314,7 +314,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPForwardedErrorPath) "warnings": [ { "id": 2001, - "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" + "message": "This is a clio server. clio only serves validated data. If you want to talk to xrpld, include 'ledger_index':'current' in your request" } ] })JSON"; @@ -361,7 +361,7 @@ TEST_F(WebRPCServerHandlerTest, WsForwardedPath) "warnings": [ { "id": 2001, - "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" + "message": "This is a clio server. clio only serves validated data. If you want to talk to xrpld, include 'ledger_index':'current' in your request" } ] })JSON"; @@ -412,7 +412,7 @@ TEST_F(WebRPCServerHandlerTest, WsForwardedErrorPath) "warnings": [ { "id": 2001, - "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" + "message": "This is a clio server. clio only serves validated data. If you want to talk to xrpld, include 'ledger_index':'current' in your request" } ] })JSON"; @@ -453,7 +453,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPErrorPath) "warnings": [ { "id": 2001, - "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" + "message": "This is a clio server. clio only serves validated data. If you want to talk to xrpld, include 'ledger_index':'current' in your request" } ] })JSON"; @@ -510,7 +510,7 @@ TEST_F(WebRPCServerHandlerTest, WsErrorPath) "warnings": [ { "id": 2001, - "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" + "message": "This is a clio server. clio only serves validated data. If you want to talk to xrpld, include 'ledger_index':'current' in your request" } ] })JSON"; @@ -860,7 +860,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPOutDated) "warnings": [ { "id": 2001, - "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" + "message": "This is a clio server. clio only serves validated data. If you want to talk to xrpld, include 'ledger_index':'current' in your request" }, { "id": 2002, @@ -903,7 +903,7 @@ TEST_F(WebRPCServerHandlerTest, WsOutdated) "warnings": [ { "id": 2001, - "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" + "message": "This is a clio server. clio only serves validated data. If you want to talk to xrpld, include 'ledger_index':'current' in your request" }, { "id": 2002, diff --git a/tests/unit/web/impl/ErrorHandlingTests.cpp b/tests/unit/web/impl/ErrorHandlingTests.cpp index 6b36c4ceef..a89d6f12db 100644 --- a/tests/unit/web/impl/ErrorHandlingTests.cpp +++ b/tests/unit/web/impl/ErrorHandlingTests.cpp @@ -1,4 +1,3 @@ -#include "rpc/Errors.hpp" #include "util/NameGenerator.hpp" #include "util/Taggable.hpp" #include "util/config/ConfigDefinition.hpp" @@ -12,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/tests/unit/web/ng/RPCServerHandlerTests.cpp b/tests/unit/web/ng/RPCServerHandlerTests.cpp index 2dab021d4d..cda529f6ef 100644 --- a/tests/unit/web/ng/RPCServerHandlerTests.cpp +++ b/tests/unit/web/ng/RPCServerHandlerTests.cpp @@ -1,4 +1,3 @@ -#include "rpc/Errors.hpp" #include "rpc/common/Types.hpp" #include "util/AsioContextTestFixture.hpp" #include "util/MockBackendTestFixture.hpp" @@ -25,6 +24,7 @@ #include #include #include +#include #include #include diff --git a/tests/unit/web/ng/impl/ErrorHandlingTests.cpp b/tests/unit/web/ng/impl/ErrorHandlingTests.cpp index f40721c359..2e16147e1d 100644 --- a/tests/unit/web/ng/impl/ErrorHandlingTests.cpp +++ b/tests/unit/web/ng/impl/ErrorHandlingTests.cpp @@ -1,4 +1,3 @@ -#include "rpc/Errors.hpp" #include "util/NameGenerator.hpp" #include "web/ng/Request.hpp" #include "web/ng/impl/ErrorHandling.hpp" @@ -11,6 +10,7 @@ #include #include #include +#include #include #include