refactor: Migrate handlers to rpc-spec (D) (#3209)

This commit is contained in:
Alex Kremer
2026-09-15 19:33:27 +01:00
committed by GitHub
parent b33364d144
commit bd666f4b3a
38 changed files with 362 additions and 1391 deletions

View File

@@ -5,7 +5,6 @@
#include <boost/asio/spawn.hpp>
#include <boost/json/array.hpp>
#include <boost/json/value_to.hpp>
#include <rpcspec/Errors.hpp>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/StringUtilities.h>
@@ -27,9 +26,9 @@
#include <optional>
#include <set>
#include <string>
#include <string_view>
#include <unordered_set>
#include <utility>
#include <vector>
namespace rpc::credentials {
@@ -99,7 +98,7 @@ parseAuthorizeCredentials(boost::json::array const& jv)
std::expected<xrpl::STArray, Status>
fetchCredentialArray(
std::optional<boost::json::array> const& credID,
std::vector<xrpl::uint256> const& credIDs,
xrpl::AccountID const& srcAcc,
BackendInterface const& backend,
xrpl::LedgerHeader const& info,
@@ -107,21 +106,11 @@ fetchCredentialArray(
)
{
xrpl::STArray authCreds;
std::unordered_set<std::string_view> elems;
for (auto const& elem : *credID) { // NOLINT(bugprone-unchecked-optional-access)
ASSERT(
elem.is_string(), "should already be checked in validators.hpp that elem is a string."
);
if (elems.contains(elem.as_string()))
std::unordered_set<xrpl::uint256, xrpl::uint256::hasher> seen;
for (auto const& credHash : credIDs) {
if (seen.contains(credHash))
return Error{Status{RippledError::RpcBadCredentials, "duplicates in credentials."}};
elems.insert(elem.as_string());
xrpl::uint256 credHash;
ASSERT(
credHash.parseHex(boost::json::value_to<std::string>(elem)),
"should already be checked in validators.hpp that elem is a uint256 hex"
);
seen.insert(credHash);
auto const credKeylet = xrpl::keylet::credential(credHash).key;
auto const credLedgerObject = backend.fetchLedgerObject(credKeylet, info.seq, yield);

View File

@@ -6,6 +6,7 @@
#include <boost/asio/spawn.hpp>
#include <boost/json/array.hpp>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/LedgerHeader.h>
@@ -17,6 +18,7 @@
#include <optional>
#include <set>
#include <utility>
#include <vector>
namespace rpc::credentials {
@@ -52,7 +54,7 @@ parseAuthorizeCredentials(boost::json::array const& jv);
/**
* @brief Get Array of Credential objects
*
* @param credID Array of CredentialID's to parse
* @param credIDs The credential IDs to look up
* @param srcAcc The Source Account
* @param backend backend interface
* @param info The ledger header
@@ -61,7 +63,7 @@ parseAuthorizeCredentials(boost::json::array const& jv);
*/
std::expected<xrpl::STArray, Status>
fetchCredentialArray(
std::optional<boost::json::array> const& credID,
std::vector<xrpl::uint256> const& credIDs,
xrpl::AccountID const& srcAcc,
BackendInterface const& backend,
xrpl::LedgerHeader const& info,

View File

@@ -1784,47 +1784,4 @@ toJsonWithBinaryTx(data::TransactionAndMetadata const& txnPlusMeta, std::uint32_
return obj;
}
std::optional<DelegateFilter::Role>
parseDelegateType(boost::json::value const& delegateType)
{
if (not delegateType.is_string())
return {};
auto const& type = delegateType.as_string();
if (type == JS(authorizer))
return DelegateFilter::Role::Authorizer;
if (type == JS(actor))
return DelegateFilter::Role::Actor;
return {};
}
std::optional<DelegateFilter>
parseDelegateFilter(boost::json::object const& delegateObject)
{
if (!delegateObject.contains(JS(delegate_filter)))
return {};
auto const& filterVal = delegateObject.at(JS(delegate_filter));
if (!filterVal.is_string())
return {};
auto const delegateTypeOpt = parseDelegateType(filterVal.as_string());
if (!delegateTypeOpt.has_value())
return {};
std::optional<std::string> counterParty;
if (delegateObject.contains(JS(counter_party))) {
auto const& counterpartyVal = delegateObject.at(JS(counter_party));
if (!counterpartyVal.is_string())
return {};
counterParty = counterpartyVal.as_string();
}
return DelegateFilter{*delegateTypeOpt, std::move(counterParty)};
}
} // namespace rpc

View File

@@ -930,22 +930,4 @@ getDeliveredAmount(
uint32_t date
);
/**
* @brief Parse the delegate type from a JSON value
*
* @param delegateType The JSON value containing the delegate type string
* @return The parsed delegate type or std::nullopt if the input is invalid or not a string
*/
std::optional<DelegateFilter::Role>
parseDelegateType(boost::json::value const& delegateType);
/**
* @brief Parse a delegate filter object from JSON
*
* @param delegateObject The JSON object containing the delegate filter input from user
* @return The constructed DelegateFilter or std::nullopt if parsing fails
*/
std::optional<DelegateFilter>
parseDelegateFilter(boost::json::object const& delegateObject);
} // namespace rpc

View File

@@ -1,13 +0,0 @@
#pragma once
#include <rpcspec/JsonBool.hpp>
namespace rpc {
/**
* @brief A wrapper around bool that allows conversion from any JSON value.
*
*/
using JsonBool = rpc::spec::JsonBool;
} // namespace rpc

View File

@@ -14,7 +14,6 @@
#include <cstdint>
#include <expected>
#include <optional>
#include <string>
#include <utility>
@@ -175,35 +174,6 @@ struct AccountCursor {
}
};
/**
* @brief A delegate object used filter account_tx by specific delegate accounts
*/
struct DelegateFilter {
/**
* @brief A delegate type used in delegate filter
*/
enum class Role : std::uint8_t {
Actor, /**< This account is the *active* sender, acting on behalf of another party.
* e.g., Account A in "A sends payment to B on behalf of C." */
Authorizer /**< This account is the *passive* party whose funds are being moved from.
* e.g., Account C in "A sends payment to B on behalf of C." */
};
/**
* @brief Construct a delegate filter
* @param role The delegate type to filter by
* @param counterParty The optional counterparty to further narrow the filter by
*/
explicit DelegateFilter(Role role, std::optional<std::string> counterParty = std::nullopt)
: delegateType(role), counterParty(std::move(counterParty))
{
}
Role delegateType;
std::optional<std::string> counterParty;
};
/**
* @brief Convert an empty output to a JSON object
*

View File

@@ -108,25 +108,6 @@ CustomValidator CustomValidators::ledgerIndexValidator =
return MaybeError{};
}};
CustomValidator CustomValidators::ledgerTypeValidator =
CustomValidator{[](boost::json::value const& value, std::string_view key) -> MaybeError {
if (!value.is_string()) {
return Error{Status{
RippledError::RpcInvalidParams, fmt::format("Invalid field '{}', not string.", key)
}};
}
auto const type =
rpc::spec::ledgerEntryTypeFromStr(boost::json::value_to<std::string>(value));
if (type == xrpl::ltANY) {
return Error{
Status{RippledError::RpcInvalidParams, fmt::format("Invalid field '{}'.", key)}
};
}
return MaybeError{};
}};
CustomValidator CustomValidators::accountValidator =
CustomValidator{[](boost::json::value const& value, std::string_view key) -> MaybeError {
if (!value.is_string())
@@ -418,34 +399,4 @@ CustomValidator CustomValidators::authorizeCredentialValidator =
return MaybeError{};
}};
CustomValidator CustomValidators::delegateValidator =
CustomValidator{[](boost::json::value const& value, std::string_view key) -> MaybeError {
if (not value.is_object())
return Error{Status{RippledError::RpcInvalidParams, std::string(key) + "NotObject"}};
auto const& delegate = value.as_object();
if (!delegate.contains(JS(delegate_filter))) {
return Error{Status{
RippledError::RpcInvalidParams, "Field 'delegate_filter' is required but missing."
}};
}
if (!parseDelegateType(delegate.at(JS(delegate_filter))).has_value()) {
return Error{Status{
RippledError::RpcInvalidParams,
"Field 'delegate_filter' value must be 'actor' or 'authorizer'."
}};
}
if (delegate.contains(JS(counter_party)) &&
!accountValidator.verify(delegate, JS(counter_party))) {
return Error{Status{
RippledError::RpcActMalformed,
"Field 'counter_party' value must be a valid account."
}};
}
return MaybeError{};
}};
} // namespace rpc::validation

View File

@@ -488,14 +488,6 @@ struct CustomValidators final {
*/
static CustomValidator ledgerIndexValidator;
/**
* @brief Provides a validator for ledger type.
*
* A type accepts canonical names of ledger entry types (case insensitive) or short names.
* Used by ledger_data.
*/
static CustomValidator ledgerTypeValidator;
/**
* @brief Provides a commonly used validator for accounts.
*
@@ -608,13 +600,6 @@ struct CustomValidators final {
* object lookup in ledger_entry.
*/
static CustomValidator credentialTypeValidator;
/**
* @brief Provides a validator for validating filtering by delegation.
*
* Used by account_tx if user wants to filter by delegation.
*/
static CustomValidator delegateValidator;
};
/**

View File

@@ -3,15 +3,22 @@
#include "data/Types.hpp"
#include "rpc/common/Types.hpp"
#include <rpcspec/handlers/account_tx/Types.hpp>
#include <xrpl/protocol/AccountID.h>
#include <optional>
namespace rpc {
/**
* @brief The delegate filter used to narrow account_tx by delegation.
*/
using DelegateFilter = rpc::spec::handlers::account_tx::DelegateFilter;
/**
* @brief Interface for filtering transactions.
*/
// TODO: consider moving to rpc-spec later on
class TransactionFilter {
public:
/**

View File

@@ -1,7 +1,6 @@
#include "rpc/filters/impl/DelegateTransactionsFilter.hpp"
#include "data/Types.hpp"
#include "rpc/common/Types.hpp"
#include "rpc/filters/TransactionFilter.hpp"
#include <xrpl/protocol/AccountID.h>

View File

@@ -3,7 +3,6 @@
#include "data/Types.hpp"
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/JsonBool.hpp"
#include "rpc/common/Types.hpp"
#include "rpc/filters/TransactionFilter.hpp"
#include "rpc/filters/impl/DelegateTransactionsFilter.hpp"
@@ -19,7 +18,7 @@
#include <boost/json/value_from.hpp>
#include <boost/json/value_to.hpp>
#include <rpcspec/Errors.hpp>
#include <xrpl/basics/base_uint.h>
#include <rpcspec/handlers/account_tx/Types.hpp>
#include <xrpl/basics/chrono.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/protocol/AccountID.h>
@@ -45,26 +44,26 @@ AccountTxHandler::process(AccountTxHandler::Input const& input, Context const& c
auto [minIndex, maxIndex] = *range; // NOLINT(bugprone-unchecked-optional-access)
if (input.ledgerIndexMin) {
// NOLINTBEGIN(bugprone-unchecked-optional-access)
if (ctx.apiVersion > 1u &&
(input.ledgerIndexMin >
range->maxSequence || // NOLINT(bugprone-unchecked-optional-access)
input.ledgerIndexMin <
range->minSequence)) { // NOLINT(bugprone-unchecked-optional-access)
(input.ledgerIndexMin > range->maxSequence ||
input.ledgerIndexMin < range->minSequence)) {
return Error{Status{RippledError::RpcLgrIdxMalformed, "ledgerSeqMinOutOfRange"}};
}
// NOLINTEND(bugprone-unchecked-optional-access)
if (static_cast<std::uint32_t>(*input.ledgerIndexMin) > minIndex)
minIndex = *input.ledgerIndexMin;
}
if (input.ledgerIndexMax) {
// NOLINTBEGIN(bugprone-unchecked-optional-access)
if (ctx.apiVersion > 1u &&
(input.ledgerIndexMax >
range->maxSequence || // NOLINT(bugprone-unchecked-optional-access)
input.ledgerIndexMax <
range->minSequence)) { // NOLINT(bugprone-unchecked-optional-access)
(input.ledgerIndexMax > range->maxSequence ||
input.ledgerIndexMax < range->minSequence)) {
return Error{Status{RippledError::RpcLgrIdxMalformed, "ledgerSeqMaxOutOfRange"}};
}
// NOLINTEND(bugprone-unchecked-optional-access)
if (static_cast<std::uint32_t>(*input.ledgerIndexMax) < maxIndex)
maxIndex = *input.ledgerIndexMax;
@@ -77,7 +76,7 @@ AccountTxHandler::process(AccountTxHandler::Input const& input, Context const& c
return Error{Status{RippledError::RpcInvalidLgrRange}};
}
if (input.ledgerHash || input.ledgerIndex || input.usingValidatedLedger) {
if (not input.ledger.isUnspecified()) {
if (ctx.apiVersion > 1u && (input.ledgerIndexMax || input.ledgerIndexMin)) {
return Error{Status{RippledError::RpcInvalidParams, "containsLedgerSpecifierAndRange"}};
}
@@ -85,11 +84,10 @@ AccountTxHandler::process(AccountTxHandler::Input const& input, Context const& c
if (!input.ledgerIndexMax && !input.ledgerIndexMin) {
// mimic rippled, when both range and index specified, respect the range.
// take ledger from ledgerHash or ledgerIndex only when range is not specified
auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq(
auto const expectedLgrInfo = getLedgerHeaderFromLedgerSpecifier(
*sharedPtrBackend_,
ctx.yield,
input.ledgerHash,
input.ledgerIndex,
input.ledger,
range->maxSequence // NOLINT(bugprone-unchecked-optional-access)
);
@@ -115,20 +113,15 @@ AccountTxHandler::process(AccountTxHandler::Input const& input, Context const& c
}
}
auto const accountID = accountFromStringStrict(input.account);
std::optional<rpc::DelegateTransactionFilter> txFilter;
if (input.delegateFilter) {
txFilter.emplace(
*input.delegateFilter,
*accountID // NOLINT(bugprone-unchecked-optional-access)
);
txFilter.emplace(*input.delegateFilter, input.account);
}
auto const limit = input.limit.value_or(kLimitDefault);
auto const [txnsAndCursor, timeDiff] = util::timed([&]() {
return sharedPtrBackend_->fetchAccountTransactions(
*accountID, limit, input.forward, cursor, ctx.yield
input.account, limit, input.forward, cursor, ctx.yield
);
});
@@ -141,10 +134,6 @@ AccountTxHandler::process(AccountTxHandler::Input const& input, Context const& c
if (retCursor)
response.marker = {.ledger = retCursor->ledgerSequence, .seq = retCursor->transactionIndex};
std::optional<xrpl::uint192> mptIssuanceFilter;
if (input.mptIssuanceId)
mptIssuanceFilter = xrpl::uint192{input.mptIssuanceId->c_str()};
for (auto const& txnPlusMeta : blobs) {
// over the range
if ((txnPlusMeta.ledgerSequence < minIndex && !input.forward) ||
@@ -167,10 +156,10 @@ AccountTxHandler::process(AccountTxHandler::Input const& input, Context const& c
boost::json::object obj;
// Skip all Txns where the specified filter mpt_id doesn't match the query
if (mptIssuanceFilter) {
if (input.mptIssuanceId) {
auto const [sttx, txMeta] =
deserializeTxPlusMeta(txnPlusMeta, txnPlusMeta.ledgerSequence);
if (!util::referencesMptIssuance(*txMeta, *sttx, *mptIssuanceFilter))
if (!util::referencesMptIssuance(*txMeta, *sttx, *input.mptIssuanceId))
continue;
}
@@ -244,7 +233,7 @@ AccountTxHandler::process(AccountTxHandler::Input const& input, Context const& c
}
response.limit = input.limit;
response.account = xrpl::to_string(*accountID); // NOLINT(bugprone-unchecked-optional-access)
response.account = xrpl::to_string(input.account);
response.ledgerIndexMin = minIndex;
response.ledgerIndexMax = maxIndex;
@@ -273,12 +262,12 @@ tag_invoke(
jv.as_object()[JS(limit)] = *(output.limit);
}
} // namespace rpc
namespace rpc::spec::handlers::account_tx {
void
tag_invoke(
boost::json::value_from_tag,
boost::json::value& jv,
AccountTxHandler::Marker const& marker
)
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Marker const& marker)
{
jv = {
{JS(ledger), marker.ledger},
@@ -286,68 +275,4 @@ tag_invoke(
};
}
AccountTxHandler::Input
tag_invoke(boost::json::value_to_tag<AccountTxHandler::Input>, boost::json::value const& jv)
{
auto input = AccountTxHandler::Input{};
auto const& jsonObject = jv.as_object();
input.account = boost::json::value_to<std::string>(jsonObject.at(JS(account)));
if (jsonObject.contains(JS(ledger_index_min)) &&
util::integralValueAs<int32_t>(jsonObject.at(JS(ledger_index_min))) != -1)
input.ledgerIndexMin = util::integralValueAs<int32_t>(jsonObject.at(JS(ledger_index_min)));
if (jsonObject.contains(JS(ledger_index_max)) &&
util::integralValueAs<int32_t>(jsonObject.at(JS(ledger_index_max))) != -1)
input.ledgerIndexMax = util::integralValueAs<int32_t>(jsonObject.at(JS(ledger_index_max)));
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jsonObject.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value()) {
input.ledgerIndex = *expectedLedgerIndex;
} else {
// could not get the latest validated ledger seq here, using this flag to indicate that
input.usingValidatedLedger = true;
}
}
if (jsonObject.contains(JS(binary)))
input.binary = boost::json::value_to<JsonBool>(jsonObject.at(JS(binary)));
if (jsonObject.contains(JS(forward)))
input.forward = boost::json::value_to<JsonBool>(jsonObject.at(JS(forward)));
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));
if (jsonObject.contains(JS(marker))) {
input.marker = AccountTxHandler::Marker{
.ledger = util::integralValueAs<uint32_t>(
jsonObject.at(JS(marker)).as_object().at(JS(ledger))
),
.seq =
util::integralValueAs<uint32_t>(jsonObject.at(JS(marker)).as_object().at(JS(seq)))
};
}
if (jsonObject.contains("tx_type")) {
input.transactionTypeInLowercase =
boost::json::value_to<std::string>(jsonObject.at("tx_type"));
}
if (jsonObject.contains(JS(delegate)))
input.delegateFilter = parseDelegateFilter(jsonObject.at(JS(delegate)).as_object());
if (jsonObject.contains(JS(mpt_issuance_id))) {
input.mptIssuanceId =
boost::json::value_to<std::string>(jsonObject.at(JS(mpt_issuance_id)));
}
return input;
}
} // namespace rpc
} // namespace rpc::spec::handlers::account_tx

View File

@@ -2,30 +2,22 @@
#include "data/BackendInterface.hpp"
#include "etl/ETLServiceInterface.hpp"
#include "rpc/Errors.hpp"
#include "rpc/JS.hpp"
#include "rpc/common/JsonBool.hpp"
#include "rpc/common/MetaProcessors.hpp"
#include "rpc/common/Modifiers.hpp"
#include "rpc/common/Specs.hpp"
#include "rpc/common/Types.hpp"
#include "rpc/common/Validators.hpp"
#include "util/log/Logger.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
#include <boost/json/value.hpp>
#include <rpcspec/TxTypes.hpp>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/jss.h>
#include <rpcspec/HandlerFor.hpp>
#include <rpcspec/handlers/account_tx/Types.hpp>
#include <cstdint>
#include <expected>
#include <memory>
#include <optional>
#include <string>
#include <unordered_set>
#include <utility>
namespace rpc {
@@ -35,23 +27,17 @@ namespace rpc {
*
* For more details see: https://xrpl.org/account_tx.html
*/
class AccountTxHandler {
class AccountTxHandler : public rpc::spec::HandlerFor<rpc::spec::handlers::account_tx::Input> {
util::Logger log_{"RPC"};
std::shared_ptr<BackendInterface> sharedPtrBackend_;
std::shared_ptr<etl::ETLServiceInterface const> etl_;
public:
static constexpr auto kLimitMin = 1;
static constexpr auto kLimitMax = 1000;
static constexpr auto kLimitDefault = 200;
static constexpr auto kLimitMin = rpc::spec::handlers::account_tx::kLimitMin;
static constexpr auto kLimitMax = rpc::spec::handlers::account_tx::kLimitMax;
static constexpr auto kLimitDefault = rpc::spec::handlers::account_tx::kLimitDefault;
/**
* @brief A struct to hold the marker data
*/
struct Marker {
uint32_t ledger;
uint32_t seq;
};
using Marker = rpc::spec::handlers::account_tx::Marker;
/**
* @brief A struct to hold the output data of the command
@@ -68,27 +54,6 @@ public:
bool validated = true;
};
/**
* @brief A struct to hold the input data for the command
*/
struct Input {
std::string account;
// You must use at least one of the following fields in your request:
// ledger_index, ledger_hash, ledger_index_min, or ledger_index_max.
std::optional<std::string> ledgerHash;
std::optional<uint32_t> ledgerIndex;
std::optional<int32_t> ledgerIndexMin;
std::optional<int32_t> ledgerIndexMax;
bool usingValidatedLedger = false;
JsonBool binary{false};
JsonBool forward{false};
std::optional<uint32_t> limit;
std::optional<Marker> marker;
std::optional<std::string> transactionTypeInLowercase;
std::optional<DelegateFilter> delegateFilter;
std::optional<std::string> mptIssuanceId;
};
using Result = HandlerReturnType<Output>;
/**
@@ -105,61 +70,6 @@ public:
{
}
/**
* @brief Returns the API specification for the command
*
* @param apiVersion The api version to return the spec for
* @return The spec for the given apiVersion
*/
static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion)
{
// TODO: goes away when account_tx moves to the shared spec, where the tx_type
// validator calls this internally.
auto const& typesKeysInLowercase = rpc::spec::txTypesInLowercase();
static auto const kRpcSpecForV1 = RpcSpec{
{JS(account), validation::Required{}, validation::CustomValidators::accountValidator},
{JS(ledger_hash), validation::CustomValidators::uint256HexStringValidator},
{JS(ledger_index), validation::CustomValidators::ledgerIndexValidator},
{JS(ledger_index_min), validation::Type<int32_t>{}},
{JS(ledger_index_max), validation::Type<int32_t>{}},
{JS(ctid), validation::Type<std::string>{}},
{JS(limit),
validation::Type<uint32_t>{},
validation::Min(1u),
modifiers::Clamp<int32_t>{kLimitMin, kLimitMax}},
{JS(marker),
meta::WithCustomError{
validation::Type<boost::json::object>{},
Status{RippledError::RpcInvalidParams, "invalidMarker"},
},
meta::Section{
{JS(ledger), validation::Required{}, validation::Type<uint32_t>{}},
{JS(seq), validation::Required{}, validation::Type<uint32_t>{}},
}},
{
"tx_type",
validation::Type<std::string>{},
modifiers::ToLower{},
validation::OneOf<std::string>(
typesKeysInLowercase.cbegin(), typesKeysInLowercase.cend()
),
},
{JS(delegate), validation::CustomValidators::delegateValidator},
{JS(mpt_issuance_id), validation::CustomValidators::uint192HexStringValidator},
};
static auto const kRpcSpec = RpcSpec{
kRpcSpecForV1,
{
{JS(binary), validation::Type<bool>{}},
{JS(forward), validation::Type<bool>{}},
}
};
return apiVersion == 1 ? kRpcSpecForV1 : kRpcSpec;
}
/**
* @brief Process the AccountTx command
*
@@ -179,23 +89,13 @@ private:
*/
friend void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output const& output);
/**
* @brief Convert a JSON object to Input type
*
* @param jv The JSON object to convert
* @return Input parsed from the JSON object
*/
friend Input
tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
/**
* @brief Convert the Marker to a JSON object
*
* @param [out] jv The JSON object to convert to
* @param marker The marker to convert
*/
friend void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Marker const& marker);
};
} // namespace rpc
namespace rpc::spec::handlers::account_tx {
void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Marker const& marker);
} // namespace rpc::spec::handlers::account_tx

View File

@@ -5,16 +5,16 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
#include <boost/json/string.hpp>
#include <boost/json/value.hpp>
#include <boost/json/value_to.hpp>
#include <rpcspec/Errors.hpp>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/LedgerHeader.h>
@@ -24,6 +24,8 @@
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/jss.h>
#include <optional>
#include <ranges>
#include <string>
#include <utility>
@@ -38,11 +40,10 @@ DepositAuthorizedHandler::process(
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "DepositAuthorized ledger range must be available");
auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq(
auto const expectedLgrInfo = getLedgerHeaderFromLedgerSpecifier(
*sharedPtrBackend_,
ctx.yield,
input.ledgerHash,
input.ledgerIndex,
input.ledger,
range->maxSequence // NOLINT(bugprone-unchecked-optional-access)
);
@@ -50,21 +51,17 @@ DepositAuthorizedHandler::process(
return Error{expectedLgrInfo.error()};
auto const& lgrInfo = *expectedLgrInfo;
auto const sourceAccountID = accountFromStringStrict(input.sourceAccount);
auto const destinationAccountID = accountFromStringStrict(input.destinationAccount);
auto const& sourceAccountID = input.sourceAccount;
auto const& destinationAccountID = input.destinationAccount;
auto const srcAccountLedgerObject = sharedPtrBackend_->fetchLedgerObject(
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
xrpl::keylet::account(*sourceAccountID).key,
lgrInfo.seq,
ctx.yield
xrpl::keylet::account(sourceAccountID).key, lgrInfo.seq, ctx.yield
);
if (!srcAccountLedgerObject)
return Error{Status{RippledError::RpcSrcActNotFound, "source_accountNotFound"}};
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
auto const dstKeylet = xrpl::keylet::account(*destinationAccountID).key;
auto const dstKeylet = xrpl::keylet::account(destinationAccountID).key;
auto const dstAccountLedgerObject =
sharedPtrBackend_->fetchLedgerObject(dstKeylet, lgrInfo.seq, ctx.yield);
@@ -91,11 +88,7 @@ DepositAuthorizedHandler::process(
return Error{Status{RippledError::RpcInvalidParams, "credential array too long."}};
}
auto const credArray = credentials::fetchCredentialArray(
input.credentials,
*sourceAccountID, // NOLINT(bugprone-unchecked-optional-access)
*sharedPtrBackend_,
lgrInfo,
ctx.yield
*creds, sourceAccountID, *sharedPtrBackend_, lgrInfo, ctx.yield
);
if (!credArray.has_value())
return Error{std::move(credArray).error()};
@@ -115,52 +108,26 @@ DepositAuthorizedHandler::process(
"should already be checked above that there is no duplicate"
);
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
hashKey = xrpl::keylet::depositPreauth(*destinationAccountID, sortedAuthCreds).key;
hashKey = xrpl::keylet::depositPreauth(destinationAccountID, sortedAuthCreds).key;
} else {
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
hashKey = xrpl::keylet::depositPreauth(*destinationAccountID, *sourceAccountID).key;
hashKey = xrpl::keylet::depositPreauth(destinationAccountID, sourceAccountID).key;
}
depositAuthorized =
sharedPtrBackend_->fetchLedgerObject(hashKey, lgrInfo.seq, ctx.yield).has_value();
}
response.sourceAccount = input.sourceAccount;
response.destinationAccount = input.destinationAccount;
response.sourceAccount = xrpl::to_string(sourceAccountID);
response.destinationAccount = xrpl::to_string(destinationAccountID);
response.ledgerHash = xrpl::strHex(lgrInfo.hash);
response.ledgerIndex = lgrInfo.seq;
response.depositAuthorized = depositAuthorized;
if (credentialsPresent)
response.credentials = *input.credentials;
response.credentials = creds;
return response;
}
DepositAuthorizedHandler::Input
tag_invoke(boost::json::value_to_tag<DepositAuthorizedHandler::Input>, boost::json::value const& jv)
{
auto input = DepositAuthorizedHandler::Input{};
auto const& jsonObject = jv.as_object();
input.sourceAccount = boost::json::value_to<std::string>(jv.at(JS(source_account)));
input.destinationAccount = boost::json::value_to<std::string>(jv.at(JS(destination_account)));
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(credentials)))
input.credentials = boost::json::value_to<boost::json::array>(jv.at(JS(credentials)));
return input;
}
void
tag_invoke(
boost::json::value_from_tag,
@@ -176,8 +143,11 @@ tag_invoke(
{JS(ledger_index), output.ledgerIndex},
{JS(validated), output.validated}
};
if (output.credentials)
jv.as_object()[JS(credentials)] = *output.credentials;
if (output.credentials.has_value()) {
jv.as_object()[JS(credentials)] = *output.credentials //
| std::views::transform([](auto const& cred) { return xrpl::strHex(cred); }) //
| std::ranges::to<boost::json::array>();
}
}
} // namespace rpc

