mirror of
				https://github.com/XRPLF/clio.git
				synced 2025-11-04 11:55:51 +00:00 
			
		
		
		
	@@ -73,21 +73,21 @@ target_sources(clio PRIVATE
 | 
			
		||||
  src/rpc/ngHandlers/AccountCurrencies.cpp
 | 
			
		||||
  src/rpc/ngHandlers/AccountLines.cpp
 | 
			
		||||
  src/rpc/ngHandlers/AccountTx.cpp
 | 
			
		||||
  src/rpc/ngHandlers/AccountTx.cpp
 | 
			
		||||
  src/rpc/ngHandlers/AccountOffers.cpp
 | 
			
		||||
  src/rpc/ngHandlers/AccountInfo.cpp
 | 
			
		||||
  src/rpc/ngHandlers/Tx.cpp
 | 
			
		||||
  src/rpc/ngHandlers/BookOffers.cpp
 | 
			
		||||
  src/rpc/ngHandlers/GatewayBalances.cpp
 | 
			
		||||
  src/rpc/ngHandlers/LedgerEntry.cpp
 | 
			
		||||
  src/rpc/ngHandlers/LedgerRange.cpp
 | 
			
		||||
  src/rpc/ngHandlers/BookOffers.cpp
 | 
			
		||||
  src/rpc/ngHandlers/TransactionEntry.cpp
 | 
			
		||||
  src/rpc/ngHandlers/Tx.cpp
 | 
			
		||||
  src/rpc/ngHandlers/Random.cpp
 | 
			
		||||
  src/rpc/ngHandlers/NoRippleCheck.cpp
 | 
			
		||||
  src/rpc/ngHandlers/NFTInfo.cpp
 | 
			
		||||
  src/rpc/ngHandlers/NFTOffersCommon.cpp
 | 
			
		||||
  src/rpc/ngHandlers/NFTBuyOffers.cpp
 | 
			
		||||
  src/rpc/ngHandlers/NFTSellOffers.cpp
 | 
			
		||||
  src/rpc/ngHandlers/Random.cpp
 | 
			
		||||
  src/rpc/ngHandlers/NFTHistory.cpp
 | 
			
		||||
  ## RPC Methods
 | 
			
		||||
  # Account
 | 
			
		||||
  src/rpc/handlers/AccountChannels.cpp
 | 
			
		||||
@@ -147,28 +147,27 @@ if(BUILD_TESTS)
 | 
			
		||||
    unittests/rpc/BaseTests.cpp
 | 
			
		||||
    unittests/rpc/RPCHelpersTest.cpp
 | 
			
		||||
    ## RPC handlers
 | 
			
		||||
    unittests/rpc/handlers/DefaultProcessorTests.cpp
 | 
			
		||||
    unittests/rpc/handlers/TestHandlerTests.cpp
 | 
			
		||||
    unittests/rpc/handlers/AccountCurrenciesTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/AccountLinesTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/AccountTxTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/AccountTxTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/AccountOffersTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/AccountInfoTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/DefaultProcessorTests.cpp
 | 
			
		||||
    unittests/rpc/handlers/PingTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/AccountChannelsTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/BookOffersTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/GatewayBalancesTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/TxTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/TransactionEntryTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/GatewayBalancesTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/LedgerEntryTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/LedgerRangeTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/BookOffersTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/NoRippleCheckTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/NFTInfoTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/PingTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/RandomTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/NFTInfoTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/NFTBuyOffersTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/NFTSellOffersTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/RandomTest.cpp
 | 
			
		||||
    unittests/rpc/handlers/NFTHistoryTest.cpp
 | 
			
		||||
    # Backend
 | 
			
		||||
    unittests/backend/cassandra/BaseTests.cpp
 | 
			
		||||
    unittests/backend/cassandra/BackendTests.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,6 @@
 | 
			
		||||
#include <rpc/common/Types.h>
 | 
			
		||||
#include <rpc/common/Validators.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
@@ -106,22 +104,20 @@ private:
 | 
			
		||||
    void
 | 
			
		||||
    addChannel(std::vector<ChannelResponse>& jsonLines, ripple::SLE const& line)
 | 
			
		||||
        const;
 | 
			
		||||
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Output const& output);
 | 
			
		||||
 | 
			
		||||
    friend Input
 | 
			
		||||
    tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
 | 
			
		||||
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        ChannelResponse const& channel);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
AccountChannelsHandler::Input
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_to_tag<AccountChannelsHandler::Input>,
 | 
			
		||||
    boost::json::value const& jv);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    AccountChannelsHandler::Output const& output);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    AccountChannelsHandler::ChannelResponse const& channel);
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,6 @@
 | 
			
		||||
#include <rpc/common/Types.h>
 | 
			
		||||
#include <rpc/common/Validators.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
#include <set>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
@@ -73,16 +71,15 @@ public:
 | 
			
		||||
 | 
			
		||||
    Result
 | 
			
		||||
    process(Input input, Context const& ctx) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Output const& output);
 | 
			
		||||
 | 
			
		||||
    friend Input
 | 
			
		||||
    tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    AccountCurrenciesHandler::Output const& output);
 | 
			
		||||
 | 
			
		||||
AccountCurrenciesHandler::Input
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_to_tag<AccountCurrenciesHandler::Input>,
 | 
			
		||||
    boost::json::value const& jv);
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,6 @@
 | 
			
		||||
#include <rpc/common/Types.h>
 | 
			
		||||
#include <rpc/common/Validators.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
class AccountInfoHandler
 | 
			
		||||
{
 | 
			
		||||
@@ -97,16 +95,15 @@ public:
 | 
			
		||||
 | 
			
		||||
    Result
 | 
			
		||||
    process(Input input, Context const& ctx) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Output const& output);
 | 
			
		||||
 | 
			
		||||
    friend Input
 | 
			
		||||
    tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    AccountInfoHandler::Output const& output);
 | 
			
		||||
 | 
			
		||||
AccountInfoHandler::Input
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_to_tag<AccountInfoHandler::Input>,
 | 
			
		||||
    boost::json::value const& jv);
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,6 @@
 | 
			
		||||
#include <rpc/common/Types.h>
 | 
			
		||||
#include <rpc/common/Validators.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
@@ -113,23 +111,21 @@ private:
 | 
			
		||||
        ripple::SLE const& lineSle,
 | 
			
		||||
        ripple::AccountID const& account,
 | 
			
		||||
        std::optional<ripple::AccountID> const& peerAccount) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Output const& output);
 | 
			
		||||
 | 
			
		||||
    friend Input
 | 
			
		||||
    tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
 | 
			
		||||
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        LineResponse const& line);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
AccountLinesHandler::Input
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_to_tag<AccountLinesHandler::Input>,
 | 
			
		||||
    boost::json::value const& jv);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    AccountLinesHandler::Output const& output);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    AccountLinesHandler::LineResponse const& line);
 | 
			
		||||
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,6 @@
 | 
			
		||||
#include <rpc/common/Types.h>
 | 
			
		||||
#include <rpc/common/Validators.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
class AccountOffersHandler
 | 
			
		||||
{
 | 
			
		||||
@@ -91,22 +89,20 @@ public:
 | 
			
		||||
private:
 | 
			
		||||
    void
 | 
			
		||||
    addOffer(std::vector<Offer>& offers, ripple::SLE const& offerSle) const;
 | 
			
		||||
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Output const& output);
 | 
			
		||||
 | 
			
		||||
    friend Input
 | 
			
		||||
    tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
 | 
			
		||||
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Offer const& offer);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    AccountOffersHandler::Output const& output);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    AccountOffersHandler::Offer const& offer);
 | 
			
		||||
 | 
			
		||||
AccountOffersHandler::Input
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_to_tag<AccountOffersHandler::Input>,
 | 
			
		||||
    boost::json::value const& jv);
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
 
 | 
			
		||||
@@ -18,12 +18,12 @@
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <rpc/ngHandlers/AccountTx.h>
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
clio::Logger gLog{"RPC-AccountTxHandler"};
 | 
			
		||||
}
 | 
			
		||||
#include <util/Profiler.h>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
 | 
			
		||||
// TODO: this is currently very similar to nft_history but its own copy for time
 | 
			
		||||
// being. we should aim to reuse common logic in some way in the future.
 | 
			
		||||
AccountTxHandler::Result
 | 
			
		||||
AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx)
 | 
			
		||||
    const
 | 
			
		||||
@@ -76,7 +76,9 @@ AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx)
 | 
			
		||||
 | 
			
		||||
        maxIndex = minIndex = std::get<ripple::LedgerInfo>(lgrInfoOrStatus).seq;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::optional<Backend::TransactionsCursor> cursor;
 | 
			
		||||
 | 
			
		||||
    // if marker exists
 | 
			
		||||
    if (input.marker)
 | 
			
		||||
    {
 | 
			
		||||
@@ -89,11 +91,17 @@ AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx)
 | 
			
		||||
        else
 | 
			
		||||
            cursor = {maxIndex, INT32_MAX};
 | 
			
		||||
    }
 | 
			
		||||
    auto constexpr limitDefault = 50;
 | 
			
		||||
 | 
			
		||||
    static auto constexpr limitDefault = 50;
 | 
			
		||||
    auto const limit = input.limit.value_or(limitDefault);
 | 
			
		||||
    auto const accountID = RPC::accountFromStringStrict(input.account);
 | 
			
		||||
    auto const [blobs, retCursor] = sharedPtrBackend_->fetchAccountTransactions(
 | 
			
		||||
        *accountID, limit, input.forward, cursor, ctx.yield);
 | 
			
		||||
    auto const [txnsAndCursor, timeDiff] = util::timed([&]() {
 | 
			
		||||
        return sharedPtrBackend_->fetchAccountTransactions(
 | 
			
		||||
            *accountID, limit, input.forward, cursor, ctx.yield);
 | 
			
		||||
    });
 | 
			
		||||
    log_.info() << "db fetch took " << timeDiff
 | 
			
		||||
                << " milliseconds - num blobs = " << txnsAndCursor.txns.size();
 | 
			
		||||
    auto const [blobs, retCursor] = txnsAndCursor;
 | 
			
		||||
 | 
			
		||||
    Output response;
 | 
			
		||||
    if (retCursor)
 | 
			
		||||
