fix: Check ledger range in every handler (#1755)

fixes #1565
This commit is contained in:
Peter Chen
2024-11-29 10:11:07 -05:00
committed by GitHub
parent f62fadc9f9
commit fe4f95dabd
47 changed files with 377 additions and 25 deletions

View File

@@ -111,7 +111,7 @@ ProductionHandlerProvider::ProductionHandlerProvider(
{"transaction_entry", {TransactionEntryHandler{backend}}},
{"tx", {TxHandler{backend, etl}}},
{"subscribe", {SubscribeHandler{backend, subscriptionManager}}},
{"unsubscribe", {UnsubscribeHandler{backend, subscriptionManager}}},
{"unsubscribe", {UnsubscribeHandler{subscriptionManager}}},
{"version", {VersionHandler{config}}},
}
{

View File

@@ -28,6 +28,7 @@
#include "rpc/common/Specs.hpp"
#include "rpc/common/Types.hpp"
#include "rpc/common/Validators.hpp"
#include "util/Assert.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
@@ -94,6 +95,8 @@ AMMInfoHandler::process(AMMInfoHandler::Input input, Context const& ctx) const
return Error{Status{RippledError::rpcINVALID_PARAMS}};
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "AMMInfo's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -23,6 +23,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
@@ -84,6 +85,7 @@ AccountChannelsHandler::Result
AccountChannelsHandler::process(AccountChannelsHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "AccountChannel's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -23,6 +23,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/value.hpp>
@@ -46,6 +47,7 @@ AccountCurrenciesHandler::Result
AccountCurrenciesHandler::process(AccountCurrenciesHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "AccountCurrencies' ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -25,6 +25,7 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/JsonBool.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
@@ -58,6 +59,7 @@ AccountInfoHandler::process(AccountInfoHandler::Input input, Context const& ctx)
return Error{Status{RippledError::rpcINVALID_PARAMS, ripple::RPC::missing_field_message(JS(account))}};
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "AccountInfo's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -23,6 +23,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
@@ -120,6 +121,7 @@ AccountLinesHandler::Result
AccountLinesHandler::process(AccountLinesHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "AccountLines' ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -23,6 +23,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/value.hpp>
@@ -52,6 +53,7 @@ AccountNFTsHandler::Result
AccountNFTsHandler::process(AccountNFTsHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "AccountNFT's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -23,6 +23,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/LedgerUtils.hpp"
#include <boost/json/array.hpp>
@@ -52,6 +53,7 @@ AccountObjectsHandler::Result
AccountObjectsHandler::process(AccountObjectsHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "AccountObject's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -23,6 +23,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/value.hpp>
@@ -68,6 +69,7 @@ AccountOffersHandler::Result
AccountOffersHandler::process(AccountOffersHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "AccountOffer's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -25,6 +25,7 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/JsonBool.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include "util/Profiler.hpp"
#include "util/log/Logger.hpp"
@@ -55,6 +56,8 @@ AccountTxHandler::Result
AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "AccountTX's ledger range must be available");
auto [minIndex, maxIndex] = *range;
if (input.ledgerIndexMin) {

View File

@@ -25,6 +25,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
@@ -45,6 +46,8 @@ BookChangesHandler::Result
BookChangesHandler::process(BookChangesHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "BookChanges' ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -23,6 +23,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
@@ -51,6 +52,8 @@ BookOffersHandler::process(Input input, Context const& ctx) const
// check ledger
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "BookOffer's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -53,6 +53,8 @@ DepositAuthorizedHandler::Result
DepositAuthorizedHandler::process(DepositAuthorizedHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "DepositAuthorized ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -27,6 +27,7 @@
#include "rpc/common/Specs.hpp"
#include "rpc/common/Types.hpp"
#include "rpc/common/Validators.hpp"
#include "util/Assert.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/value.hpp>
@@ -56,6 +57,8 @@ FeatureHandler::process(FeatureHandler::Input input, Context const& ctx) const
namespace rg = std::ranges;
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "Feature's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -23,6 +23,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
@@ -59,6 +60,8 @@ GatewayBalancesHandler::process(GatewayBalancesHandler::Input input, Context con
{
// check ledger
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "GatewayBalances' ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -24,6 +24,7 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/AccountUtils.hpp"
#include "util/Assert.hpp"
#include <boost/asio/spawn.hpp>
#include <boost/bimap/bimap.hpp>
@@ -61,6 +62,8 @@ GetAggregatePriceHandler::Result
GetAggregatePriceHandler::process(GetAggregatePriceHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "GetAggregatePrice's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -24,6 +24,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
@@ -52,6 +53,8 @@ LedgerHandler::Result
LedgerHandler::process(LedgerHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "LedgerHandler's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -24,6 +24,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/LedgerUtils.hpp"
#include "util/log/Logger.hpp"
@@ -61,6 +62,8 @@ LedgerDataHandler::process(Input input, Context const& ctx) const
return Error{Status{RippledError::rpcINVALID_PARAMS, "markerNotString"}};
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "LedgerData's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);
@@ -117,7 +120,7 @@ LedgerDataHandler::process(Input input, Context const& ctx) const
if (page.cursor) {
output.marker = ripple::strHex(*(page.cursor));
} else if (input.outOfOrder) {
output.diffMarker = sharedPtrBackend_->fetchLedgerRange()->maxSequence;
output.diffMarker = range->maxSequence;
}
}

View File

@@ -188,7 +188,7 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx)
// check ledger exists
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "Ledger range must be available");
ASSERT(range.has_value(), "LedgerEntry's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -22,6 +22,7 @@
#include "rpc/Errors.hpp"
#include "rpc/JS.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/TimeUtils.hpp"
#include <boost/json/conversion.hpp>
@@ -42,6 +43,8 @@ LedgerIndexHandler::Result
LedgerIndexHandler::process(LedgerIndexHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "LedgerIndex's ledger range must be available");
auto const [minIndex, maxIndex] = *range;
auto const fillOutputByIndex = [&](std::uint32_t index) {

View File

@@ -23,6 +23,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
@@ -49,6 +50,8 @@ MPTHoldersHandler::Result
MPTHoldersHandler::process(MPTHoldersHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "MPTHolder's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -24,6 +24,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/Profiler.hpp"
#include "util/log/Logger.hpp"
@@ -53,6 +54,8 @@ NFTHistoryHandler::Result
NFTHistoryHandler::process(NFTHistoryHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "NFTHistory's ledger range must be available");
auto [minIndex, maxIndex] = *range;
if (input.ledgerIndexMin) {

View File

@@ -23,6 +23,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
@@ -47,6 +48,8 @@ NFTInfoHandler::process(NFTInfoHandler::Input input, Context const& ctx) const
{
auto const tokenID = ripple::uint256{input.nftID.c_str()};
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "NFTInfo's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -23,6 +23,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/asio/spawn.hpp>
#include <boost/json/conversion.hpp>
@@ -90,6 +91,8 @@ NFTOffersHandlerBase::iterateOfferDirectory(
) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "NFTOffersCommon's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -23,6 +23,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
@@ -48,6 +49,8 @@ NFTsByIssuerHandler::Result
NFTsByIssuerHandler::process(NFTsByIssuerHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "NFTsByIssuer's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -24,6 +24,7 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/JsonBool.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
@@ -58,6 +59,8 @@ NoRippleCheckHandler::Result
NoRippleCheckHandler::process(NoRippleCheckHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "NoRippleCheck's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -26,6 +26,7 @@
#include "rpc/JS.hpp"
#include "rpc/common/Specs.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include "util/build/Build.hpp"
#include <boost/json/conversion.hpp>
@@ -196,6 +197,8 @@ public:
using namespace std::chrono;
auto const range = backend_->fetchLedgerRange();
ASSERT(range.has_value(), "ServerInfo's ledger range must be available");
auto const lgrInfo = backend_->fetchLedgerBySequence(range->maxSequence, ctx.yield);
if (not lgrInfo.has_value())
return Error{Status{RippledError::rpcINTERNAL}};

View File

@@ -31,6 +31,7 @@
#include "rpc/common/Specs.hpp"
#include "rpc/common/Types.hpp"
#include "rpc/common/Validators.hpp"
#include "util/Assert.hpp"
#include <boost/asio/spawn.hpp>
#include <boost/json/array.hpp>
@@ -201,8 +202,10 @@ SubscribeHandler::subscribeToBooks(
for (auto const& internalBook : books) {
if (internalBook.snapshot) {
if (!rng)
if (!rng) {
rng = sharedPtrBackend_->fetchLedgerRange();
ASSERT(rng.has_value(), "Subscribe's ledger range must be available");
}
auto const getOrderBook = [&](auto const& book, auto& snapshots) {
auto const bookBase = getBookBase(book);

View File

@@ -23,6 +23,7 @@
#include "rpc/JS.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Assert.hpp"
#include <boost/json/conversion.hpp>
#include <boost/json/value.hpp>
@@ -43,6 +44,8 @@ TransactionEntryHandler::Result
TransactionEntryHandler::process(TransactionEntryHandler::Input input, Context const& ctx) const
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "TransactionEntry's ledger range must be available");
auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq(
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
);

View File

@@ -29,6 +29,7 @@
#include "rpc/common/Specs.hpp"
#include "rpc/common/Types.hpp"
#include "rpc/common/Validators.hpp"
#include "util/Assert.hpp"
#include "util/JsonUtils.hpp"
#include <boost/asio/spawn.hpp>
@@ -188,6 +189,8 @@ public:
if (rangeSupplied && input.transaction) // ranges not for ctid
{
auto const range = sharedPtrBackend_->fetchLedgerRange();
ASSERT(range.has_value(), "Tx's ledger range must be available");
auto const searchedAll =
range->maxSequence >= *input.maxLedger && range->minSequence <= *input.minLedger;
boost::json::object extra;

View File

@@ -19,7 +19,6 @@
#include "rpc/handlers/Unsubscribe.hpp"
#include "data/BackendInterface.hpp"
#include "feed/SubscriptionManagerInterface.hpp"
#include "feed/Types.hpp"
#include "rpc/Errors.hpp"
@@ -46,11 +45,8 @@
namespace rpc {
UnsubscribeHandler::UnsubscribeHandler(
std::shared_ptr<BackendInterface> const& sharedPtrBackend,
std::shared_ptr<feed::SubscriptionManagerInterface> const& subscriptions
)
: sharedPtrBackend_(sharedPtrBackend), subscriptions_(subscriptions)
UnsubscribeHandler::UnsubscribeHandler(std::shared_ptr<feed::SubscriptionManagerInterface> const& subscriptions)
: subscriptions_(subscriptions)
{
}

View File

@@ -19,7 +19,6 @@
#pragma once
#include "data/BackendInterface.hpp"
#include "feed/SubscriptionManagerInterface.hpp"
#include "feed/Types.hpp"
#include "rpc/common/Specs.hpp"
@@ -49,7 +48,6 @@ namespace rpc {
*/
class UnsubscribeHandler {
std::shared_ptr<BackendInterface> sharedPtrBackend_;
std::shared_ptr<feed::SubscriptionManagerInterface> subscriptions_;
public:
@@ -77,13 +75,9 @@ public:
/**
* @brief Construct a new BaseUnsubscribeHandler object
*
* @param sharedPtrBackend The backend to use
* @param subscriptions The subscription manager to use
*/
UnsubscribeHandler(
std::shared_ptr<BackendInterface> const& sharedPtrBackend,
std::shared_ptr<feed::SubscriptionManagerInterface> const& subscriptions
);
UnsubscribeHandler(std::shared_ptr<feed::SubscriptionManagerInterface> const& subscriptions);
/**
* @brief Returns the API specification for the command

View File

@@ -77,11 +77,14 @@ private:
struct SyncAsioContextTest : virtual public NoLoggerFixture {
template <typename F>
void
runSpawn(F&& f)
runSpawn(F&& f, bool allowMockLeak = false)
{
using namespace boost::asio;
testing::MockFunction<void()> call;
if (allowMockLeak)
testing::Mock::AllowLeak(&call);
spawn(ctx, [&, _ = make_work_guard(ctx)](yield_context yield) {
f(yield);
call.Call();

View File

@@ -67,6 +67,7 @@ target_sources(
rpc/handlers/AccountOffersTests.cpp
rpc/handlers/AccountTxTests.cpp
rpc/handlers/AMMInfoTests.cpp
rpc/handlers/AllHandlerTests.cpp
rpc/handlers/BookChangesTests.cpp
rpc/handlers/BookOffersTests.cpp
rpc/handlers/CredentialHelpersTests.cpp

View File

@@ -197,6 +197,7 @@ TEST_F(LoadBalancerConstructorDeathTest, numMarkersSpecifiedInConfigIsInvalid)
{
uint32_t const numMarkers = 257;
configJson_.as_object()["num_markers"] = numMarkers;
testing::Mock::AllowLeak(&sourceFactory_);
EXPECT_DEATH({ makeLoadBalancer(); }, ".*");
}

View File

@@ -273,6 +273,8 @@ TEST_F(RPCAccountChannelsHandlerTest, AccountNotString)
// error case ledger non exist via hash
TEST_F(RPCAccountChannelsHandlerTest, NonExistLedgerViaLedgerHash)
{
backend->setRange(10, 30);
// mock fetchLedgerByHash return empty
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _))
.WillByDefault(Return(std::optional<ripple::LedgerHeader>{}));

View File

@@ -133,6 +133,8 @@ TEST_P(AccountInfoParameterTest, InvalidParams)
TEST_F(AccountInfoParameterTest, ApiV1SignerListIsNotBool)
{
backend->setRange(10, 30);
static constexpr auto reqJson = R"(
{"ident":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "signer_lists":1}
)";

View File

@@ -319,6 +319,8 @@ TEST_F(RPCAccountLinesHandlerTest, LimitZero)
// error case ledger non exist via hash
TEST_F(RPCAccountLinesHandlerTest, NonExistLedgerViaLedgerHash)
{
backend->setRange(10, 30);
// mock fetchLedgerByHash return empty
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _))
.WillByDefault(Return(std::optional<ripple::LedgerHeader>{}));

View File

@@ -0,0 +1,261 @@
//------------------------------------------------------------------------------
/*
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/common/Types.hpp"
#include "rpc/handlers/AMMInfo.hpp"
#include "rpc/handlers/AccountChannels.hpp"
#include "rpc/handlers/AccountCurrencies.hpp"
#include "rpc/handlers/AccountInfo.hpp"
#include "rpc/handlers/AccountLines.hpp"
#include "rpc/handlers/AccountNFTs.hpp"
#include "rpc/handlers/AccountObjects.hpp"
#include "rpc/handlers/AccountOffers.hpp"
#include "rpc/handlers/AccountTx.hpp"
#include "rpc/handlers/BookChanges.hpp"
#include "rpc/handlers/BookOffers.hpp"
#include "rpc/handlers/DepositAuthorized.hpp"
#include "rpc/handlers/Feature.hpp"
#include "rpc/handlers/GatewayBalances.hpp"
#include "rpc/handlers/GetAggregatePrice.hpp"
#include "rpc/handlers/Ledger.hpp"
#include "rpc/handlers/LedgerData.hpp"
#include "rpc/handlers/LedgerEntry.hpp"
#include "rpc/handlers/LedgerIndex.hpp"
#include "rpc/handlers/MPTHolders.hpp"
#include "rpc/handlers/NFTBuyOffers.hpp"
#include "rpc/handlers/NFTHistory.hpp"
#include "rpc/handlers/NFTInfo.hpp"
#include "rpc/handlers/NFTSellOffers.hpp"
#include "rpc/handlers/NFTsByIssuer.hpp"
#include "rpc/handlers/NoRippleCheck.hpp"
#include "rpc/handlers/ServerInfo.hpp"
#include "rpc/handlers/Subscribe.hpp"
#include "rpc/handlers/TransactionEntry.hpp"
#include "util/Assert.hpp"
#include "util/HandlerBaseTestFixture.hpp"
#include "util/MockAmendmentCenter.hpp"
#include "util/MockCountersFixture.hpp"
#include "util/MockETLServiceTestFixture.hpp"
#include "util/MockSubscriptionManager.hpp"
#include "util/MockWsBase.hpp"
#include "util/TestObject.hpp"
#include "web/SubscriptionContextInterface.hpp"
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/json/parse.hpp>
#include <fmt/core.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Book.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/UintTypes.h>
#include <memory>
#include <string>
#include <vector>
using ::testing::Types;
using namespace rpc;
using TestServerInfoHandler = BaseServerInfoHandler<MockLoadBalancer, MockETLService, MockCounters>;
constexpr static auto Index1 = "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD";
constexpr static auto AmmAccount = "rLcS7XL6nxRAi7JcbJcn1Na179oF3vdfbh";
constexpr static auto Account = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
constexpr static auto NftID = "00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004";
constexpr static auto Currency = "0158415500000000C1F76FF6ECB0BAC600000000";
using AnyHandlerType = Types<
AccountChannelsHandler,
AccountCurrenciesHandler,
AccountInfoHandler,
AccountLinesHandler,
AccountNFTsHandler,
AccountObjectsHandler,
AccountOffersHandler,
AccountTxHandler,
AMMInfoHandler,
BookChangesHandler,
BookOffersHandler,
DepositAuthorizedHandler,
FeatureHandler,
GatewayBalancesHandler,
GetAggregatePriceHandler,
LedgerHandler,
LedgerDataHandler,
LedgerEntryHandler,
LedgerIndexHandler,
MPTHoldersHandler,
NFTsByIssuerHandler,
NFTHistoryHandler,
NFTBuyOffersHandler,
NFTInfoHandler,
NFTSellOffersHandler,
NoRippleCheckHandler,
TestServerInfoHandler,
SubscribeHandler,
TransactionEntryHandler>;
template <typename HandlerType>
struct AllHandlersDeathTest : HandlerBaseTest,
MockLoadBalancerTest,
MockCountersTest,
testing::WithParamInterface<std::string> {
AllHandlersDeathTest() : handler_{initHandler()}
{
ASSERT(mockAmendmentCenterPtr.amendmentCenterMock != nullptr, "mockAmendmentCenterPtr is not initialized.");
ASSERT(mockSubscriptionManagerPtr.subscriptionManagerMock != nullptr, "mockSubscriptionPtr is not initialized");
}
web::SubscriptionContextPtr session_ = std::make_shared<MockSession>();
MockSession* mockSession_ = dynamic_cast<MockSession*>(session_.get());
StrictMockSubscriptionManagerSharedPtr mockSubscriptionManagerPtr;
StrictMockAmendmentCenterSharedPtr mockAmendmentCenterPtr;
HandlerType handler_;
private:
HandlerType
initHandler()
{
if constexpr (std::is_same_v<HandlerType, AccountInfoHandler> || std::is_same_v<HandlerType, FeatureHandler>) {
return HandlerType{this->backend, this->mockAmendmentCenterPtr};
} else if constexpr (std::is_same_v<HandlerType, SubscribeHandler>) {
return HandlerType{this->backend, this->mockSubscriptionManagerPtr};
} else if constexpr (std::is_same_v<HandlerType, TestServerInfoHandler>) {
return HandlerType{
this->backend,
this->mockSubscriptionManagerPtr,
mockLoadBalancerPtr,
mockETLServicePtr,
*mockCountersPtr
};
} else {
return HandlerType{this->backend};
}
}
};
template <typename Handler>
Handler::Input
createInput()
{
return typename Handler::Input{};
}
// need to set specific values for input for some handler's to pass checks in .process() function
template <>
AccountInfoHandler::Input
createInput<AccountInfoHandler>()
{
AccountInfoHandler::Input input{};
input.account = Account;
input.ident = "asdf";
return input;
}
template <>
AMMInfoHandler::Input
createInput<AMMInfoHandler>()
{
AMMInfoHandler::Input input{};
input.ammAccount = GetAccountIDWithString(AmmAccount);
return input;
}
template <>
BookOffersHandler::Input
createInput<BookOffersHandler>()
{
BookOffersHandler::Input input{};
input.paysCurrency = ripple::xrpCurrency();
input.getsCurrency = ripple::Currency(Currency);
input.paysID = ripple::xrpAccount();
input.getsID = GetAccountIDWithString(Account);
return input;
}
template <>
LedgerEntryHandler::Input
createInput<LedgerEntryHandler>()
{
LedgerEntryHandler::Input input{};
input.index = Index1;
return input;
}
template <>
NFTBuyOffersHandler::Input
createInput<NFTBuyOffersHandler>()
{
NFTBuyOffersHandler::Input input{};
input.nftID = NftID;
return input;
}
template <>
NFTInfoHandler::Input
createInput<NFTInfoHandler>()
{
NFTInfoHandler::Input input{};
input.nftID = NftID;
return input;
}
template <>
NFTSellOffersHandler::Input
createInput<NFTSellOffersHandler>()
{
NFTSellOffersHandler::Input input{};
input.nftID = NftID;
return input;
}
template <>
SubscribeHandler::Input
createInput<SubscribeHandler>()
{
SubscribeHandler::Input input{};
input.books =
std::vector<SubscribeHandler::OrderBook>{SubscribeHandler::OrderBook{ripple::Book{}, Account, true, true}};
return input;
}
TYPED_TEST_CASE(AllHandlersDeathTest, AnyHandlerType);
TYPED_TEST(AllHandlersDeathTest, NoRangeAvailable)
{
// doesn't work without 'this'
this->runSpawn(
[&](boost::asio::yield_context yield) {
TypeParam handler = this->handler_;
auto const input = createInput<TypeParam>();
auto const context = Context{yield, this->session_};
EXPECT_DEATH(
{ [[maybe_unused]] auto _unused = handler.process(input, context); }, "Assertion .* failed at .*"
);
},
true
);
}

View File

@@ -223,6 +223,8 @@ TEST_F(RPCMPTHoldersHandlerTest, MarkerNotString)
// error case ledger non exist via hash
TEST_F(RPCMPTHoldersHandlerTest, NonExistLedgerViaLedgerHash)
{
backend->setRange(10, 30);
// mock fetchLedgerByHash return empty
EXPECT_CALL(*backend, fetchLedgerByHash).Times(1);
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _))

View File

@@ -210,6 +210,8 @@ TEST_F(RPCNFTBuyOffersHandlerTest, NFTIDNotString)
// error case ledger non exist via hash
TEST_F(RPCNFTBuyOffersHandlerTest, NonExistLedgerViaLedgerHash)
{
backend->setRange(10, 30);
// mock fetchLedgerByHash return empty
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _))
.WillByDefault(Return(std::optional<ripple::LedgerHeader>{}));

View File

@@ -148,6 +148,8 @@ TEST_F(RPCNFTInfoHandlerTest, NFTIDNotString)
// error case ledger non exist via hash
TEST_F(RPCNFTInfoHandlerTest, NonExistLedgerViaLedgerHash)
{
backend->setRange(10, 30);
// mock fetchLedgerByHash return empty
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _))
.WillByDefault(Return(std::optional<ripple::LedgerHeader>{}));

View File

@@ -210,6 +210,8 @@ TEST_F(RPCNFTSellOffersHandlerTest, NFTIDNotString)
// error case ledger non exist via hash
TEST_F(RPCNFTSellOffersHandlerTest, NonExistLedgerViaLedgerHash)
{
backend->setRange(10, 30);
// mock fetchLedgerByHash return empty
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _))
.WillByDefault(Return(std::optional<ripple::LedgerHeader>{}));

View File

@@ -206,6 +206,8 @@ TEST_F(RPCNFTsByIssuerHandlerTest, NFTIssuerNotString)
// error case ledger non exist via hash
TEST_F(RPCNFTsByIssuerHandlerTest, NonExistLedgerViaLedgerHash)
{
backend->setRange(10, 30);
// mock fetchLedgerByHash return empty
EXPECT_CALL(*backend, fetchLedgerByHash).Times(1);
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _))

View File

@@ -181,6 +181,8 @@ TEST_P(NoRippleCheckParameterTest, InvalidParams)
TEST_F(NoRippleCheckParameterTest, V1ApiTransactionsIsNotBool)
{
backend->setRange(10, 30);
static constexpr auto reqJson = R"(
{
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",

View File

@@ -77,6 +77,8 @@ TEST_F(RPCTransactionEntryHandlerTest, TxHashWrongFormat)
TEST_F(RPCTransactionEntryHandlerTest, NonExistLedgerViaLedgerHash)
{
backend->setRange(10, 30);
// mock fetchLedgerByHash return empty
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{INDEX}, _))
.WillByDefault(Return(std::optional<ripple::LedgerHeader>{}));

View File

@@ -496,7 +496,7 @@ TEST_P(UnsubscribeParameterTest, InvalidParams)
{
auto const testBundle = GetParam();
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{UnsubscribeHandler{backend, mockSubscriptionManagerPtr}};
auto const handler = AnyHandler{UnsubscribeHandler{mockSubscriptionManagerPtr}};
auto const req = json::parse(testBundle.testJson);
auto const output = handler.process(req, Context{yield});
ASSERT_FALSE(output);
@@ -509,7 +509,7 @@ TEST_P(UnsubscribeParameterTest, InvalidParams)
TEST_F(RPCUnsubscribeTest, EmptyResponse)
{
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{UnsubscribeHandler{backend, mockSubscriptionManagerPtr}};
auto const handler = AnyHandler{UnsubscribeHandler{mockSubscriptionManagerPtr}};
auto const output = handler.process(json::parse(R"({})"), Context{yield, session_});
ASSERT_TRUE(output);
EXPECT_TRUE(output.result->as_object().empty());
@@ -532,7 +532,7 @@ TEST_F(RPCUnsubscribeTest, Streams)
EXPECT_CALL(*mockSubscriptionManagerPtr, unsubProposedTransactions).Times(1);
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{UnsubscribeHandler{backend, mockSubscriptionManagerPtr}};
auto const handler = AnyHandler{UnsubscribeHandler{mockSubscriptionManagerPtr}};
auto const output = handler.process(input, Context{yield, session_});
ASSERT_TRUE(output);
EXPECT_TRUE(output.result->as_object().empty());
@@ -553,7 +553,7 @@ TEST_F(RPCUnsubscribeTest, Accounts)
EXPECT_CALL(*mockSubscriptionManagerPtr, unsubAccount(rpc::accountFromStringStrict(ACCOUNT2).value(), _)).Times(1);
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{UnsubscribeHandler{backend, mockSubscriptionManagerPtr}};
auto const handler = AnyHandler{UnsubscribeHandler{mockSubscriptionManagerPtr}};
auto const output = handler.process(input, Context{yield, session_});
ASSERT_TRUE(output);
EXPECT_TRUE(output.result->as_object().empty());
@@ -576,7 +576,7 @@ TEST_F(RPCUnsubscribeTest, AccountsProposed)
.Times(1);
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{UnsubscribeHandler{backend, mockSubscriptionManagerPtr}};
auto const handler = AnyHandler{UnsubscribeHandler{mockSubscriptionManagerPtr}};
auto const output = handler.process(input, Context{yield, session_});
ASSERT_TRUE(output);
EXPECT_TRUE(output.result->as_object().empty());
@@ -610,7 +610,7 @@ TEST_F(RPCUnsubscribeTest, Books)
EXPECT_CALL(*mockSubscriptionManagerPtr, unsubBook(ripple::reversed(book), _)).Times(1);
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{UnsubscribeHandler{backend, mockSubscriptionManagerPtr}};
auto const handler = AnyHandler{UnsubscribeHandler{mockSubscriptionManagerPtr}};
auto const output = handler.process(input, Context{yield, session_});
ASSERT_TRUE(output);
EXPECT_TRUE(output.result->as_object().empty());
@@ -642,7 +642,7 @@ TEST_F(RPCUnsubscribeTest, SingleBooks)
EXPECT_CALL(*mockSubscriptionManagerPtr, unsubBook(book, _)).Times(1);
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{UnsubscribeHandler{backend, mockSubscriptionManagerPtr}};
auto const handler = AnyHandler{UnsubscribeHandler{mockSubscriptionManagerPtr}};
auto const output = handler.process(input, Context{yield, session_});
ASSERT_TRUE(output);
EXPECT_TRUE(output.result->as_object().empty());