View File

@@ -1,21 +1,23 @@
#pragma once
#include "data/BackendInterface.hpp"
#include "rpc/JS.hpp"
#include "rpc/common/Specs.hpp"
#include "rpc/common/Types.hpp"
#include "rpc/common/Validators.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
#include <boost/json/value.hpp>
#include <rpcspec/HandlerFor.hpp>
#include <rpcspec/handlers/deposit_authorized/Types.hpp>
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/STArray.h>
#include <xrpl/protocol/jss.h>
#include <cstdint>
#include <expected>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
namespace rpc {
@@ -28,7 +30,8 @@ namespace rpc {
*
* For more details see: https://xrpl.org/deposit_authorized.html
*/
class DepositAuthorizedHandler {
class DepositAuthorizedHandler
: public rpc::spec::HandlerFor<rpc::spec::handlers::deposit_authorized::Input> {
// dependencies
std::shared_ptr<BackendInterface> const sharedPtrBackend_;
@@ -43,23 +46,12 @@ public:
std::string destinationAccount;
std::string ledgerHash;
uint32_t ledgerIndex{};
std::optional<boost::json::array> credentials;
std::optional<std::vector<xrpl::uint256>> credentials;
// validated should be sent via framework
bool validated = true;
};
/**
* @brief A struct to hold the input data for the command
*/
struct Input {
std::string sourceAccount;
std::string destinationAccount;
std::optional<std::string> ledgerHash;
std::optional<uint32_t> ledgerIndex;
std::optional<boost::json::array> credentials;
};
using Result = HandlerReturnType<Output>;
/**
@@ -72,30 +64,6 @@ public:
{
}
/**
* @brief Returns the API specification for the command
*
* @param apiVersion The api version to return the spec for
* @return The spec for the given apiVersion
*/
static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion)
{
static auto const kRpcSpec = RpcSpec{
{JS(source_account),
validation::Required{},
validation::CustomValidators::accountValidator},
{JS(destination_account),
validation::Required{},
validation::CustomValidators::accountValidator},
{JS(ledger_hash), validation::CustomValidators::uint256HexStringValidator},
{JS(ledger_index), validation::CustomValidators::ledgerIndexValidator},
{JS(credentials), validation::Type<boost::json::array>{}, validation::Hex256ItemType()}
};
return kRpcSpec;
}
/**
* @brief Process the DepositAuthorized command
*
@@ -115,14 +83,6 @@ private:
*/
friend void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output const& output);
/**
* @brief Convert a JSON object to Input type
*
* @param jv The JSON object to convert
* @return Input parsed from the JSON object
*/
friend Input
tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
};
} // namespace rpc

