mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-08 13:55:51 +00:00
Fixes #1825 by removing the check in the gateway_balances RPC handler that returns the RpcInvalidHotWallet error code if one of the addresses supplied in the request's `hotwallet` array does not have a trustline with the `account` from the request. As stated in the original ticket, this change fixes a discrepancy in behavior between Clio and rippled, as rippled does not check for trustline existence when handling gateway_balances RPCs Co-authored-by: Sergey Kuznetsov <skuznetsov@ripple.com>
179 lines
6.5 KiB
C++
179 lines
6.5 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of clio: https://github.com/XRPLF/clio
|
|
Copyright (c) 2023, the clio developers.
|
|
|
|
Permission to use, copy, modify, and distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#pragma once
|
|
|
|
#include "rpc/Errors.hpp"
|
|
#include "rpc/JS.hpp"
|
|
#include "util/Assert.hpp"
|
|
#include "web/interface/ConnectionBase.hpp"
|
|
|
|
#include <boost/beast/http/status.hpp>
|
|
#include <boost/json/object.hpp>
|
|
#include <boost/json/serialize.hpp>
|
|
#include <fmt/core.h>
|
|
#include <xrpl/protocol/ErrorCodes.h>
|
|
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <variant>
|
|
|
|
namespace web::impl {
|
|
|
|
/**
|
|
* @brief A helper that attempts to match rippled reporting mode HTTP errors as close as possible.
|
|
*/
|
|
class ErrorHelper {
|
|
std::shared_ptr<web::ConnectionBase> connection_;
|
|
std::optional<boost::json::object> request_;
|
|
|
|
public:
|
|
ErrorHelper(
|
|
std::shared_ptr<web::ConnectionBase> const& connection,
|
|
std::optional<boost::json::object> request = std::nullopt
|
|
)
|
|
: connection_{connection}, request_{std::move(request)}
|
|
{
|
|
}
|
|
|
|
void
|
|
sendError(rpc::Status const& err) const
|
|
{
|
|
if (connection_->upgraded) {
|
|
connection_->send(boost::json::serialize(composeError(err)));
|
|
} else {
|
|
// Note: a collection of crutches to match rippled output follows
|
|
if (auto const clioCode = std::get_if<rpc::ClioError>(&err.code)) {
|
|
switch (*clioCode) {
|
|
case rpc::ClioError::rpcINVALID_API_VERSION:
|
|
connection_->send(
|
|
std::string{rpc::getErrorInfo(*clioCode).error}, boost::beast::http::status::bad_request
|
|
);
|
|
break;
|
|
case rpc::ClioError::rpcCOMMAND_IS_MISSING:
|
|
connection_->send("Null method", boost::beast::http::status::bad_request);
|
|
break;
|
|
case rpc::ClioError::rpcCOMMAND_IS_EMPTY:
|
|
connection_->send("method is empty", boost::beast::http::status::bad_request);
|
|
break;
|
|
case rpc::ClioError::rpcCOMMAND_NOT_STRING:
|
|
connection_->send("method is not string", boost::beast::http::status::bad_request);
|
|
break;
|
|
case rpc::ClioError::rpcPARAMS_UNPARSEABLE:
|
|
connection_->send("params unparseable", boost::beast::http::status::bad_request);
|
|
break;
|
|
|
|
// others are not applicable but we want a compilation error next time we add one
|
|
case rpc::ClioError::rpcUNKNOWN_OPTION:
|
|
case rpc::ClioError::rpcMALFORMED_CURRENCY:
|
|
case rpc::ClioError::rpcMALFORMED_REQUEST:
|
|
case rpc::ClioError::rpcMALFORMED_OWNER:
|
|
case rpc::ClioError::rpcMALFORMED_ADDRESS:
|
|
case rpc::ClioError::rpcFIELD_NOT_FOUND_TRANSACTION:
|
|
case rpc::ClioError::rpcMALFORMED_ORACLE_DOCUMENT_ID:
|
|
case rpc::ClioError::rpcMALFORMED_AUTHORIZED_CREDENTIALS:
|
|
case rpc::ClioError::etlCONNECTION_ERROR:
|
|
case rpc::ClioError::etlREQUEST_ERROR:
|
|
case rpc::ClioError::etlREQUEST_TIMEOUT:
|
|
case rpc::ClioError::etlINVALID_RESPONSE:
|
|
ASSERT(
|
|
false, "Unknown rpc error code {}", static_cast<int>(*clioCode)
|
|
); // this should never happen
|
|
break;
|
|
}
|
|
} else {
|
|
connection_->send(boost::json::serialize(composeError(err)), boost::beast::http::status::bad_request);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
sendInternalError() const
|
|
{
|
|
connection_->send(
|
|
boost::json::serialize(composeError(rpc::RippledError::rpcINTERNAL)),
|
|
boost::beast::http::status::internal_server_error
|
|
);
|
|
}
|
|
|
|
void
|
|
sendNotReadyError() const
|
|
{
|
|
connection_->send(
|
|
boost::json::serialize(composeError(rpc::RippledError::rpcNOT_READY)), boost::beast::http::status::ok
|
|
);
|
|
}
|
|
|
|
void
|
|
sendTooBusyError() const
|
|
{
|
|
if (connection_->upgraded) {
|
|
connection_->send(
|
|
boost::json::serialize(rpc::makeError(rpc::RippledError::rpcTOO_BUSY)), boost::beast::http::status::ok
|
|
);
|
|
} else {
|
|
connection_->send(
|
|
boost::json::serialize(rpc::makeError(rpc::RippledError::rpcTOO_BUSY)),
|
|
boost::beast::http::status::service_unavailable
|
|
);
|
|
}
|
|
}
|
|
|
|
void
|
|
sendJsonParsingError() const
|
|
{
|
|
if (connection_->upgraded) {
|
|
connection_->send(boost::json::serialize(rpc::makeError(rpc::RippledError::rpcBAD_SYNTAX)));
|
|
} else {
|
|
connection_->send(
|
|
fmt::format("Unable to parse JSON from the request"), boost::beast::http::status::bad_request
|
|
);
|
|
}
|
|
}
|
|
|
|
boost::json::object
|
|
composeError(auto const& error) const
|
|
{
|
|
auto e = rpc::makeError(error);
|
|
|
|
if (request_) {
|
|
auto const appendFieldIfExist = [&](auto const& field) {
|
|
if (request_->contains(field) and not request_->at(field).is_null())
|
|
e[field] = request_->at(field);
|
|
};
|
|
|
|
appendFieldIfExist(JS(id));
|
|
|
|
if (connection_->upgraded)
|
|
appendFieldIfExist(JS(api_version));
|
|
|
|
e[JS(request)] = request_.value();
|
|
}
|
|
|
|
if (connection_->upgraded) {
|
|
return e;
|
|
}
|
|
return {{JS(result), e}};
|
|
}
|
|
};
|
|
|
|
} // namespace web::impl
|