From 0a5bf911c1a38fc0d5154c1397c2b768d8e7c770 Mon Sep 17 00:00:00 2001 From: Alex Kremer Date: Tue, 15 Nov 2022 18:08:09 +0100 Subject: [PATCH] Add error code extension mechanism and use malformed currency code (#396) Fixes #275 --- .githooks/pre-commit | 2 +- CMakeLists.txt | 4 +- src/rpc/Errors.cpp | 135 ++++++++++++++++ src/rpc/Errors.h | 206 +++++++++++++++++++++++++ src/rpc/RPC.cpp | 188 ++++++++-------------- src/rpc/RPC.h | 103 +------------ src/rpc/RPCHelpers.cpp | 152 ++++++++++-------- src/rpc/handlers/AccountChannels.cpp | 4 +- src/rpc/handlers/AccountCurrencies.cpp | 2 +- src/rpc/handlers/AccountInfo.cpp | 10 +- src/rpc/handlers/AccountLines.cpp | 7 +- src/rpc/handlers/AccountObjects.cpp | 10 +- src/rpc/handlers/AccountOffers.cpp | 4 +- src/rpc/handlers/BookOffers.cpp | 4 +- src/rpc/handlers/ChannelAuthorize.cpp | 12 +- src/rpc/handlers/ChannelVerify.cpp | 23 +-- src/rpc/handlers/GatewayBalances.cpp | 2 +- src/rpc/handlers/Ledger.cpp | 9 +- src/rpc/handlers/LedgerData.cpp | 14 +- src/rpc/handlers/LedgerEntry.cpp | 100 +++++++----- src/rpc/handlers/LedgerRange.cpp | 2 +- src/rpc/handlers/NFTInfo.cpp | 9 +- src/rpc/handlers/NFTOffers.cpp | 8 +- src/rpc/handlers/NoRippleCheck.cpp | 3 +- src/rpc/handlers/ServerInfo.cpp | 4 +- src/rpc/handlers/Subscribe.cpp | 32 ++-- src/rpc/handlers/TransactionEntry.cpp | 4 +- src/rpc/handlers/Tx.cpp | 12 +- src/webserver/HttpBase.h | 19 +-- src/webserver/WsBase.h | 25 +-- unittests/RPCErrors.cpp | 133 ++++++++++++++++ 31 files changed, 808 insertions(+), 434 deletions(-) create mode 100644 src/rpc/Errors.cpp create mode 100644 src/rpc/Errors.h create mode 100644 unittests/RPCErrors.cpp diff --git a/.githooks/pre-commit b/.githooks/pre-commit index c446f75b..cf9fe0e4 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -6,7 +6,7 @@ exec 1>&2 find src unittests -type f \( -name '*.cpp' -o -name '*.h' -o -name '*.ipp' \) -print0 | xargs -0 clang-format -i # check how many lines differ -lines=$(git diff | wc -l) +lines=$(git diff src unittests | wc -l) # check if there is any updated files if [ "$lines" != "0" ]; then diff --git a/CMakeLists.txt b/CMakeLists.txt index d6f48463..85caa8d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,7 @@ target_sources(clio PRIVATE ## Subscriptions src/subscriptions/SubscriptionManager.cpp ## RPC + src/rpc/Errors.cpp src/rpc/RPC.cpp src/rpc/RPCHelpers.cpp src/rpc/Counters.cpp @@ -107,7 +108,8 @@ add_executable(clio_server src/main/main.cpp) target_link_libraries(clio_server PUBLIC clio) if(BUILD_TESTS) - add_executable(clio_tests + add_executable(clio_tests + unittests/RPCErrors.cpp unittests/config.cpp unittests/main.cpp) include(CMake/deps/gtest.cmake) diff --git a/src/rpc/Errors.cpp b/src/rpc/Errors.cpp new file mode 100644 index 00000000..b417c466 --- /dev/null +++ b/src/rpc/Errors.cpp @@ -0,0 +1,135 @@ +#include + +#include + +using namespace std; + +namespace { +template +struct overloadSet : Ts... +{ + using Ts::operator()...; +}; + +// explicit deduction guide (not needed as of C++20, but clang be clang) +template +overloadSet(Ts...) -> overloadSet; +} // namespace + +namespace RPC { + +WarningInfo const& +getWarningInfo(WarningCode code) +{ + constexpr static WarningInfo infos[]{ + {warnUNKNOWN, "Unknown warning"}, + {warnRPC_CLIO, + "This is a clio server. clio only serves validated data. If you " + "want to talk to rippled, include 'ledger_index':'current' in your " + "request"}, + {warnRPC_OUTDATED, "This server may be out of date"}, + {warnRPC_RATE_LIMIT, "You are about to be rate limited"}}; + + auto matchByCode = [code](auto const& info) { return info.code == code; }; + if (auto it = find_if(begin(infos), end(infos), matchByCode); + it != end(infos)) + return *it; + + throw(out_of_range("Invalid WarningCode")); +} + +boost::json::object +makeWarning(WarningCode code) +{ + boost::json::object json; + auto const& info = getWarningInfo(code); + json["id"] = code; + json["message"] = static_cast(info.message); + return json; +} + +ClioErrorInfo const& +getErrorInfo(ClioError code) +{ + constexpr static ClioErrorInfo infos[]{ + {ClioError::rpcMALFORMED_CURRENCY, + "malformedCurrency", + "Malformed currency."}, + }; + + auto matchByCode = [code](auto const& info) { return info.code == code; }; + if (auto it = find_if(begin(infos), end(infos), matchByCode); + it != end(infos)) + return *it; + + throw(out_of_range("Invalid error code")); +} + +boost::json::object +makeError( + RippledError err, + optional customError, + optional customMessage) +{ + boost::json::object json; + auto const& info = ripple::RPC::get_error_info(err); + + json["error"] = customError.value_or(info.token.c_str()).data(); + json["error_code"] = static_cast(err); + json["error_message"] = customMessage.value_or(info.message.c_str()).data(); + json["status"] = "error"; + json["type"] = "response"; + return json; +} + +boost::json::object +makeError( + ClioError err, + optional customError, + optional customMessage) +{ + boost::json::object json; + auto const& info = getErrorInfo(err); + + json["error"] = customError.value_or(info.error).data(); + json["error_code"] = static_cast(info.code); + json["error_message"] = customMessage.value_or(info.message).data(); + json["status"] = "error"; + json["type"] = "response"; + return json; +} + +boost::json::object +makeError(Status const& status) +{ + auto wrapOptional = [](string_view const& str) { + return str.empty() ? nullopt : make_optional(str); + }; + + return visit( + overloadSet{ + [&status, &wrapOptional](RippledError err) { + if (err == ripple::rpcUNKNOWN) + { + return boost::json::object{ + {"error", status.message}, + {"type", "response"}, + {"status", "error"}}; + } + + return makeError( + err, + wrapOptional(status.error), + wrapOptional(status.message)); + }, + [&status, &wrapOptional](ClioError err) { + return makeError( + err, + wrapOptional(status.error), + wrapOptional(status.message)); + }, + }, + status.code); +} + +} // namespace RPC diff --git a/src/rpc/Errors.h b/src/rpc/Errors.h new file mode 100644 index 00000000..6854cc45 --- /dev/null +++ b/src/rpc/Errors.h @@ -0,0 +1,206 @@ +#ifndef REPORTING_RPC_ERRORS_H_INCLUDED +#define REPORTING_RPC_ERRORS_H_INCLUDED + +#include + +#include + +#include +#include +#include +#include + +namespace RPC { + +/** + * @brief Custom clio RPC Errors. + */ +enum class ClioError { + rpcMALFORMED_CURRENCY = 5000, +}; + +/** + * @brief Holds info about a particular @ref ClioError. + */ +struct ClioErrorInfo +{ + ClioError const code; + std::string_view const error; + std::string_view const message; +}; + +/** + * @brief Clio uses compatible Rippled error codes for most RPC errors. + */ +using RippledError = ripple::error_code_i; + +/** + * @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 = ""; + + Status() = default; + /* implicit */ Status(CombinedError code) : code(code){}; + + // HACK. Some rippled handlers explicitly specify errors. + // This means that we have to be able to duplicate this + // functionality. + explicit Status(std::string const& message) + : code(ripple::rpcUNKNOWN), message(message) + { + } + + Status(CombinedError code, std::string message) + : code(code), message(message) + { + } + + Status(CombinedError code, std::string error, std::string message) + : code(code), error(error), message(message) + { + } + + /** + * @brief Returns true if the Status is *not* OK. + */ + operator bool() const + { + if (auto err = std::get_if(&code)) + return *err != RippledError::rpcSUCCESS; + return true; + } +}; + +/** + * @brief Warning codes that can be returned by clio. + */ +enum WarningCode { + warnUNKNOWN = -1, + warnRPC_CLIO = 2001, + warnRPC_OUTDATED = 2002, + warnRPC_RATE_LIMIT = 2003 +}; + +/** + * @brief Holds information about a clio warning. + */ +struct WarningInfo +{ + constexpr WarningInfo() = default; + constexpr WarningInfo(WarningCode code, char const* message) + : code(code), message(message) + { + } + + WarningCode code = warnUNKNOWN; + std::string_view const message = "unknown warning"; +}; + +/** + * @brief Invalid parameters error. + */ +class InvalidParamsError : public std::exception +{ + std::string msg; + +public: + explicit InvalidParamsError(std::string const& msg) : msg(msg) + { + } + + const char* + what() const throw() override + { + return msg.c_str(); + } +}; + +/** + * @brief Account not found error. + */ +class AccountNotFoundError : public std::exception +{ + std::string account; + +public: + explicit AccountNotFoundError(std::string const& acct) : account(acct) + { + } + const char* + what() const throw() override + { + return account.c_str(); + } +}; + +/** + * @brief A globally available @ref Status that represents a successful state + */ +static Status OK; + +/** + * @brief Get the warning info object from a warning code. + * + * @param code The warning code + * @return WarningInfo const& A reference to the static warning info + */ +WarningInfo const& +getWarningInfo(WarningCode code); + +/** + * @brief Generate JSON from a warning code. + * + * @param code The @ref WarningCode + * @return boost::json::object The JSON output + */ +boost::json::object +makeWarning(WarningCode code); + +/** + * @brief Generate JSON from a @ref Status. + * + * @param status The @ref Status + * @return boost::json::object The JSON output + */ +boost::json::object +makeError(Status const& status); + +/** + * @brief Generate JSON from a @ref RippledError. + * + * @param status The rippled @ref RippledError + * @return boost::json::object The JSON output + */ +boost::json::object +makeError( + RippledError err, + std::optional customError = std::nullopt, + std::optional customMessage = std::nullopt); + +/** + * @brief Generate JSON from a @ref ClioError. + * + * @param status The clio's custom @ref ClioError + * @return boost::json::object The JSON output + */ +boost::json::object +makeError( + ClioError err, + std::optional customError = std::nullopt, + std::optional customMessage = std::nullopt); + +} // namespace RPC + +#endif // REPORTING_RPC_ERRORS_H_INCLUDED diff --git a/src/rpc/RPC.cpp b/src/rpc/RPC.cpp index 26a700f6..27e1b00e 100644 --- a/src/rpc/RPC.cpp +++ b/src/rpc/RPC.cpp @@ -8,22 +8,24 @@ #include +using namespace std; + namespace RPC { Context::Context( boost::asio::yield_context& yield_, - std::string const& command_, - std::uint32_t version_, + string const& command_, + uint32_t version_, boost::json::object const& params_, - std::shared_ptr const& backend_, - std::shared_ptr const& subscriptions_, - std::shared_ptr const& balancer_, - std::shared_ptr const& etl_, - std::shared_ptr const& session_, + shared_ptr const& backend_, + shared_ptr const& subscriptions_, + shared_ptr const& balancer_, + shared_ptr const& etl_, + shared_ptr const& session_, util::TagDecoratorFactory const& tagFactory_, Backend::LedgerRange const& range_, Counters& counters_, - std::string const& clientIp_) + string const& clientIp_) : Taggable(tagFactory_) , yield(yield_) , method(command_) @@ -41,19 +43,19 @@ Context::Context( BOOST_LOG_TRIVIAL(debug) << tag() << "new Context created"; } -std::optional +optional make_WsContext( boost::asio::yield_context& yc, boost::json::object const& request, - std::shared_ptr const& backend, - std::shared_ptr const& subscriptions, - std::shared_ptr const& balancer, - std::shared_ptr const& etl, - std::shared_ptr const& session, + shared_ptr const& backend, + shared_ptr const& subscriptions, + shared_ptr const& balancer, + shared_ptr const& etl, + shared_ptr const& session, util::TagDecoratorFactory const& tagFactory, Backend::LedgerRange const& range, Counters& counters, - std::string const& clientIp) + string const& clientIp) { boost::json::value commandValue = nullptr; if (!request.contains("command") && request.contains("method")) @@ -64,9 +66,9 @@ make_WsContext( if (!commandValue.is_string()) return {}; - std::string command = commandValue.as_string().c_str(); + string command = commandValue.as_string().c_str(); - return std::make_optional( + return make_optional( yc, command, 1, @@ -82,23 +84,23 @@ make_WsContext( clientIp); } -std::optional +optional make_HttpContext( boost::asio::yield_context& yc, boost::json::object const& request, - std::shared_ptr const& backend, - std::shared_ptr const& subscriptions, - std::shared_ptr const& balancer, - std::shared_ptr const& etl, + shared_ptr const& backend, + shared_ptr const& subscriptions, + shared_ptr const& balancer, + shared_ptr const& etl, util::TagDecoratorFactory const& tagFactory, Backend::LedgerRange const& range, RPC::Counters& counters, - std::string const& clientIp) + string const& clientIp) { if (!request.contains("method") || !request.at("method").is_string()) return {}; - std::string const& command = request.at("method").as_string().c_str(); + string const& command = request.at("method").as_string().c_str(); if (command == "subscribe" || command == "unsubscribe") return {}; @@ -114,7 +116,7 @@ make_HttpContext( if (!array.at(0).is_object()) return {}; - return std::make_optional( + return make_optional( yc, command, 1, @@ -130,107 +132,38 @@ make_HttpContext( clientIp); } -constexpr static WarningInfo warningInfos[]{ - {warnUNKNOWN, "Unknown warning"}, - {warnRPC_CLIO, - "This is a clio server. clio only serves validated data. If you " - "want to talk to rippled, include 'ledger_index':'current' in your " - "request"}, - {warnRPC_OUTDATED, "This server may be out of date"}, - {warnRPC_RATE_LIMIT, "You are about to be rate limited"}}; - -WarningInfo const& -get_warning_info(warning_code code) -{ - for (WarningInfo const& info : warningInfos) - { - if (info.code == code) - { - return info; - } - } - throw(std::out_of_range("Invalid warning_code")); -} - -boost::json::object -make_warning(warning_code code) -{ - boost::json::object json; - WarningInfo const& info(get_warning_info(code)); - json["id"] = code; - json["message"] = static_cast(info.message); - return json; -} - -boost::json::object -make_error(Error err) -{ - boost::json::object json; - ripple::RPC::ErrorInfo const& info(ripple::RPC::get_error_info(err)); - json["error"] = info.token; - json["error_code"] = static_cast(err); - json["error_message"] = info.message; - json["status"] = "error"; - json["type"] = "response"; - return json; -} - -boost::json::object -make_error(Status const& status) -{ - if (status.error == ripple::rpcUNKNOWN) - { - return { - {"error", status.message}, - {"type", "response"}, - {"status", "error"}}; - } - - boost::json::object json; - ripple::RPC::ErrorInfo const& info( - ripple::RPC::get_error_info(status.error)); - json["error"] = - status.strCode.size() ? status.strCode.c_str() : info.token.c_str(); - json["error_code"] = static_cast(status.error); - json["error_message"] = - status.message.size() ? status.message.c_str() : info.message.c_str(); - json["status"] = "error"; - json["type"] = "response"; - return json; -} - -using LimitRange = std::tuple; -using HandlerFunction = std::function; +using LimitRange = tuple; +using HandlerFunction = function; struct Handler { - std::string method; - std::function handler; - std::optional limit; + string method; + function handler; + optional limit; bool isClioOnly = false; }; class HandlerTable { - std::unordered_map handlerMap_; + unordered_map handlerMap_; public: - HandlerTable(std::initializer_list handlers) + HandlerTable(initializer_list handlers) { for (auto const& handler : handlers) { - handlerMap_[handler.method] = std::move(handler); + handlerMap_[handler.method] = move(handler); } } bool - contains(std::string const& method) + contains(string const& method) { return handlerMap_.contains(method); } - std::optional - getLimitRange(std::string const& command) + optional + getLimitRange(string const& command) { if (!handlerMap_.contains(command)) return {}; @@ -238,8 +171,8 @@ public: return handlerMap_[command].limit; } - std::optional - getHandler(std::string const& command) + optional + getHandler(string const& command) { if (!handlerMap_.contains(command)) return {}; @@ -248,7 +181,7 @@ public: } bool - isClioOnly(std::string const& command) + isClioOnly(string const& command) { return handlerMap_.contains(command) && handlerMap_[command].isClioOnly; } @@ -282,7 +215,7 @@ static HandlerTable handlerTable{ {"transaction_entry", &doTransactionEntry, {}}, {"random", &doRandom, {}}}; -static std::unordered_set forwardCommands{ +static unordered_set forwardCommands{ "submit", "submit_multisigned", "fee", @@ -294,13 +227,13 @@ static std::unordered_set forwardCommands{ "channel_verify"}; bool -validHandler(std::string const& method) +validHandler(string const& method) { return handlerTable.contains(method) || forwardCommands.contains(method); } bool -isClioOnly(std::string const& method) +isClioOnly(string const& method) { return handlerTable.isClioOnly(method); } @@ -313,27 +246,28 @@ shouldSuppressValidatedFlag(RPC::Context const& context) } Status -getLimit(RPC::Context const& context, std::uint32_t& limit) +getLimit(RPC::Context const& context, uint32_t& limit) { if (!handlerTable.getHandler(context.method)) - return Status{Error::rpcUNKNOWN_COMMAND}; + return Status{RippledError::rpcUNKNOWN_COMMAND}; if (!handlerTable.getLimitRange(context.method)) - return Status{Error::rpcINVALID_PARAMS, "rpcDoesNotRequireLimit"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "rpcDoesNotRequireLimit"}; auto [lo, def, hi] = *handlerTable.getLimitRange(context.method); if (context.params.contains(JS(limit))) { - std::string errMsg = "Invalid field 'limit', not unsigned integer."; + string errMsg = "Invalid field 'limit', not unsigned integer."; if (!context.params.at(JS(limit)).is_int64()) - return Status{Error::rpcINVALID_PARAMS, errMsg}; + return Status{RippledError::rpcINVALID_PARAMS, errMsg}; int input = context.params.at(JS(limit)).as_int64(); if (input <= 0) - return Status{Error::rpcINVALID_PARAMS, errMsg}; + return Status{RippledError::rpcINVALID_PARAMS, errMsg}; - limit = std::clamp(static_cast(input), lo, hi); + limit = clamp(static_cast(input), lo, hi); } else { @@ -378,7 +312,7 @@ buildResponse(Context const& ctx) ctx.counters.rpcForwarded(ctx.method); if (!res) - return Status{Error::rpcFAILED_TO_FORWARD}; + return Status{RippledError::rpcFAILED_TO_FORWARD}; if (res->contains("result") && res->at("result").is_object()) return res->at("result").as_object(); @@ -393,13 +327,13 @@ buildResponse(Context const& ctx) { BOOST_LOG_TRIVIAL(error) << __func__ << " Database is too busy. Rejecting request"; - return Status{Error::rpcTOO_BUSY}; + return Status{RippledError::rpcTOO_BUSY}; } auto method = handlerTable.getHandler(ctx.method); if (!method) - return Status{Error::rpcUNKNOWN_COMMAND}; + return Status{RippledError::rpcUNKNOWN_COMMAND}; try { @@ -411,7 +345,7 @@ buildResponse(Context const& ctx) << ctx.tag() << __func__ << " finish executing rpc `" << ctx.method << '`'; - if (auto object = std::get_if(&v); + if (auto object = get_if(&v); object && not shouldSuppressValidatedFlag(ctx)) { (*object)["validated"] = true; @@ -421,22 +355,22 @@ buildResponse(Context const& ctx) } catch (InvalidParamsError const& err) { - return Status{Error::rpcINVALID_PARAMS, err.what()}; + return Status{RippledError::rpcINVALID_PARAMS, err.what()}; } catch (AccountNotFoundError const& err) { - return Status{Error::rpcACT_NOT_FOUND, err.what()}; + return Status{RippledError::rpcACT_NOT_FOUND, err.what()}; } catch (Backend::DatabaseTimeout const& t) { BOOST_LOG_TRIVIAL(error) << __func__ << " Database timeout"; - return Status{Error::rpcTOO_BUSY}; + return Status{RippledError::rpcTOO_BUSY}; } - catch (std::exception const& err) + catch (exception const& err) { BOOST_LOG_TRIVIAL(error) << ctx.tag() << __func__ << " caught exception : " << err.what(); - return Status{Error::rpcINTERNAL}; + return Status{RippledError::rpcINTERNAL}; } } diff --git a/src/rpc/RPC.h b/src/rpc/RPC.h index b1efc2cd..fc73084d 100644 --- a/src/rpc/RPC.h +++ b/src/rpc/RPC.h @@ -1,7 +1,7 @@ #ifndef REPORTING_RPC_H_INCLUDED #define REPORTING_RPC_H_INCLUDED -#include +#include #include #include @@ -65,7 +65,6 @@ struct Context : public util::Taggable Counters& counters_, std::string const& clientIp_); }; -using Error = ripple::error_code_i; struct AccountCursor { @@ -85,108 +84,8 @@ struct AccountCursor } }; -struct Status -{ - Error error = Error::rpcSUCCESS; - std::string strCode = ""; - std::string message = ""; - - Status(){}; - - Status(Error error_) : error(error_){}; - - // HACK. Some rippled handlers explicitly specify errors. - // This means that we have to be able to duplicate this - // functionality. - Status(std::string const& message_) - : error(ripple::rpcUNKNOWN), message(message_) - { - } - - Status(Error error_, std::string message_) - : error(error_), message(message_) - { - } - - Status(Error error_, std::string strCode_, std::string message_) - : error(error_), strCode(strCode_), message(message_) - { - } - - /** Returns true if the Status is *not* OK. */ - operator bool() const - { - return error != Error::rpcSUCCESS; - } -}; - -static Status OK; - using Result = std::variant; -class InvalidParamsError : public std::exception -{ - std::string msg; - -public: - InvalidParamsError(std::string const& msg) : msg(msg) - { - } - - const char* - what() const throw() override - { - return msg.c_str(); - } -}; -class AccountNotFoundError : public std::exception -{ - std::string account; - -public: - AccountNotFoundError(std::string const& acct) : account(acct) - { - } - const char* - what() const throw() override - { - return account.c_str(); - } -}; - -enum warning_code { - warnUNKNOWN = -1, - warnRPC_CLIO = 2001, - warnRPC_OUTDATED = 2002, - warnRPC_RATE_LIMIT = 2003 -}; - -struct WarningInfo -{ - constexpr WarningInfo() : code(warnUNKNOWN), message("unknown warning") - { - } - - constexpr WarningInfo(warning_code code_, char const* message_) - : code(code_), message(message_) - { - } - warning_code code; - std::string_view const message; -}; - -WarningInfo const& -get_warning_info(warning_code code); - -boost::json::object -make_warning(warning_code code); - -boost::json::object -make_error(Status const& status); - -boost::json::object -make_error(Error err); - std::optional make_WsContext( boost::asio::yield_context& yc, diff --git a/src/rpc/RPCHelpers.cpp b/src/rpc/RPCHelpers.cpp index 327d0c27..f9a641b3 100644 --- a/src/rpc/RPCHelpers.cpp +++ b/src/rpc/RPCHelpers.cpp @@ -187,10 +187,10 @@ getHexMarker(boost::json::object const& request, ripple::uint256& marker) if (request.contains(JS(marker))) { if (!request.at(JS(marker)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "markerNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "markerNotString"}; if (!marker.parseHex(request.at(JS(marker)).as_string().c_str())) - return Status{Error::rpcINVALID_PARAMS, "malformedMarker"}; + return Status{RippledError::rpcINVALID_PARAMS, "malformedMarker"}; } return {}; @@ -207,14 +207,14 @@ getAccount( { if (required) return Status{ - Error::rpcINVALID_PARAMS, field.to_string() + "Missing"}; + RippledError::rpcINVALID_PARAMS, field.to_string() + "Missing"}; return {}; } if (!request.at(field).is_string()) return Status{ - Error::rpcINVALID_PARAMS, field.to_string() + "NotString"}; + RippledError::rpcINVALID_PARAMS, field.to_string() + "NotString"}; if (auto a = accountFromStringStrict(request.at(field).as_string().c_str()); a) @@ -223,7 +223,8 @@ getAccount( return {}; } - return Status{Error::rpcACT_MALFORMED, field.to_string() + "Malformed"}; + return Status{ + RippledError::rpcACT_MALFORMED, field.to_string() + "Malformed"}; } Status @@ -240,7 +241,7 @@ getOptionalAccount( if (!request.at(field).is_string()) return Status{ - Error::rpcINVALID_PARAMS, field.to_string() + "NotString"}; + RippledError::rpcINVALID_PARAMS, field.to_string() + "NotString"}; if (auto a = accountFromStringStrict(request.at(field).as_string().c_str()); a) @@ -249,7 +250,8 @@ getOptionalAccount( return {}; } - return Status{Error::rpcINVALID_PARAMS, field.to_string() + "Malformed"}; + return Status{ + RippledError::rpcINVALID_PARAMS, field.to_string() + "Malformed"}; } Status @@ -286,13 +288,13 @@ Status getChannelId(boost::json::object const& request, ripple::uint256& channelId) { if (!request.contains(JS(channel_id))) - return Status{Error::rpcINVALID_PARAMS, "missingChannelID"}; + return Status{RippledError::rpcINVALID_PARAMS, "missingChannelID"}; if (!request.at(JS(channel_id)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "channelIDNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "channelIDNotString"}; if (!channelId.parseHex(request.at(JS(channel_id)).as_string().c_str())) - return Status{Error::rpcCHANNEL_MALFORMED, "malformedChannelID"}; + return Status{RippledError::rpcCHANNEL_MALFORMED, "malformedChannelID"}; return {}; } @@ -554,16 +556,18 @@ ledgerInfoFromRequest(Context const& ctx) if (!hashValue.is_null()) { if (!hashValue.is_string()) - return Status{Error::rpcINVALID_PARAMS, "ledgerHashNotString"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "ledgerHashNotString"}; ripple::uint256 ledgerHash; if (!ledgerHash.parseHex(hashValue.as_string().c_str())) - return Status{Error::rpcINVALID_PARAMS, "ledgerHashMalformed"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "ledgerHashMalformed"}; auto lgrInfo = ctx.backend->fetchLedgerByHash(ledgerHash, ctx.yield); if (!lgrInfo || lgrInfo->seq > ctx.range.maxSequence) - return Status{Error::rpcLGR_NOT_FOUND, "ledgerNotFound"}; + return Status{RippledError::rpcLGR_NOT_FOUND, "ledgerNotFound"}; return *lgrInfo; } @@ -592,13 +596,13 @@ ledgerInfoFromRequest(Context const& ctx) } if (!ledgerSequence) - return Status{Error::rpcINVALID_PARAMS, "ledgerIndexMalformed"}; + return Status{RippledError::rpcINVALID_PARAMS, "ledgerIndexMalformed"}; auto lgrInfo = ctx.backend->fetchLedgerBySequence(*ledgerSequence, ctx.yield); if (!lgrInfo || lgrInfo->seq > ctx.range.maxSequence) - return Status{Error::rpcLGR_NOT_FOUND, "ledgerNotFound"}; + return Status{RippledError::rpcLGR_NOT_FOUND, "ledgerNotFound"}; return *lgrInfo; } @@ -651,7 +655,7 @@ traverseOwnedNodes( { if (!backend.fetchLedgerObject( ripple::keylet::account(accountID).key, sequence, yield)) - return Status{Error::rpcACT_NOT_FOUND}; + return Status{RippledError::rpcACT_NOT_FOUND}; auto parsedCursor = parseAccountCursor(backend, sequence, jsonCursor, accountID, yield); @@ -891,12 +895,12 @@ keypairFromRequst(boost::json::object const& request) } if (count == 0) - return Status{Error::rpcINVALID_PARAMS, "missing field secret"}; + return Status{RippledError::rpcINVALID_PARAMS, "missing field secret"}; if (count > 1) { return Status{ - Error::rpcINVALID_PARAMS, + RippledError::rpcINVALID_PARAMS, "Exactly one of the following must be specified: " " passphrase, secret, seed, or seed_hex"}; } @@ -907,17 +911,18 @@ keypairFromRequst(boost::json::object const& request) if (has_key_type) { if (!request.at("key_type").is_string()) - return Status{Error::rpcINVALID_PARAMS, "keyTypeNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "keyTypeNotString"}; std::string key_type = request.at("key_type").as_string().c_str(); keyType = ripple::keyTypeFromString(key_type); if (!keyType) - return Status{Error::rpcINVALID_PARAMS, "invalidFieldKeyType"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "invalidFieldKeyType"}; if (secretType == "secret") return Status{ - Error::rpcINVALID_PARAMS, + RippledError::rpcINVALID_PARAMS, "The secret field is not allowed if key_type is used."}; } @@ -935,7 +940,7 @@ keypairFromRequst(boost::json::object const& request) if (keyType.value_or(ripple::KeyType::ed25519) != ripple::KeyType::ed25519) return Status{ - Error::rpcINVALID_PARAMS, + RippledError::rpcINVALID_PARAMS, "Specified seed is for an Ed25519 wallet."}; keyType = ripple::KeyType::ed25519; @@ -951,7 +956,8 @@ keypairFromRequst(boost::json::object const& request) { if (!request.at(secretType).is_string()) return Status{ - Error::rpcINVALID_PARAMS, "secret value must be string"}; + RippledError::rpcINVALID_PARAMS, + "secret value must be string"}; std::string key = request.at(secretType).as_string().c_str(); @@ -970,7 +976,7 @@ keypairFromRequst(boost::json::object const& request) { if (!request.at("secret").is_string()) return Status{ - Error::rpcINVALID_PARAMS, + RippledError::rpcINVALID_PARAMS, "field secret should be a string"}; std::string secret = request.at("secret").as_string().c_str(); @@ -980,12 +986,14 @@ keypairFromRequst(boost::json::object const& request) if (!seed) return Status{ - Error::rpcBAD_SEED, "Bad Seed: invalid field message secretType"}; + RippledError::rpcBAD_SEED, + "Bad Seed: invalid field message secretType"}; if (keyType != ripple::KeyType::secp256k1 && keyType != ripple::KeyType::ed25519) return Status{ - Error::rpcINVALID_PARAMS, "keypairForSignature: invalid key type"}; + RippledError::rpcINVALID_PARAMS, + "keypairForSignature: invalid key type"}; return generateKeyPair(*keyType, *seed); } @@ -1343,54 +1351,63 @@ std::variant parseBook(boost::json::object const& request) { if (!request.contains("taker_pays")) - return Status{Error::rpcBAD_MARKET, "missingTakerPays"}; + return Status{RippledError::rpcBAD_MARKET, "missingTakerPays"}; if (!request.contains("taker_gets")) - return Status{Error::rpcINVALID_PARAMS, "missingTakerGets"}; + return Status{RippledError::rpcINVALID_PARAMS, "missingTakerGets"}; if (!request.at("taker_pays").is_object()) - return Status{Error::rpcINVALID_PARAMS, "takerPaysNotObject"}; + return Status{RippledError::rpcINVALID_PARAMS, "takerPaysNotObject"}; if (!request.at("taker_gets").is_object()) - return Status{Error::rpcINVALID_PARAMS, "takerGetsNotObject"}; + return Status{RippledError::rpcINVALID_PARAMS, "takerGetsNotObject"}; auto taker_pays = request.at("taker_pays").as_object(); if (!taker_pays.contains("currency")) - return Status{Error::rpcINVALID_PARAMS, "missingTakerPaysCurrency"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "missingTakerPaysCurrency"}; if (!taker_pays.at("currency").is_string()) - return Status{Error::rpcINVALID_PARAMS, "takerPaysCurrencyNotString"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "takerPaysCurrencyNotString"}; auto taker_gets = request.at("taker_gets").as_object(); if (!taker_gets.contains("currency")) - return Status{Error::rpcINVALID_PARAMS, "missingTakerGetsCurrency"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "missingTakerGetsCurrency"}; if (!taker_gets.at("currency").is_string()) - return Status{Error::rpcINVALID_PARAMS, "takerGetsCurrencyNotString"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "takerGetsCurrencyNotString"}; ripple::Currency pay_currency; if (!ripple::to_currency( pay_currency, taker_pays.at("currency").as_string().c_str())) - return Status{Error::rpcSRC_CUR_MALFORMED, "badTakerPaysCurrency"}; + return Status{ + RippledError::rpcSRC_CUR_MALFORMED, "badTakerPaysCurrency"}; ripple::Currency get_currency; if (!ripple::to_currency( get_currency, taker_gets["currency"].as_string().c_str())) - return Status{Error::rpcDST_AMT_MALFORMED, "badTakerGetsCurrency"}; + return Status{ + RippledError::rpcDST_AMT_MALFORMED, "badTakerGetsCurrency"}; ripple::AccountID pay_issuer; if (taker_pays.contains("issuer")) { if (!taker_pays.at("issuer").is_string()) - return Status{Error::rpcINVALID_PARAMS, "takerPaysIssuerNotString"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "takerPaysIssuerNotString"}; if (!ripple::to_issuer( pay_issuer, taker_pays.at("issuer").as_string().c_str())) - return Status{Error::rpcSRC_ISR_MALFORMED, "badTakerPaysIssuer"}; + return Status{ + RippledError::rpcSRC_ISR_MALFORMED, "badTakerPaysIssuer"}; if (pay_issuer == ripple::noAccount()) return Status{ - Error::rpcSRC_ISR_MALFORMED, "badTakerPaysIssuerAccountOne"}; + RippledError::rpcSRC_ISR_MALFORMED, + "badTakerPaysIssuerAccountOne"}; } else { @@ -1399,18 +1416,19 @@ parseBook(boost::json::object const& request) if (isXRP(pay_currency) && !isXRP(pay_issuer)) return Status{ - Error::rpcSRC_ISR_MALFORMED, + RippledError::rpcSRC_ISR_MALFORMED, "Unneeded field 'taker_pays.issuer' for XRP currency " "specification."}; if (!isXRP(pay_currency) && isXRP(pay_issuer)) return Status{ - Error::rpcSRC_ISR_MALFORMED, + RippledError::rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', expected non-XRP " "issuer."}; if ((!isXRP(pay_currency)) && (!taker_pays.contains("issuer"))) - return Status{Error::rpcSRC_ISR_MALFORMED, "Missing non-XRP issuer."}; + return Status{ + RippledError::rpcSRC_ISR_MALFORMED, "Missing non-XRP issuer."}; ripple::AccountID get_issuer; @@ -1418,17 +1436,18 @@ parseBook(boost::json::object const& request) { if (!taker_gets["issuer"].is_string()) return Status{ - Error::rpcINVALID_PARAMS, "taker_gets.issuer should be string"}; + RippledError::rpcINVALID_PARAMS, + "taker_gets.issuer should be string"}; if (!ripple::to_issuer( get_issuer, taker_gets.at("issuer").as_string().c_str())) return Status{ - Error::rpcDST_ISR_MALFORMED, + RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer."}; if (get_issuer == ripple::noAccount()) return Status{ - Error::rpcDST_ISR_MALFORMED, + RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer account " "one."}; } @@ -1439,17 +1458,17 @@ parseBook(boost::json::object const& request) if (ripple::isXRP(get_currency) && !ripple::isXRP(get_issuer)) return Status{ - Error::rpcDST_ISR_MALFORMED, + RippledError::rpcDST_ISR_MALFORMED, "Unneeded field 'taker_gets.issuer' for XRP currency " "specification."}; if (!ripple::isXRP(get_currency) && ripple::isXRP(get_issuer)) return Status{ - Error::rpcDST_ISR_MALFORMED, + RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', expected non-XRP issuer."}; if (pay_currency == get_currency && pay_issuer == get_issuer) - return Status{Error::rpcBAD_MARKET, "badMarket"}; + return Status{RippledError::rpcBAD_MARKET, "badMarket"}; return ripple::Book{{pay_currency, pay_issuer}, {get_currency, get_issuer}}; } @@ -1459,12 +1478,12 @@ parseTaker(boost::json::value const& taker) { std::optional takerID = {}; if (!taker.is_string()) - return {Status{Error::rpcINVALID_PARAMS, "takerNotString"}}; + return {Status{RippledError::rpcINVALID_PARAMS, "takerNotString"}}; takerID = accountFromStringStrict(taker.as_string().c_str()); if (!takerID) - return Status{Error::rpcBAD_ISSUER, "invalidTakerAccount"}; + return Status{RippledError::rpcBAD_ISSUER, "invalidTakerAccount"}; return *takerID; } bool @@ -1486,14 +1505,14 @@ std::variant getNFTID(boost::json::object const& request) { if (!request.contains(JS(nft_id))) - return Status{Error::rpcINVALID_PARAMS, "missingTokenID"}; + return Status{RippledError::rpcINVALID_PARAMS, "missingTokenID"}; if (!request.at(JS(nft_id)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "tokenIDNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "tokenIDNotString"}; ripple::uint256 tokenid; if (!tokenid.parseHex(request.at(JS(nft_id)).as_string().c_str())) - return Status{Error::rpcINVALID_PARAMS, "malformedTokenID"}; + return Status{RippledError::rpcINVALID_PARAMS, "malformedTokenID"}; return tokenid; } @@ -1521,7 +1540,7 @@ traverseTransactions( if (request.contains(JS(marker))) { if (!request.at(JS(marker)).is_object()) - return Status{Error::rpcINVALID_PARAMS, "invalidMarker"}; + return Status{RippledError::rpcINVALID_PARAMS, "invalidMarker"}; auto const& obj = request.at(JS(marker)).as_object(); std::optional transactionIndex = {}; @@ -1529,7 +1548,7 @@ traverseTransactions( { if (!obj.at(JS(seq)).is_int64()) return Status{ - Error::rpcINVALID_PARAMS, "transactionIndexNotInt"}; + RippledError::rpcINVALID_PARAMS, "transactionIndexNotInt"}; transactionIndex = boost::json::value_to(obj.at(JS(seq))); @@ -1539,14 +1558,16 @@ traverseTransactions( if (obj.contains(JS(ledger))) { if (!obj.at(JS(ledger)).is_int64()) - return Status{Error::rpcINVALID_PARAMS, "ledgerIndexNotInt"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "ledgerIndexNotInt"}; ledgerIndex = boost::json::value_to(obj.at(JS(ledger))); } if (!transactionIndex || !ledgerIndex) - return Status{Error::rpcINVALID_PARAMS, "missingLedgerOrSeq"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "missingLedgerOrSeq"}; cursor = {*ledgerIndex, *transactionIndex}; } @@ -1557,14 +1578,16 @@ traverseTransactions( auto& min = request.at(JS(ledger_index_min)); if (!min.is_int64()) - return Status{Error::rpcINVALID_PARAMS, "ledgerSeqMinNotNumber"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "ledgerSeqMinNotNumber"}; if (min.as_int64() != -1) { if (context.range.maxSequence < min.as_int64() || context.range.minSequence > min.as_int64()) return Status{ - Error::rpcLGR_IDX_MALFORMED, "ledgerSeqMinOutOfRange"}; + RippledError::rpcLGR_IDX_MALFORMED, + "ledgerSeqMinOutOfRange"}; else minIndex = boost::json::value_to(min); } @@ -1579,20 +1602,22 @@ traverseTransactions( auto& max = request.at(JS(ledger_index_max)); if (!max.is_int64()) - return Status{Error::rpcINVALID_PARAMS, "ledgerSeqMaxNotNumber"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "ledgerSeqMaxNotNumber"}; if (max.as_int64() != -1) { if (context.range.maxSequence < max.as_int64() || context.range.minSequence > max.as_int64()) return Status{ - Error::rpcLGR_IDX_MALFORMED, "ledgerSeqMaxOutOfRange"}; + RippledError::rpcLGR_IDX_MALFORMED, + "ledgerSeqMaxOutOfRange"}; else maxIndex = boost::json::value_to(max); } if (minIndex > maxIndex) - return Status{Error::rpcINVALID_PARAMS, "invalidIndex"}; + return Status{RippledError::rpcINVALID_PARAMS, "invalidIndex"}; if (!forward && !cursor) cursor = {maxIndex, INT32_MAX}; @@ -1603,7 +1628,8 @@ traverseTransactions( if (request.contains(JS(ledger_index_max)) || request.contains(JS(ledger_index_min))) return Status{ - Error::rpcINVALID_PARAMS, "containsLedgerSpecifierAndRange"}; + RippledError::rpcINVALID_PARAMS, + "containsLedgerSpecifierAndRange"}; auto v = ledgerInfoFromRequest(context); if (auto status = std::get_if(&v); status) diff --git a/src/rpc/handlers/AccountChannels.cpp b/src/rpc/handlers/AccountChannels.cpp index 9ce3cc7a..337d5e8e 100644 --- a/src/rpc/handlers/AccountChannels.cpp +++ b/src/rpc/handlers/AccountChannels.cpp @@ -62,7 +62,7 @@ doAccountChannels(Context const& context) ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield); if (!rawAcct) - return Status{Error::rpcACT_NOT_FOUND, "accountNotFound"}; + return Status{RippledError::rpcACT_NOT_FOUND, "accountNotFound"}; ripple::AccountID destAccount; if (auto const status = @@ -78,7 +78,7 @@ doAccountChannels(Context const& context) if (request.contains(JS(marker))) { if (!request.at(JS(marker)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "markerNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "markerNotString"}; marker = request.at(JS(marker)).as_string().c_str(); } diff --git a/src/rpc/handlers/AccountCurrencies.cpp b/src/rpc/handlers/AccountCurrencies.cpp index fd3e2462..f32c884f 100644 --- a/src/rpc/handlers/AccountCurrencies.cpp +++ b/src/rpc/handlers/AccountCurrencies.cpp @@ -32,7 +32,7 @@ doAccountCurrencies(Context const& context) ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield); if (!rawAcct) - return Status{Error::rpcACT_NOT_FOUND, "accountNotFound"}; + return Status{RippledError::rpcACT_NOT_FOUND, "accountNotFound"}; std::set send, receive; auto const addToResponse = [&](ripple::SLE&& sle) { diff --git a/src/rpc/handlers/AccountInfo.cpp b/src/rpc/handlers/AccountInfo.cpp index d8d1517c..be004c70 100644 --- a/src/rpc/handlers/AccountInfo.cpp +++ b/src/rpc/handlers/AccountInfo.cpp @@ -34,7 +34,7 @@ doAccountInfo(Context const& context) else if (request.contains(JS(ident))) strIdent = request.at(JS(ident)).as_string().c_str(); else - return Status{Error::rpcACT_MALFORMED}; + return Status{RippledError::rpcACT_MALFORMED}; // We only need to fetch the ledger header because the ledger hash is // supposed to be included in the response. The ledger sequence is specified @@ -48,7 +48,7 @@ doAccountInfo(Context const& context) // Get info on account. auto accountID = accountFromStringStrict(strIdent); if (!accountID) - return Status{Error::rpcACT_MALFORMED}; + return Status{RippledError::rpcACT_MALFORMED}; assert(accountID.has_value()); @@ -59,14 +59,14 @@ doAccountInfo(Context const& context) if (!dbResponse) { - return Status{Error::rpcACT_NOT_FOUND}; + return Status{RippledError::rpcACT_NOT_FOUND}; } ripple::STLedgerEntry sle{ ripple::SerialIter{dbResponse->data(), dbResponse->size()}, key.key}; if (!key.check(sle)) - return Status{Error::rpcDB_DESERIALIZATION}; + return Status{RippledError::rpcDB_DESERIALIZATION}; // if (!binary) // response[JS(account_data)] = getJson(sle); @@ -97,7 +97,7 @@ doAccountInfo(Context const& context) ripple::SerialIter{signers->data(), signers->size()}, signersKey.key}; if (!signersKey.check(sleSigners)) - return Status{Error::rpcDB_DESERIALIZATION}; + return Status{RippledError::rpcDB_DESERIALIZATION}; signerList.push_back(toJson(sleSigners)); } diff --git a/src/rpc/handlers/AccountLines.cpp b/src/rpc/handlers/AccountLines.cpp index 8d4cc36a..18092932 100644 --- a/src/rpc/handlers/AccountLines.cpp +++ b/src/rpc/handlers/AccountLines.cpp @@ -107,7 +107,7 @@ doAccountLines(Context const& context) ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield); if (!rawAcct) - return Status{Error::rpcACT_NOT_FOUND, "accountNotFound"}; + return Status{RippledError::rpcACT_NOT_FOUND, "accountNotFound"}; std::optional peerAccount; if (auto const status = getOptionalAccount(request, peerAccount, JS(peer)); @@ -122,7 +122,7 @@ doAccountLines(Context const& context) if (request.contains(JS(marker))) { if (not request.at(JS(marker)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "markerNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "markerNotString"}; marker = request.at(JS(marker)).as_string().c_str(); } @@ -131,7 +131,8 @@ doAccountLines(Context const& context) if (request.contains(JS(ignore_default))) { if (not request.at(JS(ignore_default)).is_bool()) - return Status{Error::rpcINVALID_PARAMS, "ignoreDefaultNotBool"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "ignoreDefaultNotBool"}; ignoreDefault = request.at(JS(ignore_default)).as_bool(); } diff --git a/src/rpc/handlers/AccountObjects.cpp b/src/rpc/handlers/AccountObjects.cpp index ff1429a8..b059d94c 100644 --- a/src/rpc/handlers/AccountObjects.cpp +++ b/src/rpc/handlers/AccountObjects.cpp @@ -45,13 +45,13 @@ doAccountNFTs(Context const& context) return status; if (!accountID) - return Status{Error::rpcINVALID_PARAMS, "malformedAccount"}; + return Status{RippledError::rpcINVALID_PARAMS, "malformedAccount"}; auto rawAcct = context.backend->fetchLedgerObject( ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield); if (!rawAcct) - return Status{Error::rpcACT_NOT_FOUND, "accountNotFound"}; + return Status{RippledError::rpcACT_NOT_FOUND, "accountNotFound"}; std::uint32_t limit; if (auto const status = getLimit(context, limit); status) @@ -157,7 +157,7 @@ doAccountObjects(Context const& context) if (request.contains("marker")) { if (!request.at("marker").is_string()) - return Status{Error::rpcINVALID_PARAMS, "markerNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "markerNotString"}; marker = request.at("marker").as_string().c_str(); } @@ -166,11 +166,11 @@ doAccountObjects(Context const& context) if (request.contains(JS(type))) { if (!request.at(JS(type)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "typeNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "typeNotString"}; std::string typeAsString = request.at(JS(type)).as_string().c_str(); if (types.find(typeAsString) == types.end()) - return Status{Error::rpcINVALID_PARAMS, "typeInvalid"}; + return Status{RippledError::rpcINVALID_PARAMS, "typeInvalid"}; objectType = types[typeAsString]; } diff --git a/src/rpc/handlers/AccountOffers.cpp b/src/rpc/handlers/AccountOffers.cpp index 53de6d86..0dcafdc1 100644 --- a/src/rpc/handlers/AccountOffers.cpp +++ b/src/rpc/handlers/AccountOffers.cpp @@ -84,7 +84,7 @@ doAccountOffers(Context const& context) ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield); if (!rawAcct) - return Status{Error::rpcACT_NOT_FOUND, "accountNotFound"}; + return Status{RippledError::rpcACT_NOT_FOUND, "accountNotFound"}; std::uint32_t limit; if (auto const status = getLimit(context, limit); status) @@ -94,7 +94,7 @@ doAccountOffers(Context const& context) if (request.contains(JS(marker))) { if (!request.at(JS(marker)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "markerNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "markerNotString"}; marker = request.at(JS(marker)).as_string().c_str(); } diff --git a/src/rpc/handlers/BookOffers.cpp b/src/rpc/handlers/BookOffers.cpp index 0666106c..3de025f8 100644 --- a/src/rpc/handlers/BookOffers.cpp +++ b/src/rpc/handlers/BookOffers.cpp @@ -30,10 +30,10 @@ doBookOffers(Context const& context) if (request.contains("book")) { if (!request.at("book").is_string()) - return Status{Error::rpcINVALID_PARAMS, "bookNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "bookNotString"}; if (!bookBase.parseHex(request.at("book").as_string().c_str())) - return Status{Error::rpcINVALID_PARAMS, "invalidBook"}; + return Status{RippledError::rpcINVALID_PARAMS, "invalidBook"}; } else { diff --git a/src/rpc/handlers/ChannelAuthorize.cpp b/src/rpc/handlers/ChannelAuthorize.cpp index 4f8fe894..c71d1d18 100644 --- a/src/rpc/handlers/ChannelAuthorize.cpp +++ b/src/rpc/handlers/ChannelAuthorize.cpp @@ -28,13 +28,14 @@ doChannelAuthorize(Context const& context) boost::json::object response = {}; if (!request.contains(JS(amount))) - return Status{Error::rpcINVALID_PARAMS, "missingAmount"}; + return Status{RippledError::rpcINVALID_PARAMS, "missingAmount"}; if (!request.at(JS(amount)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "amountNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "amountNotString"}; if (!request.contains(JS(key_type)) && !request.contains(JS(secret))) - return Status{Error::rpcINVALID_PARAMS, "missingKeyTypeOrSecret"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "missingKeyTypeOrSecret"}; auto v = keypairFromRequst(request); if (auto status = std::get_if(&v)) @@ -51,7 +52,8 @@ doChannelAuthorize(Context const& context) ripple::to_uint64(request.at(JS(amount)).as_string().c_str()); if (!optDrops) - return Status{Error::rpcCHANNEL_AMT_MALFORMED, "couldNotParseAmount"}; + return Status{ + RippledError::rpcCHANNEL_AMT_MALFORMED, "couldNotParseAmount"}; std::uint64_t drops = *optDrops; @@ -66,7 +68,7 @@ doChannelAuthorize(Context const& context) } catch (std::exception&) { - return Status{Error::rpcINTERNAL}; + return Status{RippledError::rpcINTERNAL}; } return response; diff --git a/src/rpc/handlers/ChannelVerify.cpp b/src/rpc/handlers/ChannelVerify.cpp index 1cf7ba4b..ca9b59d5 100644 --- a/src/rpc/handlers/ChannelVerify.cpp +++ b/src/rpc/handlers/ChannelVerify.cpp @@ -17,22 +17,22 @@ doChannelVerify(Context const& context) boost::json::object response = {}; if (!request.contains(JS(amount))) - return Status{Error::rpcINVALID_PARAMS, "missingAmount"}; + return Status{RippledError::rpcINVALID_PARAMS, "missingAmount"}; if (!request.at(JS(amount)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "amountNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "amountNotString"}; if (!request.contains(JS(signature))) - return Status{Error::rpcINVALID_PARAMS, "missingSignature"}; + return Status{RippledError::rpcINVALID_PARAMS, "missingSignature"}; if (!request.at(JS(signature)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "signatureNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "signatureNotString"}; if (!request.contains(JS(public_key))) - return Status{Error::rpcINVALID_PARAMS, "missingPublicKey"}; + return Status{RippledError::rpcINVALID_PARAMS, "missingPublicKey"}; if (!request.at(JS(public_key)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "publicKeyNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "publicKeyNotString"}; std::optional pk; { @@ -45,12 +45,14 @@ doChannelVerify(Context const& context) { auto pkHex = ripple::strUnHex(strPk); if (!pkHex) - return Status{Error::rpcPUBLIC_MALFORMED, "malformedPublicKey"}; + return Status{ + RippledError::rpcPUBLIC_MALFORMED, "malformedPublicKey"}; auto const pkType = ripple::publicKeyType(ripple::makeSlice(*pkHex)); if (!pkType) - return Status{Error::rpcPUBLIC_MALFORMED, "invalidKeyType"}; + return Status{ + RippledError::rpcPUBLIC_MALFORMED, "invalidKeyType"}; pk.emplace(ripple::makeSlice(*pkHex)); } @@ -64,14 +66,15 @@ doChannelVerify(Context const& context) ripple::to_uint64(request.at(JS(amount)).as_string().c_str()); if (!optDrops) - return Status{Error::rpcCHANNEL_AMT_MALFORMED, "couldNotParseAmount"}; + return Status{ + RippledError::rpcCHANNEL_AMT_MALFORMED, "couldNotParseAmount"}; std::uint64_t drops = *optDrops; auto sig = ripple::strUnHex(request.at(JS(signature)).as_string().c_str()); if (!sig || !sig->size()) - return Status{Error::rpcINVALID_PARAMS, "invalidSignature"}; + return Status{RippledError::rpcINVALID_PARAMS, "invalidSignature"}; ripple::Serializer msg; ripple::serializePayChanAuthorization( diff --git a/src/rpc/handlers/GatewayBalances.cpp b/src/rpc/handlers/GatewayBalances.cpp index fa54232a..8d6c7477 100644 --- a/src/rpc/handlers/GatewayBalances.cpp +++ b/src/rpc/handlers/GatewayBalances.cpp @@ -187,7 +187,7 @@ doGatewayBalances(Context const& context) }; if (not std::all_of( hotWallets.begin(), hotWallets.end(), containsHotWallet)) - return Status{Error::rpcINVALID_PARAMS, "invalidHotWallet"}; + return Status{RippledError::rpcINVALID_PARAMS, "invalidHotWallet"}; if (auto balances = toJson(hotBalances); balances.size()) response[JS(balances)] = balances; diff --git a/src/rpc/handlers/Ledger.cpp b/src/rpc/handlers/Ledger.cpp index 9d572d6c..75e83e0c 100644 --- a/src/rpc/handlers/Ledger.cpp +++ b/src/rpc/handlers/Ledger.cpp @@ -13,7 +13,7 @@ doLedger(Context const& context) if (params.contains(JS(binary))) { if (!params.at(JS(binary)).is_bool()) - return Status{Error::rpcINVALID_PARAMS, "binaryFlagNotBool"}; + return Status{RippledError::rpcINVALID_PARAMS, "binaryFlagNotBool"}; binary = params.at(JS(binary)).as_bool(); } @@ -22,7 +22,8 @@ doLedger(Context const& context) if (params.contains(JS(transactions))) { if (!params.at(JS(transactions)).is_bool()) - return Status{Error::rpcINVALID_PARAMS, "transactionsFlagNotBool"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "transactionsFlagNotBool"}; transactions = params.at(JS(transactions)).as_bool(); } @@ -31,7 +32,7 @@ doLedger(Context const& context) if (params.contains(JS(expand))) { if (!params.at(JS(expand)).is_bool()) - return Status{Error::rpcINVALID_PARAMS, "expandFlagNotBool"}; + return Status{RippledError::rpcINVALID_PARAMS, "expandFlagNotBool"}; expand = params.at(JS(expand)).as_bool(); } @@ -40,7 +41,7 @@ doLedger(Context const& context) if (params.contains("diff")) { if (!params.at("diff").is_bool()) - return Status{Error::rpcINVALID_PARAMS, "diffFlagNotBool"}; + return Status{RippledError::rpcINVALID_PARAMS, "diffFlagNotBool"}; diff = params.at("diff").as_bool(); } diff --git a/src/rpc/handlers/LedgerData.cpp b/src/rpc/handlers/LedgerData.cpp index 1d6bd0dd..335ec5e4 100644 --- a/src/rpc/handlers/LedgerData.cpp +++ b/src/rpc/handlers/LedgerData.cpp @@ -41,7 +41,7 @@ doLedgerData(Context const& context) if (request.contains("out_of_order")) { if (!request.at("out_of_order").is_bool()) - return Status{Error::rpcINVALID_PARAMS, "binaryFlagNotBool"}; + return Status{RippledError::rpcINVALID_PARAMS, "binaryFlagNotBool"}; outOfOrder = request.at("out_of_order").as_bool(); } @@ -55,11 +55,13 @@ doLedgerData(Context const& context) { if (!request.at(JS(marker)).is_int64()) return Status{ - Error::rpcINVALID_PARAMS, "markerNotStringOrInt"}; + RippledError::rpcINVALID_PARAMS, + "markerNotStringOrInt"}; diffMarker = value_to(request.at(JS(marker))); } else - return Status{Error::rpcINVALID_PARAMS, "markerNotString"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "markerNotString"}; } else { @@ -67,7 +69,8 @@ doLedgerData(Context const& context) marker = ripple::uint256{}; if (!marker->parseHex(request.at(JS(marker)).as_string().c_str())) - return Status{Error::rpcINVALID_PARAMS, "markerMalformed"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "markerMalformed"}; } } @@ -115,7 +118,8 @@ doLedgerData(Context const& context) if (!outOfOrder && !context.backend->fetchLedgerObject( *marker, lgrInfo.seq, context.yield)) - return Status{Error::rpcINVALID_PARAMS, "markerDoesNotExist"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "markerDoesNotExist"}; } response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash); diff --git a/src/rpc/handlers/LedgerEntry.cpp b/src/rpc/handlers/LedgerEntry.cpp index a3eb3c82..647788a7 100644 --- a/src/rpc/handlers/LedgerEntry.cpp +++ b/src/rpc/handlers/LedgerEntry.cpp @@ -32,31 +32,32 @@ doLedgerEntry(Context const& context) if (request.contains(JS(index))) { if (!request.at(JS(index)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "indexNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "indexNotString"}; if (!key.parseHex(request.at(JS(index)).as_string().c_str())) - return Status{Error::rpcINVALID_PARAMS, "malformedIndex"}; + return Status{RippledError::rpcINVALID_PARAMS, "malformedIndex"}; } else if (request.contains(JS(account_root))) { if (!request.at(JS(account_root)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "account_rootNotString"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "account_rootNotString"}; auto const account = ripple::parseBase58( request.at(JS(account_root)).as_string().c_str()); if (!account || account->isZero()) - return Status{Error::rpcINVALID_PARAMS, "malformedAddress"}; + return Status{RippledError::rpcINVALID_PARAMS, "malformedAddress"}; else key = ripple::keylet::account(*account).key; } else if (request.contains(JS(check))) { if (!request.at(JS(check)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "checkNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "checkNotString"}; if (!key.parseHex(request.at(JS(check)).as_string().c_str())) { - return Status{Error::rpcINVALID_PARAMS, "checkMalformed"}; + return Status{RippledError::rpcINVALID_PARAMS, "checkMalformed"}; } } else if (request.contains(JS(deposit_preauth))) @@ -68,7 +69,8 @@ doLedgerEntry(Context const& context) request.at(JS(deposit_preauth)).as_string().c_str())) { return Status{ - Error::rpcINVALID_PARAMS, "deposit_preauthMalformed"}; + RippledError::rpcINVALID_PARAMS, + "deposit_preauthMalformed"}; } } else if ( @@ -78,7 +80,7 @@ doLedgerEntry(Context const& context) .at(JS(owner)) .is_string()) { - return Status{Error::rpcINVALID_PARAMS, "malformedOwner"}; + return Status{RippledError::rpcINVALID_PARAMS, "malformedOwner"}; } else if ( !request.at(JS(deposit_preauth)) @@ -89,7 +91,8 @@ doLedgerEntry(Context const& context) .at(JS(authorized)) .is_string()) { - return Status{Error::rpcINVALID_PARAMS, "authorizedNotString"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "authorizedNotString"}; } else { @@ -103,9 +106,11 @@ doLedgerEntry(Context const& context) deposit_preauth.at(JS(authorized)).as_string().c_str()); if (!owner) - return Status{Error::rpcINVALID_PARAMS, "malformedOwner"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "malformedOwner"}; else if (!authorized) - return Status{Error::rpcINVALID_PARAMS, "malformedAuthorized"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "malformedAuthorized"}; else key = ripple::keylet::depositPreauth(*owner, *authorized).key; } @@ -115,18 +120,20 @@ doLedgerEntry(Context const& context) if (!request.at(JS(directory)).is_object()) { if (!request.at(JS(directory)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "directoryNotString"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "directoryNotString"}; if (!key.parseHex(request.at(JS(directory)).as_string().c_str())) { - return Status{Error::rpcINVALID_PARAMS, "malformedDirectory"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "malformedDirectory"}; } } else if ( request.at(JS(directory)).as_object().contains(JS(sub_index)) && !request.at(JS(directory)).as_object().at(JS(sub_index)).is_int64()) { - return Status{Error::rpcINVALID_PARAMS, "sub_indexNotInt"}; + return Status{RippledError::rpcINVALID_PARAMS, "sub_indexNotInt"}; } else { @@ -144,13 +151,14 @@ doLedgerEntry(Context const& context) { // May not specify both dir_root and owner. return Status{ - Error::rpcINVALID_PARAMS, + RippledError::rpcINVALID_PARAMS, "mayNotSpecifyBothDirRootAndOwner"}; } else if (!uDirRoot.parseHex( directory.at(JS(dir_root)).as_string().c_str())) { - return Status{Error::rpcINVALID_PARAMS, "malformedDirRoot"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "malformedDirRoot"}; } else { @@ -164,7 +172,8 @@ doLedgerEntry(Context const& context) if (!ownerID) { - return Status{Error::rpcINVALID_PARAMS, "malformedAddress"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "malformedAddress"}; } else { @@ -176,7 +185,7 @@ doLedgerEntry(Context const& context) else { return Status{ - Error::rpcINVALID_PARAMS, "missingOwnerOrDirRoot"}; + RippledError::rpcINVALID_PARAMS, "missingOwnerOrDirRoot"}; } } } @@ -185,19 +194,20 @@ doLedgerEntry(Context const& context) if (!request.at(JS(escrow)).is_object()) { if (!key.parseHex(request.at(JS(escrow)).as_string().c_str())) - return Status{Error::rpcINVALID_PARAMS, "malformedEscrow"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "malformedEscrow"}; } else if ( !request.at(JS(escrow)).as_object().contains(JS(owner)) || !request.at(JS(escrow)).as_object().at(JS(owner)).is_string()) { - return Status{Error::rpcINVALID_PARAMS, "malformedOwner"}; + return Status{RippledError::rpcINVALID_PARAMS, "malformedOwner"}; } else if ( !request.at(JS(escrow)).as_object().contains(JS(seq)) || !request.at(JS(escrow)).as_object().at(JS(seq)).is_int64()) { - return Status{Error::rpcINVALID_PARAMS, "malformedSeq"}; + return Status{RippledError::rpcINVALID_PARAMS, "malformedSeq"}; } else { @@ -209,7 +219,8 @@ doLedgerEntry(Context const& context) .c_str()); if (!id) - return Status{Error::rpcINVALID_PARAMS, "malformedAddress"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "malformedAddress"}; else { std::uint32_t seq = @@ -223,19 +234,20 @@ doLedgerEntry(Context const& context) if (!request.at(JS(offer)).is_object()) { if (!key.parseHex(request.at(JS(offer)).as_string().c_str())) - return Status{Error::rpcINVALID_PARAMS, "malformedOffer"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "malformedOffer"}; } else if ( !request.at(JS(offer)).as_object().contains(JS(account)) || !request.at(JS(offer)).as_object().at(JS(account)).is_string()) { - return Status{Error::rpcINVALID_PARAMS, "malformedAccount"}; + return Status{RippledError::rpcINVALID_PARAMS, "malformedAccount"}; } else if ( !request.at(JS(offer)).as_object().contains(JS(seq)) || !request.at(JS(offer)).as_object().at(JS(seq)).is_int64()) { - return Status{Error::rpcINVALID_PARAMS, "malformedSeq"}; + return Status{RippledError::rpcINVALID_PARAMS, "malformedSeq"}; } else { @@ -244,7 +256,8 @@ doLedgerEntry(Context const& context) offer.at(JS(account)).as_string().c_str()); if (!id) - return Status{Error::rpcINVALID_PARAMS, "malformedAddress"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "malformedAddress"}; else { std::uint32_t seq = @@ -256,15 +269,18 @@ doLedgerEntry(Context const& context) else if (request.contains(JS(payment_channel))) { if (!request.at(JS(payment_channel)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "paymentChannelNotString"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "paymentChannelNotString"}; if (!key.parseHex(request.at(JS(payment_channel)).as_string().c_str())) - return Status{Error::rpcINVALID_PARAMS, "malformedPaymentChannel"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "malformedPaymentChannel"}; } else if (request.contains(JS(ripple_state))) { if (!request.at(JS(ripple_state)).is_object()) - return Status{Error::rpcINVALID_PARAMS, "rippleStateNotObject"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "rippleStateNotObject"}; ripple::Currency currency; boost::json::object const& state = @@ -273,7 +289,7 @@ doLedgerEntry(Context const& context) if (!state.contains(JS(currency)) || !state.at(JS(currency)).is_string()) { - return Status{Error::rpcINVALID_PARAMS, "malformedCurrency"}; + return Status{RippledError::rpcINVALID_PARAMS, "currencyNotString"}; } if (!state.contains(JS(accounts)) || @@ -284,7 +300,7 @@ doLedgerEntry(Context const& context) (state.at(JS(accounts)).as_array().at(0).as_string() == state.at(JS(accounts)).as_array().at(1).as_string())) { - return Status{Error::rpcINVALID_PARAMS, "malformedAccounts"}; + return Status{RippledError::rpcINVALID_PARAMS, "malformedAccounts"}; } auto const id1 = ripple::parseBase58( @@ -293,11 +309,13 @@ doLedgerEntry(Context const& context) state.at(JS(accounts)).as_array().at(1).as_string().c_str()); if (!id1 || !id2) - return Status{Error::rpcINVALID_PARAMS, "malformedAddresses"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "malformedAddresses"}; else if (!ripple::to_currency( currency, state.at(JS(currency)).as_string().c_str())) - return Status{Error::rpcINVALID_PARAMS, "malformedCurrency"}; + return Status{ + ClioError::rpcMALFORMED_CURRENCY, "malformedCurrency"}; key = ripple::keylet::line(*id1, *id2, currency).key; } @@ -306,22 +324,25 @@ doLedgerEntry(Context const& context) if (!request.at(JS(ticket)).is_object()) { if (!request.at(JS(ticket)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "ticketNotString"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "ticketNotString"}; if (!key.parseHex(request.at(JS(ticket)).as_string().c_str())) - return Status{Error::rpcINVALID_PARAMS, "malformedTicket"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "malformedTicket"}; } else if ( !request.at(JS(ticket)).as_object().contains(JS(owner)) || !request.at(JS(ticket)).as_object().at(JS(owner)).is_string()) { - return Status{Error::rpcINVALID_PARAMS, "malformedOwner"}; + return Status{RippledError::rpcINVALID_PARAMS, "malformedOwner"}; } else if ( !request.at(JS(ticket)).as_object().contains(JS(ticket_seq)) || !request.at(JS(ticket)).as_object().at(JS(ticket_seq)).is_int64()) { - return Status{Error::rpcINVALID_PARAMS, "malformedTicketSeq"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "malformedTicketSeq"}; } else { @@ -333,7 +354,8 @@ doLedgerEntry(Context const& context) .c_str()); if (!id) - return Status{Error::rpcINVALID_PARAMS, "malformedOwner"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "malformedOwner"}; else { std::uint32_t seq = request.at(JS(offer)) @@ -347,7 +369,7 @@ doLedgerEntry(Context const& context) } else { - return Status{Error::rpcINVALID_PARAMS, "unknownOption"}; + return Status{RippledError::rpcINVALID_PARAMS, "unknownOption"}; } auto dbResponse = diff --git a/src/rpc/handlers/LedgerRange.cpp b/src/rpc/handlers/LedgerRange.cpp index 34af8e77..c79779ba 100644 --- a/src/rpc/handlers/LedgerRange.cpp +++ b/src/rpc/handlers/LedgerRange.cpp @@ -12,7 +12,7 @@ doLedgerRange(Context const& context) auto range = context.backend->fetchLedgerRange(); if (!range) { - return Status{Error::rpcNOT_READY, "rangeNotFound"}; + return Status{RippledError::rpcNOT_READY, "rangeNotFound"}; } else { diff --git a/src/rpc/handlers/NFTInfo.cpp b/src/rpc/handlers/NFTInfo.cpp index da234f7c..9b200fbf 100644 --- a/src/rpc/handlers/NFTInfo.cpp +++ b/src/rpc/handlers/NFTInfo.cpp @@ -45,7 +45,8 @@ getURI(Backend::NFT const& dbResponse, Context const& context) if (!blob || blob->size() == 0) return Status{ - Error::rpcINTERNAL, "Cannot find NFTokenPage for this NFT"}; + RippledError::rpcINTERNAL, + "Cannot find NFTokenPage for this NFT"}; sle = ripple::STLedgerEntry( ripple::SerialIter{blob->data(), blob->size()}, nextKey); @@ -57,7 +58,7 @@ getURI(Backend::NFT const& dbResponse, Context const& context) if (!sle) return Status{ - Error::rpcINTERNAL, "Cannot find NFTokenPage for this NFT"}; + RippledError::rpcINTERNAL, "Cannot find NFTokenPage for this NFT"}; auto const nfts = sle->getFieldArray(ripple::sfNFTokens); auto const nft = std::find_if( @@ -70,7 +71,7 @@ getURI(Backend::NFT const& dbResponse, Context const& context) if (nft == nfts.end()) return Status{ - Error::rpcINTERNAL, "Cannot find NFTokenPage for this NFT"}; + RippledError::rpcINTERNAL, "Cannot find NFTokenPage for this NFT"}; ripple::Blob const uriField = nft->getFieldVL(ripple::sfURI); @@ -102,7 +103,7 @@ doNFTInfo(Context const& context) std::optional dbResponse = context.backend->fetchNFT(tokenID, lgrInfo.seq, context.yield); if (!dbResponse) - return Status{Error::rpcOBJECT_NOT_FOUND, "NFT not found"}; + return Status{RippledError::rpcOBJECT_NOT_FOUND, "NFT not found"}; response["nft_id"] = ripple::strHex(dbResponse->tokenID); response["ledger_index"] = dbResponse->ledgerSequence; diff --git a/src/rpc/handlers/NFTOffers.cpp b/src/rpc/handlers/NFTOffers.cpp index 2de2df13..08c9a940 100644 --- a/src/rpc/handlers/NFTOffers.cpp +++ b/src/rpc/handlers/NFTOffers.cpp @@ -56,7 +56,7 @@ enumerateNFTOffers( // TODO: just check for existence without pulling if (!context.backend->fetchLedgerObject( directory.key, lgrInfo.seq, context.yield)) - return Status{Error::rpcOBJECT_NOT_FOUND, "notFound"}; + return Status{RippledError::rpcOBJECT_NOT_FOUND, "notFound"}; std::uint32_t limit; if (auto const status = getLimit(context, limit); status) @@ -78,10 +78,10 @@ enumerateNFTOffers( auto const& marker(request.at(JS(marker))); if (!marker.is_string()) - return Status{Error::rpcINVALID_PARAMS, "markerNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "markerNotString"}; if (!cursor.parseHex(marker.as_string().c_str())) - return Status{Error::rpcINVALID_PARAMS, "malformedCursor"}; + return Status{RippledError::rpcINVALID_PARAMS, "malformedCursor"}; auto const sle = read(ripple::keylet::nftoffer(cursor), lgrInfo, context); @@ -90,7 +90,7 @@ enumerateNFTOffers( sle->getFieldU16(ripple::sfLedgerEntryType) != ripple::ltNFTOKEN_OFFER || tokenid != sle->getFieldH256(ripple::sfNFTokenID)) - return Status{Error::rpcINVALID_PARAMS}; + return Status{RippledError::rpcINVALID_PARAMS}; startHint = sle->getFieldU64(ripple::sfNFTokenOfferNode); jsonOffers.push_back(json::value_from(*sle)); diff --git a/src/rpc/handlers/NoRippleCheck.cpp b/src/rpc/handlers/NoRippleCheck.cpp index 17c72646..a4ecd585 100644 --- a/src/rpc/handlers/NoRippleCheck.cpp +++ b/src/rpc/handlers/NoRippleCheck.cpp @@ -31,7 +31,8 @@ doNoRippleCheck(Context const& context) if (role == "gateway") roleGateway = true; else if (role != "user") - return Status{Error::rpcINVALID_PARAMS, "role field is invalid"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "role field is invalid"}; } std::uint32_t limit = 300; diff --git a/src/rpc/handlers/ServerInfo.cpp b/src/rpc/handlers/ServerInfo.cpp index acdc61ca..0af32e10 100644 --- a/src/rpc/handlers/ServerInfo.cpp +++ b/src/rpc/handlers/ServerInfo.cpp @@ -16,7 +16,7 @@ doServerInfo(Context const& context) if (!range) { return Status{ - Error::rpcNOT_READY, + RippledError::rpcNOT_READY, "emptyDatabase", "The server has no data in the database"}; } @@ -27,7 +27,7 @@ doServerInfo(Context const& context) auto fees = context.backend->fetchFees(lgrInfo->seq, context.yield); if (!lgrInfo || !fees) - return Status{Error::rpcINTERNAL}; + return Status{RippledError::rpcINTERNAL}; auto age = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) diff --git a/src/rpc/handlers/Subscribe.cpp b/src/rpc/handlers/Subscribe.cpp index 05387155..1ce2fd76 100644 --- a/src/rpc/handlers/Subscribe.cpp +++ b/src/rpc/handlers/Subscribe.cpp @@ -22,10 +22,10 @@ validateStreams(boost::json::object const& request) auto const& stream : streams) { if (!stream.is_string()) - return Status{Error::rpcINVALID_PARAMS, "streamNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "streamNotString"}; if (!validCommonStreams.contains(stream.as_string().c_str())) - return Status{Error::rpcSTREAM_MALFORMED}; + return Status{RippledError::rpcSTREAM_MALFORMED}; } return OK; @@ -98,10 +98,10 @@ validateAccounts(boost::json::array const& accounts) for (auto const& account : accounts) { if (!account.is_string()) - return Status{Error::rpcINVALID_PARAMS, "accountNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "accountNotString"}; if (!accountFromStringStrict(account.as_string().c_str())) - return Status{Error::rpcACT_MALFORMED, "Account malformed."}; + return Status{RippledError::rpcACT_MALFORMED, "Account malformed."}; } return OK; @@ -212,7 +212,7 @@ validateAndGetBooks( std::shared_ptr const& backend) { if (!request.at(JS(books)).is_array()) - return Status{Error::rpcINVALID_PARAMS, "booksNotArray"}; + return Status{RippledError::rpcINVALID_PARAMS, "booksNotArray"}; boost::json::array const& books = request.at(JS(books)).as_array(); std::vector booksToSub; @@ -294,7 +294,7 @@ doSubscribe(Context const& context) if (request.contains(JS(streams))) { if (!request.at(JS(streams)).is_array()) - return Status{Error::rpcINVALID_PARAMS, "streamsNotArray"}; + return Status{RippledError::rpcINVALID_PARAMS, "streamsNotArray"}; auto status = validateStreams(request); @@ -306,11 +306,11 @@ doSubscribe(Context const& context) { auto const& jsonAccounts = request.at(JS(accounts)); if (!jsonAccounts.is_array()) - return Status{Error::rpcINVALID_PARAMS, "accountsNotArray"}; + return Status{RippledError::rpcINVALID_PARAMS, "accountsNotArray"}; auto const& accounts = jsonAccounts.as_array(); if (accounts.empty()) - return Status{Error::rpcACT_MALFORMED, "Account malformed."}; + return Status{RippledError::rpcACT_MALFORMED, "Account malformed."}; auto const status = validateAccounts(accounts); if (status) @@ -321,11 +321,12 @@ doSubscribe(Context const& context) { auto const& jsonAccounts = request.at(JS(accounts_proposed)); if (!jsonAccounts.is_array()) - return Status{Error::rpcINVALID_PARAMS, "accountsProposedNotArray"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "accountsProposedNotArray"}; auto const& accounts = jsonAccounts.as_array(); if (accounts.empty()) - return Status{Error::rpcACT_MALFORMED, "Account malformed."}; + return Status{RippledError::rpcACT_MALFORMED, "Account malformed."}; auto const status = validateAccounts(accounts); if (status) @@ -373,7 +374,7 @@ doUnsubscribe(Context const& context) if (request.contains(JS(streams))) { if (!request.at(JS(streams)).is_array()) - return Status{Error::rpcINVALID_PARAMS, "streamsNotArray"}; + return Status{RippledError::rpcINVALID_PARAMS, "streamsNotArray"}; auto status = validateStreams(request); @@ -385,11 +386,11 @@ doUnsubscribe(Context const& context) { auto const& jsonAccounts = request.at(JS(accounts)); if (!jsonAccounts.is_array()) - return Status{Error::rpcINVALID_PARAMS, "accountsNotArray"}; + return Status{RippledError::rpcINVALID_PARAMS, "accountsNotArray"}; auto const& accounts = jsonAccounts.as_array(); if (accounts.empty()) - return Status{Error::rpcACT_MALFORMED, "Account malformed."}; + return Status{RippledError::rpcACT_MALFORMED, "Account malformed."}; auto const status = validateAccounts(accounts); if (status) @@ -400,11 +401,12 @@ doUnsubscribe(Context const& context) { auto const& jsonAccounts = request.at(JS(accounts_proposed)); if (!jsonAccounts.is_array()) - return Status{Error::rpcINVALID_PARAMS, "accountsProposedNotArray"}; + return Status{ + RippledError::rpcINVALID_PARAMS, "accountsProposedNotArray"}; auto const& accounts = jsonAccounts.as_array(); if (accounts.empty()) - return Status{Error::rpcACT_MALFORMED, "Account malformed."}; + return Status{RippledError::rpcACT_MALFORMED, "Account malformed."}; auto const status = validateAccounts(accounts); if (status) diff --git a/src/rpc/handlers/TransactionEntry.cpp b/src/rpc/handlers/TransactionEntry.cpp index 3458cf34..c89638dd 100644 --- a/src/rpc/handlers/TransactionEntry.cpp +++ b/src/rpc/handlers/TransactionEntry.cpp @@ -14,7 +14,7 @@ doTransactionEntry(Context const& context) ripple::uint256 hash; if (!hash.parseHex(getRequiredString(context.params, JS(tx_hash)))) - return Status{Error::rpcINVALID_PARAMS, "malformedTransaction"}; + return Status{RippledError::rpcINVALID_PARAMS, "malformedTransaction"}; auto dbResponse = context.backend->fetchTransaction(hash, context.yield); // Note: transaction_entry is meant to only search a specified ledger for @@ -28,7 +28,7 @@ doTransactionEntry(Context const& context) // is in a different ledger than the one specified. if (!dbResponse || dbResponse->ledgerSequence != lgrInfo.seq) return Status{ - Error::rpcTXN_NOT_FOUND, + RippledError::rpcTXN_NOT_FOUND, "transactionNotFound", "Transaction not found."}; diff --git a/src/rpc/handlers/Tx.cpp b/src/rpc/handlers/Tx.cpp index 0549495d..2e59bedf 100644 --- a/src/rpc/handlers/Tx.cpp +++ b/src/rpc/handlers/Tx.cpp @@ -14,31 +14,31 @@ doTx(Context const& context) boost::json::object response = {}; if (!request.contains(JS(transaction))) - return Status{Error::rpcINVALID_PARAMS, "specifyTransaction"}; + return Status{RippledError::rpcINVALID_PARAMS, "specifyTransaction"}; if (!request.at(JS(transaction)).is_string()) - return Status{Error::rpcINVALID_PARAMS, "transactionNotString"}; + return Status{RippledError::rpcINVALID_PARAMS, "transactionNotString"}; ripple::uint256 hash; if (!hash.parseHex(request.at(JS(transaction)).as_string().c_str())) - return Status{Error::rpcINVALID_PARAMS, "malformedTransaction"}; + return Status{RippledError::rpcINVALID_PARAMS, "malformedTransaction"}; bool binary = false; if (request.contains(JS(binary))) { if (!request.at(JS(binary)).is_bool()) - return Status{Error::rpcINVALID_PARAMS, "binaryFlagNotBool"}; + return Status{RippledError::rpcINVALID_PARAMS, "binaryFlagNotBool"}; binary = request.at(JS(binary)).as_bool(); } auto range = context.backend->fetchLedgerRange(); if (!range) - return Status{Error::rpcNOT_READY}; + return Status{RippledError::rpcNOT_READY}; auto dbResponse = context.backend->fetchTransaction(hash, context.yield); if (!dbResponse) - return Status{Error::rpcTXN_NOT_FOUND}; + return Status{RippledError::rpcTXN_NOT_FOUND}; if (!binary) { diff --git a/src/webserver/HttpBase.h b/src/webserver/HttpBase.h index 3fdf7cb4..9dd1285a 100644 --- a/src/webserver/HttpBase.h +++ b/src/webserver/HttpBase.h @@ -270,7 +270,7 @@ public: res.set(http::field::content_type, "application/json"); res.keep_alive(req_.keep_alive()); res.body() = boost::json::serialize( - RPC::make_error(RPC::Error::rpcTOO_BUSY)); + RPC::makeError(RPC::RippledError::rpcTOO_BUSY)); res.prepare_payload(); lambda_(std::move(res)); } @@ -375,7 +375,7 @@ handle_request( http::status::ok, "application/json", boost::json::serialize( - RPC::make_error(RPC::Error::rpcBAD_SYNTAX)))); + RPC::makeError(RPC::RippledError::rpcBAD_SYNTAX)))); } auto range = backend->fetchLedgerRange(); @@ -384,7 +384,7 @@ handle_request( http::status::ok, "application/json", boost::json::serialize( - RPC::make_error(RPC::Error::rpcNOT_READY)))); + RPC::makeError(RPC::RippledError::rpcNOT_READY)))); std::optional context = RPC::make_HttpContext( yc, @@ -403,7 +403,7 @@ handle_request( http::status::ok, "application/json", boost::json::serialize( - RPC::make_error(RPC::Error::rpcBAD_SYNTAX)))); + RPC::makeError(RPC::RippledError::rpcBAD_SYNTAX)))); boost::json::object response{{"result", boost::json::object{}}}; boost::json::object& result = response["result"].as_object(); @@ -418,7 +418,7 @@ handle_request( if (auto status = std::get_if(&v)) { counters.rpcErrored(context->method); - auto error = RPC::make_error(*status); + auto error = RPC::makeError(*status); error["request"] = request; result = error; @@ -438,16 +438,16 @@ handle_request( } boost::json::array warnings; - warnings.emplace_back(RPC::make_warning(RPC::warnRPC_CLIO)); + warnings.emplace_back(RPC::makeWarning(RPC::warnRPC_CLIO)); auto lastCloseAge = context->etl->lastCloseAgeSeconds(); if (lastCloseAge >= 60) - warnings.emplace_back(RPC::make_warning(RPC::warnRPC_OUTDATED)); + warnings.emplace_back(RPC::makeWarning(RPC::warnRPC_OUTDATED)); response["warnings"] = warnings; responseStr = boost::json::serialize(response); if (!dosGuard.add(ip, responseStr.size())) { response["warning"] = "load"; - warnings.emplace_back(RPC::make_warning(RPC::warnRPC_RATE_LIMIT)); + warnings.emplace_back(RPC::makeWarning(RPC::warnRPC_RATE_LIMIT)); response["warnings"] = warnings; // reserialize when we need to include this warning responseStr = boost::json::serialize(response); @@ -462,7 +462,8 @@ handle_request( return send(httpResponse( http::status::internal_server_error, "application/json", - boost::json::serialize(RPC::make_error(RPC::Error::rpcINTERNAL)))); + boost::json::serialize( + RPC::makeError(RPC::RippledError::rpcINTERNAL)))); } } diff --git a/src/webserver/WsBase.h b/src/webserver/WsBase.h index ceaa72de..6e902299 100644 --- a/src/webserver/WsBase.h +++ b/src/webserver/WsBase.h @@ -278,7 +278,7 @@ public: boost::json::object response = {}; auto sendError = [this, &request, id](auto error) { - auto e = RPC::make_error(error); + auto e = RPC::makeError(error); if (!id.is_null()) e["id"] = id; e["request"] = request; @@ -292,7 +292,7 @@ public: auto range = backend_->fetchLedgerRange(); if (!range) - return sendError(RPC::Error::rpcNOT_READY); + return sendError(RPC::RippledError::rpcNOT_READY); std::optional context = RPC::make_WsContext( yield, @@ -311,7 +311,7 @@ public: { BOOST_LOG_TRIVIAL(warning) << tag() << " could not create RPC context"; - return sendError(RPC::Error::rpcBAD_SYNTAX); + return sendError(RPC::RippledError::rpcBAD_SYNTAX); } response = getDefaultWsResponse(id); @@ -327,7 +327,7 @@ public: { counters_.rpcErrored(context->method); - auto error = RPC::make_error(*status); + auto error = RPC::makeError(*status); if (!id.is_null()) error["id"] = id; @@ -347,22 +347,22 @@ public: BOOST_LOG_TRIVIAL(error) << tag() << __func__ << " caught exception : " << e.what(); - return sendError(RPC::Error::rpcINTERNAL); + return sendError(RPC::RippledError::rpcINTERNAL); } boost::json::array warnings; - warnings.emplace_back(RPC::make_warning(RPC::warnRPC_CLIO)); + warnings.emplace_back(RPC::makeWarning(RPC::warnRPC_CLIO)); auto lastCloseAge = etl_->lastCloseAgeSeconds(); if (lastCloseAge >= 60) - warnings.emplace_back(RPC::make_warning(RPC::warnRPC_OUTDATED)); + warnings.emplace_back(RPC::makeWarning(RPC::warnRPC_OUTDATED)); response["warnings"] = warnings; std::string responseStr = boost::json::serialize(response); if (!dosGuard_.add(*ip, responseStr.size())) { response["warning"] = "load"; - warnings.emplace_back(RPC::make_warning(RPC::warnRPC_RATE_LIMIT)); + warnings.emplace_back(RPC::makeWarning(RPC::warnRPC_RATE_LIMIT)); response["warnings"] = warnings; // reserialize if we need to include this warning responseStr = boost::json::serialize(response); @@ -392,7 +392,7 @@ public: auto error, boost::json::value const& id, boost::json::object const& request) { - auto e = RPC::make_error(error); + auto e = RPC::makeError(error); if (!id.is_null()) e["id"] = id; @@ -417,14 +417,15 @@ public: boost::json::object request; if (!raw.is_object()) - return sendError(RPC::Error::rpcINVALID_PARAMS, nullptr, request); + return sendError( + RPC::RippledError::rpcINVALID_PARAMS, nullptr, request); request = raw.as_object(); auto id = request.contains("id") ? request.at("id") : nullptr; if (!dosGuard_.isOk(*ip)) { - sendError(RPC::Error::rpcSLOW_DOWN, id, request); + sendError(RPC::RippledError::rpcSLOW_DOWN, id, request); } else { @@ -438,7 +439,7 @@ public: shared_this->handle_request(std::move(r), id, yield); }, dosGuard_.isWhiteListed(*ip))) - sendError(RPC::Error::rpcTOO_BUSY, id, request); + sendError(RPC::RippledError::rpcTOO_BUSY, id, request); } do_read(); diff --git a/unittests/RPCErrors.cpp b/unittests/RPCErrors.cpp new file mode 100644 index 00000000..805eb0f8 --- /dev/null +++ b/unittests/RPCErrors.cpp @@ -0,0 +1,133 @@ +#include + +#include +#include + +using namespace RPC; +using namespace std; + +namespace { +void +check( + boost::json::object const& j, + std::string_view error, + uint32_t errorCode, + std::string_view errorMessage) +{ + EXPECT_TRUE(j.contains("error")); + EXPECT_TRUE(j.contains("error_code")); + EXPECT_TRUE(j.contains("error_message")); + EXPECT_TRUE(j.contains("status")); + EXPECT_TRUE(j.contains("type")); + + EXPECT_TRUE(j.at("error").is_string()); + EXPECT_TRUE(j.at("error_code").is_uint64()); + EXPECT_TRUE(j.at("error_message").is_string()); + EXPECT_TRUE(j.at("status").is_string()); + EXPECT_TRUE(j.at("type").is_string()); + + EXPECT_STREQ(j.at("status").as_string().c_str(), "error"); + EXPECT_STREQ(j.at("type").as_string().c_str(), "response"); + + EXPECT_STREQ(j.at("error").as_string().c_str(), error.data()); + EXPECT_EQ(j.at("error_code").as_uint64(), errorCode); + EXPECT_STREQ( + j.at("error_message").as_string().c_str(), errorMessage.data()); +} +} // namespace + +TEST(RPCErrorsTest, StatusAsBool) +{ + // Only rpcSUCCESS status should return false + EXPECT_FALSE(Status{RippledError::rpcSUCCESS}); + + // true should be returned for any error state, we just test a few + CombinedError const errors[]{ + RippledError::rpcINVALID_PARAMS, + RippledError::rpcUNKNOWN_COMMAND, + RippledError::rpcTOO_BUSY, + RippledError::rpcNO_NETWORK, + RippledError::rpcACT_MALFORMED, + RippledError::rpcBAD_MARKET, + ClioError::rpcMALFORMED_CURRENCY, + }; + + for (auto const& ec : errors) + EXPECT_TRUE(Status{ec}); +} + +TEST(RPCErrorsTest, SuccessToJSON) +{ + auto const status = Status{RippledError::rpcSUCCESS}; + check(makeError(status), "unknown", 0, "An unknown error code."); +} + +TEST(RPCErrorsTest, RippledErrorToJSON) +{ + auto const status = Status{RippledError::rpcINVALID_PARAMS}; + check(makeError(status), "invalidParams", 31, "Invalid parameters."); +} + +TEST(RPCErrorsTest, RippledErrorFromStringToJSON) +{ + auto const j = makeError(Status{"veryCustomError"}); + EXPECT_STREQ(j.at("error").as_string().c_str(), "veryCustomError"); +} + +TEST(RPCErrorsTest, RippledErrorToJSONCustomMessage) +{ + auto const status = Status{RippledError::rpcINVALID_PARAMS, "custom"}; + check(makeError(status), "invalidParams", 31, "custom"); +} + +TEST(RPCErrorsTest, RippledErrorToJSONCustomStrCodeAndMessage) +{ + auto const status = + Status{RippledError::rpcINVALID_PARAMS, "customCode", "customMessage"}; + check(makeError(status), "customCode", 31, "customMessage"); +} + +TEST(RPCErrorsTest, ClioErrorToJSON) +{ + auto const status = Status{ClioError::rpcMALFORMED_CURRENCY}; + check(makeError(status), "malformedCurrency", 5000, "Malformed currency."); +} + +TEST(RPCErrorsTest, ClioErrorToJSONCustomMessage) +{ + auto const status = Status{ClioError::rpcMALFORMED_CURRENCY, "custom"}; + check(makeError(status), "malformedCurrency", 5000, "custom"); +} + +TEST(RPCErrorsTest, ClioErrorToJSONCustomStrCodeAndMessage) +{ + auto const status = + Status{ClioError::rpcMALFORMED_CURRENCY, "customCode", "customMessage"}; + check(makeError(status), "customCode", 5000, "customMessage"); +} + +TEST(RPCErrorsTest, InvalidClioErrorToJSON) +{ + EXPECT_ANY_THROW((void)makeError(static_cast(999999))); +} + +TEST(RPCErrorsTest, WarningToJSON) +{ + auto j = makeWarning(WarningCode::warnRPC_OUTDATED); + EXPECT_TRUE(j.contains("id")); + EXPECT_TRUE(j.contains("message")); + + EXPECT_TRUE(j.at("id").is_int64()); + EXPECT_TRUE(j.at("message").is_string()); + + EXPECT_EQ( + j.at("id").as_int64(), + static_cast(WarningCode::warnRPC_OUTDATED)); + EXPECT_STREQ( + j.at("message").as_string().c_str(), "This server may be out of date"); +} + +TEST(RPCErrorsTest, InvalidWarningToJSON) +{ + EXPECT_ANY_THROW((void)makeWarning(static_cast(999999))); +}