View File

@@ -3,9 +3,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/AccountUtils.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/asio/spawn.hpp>
#include <boost/bimap/bimap.hpp>
@@ -13,10 +11,9 @@
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
#include <boost/json/value.hpp>
#include <boost/json/value_to.hpp>
#include <rpcspec/handlers/get_aggregate_price/Types.hpp>
#include <xrpl/basics/Number.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Issue.h>
@@ -47,11 +44,10 @@ GetAggregatePriceHandler::process(
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "GetAggregatePrice's ledger range must be available");
auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq(
auto const expectedLgrInfo = getLedgerHeaderFromLedgerSpecifier(
*sharedPtrBackend_,
ctx.yield,
input.ledgerHash,
input.ledgerIndex,
input.ledger,
range->maxSequence // NOLINT(bugprone-unchecked-optional-access)
);
@@ -86,9 +82,8 @@ GetAggregatePriceHandler::process(
series.begin(),
series.end(),
[&](xrpl::STObject const& o) -> bool {
return o.getFieldCurrency(xrpl::sfBaseAsset).getText() ==
input.baseAsset and
o.getFieldCurrency(xrpl::sfQuoteAsset).getText() == input.quoteAsset and
return o.getFieldCurrency(xrpl::sfBaseAsset) == input.baseAsset and
o.getFieldCurrency(xrpl::sfQuoteAsset) == input.quoteAsset and
o.isFieldPresent(xrpl::sfAssetPrice);
}
);
@@ -249,46 +244,6 @@ GetAggregatePriceHandler::tracebackOracleObject(
}
}
GetAggregatePriceHandler::Input
tag_invoke(boost::json::value_to_tag<GetAggregatePriceHandler::Input>, boost::json::value const& jv)
{
auto input = GetAggregatePriceHandler::Input{};
auto const& jsonObject = jv.as_object();
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
for (auto const& oracle : jsonObject.at(JS(oracles)).as_array()) {
input.oracles.push_back(
GetAggregatePriceHandler::Oracle{
.documentId = boost::json::value_to<std::uint64_t>(
oracle.as_object().at(JS(oracle_document_id))
),
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
.account = *util::parseBase58Wrapper<xrpl::AccountID>(
boost::json::value_to<std::string>(oracle.as_object().at(JS(account)))
)
}
);
}
input.baseAsset = boost::json::value_to<std::string>(jv.at(JS(base_asset)));
input.quoteAsset = boost::json::value_to<std::string>(jv.at(JS(quote_asset)));
if (jsonObject.contains(JS(trim)))
input.trim = util::integralValueAs<uint8_t>(jv.at(JS(trim)));
if (jsonObject.contains(JS(time_threshold)))
input.timeThreshold = util::integralValueAs<uint32_t>(jv.at(JS(time_threshold)));
return input;
}
void
tag_invoke(
boost::json::value_from_tag,

View File

@@ -2,28 +2,27 @@
#include "data/BackendInterface.hpp"
#include "rpc/Errors.hpp"
#include "rpc/JS.hpp"
#include "rpc/common/MetaProcessors.hpp"
#include "rpc/common/Modifiers.hpp"
#include "rpc/common/Specs.hpp"
#include "rpc/common/Types.hpp"
#include "rpc/common/Validators.hpp"
#include <boost/asio/spawn.hpp>
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
#include <boost/json/value.hpp>
#include <rpcspec/HandlerFor.hpp>
#include <rpcspec/handlers/get_aggregate_price/Types.hpp>
#include <xrpl/basics/Number.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/jss.h>
#include <cstddef>
#include <cstdint>
#include <expected>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace rpc {
@@ -31,7 +30,8 @@ namespace rpc {
/**
*@brief The get_aggregate_price method.
*/
class GetAggregatePriceHandler {
class GetAggregatePriceHandler
: public rpc::spec::HandlerFor<rpc::spec::handlers::get_aggregate_price::Input> {
std::shared_ptr<BackendInterface> sharedPtrBackend_;
public:
@@ -58,27 +58,6 @@ public:
bool validated = true;
};
/**
* @brief A struct to hold the input oracle data
*/
struct Oracle {
std::uint32_t documentId{0};
xrpl::AccountID account;
};
/**
* @brief A struct to hold the input data for the command
*/
struct Input {
std::optional<std::string> ledgerHash;
std::optional<std::uint32_t> ledgerIndex;
std::vector<Oracle> oracles; // valid range is 1-200
std::string baseAsset;
std::string quoteAsset;
std::optional<std::uint32_t> timeThreshold;
std::optional<std::uint8_t> trim; // valid range is 1-25
};
using Result = HandlerReturnType<Output>;
/**
@@ -91,83 +70,6 @@ public:
{
}
/**
* @brief Returns the API specification for the command
*
* @param apiVersion The api version to return the spec for
* @return The spec for the given apiVersion
*/
static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion)
{
static constexpr auto kOraclesMax = 200;
static auto const kOraclesValidator = modifiers::CustomModifier{
[](boost::json::value& value, std::string_view) -> MaybeError {
if (!value.is_array() or value.as_array().empty() or
value.as_array().size() > kOraclesMax)
return Error{Status{RippledError::RpcOracleMalformed}};
for (auto& oracle : value.as_array()) {
if (!oracle.is_object() or
!oracle.as_object().contains(JS(oracle_document_id)) or
!oracle.as_object().contains(JS(account)))
return Error{Status{RippledError::RpcOracleMalformed}};
auto maybeError = validation::Type<std::uint32_t, std::string>{}.verify(
oracle, JS(oracle_document_id)
);
if (!maybeError)
return maybeError;
maybeError = modifiers::ToNumber::modify(oracle, JS(oracle_document_id));
if (!maybeError)
return maybeError;
maybeError = validation::CustomValidators::accountBase58Validator.verify(
oracle.as_object(), JS(account)
);
if (!maybeError)
return Error{Status{RippledError::RpcInvalidParams}};
};
return MaybeError{};
}
};
static auto const kRpcSpec = RpcSpec{
{JS(ledger_hash), validation::CustomValidators::uint256HexStringValidator},
{JS(ledger_index), validation::CustomValidators::ledgerIndexValidator},
// validate quoteAsset and base_asset in accordance to the currency code found in XRPL
// doc:
// https://xrpl.org/docs/references/protocol/data-types/currency-formats#currency-codes
// usually Clio returns rpcMALFORMED_CURRENCY , return InvalidParam here just to mimic
// rippled
{JS(base_asset),
validation::Required{},
meta::WithCustomError{
validation::CustomValidators::currencyValidator,
Status(RippledError::RpcInvalidParams)
}},
{JS(quote_asset),
validation::Required{},
meta::WithCustomError{
validation::CustomValidators::currencyValidator,
Status(RippledError::RpcInvalidParams)
}},
{JS(oracles), validation::Required{}, kOraclesValidator},
// note: Unlike `rippled`, Clio only supports UInt as input, no string, no `null`, etc.
{JS(time_threshold), validation::Type<std::uint32_t>{}},
{
JS(trim),
validation::Type<std::uint8_t>{},
validation::Between<std::uint8_t>{1, 25},
}
};
return kRpcSpec;
}
/**
* @brief Process the GetAggregatePrice command
*
@@ -200,15 +102,6 @@ private:
*/
friend void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output const& output);
/**
* @brief Convert a JSON object to Input type
*
* @param jv The JSON object to convert
* @return Input parsed from the JSON object
*/
friend Input
tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
};
} // namespace rpc