@@ -111,7 +119,7 @@ AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx)
 | 
			
		||||
        }
 | 
			
		||||
        else if (txnPlusMeta.ledgerSequence > maxIndex && !input.forward)
 | 
			
		||||
        {
 | 
			
		||||
            gLog.debug() << "Skipping over transactions from incomplete ledger";
 | 
			
		||||
            log_.debug() << "Skipping over transactions from incomplete ledger";
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -134,13 +142,15 @@ AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx)
 | 
			
		||||
            obj[JS(date)] = txnPlusMeta.date;
 | 
			
		||||
        }
 | 
			
		||||
        obj[JS(validated)] = true;
 | 
			
		||||
 | 
			
		||||
        response.transactions.push_back(obj);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    response.limit = input.limit;
 | 
			
		||||
    response.account = input.account;
 | 
			
		||||
    response.account = ripple::to_string(*accountID);
 | 
			
		||||
    response.ledgerIndexMin = minIndex;
 | 
			
		||||
    response.ledgerIndexMax = maxIndex;
 | 
			
		||||
 | 
			
		||||
    return response;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,15 +20,15 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <backend/BackendInterface.h>
 | 
			
		||||
#include <log/Logger.h>
 | 
			
		||||
#include <rpc/RPCHelpers.h>
 | 
			
		||||
#include <rpc/common/Types.h>
 | 
			
		||||
#include <rpc/common/Validators.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
class AccountTxHandler
 | 
			
		||||
{
 | 
			
		||||
    clio::Logger log_{"RPC"};
 | 
			
		||||
    std::shared_ptr<BackendInterface> sharedPtrBackend_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
@@ -106,22 +106,21 @@ public:
 | 
			
		||||
 | 
			
		||||
    Result
 | 
			
		||||
    process(Input input, Context const& ctx) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Output const& output);
 | 
			
		||||
 | 
			
		||||
    friend Input
 | 
			
		||||
    tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
 | 
			
		||||
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Marker const& marker);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    AccountTxHandler::Output const& output);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    AccountTxHandler::Marker const& marker);
 | 
			
		||||
 | 
			
		||||
AccountTxHandler::Input
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_to_tag<AccountTxHandler::Input>,
 | 
			
		||||
    boost::json::value const& jv);
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
 
 | 
			
		||||
@@ -23,8 +23,6 @@
 | 
			
		||||
#include <rpc/common/Types.h>
 | 
			
		||||
#include <rpc/common/Validators.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
class BookOffersHandler
 | 
			
		||||
{
 | 
			
		||||
@@ -103,16 +101,15 @@ public:
 | 
			
		||||
 | 
			
		||||
    Result
 | 
			
		||||
    process(Input input, Context const& ctx) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Output const& output);
 | 
			
		||||
 | 
			
		||||
    friend Input
 | 
			
		||||
    tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    BookOffersHandler::Output const& output);
 | 
			
		||||
 | 
			
		||||
BookOffersHandler::Input
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_to_tag<BookOffersHandler::Input>,
 | 
			
		||||
    boost::json::value const& jv);
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,6 @@
 | 
			
		||||
#include <rpc/common/Types.h>
 | 
			
		||||
#include <rpc/common/Validators.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
class GatewayBalancesHandler
 | 
			
		||||
{
 | 
			
		||||
@@ -114,16 +112,15 @@ public:
 | 
			
		||||
 | 
			
		||||
    Result
 | 
			
		||||
    process(Input input, Context const& ctx) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Output const& output);
 | 
			
		||||
 | 
			
		||||
    friend Input
 | 
			
		||||
    tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    GatewayBalancesHandler::Output const& output);
 | 
			
		||||
 | 
			
		||||
GatewayBalancesHandler::Input
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_to_tag<GatewayBalancesHandler::Input>,
 | 
			
		||||
    boost::json::value const& jv);
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,6 @@
 | 
			
		||||
#include <rpc/common/Types.h>
 | 
			
		||||
#include <rpc/common/Validators.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
 | 
			
		||||
class LedgerEntryHandler
 | 
			
		||||
@@ -197,16 +195,14 @@ private:
 | 
			
		||||
    std::variant<ripple::uint256, RPC::Status>
 | 
			
		||||
    composeKeyFromDirectory(
 | 
			
		||||
        boost::json::object const& directory) const noexcept;
 | 
			
		||||
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Output const& output);
 | 
			
		||||
 | 
			
		||||
    friend Input
 | 
			
		||||
    tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    LedgerEntryHandler::Output const& output);
 | 
			
		||||
 | 
			
		||||
LedgerEntryHandler::Input
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_to_tag<LedgerEntryHandler::Input>,
 | 
			
		||||
    boost::json::value const& jv);
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
 
 | 
			
		||||
@@ -48,12 +48,12 @@ public:
 | 
			
		||||
 | 
			
		||||
    Result
 | 
			
		||||
    process() const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Output const& output);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    LedgerRangeHandler::Output const& output);
 | 
			
		||||
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,6 @@
 | 
			
		||||
#include <backend/BackendInterface.h>
 | 
			
		||||
#include <rpc/ngHandlers/NFTOffersCommon.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
class NFTBuyOffersHandler : public NFTOffersHandlerBase
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										239
									
								
								src/rpc/ngHandlers/NFTHistory.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								src/rpc/ngHandlers/NFTHistory.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,239 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2023, 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/ngHandlers/NFTHistory.h>
 | 
			
		||||
#include <util/Profiler.h>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
 | 
			
		||||
// TODO: this is currently very similar to account_tx but its own copy for time
 | 
			
		||||
// being. we should aim to reuse common logic in some way in the future.
 | 
			
		||||
NFTHistoryHandler::Result
 | 
			
		||||
