AccountTx filtering by transaction type (#851)

Fixes #685
This commit is contained in:
Alex Kremer
2023-09-18 18:52:00 +01:00
committed by GitHub
parent 83af5af3c6
commit 1846f629a5
10 changed files with 471 additions and 28 deletions

View File

@@ -19,10 +19,15 @@
#pragma once
#include <util/JsonUtils.h>
#include <ripple/protocol/jss.h>
/** @brief Helper macro for borrowing from ripple::jss static (J)son (S)trings. */
#define JS(x) ripple::jss::x.c_str()
/** @brief Access the lower case copy of a static (J)son (S)tring. */
#define JSL(x) util::toLower(JS(x))
/** @brief Provides access to (SF)ield name (S)trings. */
#define SFS(x) ripple::x.jsonName.c_str()

View File

@@ -141,6 +141,7 @@ accountFromStringStrict(std::string const& account)
else
return {};
}
std::pair<std::shared_ptr<ripple::STTx const>, std::shared_ptr<ripple::STObject const>>
deserializeTxPlusMeta(data::TransactionAndMetadata const& blobs)
{

View File

@@ -22,6 +22,9 @@
#include <rpc/common/Concepts.h>
#include <rpc/common/Specs.h>
#include <rpc/common/Types.h>
#include <util/JsonUtils.h>
#include <string_view>
namespace rpc::modifiers {
@@ -68,4 +71,32 @@ public:
}
};
/**
* @brief Convert input string to lower case.
*
* Note: the conversion is only performed if the input value is a string.
*/
struct ToLower final
{
/**
* @brief Update the input string to lower case.
*
* @param value The JSON value representing the outer object
* @param key The key used to retrieve the modified value from the outer object
* @return Possibly an error
*/
[[nodiscard]] MaybeError
modify(boost::json::value& value, std::string_view key) const
{
if (not value.is_object() or not value.as_object().contains(key.data()))
return {}; // ignore. field does not exist, let 'required' fail instead
if (not value.as_object().at(key.data()).is_string())
return {}; // ignore for non-string types
value.as_object()[key.data()] = util::toLower(value.as_object().at(key.data()).as_string().c_str());
return {};
}
};
} // namespace rpc::modifiers

View File