View File

@@ -5,15 +5,13 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include "util/log/Logger.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
#include <boost/json/value.hpp>
#include <boost/json/value_to.hpp>
#include <rpcspec/Errors.hpp>
#include <rpcspec/LedgerTypes.hpp>
#include <rpcspec/handlers/ledger_data/Types.hpp>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/protocol/LedgerFormats.h>
@@ -27,8 +25,10 @@
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <string>
#include <utility>
#include <variant>
#include <vector>
namespace rpc {
@@ -36,21 +36,24 @@ namespace rpc {
LedgerDataHandler::Result
LedgerDataHandler::process(Input const& input, Context const& ctx) const
{
// marker must be int if outOfOrder is true
if (input.outOfOrder && input.marker)
using rpc::spec::handlers::ledger_data::MarkerValue;
auto const* uint256Marker = input.marker ? std::get_if<xrpl::uint256>(&*input.marker) : nullptr;
auto const* diffMarker = input.marker ? std::get_if<uint32_t>(&*input.marker) : nullptr;
if (input.outOfOrder && uint256Marker != nullptr)
return Error{Status{RippledError::RpcInvalidParams, "outOfOrderMarkerNotInt"}};
if (!input.outOfOrder && input.diffMarker)
if (!input.outOfOrder && diffMarker != nullptr)
return Error{Status{RippledError::RpcInvalidParams, "markerNotString"}};
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "LedgerData's ledger range must be available");
auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq(
auto const expectedLgrInfo = getLedgerHeaderFromLedgerSpecifier(
*sharedPtrBackend_,
ctx.yield,
input.ledgerHash,
input.ledgerIndex,
input.ledger,
range->maxSequence // NOLINT(bugprone-unchecked-optional-access)
);
@@ -62,11 +65,11 @@ LedgerDataHandler::process(Input const& input, Context const& ctx) const
Output output;
// no marker -> first call, return header information
if ((!input.marker) && (!input.diffMarker)) {
if (not input.marker.has_value()) {
output.header = toJson(lgrInfo, input.binary, ctx.apiVersion);
} else {
if (input.marker &&
!sharedPtrBackend_->fetchLedgerObject(*(input.marker), lgrInfo.seq, ctx.yield))
if (uint256Marker != nullptr &&
!sharedPtrBackend_->fetchLedgerObject(*uint256Marker, lgrInfo.seq, ctx.yield))
return Error{Status{RippledError::RpcInvalidParams, "markerDoesNotExist"}};
}
@@ -76,9 +79,9 @@ LedgerDataHandler::process(Input const& input, Context const& ctx) const
auto const start = std::chrono::system_clock::now();
std::vector<data::LedgerObject> results;
if (input.diffMarker) {
if (diffMarker != nullptr) {
// keep the same logic as previous implementation
auto diff = sharedPtrBackend_->fetchLedgerDiff(*(input.diffMarker), ctx.yield);
auto diff = sharedPtrBackend_->fetchLedgerDiff(*diffMarker, ctx.yield);
std::vector<xrpl::uint256> keys;
for (auto& [key, object] : diff) {
@@ -94,17 +97,19 @@ LedgerDataHandler::process(Input const& input, Context const& ctx) const
results.push_back({.key = keys[i], .blob = std::move(obj)});
}
if (*(input.diffMarker) > lgrInfo.seq)
output.diffMarker = *(input.diffMarker) - 1;
if (*diffMarker > lgrInfo.seq)
output.diffMarker = *diffMarker - 1;
} else {
// limit's limitation is different based on binary or json
// framework can not handler the check right now, adjust the value here
auto const limit = std::min(
input.limit,
input.binary ? LedgerDataHandler::kLimitBinary : LedgerDataHandler::kLimitJson
);
auto const maxLimit =
input.binary ? LedgerDataHandler::kLimitBinary : LedgerDataHandler::kLimitJson;
auto const limit = std::min(input.limit.value_or(maxLimit), maxLimit);
std::optional<xrpl::uint256> pageMarker;
if (uint256Marker != nullptr)
pageMarker = *uint256Marker;
auto page = sharedPtrBackend_->fetchLedgerPage(
input.marker, lgrInfo.seq, limit, input.outOfOrder, ctx.yield
pageMarker, lgrInfo.seq, limit, input.outOfOrder, ctx.yield
);
results = std::move(page.objects);
@@ -178,48 +183,4 @@ tag_invoke(
jv = std::move(obj);
}
LedgerDataHandler::Input
tag_invoke(boost::json::value_to_tag<LedgerDataHandler::Input>, boost::json::value const& jv)
{
auto input = LedgerDataHandler::Input{};
auto const& jsonObject = jv.as_object();
if (jsonObject.contains(JS(binary))) {
input.binary = jsonObject.at(JS(binary)).as_bool();
input.limit =
input.binary ? LedgerDataHandler::kLimitBinary : LedgerDataHandler::kLimitJson;
}
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));
if (jsonObject.contains("out_of_order"))
input.outOfOrder = jsonObject.at("out_of_order").as_bool();
if (jsonObject.contains(JS(marker))) {
if (jsonObject.at(JS(marker)).is_string()) {
input.marker =
xrpl::uint256{boost::json::value_to<std::string>(jsonObject.at(JS(marker))).data()};
} else {
input.diffMarker = util::integralValueAs<uint32_t>(jsonObject.at(JS(marker)));
}
}
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jsonObject.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(type))) {
input.type =
rpc::spec::ledgerEntryTypeFromStr(boost::json::value_to<std::string>(jv.at(JS(type))));
}
return input;
}
} // namespace rpc

View File

@@ -1,26 +1,25 @@
#pragma once
#include "data/BackendInterface.hpp"
#include "rpc/JS.hpp"
#include "rpc/common/Checkers.hpp"
#include "rpc/common/MetaProcessors.hpp"
#include "rpc/common/Specs.hpp"
#include "rpc/Errors.hpp"
#include "rpc/common/Types.hpp"
#include "rpc/common/Validators.hpp"
#include "util/log/Logger.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
#include <boost/json/value.hpp>
#include <rpcspec/HandlerFor.hpp>
#include <rpcspec/handlers/ledger_data/Types.hpp>
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <cstdint>
#include <expected>
#include <memory>
#include <optional>
#include <string>
#include <utility>
namespace rpc {
@@ -30,15 +29,15 @@ namespace rpc {
*
* For more details see: https://xrpl.org/ledger_data.html
*/
class LedgerDataHandler {
class LedgerDataHandler : public rpc::spec::HandlerFor<rpc::spec::handlers::ledger_data::Input> {
// dependencies
std::shared_ptr<BackendInterface> sharedPtrBackend_;
util::Logger log_{"RPC"};
public:
// constants
static constexpr uint32_t kLimitBinary = 2048;
static constexpr uint32_t kLimitJson = 256;
static constexpr auto kLimitBinary = rpc::spec::handlers::ledger_data::kLimitBinary;
static constexpr auto kLimitJson = rpc::spec::handlers::ledger_data::kLimitJson;
/**
* @brief A struct to hold the output data of the command
@@ -54,23 +53,6 @@ public:
bool validated = true;
};
/**
* @brief A struct to hold the input data for the command
*
* @note `outOfOrder` is only for Clio, there is no document, traverse via seq diff (outOfOrder
* implementation is copied from old rpc handler)
*/
struct Input {
std::optional<std::string> ledgerHash;
std::optional<uint32_t> ledgerIndex;
bool binary = false;
uint32_t limit = LedgerDataHandler::kLimitJson; // max 256 for json ; 2048 for binary
std::optional<xrpl::uint256> marker;
std::optional<uint32_t> diffMarker;
bool outOfOrder = false;
xrpl::LedgerEntryType type = xrpl::LedgerEntryType::ltANY;
};
using Result = HandlerReturnType<Output>;
/**
@@ -83,30 +65,6 @@ public:
{
}
/**
* @brief Returns the API specification for the command
*
* @param apiVersion The api version to return the spec for
* @return The spec for the given apiVersion
*/
static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion)
{
static auto const kRpcSpec = RpcSpec{
{JS(binary), validation::Type<bool>{}},
{"out_of_order", validation::Type<bool>{}},
{JS(ledger_hash), validation::CustomValidators::uint256HexStringValidator},
{JS(ledger_index), validation::CustomValidators::ledgerIndexValidator},
{JS(limit), validation::Type<uint32_t>{}, validation::Min(1u)},
{JS(marker),
validation::Type<uint32_t, std::string>{},
meta::IfType<std::string>{validation::CustomValidators::uint256HexStringValidator}},
{JS(type), validation::CustomValidators::ledgerTypeValidator},
{JS(ledger), check::Deprecated{}},
};
return kRpcSpec;
}
/**
* @brief Process the LedgerData command
*
@@ -126,14 +84,6 @@ private:
*/
friend void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output const& output);
/**
* @brief Convert a JSON object to Input type
*
* @param jv The JSON object to convert
* @return Input parsed from the JSON object
*/
friend Input
tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
};
} // namespace rpc

View File

@@ -15,6 +15,7 @@
#include <boost/json/value_from.hpp>
#include <boost/json/value_to.hpp>
#include <rpcspec/Errors.hpp>
#include <rpcspec/handlers/mptoken_issuance_history/Types.hpp>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/basics/strHex.h>
@@ -56,7 +57,7 @@ MPTokenIssuanceHistoryHandler::process(
if (not range.has_value())
return Error{range.error()};
auto const mptIssuanceID = xrpl::uint192{input.mptIssuanceID.c_str()};
auto const& mptIssuanceID = input.mptIssuanceId;
auto const [page, timeDiff] =
util::timed([&] { return fetchTransactions(input, ctx, mptIssuanceID, *range); });
@@ -120,13 +121,13 @@ MPTokenIssuanceHistoryHandler::resolveSequenceRange(Input const& input, Context
if (resolved.min > resolved.max)
return Error{Status{RippledError::RpcLgrIdxsInvalid}};
if (input.ledgerHash.has_value() || input.ledgerIndex.has_value()) {
if (not input.ledger.isUnspecified()) {
// rippled does not have this check
if (input.ledgerIndexMax.has_value() || input.ledgerIndexMin.has_value())
return Error{Status{RippledError::RpcInvalidParams, "containsLedgerSpecifierAndRange"}};
auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, dbMaxSeq
auto const expectedLgrInfo = getLedgerHeaderFromLedgerSpecifier(
*sharedPtrBackend_, ctx.yield, input.ledger, dbMaxSeq
);
if (not expectedLgrInfo.has_value())
@@ -163,13 +164,8 @@ MPTokenIssuanceHistoryHandler::fetchTransactions(
// tx_type is applied post-fetch, as account_tx does.
if (input.account.has_value()) {
auto const account = accountFromStringStrict(*input.account);
if (not account.has_value()) {
ASSERT(false, "Account must be decodable after spec validation");
std::unreachable();
}
return sharedPtrBackend_->fetchAccountMPTokenIssuanceTransactions(
mptIssuanceID, *account, limit, input.forward, startCursor, ctx.yield
mptIssuanceID, *input.account, limit, input.forward, startCursor, ctx.yield
);
}
@@ -199,7 +195,7 @@ MPTokenIssuanceHistoryHandler::processTransactionsPage(
if (txnPlusMeta.transaction.empty() || txnPlusMeta.metadata.empty()) {
LOG(log_.warn()) << "Skipping index entry with no matching transaction record; "
"mpt_issuance_id = "
<< input.mptIssuanceID;
<< xrpl::to_string(input.mptIssuanceId);
continue;
}
@@ -304,12 +300,12 @@ tag_invoke(
jv.as_object()[JS(limit)] = *(output.limit);
}
} // namespace rpc
namespace rpc::spec::handlers::mptoken_issuance_history {
void
tag_invoke(
boost::json::value_from_tag,
boost::json::value& jv,
MPTokenIssuanceHistoryHandler::Marker const& marker
)
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Marker const& marker)
{
jv = {
{JS(ledger), marker.ledger},
@@ -317,62 +313,4 @@ tag_invoke(
};
}
MPTokenIssuanceHistoryHandler::Input
tag_invoke(
boost::json::value_to_tag<MPTokenIssuanceHistoryHandler::Input>,
boost::json::value const& jv
)
{
auto const& jsonObject = jv.as_object();
auto input = MPTokenIssuanceHistoryHandler::Input{};
input.mptIssuanceID = boost::json::value_to<std::string>(jsonObject.at(JS(mpt_issuance_id)));
if (jsonObject.contains(JS(account)))
input.account = boost::json::value_to<std::string>(jsonObject.at(JS(account)));
if (jsonObject.contains("tx_type")) {
input.transactionTypeInLowercase =
boost::json::value_to<std::string>(jsonObject.at("tx_type"));
}
if (jsonObject.contains(JS(ledger_index_min)) &&
util::integralValueAs<int32_t>(jsonObject.at(JS(ledger_index_min))) != -1)
input.ledgerIndexMin = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index_min)));
if (jsonObject.contains(JS(ledger_index_max)) &&
util::integralValueAs<int32_t>(jsonObject.at(JS(ledger_index_max))) != -1)
input.ledgerIndexMax = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index_max)));
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jsonObject.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(binary)))
input.binary = jsonObject.at(JS(binary)).as_bool();
if (jsonObject.contains(JS(forward)))
input.forward = jsonObject.at(JS(forward)).as_bool();
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));
if (jsonObject.contains(JS(marker))) {
input.marker = MPTokenIssuanceHistoryHandler::Marker{
.ledger = util::integralValueAs<uint32_t>(
jsonObject.at(JS(marker)).as_object().at(JS(ledger))
),
.seq =
util::integralValueAs<uint32_t>(jsonObject.at(JS(marker)).as_object().at(JS(seq)))
};
}
return input;
}
} // namespace rpc
} // namespace rpc::spec::handlers::mptoken_issuance_history

