mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-26 14:05:51 +00:00
Incremental improvements to path finding memory usage:
* Abort background path finding when closed or disconnected * Exit pathfinding job thread if there are no requests left * Don't bother creating the path find job if there are no requests * Refactor to remove circular dependency between InfoSub and PathRequest
This commit is contained in:
committed by
Nik Bougalis
parent
4d5459d041
commit
e7e672c3f8
@@ -733,6 +733,7 @@ if (tests)
|
|||||||
src/test/basics/contract_test.cpp
|
src/test/basics/contract_test.cpp
|
||||||
src/test/basics/FeeUnits_test.cpp
|
src/test/basics/FeeUnits_test.cpp
|
||||||
src/test/basics/hardened_hash_test.cpp
|
src/test/basics/hardened_hash_test.cpp
|
||||||
|
src/test/basics/join_test.cpp
|
||||||
src/test/basics/mulDiv_test.cpp
|
src/test/basics/mulDiv_test.cpp
|
||||||
src/test/basics/tagged_integer_test.cpp
|
src/test/basics/tagged_integer_test.cpp
|
||||||
#[===============================[
|
#[===============================[
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#ifndef RIPPLE_APP_LEDGER_LEDGERHOLDER_H_INCLUDED
|
#ifndef RIPPLE_APP_LEDGER_LEDGERHOLDER_H_INCLUDED
|
||||||
#define RIPPLE_APP_LEDGER_LEDGERHOLDER_H_INCLUDED
|
#define RIPPLE_APP_LEDGER_LEDGERHOLDER_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/basics/CountedObject.h>
|
||||||
#include <ripple/basics/contract.h>
|
#include <ripple/basics/contract.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ namespace ripple {
|
|||||||
way the object always holds a value. We can use the
|
way the object always holds a value. We can use the
|
||||||
genesis ledger in all cases.
|
genesis ledger in all cases.
|
||||||
*/
|
*/
|
||||||
class LedgerHolder
|
class LedgerHolder : public CountedObject<LedgerHolder>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Update the held ledger
|
// Update the held ledger
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#ifndef RIPPLE_APP_LEDGER_LEDGERREPLAY_H_INCLUDED
|
#ifndef RIPPLE_APP_LEDGER_LEDGERREPLAY_H_INCLUDED
|
||||||
#define RIPPLE_APP_LEDGER_LEDGERREPLAY_H_INCLUDED
|
#define RIPPLE_APP_LEDGER_LEDGERREPLAY_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/basics/CountedObject.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -29,7 +30,7 @@ namespace ripple {
|
|||||||
class Ledger;
|
class Ledger;
|
||||||
class STTx;
|
class STTx;
|
||||||
|
|
||||||
class LedgerReplay
|
class LedgerReplay : public CountedObject<LedgerReplay>
|
||||||
{
|
{
|
||||||
std::shared_ptr<Ledger const> parent_;
|
std::shared_ptr<Ledger const> parent_;
|
||||||
std::shared_ptr<Ledger const> replay_;
|
std::shared_ptr<Ledger const> replay_;
|
||||||
|
|||||||
@@ -261,8 +261,13 @@ LedgerMaster::getPublishedLedgerAge()
|
|||||||
std::chrono::seconds ret = app_.timeKeeper().closeTime().time_since_epoch();
|
std::chrono::seconds ret = app_.timeKeeper().closeTime().time_since_epoch();
|
||||||
ret -= pubClose;
|
ret -= pubClose;
|
||||||
ret = (ret > 0s) ? ret : 0s;
|
ret = (ret > 0s) ? ret : 0s;
|
||||||
|
static std::chrono::seconds lastRet = -1s;
|
||||||
|
|
||||||
|
if (ret != lastRet)
|
||||||
|
{
|
||||||
JLOG(m_journal.trace()) << "Published ledger age is " << ret.count();
|
JLOG(m_journal.trace()) << "Published ledger age is " << ret.count();
|
||||||
|
lastRet = ret;
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,8 +292,13 @@ LedgerMaster::getValidatedLedgerAge()
|
|||||||
std::chrono::seconds ret = app_.timeKeeper().closeTime().time_since_epoch();
|
std::chrono::seconds ret = app_.timeKeeper().closeTime().time_since_epoch();
|
||||||
ret -= valClose;
|
ret -= valClose;
|
||||||
ret = (ret > 0s) ? ret : 0s;
|
ret = (ret > 0s) ? ret : 0s;
|
||||||
|
static std::chrono::seconds lastRet = -1s;
|
||||||
|
|
||||||
|
if (ret != lastRet)
|
||||||
|
{
|
||||||
JLOG(m_journal.trace()) << "Validated ledger age is " << ret.count();
|
JLOG(m_journal.trace()) << "Validated ledger age is " << ret.count();
|
||||||
|
lastRet = ret;
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1483,12 +1493,14 @@ LedgerMaster::updatePaths()
|
|||||||
if (app_.getOPs().isNeedNetworkLedger())
|
if (app_.getOPs().isNeedNetworkLedger())
|
||||||
{
|
{
|
||||||
--mPathFindThread;
|
--mPathFindThread;
|
||||||
|
JLOG(m_journal.debug()) << "Need network ledger for updating paths";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!app_.getJobQueue().isStopping())
|
while (!app_.getJobQueue().isStopping())
|
||||||
{
|
{
|
||||||
|
JLOG(m_journal.debug()) << "updatePaths running";
|
||||||
std::shared_ptr<ReadView const> lastLedger;
|
std::shared_ptr<ReadView const> lastLedger;
|
||||||
{
|
{
|
||||||
std::lock_guard ml(m_mutex);
|
std::lock_guard ml(m_mutex);
|
||||||
@@ -1506,6 +1518,7 @@ LedgerMaster::updatePaths()
|
|||||||
else
|
else
|
||||||
{ // Nothing to do
|
{ // Nothing to do
|
||||||
--mPathFindThread;
|
--mPathFindThread;
|
||||||
|
JLOG(m_journal.debug()) << "Nothing to do for updating paths";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1527,7 +1540,31 @@ LedgerMaster::updatePaths()
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
app_.getPathRequests().updateAll(lastLedger);
|
auto& pathRequests = app_.getPathRequests();
|
||||||
|
{
|
||||||
|
std::lock_guard ml(m_mutex);
|
||||||
|
if (!pathRequests.requestsPending())
|
||||||
|
{
|
||||||
|
--mPathFindThread;
|
||||||
|
JLOG(m_journal.debug())
|
||||||
|
<< "No path requests found. Nothing to do for updating "
|
||||||
|
"paths. "
|
||||||
|
<< mPathFindThread << " jobs remaining";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JLOG(m_journal.debug()) << "Updating paths";
|
||||||
|
pathRequests.updateAll(lastLedger);
|
||||||
|
|
||||||
|
std::lock_guard ml(m_mutex);
|
||||||
|
if (!pathRequests.requestsPending())
|
||||||
|
{
|
||||||
|
JLOG(m_journal.debug())
|
||||||
|
<< "No path requests left. No need for further updating "
|
||||||
|
"paths";
|
||||||
|
--mPathFindThread;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (SHAMapMissingNode const& mn)
|
catch (SHAMapMissingNode const& mn)
|
||||||
{
|
{
|
||||||
@@ -1587,8 +1624,11 @@ LedgerMaster::newPFWork(
|
|||||||
const char* name,
|
const char* name,
|
||||||
std::unique_lock<std::recursive_mutex>&)
|
std::unique_lock<std::recursive_mutex>&)
|
||||||
{
|
{
|
||||||
if (mPathFindThread < 2)
|
if (mPathFindThread < 2 && app_.getPathRequests().requestsPending())
|
||||||
{
|
{
|
||||||
|
JLOG(m_journal.debug())
|
||||||
|
<< "newPFWork: Creating job. path find threads: "
|
||||||
|
<< mPathFindThread;
|
||||||
if (app_.getJobQueue().addJob(
|
if (app_.getJobQueue().addJob(
|
||||||
jtUPDATE_PF, name, [this]() { updatePaths(); }))
|
jtUPDATE_PF, name, [this]() { updatePaths(); }))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#ifndef RIPPLE_APP_MISC_CANONICALTXSET_H_INCLUDED
|
#ifndef RIPPLE_APP_MISC_CANONICALTXSET_H_INCLUDED
|
||||||
#define RIPPLE_APP_MISC_CANONICALTXSET_H_INCLUDED
|
#define RIPPLE_APP_MISC_CANONICALTXSET_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/basics/CountedObject.h>
|
||||||
#include <ripple/protocol/RippleLedgerHash.h>
|
#include <ripple/protocol/RippleLedgerHash.h>
|
||||||
#include <ripple/protocol/STTx.h>
|
#include <ripple/protocol/STTx.h>
|
||||||
#include <ripple/protocol/SeqProxy.h>
|
#include <ripple/protocol/SeqProxy.h>
|
||||||
@@ -34,7 +35,7 @@ namespace ripple {
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
// VFALCO TODO rename to SortedTxSet
|
// VFALCO TODO rename to SortedTxSet
|
||||||
class CanonicalTXSet
|
class CanonicalTXSet : public CountedObject<CanonicalTXSet>
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
class Key
|
class Key
|
||||||
|
|||||||
@@ -20,10 +20,12 @@
|
|||||||
#ifndef RIPPLE_APP_MISC_ORDERBOOK_H_INCLUDED
|
#ifndef RIPPLE_APP_MISC_ORDERBOOK_H_INCLUDED
|
||||||
#define RIPPLE_APP_MISC_ORDERBOOK_H_INCLUDED
|
#define RIPPLE_APP_MISC_ORDERBOOK_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/basics/CountedObject.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
/** Describes a serialized ledger entry for an order book. */
|
/** Describes a serialized ledger entry for an order book. */
|
||||||
class OrderBook
|
class OrderBook : public CountedObject<OrderBook>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using pointer = std::shared_ptr<OrderBook>;
|
using pointer = std::shared_ptr<OrderBook>;
|
||||||
|
|||||||
@@ -441,7 +441,7 @@ PathRequest::parseJson(Json::Value const& jvParams)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Json::Value
|
Json::Value
|
||||||
PathRequest::doClose(Json::Value const&)
|
PathRequest::doClose()
|
||||||
{
|
{
|
||||||
JLOG(m_journal.debug()) << iIdentifier << " closed";
|
JLOG(m_journal.debug()) << iIdentifier << " closed";
|
||||||
std::lock_guard sl(mLock);
|
std::lock_guard sl(mLock);
|
||||||
@@ -457,13 +457,20 @@ PathRequest::doStatus(Json::Value const&)
|
|||||||
return jvStatus;
|
return jvStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PathRequest::doAborting() const
|
||||||
|
{
|
||||||
|
JLOG(m_journal.info()) << iIdentifier << " aborting early";
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<Pathfinder> const&
|
std::unique_ptr<Pathfinder> const&
|
||||||
PathRequest::getPathFinder(
|
PathRequest::getPathFinder(
|
||||||
std::shared_ptr<RippleLineCache> const& cache,
|
std::shared_ptr<RippleLineCache> const& cache,
|
||||||
hash_map<Currency, std::unique_ptr<Pathfinder>>& currency_map,
|
hash_map<Currency, std::unique_ptr<Pathfinder>>& currency_map,
|
||||||
Currency const& currency,
|
Currency const& currency,
|
||||||
STAmount const& dst_amount,
|
STAmount const& dst_amount,
|
||||||
int const level)
|
int const level,
|
||||||
|
std::function<bool(void)> const& continueCallback)
|
||||||
{
|
{
|
||||||
auto i = currency_map.find(currency);
|
auto i = currency_map.find(currency);
|
||||||
if (i != currency_map.end())
|
if (i != currency_map.end())
|
||||||
@@ -477,8 +484,8 @@ PathRequest::getPathFinder(
|
|||||||
dst_amount,
|
dst_amount,
|
||||||
saSendMax,
|
saSendMax,
|
||||||
app_);
|
app_);
|
||||||
if (pathfinder->findPaths(level))
|
if (pathfinder->findPaths(level, continueCallback))
|
||||||
pathfinder->computePathRanks(max_paths_);
|
pathfinder->computePathRanks(max_paths_, continueCallback);
|
||||||
else
|
else
|
||||||
pathfinder.reset(); // It's a bad request - clear it.
|
pathfinder.reset(); // It's a bad request - clear it.
|
||||||
return currency_map[currency] = std::move(pathfinder);
|
return currency_map[currency] = std::move(pathfinder);
|
||||||
@@ -488,7 +495,8 @@ bool
|
|||||||
PathRequest::findPaths(
|
PathRequest::findPaths(
|
||||||
std::shared_ptr<RippleLineCache> const& cache,
|
std::shared_ptr<RippleLineCache> const& cache,
|
||||||
int const level,
|
int const level,
|
||||||
Json::Value& jvArray)
|
Json::Value& jvArray,
|
||||||
|
std::function<bool(void)> const& continueCallback)
|
||||||
{
|
{
|
||||||
auto sourceCurrencies = sciSourceCurrencies;
|
auto sourceCurrencies = sciSourceCurrencies;
|
||||||
if (sourceCurrencies.empty() && saSendMax)
|
if (sourceCurrencies.empty() && saSendMax)
|
||||||
@@ -515,22 +523,33 @@ PathRequest::findPaths(
|
|||||||
hash_map<Currency, std::unique_ptr<Pathfinder>> currency_map;
|
hash_map<Currency, std::unique_ptr<Pathfinder>> currency_map;
|
||||||
for (auto const& issue : sourceCurrencies)
|
for (auto const& issue : sourceCurrencies)
|
||||||
{
|
{
|
||||||
|
if (continueCallback && !continueCallback())
|
||||||
|
break;
|
||||||
JLOG(m_journal.debug())
|
JLOG(m_journal.debug())
|
||||||
<< iIdentifier
|
<< iIdentifier
|
||||||
<< " Trying to find paths: " << STAmount(issue, 1).getFullText();
|
<< " Trying to find paths: " << STAmount(issue, 1).getFullText();
|
||||||
|
|
||||||
auto& pathfinder = getPathFinder(
|
auto& pathfinder = getPathFinder(
|
||||||
cache, currency_map, issue.currency, dst_amount, level);
|
cache,
|
||||||
|
currency_map,
|
||||||
|
issue.currency,
|
||||||
|
dst_amount,
|
||||||
|
level,
|
||||||
|
continueCallback);
|
||||||
if (!pathfinder)
|
if (!pathfinder)
|
||||||
{
|
{
|
||||||
assert(false);
|
assert(continueCallback && !continueCallback());
|
||||||
JLOG(m_journal.debug()) << iIdentifier << " No paths found";
|
JLOG(m_journal.debug()) << iIdentifier << " No paths found";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
STPath fullLiquidityPath;
|
STPath fullLiquidityPath;
|
||||||
auto ps = pathfinder->getBestPaths(
|
auto ps = pathfinder->getBestPaths(
|
||||||
max_paths_, fullLiquidityPath, mContext[issue], issue.account);
|
max_paths_,
|
||||||
|
fullLiquidityPath,
|
||||||
|
mContext[issue],
|
||||||
|
issue.account,
|
||||||
|
continueCallback);
|
||||||
mContext[issue] = ps;
|
mContext[issue] = ps;
|
||||||
|
|
||||||
auto& sourceAccount = !isXRP(issue.account)
|
auto& sourceAccount = !isXRP(issue.account)
|
||||||
@@ -628,7 +647,10 @@ PathRequest::findPaths(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Json::Value
|
Json::Value
|
||||||
PathRequest::doUpdate(std::shared_ptr<RippleLineCache> const& cache, bool fast)
|
PathRequest::doUpdate(
|
||||||
|
std::shared_ptr<RippleLineCache> const& cache,
|
||||||
|
bool fast,
|
||||||
|
std::function<bool(void)> const& continueCallback)
|
||||||
{
|
{
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
JLOG(m_journal.debug())
|
JLOG(m_journal.debug())
|
||||||
@@ -699,7 +721,7 @@ PathRequest::doUpdate(std::shared_ptr<RippleLineCache> const& cache, bool fast)
|
|||||||
JLOG(m_journal.debug()) << iIdentifier << " processing at level " << iLevel;
|
JLOG(m_journal.debug()) << iIdentifier << " processing at level " << iLevel;
|
||||||
|
|
||||||
Json::Value jvArray = Json::arrayValue;
|
Json::Value jvArray = Json::arrayValue;
|
||||||
if (findPaths(cache, iLevel, jvArray))
|
if (findPaths(cache, iLevel, jvArray, continueCallback))
|
||||||
{
|
{
|
||||||
bLastSuccess = jvArray.size() != 0;
|
bLastSuccess = jvArray.size() != 0;
|
||||||
newStatus[jss::alternatives] = std::move(jvArray);
|
newStatus[jss::alternatives] = std::move(jvArray);
|
||||||
@@ -730,7 +752,7 @@ PathRequest::doUpdate(std::shared_ptr<RippleLineCache> const& cache, bool fast)
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfoSub::pointer
|
InfoSub::pointer
|
||||||
PathRequest::getSubscriber()
|
PathRequest::getSubscriber() const
|
||||||
{
|
{
|
||||||
return wpSubscriber.lock();
|
return wpSubscriber.lock();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ class PathRequests;
|
|||||||
// Return values from parseJson <0 = invalid, >0 = valid
|
// Return values from parseJson <0 = invalid, >0 = valid
|
||||||
#define PFR_PJ_INVALID -1
|
#define PFR_PJ_INVALID -1
|
||||||
#define PFR_PJ_NOCHANGE 0
|
#define PFR_PJ_NOCHANGE 0
|
||||||
#define PFR_PJ_CHANGE 1
|
|
||||||
|
|
||||||
class PathRequest : public std::enable_shared_from_this<PathRequest>,
|
class PathRequest final : public InfoSubRequest,
|
||||||
|
public std::enable_shared_from_this<PathRequest>,
|
||||||
public CountedObject<PathRequest>
|
public CountedObject<PathRequest>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -55,8 +55,6 @@ public:
|
|||||||
using wref = const wptr&;
|
using wref = const wptr&;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// VFALCO TODO Break the cyclic dependency on InfoSub
|
|
||||||
|
|
||||||
// path_find semantics
|
// path_find semantics
|
||||||
// Subscriber is updated
|
// Subscriber is updated
|
||||||
PathRequest(
|
PathRequest(
|
||||||
@@ -91,15 +89,20 @@ public:
|
|||||||
doCreate(std::shared_ptr<RippleLineCache> const&, Json::Value const&);
|
doCreate(std::shared_ptr<RippleLineCache> const&, Json::Value const&);
|
||||||
|
|
||||||
Json::Value
|
Json::Value
|
||||||
doClose(Json::Value const&);
|
doClose() override;
|
||||||
Json::Value
|
Json::Value
|
||||||
doStatus(Json::Value const&);
|
doStatus(Json::Value const&) override;
|
||||||
|
void
|
||||||
|
doAborting() const;
|
||||||
|
|
||||||
// update jvStatus
|
// update jvStatus
|
||||||
Json::Value
|
Json::Value
|
||||||
doUpdate(std::shared_ptr<RippleLineCache> const&, bool fast);
|
doUpdate(
|
||||||
|
std::shared_ptr<RippleLineCache> const&,
|
||||||
|
bool fast,
|
||||||
|
std::function<bool(void)> const& continueCallback = {});
|
||||||
InfoSub::pointer
|
InfoSub::pointer
|
||||||
getSubscriber();
|
getSubscriber() const;
|
||||||
bool
|
bool
|
||||||
hasCompletion();
|
hasCompletion();
|
||||||
|
|
||||||
@@ -113,13 +116,18 @@ private:
|
|||||||
hash_map<Currency, std::unique_ptr<Pathfinder>>&,
|
hash_map<Currency, std::unique_ptr<Pathfinder>>&,
|
||||||
Currency const&,
|
Currency const&,
|
||||||
STAmount const&,
|
STAmount const&,
|
||||||
int const);
|
int const,
|
||||||
|
std::function<bool(void)> const&);
|
||||||
|
|
||||||
/** Finds and sets a PathSet in the JSON argument.
|
/** Finds and sets a PathSet in the JSON argument.
|
||||||
Returns false if the source currencies are inavlid.
|
Returns false if the source currencies are inavlid.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
findPaths(std::shared_ptr<RippleLineCache> const&, int const, Json::Value&);
|
findPaths(
|
||||||
|
std::shared_ptr<RippleLineCache> const&,
|
||||||
|
int const,
|
||||||
|
Json::Value&,
|
||||||
|
std::function<bool(void)> const&);
|
||||||
|
|
||||||
int
|
int
|
||||||
parseJson(Json::Value const&);
|
parseJson(Json::Value const&);
|
||||||
@@ -156,7 +164,7 @@ private:
|
|||||||
int iLevel;
|
int iLevel;
|
||||||
bool bLastSuccess;
|
bool bLastSuccess;
|
||||||
|
|
||||||
int iIdentifier;
|
int const iIdentifier;
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point const created_;
|
std::chrono::steady_clock::time_point const created_;
|
||||||
std::chrono::steady_clock::time_point quick_reply_;
|
std::chrono::steady_clock::time_point quick_reply_;
|
||||||
|
|||||||
@@ -40,8 +40,12 @@ PathRequests::getLineCache(
|
|||||||
{
|
{
|
||||||
std::lock_guard sl(mLock);
|
std::lock_guard sl(mLock);
|
||||||
|
|
||||||
std::uint32_t lineSeq = mLineCache ? mLineCache->getLedger()->seq() : 0;
|
auto lineCache = lineCache_.lock();
|
||||||
std::uint32_t lgrSeq = ledger->seq();
|
|
||||||
|
std::uint32_t const lineSeq = lineCache ? lineCache->getLedger()->seq() : 0;
|
||||||
|
std::uint32_t const lgrSeq = ledger->seq();
|
||||||
|
JLOG(mJournal.debug()) << "getLineCache has cache for " << lineSeq
|
||||||
|
<< ", considering " << lgrSeq;
|
||||||
|
|
||||||
if ((lineSeq == 0) || // no ledger
|
if ((lineSeq == 0) || // no ledger
|
||||||
(authoritative && (lgrSeq > lineSeq)) || // newer authoritative ledger
|
(authoritative && (lgrSeq > lineSeq)) || // newer authoritative ledger
|
||||||
@@ -49,9 +53,15 @@ PathRequests::getLineCache(
|
|||||||
((lgrSeq + 8) < lineSeq)) || // we jumped way back for some reason
|
((lgrSeq + 8) < lineSeq)) || // we jumped way back for some reason
|
||||||
(lgrSeq > (lineSeq + 8))) // we jumped way forward for some reason
|
(lgrSeq > (lineSeq + 8))) // we jumped way forward for some reason
|
||||||
{
|
{
|
||||||
mLineCache = std::make_shared<RippleLineCache>(ledger);
|
JLOG(mJournal.debug())
|
||||||
|
<< "getLineCache creating new cache for " << lgrSeq;
|
||||||
|
// Assign to the local before the member, because the member is a
|
||||||
|
// weak_ptr, and will immediately discard it if there are no other
|
||||||
|
// references.
|
||||||
|
lineCache_ = lineCache = std::make_shared<RippleLineCache>(
|
||||||
|
ledger, app_.journal("RippleLineCache"));
|
||||||
}
|
}
|
||||||
return mLineCache;
|
return lineCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -78,8 +88,20 @@ PathRequests::updateAll(std::shared_ptr<ReadView const> const& inLedger)
|
|||||||
|
|
||||||
int processed = 0, removed = 0;
|
int processed = 0, removed = 0;
|
||||||
|
|
||||||
|
auto getSubscriber =
|
||||||
|
[](PathRequest::pointer const& request) -> InfoSub::pointer {
|
||||||
|
if (auto ipSub = request->getSubscriber();
|
||||||
|
ipSub && ipSub->getRequest() == request)
|
||||||
|
{
|
||||||
|
return ipSub;
|
||||||
|
}
|
||||||
|
request->doAborting();
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
JLOG(mJournal.trace()) << "updateAll looping";
|
||||||
for (auto const& wr : requests)
|
for (auto const& wr : requests)
|
||||||
{
|
{
|
||||||
if (app_.getJobQueue().isStopping())
|
if (app_.getJobQueue().isStopping())
|
||||||
@@ -87,27 +109,42 @@ PathRequests::updateAll(std::shared_ptr<ReadView const> const& inLedger)
|
|||||||
|
|
||||||
auto request = wr.lock();
|
auto request = wr.lock();
|
||||||
bool remove = true;
|
bool remove = true;
|
||||||
|
JLOG(mJournal.trace())
|
||||||
|
<< "updateAll request " << (request ? "" : "not ") << "found";
|
||||||
|
|
||||||
if (request)
|
if (request)
|
||||||
{
|
{
|
||||||
|
auto continueCallback = [&getSubscriber, &request]() {
|
||||||
|
// This callback is used by doUpdate to determine whether to
|
||||||
|
// continue working. If getSubscriber returns null, that
|
||||||
|
// indicates that this request is no longer relevant.
|
||||||
|
return (bool)getSubscriber(request);
|
||||||
|
};
|
||||||
if (!request->needsUpdate(
|
if (!request->needsUpdate(
|
||||||
newRequests, cache->getLedger()->seq()))
|
newRequests, cache->getLedger()->seq()))
|
||||||
remove = false;
|
remove = false;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (auto ipSub = request->getSubscriber())
|
if (auto ipSub = getSubscriber(request))
|
||||||
{
|
{
|
||||||
if (!ipSub->getConsumer().warn())
|
if (!ipSub->getConsumer().warn())
|
||||||
{
|
{
|
||||||
Json::Value update =
|
// Release the shared ptr to the subscriber so that
|
||||||
request->doUpdate(cache, false);
|
// it can be freed if the client disconnects, and
|
||||||
|
// thus fail to lock later.
|
||||||
|
ipSub.reset();
|
||||||
|
Json::Value update = request->doUpdate(
|
||||||
|
cache, false, continueCallback);
|
||||||
request->updateComplete();
|
request->updateComplete();
|
||||||
update[jss::type] = "path_find";
|
update[jss::type] = "path_find";
|
||||||
|
if ((ipSub = getSubscriber(request)))
|
||||||
|
{
|
||||||
ipSub->send(update, false);
|
ipSub->send(update, false);
|
||||||
remove = false;
|
remove = false;
|
||||||
++processed;
|
++processed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (request->hasCompletion())
|
else if (request->hasCompletion())
|
||||||
{
|
{
|
||||||
// One-shot request with completion function
|
// One-shot request with completion function
|
||||||
@@ -178,6 +215,13 @@ PathRequests::updateAll(std::shared_ptr<ReadView const> const& inLedger)
|
|||||||
<< " processed and " << removed << " removed";
|
<< " processed and " << removed << " removed";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PathRequests::requestsPending() const
|
||||||
|
{
|
||||||
|
std::lock_guard sl(mLock);
|
||||||
|
return !requests_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PathRequests::insertPathRequest(PathRequest::pointer const& req)
|
PathRequests::insertPathRequest(PathRequest::pointer const& req)
|
||||||
{
|
{
|
||||||
@@ -211,7 +255,7 @@ PathRequests::makePathRequest(
|
|||||||
|
|
||||||
if (valid)
|
if (valid)
|
||||||
{
|
{
|
||||||
subscriber->setPathRequest(req);
|
subscriber->setRequest(req);
|
||||||
insertPathRequest(req);
|
insertPathRequest(req);
|
||||||
app_.getLedgerMaster().newPathRequest();
|
app_.getLedgerMaster().newPathRequest();
|
||||||
}
|
}
|
||||||
@@ -258,7 +302,8 @@ PathRequests::doLegacyPathRequest(
|
|||||||
std::shared_ptr<ReadView const> const& inLedger,
|
std::shared_ptr<ReadView const> const& inLedger,
|
||||||
Json::Value const& request)
|
Json::Value const& request)
|
||||||
{
|
{
|
||||||
auto cache = std::make_shared<RippleLineCache>(inLedger);
|
auto cache = std::make_shared<RippleLineCache>(
|
||||||
|
inLedger, app_.journal("RippleLineCache"));
|
||||||
|
|
||||||
auto req = std::make_shared<PathRequest>(
|
auto req = std::make_shared<PathRequest>(
|
||||||
app_, [] {}, consumer, ++mLastIdentifier, *this, mJournal);
|
app_, [] {}, consumer, ++mLastIdentifier, *this, mJournal);
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ public:
|
|||||||
void
|
void
|
||||||
updateAll(std::shared_ptr<ReadView const> const& ledger);
|
updateAll(std::shared_ptr<ReadView const> const& ledger);
|
||||||
|
|
||||||
|
bool
|
||||||
|
requestsPending() const;
|
||||||
|
|
||||||
std::shared_ptr<RippleLineCache>
|
std::shared_ptr<RippleLineCache>
|
||||||
getLineCache(
|
getLineCache(
|
||||||
std::shared_ptr<ReadView const> const& ledger,
|
std::shared_ptr<ReadView const> const& ledger,
|
||||||
@@ -109,11 +112,11 @@ private:
|
|||||||
std::vector<PathRequest::wptr> requests_;
|
std::vector<PathRequest::wptr> requests_;
|
||||||
|
|
||||||
// Use a RippleLineCache
|
// Use a RippleLineCache
|
||||||
std::shared_ptr<RippleLineCache> mLineCache;
|
std::weak_ptr<RippleLineCache> lineCache_;
|
||||||
|
|
||||||
std::atomic<int> mLastIdentifier;
|
std::atomic<int> mLastIdentifier;
|
||||||
|
|
||||||
std::recursive_mutex mLock;
|
std::recursive_mutex mutable mLock;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <ripple/app/paths/RippleLineCache.h>
|
#include <ripple/app/paths/RippleLineCache.h>
|
||||||
#include <ripple/app/paths/impl/PathfinderUtils.h>
|
#include <ripple/app/paths/impl/PathfinderUtils.h>
|
||||||
#include <ripple/basics/Log.h>
|
#include <ripple/basics/Log.h>
|
||||||
|
#include <ripple/basics/join.h>
|
||||||
#include <ripple/core/Config.h>
|
#include <ripple/core/Config.h>
|
||||||
#include <ripple/core/JobQueue.h>
|
#include <ripple/core/JobQueue.h>
|
||||||
#include <ripple/json/to_string.h>
|
#include <ripple/json/to_string.h>
|
||||||
@@ -191,8 +192,11 @@ Pathfinder::Pathfinder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Pathfinder::findPaths(int searchLevel)
|
Pathfinder::findPaths(
|
||||||
|
int searchLevel,
|
||||||
|
std::function<bool(void)> const& continueCallback)
|
||||||
{
|
{
|
||||||
|
JLOG(j_.trace()) << "findPaths start";
|
||||||
if (mDstAmount == beast::zero)
|
if (mDstAmount == beast::zero)
|
||||||
{
|
{
|
||||||
// No need to send zero money.
|
// No need to send zero money.
|
||||||
@@ -316,10 +320,13 @@ Pathfinder::findPaths(int searchLevel)
|
|||||||
// Now iterate over all paths for that paymentType.
|
// Now iterate over all paths for that paymentType.
|
||||||
for (auto const& costedPath : mPathTable[paymentType])
|
for (auto const& costedPath : mPathTable[paymentType])
|
||||||
{
|
{
|
||||||
|
if (continueCallback && !continueCallback())
|
||||||
|
return false;
|
||||||
// Only use paths with at most the current search level.
|
// Only use paths with at most the current search level.
|
||||||
if (costedPath.searchLevel <= searchLevel)
|
if (costedPath.searchLevel <= searchLevel)
|
||||||
{
|
{
|
||||||
addPathsForType(costedPath.type);
|
JLOG(j_.trace()) << "findPaths trying payment type " << paymentType;
|
||||||
|
addPathsForType(costedPath.type, continueCallback);
|
||||||
|
|
||||||
if (mCompletePaths.size() > PATHFINDER_MAX_COMPLETE_PATHS)
|
if (mCompletePaths.size() > PATHFINDER_MAX_COMPLETE_PATHS)
|
||||||
break;
|
break;
|
||||||
@@ -401,7 +408,9 @@ Pathfinder::getPathLiquidity(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Pathfinder::computePathRanks(int maxPaths)
|
Pathfinder::computePathRanks(
|
||||||
|
int maxPaths,
|
||||||
|
std::function<bool(void)> const& continueCallback)
|
||||||
{
|
{
|
||||||
mRemainingAmount = convertAmount(mDstAmount, convert_all_);
|
mRemainingAmount = convertAmount(mDstAmount, convert_all_);
|
||||||
|
|
||||||
@@ -439,7 +448,7 @@ Pathfinder::computePathRanks(int maxPaths)
|
|||||||
JLOG(j_.debug()) << "Default path causes exception";
|
JLOG(j_.debug()) << "Default path causes exception";
|
||||||
}
|
}
|
||||||
|
|
||||||
rankPaths(maxPaths, mCompletePaths, mPathRanks);
|
rankPaths(maxPaths, mCompletePaths, mPathRanks, continueCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@@ -480,8 +489,11 @@ void
|
|||||||
Pathfinder::rankPaths(
|
Pathfinder::rankPaths(
|
||||||
int maxPaths,
|
int maxPaths,
|
||||||
STPathSet const& paths,
|
STPathSet const& paths,
|
||||||
std::vector<PathRank>& rankedPaths)
|
std::vector<PathRank>& rankedPaths,
|
||||||
|
std::function<bool(void)> const& continueCallback)
|
||||||
{
|
{
|
||||||
|
JLOG(j_.trace()) << "rankPaths with " << paths.size() << " candidates, and "
|
||||||
|
<< maxPaths << " maximum";
|
||||||
rankedPaths.clear();
|
rankedPaths.clear();
|
||||||
rankedPaths.reserve(paths.size());
|
rankedPaths.reserve(paths.size());
|
||||||
|
|
||||||
@@ -499,6 +511,8 @@ Pathfinder::rankPaths(
|
|||||||
|
|
||||||
for (int i = 0; i < paths.size(); ++i)
|
for (int i = 0; i < paths.size(); ++i)
|
||||||
{
|
{
|
||||||
|
if (continueCallback && !continueCallback())
|
||||||
|
return;
|
||||||
auto const& currentPath = paths[i];
|
auto const& currentPath = paths[i];
|
||||||
if (!currentPath.empty())
|
if (!currentPath.empty())
|
||||||
{
|
{
|
||||||
@@ -554,7 +568,8 @@ Pathfinder::getBestPaths(
|
|||||||
int maxPaths,
|
int maxPaths,
|
||||||
STPath& fullLiquidityPath,
|
STPath& fullLiquidityPath,
|
||||||
STPathSet const& extraPaths,
|
STPathSet const& extraPaths,
|
||||||
AccountID const& srcIssuer)
|
AccountID const& srcIssuer,
|
||||||
|
std::function<bool(void)> const& continueCallback)
|
||||||
{
|
{
|
||||||
JLOG(j_.debug()) << "findPaths: " << mCompletePaths.size() << " paths and "
|
JLOG(j_.debug()) << "findPaths: " << mCompletePaths.size() << " paths and "
|
||||||
<< extraPaths.size() << " extras";
|
<< extraPaths.size() << " extras";
|
||||||
@@ -567,7 +582,7 @@ Pathfinder::getBestPaths(
|
|||||||
isXRP(mSrcCurrency) || (srcIssuer == mSrcAccount);
|
isXRP(mSrcCurrency) || (srcIssuer == mSrcAccount);
|
||||||
|
|
||||||
std::vector<PathRank> extraPathRanks;
|
std::vector<PathRank> extraPathRanks;
|
||||||
rankPaths(maxPaths, extraPaths, extraPathRanks);
|
rankPaths(maxPaths, extraPaths, extraPathRanks, continueCallback);
|
||||||
|
|
||||||
STPathSet bestPaths;
|
STPathSet bestPaths;
|
||||||
|
|
||||||
@@ -582,6 +597,8 @@ Pathfinder::getBestPaths(
|
|||||||
while (pathsIterator != mPathRanks.end() ||
|
while (pathsIterator != mPathRanks.end() ||
|
||||||
extraPathsIterator != extraPathRanks.end())
|
extraPathsIterator != extraPathRanks.end())
|
||||||
{
|
{
|
||||||
|
if (continueCallback && !continueCallback())
|
||||||
|
break;
|
||||||
bool usePath = false;
|
bool usePath = false;
|
||||||
bool useExtraPath = false;
|
bool useExtraPath = false;
|
||||||
|
|
||||||
@@ -692,7 +709,8 @@ Pathfinder::getPathsOut(
|
|||||||
Currency const& currency,
|
Currency const& currency,
|
||||||
AccountID const& account,
|
AccountID const& account,
|
||||||
bool isDstCurrency,
|
bool isDstCurrency,
|
||||||
AccountID const& dstAccount)
|
AccountID const& dstAccount,
|
||||||
|
std::function<bool(void)> const& continueCallback)
|
||||||
{
|
{
|
||||||
Issue const issue(currency, account);
|
Issue const issue(currency, account);
|
||||||
|
|
||||||
@@ -755,17 +773,26 @@ void
|
|||||||
Pathfinder::addLinks(
|
Pathfinder::addLinks(
|
||||||
STPathSet const& currentPaths, // The paths to build from
|
STPathSet const& currentPaths, // The paths to build from
|
||||||
STPathSet& incompletePaths, // The set of partial paths we add to
|
STPathSet& incompletePaths, // The set of partial paths we add to
|
||||||
int addFlags)
|
int addFlags,
|
||||||
|
std::function<bool(void)> const& continueCallback)
|
||||||
{
|
{
|
||||||
JLOG(j_.debug()) << "addLink< on " << currentPaths.size()
|
JLOG(j_.debug()) << "addLink< on " << currentPaths.size()
|
||||||
<< " source(s), flags=" << addFlags;
|
<< " source(s), flags=" << addFlags;
|
||||||
for (auto const& path : currentPaths)
|
for (auto const& path : currentPaths)
|
||||||
addLink(path, incompletePaths, addFlags);
|
{
|
||||||
|
if (continueCallback && !continueCallback())
|
||||||
|
return;
|
||||||
|
addLink(path, incompletePaths, addFlags, continueCallback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
STPathSet&
|
STPathSet&
|
||||||
Pathfinder::addPathsForType(PathType const& pathType)
|
Pathfinder::addPathsForType(
|
||||||
|
PathType const& pathType,
|
||||||
|
std::function<bool(void)> const& continueCallback)
|
||||||
{
|
{
|
||||||
|
JLOG(j_.warn()) << "addPathsForType "
|
||||||
|
<< CollectionAndDelimiter(pathType, ", ");
|
||||||
// See if the set of paths for this type already exists.
|
// See if the set of paths for this type already exists.
|
||||||
auto it = mPaths.find(pathType);
|
auto it = mPaths.find(pathType);
|
||||||
if (it != mPaths.end())
|
if (it != mPaths.end())
|
||||||
@@ -774,13 +801,16 @@ Pathfinder::addPathsForType(PathType const& pathType)
|
|||||||
// Otherwise, if the type has no nodes, return the empty path.
|
// Otherwise, if the type has no nodes, return the empty path.
|
||||||
if (pathType.empty())
|
if (pathType.empty())
|
||||||
return mPaths[pathType];
|
return mPaths[pathType];
|
||||||
|
if (continueCallback && !continueCallback())
|
||||||
|
return mPaths[{}];
|
||||||
|
|
||||||
// Otherwise, get the paths for the parent PathType by calling
|
// Otherwise, get the paths for the parent PathType by calling
|
||||||
// addPathsForType recursively.
|
// addPathsForType recursively.
|
||||||
PathType parentPathType = pathType;
|
PathType parentPathType = pathType;
|
||||||
parentPathType.pop_back();
|
parentPathType.pop_back();
|
||||||
|
|
||||||
STPathSet const& parentPaths = addPathsForType(parentPathType);
|
STPathSet const& parentPaths =
|
||||||
|
addPathsForType(parentPathType, continueCallback);
|
||||||
STPathSet& pathsOut = mPaths[pathType];
|
STPathSet& pathsOut = mPaths[pathType];
|
||||||
|
|
||||||
JLOG(j_.debug()) << "getPaths< adding onto '"
|
JLOG(j_.debug()) << "getPaths< adding onto '"
|
||||||
@@ -800,26 +830,38 @@ Pathfinder::addPathsForType(PathType const& pathType)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case nt_ACCOUNTS:
|
case nt_ACCOUNTS:
|
||||||
addLinks(parentPaths, pathsOut, afADD_ACCOUNTS);
|
addLinks(parentPaths, pathsOut, afADD_ACCOUNTS, continueCallback);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nt_BOOKS:
|
case nt_BOOKS:
|
||||||
addLinks(parentPaths, pathsOut, afADD_BOOKS);
|
addLinks(parentPaths, pathsOut, afADD_BOOKS, continueCallback);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nt_XRP_BOOK:
|
case nt_XRP_BOOK:
|
||||||
addLinks(parentPaths, pathsOut, afADD_BOOKS | afOB_XRP);
|
addLinks(
|
||||||
|
parentPaths,
|
||||||
|
pathsOut,
|
||||||
|
afADD_BOOKS | afOB_XRP,
|
||||||
|
continueCallback);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nt_DEST_BOOK:
|
case nt_DEST_BOOK:
|
||||||
addLinks(parentPaths, pathsOut, afADD_BOOKS | afOB_LAST);
|
addLinks(
|
||||||
|
parentPaths,
|
||||||
|
pathsOut,
|
||||||
|
afADD_BOOKS | afOB_LAST,
|
||||||
|
continueCallback);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nt_DESTINATION:
|
case nt_DESTINATION:
|
||||||
// FIXME: What if a different issuer was specified on the
|
// FIXME: What if a different issuer was specified on the
|
||||||
// destination amount?
|
// destination amount?
|
||||||
// TODO(tom): what does this even mean? Should it be a JIRA?
|
// TODO(tom): what does this even mean? Should it be a JIRA?
|
||||||
addLinks(parentPaths, pathsOut, afADD_ACCOUNTS | afAC_LAST);
|
addLinks(
|
||||||
|
parentPaths,
|
||||||
|
pathsOut,
|
||||||
|
afADD_ACCOUNTS | afAC_LAST,
|
||||||
|
continueCallback);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -890,7 +932,8 @@ void
|
|||||||
Pathfinder::addLink(
|
Pathfinder::addLink(
|
||||||
const STPath& currentPath, // The path to build from
|
const STPath& currentPath, // The path to build from
|
||||||
STPathSet& incompletePaths, // The set of partial paths we add to
|
STPathSet& incompletePaths, // The set of partial paths we add to
|
||||||
int addFlags)
|
int addFlags,
|
||||||
|
std::function<bool(void)> const& continueCallback)
|
||||||
{
|
{
|
||||||
auto const& pathEnd = currentPath.empty() ? mSource : currentPath.back();
|
auto const& pathEnd = currentPath.empty() ? mSource : currentPath.back();
|
||||||
auto const& uEndCurrency = pathEnd.getCurrency();
|
auto const& uEndCurrency = pathEnd.getCurrency();
|
||||||
@@ -903,7 +946,8 @@ Pathfinder::addLink(
|
|||||||
// rather than the ultimate destination?
|
// rather than the ultimate destination?
|
||||||
bool const hasEffectiveDestination = mEffectiveDst != mDstAccount;
|
bool const hasEffectiveDestination = mEffectiveDst != mDstAccount;
|
||||||
|
|
||||||
JLOG(j_.trace()) << "addLink< flags=" << addFlags << " onXRP=" << bOnXRP;
|
JLOG(j_.trace()) << "addLink< flags=" << addFlags << " onXRP=" << bOnXRP
|
||||||
|
<< " completePaths size=" << mCompletePaths.size();
|
||||||
JLOG(j_.trace()) << currentPath.getJson(JsonOptions::none);
|
JLOG(j_.trace()) << currentPath.getJson(JsonOptions::none);
|
||||||
|
|
||||||
if (addFlags & afADD_ACCOUNTS)
|
if (addFlags & afADD_ACCOUNTS)
|
||||||
@@ -939,6 +983,8 @@ Pathfinder::addLink(
|
|||||||
|
|
||||||
for (auto const& rs : rippleLines)
|
for (auto const& rs : rippleLines)
|
||||||
{
|
{
|
||||||
|
if (continueCallback && !continueCallback())
|
||||||
|
return;
|
||||||
auto const& acct = rs.getAccountIDPeer();
|
auto const& acct = rs.getAccountIDPeer();
|
||||||
|
|
||||||
if (hasEffectiveDestination && (acct == mDstAccount))
|
if (hasEffectiveDestination && (acct == mDstAccount))
|
||||||
@@ -1002,7 +1048,8 @@ Pathfinder::addLink(
|
|||||||
uEndCurrency,
|
uEndCurrency,
|
||||||
acct,
|
acct,
|
||||||
bIsEndCurrency,
|
bIsEndCurrency,
|
||||||
mEffectiveDst);
|
mEffectiveDst,
|
||||||
|
continueCallback);
|
||||||
if (out)
|
if (out)
|
||||||
candidates.push_back({out, acct});
|
candidates.push_back({out, acct});
|
||||||
}
|
}
|
||||||
@@ -1030,6 +1077,8 @@ Pathfinder::addLink(
|
|||||||
auto it = candidates.begin();
|
auto it = candidates.begin();
|
||||||
while (count-- != 0)
|
while (count-- != 0)
|
||||||
{
|
{
|
||||||
|
if (continueCallback && !continueCallback())
|
||||||
|
return;
|
||||||
// Add accounts to incompletePaths
|
// Add accounts to incompletePaths
|
||||||
STPathElement pathElement(
|
STPathElement pathElement(
|
||||||
STPathElement::typeAccount,
|
STPathElement::typeAccount,
|
||||||
@@ -1074,6 +1123,8 @@ Pathfinder::addLink(
|
|||||||
|
|
||||||
for (auto const& book : books)
|
for (auto const& book : books)
|
||||||
{
|
{
|
||||||
|
if (continueCallback && !continueCallback())
|
||||||
|
return;
|
||||||
if (!currentPath.hasSeen(
|
if (!currentPath.hasSeen(
|
||||||
xrpAccount(),
|
xrpAccount(),
|
||||||
book->getCurrencyOut(),
|
book->getCurrencyOut(),
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <ripple/app/ledger/Ledger.h>
|
#include <ripple/app/ledger/Ledger.h>
|
||||||
#include <ripple/app/paths/RippleLineCache.h>
|
#include <ripple/app/paths/RippleLineCache.h>
|
||||||
|
#include <ripple/basics/CountedObject.h>
|
||||||
#include <ripple/core/LoadEvent.h>
|
#include <ripple/core/LoadEvent.h>
|
||||||
#include <ripple/protocol/STAmount.h>
|
#include <ripple/protocol/STAmount.h>
|
||||||
#include <ripple/protocol/STPathSet.h>
|
#include <ripple/protocol/STPathSet.h>
|
||||||
@@ -34,7 +35,7 @@ namespace ripple {
|
|||||||
|
|
||||||
@see RippleCalc
|
@see RippleCalc
|
||||||
*/
|
*/
|
||||||
class Pathfinder
|
class Pathfinder : public CountedObject<Pathfinder>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/** Construct a pathfinder without an issuer.*/
|
/** Construct a pathfinder without an issuer.*/
|
||||||
@@ -56,11 +57,15 @@ public:
|
|||||||
initPathTable();
|
initPathTable();
|
||||||
|
|
||||||
bool
|
bool
|
||||||
findPaths(int searchLevel);
|
findPaths(
|
||||||
|
int searchLevel,
|
||||||
|
std::function<bool(void)> const& continueCallback = {});
|
||||||
|
|
||||||
/** Compute the rankings of the paths. */
|
/** Compute the rankings of the paths. */
|
||||||
void
|
void
|
||||||
computePathRanks(int maxPaths);
|
computePathRanks(
|
||||||
|
int maxPaths,
|
||||||
|
std::function<bool(void)> const& continueCallback = {});
|
||||||
|
|
||||||
/* Get the best paths, up to maxPaths in number, from mCompletePaths.
|
/* Get the best paths, up to maxPaths in number, from mCompletePaths.
|
||||||
|
|
||||||
@@ -72,7 +77,8 @@ public:
|
|||||||
int maxPaths,
|
int maxPaths,
|
||||||
STPath& fullLiquidityPath,
|
STPath& fullLiquidityPath,
|
||||||
STPathSet const& extraPaths,
|
STPathSet const& extraPaths,
|
||||||
AccountID const& srcIssuer);
|
AccountID const& srcIssuer,
|
||||||
|
std::function<bool(void)> const& continueCallback = {});
|
||||||
|
|
||||||
enum NodeType {
|
enum NodeType {
|
||||||
nt_SOURCE, // The source account: with an issuer account, if needed.
|
nt_SOURCE, // The source account: with an issuer account, if needed.
|
||||||
@@ -127,7 +133,9 @@ private:
|
|||||||
|
|
||||||
// Add all paths of one type to mCompletePaths.
|
// Add all paths of one type to mCompletePaths.
|
||||||
STPathSet&
|
STPathSet&
|
||||||
addPathsForType(PathType const& type);
|
addPathsForType(
|
||||||
|
PathType const& type,
|
||||||
|
std::function<bool(void)> const& continueCallback);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
issueMatchesOrigin(Issue const&);
|
issueMatchesOrigin(Issue const&);
|
||||||
@@ -137,20 +145,23 @@ private:
|
|||||||
Currency const& currency,
|
Currency const& currency,
|
||||||
AccountID const& account,
|
AccountID const& account,
|
||||||
bool isDestCurrency,
|
bool isDestCurrency,
|
||||||
AccountID const& dest);
|
AccountID const& dest,
|
||||||
|
std::function<bool(void)> const& continueCallback);
|
||||||
|
|
||||||
void
|
void
|
||||||
addLink(
|
addLink(
|
||||||
STPath const& currentPath,
|
STPath const& currentPath,
|
||||||
STPathSet& incompletePaths,
|
STPathSet& incompletePaths,
|
||||||
int addFlags);
|
int addFlags,
|
||||||
|
std::function<bool(void)> const& continueCallback);
|
||||||
|
|
||||||
// Call addLink() for each path in currentPaths.
|
// Call addLink() for each path in currentPaths.
|
||||||
void
|
void
|
||||||
addLinks(
|
addLinks(
|
||||||
STPathSet const& currentPaths,
|
STPathSet const& currentPaths,
|
||||||
STPathSet& incompletePaths,
|
STPathSet& incompletePaths,
|
||||||
int addFlags);
|
int addFlags,
|
||||||
|
std::function<bool(void)> const& continueCallback);
|
||||||
|
|
||||||
// Compute the liquidity for a path. Return tesSUCCESS if it has has enough
|
// Compute the liquidity for a path. Return tesSUCCESS if it has has enough
|
||||||
// liquidity to be worth keeping, otherwise an error.
|
// liquidity to be worth keeping, otherwise an error.
|
||||||
@@ -178,7 +189,8 @@ private:
|
|||||||
rankPaths(
|
rankPaths(
|
||||||
int maxPaths,
|
int maxPaths,
|
||||||
STPathSet const& paths,
|
STPathSet const& paths,
|
||||||
std::vector<PathRank>& rankedPaths);
|
std::vector<PathRank>& rankedPaths,
|
||||||
|
std::function<bool(void)> const& continueCallback);
|
||||||
|
|
||||||
AccountID mSrcAccount;
|
AccountID mSrcAccount;
|
||||||
AccountID mDstAccount;
|
AccountID mDstAccount;
|
||||||
|
|||||||
@@ -23,12 +23,22 @@
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
RippleLineCache::RippleLineCache(std::shared_ptr<ReadView const> const& ledger)
|
RippleLineCache::RippleLineCache(
|
||||||
|
std::shared_ptr<ReadView const> const& ledger,
|
||||||
|
beast::Journal j)
|
||||||
|
: journal_(j)
|
||||||
{
|
{
|
||||||
// We want the caching that OpenView provides
|
mLedger = ledger;
|
||||||
// And we need to own a shared_ptr to the input view
|
|
||||||
// VFALCO TODO This should be a CachedLedger
|
JLOG(journal_.debug()) << "RippleLineCache created for ledger "
|
||||||
mLedger = std::make_shared<OpenView>(&*ledger, ledger);
|
<< mLedger->info().seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
RippleLineCache::~RippleLineCache()
|
||||||
|
{
|
||||||
|
JLOG(journal_.debug()) << "~RippleLineCache destroyed for ledger "
|
||||||
|
<< mLedger->info().seq << " with " << lines_.size()
|
||||||
|
<< " accounts";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<PathFindTrustLine> const&
|
std::vector<PathFindTrustLine> const&
|
||||||
@@ -43,6 +53,13 @@ RippleLineCache::getRippleLines(AccountID const& accountID)
|
|||||||
if (inserted)
|
if (inserted)
|
||||||
it->second = PathFindTrustLine::getItems(accountID, *mLedger);
|
it->second = PathFindTrustLine::getItems(accountID, *mLedger);
|
||||||
|
|
||||||
|
JLOG(journal_.debug()) << "RippleLineCache getRippleLines for ledger "
|
||||||
|
<< mLedger->info().seq << " found "
|
||||||
|
<< it->second.size() << " lines for "
|
||||||
|
<< (inserted ? "new " : "existing ") << accountID
|
||||||
|
<< " out of a total of " << lines_.size()
|
||||||
|
<< " accounts";
|
||||||
|
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,10 +33,13 @@
|
|||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
// Used by Pathfinder
|
// Used by Pathfinder
|
||||||
class RippleLineCache : public CountedObject<RippleLineCache>
|
class RippleLineCache final : public CountedObject<RippleLineCache>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit RippleLineCache(std::shared_ptr<ReadView const> const& l);
|
explicit RippleLineCache(
|
||||||
|
std::shared_ptr<ReadView const> const& l,
|
||||||
|
beast::Journal j);
|
||||||
|
~RippleLineCache();
|
||||||
|
|
||||||
std::shared_ptr<ReadView const> const&
|
std::shared_ptr<ReadView const> const&
|
||||||
getLedger() const
|
getLedger() const
|
||||||
@@ -53,7 +56,9 @@ private:
|
|||||||
ripple::hardened_hash<> hasher_;
|
ripple::hardened_hash<> hasher_;
|
||||||
std::shared_ptr<ReadView const> mLedger;
|
std::shared_ptr<ReadView const> mLedger;
|
||||||
|
|
||||||
struct AccountKey
|
beast::Journal journal_;
|
||||||
|
|
||||||
|
struct AccountKey final : public CountedObject<AccountKey>
|
||||||
{
|
{
|
||||||
AccountID account_;
|
AccountID account_;
|
||||||
std::size_t hash_value_;
|
std::size_t hash_value_;
|
||||||
|
|||||||
108
src/ripple/basics/join.h
Normal file
108
src/ripple/basics/join.h
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2022 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef JOIN_H_INCLUDED
|
||||||
|
#define JOIN_H_INCLUDED
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
template <class Stream, class Iter>
|
||||||
|
Stream&
|
||||||
|
join(Stream& s, Iter iter, Iter end, std::string const& delimiter)
|
||||||
|
{
|
||||||
|
if (iter == end)
|
||||||
|
return s;
|
||||||
|
s << *iter;
|
||||||
|
for (++iter; iter != end; ++iter)
|
||||||
|
s << delimiter << *iter;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Collection>
|
||||||
|
class CollectionAndDelimiter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Collection const& collection;
|
||||||
|
std::string const delimiter;
|
||||||
|
|
||||||
|
explicit CollectionAndDelimiter(Collection const& c, std::string delim)
|
||||||
|
: collection(c), delimiter(std::move(delim))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Stream>
|
||||||
|
friend Stream&
|
||||||
|
operator<<(Stream& s, CollectionAndDelimiter const& cd)
|
||||||
|
{
|
||||||
|
return join(
|
||||||
|
s,
|
||||||
|
std::begin(cd.collection),
|
||||||
|
std::end(cd.collection),
|
||||||
|
cd.delimiter);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Collection, std::size_t N>
|
||||||
|
class CollectionAndDelimiter<Collection[N]>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Collection const* collection;
|
||||||
|
std::string const delimiter;
|
||||||
|
|
||||||
|
explicit CollectionAndDelimiter(Collection const c[N], std::string delim)
|
||||||
|
: collection(c), delimiter(std::move(delim))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Stream>
|
||||||
|
friend Stream&
|
||||||
|
operator<<(Stream& s, CollectionAndDelimiter const& cd)
|
||||||
|
{
|
||||||
|
return join(s, cd.collection, cd.collection + N, cd.delimiter);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Specialization for const char* strings
|
||||||
|
template <std::size_t N>
|
||||||
|
class CollectionAndDelimiter<char[N]>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
char const* collection;
|
||||||
|
std::string const delimiter;
|
||||||
|
|
||||||
|
explicit CollectionAndDelimiter(char const c[N], std::string delim)
|
||||||
|
: collection(c), delimiter(std::move(delim))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Stream>
|
||||||
|
friend Stream&
|
||||||
|
operator<<(Stream& s, CollectionAndDelimiter const& cd)
|
||||||
|
{
|
||||||
|
auto end = cd.collection + N;
|
||||||
|
if (N > 0 && *(end - 1) == '\0')
|
||||||
|
--end;
|
||||||
|
return join(s, cd.collection, end, cd.delimiter);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
#ifndef RIPPLE_CORE_JOB_H_INCLUDED
|
#ifndef RIPPLE_CORE_JOB_H_INCLUDED
|
||||||
#define RIPPLE_CORE_JOB_H_INCLUDED
|
#define RIPPLE_CORE_JOB_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/basics/CountedObject.h>
|
||||||
#include <ripple/core/ClosureCounter.h>
|
#include <ripple/core/ClosureCounter.h>
|
||||||
#include <ripple/core/LoadMonitor.h>
|
#include <ripple/core/LoadMonitor.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -92,7 +93,7 @@ enum JobType {
|
|||||||
jtNS_WRITE,
|
jtNS_WRITE,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Job
|
class Job : public CountedObject<Job>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using clock_type = std::chrono::steady_clock;
|
using clock_type = std::chrono::steady_clock;
|
||||||
|
|||||||
@@ -33,7 +33,18 @@ namespace ripple {
|
|||||||
// Operations that clients may wish to perform against the network
|
// Operations that clients may wish to perform against the network
|
||||||
// Master operational handler, server sequencer, network tracker
|
// Master operational handler, server sequencer, network tracker
|
||||||
|
|
||||||
class PathRequest;
|
class InfoSubRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using pointer = std::shared_ptr<InfoSubRequest>;
|
||||||
|
|
||||||
|
virtual ~InfoSubRequest() = default;
|
||||||
|
|
||||||
|
virtual Json::Value
|
||||||
|
doClose() = 0;
|
||||||
|
virtual Json::Value
|
||||||
|
doStatus(Json::Value const&) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
/** Manages a client's subscription to data feeds.
|
/** Manages a client's subscription to data feeds.
|
||||||
*/
|
*/
|
||||||
@@ -205,13 +216,13 @@ public:
|
|||||||
deleteSubAccountHistory(AccountID const& account);
|
deleteSubAccountHistory(AccountID const& account);
|
||||||
|
|
||||||
void
|
void
|
||||||
clearPathRequest();
|
clearRequest();
|
||||||
|
|
||||||
void
|
void
|
||||||
setPathRequest(const std::shared_ptr<PathRequest>& req);
|
setRequest(const std::shared_ptr<InfoSubRequest>& req);
|
||||||
|
|
||||||
std::shared_ptr<PathRequest> const&
|
std::shared_ptr<InfoSubRequest> const&
|
||||||
getPathRequest();
|
getRequest();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::mutex mLock;
|
std::mutex mLock;
|
||||||
@@ -221,7 +232,7 @@ private:
|
|||||||
Source& m_source;
|
Source& m_source;
|
||||||
hash_set<AccountID> realTimeSubscriptions_;
|
hash_set<AccountID> realTimeSubscriptions_;
|
||||||
hash_set<AccountID> normalSubscriptions_;
|
hash_set<AccountID> normalSubscriptions_;
|
||||||
std::shared_ptr<PathRequest> mPathRequest;
|
std::shared_ptr<InfoSubRequest> request_;
|
||||||
std::uint64_t mSeq;
|
std::uint64_t mSeq;
|
||||||
hash_set<AccountID> accountHistorySubscriptions_;
|
hash_set<AccountID> accountHistorySubscriptions_;
|
||||||
|
|
||||||
|
|||||||
@@ -119,21 +119,21 @@ InfoSub::deleteSubAccountHistory(AccountID const& account)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
InfoSub::clearPathRequest()
|
InfoSub::clearRequest()
|
||||||
{
|
{
|
||||||
mPathRequest.reset();
|
request_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
InfoSub::setPathRequest(const std::shared_ptr<PathRequest>& req)
|
InfoSub::setRequest(const std::shared_ptr<InfoSubRequest>& req)
|
||||||
{
|
{
|
||||||
mPathRequest = req;
|
request_ = req;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::shared_ptr<PathRequest>&
|
const std::shared_ptr<InfoSubRequest>&
|
||||||
InfoSub::getPathRequest()
|
InfoSub::getRequest()
|
||||||
{
|
{
|
||||||
return mPathRequest;
|
return request_;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#ifndef RIPPLE_PROTOCOL_BOOK_H_INCLUDED
|
#ifndef RIPPLE_PROTOCOL_BOOK_H_INCLUDED
|
||||||
#define RIPPLE_PROTOCOL_BOOK_H_INCLUDED
|
#define RIPPLE_PROTOCOL_BOOK_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/basics/CountedObject.h>
|
||||||
#include <ripple/protocol/Issue.h>
|
#include <ripple/protocol/Issue.h>
|
||||||
#include <boost/utility/base_from_member.hpp>
|
#include <boost/utility/base_from_member.hpp>
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ namespace ripple {
|
|||||||
The order book is a pair of Issues called in and out.
|
The order book is a pair of Issues called in and out.
|
||||||
@see Issue.
|
@see Issue.
|
||||||
*/
|
*/
|
||||||
class Book
|
class Book final : public CountedObject<Book>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Issue in;
|
Issue in;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#ifndef RIPPLE_PROTOCOL_STAMOUNT_H_INCLUDED
|
#ifndef RIPPLE_PROTOCOL_STAMOUNT_H_INCLUDED
|
||||||
#define RIPPLE_PROTOCOL_STAMOUNT_H_INCLUDED
|
#define RIPPLE_PROTOCOL_STAMOUNT_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/basics/CountedObject.h>
|
||||||
#include <ripple/basics/IOUAmount.h>
|
#include <ripple/basics/IOUAmount.h>
|
||||||
#include <ripple/basics/LocalValue.h>
|
#include <ripple/basics/LocalValue.h>
|
||||||
#include <ripple/basics/XRPAmount.h>
|
#include <ripple/basics/XRPAmount.h>
|
||||||
@@ -40,7 +41,7 @@ namespace ripple {
|
|||||||
// Wire form:
|
// Wire form:
|
||||||
// High 8 bits are (offset+142), legal range is, 80 to 22 inclusive
|
// High 8 bits are (offset+142), legal range is, 80 to 22 inclusive
|
||||||
// Low 56 bits are value, legal range is 10^15 to (10^16 - 1) inclusive
|
// Low 56 bits are value, legal range is 10^15 to (10^16 - 1) inclusive
|
||||||
class STAmount : public STBase
|
class STAmount final : public STBase, public CountedObject<STAmount>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using mantissa_type = std::uint64_t;
|
using mantissa_type = std::uint64_t;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#ifndef RIPPLE_PROTOCOL_STPATHSET_H_INCLUDED
|
#ifndef RIPPLE_PROTOCOL_STPATHSET_H_INCLUDED
|
||||||
#define RIPPLE_PROTOCOL_STPATHSET_H_INCLUDED
|
#define RIPPLE_PROTOCOL_STPATHSET_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/basics/CountedObject.h>
|
||||||
#include <ripple/json/json_value.h>
|
#include <ripple/json/json_value.h>
|
||||||
#include <ripple/protocol/SField.h>
|
#include <ripple/protocol/SField.h>
|
||||||
#include <ripple/protocol/STBase.h>
|
#include <ripple/protocol/STBase.h>
|
||||||
@@ -30,7 +31,7 @@
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
class STPathElement
|
class STPathElement final : public CountedObject<STPathElement>
|
||||||
{
|
{
|
||||||
unsigned int mType;
|
unsigned int mType;
|
||||||
AccountID mAccountID;
|
AccountID mAccountID;
|
||||||
@@ -114,7 +115,7 @@ private:
|
|||||||
get_hash(STPathElement const& element);
|
get_hash(STPathElement const& element);
|
||||||
};
|
};
|
||||||
|
|
||||||
class STPath
|
class STPath final : public CountedObject<STPath>
|
||||||
{
|
{
|
||||||
std::vector<STPathElement> mPath;
|
std::vector<STPathElement> mPath;
|
||||||
|
|
||||||
@@ -172,7 +173,7 @@ public:
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
// A set of zero or more payment paths
|
// A set of zero or more payment paths
|
||||||
class STPathSet final : public STBase
|
class STPathSet final : public STBase, public CountedObject<STPathSet>
|
||||||
{
|
{
|
||||||
std::vector<STPath> value;
|
std::vector<STPath> value;
|
||||||
|
|
||||||
|
|||||||
@@ -51,25 +51,25 @@ doPathFind(RPC::JsonContext& context)
|
|||||||
if (sSubCommand == "create")
|
if (sSubCommand == "create")
|
||||||
{
|
{
|
||||||
context.loadType = Resource::feeHighBurdenRPC;
|
context.loadType = Resource::feeHighBurdenRPC;
|
||||||
context.infoSub->clearPathRequest();
|
context.infoSub->clearRequest();
|
||||||
return context.app.getPathRequests().makePathRequest(
|
return context.app.getPathRequests().makePathRequest(
|
||||||
context.infoSub, lpLedger, context.params);
|
context.infoSub, lpLedger, context.params);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sSubCommand == "close")
|
if (sSubCommand == "close")
|
||||||
{
|
{
|
||||||
PathRequest::pointer request = context.infoSub->getPathRequest();
|
InfoSubRequest::pointer request = context.infoSub->getRequest();
|
||||||
|
|
||||||
if (!request)
|
if (!request)
|
||||||
return rpcError(rpcNO_PF_REQUEST);
|
return rpcError(rpcNO_PF_REQUEST);
|
||||||
|
|
||||||
context.infoSub->clearPathRequest();
|
context.infoSub->clearRequest();
|
||||||
return request->doClose(context.params);
|
return request->doClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sSubCommand == "status")
|
if (sSubCommand == "status")
|
||||||
{
|
{
|
||||||
PathRequest::pointer request = context.infoSub->getPathRequest();
|
InfoSubRequest::pointer request = context.infoSub->getRequest();
|
||||||
|
|
||||||
if (!request)
|
if (!request)
|
||||||
return rpcError(rpcNO_PF_REQUEST);
|
return rpcError(rpcNO_PF_REQUEST);
|
||||||
|
|||||||
@@ -223,7 +223,8 @@ checkPayment(
|
|||||||
if (auto ledger = app.openLedger().current())
|
if (auto ledger = app.openLedger().current())
|
||||||
{
|
{
|
||||||
Pathfinder pf(
|
Pathfinder pf(
|
||||||
std::make_shared<RippleLineCache>(ledger),
|
std::make_shared<RippleLineCache>(
|
||||||
|
ledger, app.journal("RippleLineCache")),
|
||||||
srcAddressID,
|
srcAddressID,
|
||||||
*dstAccountID,
|
*dstAccountID,
|
||||||
sendMax.issue().currency,
|
sendMax.issue().currency,
|
||||||
|
|||||||
105
src/test/basics/join_test.cpp
Normal file
105
src/test/basics/join_test.cpp
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2022 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or 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 <test/jtx/Account.h>
|
||||||
|
|
||||||
|
#include <ripple/basics/join.h>
|
||||||
|
#include <ripple/beast/unit_test.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
struct join_test : beast::unit_test::suite
|
||||||
|
{
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
auto test = [this](auto collectionanddelimiter, std::string expected) {
|
||||||
|
std::stringstream ss;
|
||||||
|
// Put something else in the buffer before and after to ensure that
|
||||||
|
// the << operator returns the stream correctly.
|
||||||
|
ss << "(" << collectionanddelimiter << ")";
|
||||||
|
auto const str = ss.str();
|
||||||
|
BEAST_EXPECT(str.substr(1, str.length() - 2) == expected);
|
||||||
|
BEAST_EXPECT(str.front() == '(');
|
||||||
|
BEAST_EXPECT(str.back() == ')');
|
||||||
|
};
|
||||||
|
|
||||||
|
// C++ array
|
||||||
|
test(
|
||||||
|
CollectionAndDelimiter(std::array<int, 4>{2, -1, 5, 10}, "/"),
|
||||||
|
"2/-1/5/10");
|
||||||
|
// One item C++ array edge case
|
||||||
|
test(
|
||||||
|
CollectionAndDelimiter(std::array<std::string, 1>{"test"}, " & "),
|
||||||
|
"test");
|
||||||
|
// Empty C++ array edge case
|
||||||
|
test(CollectionAndDelimiter(std::array<int, 0>{}, ","), "");
|
||||||
|
{
|
||||||
|
// C-style array
|
||||||
|
char letters[4]{'w', 'a', 's', 'd'};
|
||||||
|
test(CollectionAndDelimiter(letters, std::to_string(0)), "w0a0s0d");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Auto sized C-style array
|
||||||
|
std::string words[]{"one", "two", "three", "four"};
|
||||||
|
test(CollectionAndDelimiter(words, "\n"), "one\ntwo\nthree\nfour");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// One item C-style array edge case
|
||||||
|
std::string words[]{"thing"};
|
||||||
|
test(CollectionAndDelimiter(words, "\n"), "thing");
|
||||||
|
}
|
||||||
|
// Initializer list
|
||||||
|
test(
|
||||||
|
CollectionAndDelimiter(std::initializer_list<size_t>{19, 25}, "+"),
|
||||||
|
"19+25");
|
||||||
|
// vector
|
||||||
|
test(
|
||||||
|
CollectionAndDelimiter(std::vector<int>{0, 42}, std::to_string(99)),
|
||||||
|
"09942");
|
||||||
|
{
|
||||||
|
// vector with one item edge case
|
||||||
|
using namespace jtx;
|
||||||
|
test(
|
||||||
|
CollectionAndDelimiter(
|
||||||
|
std::vector<Account>{Account::master}, "xxx"),
|
||||||
|
Account::master.human());
|
||||||
|
}
|
||||||
|
// empty vector edge case
|
||||||
|
test(CollectionAndDelimiter(std::vector<uint256>{}, ","), "");
|
||||||
|
// C-style string
|
||||||
|
test(CollectionAndDelimiter("string", " "), "s t r i n g");
|
||||||
|
// Empty C-style string edge case
|
||||||
|
test(CollectionAndDelimiter("", "*"), "");
|
||||||
|
// Single char C-style string edge case
|
||||||
|
test(CollectionAndDelimiter("x", "*"), "x");
|
||||||
|
// std::string
|
||||||
|
test(CollectionAndDelimiter(std::string{"string"}, "-"), "s-t-r-i-n-g");
|
||||||
|
// Empty std::string edge case
|
||||||
|
test(CollectionAndDelimiter(std::string{""}, "*"), "");
|
||||||
|
// Single char std::string edge case
|
||||||
|
test(CollectionAndDelimiter(std::string{"y"}, "*"), "y");
|
||||||
|
}
|
||||||
|
}; // namespace test
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(join, ripple_basics, ripple);
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace ripple
|
||||||
@@ -33,7 +33,8 @@ paths::operator()(Env& env, JTx& jt) const
|
|||||||
auto const to = env.lookup(jv[jss::Destination].asString());
|
auto const to = env.lookup(jv[jss::Destination].asString());
|
||||||
auto const amount = amountFromJson(sfAmount, jv[jss::Amount]);
|
auto const amount = amountFromJson(sfAmount, jv[jss::Amount]);
|
||||||
Pathfinder pf(
|
Pathfinder pf(
|
||||||
std::make_shared<RippleLineCache>(env.current()),
|
std::make_shared<RippleLineCache>(
|
||||||
|
env.current(), env.app().journal("RippleLineCache")),
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
in_.currency,
|
in_.currency,
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ class GetCounts_test : public beast::unit_test::suite
|
|||||||
|
|
||||||
Json::Value result;
|
Json::Value result;
|
||||||
{
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
// Add a little delay so the App's "uptime" will have a value.
|
||||||
|
std::this_thread::sleep_for(1s);
|
||||||
// check counts with no transactions posted
|
// check counts with no transactions posted
|
||||||
result = env.rpc("get_counts")[jss::result];
|
result = env.rpc("get_counts")[jss::result];
|
||||||
BEAST_EXPECT(result[jss::status] == "success");
|
BEAST_EXPECT(result[jss::status] == "success");
|
||||||
|
|||||||
Reference in New Issue
Block a user