mirror of
https://github.com/Xahau/xahaud.git
synced 2026-06-07 10:46:42 +00:00
add strict filtering to account_tx api
This commit is contained in:
@@ -69,6 +69,7 @@ public:
|
||||
std::uint32_t offset;
|
||||
std::uint32_t limit;
|
||||
bool bUnlimited;
|
||||
bool strict;
|
||||
};
|
||||
|
||||
struct AccountTxPageOptions
|
||||
@@ -79,6 +80,7 @@ public:
|
||||
std::optional<AccountTxMarker> marker;
|
||||
std::uint32_t limit;
|
||||
bool bAdmin;
|
||||
bool strict;
|
||||
};
|
||||
|
||||
using AccountTx =
|
||||
@@ -101,6 +103,7 @@ public:
|
||||
bool forward = false;
|
||||
uint32_t limit = 0;
|
||||
std::optional<AccountTxMarker> marker;
|
||||
bool strict;
|
||||
};
|
||||
|
||||
struct AccountTxResult
|
||||
|
||||
@@ -43,6 +43,34 @@ private:
|
||||
std::map<uint256, AccountTx> transactionMap_;
|
||||
std::map<AccountID, AccountTxData> accountTxMap_;
|
||||
|
||||
// helper function to scan for an account ID inside the tx and meta blobs
|
||||
// used for strict filtering of account_tx
|
||||
bool
|
||||
isAccountInvolvedInTx(AccountID const& account, AccountTx const& accountTx)
|
||||
{
|
||||
auto const& txn = accountTx.first;
|
||||
auto const& meta = accountTx.second;
|
||||
|
||||
// Search metadata
|
||||
Blob const metaBlob = meta->getAsObject().getSerializer().peekData();
|
||||
if (metaBlob.size() >= account.size() &&
|
||||
std::search(
|
||||
metaBlob.begin(),
|
||||
metaBlob.end(),
|
||||
account.data(),
|
||||
account.data() + account.size()) != metaBlob.end())
|
||||
return true;
|
||||
|
||||
// Search transaction blob
|
||||
Blob const txnBlob = txn->getSTransaction()->getSerializer().peekData();
|
||||
return txnBlob.size() >= account.size() &&
|
||||
std::search(
|
||||
txnBlob.begin(),
|
||||
txnBlob.end(),
|
||||
account.data(),
|
||||
account.data() + account.size()) != txnBlob.end();
|
||||
}
|
||||
|
||||
public:
|
||||
RWDBDatabase(Application& app, Config const& config, JobQueue& jobQueue)
|
||||
: app_(app), useTxTables_(config.useTxTables())
|
||||
@@ -193,7 +221,17 @@ public:
|
||||
std::size_t count = 0;
|
||||
for (const auto& [_, accountData] : accountTxMap_)
|
||||
{
|
||||
count += accountData.transactions.size();
|
||||
for (const auto& tx : accountData.transactions)
|
||||
{
|
||||
// RH NOTE: options isn't provided to this function
|
||||
// but this function is probably only used internally
|
||||
// so make it reflect the true number (unfiltered)
|
||||
|
||||
// if (options.strict &&
|
||||
// !isAccountInvolvedInTx(options.account, tx))
|
||||
// continue;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
@@ -607,12 +645,17 @@ public:
|
||||
{
|
||||
for (const auto& [txSeq, txIndex] : txIt->second)
|
||||
{
|
||||
AccountTx const accountTx = accountData.transactions[txIndex];
|
||||
if (options.strict &&
|
||||
!isAccountInvolvedInTx(options.account, accountTx))
|
||||
continue;
|
||||
|
||||
if (skipped < options.offset)
|
||||
{
|
||||
++skipped;
|
||||
continue;
|
||||
}
|
||||
AccountTx const accountTx = accountData.transactions[txIndex];
|
||||
|
||||
std::uint32_t const inLedger = rangeCheckedCast<std::uint32_t>(
|
||||
accountTx.second->getLgrSeq());
|
||||
accountTx.first->setStatus(COMMITTED);
|
||||
@@ -652,13 +695,18 @@ public:
|
||||
innerRIt != rIt->second.rend();
|
||||
++innerRIt)
|
||||
{
|
||||
AccountTx const accountTx =
|
||||
accountData.transactions[innerRIt->second];
|
||||
|
||||
if (options.strict &&
|
||||
!isAccountInvolvedInTx(options.account, accountTx))
|
||||
continue;
|
||||
|
||||
if (skipped < options.offset)
|
||||
{
|
||||
++skipped;
|
||||
continue;
|
||||
}
|
||||
AccountTx const accountTx =
|
||||
accountData.transactions[innerRIt->second];
|
||||
std::uint32_t const inLedger = rangeCheckedCast<std::uint32_t>(
|
||||
accountTx.second->getLgrSeq());
|
||||
accountTx.first->setLedger(inLedger);
|
||||
@@ -694,12 +742,19 @@ public:
|
||||
{
|
||||
for (const auto& [txSeq, txIndex] : txIt->second)
|
||||
{
|
||||
AccountTx const accountTx = accountData.transactions[txIndex];
|
||||
|
||||
if (options.strict &&
|
||||
!isAccountInvolvedInTx(options.account, accountTx))
|
||||
continue;
|
||||
|
||||
const auto& [txn, txMeta] = accountTx;
|
||||
|
||||
if (skipped < options.offset)
|
||||
{
|
||||
++skipped;
|
||||
continue;
|
||||
}
|
||||
const auto& [txn, txMeta] = accountData.transactions[txIndex];
|
||||
result.emplace_back(
|
||||
txn->getSTransaction()->getSerializer().peekData(),
|
||||
txMeta->getAsObject().getSerializer().peekData(),
|
||||
@@ -738,13 +793,20 @@ public:
|
||||
innerRIt != rIt->second.rend();
|
||||
++innerRIt)
|
||||
{
|
||||
AccountTx const accountTx =
|
||||
accountData.transactions[innerRIt->second];
|
||||
|
||||
if (options.strict &&
|
||||
!isAccountInvolvedInTx(options.account, accountTx))
|
||||
continue;
|
||||
|
||||
const auto& [txn, txMeta] = accountTx;
|
||||
|
||||
if (skipped < options.offset)
|
||||
{
|
||||
++skipped;
|
||||
continue;
|
||||
}
|
||||
const auto& [txn, txMeta] =
|
||||
accountData.transactions[innerRIt->second];
|
||||
result.emplace_back(
|
||||
txn->getSTransaction()->getSerializer().peekData(),
|
||||
txMeta->getAsObject().getSerializer().peekData(),
|
||||
@@ -838,18 +900,23 @@ public:
|
||||
return {newmarker, total};
|
||||
}
|
||||
|
||||
Blob rawTxn = accountData.transactions[index]
|
||||
.first->getSTransaction()
|
||||
AccountTx const& accountTx =
|
||||
accountData.transactions[index];
|
||||
|
||||
Blob rawTxn = accountTx.first->getSTransaction()
|
||||
->getSerializer()
|
||||
.peekData();
|
||||
Blob rawMeta = accountData.transactions[index]
|
||||
.second->getAsObject()
|
||||
Blob rawMeta = accountTx.second->getAsObject()
|
||||
.getSerializer()
|
||||
.peekData();
|
||||
|
||||
if (rawMeta.size() == 0)
|
||||
onUnsavedLedger(ledgerSeq);
|
||||
|
||||
if (options.strict &&
|
||||
!isAccountInvolvedInTx(options.account, accountTx))
|
||||
continue;
|
||||
|
||||
onTransaction(
|
||||
rangeCheckedCast<std::uint32_t>(ledgerSeq),
|
||||
"COMMITTED",
|
||||
@@ -893,18 +960,23 @@ public:
|
||||
return {newmarker, total};
|
||||
}
|
||||
|
||||
Blob rawTxn = accountData.transactions[index]
|
||||
.first->getSTransaction()
|
||||
AccountTx const& accountTx =
|
||||
accountData.transactions[index];
|
||||
|
||||
Blob rawTxn = accountTx.first->getSTransaction()
|
||||
->getSerializer()
|
||||
.peekData();
|
||||
Blob rawMeta = accountData.transactions[index]
|
||||
.second->getAsObject()
|
||||
Blob rawMeta = accountTx.second->getAsObject()
|
||||
.getSerializer()
|
||||
.peekData();
|
||||
|
||||
if (rawMeta.size() == 0)
|
||||
onUnsavedLedger(ledgerSeq);
|
||||
|
||||
if (options.strict &&
|
||||
!isAccountInvolvedInTx(options.account, accountTx))
|
||||
continue;
|
||||
|
||||
onTransaction(
|
||||
rangeCheckedCast<std::uint32_t>(ledgerSeq),
|
||||
"COMMITTED",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <ripple/app/rdb/backend/detail/Node.h>
|
||||
#include <ripple/basics/BasicConfig.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/basics/strHex.h>
|
||||
#include <ripple/core/DatabaseCon.h>
|
||||
#include <ripple/core/SociDB.h>
|
||||
#include <ripple/json/to_string.h>
|
||||
@@ -758,14 +759,24 @@ transactionsSQL(
|
||||
options.minLedger);
|
||||
}
|
||||
|
||||
// Convert account ID to hex string for binary search
|
||||
std::string accountHex =
|
||||
strHex(options.account.data(), options.account.size());
|
||||
|
||||
std::string sql;
|
||||
|
||||
std::string filterClause = options.strict ? "AND (hex(TxnMeta) LIKE '%" +
|
||||
accountHex + "%' OR hex(RawTxn) LIKE '%" + accountHex + "%')"
|
||||
: "";
|
||||
|
||||
if (count)
|
||||
sql = boost::str(
|
||||
boost::format("SELECT %s FROM AccountTransactions "
|
||||
"WHERE Account = '%s' %s %s LIMIT %u, %u;") %
|
||||
selection % toBase58(options.account) % maxClause % minClause %
|
||||
beast::lexicalCastThrow<std::string>(options.offset) %
|
||||
"INNER JOIN Transactions ON Transactions.TransID = "
|
||||
"AccountTransactions.TransID "
|
||||
"WHERE Account = '%s' %s %s %s LIMIT %u, %u;") %
|
||||
selection % toBase58(options.account) % filterClause % maxClause %
|
||||
minClause % beast::lexicalCastThrow<std::string>(options.offset) %
|
||||
beast::lexicalCastThrow<std::string>(numberOfResults));
|
||||
else
|
||||
sql = boost::str(
|
||||
@@ -773,15 +784,16 @@ transactionsSQL(
|
||||
"SELECT %s FROM "
|
||||
"AccountTransactions INNER JOIN Transactions "
|
||||
"ON Transactions.TransID = AccountTransactions.TransID "
|
||||
"WHERE Account = '%s' %s %s "
|
||||
"WHERE Account = '%s' %s %s %s "
|
||||
"ORDER BY AccountTransactions.LedgerSeq %s, "
|
||||
"AccountTransactions.TxnSeq %s, AccountTransactions.TransID %s "
|
||||
"LIMIT %u, %u;") %
|
||||
selection % toBase58(options.account) % maxClause % minClause %
|
||||
selection % toBase58(options.account) % filterClause % maxClause %
|
||||
minClause % (descending ? "DESC" : "ASC") %
|
||||
(descending ? "DESC" : "ASC") % (descending ? "DESC" : "ASC") %
|
||||
(descending ? "DESC" : "ASC") %
|
||||
beast::lexicalCastThrow<std::string>(options.offset) %
|
||||
beast::lexicalCastThrow<std::string>(numberOfResults));
|
||||
|
||||
JLOG(j.trace()) << "txSQL query: " << sql;
|
||||
return sql;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,17 @@ strHex(FwdIt begin, FwdIt end)
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class FwdIt>
|
||||
std::string
|
||||
strHex(FwdIt begin, std::size_t length)
|
||||
{
|
||||
std::string result;
|
||||
result.reserve(2 * length);
|
||||
boost::algorithm::hex(
|
||||
begin, std::next(begin, length), std::back_inserter(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T, class = decltype(std::declval<T>().begin())>
|
||||
std::string
|
||||
strHex(T const& from)
|
||||
|
||||
@@ -223,7 +223,8 @@ doAccountTxHelp(RPC::Context& context, AccountTxArgs const& args)
|
||||
result.ledgerRange.max,
|
||||
result.marker,
|
||||
args.limit,
|
||||
isUnlimited(context.role)};
|
||||
isUnlimited(context.role),
|
||||
args.strict};
|
||||
|
||||
auto const db =
|
||||
dynamic_cast<SQLiteDatabase*>(&context.app.getRelationalDatabase());
|
||||
@@ -369,6 +370,9 @@ doAccountTxJson(RPC::JsonContext& context)
|
||||
args.forward =
|
||||
params.isMember(jss::forward) && params[jss::forward].asBool();
|
||||
|
||||
args.strict =
|
||||
params.isMember(jss::strict) ? params[jss::strict].asBool() : true;
|
||||
|
||||
if (!params.isMember(jss::account))
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user