refactor: Migrate handlers to rpc-spec (A) (#8345)

This commit is contained in:
Alex Kremer
2026-10-06 13:19:20 +00:00
committed by GitHub
parent cfcbe45b60
commit 2ebd745a1b
16 changed files with 291 additions and 183 deletions

View File

@@ -292,6 +292,14 @@ if(xrpld)
)
target_sources(xrpld PRIVATE ${sources})
rpcspec_generate_instantiations(
OUT_VAR rpcspec_instantiations
VALUE_TYPE "::json::Value"
VIEW_HEADER "xrpld/rpc/detail/JsonObjectView.hpp"
HANDLERS ledger
)
target_sources(xrpld PRIVATE ${rpcspec_instantiations})
if(tests)
file(
GLOB_RECURSE sources

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.19#870b2d3abcfbbf13b61c2d3c69495060%1790348286.187549",
"xrpl-rpc-spec/0.1.20#6daa13eeb4e6c92b82ea6d78866e1da8%1790720024.368048",
"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.19",
"xrpl-rpc-spec/0.1.20",
"zlib/1.3.2",
]

View File

@@ -1,28 +0,0 @@
#pragma once
namespace xrpl {
/**
* @brief Enumeration of ledger shortcuts for specifying which ledger to use.
*
* These shortcuts provide a convenient way to reference commonly used ledgers
* without needing to specify their exact hash or sequence number.
*/
enum class LedgerShortcut {
/**
* The current working ledger (open, not yet closed)
*/
Current,
/**
* The most recently closed ledger (may not be validated)
*/
Closed,
/**
* The most recently validated ledger
*/
Validated
};
} // namespace xrpl

View File

@@ -9,7 +9,6 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/LedgerHeader.h>
#include <xrpl/protocol/LedgerShortcut.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/TxMeta.h>
#include <xrpl/protocol/TxSearched.h>
@@ -105,21 +104,6 @@ public:
using TxnMetaLedgerType = std::tuple<Blob, Blob, std::uint32_t>;
using MetaTxsList = std::vector<TxnMetaLedgerType>;
using LedgerSequence = uint32_t;
using LedgerHash = UInt256;
using LedgerSpecifier = std::variant<LedgerRange, LedgerShortcut, LedgerSequence, LedgerHash>;
struct AccountTxArgs
{
AccountID account;
std::optional<LedgerSpecifier> ledger;
bool binary = false;
bool forward = false;
uint32_t limit = 0;
std::optional<AccountTxMarker> marker;
std::optional<DelegateFilter> delegate;
};
struct AccountTxResult
{
std::variant<AccountTxs, MetaTxsList> transactions;

View File

@@ -319,6 +319,22 @@ class AccountTx_test : public beast::unit_test::Suite
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), RpcLgrNotFound));
}
{
json::Value p{jParams};
p[jss::ledger_index] = "validated";
BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p))));
p[jss::ledger_index] = "closed";
BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p))));
p[jss::ledger_index] = "current";
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), RpcLgrNotValidated));
p[jss::ledger_index] = "";
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), RpcLgrNotValidated));
}
// Ledger Hash
{
json::Value p{jParams};

View File

@@ -103,6 +103,19 @@ class LedgerRPC_test : public beast::unit_test::Suite
jrr[jss::ledger][jss::ledger_index] == std::to_string(env.current()->header().seq));
BEAST_EXPECT(jrr[jss::ledger_current_index] == env.current()->header().seq);
}
{
auto const jrr = env.rpc("json", "ledger", "{}")[jss::result];
BEAST_EXPECT(!jrr.isMember(jss::ledger));
BEAST_EXPECT(jrr[jss::closed][jss::ledger][jss::closed] == true);
BEAST_EXPECT(
jrr[jss::closed][jss::ledger][jss::ledger_index] ==
std::to_string(env.closed()->header().seq));
BEAST_EXPECT(jrr[jss::open][jss::ledger][jss::closed] == false);
BEAST_EXPECT(
jrr[jss::open][jss::ledger][jss::ledger_index] ==
std::to_string(env.current()->header().seq));
}
}
void

View File

