refactor: Migrate handlers to rpc-spec (B) (#8484)

This commit is contained in:
Alex Kremer
2026-10-07 13:46:37 +00:00
committed by GitHub
parent 7acd719bf7
commit d343b542f1
31 changed files with 388 additions and 776 deletions

View File

@@ -60,6 +60,7 @@ Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta
- `account_lines`: The `peer` field now returns an error if the value is not a string. [#7728](https://github.com/XRPLF/rippled/pull/7728)
- `ledger`: `delivered_amount` is now included in the metadata of successful `AccountDelete` transactions when transactions are expanded (`expand`, or admin-only `full`). Previously it was only added for `Payment` and `CheckCash`, which made `ledger` inconsistent with `tx` and `account_tx`. [#5706](https://github.com/XRPLF/rippled/pull/5706)
- `noripple_check`: The `transactions` field is no longer included in error responses; it is still returned (possibly as an empty array) whenever `transactions` is `true` and the request succeeds. A malformed `account` is now rejected before the ledger is looked up, so that error response no longer carries the `ledger_hash`, `ledger_index`, and `validated` fields ([#6303](https://github.com/XRPLF/rippled/pull/6303)).
- `transaction_entry`: An object or an array in `tx_hash` now returns `malformedRequest`, like any other value that is not a hex hash, instead of an `internal` error.
## XRP Ledger server version 3.3.0

View File

@@ -296,7 +296,7 @@ if(xrpld)
OUT_VAR rpcspec_instantiations
VALUE_TYPE "::json::Value"
VIEW_HEADER "xrpld/rpc/detail/JsonObjectView.hpp"
HANDLERS ledger
HANDLERS book_changes ledger transaction_entry
)
target_sources(xrpld PRIVATE ${rpcspec_instantiations})

View File

@@ -3,7 +3,7 @@
"requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1782392402.420688",
"xrpl-rpc-spec/0.1.20#6daa13eeb4e6c92b82ea6d78866e1da8%1790720024.368048",
"xrpl-rpc-spec/0.1.21#d536f87a2ae7d313452746cfa3ac4404%1790869005.122975",
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1782392403.185447",
"soci/4.0.3#e726491a03468795453f7c83fc924a96%1782392402.679521",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1782307151.633168",

View File

@@ -40,7 +40,7 @@ class Xrpl(ConanFile):
"nudb/2.0.9",
"openssl/3.6.3",
"soci/4.0.3",
"xrpl-rpc-spec/0.1.20",
"xrpl-rpc-spec/0.1.21",
"zlib/1.3.2",
]

View File

@@ -1,4 +1,3 @@
#include <xrpld/rpc/Status.h>
#include <xrpld/rpc/detail/RPCHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
@@ -24,49 +23,50 @@ public:
// Test no type.
json::Value tx = json::ValueType::Object;
auto result = rpc::chooseLedgerEntryType(tx);
BEAST_EXPECT(result.first == rpc::Status::kOK);
BEAST_EXPECT(result.first == ::rpc::Status::kOK);
BEAST_EXPECT(result.second == 0);
// Test empty type.
tx[jss::type] = "";
result = rpc::chooseLedgerEntryType(tx);
BEAST_EXPECT(result.first == rpc::Status{RpcInvalidParams});
BEAST_EXPECT(result.first == ::rpc::Status(RpcInvalidParams, "Invalid field 'type'."));
BEAST_EXPECT(result.second == 0);
// Test type using canonical name in mixedcase.
tx[jss::type] = "MPTokenIssuance";
result = rpc::chooseLedgerEntryType(tx);
BEAST_EXPECT(result.first == rpc::Status::kOK);
BEAST_EXPECT(result.first == ::rpc::Status::kOK);
BEAST_EXPECT(result.second == ltMPTOKEN_ISSUANCE);
// Test type using canonical name in lowercase.
tx[jss::type] = "mptokenissuance";
result = rpc::chooseLedgerEntryType(tx);
BEAST_EXPECT(result.first == rpc::Status::kOK);
BEAST_EXPECT(result.first == ::rpc::Status::kOK);
BEAST_EXPECT(result.second == ltMPTOKEN_ISSUANCE);
// Test type using RPC name with exact match.
tx[jss::type] = "mpt_issuance";
result = rpc::chooseLedgerEntryType(tx);
BEAST_EXPECT(result.first == rpc::Status::kOK);
BEAST_EXPECT(result.first == ::rpc::Status::kOK);
BEAST_EXPECT(result.second == ltMPTOKEN_ISSUANCE);
// Test type using RPC name with inexact match.
tx[jss::type] = "MPT_Issuance";
result = rpc::chooseLedgerEntryType(tx);
BEAST_EXPECT(result.first == rpc::Status{RpcInvalidParams});
BEAST_EXPECT(result.first == ::rpc::Status(RpcInvalidParams, "Invalid field 'type'."));
BEAST_EXPECT(result.second == 0);
// Test invalid type.
tx[jss::type] = 1234;
result = rpc::chooseLedgerEntryType(tx);
BEAST_EXPECT(result.first == rpc::Status{RpcInvalidParams});
BEAST_EXPECT(
result.first == ::rpc::Status(RpcInvalidParams, "Invalid field 'type', not string."));
BEAST_EXPECT(result.second == 0);
// Test unknown type.
tx[jss::type] = "unknown";
result = rpc::chooseLedgerEntryType(tx);
BEAST_EXPECT(result.first == rpc::Status{RpcInvalidParams});
BEAST_EXPECT(result.first == ::rpc::Status(RpcInvalidParams, "Invalid field 'type'."));
BEAST_EXPECT(result.second == 0);
}

View File

@@ -1,207 +0,0 @@
#include <xrpld/rpc/Status.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/jss.h>
#include <algorithm>
#include <cstddef>
#include <exception>
#include <string>
namespace xrpl::rpc {
class codeString_test : public beast::unit_test::Suite
{
private:
template <typename Type>
std::string
codeString(Type t)
{
return Status(t).codeString();
}
void
testOk()
{
testcase("OK");
{
auto s = codeString(Status());
expect(s.empty(), "String for OK status");
}
{
auto s = codeString(Status::kOK);
expect(s.empty(), "String for OK status");
}
{
auto s = codeString(0);
expect(s.empty(), "String for 0 status");
}
{
auto s = codeString(tesSUCCESS);
expect(s.empty(), "String for tesSUCCESS");
}
{
auto s = codeString(RpcSuccess);
expect(s.empty(), "String for rpcSUCCESS");
}
}
void
testError()
{
testcase("error");
{
auto s = codeString(23);
expect(s == "23", s);
}
{
auto s = codeString(temBAD_AMOUNT);
expect(s == "temBAD_AMOUNT: Malformed: Bad amount.", s);
}
{
auto s = codeString(RpcBadSyntax);
expect(s == "badSyntax: Syntax error.", s);
}
}
public:
void
run() override
{
testOk();
testError();
}
};
BEAST_DEFINE_TESTSUITE(codeString, rpc, RPC);
class fillJson_test : public beast::unit_test::Suite
{
private:
json::Value value_;
template <typename Type>
void
fillJson(Type t)
{
value_.clear();
Status(t).fillJson(value_);
}
void
testOk()
{
testcase("OK");
fillJson(Status());
expect(!value_, "Value for empty status");
fillJson(0);
expect(!value_, "Value for 0 status");
fillJson(Status::kOK);
expect(!value_, "Value for OK status");
fillJson(tesSUCCESS);
expect(!value_, "Value for tesSUCCESS");
fillJson(RpcSuccess);
expect(!value_, "Value for rpcSUCCESS");
}
template <typename Type>
void
expectFill(
std::string const& label,
Type status,
Status::Strings messages,
std::string const& message)
{
value_.clear();
fillJson(Status(status, messages));
auto prefix = label + ": ";
expect(bool(value_), prefix + "No value");
auto error = value_[jss::error];
expect(bool(error), prefix + "No error.");
auto code = error[jss::code].asInt();
expect(
status == code,
prefix + "Wrong status " + std::to_string(code) + " != " + std::to_string(status));
auto m = error[jss::message].asString();
expect(m == message, m + " != " + message);
auto d = error[jss::data];
size_t const s1 = d.size();
size_t const s2 = messages.size();
expect(
s1 == s2,
prefix + "Data sizes differ " + std::to_string(s1) + " != " + std::to_string(s2));
for (auto i = 0; i < std::min(s1, s2); ++i)
{
auto ds = d[i].asString();
expect(ds == messages[i], prefix + ds + " != " + messages[i]);
}
}
void
testError()
{
testcase("error");
expectFill("temBAD_AMOUNT", temBAD_AMOUNT, {}, "temBAD_AMOUNT: Malformed: Bad amount.");
expectFill(
"RpcBadSyntax",
RpcBadSyntax,
{"An error.", "Another error."},
"badSyntax: Syntax error.");
expectFill("integer message", 23, {"Stuff."}, "23");
}
void
testThrow()
{
testcase("throw");
try
{
Throw<Status>(Status(temBAD_PATH, {"path=sdcdfd"}));
}
catch (Status const& s)
{
expect(s.toTER() == temBAD_PATH, "temBAD_PATH wasn't thrown");
auto msgs = s.messages();
expect(msgs.size() == 1, "Wrong number of messages");
expect(msgs[0] == "path=sdcdfd", msgs[0]);
}
catch (std::exception const&)
{
expect(false, "Didn't catch a Status");
}
}
public:
void
run() override
{
testOk();
testError();
testThrow();
}
};
BEAST_DEFINE_TESTSUITE(fillJson, rpc, xrpl);
} // namespace xrpl::rpc

View File

@@ -59,6 +59,19 @@ class TransactionEntry_test : public beast::unit_test::Suite
BEAST_EXPECT(result[jss::status] == "error");
}
{
// No ledger selects the current one, not any ledger.
json::Value params{json::ValueType::Object};
params[jss::tx_hash] =
"E2FE8D4AF3FCC3944DDF6CD8CDDC5E3F0AD50863EF8919AFEF10CB6408CD4D05";
auto const result = env.client().invoke("transaction_entry", params)[jss::result];
BEAST_EXPECT(result[jss::error] == "notYetImplemented");
BEAST_EXPECT(result[jss::status] == "error");
BEAST_EXPECT(result.isMember(jss::ledger_current_index));
BEAST_EXPECT(!result.isMember(jss::ledger_hash));
BEAST_EXPECT(result[jss::validated] == false);
}
{
json::Value params{json::ValueType::Object};
params[jss::ledger] = "closed";
@@ -69,6 +82,16 @@ class TransactionEntry_test : public beast::unit_test::Suite
BEAST_EXPECT(result[jss::status] == "error");
}
for (auto const type : {json::ValueType::Object, json::ValueType::Array})
{
json::Value params{json::ValueType::Object};
params[jss::ledger] = "closed";
params[jss::tx_hash] = json::Value{type};
auto const result = env.client().invoke("transaction_entry", params)[jss::result];
BEAST_EXPECT(result[jss::error] == "malformedRequest");
BEAST_EXPECT(result[jss::status] == "error");
}
std::string const txHash{
"E2FE8D4AF3FCC3944DDF6CD8CDDC5E3F0AD50863EF8919AFEF10CB6408CD4D05"};

View File

@@ -2,10 +2,11 @@
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/Role.h>
#include <xrpld/rpc/Status.h>
#include <xrpl/json/json_value.h>
#include <rpcspec/Errors.hpp>
#include <string_view>
namespace xrpl::rpc {
@@ -15,7 +16,7 @@ struct JsonContext;
/**
* Execute an RPC command and store the results in a json::Value.
*/
Status
::rpc::Status
doCommand(rpc::JsonContext&, json::Value&);
Role

View File

@@ -1,161 +0,0 @@
#pragma once
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/TER.h>
#include <exception>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace xrpl::rpc {
/**
* Status represents the results of an operation that might fail.
*
* It wraps the legacy codes TER and error_code_i, providing both a uniform
* interface and a way to attach additional information to existing status
* returns.
*
* A Status can also be used to fill a json::Value with a JSON-RPC 2.0
* error response: see http://www.jsonrpc.org/specification#error_object
*/
struct Status : public std::exception
{
public:
enum class Type { None, TER, ErrorCodeI };
using Code = int;
using Strings = std::vector<std::string>;
static constexpr Code kOK = 0;
Status() = default;
// The constraint allows only integers (not enums). Prevents enum narrowing.
template <typename T>
Status(T code, Strings d = {})
requires(std::is_integral_v<T>)
: code_(code), messages_(std::move(d))
{
}
Status(TER ter, Strings d = {})
: type_(Type::TER), code_(TERtoInt(ter)), messages_(std::move(d))
{
}
Status(ErrorCodeI e, Strings d = {})
: type_(Type::ErrorCodeI), code_(e), messages_(std::move(d))
{
}
Status(ErrorCodeI e, std::string const& s) : type_(Type::ErrorCodeI), code_(e), messages_({s})
{
}
/**
* If the Status is OK, the result is an empty string.
*
* @return a representation of the integer status Code as a string.
*/
[[nodiscard]] std::string
codeString() const;
/**
* Returns true if the Status is *not* OK.
*/
operator bool() const
{
return code_ != kOK;
}
/**
* Returns true if the Status is OK.
*/
bool
operator!() const
{
return !bool(*this);
}
/**
* Returns the Status as a TER.
* This may only be called if type() == Type::TER.
*/
[[nodiscard]] TER
toTER() const
{
XRPL_ASSERT(type_ == Type::TER, "xrpl::rpc::Status::toTER : type is TER");
return TER::fromInt(code_);
}
/**
* Returns the Status as an error_code_i.
* This may only be called if type() == Type::ErrorCodeI.
*/
[[nodiscard]] ErrorCodeI
toErrorCode() const
{
XRPL_ASSERT(
type_ == Type::ErrorCodeI, "xrpl::rpc::Status::toErrorCode : type is error code");
return ErrorCodeI(code_);
}
/**
* Apply the Status to a JsonObject
*/
void
inject(json::Value& object) const
{
if (auto ec = toErrorCode())
{
if (messages_.empty())
{
injectError(ec, object);
}
else
{
injectError(ec, message(), object);
}
}
}
[[nodiscard]] Strings const&
messages() const
{
return messages_;
}
/**
* Return the first message, if any.
*/
[[nodiscard]] std::string
message() const;
[[nodiscard]] Type
type() const
{
return type_;
}
[[nodiscard]] std::string
toString() const;
/**
* Fill a json::Value with an RPC 2.0 response.
* If the Status is OK, fillJson has no effect.
* Not currently used.
*/
void
fillJson(json::Value&);
private:
Type type_ = Type::None;
Code code_ = kOK;
Strings messages_;
};
} // namespace xrpl::rpc

View File

@@ -3,17 +3,17 @@
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/MethodNames.h>
#include <xrpld/rpc/Role.h>
#include <xrpld/rpc/Status.h>
#include <xrpld/rpc/detail/SpecBridge.hpp>
#include <xrpld/rpc/handlers/Handlers.h>
#include <xrpld/rpc/handlers/ledger/Ledger.h>
#include <xrpld/rpc/handlers/orderbook/BookChanges.h>
#include <xrpld/rpc/handlers/server_info/Version.h>
#include <xrpld/rpc/handlers/transaction/TransactionEntry.h>
#include <xrpl/basics/StringUtilities.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/ApiVersion.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <rpcspec/Errors.hpp>
#include <rpcspec/Types.hpp>
@@ -23,7 +23,6 @@
#include <concepts>
#include <cstddef>
#include <expected>
#include <iterator>
#include <span>
#include <string_view>
#include <utility>
@@ -41,7 +40,7 @@ using Method = Handler::Method;
* names a plain function instead of returning a closure over it.
*/
template <json::Value (*Function)(JsonContext&)>
Status
::rpc::Status
byRef(JsonContext& context, json::Value& result)
{
result = Function(context);
@@ -53,7 +52,7 @@ byRef(JsonContext& context, json::Value& result)
// LCOV_EXCL_STOP
}
return Status();
return ::rpc::Status::kOK;
}
template <typename HandlerImpl>
@@ -63,7 +62,7 @@ template <typename HandlerImpl>
concept InputlessHandler = requires(HandlerImpl& handler) { handler.process(); };
template <class HandlerImpl, class Output>
Status
::rpc::Status
respond(
HandlerImpl const& handler,
json::Value& object,
@@ -73,32 +72,29 @@ respond(
if (not output.has_value())
{
injectSpecError(object, output.error());
return std::get<ErrorCodeI>(output.error().code);
return output.error();
}
handler.writeResult(object, *output);
injectSpecWarnings(object, warnings);
json::Value result;
handler.writeResult(result, *output);
injectSpecWarnings(result, warnings);
object = std::move(result);
return Status::kOK;
return ::rpc::Status::kOK;
}
template <class HandlerImpl>
requires SpecBasedHandler<HandlerImpl> or InputlessHandler<HandlerImpl>
Status
::rpc::Status
handle(JsonContext& context, json::Value& object)
requires SpecBasedHandler<HandlerImpl> or InputlessHandler<HandlerImpl>
{
XRPL_ASSERT(
context.apiVersion >= HandlerImpl::minApiVer &&
context.apiVersion <= HandlerImpl::maxApiVer,
"xrpl::rpc::handle : valid API version");
if constexpr (SpecBasedHandler<HandlerImpl>)
{
auto const input = HandlerImpl::parseInput(context.params, context.apiVersion);
if (not input.has_value())
{
injectSpecError(object, input.error());
return std::get<ErrorCodeI>(input.error().code);
return input.error();
}
HandlerImpl handler(context);
@@ -115,28 +111,7 @@ handle(JsonContext& context, json::Value& object)
}
}
template <typename HandlerImpl>
constexpr Handler
handlerFrom()
{
static_assert(HandlerImpl::minApiVer <= HandlerImpl::maxApiVer);
static_assert(HandlerImpl::maxApiVer <= rpc::kApiMaximumValidVersion);
static_assert(rpc::kApiMinimumSupportedVersion <= HandlerImpl::minApiVer);
return {
HandlerImpl::name,
Method::of<&handle<HandlerImpl>>(),
HandlerImpl::role,
HandlerImpl::condition,
HandlerImpl::minApiVer,
HandlerImpl::maxApiVer,
};
}
// The handlers that name the function they dispatch to. The order is free:
// getHandler() searches kHandlers below, which is this array and the next one
// sorted together.
constexpr auto kFunctionHandlerArray = std::to_array<Handler>({
constexpr auto kHandlerArray = std::to_array<Handler>({
// Request-response methods
{
.name = method::kAccountInfo,
@@ -201,7 +176,7 @@ constexpr auto kFunctionHandlerArray = std::to_array<Handler>({
},
{
.name = method::kBookChanges,
.valueMethod = Method::of<&byRef<&doBookChanges>>(),
.valueMethod = Method::of<&handle<BookChangesHandler>>(),
.role = Role::USER,
.condition = Condition::NoCondition,
},
@@ -285,6 +260,12 @@ constexpr auto kFunctionHandlerArray = std::to_array<Handler>({
.condition = Condition::NoCondition,
.hasCommandLineForm = false,
},
{
.name = method::kLedger,
.valueMethod = Method::of<&handle<LedgerHandler>>(),
.role = Role::USER,
.condition = Condition::NoCondition,
},
{
.name = method::kLedgerAccept,
.valueMethod = Method::of<&byRef<&doLedgerAccept>>(),
@@ -492,7 +473,7 @@ constexpr auto kFunctionHandlerArray = std::to_array<Handler>({
},
{
.name = method::kTransactionEntry,
.valueMethod = Method::of<&byRef<&doTransactionEntry>>(),
.valueMethod = Method::of<&handle<TransactionEntryHandler>>(),
.role = Role::USER,
.condition = Condition::NoCondition,
},
@@ -555,6 +536,12 @@ constexpr auto kFunctionHandlerArray = std::to_array<Handler>({
.role = Role::USER,
.condition = Condition::NoCondition,
},
{
.name = method::kVersion,
.valueMethod = Method::of<&handle<VersionHandler>>(),
.role = Role::USER,
.condition = Condition::NoCondition,
},
{
.name = method::kWalletPropose,
.valueMethod = Method::of<&byRef<&doWalletPropose>>(),
@@ -576,39 +563,9 @@ constexpr auto kFunctionHandlerArray = std::to_array<Handler>({
},
});
// The class-based handlers, which carry their name and API range as static
// members rather than as a table entry, so they cannot go in the array above.
constexpr auto kClassHandlerArray = std::to_array<Handler>({
handlerFrom<LedgerHandler>(),
handlerFrom<VersionHandler>(),
});
/**
* Join the two handler arrays above into one.
*
* Handler has no default constructor, so every entry is built in place from an
* index pack rather than the array being sized and then copied into. The packs
* come from the arrays themselves, so adding a handler to either needs no change
* here.
*
* @return kFunctionHandlerArray followed by kClassHandlerArray.
*/
constexpr auto
joinHandlers()
{
constexpr auto kFunctionIndices = std::make_index_sequence<std::size(kFunctionHandlerArray)>{};
constexpr auto kClassIndices = std::make_index_sequence<std::size(kClassHandlerArray)>{};
return []<std::size_t... Function, std::size_t... Class>(
std::index_sequence<Function...>, std::index_sequence<Class...>) {
return std::array<Handler, sizeof...(Function) + sizeof...(Class)>{
kFunctionHandlerArray[Function]..., kClassHandlerArray[Class]...};
}(kFunctionIndices, kClassIndices);
}
// The whole dispatch table.
constexpr auto kHandlers = [] {
auto all = joinHandlers();
auto all = kHandlerArray;
// Sorted by name, so a handler can be found by binary search.
std::ranges::sort(all, {}, &Handler::name);

View File

@@ -2,7 +2,6 @@
#include <xrpld/rpc/RPCHandler.h>
#include <xrpld/rpc/Role.h>
#include <xrpld/rpc/Status.h>
#include <xrpld/rpc/detail/Tuning.h>
#include <xrpl/basics/Log.h>
@@ -13,6 +12,8 @@
#include <xrpl/protocol/jss.h>
#include <xrpl/server/NetworkOPs.h>
#include <rpcspec/Errors.hpp>
#include <span>
#include <string_view>
@@ -47,7 +48,7 @@ struct Handler
class Method
{
public:
using Function = Status (*)(JsonContext&, json::Value&);
using Function = ::rpc::Status (*)(JsonContext&, json::Value&);
/**
* Build a Method that calls a given function.
@@ -69,7 +70,7 @@ struct Handler
* @param result The object the function writes its reply into.
* @return The status the function returns.
*/
Status
::rpc::Status
operator()(JsonContext& context, json::Value& result) const
{
return fn_(context, result);

View File

@@ -5,7 +5,6 @@
#include <xrpld/core/Config.h>
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/Role.h>
#include <xrpld/rpc/Status.h>
#include <xrpld/rpc/detail/Handler.h>
#include <xrpld/rpc/detail/Tuning.h>
@@ -18,6 +17,8 @@
#include <xrpl/protocol/jss.h>
#include <xrpl/resource/Fees.h>
#include <rpcspec/Errors.hpp>
#include <atomic>
#include <chrono>
#include <cstdint>
@@ -154,7 +155,7 @@ fillHandler(JsonContext& context, Handler const*& result)
return RpcSuccess;
}
Status
::rpc::Status
callMethod(JsonContext& context, Handler::Method method, std::string_view name, json::Value& result)
{
static std::atomic<std::uint64_t> kRequestId{0};
@@ -190,7 +191,7 @@ callMethod(JsonContext& context, Handler::Method method, std::string_view name,
} // namespace
Status
::rpc::Status
doCommand(rpc::JsonContext& context, json::Value& result)
{
Handler const* handler = nullptr;

View File

@@ -3,7 +3,6 @@
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/DeliveredAmount.h>
#include <xrpld/rpc/Role.h>
#include <xrpld/rpc/Status.h>
#include <xrpld/rpc/detail/Tuning.h>
#include <xrpl/basics/Log.h>
@@ -12,7 +11,6 @@
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/AccountID.h>
@@ -33,6 +31,8 @@
#include <boost/algorithm/string/predicate.hpp>
#include <rpcspec/Errors.hpp>
#include <algorithm>
#include <array>
#include <cstdint>
@@ -370,10 +370,10 @@ keypairForSignature(json::Value const& params, json::Value& error, unsigned int
return generateKeyPair(*keyType, *seed);
}
std::pair<rpc::Status, LedgerEntryType>
std::pair<::rpc::Status, LedgerEntryType>
chooseLedgerEntryType(json::Value const& params)
{
std::pair<rpc::Status, LedgerEntryType> result{rpc::Status::kOK, ltANY};
std::pair<::rpc::Status, LedgerEntryType> result{::rpc::Status::kOK, ltANY};
if (params.isMember(jss::type))
{
static constexpr auto kTypes =
@@ -392,10 +392,7 @@ chooseLedgerEntryType(json::Value const& params)
auto const& p = params[jss::type];
if (!p.isString())
{
result.first = rpc::Status{RpcInvalidParams, "Invalid field 'type', not string."};
XRPL_ASSERT(
result.first.type() == rpc::Status::Type::ErrorCodeI,
"xrpl::rpc::chooseLedgerEntryType : first valid result type");
result.first = ::rpc::Status{RpcInvalidParams, "Invalid field 'type', not string."};
return result;
}
@@ -408,11 +405,7 @@ chooseLedgerEntryType(json::Value const& params)
});
if (iter == kTypes.end())
{
result.first = rpc::Status{RpcInvalidParams, "Invalid field 'type'."};
XRPL_ASSERT(
result.first.type() == rpc::Status::Type::ErrorCodeI,
"xrpl::rpc::chooseLedgerEntryType : second valid result "
"type");
result.first = ::rpc::Status{RpcInvalidParams, "Invalid field 'type'."};
return result;
}
result.second = std::get<2>(*iter);

View File

@@ -1,7 +1,6 @@
#pragma once
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/Status.h>
#include <xrpld/rpc/detail/Tuning.h>
#include <xrpl/basics/UnorderedContainers.h>
@@ -21,6 +20,8 @@
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/server/NetworkOPs.h>
#include <rpcspec/Errors.hpp>
#include <cstdint>
#include <optional>
#include <utility>
@@ -147,7 +148,7 @@ parseXrplLibSeed(json::Value const& params);
* @param params The JSON value containing RPC parameters.
* @return A pair consisting of the RPC status and the chosen LedgerEntryType.
*/
std::pair<rpc::Status, LedgerEntryType>
std::pair<::rpc::Status, LedgerEntryType>
chooseLedgerEntryType(json::Value const& params);
/**

View File

@@ -6,7 +6,7 @@
#include <xrpld/app/ledger/LedgerToJson.h>
#include <xrpld/app/main/Application.h>
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/Status.h>
#include <xrpld/rpc/detail/SpecBridge.hpp>
#include <xrpld/rpc/detail/Tuning.h>
#include <xrpl/basics/base_uint.h>
@@ -24,6 +24,7 @@
#include <org/xrpl/rpc/v1/get_ledger_data.pb.h> // IWYU pragma: keep
#include <org/xrpl/rpc/v1/get_ledger_entry.pb.h> // IWYU pragma: keep
#include <org/xrpl/rpc/v1/ledger.pb.h>
#include <rpcspec/Errors.hpp>
#include <rpcspec/Ledger.hpp>
#include <cstdint>
@@ -56,7 +57,7 @@ isValidatedOld(LedgerMaster& ledgerMaster, bool standalone)
}
template <class T>
Status
::rpc::Status
ledgerFromHash(
T& ledger,
json::Value hash,
@@ -70,7 +71,7 @@ ledgerFromHash(
}
template <class T>
Status
::rpc::Status
ledgerFromIndex(
T& ledger,
json::Value indexValue,
@@ -96,7 +97,7 @@ ledgerFromIndex(
}
template <class T>
Status
::rpc::Status
ledgerFromRequest(T& ledger, JsonContext const& context)
{
ledger.reset();
@@ -163,7 +164,7 @@ ledgerFromRequest(T& ledger, JsonContext const& context)
} // namespace
template <class T, class R>
Status
::rpc::Status
ledgerFromRequest(T& ledger, GRPCContext<R> const& context)
{
R const& request = context.params;
@@ -171,25 +172,25 @@ ledgerFromRequest(T& ledger, GRPCContext<R> const& context)
}
// explicit instantiation of above function
template Status
template ::rpc::Status
ledgerFromRequest<>(
std::shared_ptr<ReadView const>&,
GRPCContext<org::xrpl::rpc::v1::GetLedgerEntryRequest> const&);
// explicit instantiation of above function
template Status
template ::rpc::Status
ledgerFromRequest<>(
std::shared_ptr<ReadView const>&,
GRPCContext<org::xrpl::rpc::v1::GetLedgerDataRequest> const&);
// explicit instantiation of above function
template Status
template ::rpc::Status
ledgerFromRequest<>(
std::shared_ptr<ReadView const>&,
GRPCContext<org::xrpl::rpc::v1::GetLedgerRequest> const&);
template <class T>
Status
::rpc::Status
ledgerFromSpecifier(
T& ledger,
org::xrpl::rpc::v1::LedgerSpecifier const& specifier,
@@ -231,21 +232,21 @@ ledgerFromSpecifier(
}
}
return Status::kOK;
return ::rpc::Status::kOK;
}
template <class T>
Status
::rpc::Status
getLedger(T& ledger, UInt256 const& ledgerHash, Context const& context)
{
ledger = context.ledgerMaster.getLedgerByHash(ledgerHash);
if (ledger == nullptr)
return {RpcLgrNotFound, "ledgerNotFound"};
return Status::kOK;
return ::rpc::Status::kOK;
}
template <class T>
Status
::rpc::Status
getLedger(T& ledger, uint32_t ledgerIndex, Context const& context)
{
ledger = context.ledgerMaster.getLedgerBySeq(ledgerIndex);
@@ -270,11 +271,11 @@ getLedger(T& ledger, uint32_t ledgerIndex, Context const& context)
return {RpcNotSynced, "notSynced"};
}
return Status::kOK;
return ::rpc::Status::kOK;
}
template <class T>
Status
::rpc::Status
getLedger(T& ledger, ::rpc::spec::LedgerShortcut shortcut, Context const& context)
{
if (isValidatedOld(context.ledgerMaster, context.app.config().standalone()))
@@ -330,11 +331,11 @@ getLedger(T& ledger, ::rpc::spec::LedgerShortcut shortcut, Context const& contex
return {RpcNotSynced, "notSynced"};
}
}
return Status::kOK;
return ::rpc::Status::kOK;
}
template <class T>
Status
::rpc::Status
getLedger(T& ledger, ::rpc::spec::LedgerSpecifier const& specifier, Context const& context)
{
return std::visit(
@@ -348,20 +349,20 @@ getLedger(T& ledger, ::rpc::spec::LedgerSpecifier const& specifier, Context cons
}
// Explicit instantiation of above four functions
template Status
template ::rpc::Status
getLedger<>(std::shared_ptr<ReadView const>&, uint32_t, Context const&);
template Status
template ::rpc::Status
getLedger<>(std::shared_ptr<ReadView const>&, ::rpc::spec::LedgerShortcut shortcut, Context const&);
template Status
template ::rpc::Status
getLedger<>(std::shared_ptr<ReadView const>&, UInt256 const&, Context const&);
template Status
template ::rpc::Status
getLedger<>(std::shared_ptr<ReadView const>&, ::rpc::spec::LedgerSpecifier const&, Context const&);
// explicit instantiation of ledgerFromSpecifier
template Status
template ::rpc::Status
ledgerFromSpecifier<>(
std::shared_ptr<ReadView const>&,
org::xrpl::rpc::v1::LedgerSpecifier const&,
@@ -386,18 +387,12 @@ ledgerFromSpecifier<>(
// return value. Otherwise, the object contains the field "validated" and
// optionally the fields "ledger_hash", "ledger_index" and
// "ledger_current_index", if they are defined.
Status
lookupLedger(
std::shared_ptr<ReadView const>& ledger,
JsonContext const& context,
json::Value& result)
void
injectLedgerFields(ReadView const& ledger, Context const& context, json::Value& result)
{
if (auto status = ledgerFromRequest(ledger, context))
return status;
auto const& info = ledger.header();
auto& info = ledger->header();
if (!ledger->open())
if (!ledger.open())
{
result[jss::ledger_hash] = to_string(info.hash);
result[jss::ledger_index] = info.seq;
@@ -407,8 +402,20 @@ lookupLedger(
result[jss::ledger_current_index] = info.seq;
}
result[jss::validated] = context.ledgerMaster.isValidated(*ledger);
return Status::kOK;
result[jss::validated] = context.ledgerMaster.isValidated(ledger);
}
::rpc::Status
lookupLedger(
std::shared_ptr<ReadView const>& ledger,
JsonContext const& context,
json::Value& result)
{
if (auto status = ledgerFromRequest(ledger, context))
return status;
injectLedgerFields(*ledger, context, result);
return ::rpc::Status::kOK;
}
json::Value
@@ -416,7 +423,7 @@ lookupLedger(std::shared_ptr<ReadView const>& ledger, JsonContext const& context
{
json::Value result;
if (auto status = lookupLedger(ledger, context, result))
status.inject(result);
injectSpecError(result, status);
return result;
}

View File

@@ -1,7 +1,6 @@
#pragma once
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/Status.h>
#include <xrpld/rpc/detail/Tuning.h>
#include <xrpl/basics/base_uint.h>
@@ -10,6 +9,7 @@
#include <xrpl/server/NetworkOPs.h>
#include <org/xrpl/rpc/v1/ledger.pb.h>
#include <rpcspec/Errors.hpp>
#include <rpcspec/Ledger.hpp>
#include <cstdint>
@@ -39,7 +39,7 @@ struct JsonContext;
* @return Status indicating success or failure of the operation.
*/
template <class T>
Status
::rpc::Status
getLedger(T& ledger, UInt256 const& ledgerHash, Context const& context);
/**
@@ -57,7 +57,7 @@ getLedger(T& ledger, UInt256 const& ledgerHash, Context const& context);
* @return Status indicating success or failure of the operation.
*/
template <class T>
Status
::rpc::Status
getLedger(T& ledger, uint32_t ledgerIndex, Context const& context);
/**
@@ -75,7 +75,7 @@ getLedger(T& ledger, uint32_t ledgerIndex, Context const& context);
* @return Status indicating success or failure of the operation.
*/
template <class T>
Status
::rpc::Status
getLedger(T& ledger, ::rpc::spec::LedgerShortcut shortcut, Context const& context);
/**
@@ -90,9 +90,22 @@ getLedger(T& ledger, ::rpc::spec::LedgerShortcut shortcut, Context const& contex
* @return Status indicating success or failure of the operation.
*/
template <class T>
Status
::rpc::Status
getLedger(T& ledger, ::rpc::spec::LedgerSpecifier const& specifier, Context const& context);
/**
* @brief Writes the fields that identify a ledger into a result.
*
* A closed ledger gets `ledger_hash` and `ledger_index`, an open one
* `ledger_current_index`, and both get `validated`.
*
* @param ledger The ledger to describe.
* @param context The RPC context.
* @param result Reference to a json::Value to be filled with ledger data.
*/
void
injectLedgerFields(ReadView const& ledger, Context const& context, json::Value& result);
/**
* @brief Looks up a ledger from a request and returns a json::Value with either
* an error or ledger data.
@@ -126,7 +139,7 @@ lookupLedger(std::shared_ptr<ReadView const>&, JsonContext const&);
* @param result Reference to a json::Value to be filled with ledger data.
* @return Status indicating success or failure of the operation.
*/
Status
::rpc::Status
lookupLedger(std::shared_ptr<ReadView const>&, JsonContext const&, json::Value& result);
/**
@@ -144,7 +157,7 @@ lookupLedger(std::shared_ptr<ReadView const>&, JsonContext const&, json::Value&
* @return Status indicating success or failure of the operation.
*/
template <class T, class R>
Status
::rpc::Status
ledgerFromRequest(T& ledger, GRPCContext<R> const& context);
/**
@@ -160,7 +173,7 @@ ledgerFromRequest(T& ledger, GRPCContext<R> const& context);
* @return Status indicating success or failure of the operation.
*/
template <class T>
Status
::rpc::Status
ledgerFromSpecifier(
T& ledger,
org::xrpl::rpc::v1::LedgerSpecifier const& specifier,

View File

@@ -12,6 +12,7 @@
#include <map>
#include <string>
#include <variant>
#include <vector>
namespace xrpl::rpc {
@@ -19,6 +20,10 @@ namespace xrpl::rpc {
template <typename InputT>
using HandlerFor = ::rpc::spec::HandlerFor<InputT, json::Value>;
static_assert(
std::variant_size_v<::rpc::CombinedError> == 1,
"xrpl::rpc : an xrpld ::rpc::Status can hold only an ErrorCodeI");
inline void
injectSpecError(json::Value& object, ::rpc::Status const& status)
{

View File

@@ -1,88 +0,0 @@
#include <xrpld/rpc/Status.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/jss.h>
#include <sstream>
#include <string>
namespace xrpl::rpc {
std::string
Status::codeString() const
{
if (!*this)
return "";
if (type_ == Type::None)
return std::to_string(code_);
if (type_ == Status::Type::TER)
{
std::string s1, s2;
[[maybe_unused]] auto const success = transResultInfo(toTER(), s1, s2);
XRPL_ASSERT(success, "xrpl::rpc::codeString : valid TER result");
return s1 + ": " + s2;
}
if (type_ == Status::Type::ErrorCodeI)
{
auto info = getErrorInfo(toErrorCode());
std::ostringstream sStr;
sStr << info.token.cStr() << ": " << info.message.cStr();
return sStr.str();
}
// LCOV_EXCL_START
UNREACHABLE("xrpl::rpc::codeString : invalid type");
return "";
// LCOV_EXCL_STOP
}
void
Status::fillJson(json::Value& value)
{
if (!*this)
return;
auto& error = value[jss::error];
error[jss::code] = code_;
error[jss::message] = codeString();
// Are there any more messages?
if (!messages_.empty())
{
auto& messages = error[jss::data];
for (auto& i : messages_)
messages.append(i);
}
}
std::string
Status::message() const
{
std::string result;
for (auto& m : messages_)
{
if (!result.empty())
result += '/';
result += m;
}
return result;
}
std::string
Status::toString() const
{
if (*this)
return codeString() + ":" + message();
return "";
}
} // namespace xrpl::rpc

View File

@@ -27,8 +27,6 @@ doAMMInfo(rpc::JsonContext&);
json::Value
doBookOffers(rpc::JsonContext&);
json::Value
doBookChanges(rpc::JsonContext&);
json::Value
doBlackList(rpc::JsonContext&);
json::Value
doCanDelete(rpc::JsonContext&);
@@ -127,8 +125,6 @@ doSubmitMultiSigned(rpc::JsonContext&);
json::Value
doSubscribe(rpc::JsonContext&);
json::Value
doTransactionEntry(rpc::JsonContext&);
json::Value
doTxJson(rpc::JsonContext&);
json::Value
doTxHistory(rpc::JsonContext&);

View File

@@ -1,6 +1,7 @@
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/detail/RPCHelpers.h>
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
#include <xrpld/rpc/detail/SpecBridge.hpp>
#include <xrpld/rpc/detail/Tuning.h>
#include <xrpl/basics/base_uint.h>
@@ -340,7 +341,7 @@ doAccountObjects(rpc::JsonContext& context)
if (rpcStatus)
{
result.clear();
rpcStatus.inject(result);
rpc::injectSpecError(result, rpcStatus);
return result;
}
if (type != ltANY)

View File

@@ -5,9 +5,9 @@
#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/Role.h>
#include <xrpld/rpc/Status.h>
#include <xrpld/rpc/detail/RPCHelpers.h>
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
#include <xrpld/rpc/detail/SpecBridge.hpp>
#include <xrpld/rpc/detail/SyntheticFields.h>
#include <xrpld/rpc/detail/Tuning.h>
@@ -26,6 +26,7 @@
#include <xrpl/rdb/RelationalDatabase.h>
#include <xrpl/resource/Fees.h>
#include <rpcspec/Errors.hpp>
#include <rpcspec/Ledger.hpp>
#include <cstdint>
@@ -114,8 +115,8 @@ parseLedgerArgs(rpc::Context& context, json::Value const& params)
if ((params.isMember(jss::ledger_index_min) || params.isMember(jss::ledger_index_max)) &&
(params.isMember(jss::ledger_hash) || params.isMember(jss::ledger_index)))
{
rpc::Status const status{RpcInvalidParams, "invalidParams"};
status.inject(response);
::rpc::Status const status{RpcInvalidParams, "invalidParams"};
rpc::injectSpecError(response, status);
return response;
}
}
@@ -137,16 +138,16 @@ parseLedgerArgs(rpc::Context& context, json::Value const& params)
auto& hashValue = params[jss::ledger_hash];
if (!hashValue.isString())
{
rpc::Status const status{RpcInvalidParams, "ledgerHashNotString"};
status.inject(response);
::rpc::Status const status{RpcInvalidParams, "ledgerHashNotString"};
rpc::injectSpecError(response, status);
return response;
}
LedgerHash hash;
if (!hash.parseHex(hashValue.asString()))
{
rpc::Status const status{RpcInvalidParams, "ledgerHashMalformed"};
status.inject(response);
::rpc::Status const status{RpcInvalidParams, "ledgerHashMalformed"};
rpc::injectSpecError(response, status);
return response;
}
return hash;
@@ -176,8 +177,8 @@ parseLedgerArgs(rpc::Context& context, json::Value const& params)
}
else
{
rpc::Status const status{RpcInvalidParams, "ledger_index string malformed"};
status.inject(response);
::rpc::Status const status{RpcInvalidParams, "ledger_index string malformed"};
rpc::injectSpecError(response, status);
return response;
}
}
@@ -186,7 +187,7 @@ parseLedgerArgs(rpc::Context& context, json::Value const& params)
return std::optional<LedgerSpecifier>{};
}
std::variant<LedgerRange, rpc::Status>
std::variant<LedgerRange, ::rpc::Status>
getLedgerRange(rpc::Context& context, std::optional<LedgerSpecifier> const& ledgerSpecifier)
{
std::uint32_t uValidatedMin = 0;
@@ -207,7 +208,7 @@ getLedgerRange(rpc::Context& context, std::optional<LedgerSpecifier> const& ledg
if (ledgerSpecifier)
{
auto status = std::visit(
[&](auto const& ls) -> rpc::Status {
[&](auto const& ls) -> ::rpc::Status {
using T = std::decay_t<decltype(ls)>;
if constexpr (std::is_same_v<T, LedgerRange>)
{
@@ -255,7 +256,7 @@ getLedgerRange(rpc::Context& context, std::optional<LedgerSpecifier> const& ledg
}
uLedgerMin = uLedgerMax = ledgerView->header().seq;
}
return rpc::Status::kOK;
return ::rpc::Status::kOK;
},
*ledgerSpecifier);
@@ -265,7 +266,7 @@ getLedgerRange(rpc::Context& context, std::optional<LedgerSpecifier> const& ledg
return LedgerRange{.min = uLedgerMin, .max = uLedgerMax};
}
std::pair<AccountTxResult, rpc::Status>
std::pair<AccountTxResult, ::rpc::Status>
doAccountTxHelp(rpc::Context& context, AccountTxArgs const& args)
{
context.loadType = resource::kFeeMediumBurdenRpc;
@@ -273,7 +274,7 @@ doAccountTxHelp(rpc::Context& context, AccountTxArgs const& args)
AccountTxResult result;
auto lgrRange = getLedgerRange(context, args.ledger);
if (auto stat = std::get_if<rpc::Status>(&lgrRange))
if (auto stat = std::get_if<::rpc::Status>(&lgrRange))
{
// An error occurred getting the requested ledger range
return {result, *stat};
@@ -332,15 +333,15 @@ doAccountTxHelp(rpc::Context& context, AccountTxArgs const& args)
json::Value
populateJsonResponse(
std::pair<AccountTxResult, rpc::Status> const& res,
std::pair<AccountTxResult, ::rpc::Status> const& res,
AccountTxArgs const& args,
rpc::JsonContext const& context)
{
json::Value response;
rpc::Status const& error = res.second;
if (error.toErrorCode() != RpcSuccess)
::rpc::Status const& error = res.second;
if (error != RpcSuccess)
{
error.inject(response);
rpc::injectSpecError(response, error);
}
else
{
@@ -512,11 +513,11 @@ doAccountTx(rpc::JsonContext& context)
!token[jss::ledger].isConvertibleTo(json::ValueType::UInt) ||
!token[jss::seq].isConvertibleTo(json::ValueType::UInt))
{
rpc::Status const status{
::rpc::Status const status{
RpcInvalidParams,
"invalid marker. Provide ledger index via ledger field, and "
"transaction sequence number via seq field"};
status.inject(response);
rpc::injectSpecError(response, status);
return response;
}
args.marker = {
@@ -546,11 +547,11 @@ doAccountTx(rpc::JsonContext& context)
params[jss::marker][jss::delegate].asBool();
if (markerFromDelegate != args.delegate.has_value())
{
rpc::Status const status{
::rpc::Status const status{
RpcInvalidParams,
"Do not mix delegate and non-delegate pagination markers in account_tx; "
"repeat the same `delegate` object when using a delegate marker."};
status.inject(response);
rpc::injectSpecError(response, status);
return response;
}
}

View File

@@ -5,7 +5,6 @@
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/GRPCHandlers.h>
#include <xrpld/rpc/Role.h>
#include <xrpld/rpc/Status.h>
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
#include <xrpl/basics/Log.h>
@@ -57,7 +56,7 @@ LedgerHandler::process(Input const& input)
return output;
if (auto const status = getLedger(output.ledger, input.ledger, context_.get()))
return std::unexpected{::rpc::Status{status.toErrorCode(), status.message()}};
return std::unexpected{status};
if (input.full || input.accounts)
{
@@ -134,13 +133,13 @@ doLedgerGrpc(rpc::GRPCContext<org::xrpl::rpc::v1::GetLedgerRequest>& context)
if (auto status = rpc::ledgerFromRequest(ledger, context))
{
grpc::Status errorStatus;
if (status.toErrorCode() == RpcInvalidParams)
if (status == RpcInvalidParams)
{
errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message());
errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message);
}
else
{
errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message);
}
return {response, errorStatus};
}

View File

@@ -3,14 +3,10 @@
#include <xrpld/app/main/Application.h>
#include <xrpld/app/misc/TxQ.h> // IWYU pragma: keep
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/MethodNames.h>
#include <xrpld/rpc/Role.h>
#include <xrpld/rpc/detail/Handler.h>
#include <xrpld/rpc/detail/SpecBridge.hpp>
#include <xrpl/json/json_value.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/ApiVersion.h>
#include <rpcspec/Errors.hpp>
#include <rpcspec/handlers/ledger/Types.hpp>
@@ -18,7 +14,6 @@
#include <expected>
#include <functional>
#include <memory>
#include <string_view>
#include <vector>
namespace xrpl::rpc {
@@ -49,18 +44,6 @@ public:
void
writeResult(json::Value& value, Output const& output) const;
// NOLINTBEGIN(readability-identifier-naming)
static constexpr std::string_view name = method::kLedger;
static constexpr unsigned minApiVer = rpc::kApiMinimumSupportedVersion;
static constexpr unsigned maxApiVer = rpc::kApiMaximumValidVersion;
static constexpr Role role = Role::USER;
static constexpr Condition condition = Condition::NoCondition;
// NOLINTEND(readability-identifier-naming)
private:
std::reference_wrapper<JsonContext> context_;
};

View File

@@ -4,6 +4,7 @@
#include <xrpld/rpc/Role.h>
#include <xrpld/rpc/detail/RPCHelpers.h>
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
#include <xrpld/rpc/detail/SpecBridge.hpp>
#include <xrpld/rpc/detail/Tuning.h>
#include <xrpl/basics/base_uint.h>
@@ -90,7 +91,7 @@ doLedgerData(rpc::JsonContext& context)
if (rpcStatus)
{
jvResult.clear();
rpcStatus.inject(jvResult);
rpc::injectSpecError(jvResult, rpcStatus);
return jvResult;
}
json::Value& nodes = jvResult[jss::state];
@@ -141,13 +142,13 @@ doLedgerDataGrpc(rpc::GRPCContext<org::xrpl::rpc::v1::GetLedgerDataRequest>& con
if (auto status = rpc::ledgerFromRequest(ledger, context))
{
grpc::Status errorStatus;
if (status.toErrorCode() == RpcInvalidParams)
if (status == RpcInvalidParams)
{
errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message());
errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message);
}
else
{
errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message);
}
return {response, errorStatus};
}

View File

@@ -1111,13 +1111,13 @@ doLedgerEntryGrpc(rpc::GRPCContext<org::xrpl::rpc::v1::GetLedgerEntryRequest>& c
if (auto status = rpc::ledgerFromRequest(ledger, context))
{
grpc::Status errorStatus;
if (status.toErrorCode() == RpcInvalidParams)
if (status == RpcInvalidParams)
{
errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message());
errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message);
}
else
{
errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message);
}
return {response, errorStatus};
}

View File

@@ -1,25 +1,36 @@
#include <xrpld/rpc/BookChanges.h>
#include <xrpld/rpc/handlers/orderbook/BookChanges.h>
#include <xrpld/rpc/BookChanges.h>
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/STArray.h> // IWYU pragma: keep
#include <memory>
#include <rpcspec/Errors.hpp>
namespace xrpl {
#include <expected>
json::Value
doBookChanges(rpc::JsonContext& context)
namespace xrpl::rpc {
BookChangesHandler::BookChangesHandler(JsonContext& context) : context_(context)
{
std::shared_ptr<ReadView const> ledger;
json::Value result = rpc::lookupLedger(ledger, context);
if (ledger == nullptr)
return result;
return rpc::computeBookChanges(ledger);
}
} // namespace xrpl
std::expected<BookChangesHandler::Output, ::rpc::Status>
BookChangesHandler::process(Input const& input) const
{
Output output;
if (auto const status = getLedger(output.ledger, input.ledger, context_.get()))
return std::unexpected{status};
return output;
}
void
BookChangesHandler::writeResult(json::Value& value, Output const& output)
{
value = computeBookChanges(output.ledger);
}
} // namespace xrpl::rpc

View File

@@ -0,0 +1,38 @@
#pragma once
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/detail/SpecBridge.hpp>
#include <xrpl/json/json_value.h>
#include <xrpl/ledger/ReadView.h>
#include <rpcspec/Errors.hpp>
#include <rpcspec/handlers/book_changes/Types.hpp>
#include <expected>
#include <functional>
#include <memory>
namespace xrpl::rpc {
class BookChangesHandler : public HandlerFor<::rpc::spec::handlers::book_changes::Input>
{
public:
struct Output
{
std::shared_ptr<ReadView const> ledger;
};
explicit BookChangesHandler(JsonContext&);
[[nodiscard]] std::expected<Output, ::rpc::Status>
process(Input const& input) const;
static void
writeResult(json::Value& value, Output const& output);
private:
std::reference_wrapper<JsonContext> context_;
};
} // namespace xrpl::rpc

View File

@@ -2,9 +2,6 @@
#include <xrpld/app/main/Application.h> // IWYU pragma: keep
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/MethodNames.h>
#include <xrpld/rpc/Role.h>
#include <xrpld/rpc/detail/Handler.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/ApiVersion.h>
@@ -14,7 +11,6 @@
#include <cstdint>
#include <expected>
#include <functional>
#include <string_view>
namespace xrpl::rpc {
@@ -46,18 +42,6 @@ public:
setVersion(obj, output.apiVersion, output.betaEnabled);
}
// NOLINTBEGIN(readability-identifier-naming)
static constexpr std::string_view name = method::kVersion;
static constexpr unsigned minApiVer = rpc::kApiMinimumSupportedVersion;
static constexpr unsigned maxApiVer = rpc::kApiMaximumValidVersion;
static constexpr Role role = Role::USER;
static constexpr Condition condition = Condition::NoCondition;
// NOLINTEND(readability-identifier-naming)
private:
std::reference_wrapper<JsonContext> context_;
};

View File

@@ -1,3 +1,5 @@
#include <xrpld/rpc/handlers/transaction/TransactionEntry.h>
#include <xrpld/app/ledger/LedgerMaster.h>
#include <xrpld/app/misc/DeliverMax.h>
#include <xrpld/rpc/Context.h>
@@ -9,93 +11,95 @@
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/jss.h>
#include <memory>
#include <rpcspec/Errors.hpp>
#include <rpcspec/handlers/transaction_entry/Types.hpp>
namespace xrpl {
#include <expected>
#include <string>
#include <tuple>
// {
// ledger_hash : <ledger>,
// ledger_index : <ledger_index>
// }
//
// XXX In this case, not specify either ledger does not mean ledger current. It
// means any ledger.
json::Value
doTransactionEntry(rpc::JsonContext& context)
namespace xrpl::rpc {
TransactionEntryHandler::TransactionEntryHandler(JsonContext& context) : context_(context)
{
std::shared_ptr<ReadView const> lpLedger;
json::Value jvResult = rpc::lookupLedger(lpLedger, context);
}
if (!lpLedger)
return jvResult;
// With no ledger specified, this uses the current ledger, as every other method
// does. An open ledger is refused with notYetImplemented, so a request with no
// ledger fails the same way as one that asks for "current".
std::expected<TransactionEntryHandler::Output, ::rpc::Status>
TransactionEntryHandler::process(Input const& input) const
{
Output output;
if (auto const status = getLedger(output.ledger, input.ledger, context_.get()))
return std::unexpected{status};
if (!context.params.isMember(jss::tx_hash))
if (!input.txHash &&
input.txHash.error() == ::rpc::spec::handlers::transaction_entry::TxHashError::Missing)
{
jvResult[jss::error] = "fieldNotFoundTransaction";
output.error = "fieldNotFoundTransaction";
}
else if (jvResult.get(jss::ledger_hash, json::ValueType::Null).isNull())
else if (output.ledger->open())
{
// We don't work on ledger current.
// XXX We don't support any transaction yet.
jvResult[jss::error] = "notYetImplemented";
output.error = "notYetImplemented";
}
else if (!input.txHash)
{
output.error = "malformedRequest";
}
else
{
UInt256 uTransID;
// XXX Relying on trusted WSS client. Would be better to have a strict
// routine, returning success or failure.
if (!uTransID.parseHex(context.params[jss::tx_hash].asString()))
{
jvResult[jss::error] = "malformedRequest";
return jvResult;
}
auto [sttx, stobj] = lpLedger->txRead(uTransID);
if (!sttx)
{
jvResult[jss::error] = "transactionNotFound";
}
else
{
if (context.apiVersion > 1)
{
jvResult[jss::tx_json] = sttx->getJson(JsonOptions::Values::DisableApiPriorV2);
jvResult[jss::hash] = to_string(sttx->getTransactionID());
if (!lpLedger->open())
{
jvResult[jss::ledger_hash] =
to_string(context.ledgerMaster.getHashBySeq(lpLedger->seq()));
}
bool const validated = context.ledgerMaster.isValidated(*lpLedger);
jvResult[jss::validated] = validated;
if (validated)
{
jvResult[jss::ledger_index] = lpLedger->seq();
if (auto closeTime = context.ledgerMaster.getCloseTimeBySeq(lpLedger->seq()))
jvResult[jss::close_time_iso] = toStringIso(*closeTime);
}
}
else
{
jvResult[jss::tx_json] = sttx->getJson(JsonOptions::Values::None);
}
rpc::insertDeliverMax(jvResult[jss::tx_json], sttx->getTxnType(), context.apiVersion);
auto const jsonMeta = (context.apiVersion > 1 ? jss::meta : jss::metadata);
if (stobj)
jvResult[jsonMeta] = stobj->getJson(JsonOptions::Values::None);
// 'accounts'
// 'engine_...'
// 'ledger_...'
}
std::tie(output.tx, output.meta) = output.ledger->txRead(*input.txHash);
if (!output.tx)
output.error = "transactionNotFound";
}
return jvResult;
return output;
}
} // namespace xrpl
void
TransactionEntryHandler::writeResult(json::Value& value, Output const& output) const
{
auto const& context = context_.get();
auto const& ledger = *output.ledger;
injectLedgerFields(ledger, context, value);
if (output.error)
{
value[jss::error] = std::string{*output.error};
return;
}
if (context.apiVersion > 1)
{
value[jss::tx_json] = output.tx->getJson(JsonOptions::Values::DisableApiPriorV2);
value[jss::hash] = to_string(output.tx->getTransactionID());
if (!ledger.open())
value[jss::ledger_hash] = to_string(context.ledgerMaster.getHashBySeq(ledger.seq()));
bool const validated = context.ledgerMaster.isValidated(ledger);
value[jss::validated] = validated;
if (validated)
{
value[jss::ledger_index] = ledger.seq();
if (auto closeTime = context.ledgerMaster.getCloseTimeBySeq(ledger.seq()))
value[jss::close_time_iso] = toStringIso(*closeTime);
}
}
else
{
value[jss::tx_json] = output.tx->getJson(JsonOptions::Values::None);
}
insertDeliverMax(value[jss::tx_json], output.tx->getTxnType(), context.apiVersion);
auto const jsonMeta = (context.apiVersion > 1 ? jss::meta : jss::metadata);
if (output.meta)
value[jsonMeta] = output.meta->getJson(JsonOptions::Values::None);
}
} // namespace xrpl::rpc

View File

@@ -0,0 +1,45 @@
#pragma once
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/detail/SpecBridge.hpp>
#include <xrpl/json/json_value.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/STTx.h>
#include <rpcspec/Errors.hpp>
#include <rpcspec/handlers/transaction_entry/Types.hpp>
#include <expected>
#include <functional>
#include <memory>
#include <optional>
#include <string_view>
namespace xrpl::rpc {
class TransactionEntryHandler : public HandlerFor<::rpc::spec::handlers::transaction_entry::Input>
{
public:
struct Output
{
std::shared_ptr<ReadView const> ledger;
std::optional<std::string_view> error;
std::shared_ptr<STTx const> tx;
std::shared_ptr<STObject const> meta;
};
explicit TransactionEntryHandler(JsonContext&);
[[nodiscard]] std::expected<Output, ::rpc::Status>
process(Input const& input) const;
void
writeResult(json::Value& value, Output const& output) const;
private:
std::reference_wrapper<JsonContext> context_;
};
} // namespace xrpl::rpc

View File

@@ -5,8 +5,8 @@
#include <xrpld/rpc/CTID.h>
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/DeliveredAmount.h>
#include <xrpld/rpc/Status.h>
#include <xrpld/rpc/detail/RPCHelpers.h>
#include <xrpld/rpc/detail/SpecBridge.hpp>
#include <xrpld/rpc/detail/SyntheticFields.h>
#include <xrpl/basics/Blob.h>
@@ -27,6 +27,8 @@
#include <xrpl/rdb/RelationalDatabase.h>
#include <xrpl/server/NetworkOPs.h>
#include <rpcspec/Errors.hpp>
#include <cstdint>
#include <memory>
#include <optional>
@@ -68,7 +70,7 @@ struct TxArgs
std::optional<std::pair<uint32_t, uint32_t>> ledgerRange;
};
std::pair<TxResult, rpc::Status>
std::pair<TxResult, ::rpc::Status>
doTxHelp(rpc::Context& context, TxArgs args)
{
TxResult result;
@@ -178,25 +180,25 @@ doTxHelp(rpc::Context& context, TxArgs args)
json::Value
populateJsonResponse(
std::pair<TxResult, rpc::Status> const& res,
std::pair<TxResult, ::rpc::Status> const& res,
TxArgs const& args,
rpc::JsonContext const& context)
{
json::Value response;
rpc::Status const& error = res.second;
::rpc::Status const& error = res.second;
TxResult const& result = res.first;
// handle errors
if (error.toErrorCode() != RpcSuccess)
if (error != RpcSuccess)
{
if (error.toErrorCode() == RpcTxnNotFound && result.searchedAll != TxSearched::Unknown)
if (error == RpcTxnNotFound && result.searchedAll != TxSearched::Unknown)
{
response = json::Value(json::ValueType::Object);
response[jss::searched_all] = (result.searchedAll == TxSearched::All);
error.inject(response);
rpc::injectSpecError(response, error);
}
else
{
error.inject(response);
rpc::injectSpecError(response, error);
}
}
// no errors
@@ -325,7 +327,7 @@ doTxJson(rpc::JsonContext& context)
}
}
std::pair<TxResult, rpc::Status> const res = doTxHelp(context, args);
std::pair<TxResult, ::rpc::Status> const res = doTxHelp(context, args);
return populateJsonResponse(res, args, context);
}