View File

@@ -2,23 +2,16 @@
#include "data/BackendInterface.hpp"
#include "data/Types.hpp"
#include "rpc/Errors.hpp"
#include "rpc/JS.hpp"
#include "rpc/common/MetaProcessors.hpp"
#include "rpc/common/Modifiers.hpp"
#include "rpc/common/Specs.hpp"
#include "rpc/common/Types.hpp"
#include "rpc/common/Validators.hpp"
#include "util/log/Logger.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
#include <boost/json/value.hpp>
#include <rpcspec/TxTypes.hpp>
#include <rpcspec/HandlerFor.hpp>
#include <rpcspec/handlers/mptoken_issuance_history/Types.hpp>
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/jss.h>
#include <atomic>
#include <cstdint>
@@ -37,7 +30,8 @@ namespace rpc {
* @note This is a Clio-only method. Requests fail with `notReady` until the issuance-history
* backfill reports `Migrated`, so partial history is never served.
*/
class MPTokenIssuanceHistoryHandler {
class MPTokenIssuanceHistoryHandler
: public rpc::spec::HandlerFor<rpc::spec::handlers::mptoken_issuance_history::Input> {
util::Logger log_{"RPC"};
std::shared_ptr<BackendInterface> sharedPtrBackend_;
@@ -49,9 +43,10 @@ class MPTokenIssuanceHistoryHandler {
std::shared_ptr<std::atomic_bool> migrated_ = std::make_shared<std::atomic_bool>(false);
public:
static constexpr auto kLimitMin = 1;
static constexpr auto kLimitMax = 100;
static constexpr auto kLimitDefault = 50;
static constexpr auto kLimitMin = rpc::spec::handlers::mptoken_issuance_history::kLimitMin;
static constexpr auto kLimitMax = rpc::spec::handlers::mptoken_issuance_history::kLimitMax;
static constexpr auto kLimitDefault =
rpc::spec::handlers::mptoken_issuance_history::kLimitDefault;
/**
* @brief The name used to query the issuance-history migrator's status.
@@ -63,10 +58,7 @@ public:
/**
* @brief A struct to hold the marker data.
*/
struct Marker {
uint32_t ledger;
uint32_t seq;
};
using Marker = rpc::spec::handlers::mptoken_issuance_history::Marker;
/**
* @brief A struct to hold the output data of the command.
@@ -83,26 +75,6 @@ public:
bool validated = true;
};
/**
* @brief A struct to hold the input data for the command.
*
* @note When no ledger selector is provided, the request uses the backend's full available
* ledger range.
*/
struct Input {
std::string mptIssuanceID;
std::optional<std::string> account;
std::optional<std::string> transactionTypeInLowercase;
std::optional<std::string> ledgerHash;
std::optional<uint32_t> ledgerIndex;
std::optional<int32_t> ledgerIndexMin;
std::optional<int32_t> ledgerIndexMax;
bool binary = false;
bool forward = false;
std::optional<uint32_t> limit;
std::optional<Marker> marker;
};
using Result = HandlerReturnType<Output>;
/**
@@ -115,55 +87,6 @@ public:
{
}
/**
* @brief Returns the API specification for the command.
*
* @param apiVersion The api version to return the spec for.
* @return The spec for the given apiVersion.
*/
static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion)
{
// TODO: goes away when mptoken_issuance_history moves to the shared spec, where the
// validator calls this internally.
auto const& typesKeysInLowercase = rpc::spec::txTypesInLowercase();
static auto const kRpcSpec = RpcSpec{
{JS(mpt_issuance_id),
validation::Required{},
validation::CustomValidators::uint192HexStringValidator},
{JS(account), validation::CustomValidators::accountValidator},
{
"tx_type",
validation::Type<std::string>{},
modifiers::ToLower{},
validation::OneOf<std::string>(
typesKeysInLowercase.cbegin(), typesKeysInLowercase.cend()
),
},
{JS(ledger_hash), validation::CustomValidators::uint256HexStringValidator},
{JS(ledger_index), validation::CustomValidators::ledgerIndexValidator},
{JS(ledger_index_min), validation::Type<int32_t>{}},
{JS(ledger_index_max), validation::Type<int32_t>{}},
{JS(binary), validation::Type<bool>{}},
{JS(forward), validation::Type<bool>{}},
{JS(limit),
validation::Type<uint32_t>{},
validation::Min(1u),
modifiers::Clamp<int32_t>{kLimitMin, kLimitMax}},
{JS(marker),
meta::WithCustomError{
validation::Type<boost::json::object>{},
Status{RippledError::RpcInvalidParams, "invalidMarker"}
},
meta::Section{
{JS(ledger), validation::Required{}, validation::Type<uint32_t>{}},
{JS(seq), validation::Required{}, validation::Type<uint32_t>{}},
}},
};
return kRpcSpec;
}
/**
* @brief Process the MPTokenIssuanceHistory command.
*
@@ -295,24 +218,13 @@ private:
*/
friend void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output const& output);
/**
* @brief Convert a JSON object to Input type.
*
* @param jv The JSON object to convert.
* @return Input parsed from the JSON object.
*/
friend Input
tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
/**
* @brief Convert the Marker to a JSON object.
*
* @param [out] jv The JSON object to convert to.
* @param marker The marker to convert.
*/
friend void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Marker const& marker);
};
} // namespace rpc
namespace rpc::spec::handlers::mptoken_issuance_history {
void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Marker const& marker);
} // namespace rpc::spec::handlers::mptoken_issuance_history