@@ -311,6 +311,20 @@ TEST(SpecBridge, shared_spec_warns_on_a_deprecated_field)
EXPECT_EQ(warnings[0].code, ::rpc::WarningCode::WarnRpcDeprecated);
}
TEST(SpecBridge, inject_spec_error_uses_default_message)
{
json::Value out{json::ValueType::Object};
rpc::injectSpecError(out, ::rpc::Status{RpcLgrNotFound});
EXPECT_EQ(out, rpc::makeError(RpcLgrNotFound));
}
TEST(SpecBridge, inject_spec_error_uses_custom_message)
{
json::Value out{json::ValueType::Object};
rpc::injectSpecError(out, ::rpc::Status{RpcInvalidParams, "Invalid field 'ledger'."});
EXPECT_EQ(out, rpc::makeError(RpcInvalidParams, "Invalid field 'ledger'."));
}
TEST(SpecBridge, inject_spec_warnings)
{
std::string const kDeprecatedBase{

View File

@@ -4,6 +4,7 @@
#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/server_info/Version.h>
@@ -12,10 +13,16 @@
#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>
#include <algorithm>
#include <array>
#include <concepts>
#include <cstddef>
#include <expected>
#include <iterator>
#include <span>
#include <string_view>
@@ -49,7 +56,34 @@ byRef(JsonContext& context, json::Value& result)
return Status();
}
template <typename HandlerImpl>
concept SpecBasedHandler = std::derived_from<HandlerImpl, HandlerFor<typename HandlerImpl::Input>>;
template <typename HandlerImpl>
concept InputlessHandler = requires(HandlerImpl& handler) { handler.process(); };
template <class HandlerImpl, class Output>
Status
respond(
HandlerImpl const& handler,
json::Value& object,
std::expected<Output, ::rpc::Status> const& output,
::rpc::spec::Warnings const& warnings)
{
if (not output.has_value())
{
injectSpecError(object, output.error());
return std::get<ErrorCodeI>(output.error().code);
}
handler.writeResult(object, *output);
injectSpecWarnings(object, warnings);
return Status::kOK;
}
template <class HandlerImpl>
requires SpecBasedHandler<HandlerImpl> or InputlessHandler<HandlerImpl>
Status
handle(JsonContext& context, json::Value& object)
{
@@ -57,18 +91,28 @@ handle(JsonContext& context, json::Value& object)
context.apiVersion >= HandlerImpl::minApiVer &&
context.apiVersion <= HandlerImpl::maxApiVer,
"xrpl::rpc::handle : valid API version");
HandlerImpl handler(context);
auto status = handler.check();
if (status)
if constexpr (SpecBasedHandler<HandlerImpl>)
{
status.inject(object);
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);
}
HandlerImpl handler(context);
auto const output = handler.process(*input);
auto const warnings = output.has_value()
? HandlerImpl::spec(context.apiVersion).check(context.params)
: ::rpc::spec::Warnings{};
return respond(handler, object, output, warnings);
}
else
{
handler.writeResult(object);
HandlerImpl handler(context);
return respond(handler, object, handler.process(), /* warnings= */ {});
}
return status;
}
template <typename HandlerImpl>

View File