@@ -21,6 +21,7 @@
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::TYPESMAP{
{"state", ripple::ltRIPPLE_STATE},
{"ticket", ripple::ltTICKET},

View File

@@ -18,10 +18,50 @@
//==============================================================================
#include <rpc/handlers/AccountTx.h>
#include <util/JsonUtils.h>
#include <util/Profiler.h>
namespace rpc {
// found here : https://xrpl.org/transaction-types.html
// TODO [https://github.com/XRPLF/clio/issues/856]: add AMMBid, AMMCreate, AMMDelete, AMMDeposit, AMMVote, AMMWithdraw
std::unordered_map<std::string, ripple::TxType> const AccountTxHandler::TYPESMAP{
{JSL(AccountSet), ripple::ttACCOUNT_SET},
{JSL(AccountDelete), ripple::ttACCOUNT_DELETE},
{JSL(CheckCancel), ripple::ttCHECK_CANCEL},
{JSL(CheckCash), ripple::ttCHECK_CASH},
{JSL(CheckCreate), ripple::ttCHECK_CREATE},
{JSL(Clawback), ripple::ttCLAWBACK},
{JSL(DepositPreauth), ripple::ttDEPOSIT_PREAUTH},
{JSL(EscrowCancel), ripple::ttESCROW_CANCEL},
{JSL(EscrowCreate), ripple::ttESCROW_CREATE},
{JSL(EscrowFinish), ripple::ttESCROW_FINISH},
{JSL(NFTokenAcceptOffer), ripple::ttNFTOKEN_ACCEPT_OFFER},
{JSL(NFTokenBurn), ripple::ttNFTOKEN_BURN},
{JSL(NFTokenCancelOffer), ripple::ttNFTOKEN_CANCEL_OFFER},
{JSL(NFTokenCreateOffer), ripple::ttNFTOKEN_CREATE_OFFER},
{JSL(NFTokenMint), ripple::ttNFTOKEN_MINT},
{JSL(OfferCancel), ripple::ttOFFER_CANCEL},
{JSL(OfferCreate), ripple::ttOFFER_CREATE},
{JSL(Payment), ripple::ttPAYMENT},
{JSL(PaymentChannelClaim), ripple::ttPAYCHAN_CLAIM},
{JSL(PaymentChannelCreate), ripple::ttCHECK_CREATE},
{JSL(PaymentChannelFund), ripple::ttPAYCHAN_FUND},
{JSL(SetRegularKey), ripple::ttREGULAR_KEY_SET},
{JSL(SignerListSet), ripple::ttSIGNER_LIST_SET},
{JSL(TicketCreate), ripple::ttTICKET_CREATE},
{JSL(TrustSet), ripple::ttTRUST_SET},
};
// TODO: should be std::views::keys when clang supports it
std::unordered_set<std::string> const AccountTxHandler::TYPES_KEYS = [] {
std::unordered_set<std::string> keys;
std::transform(TYPESMAP.begin(), TYPESMAP.end(), std::inserter(keys, keys.begin()), [](auto const& pair) {
return pair.first;
});
return keys;
}();
// 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
@@ -116,6 +156,18 @@ AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx) con
auto [txn, meta] = toExpandedJson(txnPlusMeta, NFTokenjson::ENABLE);
obj[JS(meta)] = std::move(meta);
obj[JS(tx)] = std::move(txn);
if (obj[JS(tx)].as_object().contains(JS(TransactionType)))
{
auto const objTransactionType = obj[JS(tx)].as_object()[JS(TransactionType)];
auto const strType = util::toLower(objTransactionType.as_string().c_str());
// if transactionType does not match
if (input.transactionType.has_value() && AccountTxHandler::TYPESMAP.contains(strType) &&
AccountTxHandler::TYPESMAP.at(strType) != input.transactionType.value())
continue;
}
obj[JS(tx)].as_object()[JS(ledger_index)] = txnPlusMeta.ledgerSequence;
obj[JS(tx)].as_object()[JS(date)] = txnPlusMeta.date;
}
@@ -208,6 +260,12 @@ tag_invoke(boost::json::value_to_tag<AccountTxHandler::Input>, boost::json::valu
jsonObject.at(JS(marker)).as_object().at(JS(ledger)).as_int64(),
jsonObject.at(JS(marker)).as_object().at(JS(seq)).as_int64()};
if (jsonObject.contains("tx_type"))
{
auto objTransactionType = jsonObject.at("tx_type");
input.transactionType = AccountTxHandler::TYPESMAP.at(objTransactionType.as_string().c_str());
}
return input;
}

View File

@@ -39,6 +39,9 @@ class AccountTxHandler
util::Logger log_{"RPC"};
std::shared_ptr<BackendInterface> sharedPtrBackend_;
static std::unordered_map<std::string, ripple::TxType> const TYPESMAP;
static const std::unordered_set<std::string> TYPES_KEYS;
public:
// no max limit
static auto constexpr LIMIT_MIN = 1;
@@ -77,6 +80,7 @@ public:
bool forward = false;
std::optional<uint32_t> limit;
std::optional<Marker> marker;
std::optional<ripple::TxType> transactionType;
};
using Result = HandlerReturnType<Output>;
@@ -109,6 +113,12 @@ public:
{JS(ledger), validation::Required{}, validation::Type<uint32_t>{}},
{JS(seq), validation::Required{}, validation::Type<uint32_t>{}},
}},
{
"tx_type",
validation::Type<std::string>{},
modifiers::ToLower{},
validation::OneOf<std::string>(TYPES_KEYS.cbegin(), TYPES_KEYS.cend()),
},
};
return rpcSpec;

View File

@@ -21,6 +21,8 @@
#include <boost/json.hpp>
#include <algorithm>
#include <cctype>
#include <string>
/**
@@ -28,6 +30,13 @@
*/
namespace util {
inline std::string
toLower(std::string str)
{
std::transform(std::begin(str), std::end(str), std::begin(str), [](unsigned char c) { return std::tolower(c); });
return str;
}
/**
* @brief Removes any detected secret information from a response JSON object.
*