View File

@@ -5,7 +5,6 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include "util/Profiler.hpp"
#include "util/log/Logger.hpp"
@@ -13,8 +12,8 @@
#include <boost/json/object.hpp>
#include <boost/json/value.hpp>
#include <boost/json/value_from.hpp>
#include <boost/json/value_to.hpp>
#include <rpcspec/Errors.hpp>
#include <rpcspec/handlers/nft_history/Types.hpp>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/basics/strHex.h>
@@ -45,7 +44,7 @@ NFTHistoryHandler::process(NFTHistoryHandler::Input const& input, Context const&
return Error{Status{RippledError::RpcLgrIdxMalformed, "ledgerSeqMinOutOfRange"}};
// NOLINTEND(bugprone-unchecked-optional-access)
minIndex = *input.ledgerIndexMin;
minIndex = static_cast<uint32_t>(*input.ledgerIndexMin);
}
if (input.ledgerIndexMax) {
@@ -54,23 +53,22 @@ NFTHistoryHandler::process(NFTHistoryHandler::Input const& input, Context const&
return Error{Status{RippledError::RpcLgrIdxMalformed, "ledgerSeqMaxOutOfRange"}};
// NOLINTEND(bugprone-unchecked-optional-access)
maxIndex = *input.ledgerIndexMax;
maxIndex = static_cast<uint32_t>(*input.ledgerIndexMax);
}
if (minIndex > maxIndex)
return Error{Status{RippledError::RpcLgrIdxsInvalid}};
if (input.ledgerHash || input.ledgerIndex) {
if (not input.ledger.isUnspecified()) {
// rippled does not have this check
if (input.ledgerIndexMax || input.ledgerIndexMin) {
return Error{Status{RippledError::RpcInvalidParams, "containsLedgerSpecifierAndRange"}};
}
auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq(
auto const expectedLgrInfo = getLedgerHeaderFromLedgerSpecifier(
*sharedPtrBackend_,
ctx.yield,
input.ledgerHash,
input.ledgerIndex,
input.ledger,
range->maxSequence // NOLINT(bugprone-unchecked-optional-access)
);
@@ -94,7 +92,7 @@ NFTHistoryHandler::process(NFTHistoryHandler::Input const& input, Context const&
}
auto const limit = input.limit.value_or(kLimitDefault);
auto const tokenID = xrpl::uint256{input.nftID.c_str()};
auto const& tokenID = input.nftID;
auto const [txnsAndCursor, timeDiff] = util::timed([&]() {
return sharedPtrBackend_->fetchNFTTransactions(
@@ -185,12 +183,12 @@ tag_invoke(
jv.as_object()[JS(limit)] = *(output.limit);
}
} // namespace rpc
namespace rpc::spec::handlers::nft_history {
void
tag_invoke(
boost::json::value_from_tag,
boost::json::value& jv,
NFTHistoryHandler::Marker const& marker
)
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Marker const& marker)
{
jv = {
{JS(ledger), marker.ledger},
@@ -198,51 +196,4 @@ tag_invoke(
};
}
NFTHistoryHandler::Input
tag_invoke(boost::json::value_to_tag<NFTHistoryHandler::Input>, boost::json::value const& jv)
{
auto const& jsonObject = jv.as_object();
auto input = NFTHistoryHandler::Input{};
input.nftID = boost::json::value_to<std::string>(jsonObject.at(JS(nft_id)));
if (jsonObject.contains(JS(ledger_index_min)) &&
util::integralValueAs<int32_t>(jsonObject.at(JS(ledger_index_min))) != -1)
input.ledgerIndexMin = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index_min)));
if (jsonObject.contains(JS(ledger_index_max)) &&
util::integralValueAs<int32_t>(jsonObject.at(JS(ledger_index_max))) != -1)
input.ledgerIndexMax = util::integralValueAs<uint32_t>(jsonObject.at(JS(ledger_index_max)));
if (jsonObject.contains(JS(ledger_hash)))
input.ledgerHash = boost::json::value_to<std::string>(jsonObject.at(JS(ledger_hash)));
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
if (jsonObject.contains(JS(binary)))
input.binary = jsonObject.at(JS(binary)).as_bool();
if (jsonObject.contains(JS(forward)))
input.forward = jsonObject.at(JS(forward)).as_bool();
if (jsonObject.contains(JS(limit)))
input.limit = util::integralValueAs<uint32_t>(jsonObject.at(JS(limit)));
if (jsonObject.contains(JS(marker))) {
input.marker = NFTHistoryHandler::Marker{
.ledger = util::integralValueAs<uint32_t>(
jsonObject.at(JS(marker)).as_object().at(JS(ledger))
),
.seq =
util::integralValueAs<uint32_t>(jsonObject.at(JS(marker)).as_object().at(JS(seq)))
};
}
return input;
}
} // namespace rpc
} // namespace rpc::spec::handlers::nft_history

View File

@@ -2,25 +2,22 @@
#include "data/BackendInterface.hpp"
#include "rpc/Errors.hpp"
#include "rpc/JS.hpp"
#include "rpc/common/MetaProcessors.hpp"
#include "rpc/common/Modifiers.hpp"
#include "rpc/common/Specs.hpp"
#include "rpc/common/Types.hpp"
#include "rpc/common/Validators.hpp"
#include "util/log/Logger.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
#include <boost/json/value.hpp>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/jss.h>
#include <rpcspec/HandlerFor.hpp>
#include <rpcspec/handlers/nft_history/Types.hpp>
#include <cstdint>
#include <expected>
#include <memory>
#include <optional>
#include <string>
#include <utility>
namespace rpc {
@@ -30,23 +27,19 @@ namespace rpc {
*
* For more details see: https://xrpl.org/nft_history.html#nft_history
*/
class NFTHistoryHandler {
class NFTHistoryHandler : public rpc::spec::HandlerFor<rpc::spec::handlers::nft_history::Input> {
util::Logger log_{"RPC"};
std::shared_ptr<BackendInterface> sharedPtrBackend_;
public:
static constexpr auto kLimitMin = 1;
static constexpr auto kLimitMax = 100;
static constexpr auto kLimitDefault = 50;
static constexpr auto kLimitMin = rpc::spec::handlers::nft_history::kLimitMin;
static constexpr auto kLimitMax = rpc::spec::handlers::nft_history::kLimitMax;
static constexpr auto kLimitDefault = rpc::spec::handlers::nft_history::kLimitDefault;
/**
* @brief A struct to hold the marker data
*/
// TODO: this marker is same as account_tx, reuse in future
struct Marker {
uint32_t ledger;
uint32_t seq;
};
using Marker = rpc::spec::handlers::nft_history::Marker;
/**
* @brief A struct to hold the output data of the command
@@ -63,23 +56,6 @@ public:
bool validated = true;
};
/**
* @brief A struct to hold the input data for the command
*/
struct Input {
std::string nftID;
// You must use at least one of the following fields in your request:
// ledger_index, ledger_hash, ledger_index_min, or ledger_index_max.
std::optional<std::string> ledgerHash;
std::optional<uint32_t> ledgerIndex;
std::optional<int32_t> ledgerIndexMin;
std::optional<int32_t> ledgerIndexMax;
bool binary = false;
bool forward = false;
std::optional<uint32_t> limit;
std::optional<Marker> marker;
};
using Result = HandlerReturnType<Output>;
/**
@@ -92,43 +68,6 @@ public:
{
}
/**
* @brief Returns the API specification for the command
*
* @param apiVersion The api version to return the spec for
* @return The spec for the given apiVersion
*/
static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion)
{
static auto const kRpcSpec = RpcSpec{
{JS(nft_id),
validation::Required{},
validation::CustomValidators::uint256HexStringValidator},
{JS(ledger_hash), validation::CustomValidators::uint256HexStringValidator},
{JS(ledger_index), validation::CustomValidators::ledgerIndexValidator},
{JS(ledger_index_min), validation::Type<int32_t>{}},
{JS(ledger_index_max), validation::Type<int32_t>{}},
{JS(binary), validation::Type<bool>{}},
{JS(forward), validation::Type<bool>{}},
{JS(limit),
validation::Type<uint32_t>{},
validation::Min(1u),
modifiers::Clamp<int32_t>{kLimitMin, kLimitMax}},
{JS(marker),
meta::WithCustomError{
validation::Type<boost::json::object>{},
Status{RippledError::RpcInvalidParams, "invalidMarker"}
},
meta::Section{
{JS(ledger), validation::Required{}, validation::Type<uint32_t>{}},
{JS(seq), validation::Required{}, validation::Type<uint32_t>{}},
}},
};
return kRpcSpec;
}
/**
* @brief Process the NFTHistory command
*
@@ -148,24 +87,13 @@ private:
*/
friend void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output const& output);
/**
* @brief Convert a JSON object to Input type
*
* @param jv The JSON object to convert
* @return Input parsed from the JSON object
*/
friend Input
tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
/**
* @brief Convert the Marker to a JSON object
*
* @param [out] jv The JSON object to convert to
* @param marker The marker to convert
*/
friend void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Marker const& marker);
};
} // namespace rpc
namespace rpc::spec::handlers::nft_history {
void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Marker const& marker);
} // namespace rpc::spec::handlers::nft_history