@@ -16,7 +16,6 @@
#include <xrpl/json/json_value.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/LedgerShortcut.h>
#include <xrpl/protocol/RPCErr.h>
#include <xrpl/protocol/RippleLedgerHash.h>
#include <xrpl/protocol/jss.h>
@@ -25,15 +24,28 @@
#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/Ledger.hpp>
#include <cstdint>
#include <expected>
#include <memory>
#include <utility>
#include <variant>
namespace xrpl::rpc {
namespace {
template <typename... Ts>
struct Overload : Ts...
{
using Ts::operator()...;
constexpr Overload(Ts&&... ts) : Ts(std::forward<Ts>(ts))...
{
}
};
bool
isValidatedOld(LedgerMaster& ledgerMaster, bool standalone)
{
@@ -68,13 +80,13 @@ ledgerFromIndex(
auto const index = indexValue.asString();
if (index == "current" || index.empty())
return getLedger(ledger, LedgerShortcut::Current, context);
return getLedger(ledger, ::rpc::spec::LedgerShortcut::Current, context);
if (index == "validated")
return getLedger(ledger, LedgerShortcut::Validated, context);
return getLedger(ledger, ::rpc::spec::LedgerShortcut::Validated, context);
if (index == "closed")
return getLedger(ledger, LedgerShortcut::Closed, context);
return getLedger(ledger, ::rpc::spec::LedgerShortcut::Closed, context);
std::uint32_t iVal = 0;
if (!beast::lexicalCastChecked(iVal, index))
@@ -146,7 +158,7 @@ ledgerFromRequest(T& ledger, JsonContext const& context)
}
// nothing specified, `index` has a default setting
return getLedger(ledger, LedgerShortcut::Current, context);
return getLedger(ledger, ::rpc::spec::LedgerShortcut::Current, context);
}
} // namespace
@@ -204,17 +216,17 @@ ledgerFromSpecifier(
auto const shortcut = specifier.shortcut();
if (shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_VALIDATED)
{
return getLedger(ledger, LedgerShortcut::Validated, context);
return getLedger(ledger, ::rpc::spec::LedgerShortcut::Validated, context);
}
if (shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT ||
shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_UNSPECIFIED)
{
return getLedger(ledger, LedgerShortcut::Current, context);
return getLedger(ledger, ::rpc::spec::LedgerShortcut::Current, context);
}
if (shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED)
{
return getLedger(ledger, LedgerShortcut::Closed, context);
return getLedger(ledger, ::rpc::spec::LedgerShortcut::Closed, context);
}
}
}
@@ -263,7 +275,7 @@ getLedger(T& ledger, uint32_t ledgerIndex, Context const& context)
template <class T>
Status
getLedger(T& ledger, LedgerShortcut shortcut, Context const& context)
getLedger(T& ledger, ::rpc::spec::LedgerShortcut shortcut, Context const& context)
{
if (isValidatedOld(context.ledgerMaster, context.app.config().standalone()))
{
@@ -272,7 +284,7 @@ getLedger(T& ledger, LedgerShortcut shortcut, Context const& context)
return {RpcNotSynced, "notSynced"};
}
if (shortcut == LedgerShortcut::Validated)
if (shortcut == ::rpc::spec::LedgerShortcut::Validated)
{
ledger = context.ledgerMaster.getValidatedLedger();
if (ledger == nullptr)
@@ -286,12 +298,12 @@ getLedger(T& ledger, LedgerShortcut shortcut, Context const& context)
}
else
{
if (shortcut == LedgerShortcut::Current)
if (shortcut == ::rpc::spec::LedgerShortcut::Current)
{
ledger = context.ledgerMaster.getCurrentLedger();
XRPL_ASSERT(ledger->open(), "xrpl::rpc::getLedger : current is open");
}
else if (shortcut == LedgerShortcut::Closed)
else if (shortcut == ::rpc::spec::LedgerShortcut::Closed)
{
ledger = context.ledgerMaster.getClosedLedger();
XRPL_ASSERT(!ledger->open(), "xrpl::rpc::getLedger : closed is not open");
@@ -321,16 +333,33 @@ getLedger(T& ledger, LedgerShortcut shortcut, Context const& context)
return Status::kOK;
}
// Explicit instantiation of above three functions
template <class T>
Status
getLedger(T& ledger, ::rpc::spec::LedgerSpecifier const& specifier, Context const& context)
{
return std::visit(
Overload{
[&](std::monostate) {
return getLedger(ledger, ::rpc::spec::kDefaultLedgerShortcut, context);
},
[&](auto const& value) { return getLedger(ledger, value, context); },
},
specifier.value);
}
// Explicit instantiation of above four functions
template Status
getLedger<>(std::shared_ptr<ReadView const>&, uint32_t, Context const&);
template Status
getLedger<>(std::shared_ptr<ReadView const>&, LedgerShortcut shortcut, Context const&);
getLedger<>(std::shared_ptr<ReadView const>&, ::rpc::spec::LedgerShortcut shortcut, Context const&);
template Status
getLedger<>(std::shared_ptr<ReadView const>&, UInt256 const&, Context const&);
template Status
getLedger<>(std::shared_ptr<ReadView const>&, ::rpc::spec::LedgerSpecifier const&, Context const&);
// explicit instantiation of ledgerFromSpecifier
template Status
ledgerFromSpecifier<>(

View File

@@ -7,10 +7,10 @@
#include <xrpl/basics/base_uint.h>
#include <xrpl/json/json_value.h>
#include <xrpl/ledger/Ledger.h>
#include <xrpl/protocol/LedgerShortcut.h>
#include <xrpl/server/NetworkOPs.h>
#include <org/xrpl/rpc/v1/ledger.pb.h>
#include <rpcspec/Ledger.hpp>
#include <cstdint>
#include <expected>
@@ -76,7 +76,22 @@ getLedger(T& ledger, uint32_t ledgerIndex, Context const& context);
*/
template <class T>
Status
getLedger(T& ledger, LedgerShortcut shortcut, Context const& context);
getLedger(T& ledger, ::rpc::spec::LedgerShortcut shortcut, Context const& context);
/**
* @brief Retrieves the ledger a spec-parsed request selects.
*
* An unspecified selection means the current ledger.
*
* @tparam T Type of the ledger pointer to be filled.
* @param ledger Reference to the ledger pointer to be filled.
* @param specifier The ledger the request selects.
* @param context The RPC context.
* @return Status indicating success or failure of the operation.
*/
template <class T>
Status
getLedger(T& ledger, ::rpc::spec::LedgerSpecifier const& specifier, Context const& context);
/**
* @brief Looks up a ledger from a request and returns a json::Value with either

View File

@@ -1,9 +1,13 @@
#pragma once
#include <xrpld/rpc/detail/JsonObjectView.hpp>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/jss.h>
#include <rpcspec/Errors.hpp>
#include <rpcspec/HandlerFor.hpp>
#include <rpcspec/Types.hpp>
#include <map>
@@ -12,6 +16,22 @@
namespace xrpl::rpc {
template <typename InputT>
using HandlerFor = ::rpc::spec::HandlerFor<InputT, json::Value>;
inline void
injectSpecError(json::Value& object, ::rpc::Status const& status)
{
if (auto const code = std::get<ErrorCodeI>(status.code); status.message.empty())
{
injectError(code, object);
}
else
{
injectError(code, status.message, object);
}
}
// Warnings are grouped by code into one entry each: the code's standard message followed by
// every per-field detail, space separated.
//

View File

@@ -20,13 +20,14 @@
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/LedgerShortcut.h>
#include <xrpl/protocol/RPCErr.h>
#include <xrpl/protocol/RippleLedgerHash.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/rdb/RelationalDatabase.h>
#include <xrpl/resource/Fees.h>
#include <rpcspec/Ledger.hpp>
#include <cstdint>
#include <expected>
#include <memory>
@@ -82,9 +83,24 @@ parseDelegateFilter(json::Value const& delegateNode)
using TxnsData = RelationalDatabase::AccountTxs;
using TxnsDataBinary = RelationalDatabase::MetaTxsList;
using TxnDataBinary = RelationalDatabase::TxnMetaLedgerType;
using AccountTxArgs = RelationalDatabase::AccountTxArgs;
using AccountTxResult = RelationalDatabase::AccountTxResult;
using LedgerSpecifier = RelationalDatabase::LedgerSpecifier;
// Moved here from RelationalDatabase, where nothing but this handler used them, so that
// libxrpl does not depend on the spec for LedgerShortcut. Temporary: the spec's
// account_tx Input replaces both once account_tx is migrated to the spec.
using LedgerSpecifier =
std::variant<LedgerRange, ::rpc::spec::LedgerShortcut, std::uint32_t, uint256>;
struct AccountTxArgs
{
AccountID account;
std::optional<LedgerSpecifier> ledger;
bool binary = false;
bool forward = false;
uint32_t limit = 0;
std::optional<RelationalDatabase::AccountTxMarker> marker;
std::optional<DelegateFilter> delegate;
};
// parses args into a ledger specifier, or returns a Json object on error
std::variant<std::optional<LedgerSpecifier>, json::Value>
@@ -148,15 +164,15 @@ parseLedgerArgs(rpc::Context& context, json::Value const& params)
if (ledgerStr == "current" || ledgerStr.empty())
{
ledger = LedgerShortcut::Current;
ledger = ::rpc::spec::LedgerShortcut::Current;
}
else if (ledgerStr == "closed")
{
ledger = LedgerShortcut::Closed;
ledger = ::rpc::spec::LedgerShortcut::Closed;
}
else if (ledgerStr == "validated")
{
ledger = LedgerShortcut::Validated;
ledger = ::rpc::spec::LedgerShortcut::Validated;
}
else
{

View File

@@ -9,6 +9,7 @@
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/ErrorCodes.h>
@@ -19,11 +20,11 @@
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/resource/Fees.h>
#include <xrpl/server/LoadFeeTrack.h>
#include <xrpl/shamap/SHAMap.h>
#include <grpcpp/support/status.h>
#include <org/xrpl/rpc/v1/get_ledger.pb.h>
#include <rpcspec/Errors.hpp>
#include <chrono>
#include <exception>
@@ -40,126 +41,83 @@ LedgerHandler::LedgerHandler(JsonContext& context) : context_(context)
{
}
Status
LedgerHandler::check()
std::expected<LedgerHandler::Output, ::rpc::Status>
LedgerHandler::process(Input const& input)
{
auto const& params = context_.params;
Output output;
output.options = (input.full ? static_cast<int>(LedgerFill::Options::Full) : 0) |
(input.expand ? static_cast<int>(LedgerFill::Options::Expand) : 0) |
(input.transactions ? static_cast<int>(LedgerFill::Options::DumpTxrp) : 0) |
(input.accounts ? static_cast<int>(LedgerFill::Options::DumpState) : 0) |
(input.binary ? static_cast<int>(LedgerFill::Options::Binary) : 0) |
(input.ownerFunds ? static_cast<int>(LedgerFill::Options::OwnerFunds) : 0) |
(input.queue ? static_cast<int>(LedgerFill::Options::DumpQueue) : 0);
auto getBool = [&](json::StaticString const& field) -> std::expected<bool, Status> {
if (!params.isMember(field))
{
return false;
}
if (!params[field].isBool())
{
return std::unexpected(RpcInvalidParams);
}
if (input.ledger.isUnspecified())
return output;
return params[field].asBool();
};
if (auto const status = getLedger(output.ledger, input.ledger, context_.get()))
return std::unexpected{::rpc::Status{status.toErrorCode(), status.message()}};
auto const full = getBool(jss::full);
auto const transactions = getBool(jss::transactions);
auto const accounts = getBool(jss::accounts);
auto const expand = getBool(jss::expand);
auto const binary = getBool(jss::binary);
auto const ownerFunds = getBool(jss::owner_funds);
auto const queue = getBool(jss::queue);
if (!full.has_value())
return full.error();
if (!transactions.has_value())
return transactions.error();
if (!accounts.has_value())
return accounts.error();
if (!expand.has_value())
return expand.error();
if (!binary.has_value())
return binary.error();
if (!ownerFunds.has_value())
return ownerFunds.error();
if (!queue.has_value())
return queue.error();
options_ = (*full ? static_cast<int>(LedgerFill::Options::Full) : 0) |
(*expand ? static_cast<int>(LedgerFill::Options::Expand) : 0) |
(*transactions ? static_cast<int>(LedgerFill::Options::DumpTxrp) : 0) |
(*accounts ? static_cast<int>(LedgerFill::Options::DumpState) : 0) |
(*binary ? static_cast<int>(LedgerFill::Options::Binary) : 0) |
(*ownerFunds ? static_cast<int>(LedgerFill::Options::OwnerFunds) : 0) |
(*queue ? static_cast<int>(LedgerFill::Options::DumpQueue) : 0);
bool const needsLedger = params.isMember(jss::ledger) || params.isMember(jss::ledger_hash) ||
params.isMember(jss::ledger_index);
if (!needsLedger)
return Status::kOK;
if (auto s = lookupLedger(ledger_, context_, result_))
return s;
if (*full || *accounts)
if (input.full || input.accounts)
{
// Until some sane way to get full ledgers has been implemented,
// disallow retrieving all state nodes.
if (!isUnlimited(context_.role))
return RpcNoPermission;
if (!isUnlimited(context_.get().role))
return std::unexpected{::rpc::Status{RpcNoPermission}};
if (context_.app.getFeeTrack().isLoadedLocal() && !isUnlimited(context_.role))
{
return RpcTooBusy;
}
context_.loadType = binary ? resource::kFeeMediumBurdenRpc : resource::kFeeHeavyBurdenRpc;
context_.get().loadType =
input.binary ? resource::kFeeMediumBurdenRpc : resource::kFeeHeavyBurdenRpc;
}
if (*queue)
if (input.queue)
{
if (!ledger_ || !ledger_->open())
if (!output.ledger || !output.ledger->open())
{
// It doesn't make sense to request the queue
// with a non-existent or closed/validated ledger.
return RpcInvalidParams;
return std::unexpected{::rpc::Status{RpcInvalidParams}};
}
queueTxs_ = context_.app.getTxQ().getTxs();
output.queueTxs = context_.get().app.getTxQ().getTxs();
}
return Status::kOK;
return output;
}
void
LedgerHandler::writeResult(json::Value& value)
LedgerHandler::writeResult(json::Value& value, Output const& output) const
{
if (ledger_)
if (output.ledger)
{
copyFrom(value, result_);
addJson(value, {*ledger_, &context_, options_, queueTxs_});
auto const& header = output.ledger->header();
if (output.ledger->open())
{
value[jss::ledger_current_index] = header.seq;
}
else
{
value[jss::ledger_hash] = to_string(header.hash);
value[jss::ledger_index] = header.seq;
}
value[jss::validated] = context_.get().ledgerMaster.isValidated(*output.ledger);
addJson(value, {*output.ledger, &context_.get(), output.options, output.queueTxs});
}
else
{
auto& master = context_.app.getLedgerMaster();
auto& master = context_.get().app.getLedgerMaster();
{
auto& closed = value[jss::closed] = json::ValueType::Object;
addJson(closed, {*master.getClosedLedger(), &context_, 0});
addJson(closed, {*master.getClosedLedger(), &context_.get(), 0});
}
{
auto& open = value[jss::open] = json::ValueType::Object;
addJson(open, {*master.getCurrentLedger(), &context_, 0});
addJson(open, {*master.getCurrentLedger(), &context_.get(), 0});
}
}
json::Value warnings{json::ValueType::Array};
if (context_.params.isMember(jss::type))
{
json::Value& w = warnings.append(json::ValueType::Object);
w[jss::id] = WarnRpcFieldsDeprecated;
w[jss::message] =
"Some fields from your request are deprecated. Please check the "
"documentation at "
"https://xrpl.org/docs/references/http-websocket-apis/ "
"and update your request. Field `type` is deprecated.";
}
if (warnings.size() != 0u)
value[jss::warnings] = std::move(warnings);
}
} // namespace rpc

View File

@@ -5,13 +5,18 @@
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/MethodNames.h>
#include <xrpld/rpc/Role.h>
#include <xrpld/rpc/Status.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>
#include <expected>
#include <functional>
#include <memory>
#include <string_view>
#include <vector>
@@ -26,16 +31,23 @@ struct JsonContext;
// full: true | false // optional, defaults to false.
// }
class LedgerHandler
class LedgerHandler : public HandlerFor<::rpc::spec::handlers::ledger::Input>
{
public:
struct Output
{
std::shared_ptr<ReadView const> ledger;
std::vector<TxQ::TxDetails> queueTxs;
int options = 0;
};
explicit LedgerHandler(JsonContext&);
Status
check();
[[nodiscard]] std::expected<Output, ::rpc::Status>
process(Input const& input);
void
writeResult(json::Value&);
writeResult(json::Value& value, Output const& output) const;
// NOLINTBEGIN(readability-identifier-naming)
static constexpr std::string_view name = method::kLedger;
@@ -50,11 +62,7 @@ public:
// NOLINTEND(readability-identifier-naming)
private:
JsonContext& context_;
std::shared_ptr<ReadView const> ledger_;
std::vector<TxQ::TxDetails> queueTxs_;
json::Value result_;
int options_ = 0;
std::reference_wrapper<JsonContext> context_;
};
} // namespace xrpl::rpc

View File

@@ -4,12 +4,16 @@
#include <xrpld/rpc/Context.h>
#include <xrpld/rpc/MethodNames.h>
#include <xrpld/rpc/Role.h>
#include <xrpld/rpc/Status.h>
#include <xrpld/rpc/detail/Handler.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/ApiVersion.h>
#include <rpcspec/Errors.hpp>
#include <cstdint>
#include <expected>
#include <functional>
#include <string_view>
namespace xrpl::rpc {
@@ -17,21 +21,29 @@ namespace xrpl::rpc {
class VersionHandler
{
public:
explicit VersionHandler(JsonContext& c)
: apiVersion_(c.apiVersion), betaEnabled_(c.app.config().betaRpcApi)
struct Output
{
std::uint32_t apiVersion;
bool betaEnabled;
};
explicit VersionHandler(JsonContext& context) : context_(context)
{
}
static Status
check()
[[nodiscard]] std::expected<Output, ::rpc::Status>
process() const
{
return Status::kOK;
return Output{
.apiVersion = context_.get().apiVersion,
.betaEnabled = context_.get().app.config().betaRpcApi,
};
}
void
writeResult(json::Value& obj) const
static void
writeResult(json::Value& obj, Output const& output)
{
setVersion(obj, apiVersion_, betaEnabled_);
setVersion(obj, output.apiVersion, output.betaEnabled);
}
// NOLINTBEGIN(readability-identifier-naming)
@@ -47,8 +59,7 @@ public:
// NOLINTEND(readability-identifier-naming)
private:
unsigned int apiVersion_;
bool betaEnabled_;
std::reference_wrapper<JsonContext> context_;
};
} // namespace xrpl::rpc