Skip to content

Commit

Permalink
Ledger entry type filter for account_objects and ledger_data (#1116)
Browse files Browse the repository at this point in the history
Fix #1109
  • Loading branch information
cindyyan317 authored Jan 17, 2024
1 parent 12bbed1 commit 28c8fa2
Show file tree
Hide file tree
Showing 12 changed files with 279 additions and 83 deletions.
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ target_sources (clio PRIVATE
src/util/Random.cpp
src/util/Taggable.cpp
src/util/TerminationHandler.cpp
src/util/TxUtil.cpp
src/util/TxUtils.cpp
src/util/LedgerUtils.cpp
)

# Clio server
Expand Down Expand Up @@ -194,6 +195,7 @@ if (tests)
unittests/util/TxUtilTests.cpp
unittests/util/TestObject.cpp
unittests/util/StringUtils.cpp
unittests/util/LedgerUtilsTests.cpp
unittests/util/prometheus/CounterTests.cpp
unittests/util/prometheus/GaugeTests.cpp
unittests/util/prometheus/HistogramTests.cpp
Expand Down
38 changes: 4 additions & 34 deletions src/rpc/handlers/AccountObjects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "rpc/JS.h"
#include "rpc/RPCHelpers.h"
#include "rpc/common/Types.h"
#include "util/LedgerUtils.h"

#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
Expand All @@ -47,30 +48,6 @@

namespace rpc {

// found here : https://xrpl.org/ledger_entry.html#:~:text=valid%20fields%20are%3A-,index,-account_root
std::unordered_map<std::string, ripple::LedgerEntryType> const AccountObjectsHandler::TYPES_MAP{
{JS(amm), ripple::ltAMM},
{JS(state), ripple::ltRIPPLE_STATE},
{JS(ticket), ripple::ltTICKET},
{JS(signer_list), ripple::ltSIGNER_LIST},
{JS(payment_channel), ripple::ltPAYCHAN},
{JS(offer), ripple::ltOFFER},
{JS(escrow), ripple::ltESCROW},
{JS(deposit_preauth), ripple::ltDEPOSIT_PREAUTH},
{JS(check), ripple::ltCHECK},
{JS(nft_page), ripple::ltNFTOKEN_PAGE},
{JS(nft_offer), ripple::ltNFTOKEN_OFFER},
{JS(did), ripple::ltDID},
};

std::unordered_set<std::string> const AccountObjectsHandler::TYPES_KEYS = [] {
std::unordered_set<std::string> keys;
std::transform(TYPES_MAP.begin(), TYPES_MAP.end(), std::inserter(keys, keys.begin()), [](auto const& pair) {
return pair.first;
});
return keys;
}();

AccountObjectsHandler::Result
AccountObjectsHandler::process(AccountObjectsHandler::Input input, Context const& ctx) const
{
Expand All @@ -93,16 +70,9 @@ AccountObjectsHandler::process(AccountObjectsHandler::Input input, Context const
auto typeFilter = std::optional<std::vector<ripple::LedgerEntryType>>{};

if (input.deletionBlockersOnly) {
static constexpr ripple::LedgerEntryType deletionBlockers[] = {
ripple::ltCHECK,
ripple::ltESCROW,
ripple::ltNFTOKEN_PAGE,
ripple::ltPAYCHAN,
ripple::ltRIPPLE_STATE,
};

typeFilter.emplace();
typeFilter->reserve(std::size(deletionBlockers));
auto const& deletionBlockers = util::getDeletionBlockerLedgerTypes();
typeFilter->reserve(deletionBlockers.size());

for (auto type : deletionBlockers) {
if (input.type && input.type != type)
Expand Down Expand Up @@ -189,7 +159,7 @@ tag_invoke(boost::json::value_to_tag<AccountObjectsHandler::Input>, boost::json:
}

if (jsonObject.contains(JS(type)))
input.type = AccountObjectsHandler::TYPES_MAP.at(jv.at(JS(type)).as_string().c_str());
input.type = util::getLedgerEntryTypeFromStr(jv.at(JS(type)).as_string().c_str());

if (jsonObject.contains(JS(limit)))
input.limit = jv.at(JS(limit)).as_int64();
Expand Down
22 changes: 18 additions & 4 deletions src/rpc/handlers/AccountObjects.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,25 @@
#pragma once

#include "data/BackendInterface.h"
#include "rpc/RPCHelpers.h"
#include "rpc/JS.h"
#include "rpc/common/Modifiers.h"
#include "rpc/common/Types.h"
#include "rpc/common/Validators.h"

#include <set>
#include "util/LedgerUtils.h"

#include <boost/json/conversion.hpp>
#include <boost/json/value.hpp>
#include <ripple/protocol/LedgerFormats.h>
#include <ripple/protocol/STLedgerEntry.h>
#include <ripple/protocol/jss.h>

#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

namespace rpc {

Expand Down Expand Up @@ -80,6 +93,7 @@ class AccountObjectsHandler {
static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion)
{
auto const& ledgerTypeStrs = util::getLedgerEntryTypeStrs();
static auto const rpcSpec = RpcSpec{
{JS(account), validation::Required{}, validation::AccountValidator},
{JS(ledger_hash), validation::Uint256HexStringValidator},
Expand All @@ -90,7 +104,7 @@ class AccountObjectsHandler {
modifiers::Clamp<int32_t>(LIMIT_MIN, LIMIT_MAX)},
{JS(type),
validation::Type<std::string>{},
validation::OneOf<std::string>(TYPES_KEYS.cbegin(), TYPES_KEYS.cend())},
validation::OneOf<std::string>(ledgerTypeStrs.cbegin(), ledgerTypeStrs.cend())},
{JS(marker), validation::AccountMarkerValidator},
{JS(deletion_blockers_only), validation::Type<bool>{}},
};
Expand Down
2 changes: 1 addition & 1 deletion src/rpc/handlers/AccountTx.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#include "rpc/common/Modifiers.h"
#include "rpc/common/Types.h"
#include "rpc/common/Validators.h"
#include "util/TxUtil.h"
#include "util/TxUtils.h"
#include "util/log/Logger.h"

#include <boost/json/array.hpp>
Expand Down
35 changes: 2 additions & 33 deletions src/rpc/handlers/LedgerData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "rpc/JS.h"
#include "rpc/RPCHelpers.h"
#include "rpc/common/Types.h"
#include "util/LedgerUtils.h"
#include "util/log/Logger.h"

#include <boost/json/conversion.hpp>
Expand All @@ -42,45 +43,13 @@
#include <algorithm>
#include <chrono>
#include <cstddef>
#include <iterator>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <variant>
#include <vector>

namespace rpc {

std::unordered_map<std::string, ripple::LedgerEntryType> const LedgerDataHandler::TYPES_MAP{
{JS(account), ripple::ltACCOUNT_ROOT},
{JS(did), ripple::ltDID},
{JS(amendments), ripple::ltAMENDMENTS},
{JS(check), ripple::ltCHECK},
{JS(deposit_preauth), ripple::ltDEPOSIT_PREAUTH},
{JS(directory), ripple::ltDIR_NODE},
{JS(escrow), ripple::ltESCROW},
{JS(fee), ripple::ltFEE_SETTINGS},
{JS(hashes), ripple::ltLEDGER_HASHES},
{JS(offer), ripple::ltOFFER},
{JS(payment_channel), ripple::ltPAYCHAN},
{JS(signer_list), ripple::ltSIGNER_LIST},
{JS(state), ripple::ltRIPPLE_STATE},
{JS(ticket), ripple::ltTICKET},
{JS(nft_offer), ripple::ltNFTOKEN_OFFER},
{JS(nft_page), ripple::ltNFTOKEN_PAGE},
{JS(amm), ripple::ltAMM}
};

// TODO: should be std::views::keys when clang supports it
std::unordered_set<std::string> const LedgerDataHandler::TYPES_KEYS = [] {
std::unordered_set<std::string> keys;
std::transform(TYPES_MAP.begin(), TYPES_MAP.end(), std::inserter(keys, keys.begin()), [](auto const& pair) {
return pair.first;
});
return keys;
}();

LedgerDataHandler::Result
LedgerDataHandler::process(Input input, Context const& ctx) const
{
Expand Down Expand Up @@ -246,7 +215,7 @@ tag_invoke(boost::json::value_to_tag<LedgerDataHandler::Input>, boost::json::val
}

if (jsonObject.contains(JS(type)))
input.type = LedgerDataHandler::TYPES_MAP.at(jsonObject.at(JS(type)).as_string().c_str());
input.type = util::getLedgerEntryTypeFromStr(jsonObject.at(JS(type)).as_string().c_str());

return input;
}
Expand Down
28 changes: 20 additions & 8 deletions src/rpc/handlers/LedgerData.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,27 @@
#pragma once

#include "data/BackendInterface.h"
#include "rpc/RPCHelpers.h"
#include "rpc/Errors.h"
#include "rpc/JS.h"
#include "rpc/common/MetaProcessors.h"
#include "rpc/common/Types.h"
#include "rpc/common/Validators.h"

#include <unordered_map>
#include "util/LedgerUtils.h"
#include "util/log/Logger.h"

#include <boost/json/array.hpp>
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
#include <boost/json/value.hpp>
#include <ripple/basics/base_uint.h>
#include <ripple/protocol/ErrorCodes.h>
#include <ripple/protocol/LedgerFormats.h>
#include <ripple/protocol/jss.h>

#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <unordered_set>

namespace rpc {
Expand All @@ -41,10 +56,6 @@ class LedgerDataHandler {
std::shared_ptr<BackendInterface> sharedPtrBackend_;
util::Logger log_{"RPC"};

static std::unordered_map<std::string, ripple::LedgerEntryType> const TYPES_MAP;

static std::unordered_set<std::string> const TYPES_KEYS;

public:
// constants
static uint32_t constexpr LIMITBINARY = 2048;
Expand Down Expand Up @@ -84,6 +95,7 @@ class LedgerDataHandler {
static RpcSpecConstRef
spec([[maybe_unused]] uint32_t apiVersion)
{
auto const& ledgerTypeStrs = util::getLedgerEntryTypeStrs();
static auto const rpcSpec = RpcSpec{
{JS(binary), validation::Type<bool>{}},
{"out_of_order", validation::Type<bool>{}},
Expand All @@ -97,7 +109,7 @@ class LedgerDataHandler {
meta::WithCustomError{
validation::Type<std::string>{}, Status{ripple::rpcINVALID_PARAMS, "Invalid field 'type', not string."}
},
validation::OneOf<std::string>(TYPES_KEYS.cbegin(), TYPES_KEYS.cend())},
validation::OneOf<std::string>(ledgerTypeStrs.cbegin(), ledgerTypeStrs.cend())},

};
return rpcSpec;
Expand Down
115 changes: 115 additions & 0 deletions src/util/LedgerUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//------------------------------------------------------------------------------
/*
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 "util/LedgerUtils.h"

#include "rpc/JS.h"

#include <ripple/protocol/LedgerFormats.h>
#include <ripple/protocol/jss.h>

#include <algorithm>
#include <iterator>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

namespace util {
namespace detail {

struct LedgerTypeAttributes {
ripple::LedgerEntryType type = ripple::ltANY;
bool deletionBlocker = false;

LedgerTypeAttributes(ripple::LedgerEntryType type, bool blocker = false) : type(type), deletionBlocker(blocker)
{
}
};

// Ledger entry type filter list, add new types here to support filtering for ledger_data and
// account_objects
static std::unordered_map<std::string, LedgerTypeAttributes> const LEDGER_TYPES_MAP{
{{JS(account), LedgerTypeAttributes(ripple::ltACCOUNT_ROOT)},
{JS(amendments), LedgerTypeAttributes(ripple::ltAMENDMENTS)},
{JS(check), LedgerTypeAttributes(ripple::ltCHECK, true)},
{JS(deposit_preauth), LedgerTypeAttributes(ripple::ltDEPOSIT_PREAUTH)},
{JS(directory), LedgerTypeAttributes(ripple::ltDIR_NODE)},
{JS(escrow), LedgerTypeAttributes(ripple::ltESCROW, true)},
{JS(fee), LedgerTypeAttributes(ripple::ltFEE_SETTINGS)},
{JS(hashes), LedgerTypeAttributes(ripple::ltLEDGER_HASHES)},
{JS(offer), LedgerTypeAttributes(ripple::ltOFFER)},
{JS(payment_channel), LedgerTypeAttributes(ripple::ltPAYCHAN, true)},
{JS(signer_list), LedgerTypeAttributes(ripple::ltSIGNER_LIST)},
{JS(state), LedgerTypeAttributes(ripple::ltRIPPLE_STATE, true)},
{JS(ticket), LedgerTypeAttributes(ripple::ltTICKET)},
{JS(nft_offer), LedgerTypeAttributes(ripple::ltNFTOKEN_OFFER)},
{JS(nft_page), LedgerTypeAttributes(ripple::ltNFTOKEN_PAGE, true)},
{JS(amm), LedgerTypeAttributes(ripple::ltAMM)},
{JS(bridge), LedgerTypeAttributes(ripple::ltBRIDGE, true)},
{JS(xchain_owned_claim_id), LedgerTypeAttributes(ripple::ltXCHAIN_OWNED_CLAIM_ID, true)},
{JS(xchain_owned_create_account_claim_id),
LedgerTypeAttributes(ripple::ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID, true)},
{JS(did), LedgerTypeAttributes(ripple::ltDID)}}
};
} // namespace detail

std::unordered_set<std::string> const&
getLedgerEntryTypeStrs()
{
static std::unordered_set<std::string> const typesKeys = []() {
std::unordered_set<std::string> keys;
std::transform(
detail::LEDGER_TYPES_MAP.begin(),
detail::LEDGER_TYPES_MAP.end(),
std::inserter(keys, keys.begin()),
[](auto const& item) { return item.first; }
);
return keys;
}();

return typesKeys;
}

ripple::LedgerEntryType
getLedgerEntryTypeFromStr(std::string const& entryName)
{
if (detail::LEDGER_TYPES_MAP.find(entryName) == detail::LEDGER_TYPES_MAP.end())
return ripple::ltANY;

return detail::LEDGER_TYPES_MAP.at(entryName).type;
}

std::vector<ripple::LedgerEntryType> const&
getDeletionBlockerLedgerTypes()
{
static std::vector<ripple::LedgerEntryType> const deletionBlockerLedgerTypes = []() {
// TODO: Move to std::ranges::views::filter when move to higher clang
auto ret = std::vector<ripple::LedgerEntryType>{};
std::for_each(detail::LEDGER_TYPES_MAP.cbegin(), detail::LEDGER_TYPES_MAP.cend(), [&ret](auto const& item) {
if (item.second.deletionBlocker)
ret.push_back(item.second.type);
});
return ret;
}();

return deletionBlockerLedgerTypes;
}

} // namespace util
Loading

0 comments on commit 28c8fa2

Please sign in to comment.