View File

@@ -5,13 +5,11 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
#include <boost/json/value.hpp>
#include <rpcspec/Errors.hpp>
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/LedgerHeader.h>
@@ -22,10 +20,9 @@
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/jss.h>
#include <cstdint>
#include <expected>
#include <memory>
#include <optional>
#include <string>
#include <utility>
namespace rpc {
@@ -46,7 +43,7 @@ validate(VaultInfoHandler::Input const& input)
bool const hasOwner = input.owner.has_value();
bool const hasSeq = input.tnxSequence.has_value();
// Only valid combinations: (vaultID) or (owner + ledgerIndex)
// Only valid combinations: (vaultID) or (owner + seq)
// NOLINTNEXTLINE(readability-simplify-boolean-expr)
return (hasVaultId && !hasOwner && !hasSeq) || (!hasVaultId && hasOwner && hasSeq);
}
@@ -68,11 +65,10 @@ VaultInfoHandler::process(VaultInfoHandler::Input const& input, Context const& c
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "VaultInfo's ledger range must be available");
auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq(
auto const expectedLgrInfo = getLedgerHeaderFromLedgerSpecifier(
*sharedPtrBackend_,
ctx.yield,
std::nullopt,
input.ledgerIndex,
input.ledger,
range->maxSequence // NOLINT(bugprone-unchecked-optional-access)
);
@@ -84,12 +80,11 @@ VaultInfoHandler::process(VaultInfoHandler::Input const& input, Context const& c
// Extract the vault keylet based on input
auto const vaultKeylet = [&]() -> std::expected<xrpl::Keylet, Status> {
if (input.owner && input.tnxSequence) {
auto const accountStr = *input.owner;
auto const accountID = accountFromStringStrict(accountStr);
auto const& accountID = *input.owner;
// checks that account exists
{
auto const accountKeylet = xrpl::keylet::account(*accountID);
auto const accountKeylet = xrpl::keylet::account(accountID);
auto const accountLedgerObject =
sharedPtrBackend_->fetchLedgerObject(accountKeylet.key, lgrInfo.seq, ctx.yield);
@@ -97,13 +92,10 @@ VaultInfoHandler::process(VaultInfoHandler::Input const& input, Context const& c
return std::unexpected{Status{RippledError::RpcEntryNotFound}};
}
return xrpl::keylet::vault(*accountID, xrpl::SeqProxy::rawSequence(*input.tnxSequence));
return xrpl::keylet::vault(accountID, xrpl::SeqProxy::rawSequence(*input.tnxSequence));
}
xrpl::uint256 nodeIndex;
if (nodeIndex.parseHex(*input.vaultID))
return xrpl::keylet::vault(nodeIndex);
return std::unexpected{Status{RippledError::RpcEntryNotFound}};
return xrpl::keylet::vault(*input.vaultID);
}();
if (not vaultKeylet.has_value())
@@ -157,28 +149,4 @@ tag_invoke(
};
}
VaultInfoHandler::Input
tag_invoke(boost::json::value_to_tag<VaultInfoHandler::Input>, boost::json::value const& jv)
{
auto input = VaultInfoHandler::Input{};
auto const& jsonObject = jv.as_object();
if (jsonObject.contains(JS(owner)))
input.owner = jsonObject.at(JS(owner)).as_string();
if (jsonObject.contains(JS(seq)))
input.tnxSequence = util::integralValueAs<uint32_t>(jsonObject.at(JS(seq)));
if (jsonObject.contains(JS(vault_id)))
input.vaultID = jsonObject.at(JS(vault_id)).as_string();
if (jsonObject.contains(JS(ledger_index))) {
auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index)));
if (expectedLedgerIndex.has_value())
input.ledgerIndex = *expectedLedgerIndex;
}
return input;
}
} // namespace rpc