NFTHistoryHandler::process(NFTHistoryHandler::Input input, Context const& ctx)
 | 
			
		||||
    const
 | 
			
		||||
{
 | 
			
		||||
    auto const range = sharedPtrBackend_->fetchLedgerRange();
 | 
			
		||||
    auto [minIndex, maxIndex] = *range;
 | 
			
		||||
    if (input.ledgerIndexMin)
 | 
			
		||||
    {
 | 
			
		||||
        if (range->maxSequence < input.ledgerIndexMin ||
 | 
			
		||||
            range->minSequence > input.ledgerIndexMin)
 | 
			
		||||
        {
 | 
			
		||||
            return Error{RPC::Status{
 | 
			
		||||
                RPC::RippledError::rpcLGR_IDX_MALFORMED,
 | 
			
		||||
                "ledgerSeqMinOutOfRange"}};
 | 
			
		||||
        }
 | 
			
		||||
        minIndex = *input.ledgerIndexMin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (input.ledgerIndexMax)
 | 
			
		||||
    {
 | 
			
		||||
        if (range->maxSequence < input.ledgerIndexMax ||
 | 
			
		||||
            range->minSequence > input.ledgerIndexMax)
 | 
			
		||||
            return Error{RPC::Status{
 | 
			
		||||
                RPC::RippledError::rpcLGR_IDX_MALFORMED,
 | 
			
		||||
                "ledgerSeqMaxOutOfRange"}};
 | 
			
		||||
        maxIndex = *input.ledgerIndexMax;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (minIndex > maxIndex)
 | 
			
		||||
        return Error{
 | 
			
		||||
            RPC::Status{RPC::RippledError::rpcINVALID_PARAMS, "invalidIndex"}};
 | 
			
		||||
 | 
			
		||||
    if (input.ledgerHash || input.ledgerIndex)
 | 
			
		||||
    {
 | 
			
		||||
        // rippled does not have this check
 | 
			
		||||
        if (input.ledgerIndexMax || input.ledgerIndexMin)
 | 
			
		||||
            return Error{RPC::Status{
 | 
			
		||||
                RPC::RippledError::rpcINVALID_PARAMS,
 | 
			
		||||
                "containsLedgerSpecifierAndRange"}};
 | 
			
		||||
 | 
			
		||||
        auto const lgrInfoOrStatus = RPC::getLedgerInfoFromHashOrSeq(
 | 
			
		||||
            *sharedPtrBackend_,
 | 
			
		||||
            ctx.yield,
 | 
			
		||||
            input.ledgerHash,
 | 
			
		||||
            input.ledgerIndex,
 | 
			
		||||
            range->maxSequence);
 | 
			
		||||
 | 
			
		||||
        if (auto status = std::get_if<RPC::Status>(&lgrInfoOrStatus))
 | 
			
		||||
            return Error{*status};
 | 
			
		||||
 | 
			
		||||
        maxIndex = minIndex = std::get<ripple::LedgerInfo>(lgrInfoOrStatus).seq;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::optional<Backend::TransactionsCursor> cursor;
 | 
			
		||||
 | 
			
		||||
    // if marker exists
 | 
			
		||||
    if (input.marker)
 | 
			
		||||
    {
 | 
			
		||||
        cursor = {input.marker->ledger, input.marker->seq};
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        if (input.forward)
 | 
			
		||||
            cursor = {minIndex, 0};
 | 
			
		||||
        else
 | 
			
		||||
            cursor = {maxIndex, INT32_MAX};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static auto constexpr limitDefault = 50;
 | 
			
		||||
    auto const limit = input.limit.value_or(limitDefault);
 | 
			
		||||
    auto const tokenID = ripple::uint256{input.nftID.c_str()};
 | 
			
		||||
    auto const [txnsAndCursor, timeDiff] = util::timed([&]() {
 | 
			
		||||
        return sharedPtrBackend_->fetchNFTTransactions(
 | 
			
		||||
            tokenID, limit, input.forward, cursor, ctx.yield);
 | 
			
		||||
    });
 | 
			
		||||
    log_.info() << "db fetch took " << timeDiff
 | 
			
		||||
                << " milliseconds - num blobs = " << txnsAndCursor.txns.size();
 | 
			
		||||
    auto const [blobs, retCursor] = txnsAndCursor;
 | 
			
		||||
 | 
			
		||||
    Output response;
 | 
			
		||||
    if (retCursor)
 | 
			
		||||
        response.marker = {
 | 
			
		||||
            retCursor->ledgerSequence, retCursor->transactionIndex};
 | 
			
		||||
 | 
			
		||||
    for (auto const& txnPlusMeta : blobs)
 | 
			
		||||
    {
 | 
			
		||||
        // over the range
 | 
			
		||||
        if ((txnPlusMeta.ledgerSequence < minIndex && !input.forward) ||
 | 
			
		||||
            (txnPlusMeta.ledgerSequence > maxIndex && input.forward))
 | 
			
		||||
        {
 | 
			
		||||
            response.marker = std::nullopt;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        else if (txnPlusMeta.ledgerSequence > maxIndex && !input.forward)
 | 
			
		||||
        {
 | 
			
		||||
            log_.debug() << "Skipping over transactions from incomplete ledger";
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        boost::json::object obj;
 | 
			
		||||
        if (!input.binary)
 | 
			
		||||
        {
 | 
			
		||||
            auto [txn, meta] = RPC::toExpandedJson(txnPlusMeta);
 | 
			
		||||
            obj[JS(meta)] = std::move(meta);
 | 
			
		||||
            obj[JS(tx)] = std::move(txn);
 | 
			
		||||
            obj[JS(tx)].as_object()[JS(ledger_index)] =
 | 
			
		||||
                txnPlusMeta.ledgerSequence;
 | 
			
		||||
            obj[JS(tx)].as_object()[JS(date)] = txnPlusMeta.date;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            obj[JS(meta)] = ripple::strHex(txnPlusMeta.metadata);
 | 
			
		||||
            obj[JS(tx_blob)] = ripple::strHex(txnPlusMeta.transaction);
 | 
			
		||||
            obj[JS(ledger_index)] = txnPlusMeta.ledgerSequence;
 | 
			
		||||
            // only clio has this field
 | 
			
		||||
            obj[JS(date)] = txnPlusMeta.date;
 | 
			
		||||
        }
 | 
			
		||||
        obj[JS(validated)] = true;
 | 
			
		||||
 | 
			
		||||
        response.transactions.push_back(obj);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    response.limit = input.limit;
 | 
			
		||||
    response.nftID = ripple::to_string(tokenID);
 | 
			
		||||
    response.ledgerIndexMin = minIndex;
 | 
			
		||||
    response.ledgerIndexMax = maxIndex;
 | 
			
		||||
 | 
			
		||||
    return response;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    NFTHistoryHandler::Output const& output)
 | 
			
		||||
{
 | 
			
		||||
    jv = {
 | 
			
		||||
        {JS(nft_id), output.nftID},
 | 
			
		||||
        {JS(ledger_index_min), output.ledgerIndexMin},
 | 
			
		||||
        {JS(ledger_index_max), output.ledgerIndexMax},
 | 
			
		||||
        {JS(transactions), output.transactions},
 | 
			
		||||
        {JS(validated), output.validated}};
 | 
			
		||||
    if (output.marker)
 | 
			
		||||
        jv.as_object()[JS(marker)] = boost::json::value_from(*(output.marker));
 | 
			
		||||
    if (output.limit)
 | 
			
		||||
        jv.as_object()[JS(limit)] = *(output.limit);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    NFTHistoryHandler::Marker const& marker)
 | 
			
		||||
{
 | 
			
		||||
    jv = {{JS(ledger), marker.ledger}, {JS(seq), marker.seq}};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NFTHistoryHandler::Input
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_to_tag<NFTHistoryHandler::Input>,
 | 
			
		||||
    boost::json::value const& jv)
 | 
			
		||||
{
 | 
			
		||||
    auto const& jsonObject = jv.as_object();
 | 
			
		||||
    NFTHistoryHandler::Input input;
 | 
			
		||||
    input.nftID = jsonObject.at(JS(nft_id)).as_string().c_str();
 | 
			
		||||
    if (jsonObject.contains(JS(ledger_index_min)) &&
 | 
			
		||||
        jsonObject.at(JS(ledger_index_min)).as_int64() != -1)
 | 
			
		||||
    {
 | 
			
		||||
        input.ledgerIndexMin = jsonObject.at(JS(ledger_index_min)).as_int64();
 | 
			
		||||
    }
 | 
			
		||||
    if (jsonObject.contains(JS(ledger_index_max)) &&
 | 
			
		||||
        jsonObject.at(JS(ledger_index_max)).as_int64() != -1)
 | 
			
		||||
    {
 | 
			
		||||
        input.ledgerIndexMax = jsonObject.at(JS(ledger_index_max)).as_int64();
 | 
			
		||||
    }
 | 
			
		||||
    if (jsonObject.contains(JS(ledger_hash)))
 | 
			
		||||
    {
 | 
			
		||||
        input.ledgerHash = jsonObject.at(JS(ledger_hash)).as_string().c_str();
 | 
			
		||||
    }
 | 
			
		||||
    if (jsonObject.contains(JS(ledger_index)))
 | 
			
		||||
    {
 | 
			
		||||
        if (!jsonObject.at(JS(ledger_index)).is_string())
 | 
			
		||||
        {
 | 
			
		||||
            input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64();
 | 
			
		||||
        }
 | 
			
		||||
        else if (jsonObject.at(JS(ledger_index)).as_string() != "validated")
 | 
			
		||||
        {
 | 
			
		||||
            input.ledgerIndex =
 | 
			
		||||
                std::stoi(jsonObject.at(JS(ledger_index)).as_string().c_str());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    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 = jsonObject.at(JS(limit)).as_int64();
 | 
			
		||||
    }
 | 
			
		||||
    if (jsonObject.contains(JS(marker)))
 | 
			
		||||
    {
 | 
			
		||||
        input.marker = NFTHistoryHandler::Marker{
 | 
			
		||||
            jsonObject.at(JS(marker)).as_object().at(JS(ledger)).as_int64(),
 | 
			
		||||
            jsonObject.at(JS(marker)).as_object().at(JS(seq)).as_int64()};
 | 
			
		||||
    }
 | 
			
		||||
    return input;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
							
								
								
									
										129
									
								
								src/rpc/ngHandlers/NFTHistory.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								src/rpc/ngHandlers/NFTHistory.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,129 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2023, 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 <backend/BackendInterface.h>
 | 
			
		||||
#include <log/Logger.h>
 | 
			
		||||
#include <rpc/RPCHelpers.h>
 | 
			
		||||
#include <rpc/common/Types.h>
 | 
			
		||||
#include <rpc/common/Validators.h>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
class NFTHistoryHandler
 | 
			
		||||
{
 | 
			
		||||
    clio::Logger log_{"RPC"};
 | 
			
		||||
    std::shared_ptr<BackendInterface> sharedPtrBackend_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    // TODO: this marker is same as account_tx, reuse in future
 | 
			
		||||
    struct Marker
 | 
			
		||||
    {
 | 
			
		||||
        uint32_t ledger;
 | 
			
		||||
        uint32_t seq;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct Output
 | 
			
		||||
    {
 | 
			
		||||
        std::string nftID;
 | 
			
		||||
        uint32_t ledgerIndexMin;
 | 
			
		||||
        uint32_t ledgerIndexMax;
 | 
			
		||||
        std::optional<uint32_t> limit;
 | 
			
		||||
        std::optional<Marker> marker;
 | 
			
		||||
        // TODO: use a better type than json
 | 
			
		||||
        boost::json::array transactions;
 | 
			
		||||
        // validated should be sent via framework
 | 
			
		||||
        bool validated = true;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // TODO: we did not implement the "strict" field
 | 
			
		||||
    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 = RPCng::HandlerReturnType<Output>;
 | 
			
		||||
 | 
			
		||||
    NFTHistoryHandler(std::shared_ptr<BackendInterface> const& sharedPtrBackend)
 | 
			
		||||
        : sharedPtrBackend_(sharedPtrBackend)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RpcSpecConstRef
 | 
			
		||||
    spec() const
 | 
			
		||||
    {
 | 
			
		||||
        static auto const rpcSpec = RpcSpec{
 | 
			
		||||
            {JS(nft_id),
 | 
			
		||||
             validation::Required{},
 | 
			
		||||
             validation::Uint256HexStringValidator},
 | 
			
		||||
            {JS(ledger_hash), validation::Uint256HexStringValidator},
 | 
			
		||||
            {JS(ledger_index), validation::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::Between{1, 100}},
 | 
			
		||||
            {JS(marker),
 | 
			
		||||
             validation::WithCustomError{
 | 
			
		||||
                 validation::Type<boost::json::object>{},
 | 
			
		||||
                 RPC::Status{
 | 
			
		||||
                     RPC::RippledError::rpcINVALID_PARAMS, "invalidMarker"}},
 | 
			
		||||
             validation::Section{
 | 
			
		||||
                 {JS(ledger),
 | 
			
		||||
                  validation::Required{},
 | 
			
		||||
                  validation::Type<uint32_t>{}},
 | 
			
		||||
                 {JS(seq),
 | 
			
		||||
                  validation::Required{},
 | 
			
		||||
                  validation::Type<uint32_t>{}},
 | 
			
		||||
             }}};
 | 
			
		||||
        return rpcSpec;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Result
 | 
			
		||||
    process(Input input, Context const& ctx) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Output const& output);
 | 
			
		||||
 | 
			
		||||
    friend Input
 | 
			
		||||
    tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
 | 
			
		||||
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Marker const& marker);
 | 
			
		||||
};
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
@@ -24,8 +24,6 @@
 | 
			
		||||
#include <rpc/common/Types.h>
 | 
			
		||||
#include <rpc/common/Validators.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
class NFTInfoHandler
 | 
			
		||||
{
 | 
			
		||||
@@ -82,16 +80,15 @@ public:
 | 
			
		||||
 | 
			
		||||
    Result
 | 
			
		||||
    process(Input input, Context const& ctx) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Output const& output);
 | 
			
		||||
 | 
			
		||||
    friend Input
 | 
			
		||||
    tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    NFTInfoHandler::Output const& output);
 | 
			
		||||
 | 
			
		||||
NFTInfoHandler::Input
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_to_tag<NFTInfoHandler::Input>,
 | 
			
		||||
    boost::json::value const& jv);
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,6 @@
 | 
			
		||||
#include <rpc/common/Types.h>
 | 
			
		||||
#include <rpc/common/Validators.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
 | 
			
		||||
class NFTOffersHandlerBase
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,6 @@
 | 
			
		||||
#include <backend/BackendInterface.h>
 | 
			
		||||
#include <rpc/ngHandlers/NFTOffersCommon.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
class NFTSellOffersHandler : public NFTOffersHandlerBase
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,6 @@
 | 
			
		||||
#include <rpc/common/Types.h>
 | 
			
		||||
#include <rpc/common/Validators.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
#include <set>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
@@ -85,16 +83,15 @@ public:
 | 
			
		||||
 | 
			
		||||
    Result
 | 
			
		||||
    process(Input input, Context const& ctx) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Output const& output);
 | 
			
		||||
 | 
			
		||||
    friend Input
 | 
			
		||||
    tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    NoRippleCheckHandler::Output const& output);
 | 
			
		||||
 | 
			
		||||
NoRippleCheckHandler::Input
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_to_tag<NoRippleCheckHandler::Input>,
 | 
			
		||||
    boost::json::value const& jv);
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,6 @@
 | 
			
		||||
#include <rpc/common/Types.h>
 | 
			
		||||
#include <rpc/common/Validators.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
class TransactionEntryHandler
 | 
			
		||||
{
 | 
			
		||||
@@ -72,16 +70,15 @@ public:
 | 
			
		||||
 | 
			
		||||
    Result
 | 
			
		||||
    process(Input input, Context const& ctx) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Output const& output);
 | 
			
		||||
 | 
			
		||||
    friend Input
 | 
			
		||||
    tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    TransactionEntryHandler::Output const& output);
 | 
			
		||||
 | 
			
		||||
TransactionEntryHandler::Input
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_to_tag<TransactionEntryHandler::Input>,
 | 
			
		||||
    boost::json::value const& jv);
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
 
 | 
			
		||||
@@ -23,8 +23,6 @@
 | 
			
		||||
#include <rpc/common/Types.h>
 | 
			
		||||
#include <rpc/common/Validators.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/spawn.hpp>
 | 
			
		||||
 | 
			
		||||
namespace RPCng {
 | 
			
		||||
class TxHandler
 | 
			
		||||
{
 | 
			
		||||
@@ -76,16 +74,15 @@ public:
 | 
			
		||||
 | 
			
		||||
    Result
 | 
			
		||||
    process(Input input, Context const& ctx) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend void
 | 
			
		||||
    tag_invoke(
 | 
			
		||||
        boost::json::value_from_tag,
 | 
			
		||||
        boost::json::value& jv,
 | 
			
		||||
        Output const& output);
 | 
			
		||||
 | 
			
		||||
    friend Input
 | 
			
		||||
    tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_from_tag,
 | 
			
		||||
    boost::json::value& jv,
 | 
			
		||||
    TxHandler::Output const& output);
 | 
			
		||||
 | 
			
		||||
TxHandler::Input
 | 
			
		||||
tag_invoke(
 | 
			
		||||
    boost::json::value_to_tag<TxHandler::Input>,
 | 
			
		||||
    boost::json::value const& jv);
 | 
			
		||||
}  // namespace RPCng
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										809
									
								
								unittests/rpc/handlers/NFTHistoryTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										809
									
								
								unittests/rpc/handlers/NFTHistoryTest.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,809 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2023, 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/AnyHandler.h>
 | 
			
		||||
#include <rpc/ngHandlers/NFTHistory.h>
 | 
			
		||||
#include <util/Fixtures.h>
 | 
			
		||||
#include <util/TestObject.h>
 | 
			
		||||
 | 
			
		||||
#include <fmt/core.h>
 | 
			
		||||
 | 
			
		||||
using namespace RPCng;
 | 
			
		||||
namespace json = boost::json;
 | 
			
		||||
using namespace testing;
 | 
			
		||||
 | 
			
		||||
constexpr static auto MINSEQ = 10;
 | 
			
		||||
constexpr static auto MAXSEQ = 30;
 | 
			
		||||
constexpr static auto ACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
 | 
			
		||||
constexpr static auto ACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun";
 | 
			
		||||
constexpr static auto LEDGERHASH =
 | 
			
		||||
    "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652";
 | 
			
		||||
constexpr static auto NFTID =
 | 
			
		||||
    "00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004";
 | 
			
		||||
 | 
			
		||||
class RPCNFTHistoryHandlerTest : public HandlerBaseTest
 | 
			
		||||
{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct NFTHistoryParamTestCaseBundle
 | 
			
		||||
{
 | 
			
		||||
    std::string testName;
 | 
			
		||||
    std::string testJson;
 | 
			
		||||
    std::string expectedError;
 | 
			
		||||
    std::string expectedErrorMessage;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// parameterized test cases for parameters check
 | 
			
		||||
struct NFTHistoryParameterTest
 | 
			
		||||
    : public RPCNFTHistoryHandlerTest,
 | 
			
		||||
      public WithParamInterface<NFTHistoryParamTestCaseBundle>
 | 
			
		||||
{
 | 
			
		||||
    struct NameGenerator
 | 
			
		||||
    {
 | 
			
		||||
        template <class ParamType>
 | 
			
		||||
        std::string
 | 
			
		||||
        operator()(const testing::TestParamInfo<ParamType>& info) const
 | 
			
		||||
        {
 | 
			
		||||
            auto bundle =
 | 
			
		||||
                static_cast<NFTHistoryParamTestCaseBundle>(info.param);
 | 
			
		||||
            return bundle.testName;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static auto
 | 
			
		||||
generateTestValuesForParametersTest()
 | 
			
		||||
{
 | 
			
		||||
    return std::vector<NFTHistoryParamTestCaseBundle>{
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "MissingNFTID",
 | 
			
		||||
            R"({})",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "Required field 'nft_id' missing"},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "BinaryNotBool",
 | 
			
		||||
            R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "binary": 1})",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "Invalid parameters."},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "ForwardNotBool",
 | 
			
		||||
            R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "forward": 1})",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "Invalid parameters."},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "ledger_index_minNotInt",
 | 
			
		||||
            R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index_min": "x"})",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "Invalid parameters."},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "ledger_index_maxNotInt",
 | 
			
		||||
            R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index_max": "x"})",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "Invalid parameters."},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "ledger_indexInvalid",
 | 
			
		||||
            R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index": "x"})",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "ledgerIndexMalformed"},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "ledger_hashInvalid",
 | 
			
		||||
            R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_hash": "x"})",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "ledger_hashMalformed"},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "ledger_hashNotString",
 | 
			
		||||
            R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_hash": 123})",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "ledger_hashNotString"},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "limitNotInt",
 | 
			
		||||
            R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "limit": "123"})",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "Invalid parameters."},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "limitOverRange",
 | 
			
		||||
            R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "limit": 101})",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "Invalid parameters."},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "MarkerNotObject",
 | 
			
		||||
            R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "marker": 101})",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "invalidMarker"},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "MarkerMissingSeq",
 | 
			
		||||
            R"({
 | 
			
		||||
                "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004",
 | 
			
		||||
                "marker": {"ledger": 123}
 | 
			
		||||
            })",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "Required field 'seq' missing"},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "MarkerMissingLedger",
 | 
			
		||||
            R"({
 | 
			
		||||
                "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004",
 | 
			
		||||
                "marker":{"seq": 123}
 | 
			
		||||
            })",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "Required field 'ledger' missing"},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "MarkerLedgerNotInt",
 | 
			
		||||
            R"({
 | 
			
		||||
                "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004",
 | 
			
		||||
                "marker": 
 | 
			
		||||
                {
 | 
			
		||||
                    "seq": "string",
 | 
			
		||||
                    "ledger": 1
 | 
			
		||||
                }
 | 
			
		||||
            })",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "Invalid parameters."},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "MarkerSeqNotInt",
 | 
			
		||||
            R"({
 | 
			
		||||
                "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004",
 | 
			
		||||
                "marker": 
 | 
			
		||||
                {
 | 
			
		||||
                    "ledger": "string",
 | 
			
		||||
                    "seq": 1
 | 
			
		||||
                }
 | 
			
		||||
            })",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "Invalid parameters."},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "LedgerIndexMinLessThanMinSeq",
 | 
			
		||||
            R"({
 | 
			
		||||
                "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004",
 | 
			
		||||
                "ledger_index_min": 9
 | 
			
		||||
            })",
 | 
			
		||||
            "lgrIdxMalformed",
 | 
			
		||||
            "ledgerSeqMinOutOfRange"},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "LedgerIndexMaxLargeThanMaxSeq",
 | 
			
		||||
            R"({
 | 
			
		||||
                "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004",
 | 
			
		||||
                "ledger_index_max": 31
 | 
			
		||||
            })",
 | 
			
		||||
            "lgrIdxMalformed",
 | 
			
		||||
            "ledgerSeqMaxOutOfRange"},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "LedgerIndexMaxLessThanLedgerIndexMin",
 | 
			
		||||
            R"({
 | 
			
		||||
                "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004",
 | 
			
		||||
                "ledger_index_max": 11,
 | 
			
		||||
                "ledger_index_min": 20
 | 
			
		||||
            })",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "invalidIndex"},
 | 
			
		||||
        NFTHistoryParamTestCaseBundle{
 | 
			
		||||
            "LedgerIndexMaxMinAndLedgerIndex",
 | 
			
		||||
            R"({
 | 
			
		||||
                "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", 
 | 
			
		||||
                "ledger_index_max": 20,
 | 
			
		||||
                "ledger_index_min": 11,
 | 
			
		||||
                "ledger_index": 10
 | 
			
		||||
            })",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "containsLedgerSpecifierAndRange"},
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
INSTANTIATE_TEST_CASE_P(
 | 
			
		||||
    RPCNFTHistoryGroup1,
 | 
			
		||||
    NFTHistoryParameterTest,
 | 
			
		||||
    ValuesIn(generateTestValuesForParametersTest()),
 | 
			
		||||
    NFTHistoryParameterTest::NameGenerator{});
 | 
			
		||||
 | 
			
		||||
