mirror of
https://github.com/XRPLF/clio.git
synced 2025-12-06 17:27:58 +00:00
@@ -31,6 +31,7 @@ target_sources(
|
||||
handlers/Ledger.cpp
|
||||
handlers/LedgerData.cpp
|
||||
handlers/LedgerEntry.cpp
|
||||
handlers/LedgerIndex.cpp
|
||||
handlers/LedgerRange.cpp
|
||||
handlers/NFTsByIssuer.cpp
|
||||
handlers/NFTBuyOffers.cpp
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -51,6 +54,26 @@ Required::verify(boost::json::value const& value, std::string_view key)
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] MaybeError
|
||||
TimeFormatValidator::verify(boost::json::value const& value, std::string_view key) const
|
||||
{
|
||||
using boost::json::value_to;
|
||||
|
||||
if (not value.is_object() or not value.as_object().contains(key))
|
||||
return {}; // ignore. field does not exist, let 'required' fail instead
|
||||
|
||||
if (not value.as_object().at(key).is_string())
|
||||
return Error{Status{RippledError::rpcINVALID_PARAMS}};
|
||||
|
||||
std::tm time = {};
|
||||
std::stringstream stream(value_to<std::string>(value.as_object().at(key)));
|
||||
stream >> std::get_time(&time, format_.c_str());
|
||||
if (stream.fail())
|
||||
return Error{Status{RippledError::rpcINVALID_PARAMS}};
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] MaybeError
|
||||
CustomValidator::verify(boost::json::value const& value, std::string_view key) const
|
||||
{
|
||||
|
||||
@@ -30,8 +30,11 @@
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
@@ -288,6 +291,33 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Validate that value can be converted to time according to the given format.
|
||||
*/
|
||||
class TimeFormatValidator final {
|
||||
std::string format_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct the validator storing format value.
|
||||
*
|
||||
* @param format The format to use for time conversion
|
||||
*/
|
||||
explicit TimeFormatValidator(std::string format) : format_{std::move(format)}
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Verify that the JSON value is valid formatted time.
|
||||
*
|
||||
* @param value The JSON value representing the outer object
|
||||
* @param key The key used to retrieve the tested value from the outer object
|
||||
* @return `RippledError::rpcINVALID_PARAMS` if validation failed; otherwise no error is returned
|
||||
*/
|
||||
[[nodiscard]] MaybeError
|
||||
verify(boost::json::value const& value, std::string_view key) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Validates that the value is equal to the one passed in.
|
||||
*/
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "rpc/handlers/Ledger.hpp"
|
||||
#include "rpc/handlers/LedgerData.hpp"
|
||||
#include "rpc/handlers/LedgerEntry.hpp"
|
||||
#include "rpc/handlers/LedgerIndex.hpp"
|
||||
#include "rpc/handlers/LedgerRange.hpp"
|
||||
#include "rpc/handlers/NFTBuyOffers.hpp"
|
||||
#include "rpc/handlers/NFTHistory.hpp"
|
||||
@@ -94,6 +95,7 @@ ProductionHandlerProvider::ProductionHandlerProvider(
|
||||
{"ledger", {LedgerHandler{backend}}},
|
||||
{"ledger_data", {LedgerDataHandler{backend}}},
|
||||
{"ledger_entry", {LedgerEntryHandler{backend}}},
|
||||
{"ledger_index", {LedgerIndexHandler{backend}, true}}, // clio only
|
||||
{"ledger_range", {LedgerRangeHandler{backend}}},
|
||||
{"nfts_by_issuer", {NFTsByIssuerHandler{backend}, true}}, // clio only
|
||||
{"nft_history", {NFTHistoryHandler{backend}, true}}, // clio only
|
||||
|
||||
119
src/rpc/handlers/LedgerIndex.cpp
Normal file
119
src/rpc/handlers/LedgerIndex.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "rpc/handlers/LedgerIndex.hpp"
|
||||
|
||||
#include "rpc/JS.hpp"
|
||||
#include "rpc/common/Types.hpp"
|
||||
#include "util/Assert.hpp"
|
||||
|
||||
#include <boost/json/conversion.hpp>
|
||||
#include <boost/json/object.hpp>
|
||||
#include <boost/json/value.hpp>
|
||||
#include <fmt/core.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <numeric>
|
||||
#include <ranges>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace rpc {
|
||||
|
||||
LedgerIndexHandler::Result
|
||||
LedgerIndexHandler::process(LedgerIndexHandler::Input input, Context const& ctx) const
|
||||
{
|
||||
auto const range = sharedPtrBackend_->fetchLedgerRange();
|
||||
auto const [minIndex, maxIndex] = *range;
|
||||
|
||||
auto const fillOutputByIndex = [&](std::uint32_t index) {
|
||||
auto const ledger = sharedPtrBackend_->fetchLedgerBySequence(index, ctx.yield);
|
||||
return Output{
|
||||
.ledgerIndex = index,
|
||||
.ledgerHash = ripple::strHex(ledger->hash),
|
||||
.closeTimeIso = ripple::to_string_iso(ledger->closeTime)
|
||||
};
|
||||
};
|
||||
|
||||
// if no date is provided, return the latest ledger
|
||||
if (!input.date)
|
||||
return fillOutputByIndex(maxIndex);
|
||||
|
||||
auto const convertISOTimeStrToTicks = [](std::string const& isoTimeStr) {
|
||||
std::tm time = {};
|
||||
std::stringstream ss(isoTimeStr);
|
||||
ss >> std::get_time(&time, DATE_FORMAT);
|
||||
return std::chrono::system_clock::from_time_t(std::mktime(&time)).time_since_epoch().count();
|
||||
};
|
||||
|
||||
auto const ticks = convertISOTimeStrToTicks(*input.date);
|
||||
|
||||
auto const earlierThan = [&](std::uint32_t ledgerIndex) {
|
||||
auto const header = sharedPtrBackend_->fetchLedgerBySequence(ledgerIndex, ctx.yield);
|
||||
auto const ledgerTime =
|
||||
std::chrono::system_clock::time_point{header->closeTime.time_since_epoch() + ripple::epoch_offset};
|
||||
return ticks < ledgerTime.time_since_epoch().count();
|
||||
};
|
||||
|
||||
// If the given date is earlier than the first valid ledger, return lgrNotFound
|
||||
if (earlierThan(minIndex))
|
||||
return Error{Status{RippledError::rpcLGR_NOT_FOUND, "ledgerNotInRange"}};
|
||||
|
||||
auto const view = std::ranges::iota_view{minIndex, maxIndex + 1};
|
||||
|
||||
auto const greaterEqLedgerIter = std::ranges::lower_bound(
|
||||
view, ticks, [&](std::uint32_t ledgerIndex, std::int64_t) { return not earlierThan(ledgerIndex); }
|
||||
);
|
||||
|
||||
if (greaterEqLedgerIter != view.end())
|
||||
return fillOutputByIndex(std::max(static_cast<std::uint32_t>(*greaterEqLedgerIter) - 1, minIndex));
|
||||
|
||||
return fillOutputByIndex(maxIndex);
|
||||
}
|
||||
|
||||
LedgerIndexHandler::Input
|
||||
tag_invoke(boost::json::value_to_tag<LedgerIndexHandler::Input>, boost::json::value const& jv)
|
||||
{
|
||||
auto input = LedgerIndexHandler::Input{};
|
||||
|
||||
if (jv.as_object().contains(JS(date)))
|
||||
input.date = jv.at(JS(date)).as_string();
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
void
|
||||
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, LedgerIndexHandler::Output const& output)
|
||||
{
|
||||
jv = boost::json::object{
|
||||
{JS(ledger_index), output.ledgerIndex},
|
||||
{JS(ledger_hash), output.ledgerHash},
|
||||
{JS(close_time_iso), output.closeTimeIso},
|
||||
{JS(validated), true},
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace rpc
|
||||
119
src/rpc/handlers/LedgerIndex.hpp
Normal file
119
src/rpc/handlers/LedgerIndex.hpp
Normal file
@@ -0,0 +1,119 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
#include "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/conversion.hpp>
|
||||
#include <boost/json/value.hpp>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace rpc {
|
||||
|
||||
/**
|
||||
* @brief The ledger_index method fetches the lastest closed ledger before the given date.
|
||||
*
|
||||
*/
|
||||
class LedgerIndexHandler {
|
||||
std::shared_ptr<BackendInterface> sharedPtrBackend_;
|
||||
static constexpr auto DATE_FORMAT = "%Y-%m-%dT%TZ";
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief A struct to hold the output data of the command
|
||||
*/
|
||||
struct Output {
|
||||
uint32_t ledgerIndex{};
|
||||
std::string ledgerHash;
|
||||
std::string closeTimeIso;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A struct to hold the input data for the command
|
||||
*/
|
||||
struct Input {
|
||||
std::optional<std::string> date;
|
||||
};
|
||||
|
||||
using Result = HandlerReturnType<Output>;
|
||||
|
||||
/**
|
||||
* @brief Construct a new LedgerIndexHandler object
|
||||
*
|
||||
* @param sharedPtrBackend The backend to use
|
||||
*/
|
||||
LedgerIndexHandler(std::shared_ptr<BackendInterface> const& sharedPtrBackend) : sharedPtrBackend_(sharedPtrBackend)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 rpcSpec = RpcSpec{
|
||||
{JS(date), validation::Type<std::string>{}, validation::TimeFormatValidator{DATE_FORMAT}},
|
||||
};
|
||||
return rpcSpec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process the LedgerIndex command
|
||||
*
|
||||
* @param input The input data for the command
|
||||
* @param ctx The context of the request
|
||||
* @return The result of the operation
|
||||
*/
|
||||
Result
|
||||
process(Input input, Context const& ctx) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Convert the Output to a JSON object
|
||||
*
|
||||
* @param [out] jv The JSON object to convert to
|
||||
* @param output The output to convert
|
||||
*/
|
||||
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