View File

@@ -1,30 +1,22 @@
#pragma once
#include "data/BackendInterface.hpp"
#include "rpc/Errors.hpp"
#include "rpc/JS.hpp"
#include "rpc/common/MetaProcessors.hpp"
#include "rpc/common/Specs.hpp"
#include "rpc/common/Types.hpp"
#include "rpc/common/Validators.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
#include <boost/json/value.hpp>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/jss.h>
#include <rpcspec/HandlerFor.hpp>
#include <rpcspec/handlers/vault_info/Types.hpp>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
namespace rpc {
/**
* @brief The vault_info command retrieves information about a vault, currency, shares etc.
*/
class VaultInfoHandler {
class VaultInfoHandler : public rpc::spec::HandlerFor<rpc::spec::handlers::vault_info::Input> {
std::shared_ptr<BackendInterface> sharedPtrBackend_;
public:
@@ -35,16 +27,6 @@ public:
*/
VaultInfoHandler(std::shared_ptr<BackendInterface> sharedPtrBackend);
/**
* @brief A struct to hold the input data for the command
*/
struct Input {
std::optional<std::string> vaultID;
std::optional<std::string> owner;
std::optional<uint32_t> tnxSequence;
std::optional<uint32_t> ledgerIndex;
};
/**
* @brief A struct to hold the output data for the command
*/
@@ -56,36 +38,6 @@ public:
using Result = HandlerReturnType<Output>;
/**
* @brief Returns the API specification for the command
*
* @param apiVersion The api version to return the spec for
* @return The spec for the given apiVersion
*/
static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion)
{
static auto const kRpcSpec = RpcSpec{
{JS(vault_id),
meta::WithCustomError{
validation::CustomValidators::uint256HexStringValidator,
Status(ClioError::RpcMalformedRequest)
}},
{JS(owner),
meta::WithCustomError{
validation::CustomValidators::accountBase58Validator,
Status(ClioError::RpcMalformedRequest, "OwnerNotHexString")
}},
{JS(seq),
meta::WithCustomError{
validation::Type<uint32_t>{}, Status(ClioError::RpcMalformedRequest)
}},
{JS(ledger_index), validation::CustomValidators::ledgerIndexValidator},
};
return kRpcSpec;
}
/**
* @brief Process the VaultInfo command
*
@@ -105,15 +57,6 @@ private:
*/
friend void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output const& output);
/**
* @brief Convert a JSON object to Input type
*
* @param jv The JSON object to convert
* @return Input parsed from the JSON object
*/
friend Input
tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
};
} // namespace rpc