TEST_P(NFTHistoryParameterTest, InvalidParams)
 | 
			
		||||
{
 | 
			
		||||
    mockBackendPtr->updateRange(MINSEQ);  // min
 | 
			
		||||
    mockBackendPtr->updateRange(MAXSEQ);  // max
 | 
			
		||||
    auto const testBundle = GetParam();
 | 
			
		||||
    runSpawn([&, this](auto& yield) {
 | 
			
		||||
        auto const handler = AnyHandler{NFTHistoryHandler{mockBackendPtr}};
 | 
			
		||||
        auto const req = json::parse(testBundle.testJson);
 | 
			
		||||
        auto const output = handler.process(req, Context{std::ref(yield)});
 | 
			
		||||
        ASSERT_FALSE(output);
 | 
			
		||||
 | 
			
		||||
        auto const err = RPC::makeError(output.error());
 | 
			
		||||
        EXPECT_EQ(err.at("error").as_string(), testBundle.expectedError);
 | 
			
		||||
        EXPECT_EQ(
 | 
			
		||||
            err.at("error_message").as_string(),
 | 
			
		||||
            testBundle.expectedErrorMessage);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static std::vector<TransactionAndMetadata>
 | 
			
		||||
genTransactions(uint32_t seq1, uint32_t seq2)
 | 
			
		||||
{
 | 
			
		||||
    auto transactions = std::vector<TransactionAndMetadata>{};
 | 
			
		||||
    auto trans1 = TransactionAndMetadata();
 | 
			
		||||
    ripple::STObject obj =
 | 
			
		||||
        CreatePaymentTransactionObject(ACCOUNT, ACCOUNT2, 1, 1, 32);
 | 
			
		||||
    trans1.transaction = obj.getSerializer().peekData();
 | 
			
		||||
    trans1.ledgerSequence = seq1;
 | 
			
		||||
    ripple::STObject metaObj =
 | 
			
		||||
        CreatePaymentTransactionMetaObject(ACCOUNT, ACCOUNT2, 22, 23);
 | 
			
		||||
    trans1.metadata = metaObj.getSerializer().peekData();
 | 
			
		||||
    trans1.date = 1;
 | 
			
		||||
    transactions.push_back(trans1);
 | 
			
		||||
 | 
			
		||||
    auto trans2 = TransactionAndMetadata();
 | 
			
		||||
    ripple::STObject obj2 =
 | 
			
		||||
        CreatePaymentTransactionObject(ACCOUNT, ACCOUNT2, 1, 1, 32);
 | 
			
		||||
    trans2.transaction = obj.getSerializer().peekData();
 | 
			
		||||
    trans2.ledgerSequence = seq2;
 | 
			
		||||
    ripple::STObject metaObj2 =
 | 
			
		||||
        CreatePaymentTransactionMetaObject(ACCOUNT, ACCOUNT2, 22, 23);
 | 
			
		||||
    trans2.metadata = metaObj2.getSerializer().peekData();
 | 
			
		||||
    trans2.date = 2;
 | 
			
		||||
    transactions.push_back(trans2);
 | 
			
		||||
    return transactions;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCNFTHistoryHandlerTest, IndexSpecificForwardTrue)
 | 
			
		||||
{
 | 
			
		||||
    mockBackendPtr->updateRange(MINSEQ);  // min
 | 
			
		||||
    mockBackendPtr->updateRange(MAXSEQ);  // max
 | 
			
		||||
    MockBackend* rawBackendPtr =
 | 
			
		||||
        static_cast<MockBackend*>(mockBackendPtr.get());
 | 
			
		||||
    auto const transactions = genTransactions(MINSEQ + 1, MAXSEQ - 1);
 | 
			
		||||
    auto const transCursor =
 | 
			
		||||
        TransactionsAndCursor{transactions, TransactionsCursor{12, 34}};
 | 
			
		||||
    ON_CALL(*rawBackendPtr, fetchNFTTransactions)
 | 
			
		||||
        .WillByDefault(Return(transCursor));
 | 
			
		||||
    EXPECT_CALL(
 | 
			
		||||
        *rawBackendPtr,
 | 
			
		||||
        fetchNFTTransactions(
 | 
			
		||||
            testing::_,
 | 
			
		||||
            testing::_,
 | 
			
		||||
            true,
 | 
			
		||||
            testing::Optional(testing::Eq(TransactionsCursor{MINSEQ + 1, 0})),
 | 
			
		||||
            testing::_))
 | 
			
		||||
        .Times(1);
 | 
			
		||||
 | 
			
		||||
    runSpawn([&, this](auto& yield) {
 | 
			
		||||
        auto const handler = AnyHandler{NFTHistoryHandler{mockBackendPtr}};
 | 
			
		||||
        auto const static input = boost::json::parse(fmt::format(
 | 
			
		||||
            R"({{
 | 
			
		||||
                "nft_id":"{}",
 | 
			
		||||
                "ledger_index_min": {},
 | 
			
		||||
                "ledger_index_max": {},
 | 
			
		||||
                "forward": true
 | 
			
		||||
            }})",
 | 
			
		||||
            NFTID,
 | 
			
		||||
            MINSEQ + 1,
 | 
			
		||||
            MAXSEQ - 1));
 | 
			
		||||
        auto const output = handler.process(input, Context{std::ref(yield)});
 | 
			
		||||
        ASSERT_TRUE(output);
 | 
			
		||||
        EXPECT_EQ(output->at("nft_id").as_string(), NFTID);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ + 1);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ - 1);
 | 
			
		||||
        EXPECT_EQ(
 | 
			
		||||
            output->at("marker").as_object(),
 | 
			
		||||
            json::parse(R"({"ledger":12,"seq":34})"));
 | 
			
		||||
        EXPECT_EQ(output->at("transactions").as_array().size(), 2);
 | 
			
		||||
        EXPECT_FALSE(output->as_object().contains("limit"));
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCNFTHistoryHandlerTest, IndexSpecificForwardFalse)
 | 
			
		||||
{
 | 
			
		||||
    mockBackendPtr->updateRange(MINSEQ);  // min
 | 
			
		||||
    mockBackendPtr->updateRange(MAXSEQ);  // max
 | 
			
		||||
    MockBackend* rawBackendPtr =
 | 
			
		||||
        static_cast<MockBackend*>(mockBackendPtr.get());
 | 
			
		||||
    auto const transactions = genTransactions(MINSEQ + 1, MAXSEQ - 1);
 | 
			
		||||
    auto const transCursor =
 | 
			
		||||
        TransactionsAndCursor{transactions, TransactionsCursor{12, 34}};
 | 
			
		||||
    ON_CALL(*rawBackendPtr, fetchNFTTransactions)
 | 
			
		||||
        .WillByDefault(Return(transCursor));
 | 
			
		||||
    EXPECT_CALL(
 | 
			
		||||
        *rawBackendPtr,
 | 
			
		||||
        fetchNFTTransactions(
 | 
			
		||||
            testing::_,
 | 
			
		||||
            testing::_,
 | 
			
		||||
            false,
 | 
			
		||||
            testing::Optional(
 | 
			
		||||
                testing::Eq(TransactionsCursor{MAXSEQ - 1, INT32_MAX})),
 | 
			
		||||
            testing::_))
 | 
			
		||||
        .Times(1);
 | 
			
		||||
 | 
			
		||||
    runSpawn([&, this](auto& yield) {
 | 
			
		||||
        auto const handler = AnyHandler{NFTHistoryHandler{mockBackendPtr}};
 | 
			
		||||
        auto const static input = boost::json::parse(fmt::format(
 | 
			
		||||
            R"({{
 | 
			
		||||
                "nft_id":"{}",
 | 
			
		||||
                "ledger_index_min": {},
 | 
			
		||||
                "ledger_index_max": {},
 | 
			
		||||
                "forward": false
 | 
			
		||||
            }})",
 | 
			
		||||
            NFTID,
 | 
			
		||||
            MINSEQ + 1,
 | 
			
		||||
            MAXSEQ - 1));
 | 
			
		||||
        auto const output = handler.process(input, Context{std::ref(yield)});
 | 
			
		||||
        ASSERT_TRUE(output);
 | 
			
		||||
        EXPECT_EQ(output->at("nft_id").as_string(), NFTID);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ + 1);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ - 1);
 | 
			
		||||
        EXPECT_EQ(
 | 
			
		||||
            output->at("marker").as_object(),
 | 
			
		||||
            json::parse(R"({"ledger":12,"seq":34})"));
 | 
			
		||||
        EXPECT_EQ(output->at("transactions").as_array().size(), 2);
 | 
			
		||||
        EXPECT_FALSE(output->as_object().contains("limit"));
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCNFTHistoryHandlerTest, IndexNotSpecificForwardTrue)
 | 
			
		||||
{
 | 
			
		||||
    mockBackendPtr->updateRange(MINSEQ);  // min
 | 
			
		||||
    mockBackendPtr->updateRange(MAXSEQ);  // max
 | 
			
		||||
    MockBackend* rawBackendPtr =
 | 
			
		||||
        static_cast<MockBackend*>(mockBackendPtr.get());
 | 
			
		||||
    auto const transactions = genTransactions(MINSEQ + 1, MAXSEQ - 1);
 | 
			
		||||
    auto const transCursor =
 | 
			
		||||
        TransactionsAndCursor{transactions, TransactionsCursor{12, 34}};
 | 
			
		||||
    ON_CALL(*rawBackendPtr, fetchNFTTransactions)
 | 
			
		||||
        .WillByDefault(Return(transCursor));
 | 
			
		||||
    EXPECT_CALL(
 | 
			
		||||
        *rawBackendPtr,
 | 
			
		||||
        fetchNFTTransactions(
 | 
			
		||||
            testing::_,
 | 
			
		||||
            testing::_,
 | 
			
		||||
            true,
 | 
			
		||||
            testing::Optional(testing::Eq(TransactionsCursor{MINSEQ, 0})),
 | 
			
		||||
            testing::_))
 | 
			
		||||
        .Times(1);
 | 
			
		||||
 | 
			
		||||
    runSpawn([&, this](auto& yield) {
 | 
			
		||||
        auto const handler = AnyHandler{NFTHistoryHandler{mockBackendPtr}};
 | 
			
		||||
        auto const static input = boost::json::parse(fmt::format(
 | 
			
		||||
            R"({{
 | 
			
		||||
                "nft_id":"{}",
 | 
			
		||||
                "ledger_index_min": {},
 | 
			
		||||
                "ledger_index_max": {},
 | 
			
		||||
                "forward": true
 | 
			
		||||
            }})",
 | 
			
		||||
            NFTID,
 | 
			
		||||
            -1,
 | 
			
		||||
            -1));
 | 
			
		||||
        auto const output = handler.process(input, Context{std::ref(yield)});
 | 
			
		||||
        ASSERT_TRUE(output);
 | 
			
		||||
        EXPECT_EQ(output->at("nft_id").as_string(), NFTID);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ);
 | 
			
		||||
        EXPECT_EQ(
 | 
			
		||||
            output->at("marker").as_object(),
 | 
			
		||||
            json::parse(R"({"ledger":12,"seq":34})"));
 | 
			
		||||
        EXPECT_EQ(output->at("transactions").as_array().size(), 2);
 | 
			
		||||
        EXPECT_FALSE(output->as_object().contains("limit"));
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCNFTHistoryHandlerTest, IndexNotSpecificForwardFalse)
 | 
			
		||||
{
 | 
			
		||||
    mockBackendPtr->updateRange(MINSEQ);  // min
 | 
			
		||||
    mockBackendPtr->updateRange(MAXSEQ);  // max
 | 
			
		||||
    MockBackend* rawBackendPtr =
 | 
			
		||||
        static_cast<MockBackend*>(mockBackendPtr.get());
 | 
			
		||||
    auto const transactions = genTransactions(MINSEQ + 1, MAXSEQ - 1);
 | 
			
		||||
    auto const transCursor =
 | 
			
		||||
        TransactionsAndCursor{transactions, TransactionsCursor{12, 34}};
 | 
			
		||||
    ON_CALL(*rawBackendPtr, fetchNFTTransactions)
 | 
			
		||||
        .WillByDefault(Return(transCursor));
 | 
			
		||||
    EXPECT_CALL(
 | 
			
		||||
        *rawBackendPtr,
 | 
			
		||||
        fetchNFTTransactions(
 | 
			
		||||
            testing::_,
 | 
			
		||||
            testing::_,
 | 
			
		||||
            false,
 | 
			
		||||
            testing::Optional(
 | 
			
		||||
                testing::Eq(TransactionsCursor{MAXSEQ, INT32_MAX})),
 | 
			
		||||
            testing::_))
 | 
			
		||||
        .Times(1);
 | 
			
		||||
 | 
			
		||||
    runSpawn([&, this](auto& yield) {
 | 
			
		||||
        auto const handler = AnyHandler{NFTHistoryHandler{mockBackendPtr}};
 | 
			
		||||
        auto const static input = boost::json::parse(fmt::format(
 | 
			
		||||
            R"({{
 | 
			
		||||
                "nft_id":"{}",
 | 
			
		||||
                "ledger_index_min": {},
 | 
			
		||||
                "ledger_index_max": {},
 | 
			
		||||
                "forward": false
 | 
			
		||||
            }})",
 | 
			
		||||
            NFTID,
 | 
			
		||||
            -1,
 | 
			
		||||
            -1));
 | 
			
		||||
        auto const output = handler.process(input, Context{std::ref(yield)});
 | 
			
		||||
        ASSERT_TRUE(output);
 | 
			
		||||
        EXPECT_EQ(output->at("nft_id").as_string(), NFTID);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ);
 | 
			
		||||
        EXPECT_EQ(
 | 
			
		||||
            output->at("marker").as_object(),
 | 
			
		||||
            json::parse(R"({"ledger":12,"seq":34})"));
 | 
			
		||||
        EXPECT_EQ(output->at("transactions").as_array().size(), 2);
 | 
			
		||||
        EXPECT_FALSE(output->as_object().contains("limit"));
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCNFTHistoryHandlerTest, BinaryTrue)
 | 
			
		||||
{
 | 
			
		||||
    mockBackendPtr->updateRange(MINSEQ);  // min
 | 
			
		||||
    mockBackendPtr->updateRange(MAXSEQ);  // max
 | 
			
		||||
    MockBackend* rawBackendPtr =
 | 
			
		||||
        static_cast<MockBackend*>(mockBackendPtr.get());
 | 
			
		||||
    auto const transactions = genTransactions(MINSEQ + 1, MAXSEQ - 1);
 | 
			
		||||
    auto const transCursor =
 | 
			
		||||
        TransactionsAndCursor{transactions, TransactionsCursor{12, 34}};
 | 
			
		||||
    ON_CALL(*rawBackendPtr, fetchNFTTransactions)
 | 
			
		||||
        .WillByDefault(Return(transCursor));
 | 
			
		||||
    EXPECT_CALL(
 | 
			
		||||
        *rawBackendPtr,
 | 
			
		||||
        fetchNFTTransactions(
 | 
			
		||||
            testing::_,
 | 
			
		||||
            testing::_,
 | 
			
		||||
            false,
 | 
			
		||||
            testing::Optional(
 | 
			
		||||
                testing::Eq(TransactionsCursor{MAXSEQ, INT32_MAX})),
 | 
			
		||||
            testing::_))
 | 
			
		||||
        .Times(1);
 | 
			
		||||
 | 
			
		||||
    runSpawn([&, this](auto& yield) {
 | 
			
		||||
        auto const handler = AnyHandler{NFTHistoryHandler{mockBackendPtr}};
 | 
			
		||||
        auto const static input = boost::json::parse(fmt::format(
 | 
			
		||||
            R"({{
 | 
			
		||||
                "nft_id":"{}",
 | 
			
		||||
                "ledger_index_min": {},
 | 
			
		||||
                "ledger_index_max": {},
 | 
			
		||||
                "binary": true
 | 
			
		||||
            }})",
 | 
			
		||||
            NFTID,
 | 
			
		||||
            -1,
 | 
			
		||||
            -1));
 | 
			
		||||
        auto const output = handler.process(input, Context{std::ref(yield)});
 | 
			
		||||
        ASSERT_TRUE(output);
 | 
			
		||||
        EXPECT_EQ(output->at("nft_id").as_string(), NFTID);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ);
 | 
			
		||||
        EXPECT_EQ(
 | 
			
		||||
            output->at("marker").as_object(),
 | 
			
		||||
            json::parse(R"({"ledger":12,"seq":34})"));
 | 
			
		||||
        EXPECT_EQ(output->at("transactions").as_array().size(), 2);
 | 
			
		||||
        EXPECT_EQ(
 | 
			
		||||
            output->at("transactions")
 | 
			
		||||
                .as_array()[0]
 | 
			
		||||
                .as_object()
 | 
			
		||||
                .at("meta")
 | 
			
		||||
                .as_string(),
 | 
			
		||||
            "201C00000000F8E5110061E762400000000000001681144B4E9C06F24296074F7B"
 | 
			
		||||
            "C48F92A97916C6DC5EA9E1E1E5110061E76240000000000000178114D31252CF90"
 | 
			
		||||
            "2EF8DD8451243869B38667CBD89DF3E1E1F1031000");
 | 
			
		||||
        EXPECT_EQ(
 | 
			
		||||
            output->at("transactions")
 | 
			
		||||
                .as_array()[0]
 | 
			
		||||
                .as_object()
 | 
			
		||||
                .at("tx_blob")
 | 
			
		||||
                .as_string(),
 | 
			
		||||
            "120000240000002061400000000000000168400000000000000173047465737481"
 | 
			
		||||
            "144B4E9C06F24296074F7BC48F92A97916C6DC5EA98314D31252CF902EF8DD8451"
 | 
			
		||||
            "243869B38667CBD89DF3");
 | 
			
		||||
        EXPECT_EQ(
 | 
			
		||||
            output->at("transactions")
 | 
			
		||||
                .as_array()[0]
 | 
			
		||||
                .as_object()
 | 
			
		||||
                .at("date")
 | 
			
		||||
                .as_uint64(),
 | 
			
		||||
            1);
 | 
			
		||||
 | 
			
		||||
        EXPECT_FALSE(output->as_object().contains("limit"));
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCNFTHistoryHandlerTest, LimitAndMarker)
 | 
			
		||||
{
 | 
			
		||||
    mockBackendPtr->updateRange(MINSEQ);  // min
 | 
			
		||||
    mockBackendPtr->updateRange(MAXSEQ);  // max
 | 
			
		||||
    MockBackend* rawBackendPtr =
 | 
			
		||||
        static_cast<MockBackend*>(mockBackendPtr.get());
 | 
			
		||||
    auto const transactions = genTransactions(MINSEQ + 1, MAXSEQ - 1);
 | 
			
		||||
    auto const transCursor =
 | 
			
		||||
        TransactionsAndCursor{transactions, TransactionsCursor{12, 34}};
 | 
			
		||||
    ON_CALL(*rawBackendPtr, fetchNFTTransactions)
 | 
			
		||||
        .WillByDefault(Return(transCursor));
 | 
			
		||||
    EXPECT_CALL(
 | 
			
		||||
        *rawBackendPtr,
 | 
			
		||||
        fetchNFTTransactions(
 | 
			
		||||
            testing::_,
 | 
			
		||||
            testing::_,
 | 
			
		||||
            false,
 | 
			
		||||
            testing::Optional(testing::Eq(TransactionsCursor{10, 11})),
 | 
			
		||||
            testing::_))
 | 
			
		||||
        .Times(1);
 | 
			
		||||
 | 
			
		||||
    runSpawn([&, this](auto& yield) {
 | 
			
		||||
        auto const handler = AnyHandler{NFTHistoryHandler{mockBackendPtr}};
 | 
			
		||||
        auto const static input = boost::json::parse(fmt::format(
 | 
			
		||||
            R"({{
 | 
			
		||||
                "nft_id":"{}",
 | 
			
		||||
                "ledger_index_min": {},
 | 
			
		||||
                "ledger_index_max": {},
 | 
			
		||||
                "limit": 2,
 | 
			
		||||
                "forward": false,
 | 
			
		||||
                "marker": {{"ledger":10,"seq":11}}
 | 
			
		||||
            }})",
 | 
			
		||||
            NFTID,
 | 
			
		||||
            -1,
 | 
			
		||||
            -1));
 | 
			
		||||
        auto const output = handler.process(input, Context{std::ref(yield)});
 | 
			
		||||
        ASSERT_TRUE(output);
 | 
			
		||||
        EXPECT_EQ(output->at("nft_id").as_string(), NFTID);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ);
 | 
			
		||||
        EXPECT_EQ(output->at("limit").as_uint64(), 2);
 | 
			
		||||
        EXPECT_EQ(
 | 
			
		||||
            output->at("marker").as_object(),
 | 
			
		||||
            json::parse(R"({"ledger":12,"seq":34})"));
 | 
			
		||||
        EXPECT_EQ(output->at("transactions").as_array().size(), 2);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCNFTHistoryHandlerTest, SpecificLedgerIndex)
 | 
			
		||||
{
 | 
			
		||||
    mockBackendPtr->updateRange(MINSEQ);  // min
 | 
			
		||||
    mockBackendPtr->updateRange(MAXSEQ);  // max
 | 
			
		||||
    MockBackend* rawBackendPtr =
 | 
			
		||||
        static_cast<MockBackend*>(mockBackendPtr.get());
 | 
			
		||||
    // adjust the order for forward->false
 | 
			
		||||
    auto const transactions = genTransactions(MAXSEQ - 1, MINSEQ + 1);
 | 
			
		||||
    auto const transCursor =
 | 
			
		||||
        TransactionsAndCursor{transactions, TransactionsCursor{12, 34}};
 | 
			
		||||
    ON_CALL(*rawBackendPtr, fetchNFTTransactions)
 | 
			
		||||
        .WillByDefault(Return(transCursor));
 | 
			
		||||
    EXPECT_CALL(
 | 
			
		||||
        *rawBackendPtr,
 | 
			
		||||
        fetchNFTTransactions(
 | 
			
		||||
            testing::_,
 | 
			
		||||
            testing::_,
 | 
			
		||||
            false,
 | 
			
		||||
            testing::Optional(
 | 
			
		||||
                testing::Eq(TransactionsCursor{MAXSEQ - 1, INT32_MAX})),
 | 
			
		||||
            testing::_))
 | 
			
		||||
        .Times(1);
 | 
			
		||||
 | 
			
		||||
    auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, MAXSEQ - 1);
 | 
			
		||||
    EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1);
 | 
			
		||||
    ON_CALL(*rawBackendPtr, fetchLedgerBySequence(MAXSEQ - 1, _))
 | 
			
		||||
        .WillByDefault(Return(ledgerinfo));
 | 
			
		||||
 | 
			
		||||
    runSpawn([&, this](auto& yield) {
 | 
			
		||||
        auto const handler = AnyHandler{NFTHistoryHandler{mockBackendPtr}};
 | 
			
		||||
        auto const static input = boost::json::parse(fmt::format(
 | 
			
		||||
            R"({{
 | 
			
		||||
                "nft_id":"{}",
 | 
			
		||||
                "ledger_index":{}
 | 
			
		||||
            }})",
 | 
			
		||||
            NFTID,
 | 
			
		||||
            MAXSEQ - 1));
 | 
			
		||||
        auto const output = handler.process(input, Context{std::ref(yield)});
 | 
			
		||||
        ASSERT_TRUE(output);
 | 
			
		||||
        EXPECT_EQ(output->at("nft_id").as_string(), NFTID);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MAXSEQ - 1);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ - 1);
 | 
			
		||||
        EXPECT_FALSE(output->as_object().contains("limit"));
 | 
			
		||||
        EXPECT_FALSE(output->as_object().contains("marker"));
 | 
			
		||||
        EXPECT_EQ(output->at("transactions").as_array().size(), 1);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCNFTHistoryHandlerTest, SpecificNonexistLedgerIntIndex)
 | 
			
		||||
{
 | 
			
		||||
    mockBackendPtr->updateRange(MINSEQ);  // min
 | 
			
		||||
    mockBackendPtr->updateRange(MAXSEQ);  // max
 | 
			
		||||
    MockBackend* rawBackendPtr =
 | 
			
		||||
        static_cast<MockBackend*>(mockBackendPtr.get());
 | 
			
		||||
 | 
			
		||||
    EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1);
 | 
			
		||||
    ON_CALL(*rawBackendPtr, fetchLedgerBySequence(MAXSEQ - 1, _))
 | 
			
		||||
        .WillByDefault(Return(std::nullopt));
 | 
			
		||||
 | 
			
		||||
    runSpawn([&, this](auto& yield) {
 | 
			
		||||
        auto const handler = AnyHandler{NFTHistoryHandler{mockBackendPtr}};
 | 
			
		||||
        auto const static input = boost::json::parse(fmt::format(
 | 
			
		||||
            R"({{
 | 
			
		||||
                "nft_id":"{}",
 | 
			
		||||
                "ledger_index":{}
 | 
			
		||||
            }})",
 | 
			
		||||
            NFTID,
 | 
			
		||||
            MAXSEQ - 1));
 | 
			
		||||
        auto const output = handler.process(input, Context{std::ref(yield)});
 | 
			
		||||
        ASSERT_FALSE(output);
 | 
			
		||||
        auto const err = RPC::makeError(output.error());
 | 
			
		||||
        EXPECT_EQ(err.at("error").as_string(), "lgrNotFound");
 | 
			
		||||
        EXPECT_EQ(err.at("error_message").as_string(), "ledgerNotFound");
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCNFTHistoryHandlerTest, SpecificNonexistLedgerStringIndex)
 | 
			
		||||
{
 | 
			
		||||
    mockBackendPtr->updateRange(MINSEQ);  // min
 | 
			
		||||
    mockBackendPtr->updateRange(MAXSEQ);  // max
 | 
			
		||||
    MockBackend* rawBackendPtr =
 | 
			
		||||
        static_cast<MockBackend*>(mockBackendPtr.get());
 | 
			
		||||
 | 
			
		||||
    EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1);
 | 
			
		||||
    ON_CALL(*rawBackendPtr, fetchLedgerBySequence(MAXSEQ - 1, _))
 | 
			
		||||
        .WillByDefault(Return(std::nullopt));
 | 
			
		||||
 | 
			
		||||
    runSpawn([&, this](auto& yield) {
 | 
			
		||||
        auto const handler = AnyHandler{NFTHistoryHandler{mockBackendPtr}};
 | 
			
		||||
        auto const static input = boost::json::parse(fmt::format(
 | 
			
		||||
            R"({{
 | 
			
		||||
                "nft_id":"{}",
 | 
			
		||||
                "ledger_index":"{}"
 | 
			
		||||
            }})",
 | 
			
		||||
            NFTID,
 | 
			
		||||
            MAXSEQ - 1));
 | 
			
		||||
        auto const output = handler.process(input, Context{std::ref(yield)});
 | 
			
		||||
        ASSERT_FALSE(output);
 | 
			
		||||
        auto const err = RPC::makeError(output.error());
 | 
			
		||||
        EXPECT_EQ(err.at("error").as_string(), "lgrNotFound");
 | 
			
		||||
        EXPECT_EQ(err.at("error_message").as_string(), "ledgerNotFound");
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCNFTHistoryHandlerTest, SpecificLedgerHash)
 | 
			
		||||
{
 | 
			
		||||
    mockBackendPtr->updateRange(MINSEQ);  // min
 | 
			
		||||
    mockBackendPtr->updateRange(MAXSEQ);  // max
 | 
			
		||||
    MockBackend* rawBackendPtr =
 | 
			
		||||
        static_cast<MockBackend*>(mockBackendPtr.get());
 | 
			
		||||
    // adjust the order for forward->false
 | 
			
		||||
    auto const transactions = genTransactions(MAXSEQ - 1, MINSEQ + 1);
 | 
			
		||||
    auto const transCursor =
 | 
			
		||||
        TransactionsAndCursor{transactions, TransactionsCursor{12, 34}};
 | 
			
		||||
    ON_CALL(*rawBackendPtr, fetchNFTTransactions)
 | 
			
		||||
        .WillByDefault(Return(transCursor));
 | 
			
		||||
    EXPECT_CALL(
 | 
			
		||||
        *rawBackendPtr,
 | 
			
		||||
        fetchNFTTransactions(
 | 
			
		||||
            testing::_,
 | 
			
		||||
            testing::_,
 | 
			
		||||
            false,
 | 
			
		||||
            testing::Optional(
 | 
			
		||||
                testing::Eq(TransactionsCursor{MAXSEQ - 1, INT32_MAX})),
 | 
			
		||||
            testing::_))
 | 
			
		||||
        .Times(1);
 | 
			
		||||
 | 
			
		||||
    auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, MAXSEQ - 1);
 | 
			
		||||
    EXPECT_CALL(*rawBackendPtr, fetchLedgerByHash).Times(1);
 | 
			
		||||
    ON_CALL(*rawBackendPtr, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _))
 | 
			
		||||
        .WillByDefault(Return(ledgerinfo));
 | 
			
		||||
 | 
			
		||||
    runSpawn([&, this](auto& yield) {
 | 
			
		||||
        auto const handler = AnyHandler{NFTHistoryHandler{mockBackendPtr}};
 | 
			
		||||
        auto const static input = boost::json::parse(fmt::format(
 | 
			
		||||
            R"({{
 | 
			
		||||
                "nft_id":"{}",
 | 
			
		||||
                "ledger_hash":"{}"
 | 
			
		||||
            }})",
 | 
			
		||||
            NFTID,
 | 
			
		||||
            LEDGERHASH));
 | 
			
		||||
        auto const output = handler.process(input, Context{std::ref(yield)});
 | 
			
		||||
        ASSERT_TRUE(output);
 | 
			
		||||
        EXPECT_EQ(output->at("nft_id").as_string(), NFTID);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MAXSEQ - 1);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ - 1);
 | 
			
		||||
        EXPECT_FALSE(output->as_object().contains("limit"));
 | 
			
		||||
        EXPECT_FALSE(output->as_object().contains("marker"));
 | 
			
		||||
        EXPECT_EQ(output->at("transactions").as_array().size(), 1);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCNFTHistoryHandlerTest, TxLessThanMinSeq)
 | 
			
		||||
{
 | 
			
		||||
    mockBackendPtr->updateRange(MINSEQ);  // min
 | 
			
		||||
    mockBackendPtr->updateRange(MAXSEQ);  // max
 | 
			
		||||
    MockBackend* rawBackendPtr =
 | 
			
		||||
        static_cast<MockBackend*>(mockBackendPtr.get());
 | 
			
		||||
    auto const transactions = genTransactions(MAXSEQ - 1, MINSEQ + 1);
 | 
			
		||||
    auto const transCursor =
 | 
			
		||||
        TransactionsAndCursor{transactions, TransactionsCursor{12, 34}};
 | 
			
		||||
    ON_CALL(*rawBackendPtr, fetchNFTTransactions)
 | 
			
		||||
        .WillByDefault(Return(transCursor));
 | 
			
		||||
    EXPECT_CALL(
 | 
			
		||||
        *rawBackendPtr,
 | 
			
		||||
        fetchNFTTransactions(
 | 
			
		||||
            testing::_,
 | 
			
		||||
            testing::_,
 | 
			
		||||
            false,
 | 
			
		||||
            testing::Optional(
 | 
			
		||||
                testing::Eq(TransactionsCursor{MAXSEQ - 1, INT32_MAX})),
 | 
			
		||||
            testing::_))
 | 
			
		||||
        .Times(1);
 | 
			
		||||
 | 
			
		||||
    runSpawn([&, this](auto& yield) {
 | 
			
		||||
        auto const handler = AnyHandler{NFTHistoryHandler{mockBackendPtr}};
 | 
			
		||||
        auto const static input = boost::json::parse(fmt::format(
 | 
			
		||||
            R"({{
 | 
			
		||||
                "nft_id":"{}",
 | 
			
		||||
                "ledger_index_min": {},
 | 
			
		||||
                "ledger_index_max": {},
 | 
			
		||||
                "forward": false
 | 
			
		||||
            }})",
 | 
			
		||||
            NFTID,
 | 
			
		||||
            MINSEQ + 2,
 | 
			
		||||
            MAXSEQ - 1));
 | 
			
		||||
        auto const output = handler.process(input, Context{std::ref(yield)});
 | 
			
		||||
        ASSERT_TRUE(output);
 | 
			
		||||
        EXPECT_EQ(output->at("nft_id").as_string(), NFTID);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ + 2);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ - 1);
 | 
			
		||||
        EXPECT_EQ(output->at("transactions").as_array().size(), 1);
 | 
			
		||||
        EXPECT_FALSE(output->as_object().contains("limit"));
 | 
			
		||||
        EXPECT_FALSE(output->as_object().contains("marker"));
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCNFTHistoryHandlerTest, TxLargerThanMaxSeq)
 | 
			
		||||
{
 | 
			
		||||
    mockBackendPtr->updateRange(MINSEQ);  // min
 | 
			
		||||
    mockBackendPtr->updateRange(MAXSEQ);  // max
 | 
			
		||||
    MockBackend* rawBackendPtr =
 | 
			
		||||
        static_cast<MockBackend*>(mockBackendPtr.get());
 | 
			
		||||
    auto const transactions = genTransactions(MAXSEQ - 1, MINSEQ + 1);
 | 
			
		||||
    auto const transCursor =
 | 
			
		||||
        TransactionsAndCursor{transactions, TransactionsCursor{12, 34}};
 | 
			
		||||
    ON_CALL(*rawBackendPtr, fetchNFTTransactions)
 | 
			
		||||
        .WillByDefault(Return(transCursor));
 | 
			
		||||
    EXPECT_CALL(
 | 
			
		||||
        *rawBackendPtr,
 | 
			
		||||
        fetchNFTTransactions(
 | 
			
		||||
            testing::_,
 | 
			
		||||
            testing::_,
 | 
			
		||||
            false,
 | 
			
		||||
            testing::Optional(
 | 
			
		||||
                testing::Eq(TransactionsCursor{MAXSEQ - 2, INT32_MAX})),
 | 
			
		||||
            testing::_))
 | 
			
		||||
        .Times(1);
 | 
			
		||||
 | 
			
		||||
    runSpawn([&, this](auto& yield) {
 | 
			
		||||
        auto const handler = AnyHandler{NFTHistoryHandler{mockBackendPtr}};
 | 
			
		||||
        auto const static input = boost::json::parse(fmt::format(
 | 
			
		||||
            R"({{
 | 
			
		||||
                "nft_id":"{}",
 | 
			
		||||
                "ledger_index_min": {},
 | 
			
		||||
                "ledger_index_max": {},
 | 
			
		||||
                "forward": false
 | 
			
		||||
            }})",
 | 
			
		||||
            NFTID,
 | 
			
		||||
            MINSEQ + 1,
 | 
			
		||||
            MAXSEQ - 2));
 | 
			
		||||
        auto const output = handler.process(input, Context{std::ref(yield)});
 | 
			
		||||
        ASSERT_TRUE(output);
 | 
			
		||||
        EXPECT_EQ(output->at("nft_id").as_string(), NFTID);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ + 1);
 | 
			
		||||
        EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ - 2);
 | 
			
		||||
        EXPECT_EQ(output->at("transactions").as_array().size(), 1);
 | 
			
		||||
        EXPECT_FALSE(output->as_object().contains("limit"));
 | 
			
		||||
        EXPECT_EQ(
 | 
			
		||||
            output->at("marker").as_object(),
 | 
			
		||||
            json::parse(R"({"ledger":12,"seq":34})"));
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user