mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-15 17:25:51 +00:00
@@ -1026,13 +1026,13 @@ public:
|
|||||||
isTooBusy() const override;
|
isTooBusy() const override;
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
incremementOutstandingRequestCount() const
|
incrementOutstandingRequestCount() const
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lck(throttleMutex_);
|
std::unique_lock<std::mutex> lck(throttleMutex_);
|
||||||
if (!canAddRequest())
|
if (!canAddRequest())
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(info)
|
BOOST_LOG_TRIVIAL(debug)
|
||||||
<< __func__ << " : "
|
<< __func__ << " : "
|
||||||
<< "Max outstanding requests reached. "
|
<< "Max outstanding requests reached. "
|
||||||
<< "Waiting for other requests to finish";
|
<< "Waiting for other requests to finish";
|
||||||
@@ -1109,7 +1109,7 @@ public:
|
|||||||
bool isRetry) const
|
bool isRetry) const
|
||||||
{
|
{
|
||||||
if (!isRetry)
|
if (!isRetry)
|
||||||
incremementOutstandingRequestCount();
|
incrementOutstandingRequestCount();
|
||||||
executeAsyncHelper(statement, callback, callbackData);
|
executeAsyncHelper(statement, callback, callbackData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -202,6 +202,9 @@ ReportingETL::publishLedger(ripple::LedgerInfo const& lgrInfo)
|
|||||||
|
|
||||||
for (auto& txAndMeta : transactions)
|
for (auto& txAndMeta : transactions)
|
||||||
subscriptions_->pubTransaction(txAndMeta, lgrInfo);
|
subscriptions_->pubTransaction(txAndMeta, lgrInfo);
|
||||||
|
|
||||||
|
subscriptions_->pubBookChanges(lgrInfo, transactions);
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << __func__ << " - Published ledger "
|
BOOST_LOG_TRIVIAL(info) << __func__ << " - Published ledger "
|
||||||
<< std::to_string(lgrInfo.seq);
|
<< std::to_string(lgrInfo.seq);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ Result
|
|||||||
doChannelVerify(Context const& context);
|
doChannelVerify(Context const& context);
|
||||||
|
|
||||||
// book methods
|
// book methods
|
||||||
Result
|
[[nodiscard]] Result
|
||||||
doBookChanges(Context const& context);
|
doBookChanges(Context const& context);
|
||||||
|
|
||||||
Result
|
Result
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <rpc/Counters.h>
|
#include <rpc/Counters.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file contains various classes necessary for executing RPC handlers.
|
* This file contains various classes necessary for executing RPC handlers.
|
||||||
* Context gives the handlers access to various other parts of the application
|
* Context gives the handlers access to various other parts of the application
|
||||||
|
|||||||
@@ -270,5 +270,10 @@ traverseTransactions(
|
|||||||
std::optional<Backend::TransactionsCursor> const&,
|
std::optional<Backend::TransactionsCursor> const&,
|
||||||
boost::asio::yield_context& yield)> transactionFetcher);
|
boost::asio::yield_context& yield)> transactionFetcher);
|
||||||
|
|
||||||
|
[[nodiscard]] boost::json::object const
|
||||||
|
computeBookChanges(
|
||||||
|
ripple::LedgerInfo const& lgrInfo,
|
||||||
|
std::vector<Backend::TransactionAndMetadata> const& transactions);
|
||||||
|
|
||||||
} // namespace RPC
|
} // namespace RPC
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ using namespace ripple;
|
|||||||
|
|
||||||
namespace RPC {
|
namespace RPC {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents an entry in the book_changes' changes array.
|
||||||
|
*/
|
||||||
struct BookChange
|
struct BookChange
|
||||||
{
|
{
|
||||||
STAmount sideAVolume;
|
STAmount sideAVolume;
|
||||||
@@ -22,44 +25,39 @@ struct BookChange
|
|||||||
STAmount closeRate;
|
STAmount closeRate;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BookChangesHandler
|
/**
|
||||||
|
* @brief Encapsulates the book_changes computations and transformations.
|
||||||
|
*/
|
||||||
|
class BookChanges final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BookChanges() = delete; // only accessed via static handle function
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Computes all book_changes for the given transactions.
|
||||||
|
*
|
||||||
|
* @param transactions The transactions to compute book changes for
|
||||||
|
* @return std::vector<BookChange> Book changes
|
||||||
|
*/
|
||||||
|
[[nodiscard]] static std::vector<BookChange>
|
||||||
|
compute(std::vector<Backend::TransactionAndMetadata> const& transactions)
|
||||||
|
{
|
||||||
|
return HandlerImpl{}(transactions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
class HandlerImpl final
|
||||||
{
|
{
|
||||||
std::reference_wrapper<Context const> context_;
|
|
||||||
std::map<std::string, BookChange> tally_ = {};
|
std::map<std::string, BookChange> tally_ = {};
|
||||||
std::optional<uint32_t> offerCancel_ = {};
|
std::optional<uint32_t> offerCancel_ = {};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~BookChangesHandler() = default;
|
[[nodiscard]] std::vector<BookChange>
|
||||||
explicit BookChangesHandler(Context const& context)
|
operator()(
|
||||||
: context_{std::cref(context)}
|
std::vector<Backend::TransactionAndMetadata> const& transactions)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
BookChangesHandler(BookChangesHandler const&) = delete;
|
|
||||||
BookChangesHandler(BookChangesHandler&&) = delete;
|
|
||||||
BookChangesHandler&
|
|
||||||
operator=(BookChangesHandler const&) = delete;
|
|
||||||
BookChangesHandler&
|
|
||||||
operator=(BookChangesHandler&&) = delete;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Handles the `book_change` request for given transactions
|
|
||||||
*
|
|
||||||
* @param transactions The transactions to compute changes for
|
|
||||||
* @return std::vector<BookChange> The changes
|
|
||||||
*/
|
|
||||||
std::vector<BookChange>
|
|
||||||
handle(LedgerInfo const& ledger)
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
|
|
||||||
for (auto const transactions =
|
|
||||||
context_.get().backend->fetchAllTransactionsInLedger(
|
|
||||||
ledger.seq, context_.get().yield);
|
|
||||||
auto const& tx : transactions)
|
|
||||||
{
|
{
|
||||||
|
for (auto const& tx : transactions)
|
||||||
handleBookChange(tx);
|
handleBookChange(tx);
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: rewrite this with std::ranges when compilers catch up
|
// TODO: rewrite this with std::ranges when compilers catch up
|
||||||
std::vector<BookChange> changes;
|
std::vector<BookChange> changes;
|
||||||
@@ -72,13 +70,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline void
|
|
||||||
reset() noexcept
|
|
||||||
{
|
|
||||||
tally_.clear();
|
|
||||||
offerCancel_ = std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
handleAffectedNode(STObject const& node)
|
handleAffectedNode(STObject const& node)
|
||||||
{
|
{
|
||||||
@@ -121,6 +112,14 @@ private:
|
|||||||
auto const deltaPays = finalFields.getFieldAmount(sfTakerPays) -
|
auto const deltaPays = finalFields.getFieldAmount(sfTakerPays) -
|
||||||
previousFields.getFieldAmount(sfTakerPays);
|
previousFields.getFieldAmount(sfTakerPays);
|
||||||
|
|
||||||
|
transformAndStore(deltaGets, deltaPays);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
transformAndStore(
|
||||||
|
ripple::STAmount const& deltaGets,
|
||||||
|
ripple::STAmount const& deltaPays)
|
||||||
|
{
|
||||||
auto const g = to_string(deltaGets.issue());
|
auto const g = to_string(deltaGets.issue());
|
||||||
auto const p = to_string(deltaPays.issue());
|
auto const p = to_string(deltaPays.issue());
|
||||||
|
|
||||||
@@ -200,6 +199,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
tag_invoke(
|
tag_invoke(
|
||||||
@@ -228,6 +228,20 @@ tag_invoke(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
json::object const
|
||||||
|
computeBookChanges(
|
||||||
|
ripple::LedgerInfo const& lgrInfo,
|
||||||
|
std::vector<Backend::TransactionAndMetadata> const& transactions)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
{JS(type), "bookChanges"},
|
||||||
|
{JS(ledger_index), lgrInfo.seq},
|
||||||
|
{JS(ledger_hash), to_string(lgrInfo.hash)},
|
||||||
|
{JS(ledger_time), lgrInfo.closeTime.time_since_epoch().count()},
|
||||||
|
{JS(changes), json::value_from(BookChanges::compute(transactions))},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Result
|
Result
|
||||||
doBookChanges(Context const& context)
|
doBookChanges(Context const& context)
|
||||||
{
|
{
|
||||||
@@ -237,14 +251,9 @@ doBookChanges(Context const& context)
|
|||||||
return *status;
|
return *status;
|
||||||
|
|
||||||
auto const lgrInfo = std::get<ripple::LedgerInfo>(info);
|
auto const lgrInfo = std::get<ripple::LedgerInfo>(info);
|
||||||
auto const changes = BookChangesHandler{context}.handle(lgrInfo);
|
auto const transactions = context.backend->fetchAllTransactionsInLedger(
|
||||||
return json::object{
|
lgrInfo.seq, context.yield);
|
||||||
{JS(type), "bookChanges"},
|
return computeBookChanges(lgrInfo, transactions);
|
||||||
{JS(ledger_index), lgrInfo.seq},
|
|
||||||
{JS(ledger_hash), to_string(lgrInfo.hash)},
|
|
||||||
{JS(ledger_time), lgrInfo.closeTime.time_since_epoch().count()},
|
|
||||||
{JS(changes), json::value_from(changes)},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace RPC
|
} // namespace RPC
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ static std::unordered_set<std::string> validCommonStreams{
|
|||||||
"transactions",
|
"transactions",
|
||||||
"transactions_proposed",
|
"transactions_proposed",
|
||||||
"validations",
|
"validations",
|
||||||
"manifests"};
|
"manifests",
|
||||||
|
"book_changes"};
|
||||||
|
|
||||||
Status
|
Status
|
||||||
validateStreams(boost::json::object const& request)
|
validateStreams(boost::json::object const& request)
|
||||||
@@ -57,6 +58,8 @@ subscribeToStreams(
|
|||||||
manager.subValidation(session);
|
manager.subValidation(session);
|
||||||
else if (s == "manifests")
|
else if (s == "manifests")
|
||||||
manager.subManifest(session);
|
manager.subManifest(session);
|
||||||
|
else if (s == "book_changes")
|
||||||
|
manager.subBookChanges(session);
|
||||||
else
|
else
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
@@ -85,6 +88,8 @@ unsubscribeToStreams(
|
|||||||
manager.unsubValidation(session);
|
manager.unsubValidation(session);
|
||||||
else if (s == "manifests")
|
else if (s == "manifests")
|
||||||
manager.unsubManifest(session);
|
manager.unsubManifest(session);
|
||||||
|
else if (s == "book_changes")
|
||||||
|
manager.unsubBookChanges(session);
|
||||||
else
|
else
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ Subscription::unsubscribe(std::shared_ptr<WsBase> const& session)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Subscription::publish(std::shared_ptr<Message>& message)
|
Subscription::publish(std::shared_ptr<Message> const& message)
|
||||||
{
|
{
|
||||||
boost::asio::post(strand_, [this, message]() {
|
boost::asio::post(strand_, [this, message]() {
|
||||||
sendToSubscribers(message, subscribers_, subCount_);
|
sendToSubscribers(message, subscribers_, subCount_);
|
||||||
@@ -235,6 +235,22 @@ SubscriptionManager::unsubBook(
|
|||||||
bookSubscribers_.unsubscribe(session, book);
|
bookSubscribers_.unsubscribe(session, book);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SubscriptionManager::subBookChanges(std::shared_ptr<WsBase> session)
|
||||||
|
{
|
||||||
|
bookChangesSubscribers_.subscribe(session);
|
||||||
|
|
||||||
|
std::unique_lock lk(cleanupMtx_);
|
||||||
|
cleanupFuncs_[session].emplace_back(
|
||||||
|
[this](session_ptr session) { unsubBookChanges(session); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SubscriptionManager::unsubBookChanges(std::shared_ptr<WsBase> session)
|
||||||
|
{
|
||||||
|
bookChangesSubscribers_.unsubscribe(session);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SubscriptionManager::pubLedger(
|
SubscriptionManager::pubLedger(
|
||||||
ripple::LedgerInfo const& lgrInfo,
|
ripple::LedgerInfo const& lgrInfo,
|
||||||
@@ -345,6 +361,20 @@ SubscriptionManager::pubTransaction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SubscriptionManager::pubBookChanges(
|
||||||
|
ripple::LedgerInfo const& lgrInfo,
|
||||||
|
std::vector<Backend::TransactionAndMetadata> const& transactions)
|
||||||
|
{
|
||||||
|
if (bookChangesSubscribers_.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto const json = RPC::computeBookChanges(lgrInfo, transactions);
|
||||||
|
auto const bookChangesMsg =
|
||||||
|
std::make_shared<Message>(boost::json::serialize(json));
|
||||||
|
bookChangesSubscribers_.publish(bookChangesMsg);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SubscriptionManager::forwardProposedTransaction(
|
SubscriptionManager::forwardProposedTransaction(
|
||||||
boost::json::object const& response)
|
boost::json::object const& response)
|
||||||
|
|||||||
@@ -31,13 +31,19 @@ public:
|
|||||||
unsubscribe(std::shared_ptr<WsBase> const& session);
|
unsubscribe(std::shared_ptr<WsBase> const& session);
|
||||||
|
|
||||||
void
|
void
|
||||||
publish(std::shared_ptr<Message>& message);
|
publish(std::shared_ptr<Message> const& message);
|
||||||
|
|
||||||
std::uint64_t
|
std::uint64_t
|
||||||
count()
|
count() const
|
||||||
{
|
{
|
||||||
return subCount_.load();
|
return subCount_.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
empty() const
|
||||||
|
{
|
||||||
|
return count() == 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class Key>
|
template <class Key>
|
||||||
@@ -90,6 +96,7 @@ class SubscriptionManager
|
|||||||
Subscription txProposedSubscribers_;
|
Subscription txProposedSubscribers_;
|
||||||
Subscription manifestSubscribers_;
|
Subscription manifestSubscribers_;
|
||||||
Subscription validationsSubscribers_;
|
Subscription validationsSubscribers_;
|
||||||
|
Subscription bookChangesSubscribers_;
|
||||||
|
|
||||||
SubscriptionMap<ripple::AccountID> accountSubscribers_;
|
SubscriptionMap<ripple::AccountID> accountSubscribers_;
|
||||||
SubscriptionMap<ripple::AccountID> accountProposedSubscribers_;
|
SubscriptionMap<ripple::AccountID> accountProposedSubscribers_;
|
||||||
@@ -122,6 +129,7 @@ public:
|
|||||||
, txProposedSubscribers_(ioc_)
|
, txProposedSubscribers_(ioc_)
|
||||||
, manifestSubscribers_(ioc_)
|
, manifestSubscribers_(ioc_)
|
||||||
, validationsSubscribers_(ioc_)
|
, validationsSubscribers_(ioc_)
|
||||||
|
, bookChangesSubscribers_(ioc_)
|
||||||
, accountSubscribers_(ioc_)
|
, accountSubscribers_(ioc_)
|
||||||
, accountProposedSubscribers_(ioc_)
|
, accountProposedSubscribers_(ioc_)
|
||||||
, bookSubscribers_(ioc_)
|
, bookSubscribers_(ioc_)
|
||||||
@@ -159,6 +167,11 @@ public:
|
|||||||
std::string const& ledgerRange,
|
std::string const& ledgerRange,
|
||||||
std::uint32_t txnCount);
|
std::uint32_t txnCount);
|
||||||
|
|
||||||
|
void
|
||||||
|
pubBookChanges(
|
||||||
|
ripple::LedgerInfo const& lgrInfo,
|
||||||
|
std::vector<Backend::TransactionAndMetadata> const& transactions);
|
||||||
|
|
||||||
void
|
void
|
||||||
unsubLedger(session_ptr session);
|
unsubLedger(session_ptr session);
|
||||||
|
|
||||||
@@ -185,6 +198,12 @@ public:
|
|||||||
void
|
void
|
||||||
unsubBook(ripple::Book const& book, session_ptr session);
|
unsubBook(ripple::Book const& book, session_ptr session);
|
||||||
|
|
||||||
|
void
|
||||||
|
subBookChanges(std::shared_ptr<WsBase> session);
|
||||||
|
|
||||||
|
void
|
||||||
|
unsubBookChanges(std::shared_ptr<WsBase> session);
|
||||||
|
|
||||||
void
|
void
|
||||||
subManifest(session_ptr session);
|
subManifest(session_ptr session);
|
||||||
|
|
||||||
@@ -234,6 +253,7 @@ public:
|
|||||||
counts["account"] = accountSubscribers_.count();
|
counts["account"] = accountSubscribers_.count();
|
||||||
counts["accounts_proposed"] = accountProposedSubscribers_.count();
|
counts["accounts_proposed"] = accountProposedSubscribers_.count();
|
||||||
counts["books"] = bookSubscribers_.count();
|
counts["books"] = bookSubscribers_.count();
|
||||||
|
counts["book_changes"] = bookChangesSubscribers_.count();
|
||||||
|
|
||||||
return counts;
|
return counts;
|
||||||
}
|
}
|
||||||
|
|||||||
1
test.py
1
test.py
@@ -836,6 +836,7 @@ async def subscribe(ip, port):
|
|||||||
try:
|
try:
|
||||||
async with websockets.connect(address) as ws:
|
async with websockets.connect(address) as ws:
|
||||||
await ws.send(json.dumps({"command":"subscribe","streams":["ledger"]}))
|
await ws.send(json.dumps({"command":"subscribe","streams":["ledger"]}))
|
||||||
|
#await ws.send(json.dumps({"command":"subscribe","streams":["book_changes"]}))
|
||||||
#await ws.send(json.dumps({"command":"subscribe","streams":["manifests"]}))
|
#await ws.send(json.dumps({"command":"subscribe","streams":["manifests"]}))
|
||||||
while True:
|
while True:
|
||||||
res = json.loads(await ws.recv())
|
res = json.loads(await ws.recv())
|
||||||
|
|||||||
Reference in New Issue
Block a user