mirror of
https://github.com/XRPLF/clio.git
synced 2026-10-04 02:48:04 +00:00
refactor: Migrate handlers to rpc-spec (D) (#3209)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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:
|
||||
/**
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user