mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-20 03:35:55 +00:00
Add error code extension mechanism and use malformed currency code (#396)
Fixes #275
This commit is contained in:
@@ -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
|
find src unittests -type f \( -name '*.cpp' -o -name '*.h' -o -name '*.ipp' \) -print0 | xargs -0 clang-format -i
|
||||||
|
|
||||||
# check how many lines differ
|
# check how many lines differ
|
||||||
lines=$(git diff | wc -l)
|
lines=$(git diff src unittests | wc -l)
|
||||||
|
|
||||||
# check if there is any updated files
|
# check if there is any updated files
|
||||||
if [ "$lines" != "0" ]; then
|
if [ "$lines" != "0" ]; then
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ target_sources(clio PRIVATE
|
|||||||
## Subscriptions
|
## Subscriptions
|
||||||
src/subscriptions/SubscriptionManager.cpp
|
src/subscriptions/SubscriptionManager.cpp
|
||||||
## RPC
|
## RPC
|
||||||
|
src/rpc/Errors.cpp
|
||||||
src/rpc/RPC.cpp
|
src/rpc/RPC.cpp
|
||||||
src/rpc/RPCHelpers.cpp
|
src/rpc/RPCHelpers.cpp
|
||||||
src/rpc/Counters.cpp
|
src/rpc/Counters.cpp
|
||||||
@@ -107,7 +108,8 @@ add_executable(clio_server src/main/main.cpp)
|
|||||||
target_link_libraries(clio_server PUBLIC clio)
|
target_link_libraries(clio_server PUBLIC clio)
|
||||||
|
|
||||||
if(BUILD_TESTS)
|
if(BUILD_TESTS)
|
||||||
add_executable(clio_tests
|
add_executable(clio_tests
|
||||||
|
unittests/RPCErrors.cpp
|
||||||
unittests/config.cpp
|
unittests/config.cpp
|
||||||
unittests/main.cpp)
|
unittests/main.cpp)
|
||||||
include(CMake/deps/gtest.cmake)
|
include(CMake/deps/gtest.cmake)
|
||||||
|
|||||||
135
src/rpc/Errors.cpp
Normal file
135
src/rpc/Errors.cpp
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
#include <rpc/Errors.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template <class... Ts>
|
||||||
|
struct overloadSet : Ts...
|
||||||
|
{
|
||||||
|
using Ts::operator()...;
|
||||||
|
};
|
||||||
|
|
||||||
|
// explicit deduction guide (not needed as of C++20, but clang be clang)
|
||||||
|
template <class... Ts>
|
||||||
|
overloadSet(Ts...) -> overloadSet<Ts...>;
|
||||||
|
} // 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<string>(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<string_view> customError,
|
||||||
|
optional<string_view> 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<uint32_t>(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<string_view> customError,
|
||||||
|
optional<string_view> customMessage)
|
||||||
|
{
|
||||||
|
boost::json::object json;
|
||||||
|
auto const& info = getErrorInfo(err);
|
||||||
|
|
||||||
|
json["error"] = customError.value_or(info.error).data();
|
||||||
|
json["error_code"] = static_cast<uint32_t>(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
|
||||||
206
src/rpc/Errors.h
Normal file
206
src/rpc/Errors.h
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
#ifndef REPORTING_RPC_ERRORS_H_INCLUDED
|
||||||
|
#define REPORTING_RPC_ERRORS_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/protocol/ErrorCodes.h>
|
||||||
|
|
||||||
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
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<RippledError, ClioError>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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<RippledError>(&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<std::string_view> customError = std::nullopt,
|
||||||
|
std::optional<std::string_view> 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<std::string_view> customError = std::nullopt,
|
||||||
|
std::optional<std::string_view> customMessage = std::nullopt);
|
||||||
|
|
||||||
|
} // namespace RPC
|
||||||
|
|
||||||
|
#endif // REPORTING_RPC_ERRORS_H_INCLUDED
|
||||||
188
src/rpc/RPC.cpp
188
src/rpc/RPC.cpp
@@ -8,22 +8,24 @@
|
|||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
namespace RPC {
|
namespace RPC {
|
||||||
|
|
||||||
Context::Context(
|
Context::Context(
|
||||||
boost::asio::yield_context& yield_,
|
boost::asio::yield_context& yield_,
|
||||||
std::string const& command_,
|
string const& command_,
|
||||||
std::uint32_t version_,
|
uint32_t version_,
|
||||||
boost::json::object const& params_,
|
boost::json::object const& params_,
|
||||||
std::shared_ptr<BackendInterface const> const& backend_,
|
shared_ptr<BackendInterface const> const& backend_,
|
||||||
std::shared_ptr<SubscriptionManager> const& subscriptions_,
|
shared_ptr<SubscriptionManager> const& subscriptions_,
|
||||||
std::shared_ptr<ETLLoadBalancer> const& balancer_,
|
shared_ptr<ETLLoadBalancer> const& balancer_,
|
||||||
std::shared_ptr<ReportingETL const> const& etl_,
|
shared_ptr<ReportingETL const> const& etl_,
|
||||||
std::shared_ptr<WsBase> const& session_,
|
shared_ptr<WsBase> const& session_,
|
||||||
util::TagDecoratorFactory const& tagFactory_,
|
util::TagDecoratorFactory const& tagFactory_,
|
||||||
Backend::LedgerRange const& range_,
|
Backend::LedgerRange const& range_,
|
||||||
Counters& counters_,
|
Counters& counters_,
|
||||||
std::string const& clientIp_)
|
string const& clientIp_)
|
||||||
: Taggable(tagFactory_)
|
: Taggable(tagFactory_)
|
||||||
, yield(yield_)
|
, yield(yield_)
|
||||||
, method(command_)
|
, method(command_)
|
||||||
@@ -41,19 +43,19 @@ Context::Context(
|
|||||||
BOOST_LOG_TRIVIAL(debug) << tag() << "new Context created";
|
BOOST_LOG_TRIVIAL(debug) << tag() << "new Context created";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Context>
|
optional<Context>
|
||||||
make_WsContext(
|
make_WsContext(
|
||||||
boost::asio::yield_context& yc,
|
boost::asio::yield_context& yc,
|
||||||
boost::json::object const& request,
|
boost::json::object const& request,
|
||||||
std::shared_ptr<BackendInterface const> const& backend,
|
shared_ptr<BackendInterface const> const& backend,
|
||||||
std::shared_ptr<SubscriptionManager> const& subscriptions,
|
shared_ptr<SubscriptionManager> const& subscriptions,
|
||||||
std::shared_ptr<ETLLoadBalancer> const& balancer,
|
shared_ptr<ETLLoadBalancer> const& balancer,
|
||||||
std::shared_ptr<ReportingETL const> const& etl,
|
shared_ptr<ReportingETL const> const& etl,
|
||||||
std::shared_ptr<WsBase> const& session,
|
shared_ptr<WsBase> const& session,
|
||||||
util::TagDecoratorFactory const& tagFactory,
|
util::TagDecoratorFactory const& tagFactory,
|
||||||
Backend::LedgerRange const& range,
|
Backend::LedgerRange const& range,
|
||||||
Counters& counters,
|
Counters& counters,
|
||||||
std::string const& clientIp)
|
string const& clientIp)
|
||||||
{
|
{
|
||||||
boost::json::value commandValue = nullptr;
|
boost::json::value commandValue = nullptr;
|
||||||
if (!request.contains("command") && request.contains("method"))
|
if (!request.contains("command") && request.contains("method"))
|
||||||
@@ -64,9 +66,9 @@ make_WsContext(
|
|||||||
if (!commandValue.is_string())
|
if (!commandValue.is_string())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
std::string command = commandValue.as_string().c_str();
|
string command = commandValue.as_string().c_str();
|
||||||
|
|
||||||
return std::make_optional<Context>(
|
return make_optional<Context>(
|
||||||
yc,
|
yc,
|
||||||
command,
|
command,
|
||||||
1,
|
1,
|
||||||
@@ -82,23 +84,23 @@ make_WsContext(
|
|||||||
clientIp);
|
clientIp);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Context>
|
optional<Context>
|
||||||
make_HttpContext(
|
make_HttpContext(
|
||||||
boost::asio::yield_context& yc,
|
boost::asio::yield_context& yc,
|
||||||
boost::json::object const& request,
|
boost::json::object const& request,
|
||||||
std::shared_ptr<BackendInterface const> const& backend,
|
shared_ptr<BackendInterface const> const& backend,
|
||||||
std::shared_ptr<SubscriptionManager> const& subscriptions,
|
shared_ptr<SubscriptionManager> const& subscriptions,
|
||||||
std::shared_ptr<ETLLoadBalancer> const& balancer,
|
shared_ptr<ETLLoadBalancer> const& balancer,
|
||||||
std::shared_ptr<ReportingETL const> const& etl,
|
shared_ptr<ReportingETL const> const& etl,
|
||||||
util::TagDecoratorFactory const& tagFactory,
|
util::TagDecoratorFactory const& tagFactory,
|
||||||
Backend::LedgerRange const& range,
|
Backend::LedgerRange const& range,
|
||||||
RPC::Counters& counters,
|
RPC::Counters& counters,
|
||||||
std::string const& clientIp)
|
string const& clientIp)
|
||||||
{
|
{
|
||||||
if (!request.contains("method") || !request.at("method").is_string())
|
if (!request.contains("method") || !request.at("method").is_string())
|
||||||
return {};
|
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")
|
if (command == "subscribe" || command == "unsubscribe")
|
||||||
return {};
|
return {};
|
||||||
@@ -114,7 +116,7 @@ make_HttpContext(
|
|||||||
if (!array.at(0).is_object())
|
if (!array.at(0).is_object())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
return std::make_optional<Context>(
|
return make_optional<Context>(
|
||||||
yc,
|
yc,
|
||||||
command,
|
command,
|
||||||
1,
|
1,
|
||||||
@@ -130,107 +132,38 @@ make_HttpContext(
|
|||||||
clientIp);
|
clientIp);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr static WarningInfo warningInfos[]{
|
using LimitRange = tuple<uint32_t, uint32_t, uint32_t>;
|
||||||
{warnUNKNOWN, "Unknown warning"},
|
using HandlerFunction = function<Result(Context const&)>;
|
||||||
{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<std::string>(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<std::uint32_t>(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<std::uint32_t>(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<std::uint32_t, std::uint32_t, std::uint32_t>;
|
|
||||||
using HandlerFunction = std::function<Result(Context const&)>;
|
|
||||||
|
|
||||||
struct Handler
|
struct Handler
|
||||||
{
|
{
|
||||||
std::string method;
|
string method;
|
||||||
std::function<Result(Context const&)> handler;
|
function<Result(Context const&)> handler;
|
||||||
std::optional<LimitRange> limit;
|
optional<LimitRange> limit;
|
||||||
bool isClioOnly = false;
|
bool isClioOnly = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HandlerTable
|
class HandlerTable
|
||||||
{
|
{
|
||||||
std::unordered_map<std::string, Handler> handlerMap_;
|
unordered_map<string, Handler> handlerMap_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HandlerTable(std::initializer_list<Handler> handlers)
|
HandlerTable(initializer_list<Handler> handlers)
|
||||||
{
|
{
|
||||||
for (auto const& handler : handlers)
|
for (auto const& handler : handlers)
|
||||||
{
|
{
|
||||||
handlerMap_[handler.method] = std::move(handler);
|
handlerMap_[handler.method] = move(handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
contains(std::string const& method)
|
contains(string const& method)
|
||||||
{
|
{
|
||||||
return handlerMap_.contains(method);
|
return handlerMap_.contains(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<LimitRange>
|
optional<LimitRange>
|
||||||
getLimitRange(std::string const& command)
|
getLimitRange(string const& command)
|
||||||
{
|
{
|
||||||
if (!handlerMap_.contains(command))
|
if (!handlerMap_.contains(command))
|
||||||
return {};
|
return {};
|
||||||
@@ -238,8 +171,8 @@ public:
|
|||||||
return handlerMap_[command].limit;
|
return handlerMap_[command].limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<HandlerFunction>
|
optional<HandlerFunction>
|
||||||
getHandler(std::string const& command)
|
getHandler(string const& command)
|
||||||
{
|
{
|
||||||
if (!handlerMap_.contains(command))
|
if (!handlerMap_.contains(command))
|
||||||
return {};
|
return {};
|
||||||
@@ -248,7 +181,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
isClioOnly(std::string const& command)
|
isClioOnly(string const& command)
|
||||||
{
|
{
|
||||||
return handlerMap_.contains(command) && handlerMap_[command].isClioOnly;
|
return handlerMap_.contains(command) && handlerMap_[command].isClioOnly;
|
||||||
}
|
}
|
||||||
@@ -282,7 +215,7 @@ static HandlerTable handlerTable{
|
|||||||
{"transaction_entry", &doTransactionEntry, {}},
|
{"transaction_entry", &doTransactionEntry, {}},
|
||||||
{"random", &doRandom, {}}};
|
{"random", &doRandom, {}}};
|
||||||
|
|
||||||
static std::unordered_set<std::string> forwardCommands{
|
static unordered_set<string> forwardCommands{
|
||||||
"submit",
|
"submit",
|
||||||
"submit_multisigned",
|
"submit_multisigned",
|
||||||
"fee",
|
"fee",
|
||||||
@@ -294,13 +227,13 @@ static std::unordered_set<std::string> forwardCommands{
|
|||||||
"channel_verify"};
|
"channel_verify"};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
validHandler(std::string const& method)
|
validHandler(string const& method)
|
||||||
{
|
{
|
||||||
return handlerTable.contains(method) || forwardCommands.contains(method);
|
return handlerTable.contains(method) || forwardCommands.contains(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
isClioOnly(std::string const& method)
|
isClioOnly(string const& method)
|
||||||
{
|
{
|
||||||
return handlerTable.isClioOnly(method);
|
return handlerTable.isClioOnly(method);
|
||||||
}
|
}
|
||||||
@@ -313,27 +246,28 @@ shouldSuppressValidatedFlag(RPC::Context const& context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Status
|
Status
|
||||||
getLimit(RPC::Context const& context, std::uint32_t& limit)
|
getLimit(RPC::Context const& context, uint32_t& limit)
|
||||||
{
|
{
|
||||||
if (!handlerTable.getHandler(context.method))
|
if (!handlerTable.getHandler(context.method))
|
||||||
return Status{Error::rpcUNKNOWN_COMMAND};
|
return Status{RippledError::rpcUNKNOWN_COMMAND};
|
||||||
|
|
||||||
if (!handlerTable.getLimitRange(context.method))
|
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);
|
auto [lo, def, hi] = *handlerTable.getLimitRange(context.method);
|
||||||
|
|
||||||
if (context.params.contains(JS(limit)))
|
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())
|
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();
|
int input = context.params.at(JS(limit)).as_int64();
|
||||||
if (input <= 0)
|
if (input <= 0)
|
||||||
return Status{Error::rpcINVALID_PARAMS, errMsg};
|
return Status{RippledError::rpcINVALID_PARAMS, errMsg};
|
||||||
|
|
||||||
limit = std::clamp(static_cast<std::uint32_t>(input), lo, hi);
|
limit = clamp(static_cast<uint32_t>(input), lo, hi);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -378,7 +312,7 @@ buildResponse(Context const& ctx)
|
|||||||
ctx.counters.rpcForwarded(ctx.method);
|
ctx.counters.rpcForwarded(ctx.method);
|
||||||
|
|
||||||
if (!res)
|
if (!res)
|
||||||
return Status{Error::rpcFAILED_TO_FORWARD};
|
return Status{RippledError::rpcFAILED_TO_FORWARD};
|
||||||
|
|
||||||
if (res->contains("result") && res->at("result").is_object())
|
if (res->contains("result") && res->at("result").is_object())
|
||||||
return res->at("result").as_object();
|
return res->at("result").as_object();
|
||||||
@@ -393,13 +327,13 @@ buildResponse(Context const& ctx)
|
|||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(error)
|
BOOST_LOG_TRIVIAL(error)
|
||||||
<< __func__ << " Database is too busy. Rejecting request";
|
<< __func__ << " Database is too busy. Rejecting request";
|
||||||
return Status{Error::rpcTOO_BUSY};
|
return Status{RippledError::rpcTOO_BUSY};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto method = handlerTable.getHandler(ctx.method);
|
auto method = handlerTable.getHandler(ctx.method);
|
||||||
|
|
||||||
if (!method)
|
if (!method)
|
||||||
return Status{Error::rpcUNKNOWN_COMMAND};
|
return Status{RippledError::rpcUNKNOWN_COMMAND};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -411,7 +345,7 @@ buildResponse(Context const& ctx)
|
|||||||
<< ctx.tag() << __func__ << " finish executing rpc `" << ctx.method
|
<< ctx.tag() << __func__ << " finish executing rpc `" << ctx.method
|
||||||
<< '`';
|
<< '`';
|
||||||
|
|
||||||
if (auto object = std::get_if<boost::json::object>(&v);
|
if (auto object = get_if<boost::json::object>(&v);
|
||||||
object && not shouldSuppressValidatedFlag(ctx))
|
object && not shouldSuppressValidatedFlag(ctx))
|
||||||
{
|
{
|
||||||
(*object)["validated"] = true;
|
(*object)["validated"] = true;
|
||||||
@@ -421,22 +355,22 @@ buildResponse(Context const& ctx)
|
|||||||
}
|
}
|
||||||
catch (InvalidParamsError const& err)
|
catch (InvalidParamsError const& err)
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, err.what()};
|
return Status{RippledError::rpcINVALID_PARAMS, err.what()};
|
||||||
}
|
}
|
||||||
catch (AccountNotFoundError const& err)
|
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)
|
catch (Backend::DatabaseTimeout const& t)
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(error) << __func__ << " Database timeout";
|
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)
|
BOOST_LOG_TRIVIAL(error)
|
||||||
<< ctx.tag() << __func__ << " caught exception : " << err.what();
|
<< ctx.tag() << __func__ << " caught exception : " << err.what();
|
||||||
return Status{Error::rpcINTERNAL};
|
return Status{RippledError::rpcINTERNAL};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
103
src/rpc/RPC.h
103
src/rpc/RPC.h
@@ -1,7 +1,7 @@
|
|||||||
#ifndef REPORTING_RPC_H_INCLUDED
|
#ifndef REPORTING_RPC_H_INCLUDED
|
||||||
#define REPORTING_RPC_H_INCLUDED
|
#define REPORTING_RPC_H_INCLUDED
|
||||||
|
|
||||||
#include <ripple/protocol/ErrorCodes.h>
|
#include <rpc/Errors.h>
|
||||||
|
|
||||||
#include <boost/asio/spawn.hpp>
|
#include <boost/asio/spawn.hpp>
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
@@ -65,7 +65,6 @@ struct Context : public util::Taggable
|
|||||||
Counters& counters_,
|
Counters& counters_,
|
||||||
std::string const& clientIp_);
|
std::string const& clientIp_);
|
||||||
};
|
};
|
||||||
using Error = ripple::error_code_i;
|
|
||||||
|
|
||||||
struct AccountCursor
|
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<Status, boost::json::object>;
|
using Result = std::variant<Status, boost::json::object>;
|
||||||
|
|
||||||
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<Context>
|
std::optional<Context>
|
||||||
make_WsContext(
|
make_WsContext(
|
||||||
boost::asio::yield_context& yc,
|
boost::asio::yield_context& yc,
|
||||||
|
|||||||
@@ -187,10 +187,10 @@ getHexMarker(boost::json::object const& request, ripple::uint256& marker)
|
|||||||
if (request.contains(JS(marker)))
|
if (request.contains(JS(marker)))
|
||||||
{
|
{
|
||||||
if (!request.at(JS(marker)).is_string())
|
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()))
|
if (!marker.parseHex(request.at(JS(marker)).as_string().c_str()))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedMarker"};
|
return Status{RippledError::rpcINVALID_PARAMS, "malformedMarker"};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@@ -207,14 +207,14 @@ getAccount(
|
|||||||
{
|
{
|
||||||
if (required)
|
if (required)
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINVALID_PARAMS, field.to_string() + "Missing"};
|
RippledError::rpcINVALID_PARAMS, field.to_string() + "Missing"};
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!request.at(field).is_string())
|
if (!request.at(field).is_string())
|
||||||
return Status{
|
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());
|
if (auto a = accountFromStringStrict(request.at(field).as_string().c_str());
|
||||||
a)
|
a)
|
||||||
@@ -223,7 +223,8 @@ getAccount(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return Status{Error::rpcACT_MALFORMED, field.to_string() + "Malformed"};
|
return Status{
|
||||||
|
RippledError::rpcACT_MALFORMED, field.to_string() + "Malformed"};
|
||||||
}
|
}
|
||||||
|
|
||||||
Status
|
Status
|
||||||
@@ -240,7 +241,7 @@ getOptionalAccount(
|
|||||||
|
|
||||||
if (!request.at(field).is_string())
|
if (!request.at(field).is_string())
|
||||||
return Status{
|
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());
|
if (auto a = accountFromStringStrict(request.at(field).as_string().c_str());
|
||||||
a)
|
a)
|
||||||
@@ -249,7 +250,8 @@ getOptionalAccount(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, field.to_string() + "Malformed"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, field.to_string() + "Malformed"};
|
||||||
}
|
}
|
||||||
|
|
||||||
Status
|
Status
|
||||||
@@ -286,13 +288,13 @@ Status
|
|||||||
getChannelId(boost::json::object const& request, ripple::uint256& channelId)
|
getChannelId(boost::json::object const& request, ripple::uint256& channelId)
|
||||||
{
|
{
|
||||||
if (!request.contains(JS(channel_id)))
|
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())
|
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()))
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -554,16 +556,18 @@ ledgerInfoFromRequest(Context const& ctx)
|
|||||||
if (!hashValue.is_null())
|
if (!hashValue.is_null())
|
||||||
{
|
{
|
||||||
if (!hashValue.is_string())
|
if (!hashValue.is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "ledgerHashNotString"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "ledgerHashNotString"};
|
||||||
|
|
||||||
ripple::uint256 ledgerHash;
|
ripple::uint256 ledgerHash;
|
||||||
if (!ledgerHash.parseHex(hashValue.as_string().c_str()))
|
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);
|
auto lgrInfo = ctx.backend->fetchLedgerByHash(ledgerHash, ctx.yield);
|
||||||
|
|
||||||
if (!lgrInfo || lgrInfo->seq > ctx.range.maxSequence)
|
if (!lgrInfo || lgrInfo->seq > ctx.range.maxSequence)
|
||||||
return Status{Error::rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
return Status{RippledError::rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
||||||
|
|
||||||
return *lgrInfo;
|
return *lgrInfo;
|
||||||
}
|
}
|
||||||
@@ -592,13 +596,13 @@ ledgerInfoFromRequest(Context const& ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ledgerSequence)
|
if (!ledgerSequence)
|
||||||
return Status{Error::rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
return Status{RippledError::rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
||||||
|
|
||||||
auto lgrInfo =
|
auto lgrInfo =
|
||||||
ctx.backend->fetchLedgerBySequence(*ledgerSequence, ctx.yield);
|
ctx.backend->fetchLedgerBySequence(*ledgerSequence, ctx.yield);
|
||||||
|
|
||||||
if (!lgrInfo || lgrInfo->seq > ctx.range.maxSequence)
|
if (!lgrInfo || lgrInfo->seq > ctx.range.maxSequence)
|
||||||
return Status{Error::rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
return Status{RippledError::rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
||||||
|
|
||||||
return *lgrInfo;
|
return *lgrInfo;
|
||||||
}
|
}
|
||||||
@@ -651,7 +655,7 @@ traverseOwnedNodes(
|
|||||||
{
|
{
|
||||||
if (!backend.fetchLedgerObject(
|
if (!backend.fetchLedgerObject(
|
||||||
ripple::keylet::account(accountID).key, sequence, yield))
|
ripple::keylet::account(accountID).key, sequence, yield))
|
||||||
return Status{Error::rpcACT_NOT_FOUND};
|
return Status{RippledError::rpcACT_NOT_FOUND};
|
||||||
|
|
||||||
auto parsedCursor =
|
auto parsedCursor =
|
||||||
parseAccountCursor(backend, sequence, jsonCursor, accountID, yield);
|
parseAccountCursor(backend, sequence, jsonCursor, accountID, yield);
|
||||||
@@ -891,12 +895,12 @@ keypairFromRequst(boost::json::object const& request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missing field secret"};
|
return Status{RippledError::rpcINVALID_PARAMS, "missing field secret"};
|
||||||
|
|
||||||
if (count > 1)
|
if (count > 1)
|
||||||
{
|
{
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINVALID_PARAMS,
|
RippledError::rpcINVALID_PARAMS,
|
||||||
"Exactly one of the following must be specified: "
|
"Exactly one of the following must be specified: "
|
||||||
" passphrase, secret, seed, or seed_hex"};
|
" passphrase, secret, seed, or seed_hex"};
|
||||||
}
|
}
|
||||||
@@ -907,17 +911,18 @@ keypairFromRequst(boost::json::object const& request)
|
|||||||
if (has_key_type)
|
if (has_key_type)
|
||||||
{
|
{
|
||||||
if (!request.at("key_type").is_string())
|
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();
|
std::string key_type = request.at("key_type").as_string().c_str();
|
||||||
keyType = ripple::keyTypeFromString(key_type);
|
keyType = ripple::keyTypeFromString(key_type);
|
||||||
|
|
||||||
if (!keyType)
|
if (!keyType)
|
||||||
return Status{Error::rpcINVALID_PARAMS, "invalidFieldKeyType"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "invalidFieldKeyType"};
|
||||||
|
|
||||||
if (secretType == "secret")
|
if (secretType == "secret")
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINVALID_PARAMS,
|
RippledError::rpcINVALID_PARAMS,
|
||||||
"The secret field is not allowed if key_type is used."};
|
"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) !=
|
if (keyType.value_or(ripple::KeyType::ed25519) !=
|
||||||
ripple::KeyType::ed25519)
|
ripple::KeyType::ed25519)
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINVALID_PARAMS,
|
RippledError::rpcINVALID_PARAMS,
|
||||||
"Specified seed is for an Ed25519 wallet."};
|
"Specified seed is for an Ed25519 wallet."};
|
||||||
|
|
||||||
keyType = ripple::KeyType::ed25519;
|
keyType = ripple::KeyType::ed25519;
|
||||||
@@ -951,7 +956,8 @@ keypairFromRequst(boost::json::object const& request)
|
|||||||
{
|
{
|
||||||
if (!request.at(secretType).is_string())
|
if (!request.at(secretType).is_string())
|
||||||
return Status{
|
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();
|
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())
|
if (!request.at("secret").is_string())
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINVALID_PARAMS,
|
RippledError::rpcINVALID_PARAMS,
|
||||||
"field secret should be a string"};
|
"field secret should be a string"};
|
||||||
|
|
||||||
std::string secret = request.at("secret").as_string().c_str();
|
std::string secret = request.at("secret").as_string().c_str();
|
||||||
@@ -980,12 +986,14 @@ keypairFromRequst(boost::json::object const& request)
|
|||||||
|
|
||||||
if (!seed)
|
if (!seed)
|
||||||
return Status{
|
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 &&
|
if (keyType != ripple::KeyType::secp256k1 &&
|
||||||
keyType != ripple::KeyType::ed25519)
|
keyType != ripple::KeyType::ed25519)
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINVALID_PARAMS, "keypairForSignature: invalid key type"};
|
RippledError::rpcINVALID_PARAMS,
|
||||||
|
"keypairForSignature: invalid key type"};
|
||||||
|
|
||||||
return generateKeyPair(*keyType, *seed);
|
return generateKeyPair(*keyType, *seed);
|
||||||
}
|
}
|
||||||
@@ -1343,54 +1351,63 @@ std::variant<Status, ripple::Book>
|
|||||||
parseBook(boost::json::object const& request)
|
parseBook(boost::json::object const& request)
|
||||||
{
|
{
|
||||||
if (!request.contains("taker_pays"))
|
if (!request.contains("taker_pays"))
|
||||||
return Status{Error::rpcBAD_MARKET, "missingTakerPays"};
|
return Status{RippledError::rpcBAD_MARKET, "missingTakerPays"};
|
||||||
|
|
||||||
if (!request.contains("taker_gets"))
|
if (!request.contains("taker_gets"))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingTakerGets"};
|
return Status{RippledError::rpcINVALID_PARAMS, "missingTakerGets"};
|
||||||
|
|
||||||
if (!request.at("taker_pays").is_object())
|
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())
|
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();
|
auto taker_pays = request.at("taker_pays").as_object();
|
||||||
if (!taker_pays.contains("currency"))
|
if (!taker_pays.contains("currency"))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingTakerPaysCurrency"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "missingTakerPaysCurrency"};
|
||||||
|
|
||||||
if (!taker_pays.at("currency").is_string())
|
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();
|
auto taker_gets = request.at("taker_gets").as_object();
|
||||||
if (!taker_gets.contains("currency"))
|
if (!taker_gets.contains("currency"))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingTakerGetsCurrency"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "missingTakerGetsCurrency"};
|
||||||
|
|
||||||
if (!taker_gets.at("currency").is_string())
|
if (!taker_gets.at("currency").is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "takerGetsCurrencyNotString"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "takerGetsCurrencyNotString"};
|
||||||
|
|
||||||
ripple::Currency pay_currency;
|
ripple::Currency pay_currency;
|
||||||
if (!ripple::to_currency(
|
if (!ripple::to_currency(
|
||||||
pay_currency, taker_pays.at("currency").as_string().c_str()))
|
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;
|
ripple::Currency get_currency;
|
||||||
if (!ripple::to_currency(
|
if (!ripple::to_currency(
|
||||||
get_currency, taker_gets["currency"].as_string().c_str()))
|
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;
|
ripple::AccountID pay_issuer;
|
||||||
if (taker_pays.contains("issuer"))
|
if (taker_pays.contains("issuer"))
|
||||||
{
|
{
|
||||||
if (!taker_pays.at("issuer").is_string())
|
if (!taker_pays.at("issuer").is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "takerPaysIssuerNotString"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "takerPaysIssuerNotString"};
|
||||||
|
|
||||||
if (!ripple::to_issuer(
|
if (!ripple::to_issuer(
|
||||||
pay_issuer, taker_pays.at("issuer").as_string().c_str()))
|
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())
|
if (pay_issuer == ripple::noAccount())
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcSRC_ISR_MALFORMED, "badTakerPaysIssuerAccountOne"};
|
RippledError::rpcSRC_ISR_MALFORMED,
|
||||||
|
"badTakerPaysIssuerAccountOne"};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1399,18 +1416,19 @@ parseBook(boost::json::object const& request)
|
|||||||
|
|
||||||
if (isXRP(pay_currency) && !isXRP(pay_issuer))
|
if (isXRP(pay_currency) && !isXRP(pay_issuer))
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcSRC_ISR_MALFORMED,
|
RippledError::rpcSRC_ISR_MALFORMED,
|
||||||
"Unneeded field 'taker_pays.issuer' for XRP currency "
|
"Unneeded field 'taker_pays.issuer' for XRP currency "
|
||||||
"specification."};
|
"specification."};
|
||||||
|
|
||||||
if (!isXRP(pay_currency) && isXRP(pay_issuer))
|
if (!isXRP(pay_currency) && isXRP(pay_issuer))
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcSRC_ISR_MALFORMED,
|
RippledError::rpcSRC_ISR_MALFORMED,
|
||||||
"Invalid field 'taker_pays.issuer', expected non-XRP "
|
"Invalid field 'taker_pays.issuer', expected non-XRP "
|
||||||
"issuer."};
|
"issuer."};
|
||||||
|
|
||||||
if ((!isXRP(pay_currency)) && (!taker_pays.contains("issuer")))
|
if ((!isXRP(pay_currency)) && (!taker_pays.contains("issuer")))
|
||||||
return Status{Error::rpcSRC_ISR_MALFORMED, "Missing non-XRP issuer."};
|
return Status{
|
||||||
|
RippledError::rpcSRC_ISR_MALFORMED, "Missing non-XRP issuer."};
|
||||||
|
|
||||||
ripple::AccountID get_issuer;
|
ripple::AccountID get_issuer;
|
||||||
|
|
||||||
@@ -1418,17 +1436,18 @@ parseBook(boost::json::object const& request)
|
|||||||
{
|
{
|
||||||
if (!taker_gets["issuer"].is_string())
|
if (!taker_gets["issuer"].is_string())
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINVALID_PARAMS, "taker_gets.issuer should be string"};
|
RippledError::rpcINVALID_PARAMS,
|
||||||
|
"taker_gets.issuer should be string"};
|
||||||
|
|
||||||
if (!ripple::to_issuer(
|
if (!ripple::to_issuer(
|
||||||
get_issuer, taker_gets.at("issuer").as_string().c_str()))
|
get_issuer, taker_gets.at("issuer").as_string().c_str()))
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcDST_ISR_MALFORMED,
|
RippledError::rpcDST_ISR_MALFORMED,
|
||||||
"Invalid field 'taker_gets.issuer', bad issuer."};
|
"Invalid field 'taker_gets.issuer', bad issuer."};
|
||||||
|
|
||||||
if (get_issuer == ripple::noAccount())
|
if (get_issuer == ripple::noAccount())
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcDST_ISR_MALFORMED,
|
RippledError::rpcDST_ISR_MALFORMED,
|
||||||
"Invalid field 'taker_gets.issuer', bad issuer account "
|
"Invalid field 'taker_gets.issuer', bad issuer account "
|
||||||
"one."};
|
"one."};
|
||||||
}
|
}
|
||||||
@@ -1439,17 +1458,17 @@ parseBook(boost::json::object const& request)
|
|||||||
|
|
||||||
if (ripple::isXRP(get_currency) && !ripple::isXRP(get_issuer))
|
if (ripple::isXRP(get_currency) && !ripple::isXRP(get_issuer))
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcDST_ISR_MALFORMED,
|
RippledError::rpcDST_ISR_MALFORMED,
|
||||||
"Unneeded field 'taker_gets.issuer' for XRP currency "
|
"Unneeded field 'taker_gets.issuer' for XRP currency "
|
||||||
"specification."};
|
"specification."};
|
||||||
|
|
||||||
if (!ripple::isXRP(get_currency) && ripple::isXRP(get_issuer))
|
if (!ripple::isXRP(get_currency) && ripple::isXRP(get_issuer))
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcDST_ISR_MALFORMED,
|
RippledError::rpcDST_ISR_MALFORMED,
|
||||||
"Invalid field 'taker_gets.issuer', expected non-XRP issuer."};
|
"Invalid field 'taker_gets.issuer', expected non-XRP issuer."};
|
||||||
|
|
||||||
if (pay_currency == get_currency && pay_issuer == get_issuer)
|
if (pay_currency == get_currency && pay_issuer == get_issuer)
|
||||||
return Status{Error::rpcBAD_MARKET, "badMarket"};
|
return Status{RippledError::rpcBAD_MARKET, "badMarket"};
|
||||||
|
|
||||||
return ripple::Book{{pay_currency, pay_issuer}, {get_currency, get_issuer}};
|
return ripple::Book{{pay_currency, pay_issuer}, {get_currency, get_issuer}};
|
||||||
}
|
}
|
||||||
@@ -1459,12 +1478,12 @@ parseTaker(boost::json::value const& taker)
|
|||||||
{
|
{
|
||||||
std::optional<ripple::AccountID> takerID = {};
|
std::optional<ripple::AccountID> takerID = {};
|
||||||
if (!taker.is_string())
|
if (!taker.is_string())
|
||||||
return {Status{Error::rpcINVALID_PARAMS, "takerNotString"}};
|
return {Status{RippledError::rpcINVALID_PARAMS, "takerNotString"}};
|
||||||
|
|
||||||
takerID = accountFromStringStrict(taker.as_string().c_str());
|
takerID = accountFromStringStrict(taker.as_string().c_str());
|
||||||
|
|
||||||
if (!takerID)
|
if (!takerID)
|
||||||
return Status{Error::rpcBAD_ISSUER, "invalidTakerAccount"};
|
return Status{RippledError::rpcBAD_ISSUER, "invalidTakerAccount"};
|
||||||
return *takerID;
|
return *takerID;
|
||||||
}
|
}
|
||||||
bool
|
bool
|
||||||
@@ -1486,14 +1505,14 @@ std::variant<ripple::uint256, Status>
|
|||||||
getNFTID(boost::json::object const& request)
|
getNFTID(boost::json::object const& request)
|
||||||
{
|
{
|
||||||
if (!request.contains(JS(nft_id)))
|
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())
|
if (!request.at(JS(nft_id)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "tokenIDNotString"};
|
return Status{RippledError::rpcINVALID_PARAMS, "tokenIDNotString"};
|
||||||
|
|
||||||
ripple::uint256 tokenid;
|
ripple::uint256 tokenid;
|
||||||
if (!tokenid.parseHex(request.at(JS(nft_id)).as_string().c_str()))
|
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;
|
return tokenid;
|
||||||
}
|
}
|
||||||
@@ -1521,7 +1540,7 @@ traverseTransactions(
|
|||||||
if (request.contains(JS(marker)))
|
if (request.contains(JS(marker)))
|
||||||
{
|
{
|
||||||
if (!request.at(JS(marker)).is_object())
|
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();
|
auto const& obj = request.at(JS(marker)).as_object();
|
||||||
|
|
||||||
std::optional<std::uint32_t> transactionIndex = {};
|
std::optional<std::uint32_t> transactionIndex = {};
|
||||||
@@ -1529,7 +1548,7 @@ traverseTransactions(
|
|||||||
{
|
{
|
||||||
if (!obj.at(JS(seq)).is_int64())
|
if (!obj.at(JS(seq)).is_int64())
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINVALID_PARAMS, "transactionIndexNotInt"};
|
RippledError::rpcINVALID_PARAMS, "transactionIndexNotInt"};
|
||||||
|
|
||||||
transactionIndex =
|
transactionIndex =
|
||||||
boost::json::value_to<std::uint32_t>(obj.at(JS(seq)));
|
boost::json::value_to<std::uint32_t>(obj.at(JS(seq)));
|
||||||
@@ -1539,14 +1558,16 @@ traverseTransactions(
|
|||||||
if (obj.contains(JS(ledger)))
|
if (obj.contains(JS(ledger)))
|
||||||
{
|
{
|
||||||
if (!obj.at(JS(ledger)).is_int64())
|
if (!obj.at(JS(ledger)).is_int64())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "ledgerIndexNotInt"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "ledgerIndexNotInt"};
|
||||||
|
|
||||||
ledgerIndex =
|
ledgerIndex =
|
||||||
boost::json::value_to<std::uint32_t>(obj.at(JS(ledger)));
|
boost::json::value_to<std::uint32_t>(obj.at(JS(ledger)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!transactionIndex || !ledgerIndex)
|
if (!transactionIndex || !ledgerIndex)
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingLedgerOrSeq"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "missingLedgerOrSeq"};
|
||||||
|
|
||||||
cursor = {*ledgerIndex, *transactionIndex};
|
cursor = {*ledgerIndex, *transactionIndex};
|
||||||
}
|
}
|
||||||
@@ -1557,14 +1578,16 @@ traverseTransactions(
|
|||||||
auto& min = request.at(JS(ledger_index_min));
|
auto& min = request.at(JS(ledger_index_min));
|
||||||
|
|
||||||
if (!min.is_int64())
|
if (!min.is_int64())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "ledgerSeqMinNotNumber"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "ledgerSeqMinNotNumber"};
|
||||||
|
|
||||||
if (min.as_int64() != -1)
|
if (min.as_int64() != -1)
|
||||||
{
|
{
|
||||||
if (context.range.maxSequence < min.as_int64() ||
|
if (context.range.maxSequence < min.as_int64() ||
|
||||||
context.range.minSequence > min.as_int64())
|
context.range.minSequence > min.as_int64())
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcLGR_IDX_MALFORMED, "ledgerSeqMinOutOfRange"};
|
RippledError::rpcLGR_IDX_MALFORMED,
|
||||||
|
"ledgerSeqMinOutOfRange"};
|
||||||
else
|
else
|
||||||
minIndex = boost::json::value_to<std::uint32_t>(min);
|
minIndex = boost::json::value_to<std::uint32_t>(min);
|
||||||
}
|
}
|
||||||
@@ -1579,20 +1602,22 @@ traverseTransactions(
|
|||||||
auto& max = request.at(JS(ledger_index_max));
|
auto& max = request.at(JS(ledger_index_max));
|
||||||
|
|
||||||
if (!max.is_int64())
|
if (!max.is_int64())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "ledgerSeqMaxNotNumber"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "ledgerSeqMaxNotNumber"};
|
||||||
|
|
||||||
if (max.as_int64() != -1)
|
if (max.as_int64() != -1)
|
||||||
{
|
{
|
||||||
if (context.range.maxSequence < max.as_int64() ||
|
if (context.range.maxSequence < max.as_int64() ||
|
||||||
context.range.minSequence > max.as_int64())
|
context.range.minSequence > max.as_int64())
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcLGR_IDX_MALFORMED, "ledgerSeqMaxOutOfRange"};
|
RippledError::rpcLGR_IDX_MALFORMED,
|
||||||
|
"ledgerSeqMaxOutOfRange"};
|
||||||
else
|
else
|
||||||
maxIndex = boost::json::value_to<std::uint32_t>(max);
|
maxIndex = boost::json::value_to<std::uint32_t>(max);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minIndex > maxIndex)
|
if (minIndex > maxIndex)
|
||||||
return Status{Error::rpcINVALID_PARAMS, "invalidIndex"};
|
return Status{RippledError::rpcINVALID_PARAMS, "invalidIndex"};
|
||||||
|
|
||||||
if (!forward && !cursor)
|
if (!forward && !cursor)
|
||||||
cursor = {maxIndex, INT32_MAX};
|
cursor = {maxIndex, INT32_MAX};
|
||||||
@@ -1603,7 +1628,8 @@ traverseTransactions(
|
|||||||
if (request.contains(JS(ledger_index_max)) ||
|
if (request.contains(JS(ledger_index_max)) ||
|
||||||
request.contains(JS(ledger_index_min)))
|
request.contains(JS(ledger_index_min)))
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINVALID_PARAMS, "containsLedgerSpecifierAndRange"};
|
RippledError::rpcINVALID_PARAMS,
|
||||||
|
"containsLedgerSpecifierAndRange"};
|
||||||
|
|
||||||
auto v = ledgerInfoFromRequest(context);
|
auto v = ledgerInfoFromRequest(context);
|
||||||
if (auto status = std::get_if<Status>(&v); status)
|
if (auto status = std::get_if<Status>(&v); status)
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ doAccountChannels(Context const& context)
|
|||||||
ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield);
|
ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield);
|
||||||
|
|
||||||
if (!rawAcct)
|
if (!rawAcct)
|
||||||
return Status{Error::rpcACT_NOT_FOUND, "accountNotFound"};
|
return Status{RippledError::rpcACT_NOT_FOUND, "accountNotFound"};
|
||||||
|
|
||||||
ripple::AccountID destAccount;
|
ripple::AccountID destAccount;
|
||||||
if (auto const status =
|
if (auto const status =
|
||||||
@@ -78,7 +78,7 @@ doAccountChannels(Context const& context)
|
|||||||
if (request.contains(JS(marker)))
|
if (request.contains(JS(marker)))
|
||||||
{
|
{
|
||||||
if (!request.at(JS(marker)).is_string())
|
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();
|
marker = request.at(JS(marker)).as_string().c_str();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ doAccountCurrencies(Context const& context)
|
|||||||
ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield);
|
ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield);
|
||||||
|
|
||||||
if (!rawAcct)
|
if (!rawAcct)
|
||||||
return Status{Error::rpcACT_NOT_FOUND, "accountNotFound"};
|
return Status{RippledError::rpcACT_NOT_FOUND, "accountNotFound"};
|
||||||
|
|
||||||
std::set<std::string> send, receive;
|
std::set<std::string> send, receive;
|
||||||
auto const addToResponse = [&](ripple::SLE&& sle) {
|
auto const addToResponse = [&](ripple::SLE&& sle) {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ doAccountInfo(Context const& context)
|
|||||||
else if (request.contains(JS(ident)))
|
else if (request.contains(JS(ident)))
|
||||||
strIdent = request.at(JS(ident)).as_string().c_str();
|
strIdent = request.at(JS(ident)).as_string().c_str();
|
||||||
else
|
else
|
||||||
return Status{Error::rpcACT_MALFORMED};
|
return Status{RippledError::rpcACT_MALFORMED};
|
||||||
|
|
||||||
// We only need to fetch the ledger header because the ledger hash is
|
// 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
|
// supposed to be included in the response. The ledger sequence is specified
|
||||||
@@ -48,7 +48,7 @@ doAccountInfo(Context const& context)
|
|||||||
// Get info on account.
|
// Get info on account.
|
||||||
auto accountID = accountFromStringStrict(strIdent);
|
auto accountID = accountFromStringStrict(strIdent);
|
||||||
if (!accountID)
|
if (!accountID)
|
||||||
return Status{Error::rpcACT_MALFORMED};
|
return Status{RippledError::rpcACT_MALFORMED};
|
||||||
|
|
||||||
assert(accountID.has_value());
|
assert(accountID.has_value());
|
||||||
|
|
||||||
@@ -59,14 +59,14 @@ doAccountInfo(Context const& context)
|
|||||||
|
|
||||||
if (!dbResponse)
|
if (!dbResponse)
|
||||||
{
|
{
|
||||||
return Status{Error::rpcACT_NOT_FOUND};
|
return Status{RippledError::rpcACT_NOT_FOUND};
|
||||||
}
|
}
|
||||||
|
|
||||||
ripple::STLedgerEntry sle{
|
ripple::STLedgerEntry sle{
|
||||||
ripple::SerialIter{dbResponse->data(), dbResponse->size()}, key.key};
|
ripple::SerialIter{dbResponse->data(), dbResponse->size()}, key.key};
|
||||||
|
|
||||||
if (!key.check(sle))
|
if (!key.check(sle))
|
||||||
return Status{Error::rpcDB_DESERIALIZATION};
|
return Status{RippledError::rpcDB_DESERIALIZATION};
|
||||||
|
|
||||||
// if (!binary)
|
// if (!binary)
|
||||||
// response[JS(account_data)] = getJson(sle);
|
// response[JS(account_data)] = getJson(sle);
|
||||||
@@ -97,7 +97,7 @@ doAccountInfo(Context const& context)
|
|||||||
ripple::SerialIter{signers->data(), signers->size()},
|
ripple::SerialIter{signers->data(), signers->size()},
|
||||||
signersKey.key};
|
signersKey.key};
|
||||||
if (!signersKey.check(sleSigners))
|
if (!signersKey.check(sleSigners))
|
||||||
return Status{Error::rpcDB_DESERIALIZATION};
|
return Status{RippledError::rpcDB_DESERIALIZATION};
|
||||||
|
|
||||||
signerList.push_back(toJson(sleSigners));
|
signerList.push_back(toJson(sleSigners));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ doAccountLines(Context const& context)
|
|||||||
ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield);
|
ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield);
|
||||||
|
|
||||||
if (!rawAcct)
|
if (!rawAcct)
|
||||||
return Status{Error::rpcACT_NOT_FOUND, "accountNotFound"};
|
return Status{RippledError::rpcACT_NOT_FOUND, "accountNotFound"};
|
||||||
|
|
||||||
std::optional<ripple::AccountID> peerAccount;
|
std::optional<ripple::AccountID> peerAccount;
|
||||||
if (auto const status = getOptionalAccount(request, peerAccount, JS(peer));
|
if (auto const status = getOptionalAccount(request, peerAccount, JS(peer));
|
||||||
@@ -122,7 +122,7 @@ doAccountLines(Context const& context)
|
|||||||
if (request.contains(JS(marker)))
|
if (request.contains(JS(marker)))
|
||||||
{
|
{
|
||||||
if (not request.at(JS(marker)).is_string())
|
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();
|
marker = request.at(JS(marker)).as_string().c_str();
|
||||||
}
|
}
|
||||||
@@ -131,7 +131,8 @@ doAccountLines(Context const& context)
|
|||||||
if (request.contains(JS(ignore_default)))
|
if (request.contains(JS(ignore_default)))
|
||||||
{
|
{
|
||||||
if (not request.at(JS(ignore_default)).is_bool())
|
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();
|
ignoreDefault = request.at(JS(ignore_default)).as_bool();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,13 +45,13 @@ doAccountNFTs(Context const& context)
|
|||||||
return status;
|
return status;
|
||||||
|
|
||||||
if (!accountID)
|
if (!accountID)
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
|
return Status{RippledError::rpcINVALID_PARAMS, "malformedAccount"};
|
||||||
|
|
||||||
auto rawAcct = context.backend->fetchLedgerObject(
|
auto rawAcct = context.backend->fetchLedgerObject(
|
||||||
ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield);
|
ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield);
|
||||||
|
|
||||||
if (!rawAcct)
|
if (!rawAcct)
|
||||||
return Status{Error::rpcACT_NOT_FOUND, "accountNotFound"};
|
return Status{RippledError::rpcACT_NOT_FOUND, "accountNotFound"};
|
||||||
|
|
||||||
std::uint32_t limit;
|
std::uint32_t limit;
|
||||||
if (auto const status = getLimit(context, limit); status)
|
if (auto const status = getLimit(context, limit); status)
|
||||||
@@ -157,7 +157,7 @@ doAccountObjects(Context const& context)
|
|||||||
if (request.contains("marker"))
|
if (request.contains("marker"))
|
||||||
{
|
{
|
||||||
if (!request.at("marker").is_string())
|
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();
|
marker = request.at("marker").as_string().c_str();
|
||||||
}
|
}
|
||||||
@@ -166,11 +166,11 @@ doAccountObjects(Context const& context)
|
|||||||
if (request.contains(JS(type)))
|
if (request.contains(JS(type)))
|
||||||
{
|
{
|
||||||
if (!request.at(JS(type)).is_string())
|
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();
|
std::string typeAsString = request.at(JS(type)).as_string().c_str();
|
||||||
if (types.find(typeAsString) == types.end())
|
if (types.find(typeAsString) == types.end())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "typeInvalid"};
|
return Status{RippledError::rpcINVALID_PARAMS, "typeInvalid"};
|
||||||
|
|
||||||
objectType = types[typeAsString];
|
objectType = types[typeAsString];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ doAccountOffers(Context const& context)
|
|||||||
ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield);
|
ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield);
|
||||||
|
|
||||||
if (!rawAcct)
|
if (!rawAcct)
|
||||||
return Status{Error::rpcACT_NOT_FOUND, "accountNotFound"};
|
return Status{RippledError::rpcACT_NOT_FOUND, "accountNotFound"};
|
||||||
|
|
||||||
std::uint32_t limit;
|
std::uint32_t limit;
|
||||||
if (auto const status = getLimit(context, limit); status)
|
if (auto const status = getLimit(context, limit); status)
|
||||||
@@ -94,7 +94,7 @@ doAccountOffers(Context const& context)
|
|||||||
if (request.contains(JS(marker)))
|
if (request.contains(JS(marker)))
|
||||||
{
|
{
|
||||||
if (!request.at(JS(marker)).is_string())
|
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();
|
marker = request.at(JS(marker)).as_string().c_str();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ doBookOffers(Context const& context)
|
|||||||
if (request.contains("book"))
|
if (request.contains("book"))
|
||||||
{
|
{
|
||||||
if (!request.at("book").is_string())
|
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()))
|
if (!bookBase.parseHex(request.at("book").as_string().c_str()))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "invalidBook"};
|
return Status{RippledError::rpcINVALID_PARAMS, "invalidBook"};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,13 +28,14 @@ doChannelAuthorize(Context const& context)
|
|||||||
boost::json::object response = {};
|
boost::json::object response = {};
|
||||||
|
|
||||||
if (!request.contains(JS(amount)))
|
if (!request.contains(JS(amount)))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingAmount"};
|
return Status{RippledError::rpcINVALID_PARAMS, "missingAmount"};
|
||||||
|
|
||||||
if (!request.at(JS(amount)).is_string())
|
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)))
|
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);
|
auto v = keypairFromRequst(request);
|
||||||
if (auto status = std::get_if<Status>(&v))
|
if (auto status = std::get_if<Status>(&v))
|
||||||
@@ -51,7 +52,8 @@ doChannelAuthorize(Context const& context)
|
|||||||
ripple::to_uint64(request.at(JS(amount)).as_string().c_str());
|
ripple::to_uint64(request.at(JS(amount)).as_string().c_str());
|
||||||
|
|
||||||
if (!optDrops)
|
if (!optDrops)
|
||||||
return Status{Error::rpcCHANNEL_AMT_MALFORMED, "couldNotParseAmount"};
|
return Status{
|
||||||
|
RippledError::rpcCHANNEL_AMT_MALFORMED, "couldNotParseAmount"};
|
||||||
|
|
||||||
std::uint64_t drops = *optDrops;
|
std::uint64_t drops = *optDrops;
|
||||||
|
|
||||||
@@ -66,7 +68,7 @@ doChannelAuthorize(Context const& context)
|
|||||||
}
|
}
|
||||||
catch (std::exception&)
|
catch (std::exception&)
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINTERNAL};
|
return Status{RippledError::rpcINTERNAL};
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
|||||||
@@ -17,22 +17,22 @@ doChannelVerify(Context const& context)
|
|||||||
boost::json::object response = {};
|
boost::json::object response = {};
|
||||||
|
|
||||||
if (!request.contains(JS(amount)))
|
if (!request.contains(JS(amount)))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingAmount"};
|
return Status{RippledError::rpcINVALID_PARAMS, "missingAmount"};
|
||||||
|
|
||||||
if (!request.at(JS(amount)).is_string())
|
if (!request.at(JS(amount)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "amountNotString"};
|
return Status{RippledError::rpcINVALID_PARAMS, "amountNotString"};
|
||||||
|
|
||||||
if (!request.contains(JS(signature)))
|
if (!request.contains(JS(signature)))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingSignature"};
|
return Status{RippledError::rpcINVALID_PARAMS, "missingSignature"};
|
||||||
|
|
||||||
if (!request.at(JS(signature)).is_string())
|
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)))
|
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())
|
if (!request.at(JS(public_key)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "publicKeyNotString"};
|
return Status{RippledError::rpcINVALID_PARAMS, "publicKeyNotString"};
|
||||||
|
|
||||||
std::optional<ripple::PublicKey> pk;
|
std::optional<ripple::PublicKey> pk;
|
||||||
{
|
{
|
||||||
@@ -45,12 +45,14 @@ doChannelVerify(Context const& context)
|
|||||||
{
|
{
|
||||||
auto pkHex = ripple::strUnHex(strPk);
|
auto pkHex = ripple::strUnHex(strPk);
|
||||||
if (!pkHex)
|
if (!pkHex)
|
||||||
return Status{Error::rpcPUBLIC_MALFORMED, "malformedPublicKey"};
|
return Status{
|
||||||
|
RippledError::rpcPUBLIC_MALFORMED, "malformedPublicKey"};
|
||||||
|
|
||||||
auto const pkType =
|
auto const pkType =
|
||||||
ripple::publicKeyType(ripple::makeSlice(*pkHex));
|
ripple::publicKeyType(ripple::makeSlice(*pkHex));
|
||||||
if (!pkType)
|
if (!pkType)
|
||||||
return Status{Error::rpcPUBLIC_MALFORMED, "invalidKeyType"};
|
return Status{
|
||||||
|
RippledError::rpcPUBLIC_MALFORMED, "invalidKeyType"};
|
||||||
|
|
||||||
pk.emplace(ripple::makeSlice(*pkHex));
|
pk.emplace(ripple::makeSlice(*pkHex));
|
||||||
}
|
}
|
||||||
@@ -64,14 +66,15 @@ doChannelVerify(Context const& context)
|
|||||||
ripple::to_uint64(request.at(JS(amount)).as_string().c_str());
|
ripple::to_uint64(request.at(JS(amount)).as_string().c_str());
|
||||||
|
|
||||||
if (!optDrops)
|
if (!optDrops)
|
||||||
return Status{Error::rpcCHANNEL_AMT_MALFORMED, "couldNotParseAmount"};
|
return Status{
|
||||||
|
RippledError::rpcCHANNEL_AMT_MALFORMED, "couldNotParseAmount"};
|
||||||
|
|
||||||
std::uint64_t drops = *optDrops;
|
std::uint64_t drops = *optDrops;
|
||||||
|
|
||||||
auto sig = ripple::strUnHex(request.at(JS(signature)).as_string().c_str());
|
auto sig = ripple::strUnHex(request.at(JS(signature)).as_string().c_str());
|
||||||
|
|
||||||
if (!sig || !sig->size())
|
if (!sig || !sig->size())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "invalidSignature"};
|
return Status{RippledError::rpcINVALID_PARAMS, "invalidSignature"};
|
||||||
|
|
||||||
ripple::Serializer msg;
|
ripple::Serializer msg;
|
||||||
ripple::serializePayChanAuthorization(
|
ripple::serializePayChanAuthorization(
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ doGatewayBalances(Context const& context)
|
|||||||
};
|
};
|
||||||
if (not std::all_of(
|
if (not std::all_of(
|
||||||
hotWallets.begin(), hotWallets.end(), containsHotWallet))
|
hotWallets.begin(), hotWallets.end(), containsHotWallet))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "invalidHotWallet"};
|
return Status{RippledError::rpcINVALID_PARAMS, "invalidHotWallet"};
|
||||||
|
|
||||||
if (auto balances = toJson(hotBalances); balances.size())
|
if (auto balances = toJson(hotBalances); balances.size())
|
||||||
response[JS(balances)] = balances;
|
response[JS(balances)] = balances;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ doLedger(Context const& context)
|
|||||||
if (params.contains(JS(binary)))
|
if (params.contains(JS(binary)))
|
||||||
{
|
{
|
||||||
if (!params.at(JS(binary)).is_bool())
|
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();
|
binary = params.at(JS(binary)).as_bool();
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,8 @@ doLedger(Context const& context)
|
|||||||
if (params.contains(JS(transactions)))
|
if (params.contains(JS(transactions)))
|
||||||
{
|
{
|
||||||
if (!params.at(JS(transactions)).is_bool())
|
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();
|
transactions = params.at(JS(transactions)).as_bool();
|
||||||
}
|
}
|
||||||
@@ -31,7 +32,7 @@ doLedger(Context const& context)
|
|||||||
if (params.contains(JS(expand)))
|
if (params.contains(JS(expand)))
|
||||||
{
|
{
|
||||||
if (!params.at(JS(expand)).is_bool())
|
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();
|
expand = params.at(JS(expand)).as_bool();
|
||||||
}
|
}
|
||||||
@@ -40,7 +41,7 @@ doLedger(Context const& context)
|
|||||||
if (params.contains("diff"))
|
if (params.contains("diff"))
|
||||||
{
|
{
|
||||||
if (!params.at("diff").is_bool())
|
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 = params.at("diff").as_bool();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ doLedgerData(Context const& context)
|
|||||||
if (request.contains("out_of_order"))
|
if (request.contains("out_of_order"))
|
||||||
{
|
{
|
||||||
if (!request.at("out_of_order").is_bool())
|
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();
|
outOfOrder = request.at("out_of_order").as_bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,11 +55,13 @@ doLedgerData(Context const& context)
|
|||||||
{
|
{
|
||||||
if (!request.at(JS(marker)).is_int64())
|
if (!request.at(JS(marker)).is_int64())
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINVALID_PARAMS, "markerNotStringOrInt"};
|
RippledError::rpcINVALID_PARAMS,
|
||||||
|
"markerNotStringOrInt"};
|
||||||
diffMarker = value_to<uint32_t>(request.at(JS(marker)));
|
diffMarker = value_to<uint32_t>(request.at(JS(marker)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return Status{Error::rpcINVALID_PARAMS, "markerNotString"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "markerNotString"};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -67,7 +69,8 @@ doLedgerData(Context const& context)
|
|||||||
|
|
||||||
marker = ripple::uint256{};
|
marker = ripple::uint256{};
|
||||||
if (!marker->parseHex(request.at(JS(marker)).as_string().c_str()))
|
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 &&
|
if (!outOfOrder &&
|
||||||
!context.backend->fetchLedgerObject(
|
!context.backend->fetchLedgerObject(
|
||||||
*marker, lgrInfo.seq, context.yield))
|
*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);
|
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
|
|||||||
@@ -32,31 +32,32 @@ doLedgerEntry(Context const& context)
|
|||||||
if (request.contains(JS(index)))
|
if (request.contains(JS(index)))
|
||||||
{
|
{
|
||||||
if (!request.at(JS(index)).is_string())
|
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()))
|
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)))
|
else if (request.contains(JS(account_root)))
|
||||||
{
|
{
|
||||||
if (!request.at(JS(account_root)).is_string())
|
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<ripple::AccountID>(
|
auto const account = ripple::parseBase58<ripple::AccountID>(
|
||||||
request.at(JS(account_root)).as_string().c_str());
|
request.at(JS(account_root)).as_string().c_str());
|
||||||
if (!account || account->isZero())
|
if (!account || account->isZero())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAddress"};
|
return Status{RippledError::rpcINVALID_PARAMS, "malformedAddress"};
|
||||||
else
|
else
|
||||||
key = ripple::keylet::account(*account).key;
|
key = ripple::keylet::account(*account).key;
|
||||||
}
|
}
|
||||||
else if (request.contains(JS(check)))
|
else if (request.contains(JS(check)))
|
||||||
{
|
{
|
||||||
if (!request.at(JS(check)).is_string())
|
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()))
|
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)))
|
else if (request.contains(JS(deposit_preauth)))
|
||||||
@@ -68,7 +69,8 @@ doLedgerEntry(Context const& context)
|
|||||||
request.at(JS(deposit_preauth)).as_string().c_str()))
|
request.at(JS(deposit_preauth)).as_string().c_str()))
|
||||||
{
|
{
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINVALID_PARAMS, "deposit_preauthMalformed"};
|
RippledError::rpcINVALID_PARAMS,
|
||||||
|
"deposit_preauthMalformed"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
@@ -78,7 +80,7 @@ doLedgerEntry(Context const& context)
|
|||||||
.at(JS(owner))
|
.at(JS(owner))
|
||||||
.is_string())
|
.is_string())
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedOwner"};
|
return Status{RippledError::rpcINVALID_PARAMS, "malformedOwner"};
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
!request.at(JS(deposit_preauth))
|
!request.at(JS(deposit_preauth))
|
||||||
@@ -89,7 +91,8 @@ doLedgerEntry(Context const& context)
|
|||||||
.at(JS(authorized))
|
.at(JS(authorized))
|
||||||
.is_string())
|
.is_string())
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "authorizedNotString"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "authorizedNotString"};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -103,9 +106,11 @@ doLedgerEntry(Context const& context)
|
|||||||
deposit_preauth.at(JS(authorized)).as_string().c_str());
|
deposit_preauth.at(JS(authorized)).as_string().c_str());
|
||||||
|
|
||||||
if (!owner)
|
if (!owner)
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedOwner"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "malformedOwner"};
|
||||||
else if (!authorized)
|
else if (!authorized)
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAuthorized"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "malformedAuthorized"};
|
||||||
else
|
else
|
||||||
key = ripple::keylet::depositPreauth(*owner, *authorized).key;
|
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_object())
|
||||||
{
|
{
|
||||||
if (!request.at(JS(directory)).is_string())
|
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()))
|
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 (
|
else if (
|
||||||
request.at(JS(directory)).as_object().contains(JS(sub_index)) &&
|
request.at(JS(directory)).as_object().contains(JS(sub_index)) &&
|
||||||
!request.at(JS(directory)).as_object().at(JS(sub_index)).is_int64())
|
!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
|
else
|
||||||
{
|
{
|
||||||
@@ -144,13 +151,14 @@ doLedgerEntry(Context const& context)
|
|||||||
{
|
{
|
||||||
// May not specify both dir_root and owner.
|
// May not specify both dir_root and owner.
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINVALID_PARAMS,
|
RippledError::rpcINVALID_PARAMS,
|
||||||
"mayNotSpecifyBothDirRootAndOwner"};
|
"mayNotSpecifyBothDirRootAndOwner"};
|
||||||
}
|
}
|
||||||
else if (!uDirRoot.parseHex(
|
else if (!uDirRoot.parseHex(
|
||||||
directory.at(JS(dir_root)).as_string().c_str()))
|
directory.at(JS(dir_root)).as_string().c_str()))
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedDirRoot"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "malformedDirRoot"};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -164,7 +172,8 @@ doLedgerEntry(Context const& context)
|
|||||||
|
|
||||||
if (!ownerID)
|
if (!ownerID)
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAddress"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "malformedAddress"};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -176,7 +185,7 @@ doLedgerEntry(Context const& context)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Status{
|
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 (!request.at(JS(escrow)).is_object())
|
||||||
{
|
{
|
||||||
if (!key.parseHex(request.at(JS(escrow)).as_string().c_str()))
|
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 (
|
else if (
|
||||||
!request.at(JS(escrow)).as_object().contains(JS(owner)) ||
|
!request.at(JS(escrow)).as_object().contains(JS(owner)) ||
|
||||||
!request.at(JS(escrow)).as_object().at(JS(owner)).is_string())
|
!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 (
|
else if (
|
||||||
!request.at(JS(escrow)).as_object().contains(JS(seq)) ||
|
!request.at(JS(escrow)).as_object().contains(JS(seq)) ||
|
||||||
!request.at(JS(escrow)).as_object().at(JS(seq)).is_int64())
|
!request.at(JS(escrow)).as_object().at(JS(seq)).is_int64())
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedSeq"};
|
return Status{RippledError::rpcINVALID_PARAMS, "malformedSeq"};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -209,7 +219,8 @@ doLedgerEntry(Context const& context)
|
|||||||
.c_str());
|
.c_str());
|
||||||
|
|
||||||
if (!id)
|
if (!id)
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAddress"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "malformedAddress"};
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::uint32_t seq =
|
std::uint32_t seq =
|
||||||
@@ -223,19 +234,20 @@ doLedgerEntry(Context const& context)
|
|||||||
if (!request.at(JS(offer)).is_object())
|
if (!request.at(JS(offer)).is_object())
|
||||||
{
|
{
|
||||||
if (!key.parseHex(request.at(JS(offer)).as_string().c_str()))
|
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 (
|
else if (
|
||||||
!request.at(JS(offer)).as_object().contains(JS(account)) ||
|
!request.at(JS(offer)).as_object().contains(JS(account)) ||
|
||||||
!request.at(JS(offer)).as_object().at(JS(account)).is_string())
|
!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 (
|
else if (
|
||||||
!request.at(JS(offer)).as_object().contains(JS(seq)) ||
|
!request.at(JS(offer)).as_object().contains(JS(seq)) ||
|
||||||
!request.at(JS(offer)).as_object().at(JS(seq)).is_int64())
|
!request.at(JS(offer)).as_object().at(JS(seq)).is_int64())
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedSeq"};
|
return Status{RippledError::rpcINVALID_PARAMS, "malformedSeq"};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -244,7 +256,8 @@ doLedgerEntry(Context const& context)
|
|||||||
offer.at(JS(account)).as_string().c_str());
|
offer.at(JS(account)).as_string().c_str());
|
||||||
|
|
||||||
if (!id)
|
if (!id)
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAddress"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "malformedAddress"};
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::uint32_t seq =
|
std::uint32_t seq =
|
||||||
@@ -256,15 +269,18 @@ doLedgerEntry(Context const& context)
|
|||||||
else if (request.contains(JS(payment_channel)))
|
else if (request.contains(JS(payment_channel)))
|
||||||
{
|
{
|
||||||
if (!request.at(JS(payment_channel)).is_string())
|
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()))
|
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)))
|
else if (request.contains(JS(ripple_state)))
|
||||||
{
|
{
|
||||||
if (!request.at(JS(ripple_state)).is_object())
|
if (!request.at(JS(ripple_state)).is_object())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "rippleStateNotObject"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "rippleStateNotObject"};
|
||||||
|
|
||||||
ripple::Currency currency;
|
ripple::Currency currency;
|
||||||
boost::json::object const& state =
|
boost::json::object const& state =
|
||||||
@@ -273,7 +289,7 @@ doLedgerEntry(Context const& context)
|
|||||||
if (!state.contains(JS(currency)) ||
|
if (!state.contains(JS(currency)) ||
|
||||||
!state.at(JS(currency)).is_string())
|
!state.at(JS(currency)).is_string())
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedCurrency"};
|
return Status{RippledError::rpcINVALID_PARAMS, "currencyNotString"};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!state.contains(JS(accounts)) ||
|
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(0).as_string() ==
|
||||||
state.at(JS(accounts)).as_array().at(1).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<ripple::AccountID>(
|
auto const id1 = ripple::parseBase58<ripple::AccountID>(
|
||||||
@@ -293,11 +309,13 @@ doLedgerEntry(Context const& context)
|
|||||||
state.at(JS(accounts)).as_array().at(1).as_string().c_str());
|
state.at(JS(accounts)).as_array().at(1).as_string().c_str());
|
||||||
|
|
||||||
if (!id1 || !id2)
|
if (!id1 || !id2)
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAddresses"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "malformedAddresses"};
|
||||||
|
|
||||||
else if (!ripple::to_currency(
|
else if (!ripple::to_currency(
|
||||||
currency, state.at(JS(currency)).as_string().c_str()))
|
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;
|
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_object())
|
||||||
{
|
{
|
||||||
if (!request.at(JS(ticket)).is_string())
|
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()))
|
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 (
|
else if (
|
||||||
!request.at(JS(ticket)).as_object().contains(JS(owner)) ||
|
!request.at(JS(ticket)).as_object().contains(JS(owner)) ||
|
||||||
!request.at(JS(ticket)).as_object().at(JS(owner)).is_string())
|
!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 (
|
else if (
|
||||||
!request.at(JS(ticket)).as_object().contains(JS(ticket_seq)) ||
|
!request.at(JS(ticket)).as_object().contains(JS(ticket_seq)) ||
|
||||||
!request.at(JS(ticket)).as_object().at(JS(ticket_seq)).is_int64())
|
!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
|
else
|
||||||
{
|
{
|
||||||
@@ -333,7 +354,8 @@ doLedgerEntry(Context const& context)
|
|||||||
.c_str());
|
.c_str());
|
||||||
|
|
||||||
if (!id)
|
if (!id)
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedOwner"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "malformedOwner"};
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::uint32_t seq = request.at(JS(offer))
|
std::uint32_t seq = request.at(JS(offer))
|
||||||
@@ -347,7 +369,7 @@ doLedgerEntry(Context const& context)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "unknownOption"};
|
return Status{RippledError::rpcINVALID_PARAMS, "unknownOption"};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto dbResponse =
|
auto dbResponse =
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ doLedgerRange(Context const& context)
|
|||||||
auto range = context.backend->fetchLedgerRange();
|
auto range = context.backend->fetchLedgerRange();
|
||||||
if (!range)
|
if (!range)
|
||||||
{
|
{
|
||||||
return Status{Error::rpcNOT_READY, "rangeNotFound"};
|
return Status{RippledError::rpcNOT_READY, "rangeNotFound"};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ getURI(Backend::NFT const& dbResponse, Context const& context)
|
|||||||
|
|
||||||
if (!blob || blob->size() == 0)
|
if (!blob || blob->size() == 0)
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINTERNAL, "Cannot find NFTokenPage for this NFT"};
|
RippledError::rpcINTERNAL,
|
||||||
|
"Cannot find NFTokenPage for this NFT"};
|
||||||
|
|
||||||
sle = ripple::STLedgerEntry(
|
sle = ripple::STLedgerEntry(
|
||||||
ripple::SerialIter{blob->data(), blob->size()}, nextKey);
|
ripple::SerialIter{blob->data(), blob->size()}, nextKey);
|
||||||
@@ -57,7 +58,7 @@ getURI(Backend::NFT const& dbResponse, Context const& context)
|
|||||||
|
|
||||||
if (!sle)
|
if (!sle)
|
||||||
return Status{
|
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 nfts = sle->getFieldArray(ripple::sfNFTokens);
|
||||||
auto const nft = std::find_if(
|
auto const nft = std::find_if(
|
||||||
@@ -70,7 +71,7 @@ getURI(Backend::NFT const& dbResponse, Context const& context)
|
|||||||
|
|
||||||
if (nft == nfts.end())
|
if (nft == nfts.end())
|
||||||
return Status{
|
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);
|
ripple::Blob const uriField = nft->getFieldVL(ripple::sfURI);
|
||||||
|
|
||||||
@@ -102,7 +103,7 @@ doNFTInfo(Context const& context)
|
|||||||
std::optional<Backend::NFT> dbResponse =
|
std::optional<Backend::NFT> dbResponse =
|
||||||
context.backend->fetchNFT(tokenID, lgrInfo.seq, context.yield);
|
context.backend->fetchNFT(tokenID, lgrInfo.seq, context.yield);
|
||||||
if (!dbResponse)
|
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["nft_id"] = ripple::strHex(dbResponse->tokenID);
|
||||||
response["ledger_index"] = dbResponse->ledgerSequence;
|
response["ledger_index"] = dbResponse->ledgerSequence;
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ enumerateNFTOffers(
|
|||||||
// TODO: just check for existence without pulling
|
// TODO: just check for existence without pulling
|
||||||
if (!context.backend->fetchLedgerObject(
|
if (!context.backend->fetchLedgerObject(
|
||||||
directory.key, lgrInfo.seq, context.yield))
|
directory.key, lgrInfo.seq, context.yield))
|
||||||
return Status{Error::rpcOBJECT_NOT_FOUND, "notFound"};
|
return Status{RippledError::rpcOBJECT_NOT_FOUND, "notFound"};
|
||||||
|
|
||||||
std::uint32_t limit;
|
std::uint32_t limit;
|
||||||
if (auto const status = getLimit(context, limit); status)
|
if (auto const status = getLimit(context, limit); status)
|
||||||
@@ -78,10 +78,10 @@ enumerateNFTOffers(
|
|||||||
auto const& marker(request.at(JS(marker)));
|
auto const& marker(request.at(JS(marker)));
|
||||||
|
|
||||||
if (!marker.is_string())
|
if (!marker.is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "markerNotString"};
|
return Status{RippledError::rpcINVALID_PARAMS, "markerNotString"};
|
||||||
|
|
||||||
if (!cursor.parseHex(marker.as_string().c_str()))
|
if (!cursor.parseHex(marker.as_string().c_str()))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedCursor"};
|
return Status{RippledError::rpcINVALID_PARAMS, "malformedCursor"};
|
||||||
|
|
||||||
auto const sle =
|
auto const sle =
|
||||||
read(ripple::keylet::nftoffer(cursor), lgrInfo, context);
|
read(ripple::keylet::nftoffer(cursor), lgrInfo, context);
|
||||||
@@ -90,7 +90,7 @@ enumerateNFTOffers(
|
|||||||
sle->getFieldU16(ripple::sfLedgerEntryType) !=
|
sle->getFieldU16(ripple::sfLedgerEntryType) !=
|
||||||
ripple::ltNFTOKEN_OFFER ||
|
ripple::ltNFTOKEN_OFFER ||
|
||||||
tokenid != sle->getFieldH256(ripple::sfNFTokenID))
|
tokenid != sle->getFieldH256(ripple::sfNFTokenID))
|
||||||
return Status{Error::rpcINVALID_PARAMS};
|
return Status{RippledError::rpcINVALID_PARAMS};
|
||||||
|
|
||||||
startHint = sle->getFieldU64(ripple::sfNFTokenOfferNode);
|
startHint = sle->getFieldU64(ripple::sfNFTokenOfferNode);
|
||||||
jsonOffers.push_back(json::value_from(*sle));
|
jsonOffers.push_back(json::value_from(*sle));
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ doNoRippleCheck(Context const& context)
|
|||||||
if (role == "gateway")
|
if (role == "gateway")
|
||||||
roleGateway = true;
|
roleGateway = true;
|
||||||
else if (role != "user")
|
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;
|
std::uint32_t limit = 300;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ doServerInfo(Context const& context)
|
|||||||
if (!range)
|
if (!range)
|
||||||
{
|
{
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcNOT_READY,
|
RippledError::rpcNOT_READY,
|
||||||
"emptyDatabase",
|
"emptyDatabase",
|
||||||
"The server has no data in the database"};
|
"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);
|
auto fees = context.backend->fetchFees(lgrInfo->seq, context.yield);
|
||||||
|
|
||||||
if (!lgrInfo || !fees)
|
if (!lgrInfo || !fees)
|
||||||
return Status{Error::rpcINTERNAL};
|
return Status{RippledError::rpcINTERNAL};
|
||||||
|
|
||||||
auto age = std::chrono::duration_cast<std::chrono::seconds>(
|
auto age = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
std::chrono::system_clock::now().time_since_epoch())
|
std::chrono::system_clock::now().time_since_epoch())
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ validateStreams(boost::json::object const& request)
|
|||||||
auto const& stream : streams)
|
auto const& stream : streams)
|
||||||
{
|
{
|
||||||
if (!stream.is_string())
|
if (!stream.is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "streamNotString"};
|
return Status{RippledError::rpcINVALID_PARAMS, "streamNotString"};
|
||||||
|
|
||||||
if (!validCommonStreams.contains(stream.as_string().c_str()))
|
if (!validCommonStreams.contains(stream.as_string().c_str()))
|
||||||
return Status{Error::rpcSTREAM_MALFORMED};
|
return Status{RippledError::rpcSTREAM_MALFORMED};
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
@@ -98,10 +98,10 @@ validateAccounts(boost::json::array const& accounts)
|
|||||||
for (auto const& account : accounts)
|
for (auto const& account : accounts)
|
||||||
{
|
{
|
||||||
if (!account.is_string())
|
if (!account.is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountNotString"};
|
return Status{RippledError::rpcINVALID_PARAMS, "accountNotString"};
|
||||||
|
|
||||||
if (!accountFromStringStrict(account.as_string().c_str()))
|
if (!accountFromStringStrict(account.as_string().c_str()))
|
||||||
return Status{Error::rpcACT_MALFORMED, "Account malformed."};
|
return Status{RippledError::rpcACT_MALFORMED, "Account malformed."};
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
@@ -212,7 +212,7 @@ validateAndGetBooks(
|
|||||||
std::shared_ptr<Backend::BackendInterface const> const& backend)
|
std::shared_ptr<Backend::BackendInterface const> const& backend)
|
||||||
{
|
{
|
||||||
if (!request.at(JS(books)).is_array())
|
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();
|
boost::json::array const& books = request.at(JS(books)).as_array();
|
||||||
|
|
||||||
std::vector<ripple::Book> booksToSub;
|
std::vector<ripple::Book> booksToSub;
|
||||||
@@ -294,7 +294,7 @@ doSubscribe(Context const& context)
|
|||||||
if (request.contains(JS(streams)))
|
if (request.contains(JS(streams)))
|
||||||
{
|
{
|
||||||
if (!request.at(JS(streams)).is_array())
|
if (!request.at(JS(streams)).is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "streamsNotArray"};
|
return Status{RippledError::rpcINVALID_PARAMS, "streamsNotArray"};
|
||||||
|
|
||||||
auto status = validateStreams(request);
|
auto status = validateStreams(request);
|
||||||
|
|
||||||
@@ -306,11 +306,11 @@ doSubscribe(Context const& context)
|
|||||||
{
|
{
|
||||||
auto const& jsonAccounts = request.at(JS(accounts));
|
auto const& jsonAccounts = request.at(JS(accounts));
|
||||||
if (!jsonAccounts.is_array())
|
if (!jsonAccounts.is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountsNotArray"};
|
return Status{RippledError::rpcINVALID_PARAMS, "accountsNotArray"};
|
||||||
|
|
||||||
auto const& accounts = jsonAccounts.as_array();
|
auto const& accounts = jsonAccounts.as_array();
|
||||||
if (accounts.empty())
|
if (accounts.empty())
|
||||||
return Status{Error::rpcACT_MALFORMED, "Account malformed."};
|
return Status{RippledError::rpcACT_MALFORMED, "Account malformed."};
|
||||||
|
|
||||||
auto const status = validateAccounts(accounts);
|
auto const status = validateAccounts(accounts);
|
||||||
if (status)
|
if (status)
|
||||||
@@ -321,11 +321,12 @@ doSubscribe(Context const& context)
|
|||||||
{
|
{
|
||||||
auto const& jsonAccounts = request.at(JS(accounts_proposed));
|
auto const& jsonAccounts = request.at(JS(accounts_proposed));
|
||||||
if (!jsonAccounts.is_array())
|
if (!jsonAccounts.is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountsProposedNotArray"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "accountsProposedNotArray"};
|
||||||
|
|
||||||
auto const& accounts = jsonAccounts.as_array();
|
auto const& accounts = jsonAccounts.as_array();
|
||||||
if (accounts.empty())
|
if (accounts.empty())
|
||||||
return Status{Error::rpcACT_MALFORMED, "Account malformed."};
|
return Status{RippledError::rpcACT_MALFORMED, "Account malformed."};
|
||||||
|
|
||||||
auto const status = validateAccounts(accounts);
|
auto const status = validateAccounts(accounts);
|
||||||
if (status)
|
if (status)
|
||||||
@@ -373,7 +374,7 @@ doUnsubscribe(Context const& context)
|
|||||||
if (request.contains(JS(streams)))
|
if (request.contains(JS(streams)))
|
||||||
{
|
{
|
||||||
if (!request.at(JS(streams)).is_array())
|
if (!request.at(JS(streams)).is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "streamsNotArray"};
|
return Status{RippledError::rpcINVALID_PARAMS, "streamsNotArray"};
|
||||||
|
|
||||||
auto status = validateStreams(request);
|
auto status = validateStreams(request);
|
||||||
|
|
||||||
@@ -385,11 +386,11 @@ doUnsubscribe(Context const& context)
|
|||||||
{
|
{
|
||||||
auto const& jsonAccounts = request.at(JS(accounts));
|
auto const& jsonAccounts = request.at(JS(accounts));
|
||||||
if (!jsonAccounts.is_array())
|
if (!jsonAccounts.is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountsNotArray"};
|
return Status{RippledError::rpcINVALID_PARAMS, "accountsNotArray"};
|
||||||
|
|
||||||
auto const& accounts = jsonAccounts.as_array();
|
auto const& accounts = jsonAccounts.as_array();
|
||||||
if (accounts.empty())
|
if (accounts.empty())
|
||||||
return Status{Error::rpcACT_MALFORMED, "Account malformed."};
|
return Status{RippledError::rpcACT_MALFORMED, "Account malformed."};
|
||||||
|
|
||||||
auto const status = validateAccounts(accounts);
|
auto const status = validateAccounts(accounts);
|
||||||
if (status)
|
if (status)
|
||||||
@@ -400,11 +401,12 @@ doUnsubscribe(Context const& context)
|
|||||||
{
|
{
|
||||||
auto const& jsonAccounts = request.at(JS(accounts_proposed));
|
auto const& jsonAccounts = request.at(JS(accounts_proposed));
|
||||||
if (!jsonAccounts.is_array())
|
if (!jsonAccounts.is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountsProposedNotArray"};
|
return Status{
|
||||||
|
RippledError::rpcINVALID_PARAMS, "accountsProposedNotArray"};
|
||||||
|
|
||||||
auto const& accounts = jsonAccounts.as_array();
|
auto const& accounts = jsonAccounts.as_array();
|
||||||
if (accounts.empty())
|
if (accounts.empty())
|
||||||
return Status{Error::rpcACT_MALFORMED, "Account malformed."};
|
return Status{RippledError::rpcACT_MALFORMED, "Account malformed."};
|
||||||
|
|
||||||
auto const status = validateAccounts(accounts);
|
auto const status = validateAccounts(accounts);
|
||||||
if (status)
|
if (status)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ doTransactionEntry(Context const& context)
|
|||||||
|
|
||||||
ripple::uint256 hash;
|
ripple::uint256 hash;
|
||||||
if (!hash.parseHex(getRequiredString(context.params, JS(tx_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);
|
auto dbResponse = context.backend->fetchTransaction(hash, context.yield);
|
||||||
// Note: transaction_entry is meant to only search a specified ledger for
|
// 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.
|
// is in a different ledger than the one specified.
|
||||||
if (!dbResponse || dbResponse->ledgerSequence != lgrInfo.seq)
|
if (!dbResponse || dbResponse->ledgerSequence != lgrInfo.seq)
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcTXN_NOT_FOUND,
|
RippledError::rpcTXN_NOT_FOUND,
|
||||||
"transactionNotFound",
|
"transactionNotFound",
|
||||||
"Transaction not found."};
|
"Transaction not found."};
|
||||||
|
|
||||||
|
|||||||
@@ -14,31 +14,31 @@ doTx(Context const& context)
|
|||||||
boost::json::object response = {};
|
boost::json::object response = {};
|
||||||
|
|
||||||
if (!request.contains(JS(transaction)))
|
if (!request.contains(JS(transaction)))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "specifyTransaction"};
|
return Status{RippledError::rpcINVALID_PARAMS, "specifyTransaction"};
|
||||||
|
|
||||||
if (!request.at(JS(transaction)).is_string())
|
if (!request.at(JS(transaction)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "transactionNotString"};
|
return Status{RippledError::rpcINVALID_PARAMS, "transactionNotString"};
|
||||||
|
|
||||||
ripple::uint256 hash;
|
ripple::uint256 hash;
|
||||||
if (!hash.parseHex(request.at(JS(transaction)).as_string().c_str()))
|
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;
|
bool binary = false;
|
||||||
if (request.contains(JS(binary)))
|
if (request.contains(JS(binary)))
|
||||||
{
|
{
|
||||||
if (!request.at(JS(binary)).is_bool())
|
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();
|
binary = request.at(JS(binary)).as_bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto range = context.backend->fetchLedgerRange();
|
auto range = context.backend->fetchLedgerRange();
|
||||||
if (!range)
|
if (!range)
|
||||||
return Status{Error::rpcNOT_READY};
|
return Status{RippledError::rpcNOT_READY};
|
||||||
|
|
||||||
auto dbResponse = context.backend->fetchTransaction(hash, context.yield);
|
auto dbResponse = context.backend->fetchTransaction(hash, context.yield);
|
||||||
if (!dbResponse)
|
if (!dbResponse)
|
||||||
return Status{Error::rpcTXN_NOT_FOUND};
|
return Status{RippledError::rpcTXN_NOT_FOUND};
|
||||||
|
|
||||||
if (!binary)
|
if (!binary)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ public:
|
|||||||
res.set(http::field::content_type, "application/json");
|
res.set(http::field::content_type, "application/json");
|
||||||
res.keep_alive(req_.keep_alive());
|
res.keep_alive(req_.keep_alive());
|
||||||
res.body() = boost::json::serialize(
|
res.body() = boost::json::serialize(
|
||||||
RPC::make_error(RPC::Error::rpcTOO_BUSY));
|
RPC::makeError(RPC::RippledError::rpcTOO_BUSY));
|
||||||
res.prepare_payload();
|
res.prepare_payload();
|
||||||
lambda_(std::move(res));
|
lambda_(std::move(res));
|
||||||
}
|
}
|
||||||
@@ -375,7 +375,7 @@ handle_request(
|
|||||||
http::status::ok,
|
http::status::ok,
|
||||||
"application/json",
|
"application/json",
|
||||||
boost::json::serialize(
|
boost::json::serialize(
|
||||||
RPC::make_error(RPC::Error::rpcBAD_SYNTAX))));
|
RPC::makeError(RPC::RippledError::rpcBAD_SYNTAX))));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto range = backend->fetchLedgerRange();
|
auto range = backend->fetchLedgerRange();
|
||||||
@@ -384,7 +384,7 @@ handle_request(
|
|||||||
http::status::ok,
|
http::status::ok,
|
||||||
"application/json",
|
"application/json",
|
||||||
boost::json::serialize(
|
boost::json::serialize(
|
||||||
RPC::make_error(RPC::Error::rpcNOT_READY))));
|
RPC::makeError(RPC::RippledError::rpcNOT_READY))));
|
||||||
|
|
||||||
std::optional<RPC::Context> context = RPC::make_HttpContext(
|
std::optional<RPC::Context> context = RPC::make_HttpContext(
|
||||||
yc,
|
yc,
|
||||||
@@ -403,7 +403,7 @@ handle_request(
|
|||||||
http::status::ok,
|
http::status::ok,
|
||||||
"application/json",
|
"application/json",
|
||||||
boost::json::serialize(
|
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 response{{"result", boost::json::object{}}};
|
||||||
boost::json::object& result = response["result"].as_object();
|
boost::json::object& result = response["result"].as_object();
|
||||||
@@ -418,7 +418,7 @@ handle_request(
|
|||||||
if (auto status = std::get_if<RPC::Status>(&v))
|
if (auto status = std::get_if<RPC::Status>(&v))
|
||||||
{
|
{
|
||||||
counters.rpcErrored(context->method);
|
counters.rpcErrored(context->method);
|
||||||
auto error = RPC::make_error(*status);
|
auto error = RPC::makeError(*status);
|
||||||
error["request"] = request;
|
error["request"] = request;
|
||||||
result = error;
|
result = error;
|
||||||
|
|
||||||
@@ -438,16 +438,16 @@ handle_request(
|
|||||||
}
|
}
|
||||||
|
|
||||||
boost::json::array warnings;
|
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();
|
auto lastCloseAge = context->etl->lastCloseAgeSeconds();
|
||||||
if (lastCloseAge >= 60)
|
if (lastCloseAge >= 60)
|
||||||
warnings.emplace_back(RPC::make_warning(RPC::warnRPC_OUTDATED));
|
warnings.emplace_back(RPC::makeWarning(RPC::warnRPC_OUTDATED));
|
||||||
response["warnings"] = warnings;
|
response["warnings"] = warnings;
|
||||||
responseStr = boost::json::serialize(response);
|
responseStr = boost::json::serialize(response);
|
||||||
if (!dosGuard.add(ip, responseStr.size()))
|
if (!dosGuard.add(ip, responseStr.size()))
|
||||||
{
|
{
|
||||||
response["warning"] = "load";
|
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;
|
response["warnings"] = warnings;
|
||||||
// reserialize when we need to include this warning
|
// reserialize when we need to include this warning
|
||||||
responseStr = boost::json::serialize(response);
|
responseStr = boost::json::serialize(response);
|
||||||
@@ -462,7 +462,8 @@ handle_request(
|
|||||||
return send(httpResponse(
|
return send(httpResponse(
|
||||||
http::status::internal_server_error,
|
http::status::internal_server_error,
|
||||||
"application/json",
|
"application/json",
|
||||||
boost::json::serialize(RPC::make_error(RPC::Error::rpcINTERNAL))));
|
boost::json::serialize(
|
||||||
|
RPC::makeError(RPC::RippledError::rpcINTERNAL))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ public:
|
|||||||
|
|
||||||
boost::json::object response = {};
|
boost::json::object response = {};
|
||||||
auto sendError = [this, &request, id](auto error) {
|
auto sendError = [this, &request, id](auto error) {
|
||||||
auto e = RPC::make_error(error);
|
auto e = RPC::makeError(error);
|
||||||
if (!id.is_null())
|
if (!id.is_null())
|
||||||
e["id"] = id;
|
e["id"] = id;
|
||||||
e["request"] = request;
|
e["request"] = request;
|
||||||
@@ -292,7 +292,7 @@ public:
|
|||||||
|
|
||||||
auto range = backend_->fetchLedgerRange();
|
auto range = backend_->fetchLedgerRange();
|
||||||
if (!range)
|
if (!range)
|
||||||
return sendError(RPC::Error::rpcNOT_READY);
|
return sendError(RPC::RippledError::rpcNOT_READY);
|
||||||
|
|
||||||
std::optional<RPC::Context> context = RPC::make_WsContext(
|
std::optional<RPC::Context> context = RPC::make_WsContext(
|
||||||
yield,
|
yield,
|
||||||
@@ -311,7 +311,7 @@ public:
|
|||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(warning)
|
BOOST_LOG_TRIVIAL(warning)
|
||||||
<< tag() << " could not create RPC context";
|
<< tag() << " could not create RPC context";
|
||||||
return sendError(RPC::Error::rpcBAD_SYNTAX);
|
return sendError(RPC::RippledError::rpcBAD_SYNTAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
response = getDefaultWsResponse(id);
|
response = getDefaultWsResponse(id);
|
||||||
@@ -327,7 +327,7 @@ public:
|
|||||||
{
|
{
|
||||||
counters_.rpcErrored(context->method);
|
counters_.rpcErrored(context->method);
|
||||||
|
|
||||||
auto error = RPC::make_error(*status);
|
auto error = RPC::makeError(*status);
|
||||||
|
|
||||||
if (!id.is_null())
|
if (!id.is_null())
|
||||||
error["id"] = id;
|
error["id"] = id;
|
||||||
@@ -347,22 +347,22 @@ public:
|
|||||||
BOOST_LOG_TRIVIAL(error)
|
BOOST_LOG_TRIVIAL(error)
|
||||||
<< tag() << __func__ << " caught exception : " << e.what();
|
<< tag() << __func__ << " caught exception : " << e.what();
|
||||||
|
|
||||||
return sendError(RPC::Error::rpcINTERNAL);
|
return sendError(RPC::RippledError::rpcINTERNAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::json::array warnings;
|
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();
|
auto lastCloseAge = etl_->lastCloseAgeSeconds();
|
||||||
if (lastCloseAge >= 60)
|
if (lastCloseAge >= 60)
|
||||||
warnings.emplace_back(RPC::make_warning(RPC::warnRPC_OUTDATED));
|
warnings.emplace_back(RPC::makeWarning(RPC::warnRPC_OUTDATED));
|
||||||
response["warnings"] = warnings;
|
response["warnings"] = warnings;
|
||||||
std::string responseStr = boost::json::serialize(response);
|
std::string responseStr = boost::json::serialize(response);
|
||||||
if (!dosGuard_.add(*ip, responseStr.size()))
|
if (!dosGuard_.add(*ip, responseStr.size()))
|
||||||
{
|
{
|
||||||
response["warning"] = "load";
|
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;
|
response["warnings"] = warnings;
|
||||||
// reserialize if we need to include this warning
|
// reserialize if we need to include this warning
|
||||||
responseStr = boost::json::serialize(response);
|
responseStr = boost::json::serialize(response);
|
||||||
@@ -392,7 +392,7 @@ public:
|
|||||||
auto error,
|
auto error,
|
||||||
boost::json::value const& id,
|
boost::json::value const& id,
|
||||||
boost::json::object const& request) {
|
boost::json::object const& request) {
|
||||||
auto e = RPC::make_error(error);
|
auto e = RPC::makeError(error);
|
||||||
|
|
||||||
if (!id.is_null())
|
if (!id.is_null())
|
||||||
e["id"] = id;
|
e["id"] = id;
|
||||||
@@ -417,14 +417,15 @@ public:
|
|||||||
|
|
||||||
boost::json::object request;
|
boost::json::object request;
|
||||||
if (!raw.is_object())
|
if (!raw.is_object())
|
||||||
return sendError(RPC::Error::rpcINVALID_PARAMS, nullptr, request);
|
return sendError(
|
||||||
|
RPC::RippledError::rpcINVALID_PARAMS, nullptr, request);
|
||||||
request = raw.as_object();
|
request = raw.as_object();
|
||||||
|
|
||||||
auto id = request.contains("id") ? request.at("id") : nullptr;
|
auto id = request.contains("id") ? request.at("id") : nullptr;
|
||||||
|
|
||||||
if (!dosGuard_.isOk(*ip))
|
if (!dosGuard_.isOk(*ip))
|
||||||
{
|
{
|
||||||
sendError(RPC::Error::rpcSLOW_DOWN, id, request);
|
sendError(RPC::RippledError::rpcSLOW_DOWN, id, request);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -438,7 +439,7 @@ public:
|
|||||||
shared_this->handle_request(std::move(r), id, yield);
|
shared_this->handle_request(std::move(r), id, yield);
|
||||||
},
|
},
|
||||||
dosGuard_.isWhiteListed(*ip)))
|
dosGuard_.isWhiteListed(*ip)))
|
||||||
sendError(RPC::Error::rpcTOO_BUSY, id, request);
|
sendError(RPC::RippledError::rpcTOO_BUSY, id, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
do_read();
|
do_read();
|
||||||
|
|||||||
133
unittests/RPCErrors.cpp
Normal file
133
unittests/RPCErrors.cpp
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
#include <rpc/RPC.h>
|
||||||
|
|
||||||
|
#include <boost/json.hpp>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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<ClioError>(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<uint32_t>(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<WarningCode>(999999)));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user