Clean up Pathfinder.

* Restrict to 80-columns and other style cleanups.
* Make pathfinding a free function and hide the class Pathfinder.
* Split off unrelated utility functions into separate files.
This commit is contained in:
Tom Ritchford
2014-10-09 13:57:22 -04:00
parent 05a04aa801
commit 6904e66384
17 changed files with 1052 additions and 668 deletions

View File

@@ -24,6 +24,7 @@
#include <ripple/common/RippleSSLContext.h> #include <ripple/common/RippleSSLContext.h>
#include <ripple/app/main/Tuning.h> #include <ripple/app/main/Tuning.h>
#include <ripple/app/misc/ProofOfWorkFactory.h> #include <ripple/app/misc/ProofOfWorkFactory.h>
#include <ripple/app/paths/FindPaths.h>
#include <ripple/core/LoadFeeTrack.h> #include <ripple/core/LoadFeeTrack.h>
#include <ripple/rpc/Manager.h> #include <ripple/rpc/Manager.h>
#include <ripple/nodestore/Database.h> #include <ripple/nodestore/Database.h>
@@ -652,7 +653,7 @@ public:
updateTables (); updateTables ();
m_amendmentTable->addInitial(); m_amendmentTable->addInitial();
Pathfinder::initPathTable (); initializePathfinding ();
m_ledgerMaster->setMinValidations (getConfig ().VALIDATION_QUORUM); m_ledgerMaster->setMinValidations (getConfig ().VALIDATION_QUORUM);

View File

@@ -0,0 +1,93 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 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 <ripple/app/paths/AccountCurrencies.h>
namespace ripple {
CurrencySet accountSourceCurrencies (
RippleAddress const& raAccountID,
RippleLineCache::ref lrCache,
bool includeXRP)
{
CurrencySet currencies;
// YYY Only bother if they are above reserve
if (includeXRP)
currencies.insert (xrpCurrency());
// List of ripple lines.
auto& rippleLines (lrCache->getRippleLines (raAccountID.getAccountID ()));
for (auto const& item : rippleLines)
{
auto rspEntry = (RippleState*) item.get ();
assert (rspEntry);
if (!rspEntry)
continue;
auto& saBalance = rspEntry->getBalance ();
// Filter out non
if (saBalance > zero
// Have IOUs to send.
|| (rspEntry->getLimitPeer ()
// Peer extends credit.
&& ((-saBalance) < rspEntry->getLimitPeer ()))) // Credit left.
{
currencies.insert (saBalance.getCurrency ());
}
}
currencies.erase (badCurrency());
return currencies;
}
CurrencySet accountDestCurrencies (
RippleAddress const& raAccountID,
RippleLineCache::ref lrCache,
bool includeXRP)
{
CurrencySet currencies;
if (includeXRP)
currencies.insert (xrpCurrency());
// Even if account doesn't exist
// List of ripple lines.
auto& rippleLines (lrCache->getRippleLines (raAccountID.getAccountID ()));
for (auto const& item : rippleLines)
{
auto rspEntry = (RippleState*) item.get ();
assert (rspEntry);
if (!rspEntry)
continue;
auto& saBalance = rspEntry->getBalance ();
if (saBalance < rspEntry->getLimit ()) // Can take more
currencies.insert (saBalance.getCurrency ());
}
currencies.erase (badCurrency());
return currencies;
}
} // ripple

View File

@@ -0,0 +1,37 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 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 RIPPLED_RIPPLE_APP_PATHS_ACCOUNTCURRENCIES_H
#define RIPPLED_RIPPLE_APP_PATHS_ACCOUNTCURRENCIES_H
namespace ripple {
CurrencySet accountDestCurrencies
(RippleAddress const& raAccountID,
RippleLineCache::ref cache,
bool includeXRP);
CurrencySet accountSourceCurrencies
(RippleAddress const& raAccountID,
RippleLineCache::ref lrLedger,
bool includeXRP);
} // ripple
#endif

View File

@@ -0,0 +1,73 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 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 <ripple/app/paths/Credit.h>
namespace ripple {
STAmount creditLimit (
LedgerEntrySet& ledger,
Account const& account,
Account const& issuer,
Currency const& currency)
{
STAmount result ({currency, account});
auto sleRippleState = ledger.entryCache (ltRIPPLE_STATE,
Ledger::getRippleStateIndex (account, issuer, currency));
if (sleRippleState)
{
result = sleRippleState->getFieldAmount (
account < issuer ? sfLowLimit : sfHighLimit);
result.setIssuer (account);
}
assert (result.getIssuer () == account);
assert (result.getCurrency () == currency);
return result;
}
STAmount creditBalance (
LedgerEntrySet& ledger,
Account const& account,
Account const& issuer,
Currency const& currency)
{
STAmount result ({currency, account});
auto sleRippleState = ledger.entryCache (ltRIPPLE_STATE,
Ledger::getRippleStateIndex (account, issuer, currency));
if (sleRippleState)
{
result = sleRippleState->getFieldAmount (sfBalance);
if (account < issuer)
result.negate ();
result.setIssuer (account);
}
assert (result.getIssuer () == account);
assert (result.getCurrency () == currency);
return result;
}
} // ripple

View File

@@ -0,0 +1,52 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 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 RIPPLED_RIPPLE_APP_PATHS_CREDIT_H
#define RIPPLED_RIPPLE_APP_PATHS_CREDIT_H
namespace ripple {
/** Calculate the maximum amount of IOUs that an account can hold
@param ledger the ledger to check against.
@param account the account of interest.
@param issuer the issuer of the IOU.
@param currency the IOU to check.
@return The maximum amount that can be held.
*/
STAmount creditLimit (
LedgerEntrySet& ledger,
Account const& account,
Account const& issuer,
Currency const& currency);
/** Returns the amount of IOUs issued by issuer that are held by an account
@param ledger the ledger to check against.
@param account the account of interest.
@param issuer the issuer of the IOU.
@param currency the IOU to check.
*/
STAmount creditBalance (
LedgerEntrySet& ledger,
Account const& account,
Account const& issuer,
Currency const& currency);
} // ripple
#endif

View File

@@ -0,0 +1,58 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 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 <ripple/app/paths/FindPaths.h>
#include <ripple/app/paths/Pathfinder.h>
namespace ripple {
bool findPathsForOneIssuer (
RippleLineCache::ref cache,
Account const& srcAccount,
Account const& dstAccount,
Issue const& srcIssue,
STAmount const& dstAmount,
int searchLevel,
unsigned int const maxPaths,
STPathSet& pathsOut,
STPath& fullLiquidityPath)
{
Pathfinder pf (
cache,
srcAccount,
dstAccount,
srcIssue.currency,
srcIssue.account,
dstAmount);
if (!pf.findPaths (searchLevel))
return false;
// Yes, ensurePathsAreComplete is called BEFORE we compute the paths...
pf.ensurePathsAreComplete (pathsOut);
pathsOut = pf.getBestPaths(maxPaths, fullLiquidityPath);
return true;
}
void initializePathfinding ()
{
Pathfinder::initPathTable ();
}
} // ripple

View File

@@ -0,0 +1,56 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 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 RIPPLED_RIPPLE_APP_PATHS_FINDPATHS_H
#define RIPPLED_RIPPLE_APP_PATHS_FINDPATHS_H
namespace ripple {
bool findPathsForOneIssuer (
RippleLineCache::ref cache,
Account const& srcAccount,
Account const& dstAccount,
Issue const& srcIssue,
STAmount const& dstAmount,
/** searchLevel is the maximum search level allowed in an output path. */
int searchLevel,
/** maxPaths is the maximum number of paths that can be returned in
pathsOut. */
unsigned int const maxPaths,
/** On input, pathsOut contains any paths you want to ensure are included if
still good.
On output, pathsOut will have any additional paths found. Only
non-default paths without source or destination will be added. */
STPathSet& pathsOut,
/** On input, fullLiquidityPath must be an empty STPath.
On output, if fullLiquidityPath is non-empty, it contains one extra path
that can move the entire liquidity requested. */
STPath& fullLiquidityPath);
void initializePathfinding ();
} // ripple
#endif

View File

@@ -17,27 +17,31 @@
*/ */
//============================================================================== //==============================================================================
#include <ripple/types/UintTypes.h> #include <ripple/app/paths/AccountCurrencies.h>
#include <ripple/app/paths/FindPaths.h>
#include <ripple/core/Config.h> #include <ripple/core/Config.h>
#include <ripple/core/LoadFeeTrack.h> #include <ripple/core/LoadFeeTrack.h>
#include <ripple/types/UintTypes.h>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <tuple> #include <tuple>
namespace ripple { namespace ripple {
PathRequest::PathRequest ( PathRequest::PathRequest (
const std::shared_ptr<InfoSub>& subscriber, int id, PathRequests& owner, const std::shared_ptr<InfoSub>& subscriber,
int id,
PathRequests& owner,
beast::Journal journal) beast::Journal journal)
: m_journal (journal) : m_journal (journal)
, mOwner (owner) , mOwner (owner)
, wpSubscriber (subscriber) , wpSubscriber (subscriber)
, jvStatus (Json::objectValue) , jvStatus (Json::objectValue)
, bValid (false) , bValid (false)
, mLastIndex (0) , mLastIndex (0)
, mInProgress (false) , mInProgress (false)
, iLastLevel (0) , iLastLevel (0)
, bLastSuccess (false) , bLastSuccess (false)
, iIdentifier (id) , iIdentifier (id)
{ {
if (m_journal.debug) if (m_journal.debug)
m_journal.debug << iIdentifier << " created"; m_journal.debug << iIdentifier << " created";
@@ -45,7 +49,8 @@ const std::shared_ptr<InfoSub>& subscriber, int id, PathRequests& owner,
} }
static std::string const get_milli_diff ( static std::string const get_milli_diff (
boost::posix_time::ptime const& after, boost::posix_time::ptime boost::posix_time::ptime const& after,
boost::posix_time::ptime
const& before) const& before)
{ {
return beast::lexicalCastThrow <std::string> ( return beast::lexicalCastThrow <std::string> (
@@ -174,7 +179,7 @@ bool PathRequest::isValid (RippleLineCache::ref crCache)
bool const disallowXRP ( bool const disallowXRP (
asDst->peekSLE ().getFlags() & lsfDisallowXRP); asDst->peekSLE ().getFlags() & lsfDisallowXRP);
auto usDestCurrID = usAccountDestCurrencies ( auto usDestCurrID = accountDestCurrencies (
raDstAccount, crCache, !disallowXRP); raDstAccount, crCache, !disallowXRP);
for (auto const& currency : usDestCurrID) for (auto const& currency : usDestCurrID)
@@ -223,11 +228,15 @@ Json::Value PathRequest::doCreate (
{ {
if (bValid) if (bValid)
{ {
m_journal.debug << iIdentifier << " valid: " << raSrcAccount.humanAccountID (); m_journal.debug << iIdentifier
m_journal.debug << iIdentifier << " Deliver: " << saDstAmount.getFullText (); << " valid: " << raSrcAccount.humanAccountID ();
m_journal.debug << iIdentifier
<< " Deliver: " << saDstAmount.getFullText ();
} }
else else
{
m_journal.debug << iIdentifier << " invalid"; m_journal.debug << iIdentifier << " invalid";
}
} }
valid = bValid; valid = bValid;
@@ -268,10 +277,12 @@ int PathRequest::parseJson (Json::Value const& jvParams, bool complete)
if (jvParams.isMember ("destination_amount")) if (jvParams.isMember ("destination_amount"))
{ {
if (! amountFromJsonNoThrow (saDstAmount, jvParams["destination_amount"]) || if (! amountFromJsonNoThrow (
(saDstAmount.getCurrency ().isZero () && saDstAmount.getIssuer ().isNonZero ()) || saDstAmount, jvParams["destination_amount"]) ||
(saDstAmount.getCurrency () == badCurrency()) || (saDstAmount.getCurrency ().isZero () &&
saDstAmount <= zero) saDstAmount.getIssuer ().isNonZero ()) ||
(saDstAmount.getCurrency () == badCurrency ()) ||
saDstAmount <= zero)
{ {
jvStatus = rpcError (rpcDST_AMT_MALFORMED); jvStatus = rpcError (rpcDST_AMT_MALFORMED);
return PFR_PJ_INVALID; return PFR_PJ_INVALID;
@@ -363,16 +374,16 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
if (sourceCurrencies.empty ()) if (sourceCurrencies.empty ())
{ {
auto usCurrencies = auto usCurrencies =
usAccountSourceCurrencies (raSrcAccount, cache, true); accountSourceCurrencies (raSrcAccount, cache, true);
bool sameAccount = raSrcAccount == raDstAccount; bool sameAccount = raSrcAccount == raDstAccount;
for (auto const& c: usCurrencies) for (auto const& c: usCurrencies)
{ {
if (!sameAccount || (c != saDstAmount.getCurrency ())) if (!sameAccount || (c != saDstAmount.getCurrency ()))
{ {
if (c.isZero ()) if (c.isZero ())
sourceCurrencies.insert (std::make_pair (c, xrpAccount())); sourceCurrencies.insert ({c, xrpAccount()});
else else
sourceCurrencies.insert (std::make_pair (c, raSrcAccount.getAccountID ())); sourceCurrencies.insert ({c, raSrcAccount.getAccountID ()});
} }
} }
} }
@@ -390,26 +401,30 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
bool loaded = getApp().getFeeTrack().isLoadedLocal(); bool loaded = getApp().getFeeTrack().isLoadedLocal();
if (iLevel == 0) if (iLevel == 0)
{ // first pass {
// first pass
if (loaded || fast) if (loaded || fast)
iLevel = getConfig().PATH_SEARCH_FAST; iLevel = getConfig().PATH_SEARCH_FAST;
else else
iLevel = getConfig().PATH_SEARCH; iLevel = getConfig().PATH_SEARCH;
} }
else if ((iLevel == getConfig().PATH_SEARCH_FAST) && !fast) else if ((iLevel == getConfig().PATH_SEARCH_FAST) && !fast)
{ // leaving fast pathfinding {
// leaving fast pathfinding
iLevel = getConfig().PATH_SEARCH; iLevel = getConfig().PATH_SEARCH;
if (loaded && (iLevel > getConfig().PATH_SEARCH_FAST)) if (loaded && (iLevel > getConfig().PATH_SEARCH_FAST))
--iLevel; --iLevel;
} }
else if (bLastSuccess) else if (bLastSuccess)
{ // decrement, if possible {
// decrement, if possible
if (iLevel > getConfig().PATH_SEARCH || if (iLevel > getConfig().PATH_SEARCH ||
(loaded && (iLevel > getConfig().PATH_SEARCH_FAST))) (loaded && (iLevel > getConfig().PATH_SEARCH_FAST)))
--iLevel; --iLevel;
} }
else else
{ // adjust as needed {
// adjust as needed
if (!loaded && (iLevel < getConfig().PATH_SEARCH_MAX)) if (!loaded && (iLevel < getConfig().PATH_SEARCH_MAX))
++iLevel; ++iLevel;
if (loaded && (iLevel > getConfig().PATH_SEARCH_FAST)) if (loaded && (iLevel > getConfig().PATH_SEARCH_FAST))
@@ -423,7 +438,7 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
for (auto const& currIssuer: sourceCurrencies) for (auto const& currIssuer: sourceCurrencies)
{ {
{ {
STAmount test ({currIssuer.first, currIssuer.second}, 1); STAmount test (currIssuer, 1);
if (m_journal.debug) if (m_journal.debug)
{ {
m_journal.debug m_journal.debug
@@ -431,26 +446,34 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
<< " Trying to find paths: " << test.getFullText (); << " Trying to find paths: " << test.getFullText ();
} }
} }
bool valid;
STPathSet& spsPaths = mContext[currIssuer]; STPathSet& spsPaths = mContext[currIssuer];
Pathfinder pf (cache, raSrcAccount, raDstAccount, STPath fullLiquidityPath;
currIssuer.first, currIssuer.second, saDstAmount, valid); auto valid = findPathsForOneIssuer (
cache,
raSrcAccount.getAccountID(),
raDstAccount.getAccountID(),
currIssuer,
saDstAmount,
iLevel,
4, // iMaxPaths
spsPaths,
fullLiquidityPath);
CondLog (!valid, lsDEBUG, PathRequest) CondLog (!valid, lsDEBUG, PathRequest)
<< iIdentifier << " PF request not valid"; << iIdentifier << " PF request not valid";
STPath extraPath; if (valid)
if (valid && pf.findPaths (iLevel, 4, spsPaths, extraPath))
{ {
LedgerEntrySet lesSandbox (cache->getLedger (), tapNONE); LedgerEntrySet lesSandbox (cache->getLedger (), tapNONE);
auto& account = currIssuer.second.isNonZero () auto& account = !isXRP (currIssuer.account)
? Account(currIssuer.second) ? currIssuer.account
: isXRP (currIssuer.first) : isXRP (currIssuer.currency)
? xrpAccount() ? xrpAccount()
: raSrcAccount.getAccountID (); : raSrcAccount.getAccountID ();
STAmount saMaxAmount ({currIssuer.first, account}, 1); STAmount saMaxAmount ({currIssuer.currency, account}, 1);
saMaxAmount.negate (); saMaxAmount.negate ();
m_journal.debug << iIdentifier << " Paths found, calling rippleCalc"; m_journal.debug << iIdentifier
<< " Paths found, calling rippleCalc";
auto rc = path::RippleCalc::rippleCalculate ( auto rc = path::RippleCalc::rippleCalculate (
lesSandbox, lesSandbox,
saMaxAmount, saMaxAmount,
@@ -459,12 +482,12 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
raSrcAccount.getAccountID (), raSrcAccount.getAccountID (),
spsPaths); spsPaths);
if (!extraPath.empty() && if (!fullLiquidityPath.empty() &&
(rc.result () == terNO_LINE || rc.result () == tecPATH_PARTIAL)) (rc.result () == terNO_LINE || rc.result () == tecPATH_PARTIAL))
{ {
m_journal.debug m_journal.debug
<< iIdentifier << " Trying with an extra path element"; << iIdentifier << " Trying with an extra path element";
spsPaths.push_back (extraPath); spsPaths.push_back (fullLiquidityPath);
rc = path::RippleCalc::rippleCalculate (lesSandbox, rc = path::RippleCalc::rippleCalculate (lesSandbox,
saMaxAmount, saMaxAmount,
saDstAmount, saDstAmount,
@@ -484,8 +507,8 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
if (rc.result () == tesSUCCESS) if (rc.result () == tesSUCCESS)
{ {
Json::Value jvEntry (Json::objectValue); Json::Value jvEntry (Json::objectValue);
jvEntry["source_amount"] = rc.actualAmountIn.getJson (0); jvEntry["source_amount"] = rc.actualAmountIn.getJson (0);
jvEntry["paths_computed"] = spsPaths.getJson (0); jvEntry["paths_computed"] = spsPaths.getJson (0);
found = true; found = true;
jvArray.append (jvEntry); jvArray.append (jvEntry);
} }

View File

@@ -45,13 +45,13 @@ public:
typedef const pointer& ref; typedef const pointer& ref;
typedef const wptr& wref; typedef const wptr& wref;
// TODO(tom): Use Issue instead!
typedef std::pair<Currency, Account> CurrencyIssuer;
public: public:
// VFALCO TODO Break the cyclic dependency on InfoSub // VFALCO TODO Break the cyclic dependency on InfoSub
PathRequest (std::shared_ptr <InfoSub> const& subscriber, PathRequest (
int id, PathRequests&, beast::Journal journal); std::shared_ptr <InfoSub> const& subscriber,
int id,
PathRequests&,
beast::Journal journal);
~PathRequest (); ~PathRequest ();
@@ -97,8 +97,8 @@ private:
RippleAddress raDstAccount; RippleAddress raDstAccount;
STAmount saDstAmount; STAmount saDstAmount;
std::set<CurrencyIssuer> sciSourceCurrencies; std::set<Issue> sciSourceCurrencies;
std::map<CurrencyIssuer, STPathSet> mContext; std::map<Issue, STPathSet> mContext;
bool bValid; bool bValid;

View File

@@ -17,6 +17,8 @@
*/ */
//============================================================================== //==============================================================================
#include <ripple/app/paths/Credit.h>
namespace ripple { namespace ripple {
// OPTIMIZE: When calculating path increment, note if increment consumes all // OPTIMIZE: When calculating path increment, note if increment consumes all
@@ -314,13 +316,13 @@ TER PathState::pushNode (
if (resultCode == tesSUCCESS) if (resultCode == tesSUCCESS)
{ {
STAmount saOwed = credit_balance (lesEntries, STAmount saOwed = creditBalance (lesEntries,
node.account_, backNode.account_, node.account_, backNode.account_,
node.issue_.currency); node.issue_.currency);
STAmount saLimit; STAmount saLimit;
if (saOwed <= zero) { if (saOwed <= zero) {
saLimit = credit_limit (lesEntries, saLimit = creditLimit (lesEntries,
node.account_, node.account_,
backNode.account_, backNode.account_,
node.issue_.currency); node.issue_.currency);

File diff suppressed because it is too large Load Diff

View File

@@ -33,112 +33,126 @@ class Pathfinder
public: public:
Pathfinder ( Pathfinder (
RippleLineCache::ref cache, RippleLineCache::ref cache,
RippleAddress const& srcAccountID, Account const& srcAccount,
RippleAddress const& dstAccountID, Account const& dstAccount,
Currency const& srcCurrencyID, Currency const& uSrcCurrency,
Account const& srcIssuerID, Account const& uSrcIssuer,
STAmount const& dstAmount, STAmount const& dstAmount);
bool& bValid);
static void initPathTable(); static void initPathTable ();
bool findPaths ( bool findPaths (int searchLevel);
int iLevel,
unsigned int const iMaxPaths,
STPathSet& spsDst,
STPath& spExtraPath);
private: void ensurePathsAreComplete (STPathSet&);
/* Get the best paths, up to maxPaths in number, from mCompletePaths.
On return, if fullLiquidityPath is not empty, then it contains the best
additional single path which can consume all the liquidity.
*/
STPathSet getBestPaths (int maxPaths, STPath& fullLiquidityPath);
enum NodeType
{
nt_SOURCE, // The source account: with an issuer account, if needed.
nt_ACCOUNTS, // Accounts that connect from this source/currency.
nt_BOOKS, // Order books that connect to this currency.
nt_XRP_BOOK, // The order book from this currency to XRP.
nt_DEST_BOOK, // The order book to the destination currency/issuer.
nt_DESTINATION // The destination account only.
};
// The PathType is a list of the NodeTypes for a path.
using PathType = std::vector <NodeType>;
// PaymentType represents the types of the source and destination currencies
// in a path request.
enum PaymentType enum PaymentType
{ {
pt_XRP_to_XRP, pt_XRP_to_XRP,
pt_XRP_to_nonXRP, pt_XRP_to_nonXRP,
pt_nonXRP_to_XRP, pt_nonXRP_to_XRP,
pt_nonXRP_to_same, pt_nonXRP_to_same, // Destination currency is the same as source.
pt_nonXRP_to_nonXRP pt_nonXRP_to_nonXRP // Destination currency is NOT the same as source.
}; };
enum NodeType private:
{ /*
nt_SOURCE, // The source account with an issuer account, if required Call graph of methoids
nt_ACCOUNTS, // Accounts that connect from this source/currency
nt_BOOKS, // Order books that connect to this currency
nt_XRP_BOOK, // The order book from this currency to XRP
nt_DEST_BOOK, // The order book to the destination currency/issuer
nt_DESTINATION // The destination account only
};
typedef std::vector<NodeType> PathType_t; findPaths:
typedef std::pair<int, PathType_t> CostedPath_t; addPathsForType:
typedef std::vector<CostedPath_t> CostedPathList_t; addLinks:
addLink:
getPathsOut
issueMatchesOrigin
isNoRippleOut:
isNoRipple
typedef std::pair<int, const char*> PathCost; ensurePathsAreComplete
typedef std::vector<PathCost> PathCostList;
typedef std::map<PaymentType, CostedPathList_t> PathTable; getBestPaths:
rippleCalculate
getPathLiquidity:
rippleCalculate
*/
/** Fill a CostedPathList_t from its description. */ // Add all paths of one type to mCompletePaths.
static void fillPaths(PaymentType type, STPathSet& addPathsForType (PathType const& type);
PathCostList const& costs);
/** @return true if any building paths are now complete. */ bool issueMatchesOrigin (Issue const&);
bool checkComplete (STPathSet& retPathSet);
static std::string pathTypeToString(PathType_t const&);
bool matchesOrigin (Issue const&);
int getPathsOut ( int getPathsOut (
Currency const& currency, Currency const& currency,
Account const& accountID, Account const& account,
bool isDestCurrency, bool isDestCurrency,
Account const& dest); Account const& dest);
void addLink( void addLink (
STPath const& currentPath, STPath const& currentPath,
STPathSet& incompletePaths, STPathSet& incompletePaths,
int addFlags); int addFlags);
void addLink( // Call addLink() for each path in currentPaths.
void addLinks (
STPathSet const& currentPaths, STPathSet const& currentPaths,
STPathSet& incompletePaths, STPathSet& incompletePaths,
int addFlags); int addFlags);
STPathSet& getPaths(PathType_t const& type, // Compute the liquidity for a path. Return tesSUCCESS if it has has enough
bool addComplete = true); // liquidity to be worth keeping, otherwise an error.
TER getPathLiquidity (
STPathSet filterPaths(int iMaxPaths, STPath const& path, // IN: The path to check.
STPath& extraPath); STAmount const& minDstAmount, // IN: The minimum output this path must
// deliver to be worth keeping.
TER checkPath (STPath const& path, STAmount const& minDestAmount, STAmount& amountOut, // OUT: The actual liquidity on the path.
STAmount& amount, uint64_t& quality) const; uint64_t& qualityOut) const; // OUT: The returned initial quality
// Does this path end on an account-to-account link whose last account has
// set the "no ripple" flag on the link?
bool isNoRippleOut (STPath const& currentPath); bool isNoRippleOut (STPath const& currentPath);
// Is the "no ripple" flag set from one account to another?
bool isNoRipple ( bool isNoRipple (
Account const& setByID, Account const& fromAccount,
Account const& setOnID, Account const& toAccount,
Currency const& currencyID); Currency const& currency);
// Our main table of paths Account mSrcAccount;
Account mDstAccount;
STAmount mDstAmount;
Currency mSrcCurrency;
Account mSrcIssuer;
STAmount mSrcAmount;
static PathTable mPathTable; Ledger::pointer mLedger;
static PathType_t makePath(char const*); LoadEvent::pointer m_loadEvent;
RippleLineCache::pointer mRLCache;
Account mSrcAccountID;
Account mDstAccountID;
STAmount mDstAmount;
Currency mSrcCurrencyID;
Account mSrcIssuerID;
STAmount mSrcAmount;
Ledger::pointer mLedger;
LoadEvent::pointer m_loadEvent;
RippleLineCache::pointer mRLCache;
STPathElement mSource; STPathElement mSource;
STPathSet mCompletePaths; STPathSet mCompletePaths;
std::map<PathType_t, STPathSet> mPaths; std::map<PathType, STPathSet> mPaths;
hash_map<Issue, int> mPathsOutCountMap; hash_map<Issue, int> mPathsOutCountMap;
@@ -146,51 +160,18 @@ private:
static std::uint32_t const afADD_ACCOUNTS = 0x001; static std::uint32_t const afADD_ACCOUNTS = 0x001;
// Add order books // Add order books
static std::uint32_t const afADD_BOOKS = 0x002; static std::uint32_t const afADD_BOOKS = 0x002;
// Add order book to XRP only // Add order book to XRP only
static std::uint32_t const afOB_XRP = 0x010; static std::uint32_t const afOB_XRP = 0x010;
// Must link to destination currency // Must link to destination currency
static std::uint32_t const afOB_LAST = 0x040; static std::uint32_t const afOB_LAST = 0x040;
// Destination account only // Destination account only
static std::uint32_t const afAC_LAST = 0x080; static std::uint32_t const afAC_LAST = 0x080;
}; };
CurrencySet usAccountDestCurrencies
(RippleAddress const& raAccountID,
RippleLineCache::ref cache,
bool includeXRP);
CurrencySet usAccountSourceCurrencies
(RippleAddress const& raAccountID,
RippleLineCache::ref lrLedger,
bool includeXRP);
/** Calculate the maximum amount of IOUs that an account can hold
@param ledger the ledger to check against.
@param account the account of interest.
@param issuer the issuer of the IOU.
@param currency the IOU to check.
@return The maximum amount that can be held.
*/
STAmount
credit_limit (
LedgerEntrySet& ledger, Account const& account,
Account const& issuer, Currency const& currency);
/** Returns the amount of IOUs issued by issuer that are held by an account
@param ledger the ledger to check against.
@param account the account of interest.
@param issuer the issuer of the IOU.
@param currency the IOU to check.
*/
STAmount
credit_balance (
LedgerEntrySet& ledger, Account const& account,
Account const& issuer, Currency const& currency);
} // ripple } // ripple
#endif #endif

View File

@@ -18,6 +18,7 @@
//============================================================================== //==============================================================================
#include <ripple/app/book/Quality.h> #include <ripple/app/book/Quality.h>
#include <ripple/app/paths/Credit.h>
#include <ripple/app/paths/cursor/RippleLiquidity.h> #include <ripple/app/paths/cursor/RippleLiquidity.h>
namespace ripple { namespace ripple {
@@ -46,8 +47,8 @@ TER PathCursor::reverseLiquidityForAccount () const
auto const isFinalNode = (nodeIndex_ == lastNodeIndex); auto const isFinalNode = (nodeIndex_ == lastNodeIndex);
// 0 quality means none has yet been determined. // 0 quality means none has yet been determined.
std::uint64_t uRateMax = 0; std::uint64_t uRateMax = 0
;
// Current is allowed to redeem to next. // Current is allowed to redeem to next.
const bool previousNodeIsAccount = !nodeIndex_ || const bool previousNodeIsAccount = !nodeIndex_ ||
previousNode().isAccount(); previousNode().isAccount();
@@ -80,7 +81,7 @@ TER PathCursor::reverseLiquidityForAccount () const
// For previousNodeIsAccount: // For previousNodeIsAccount:
// Previous account is already owed. // Previous account is already owed.
const STAmount saPrvOwed = (previousNodeIsAccount && nodeIndex_ != 0) const STAmount saPrvOwed = (previousNodeIsAccount && nodeIndex_ != 0)
? credit_balance (ledger(), ? creditBalance (ledger(),
node().account_, node().account_,
previousAccountID, previousAccountID,
node().issue_.currency) node().issue_.currency)
@@ -88,7 +89,7 @@ TER PathCursor::reverseLiquidityForAccount () const
// The limit amount that the previous account may owe. // The limit amount that the previous account may owe.
const STAmount saPrvLimit = (previousNodeIsAccount && nodeIndex_ != 0) const STAmount saPrvLimit = (previousNodeIsAccount && nodeIndex_ != 0)
? credit_limit (ledger(), ? creditLimit (ledger(),
node().account_, node().account_,
previousAccountID, previousAccountID,
node().issue_.currency) node().issue_.currency)
@@ -96,7 +97,7 @@ TER PathCursor::reverseLiquidityForAccount () const
// Next account is owed. // Next account is owed.
const STAmount saNxtOwed = (nextNodeIsAccount && nodeIndex_ != lastNodeIndex) const STAmount saNxtOwed = (nextNodeIsAccount && nodeIndex_ != lastNodeIndex)
? credit_balance (ledger(), ? creditBalance (ledger(),
node().account_, node().account_,
nextAccountID, nextAccountID,
node().issue_.currency) node().issue_.currency)

View File

@@ -17,6 +17,8 @@
*/ */
//============================================================================== //==============================================================================
#include <ripple/app/paths/AccountCurrencies.h>
#include <ripple/app/paths/FindPaths.h>
#include <ripple/rpc/impl/LegacyPathFind.h> #include <ripple/rpc/impl/LegacyPathFind.h>
namespace ripple { namespace ripple {
@@ -119,11 +121,10 @@ Json::Value doRipplePathFind (RPC::Context& context)
} }
else else
{ {
auto usCurrencies = usAccountSourceCurrencies (raSrc, cache, true); auto currencies = accountSourceCurrencies (raSrc, cache, true);
jvSrcCurrencies = Json::Value (Json::arrayValue); jvSrcCurrencies = Json::Value (Json::arrayValue);
for (auto const& uCurrency: usCurrencies) for (auto const& uCurrency: currencies)
{ {
Json::Value jvCurrency (Json::objectValue); Json::Value jvCurrency (Json::objectValue);
jvCurrency["currency"] = to_string(uCurrency); jvCurrency["currency"] = to_string(uCurrency);
@@ -134,7 +135,7 @@ Json::Value doRipplePathFind (RPC::Context& context)
// Fill in currencies destination will accept // Fill in currencies destination will accept
Json::Value jvDestCur (Json::arrayValue); Json::Value jvDestCur (Json::arrayValue);
auto usDestCurrID = usAccountDestCurrencies (raDst, cache, true); auto usDestCurrID = accountDestCurrencies (raDst, cache, true);
for (auto const& uCurrency: usDestCurrID) for (auto const& uCurrency: usDestCurrID)
jvDestCur.append (to_string (uCurrency)); jvDestCur.append (to_string (uCurrency));
@@ -177,11 +178,6 @@ Json::Value doRipplePathFind (RPC::Context& context)
return rpcError (rpcSRC_ISR_MALFORMED); return rpcError (rpcSRC_ISR_MALFORMED);
} }
STPathSet spsComputed;
bool bValid;
Pathfinder pf (cache, raSrc, raDst, uSrcCurrencyID,
uSrcIssuerID, saDstAmount, bValid);
int level = getConfig().PATH_SEARCH_OLD; int level = getConfig().PATH_SEARCH_OLD;
if ((getConfig().PATH_SEARCH_MAX > level) if ((getConfig().PATH_SEARCH_MAX > level)
&& !getApp().getFeeTrack().isLoadedLocal()) && !getApp().getFeeTrack().isLoadedLocal())
@@ -196,6 +192,7 @@ Json::Value doRipplePathFind (RPC::Context& context)
level = rLev; level = rLev;
} }
STPathSet spsComputed;
if (context.params_.isMember("paths")) if (context.params_.isMember("paths"))
{ {
STParsedJSONObject paths ("paths", context.params_["paths"]); STParsedJSONObject paths ("paths", context.params_["paths"]);
@@ -205,8 +202,18 @@ Json::Value doRipplePathFind (RPC::Context& context)
spsComputed = paths.object.get()->downcast<STPathSet> (); spsComputed = paths.object.get()->downcast<STPathSet> ();
} }
STPath extraPath; STPath fullLiquidityPath;
if (!bValid || !pf.findPaths (level, 4, spsComputed, extraPath)) auto valid = findPathsForOneIssuer(
cache,
raSrc.getAccountID(),
raDst.getAccountID(),
{uSrcCurrencyID, uSrcIssuerID},
saDstAmount,
level,
4, // iMaxPaths
spsComputed,
fullLiquidityPath);
if (!valid)
{ {
WriteLog (lsWARNING, RPCHandler) WriteLog (lsWARNING, RPCHandler)
<< "ripple_path_find: No paths found."; << "ripple_path_find: No paths found.";
@@ -241,13 +248,13 @@ Json::Value doRipplePathFind (RPC::Context& context)
<< " saMaxAmountAct=" << rc.actualAmountIn << " saMaxAmountAct=" << rc.actualAmountIn
<< " saDstAmountAct=" << rc.actualAmountOut; << " saDstAmountAct=" << rc.actualAmountOut;
if (extraPath.size() > 0 && if (fullLiquidityPath.size() > 0 &&
(rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL)) (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL))
{ {
WriteLog (lsDEBUG, PathRequest) WriteLog (lsDEBUG, PathRequest)
<< "Trying with an extra path element"; << "Trying with an extra path element";
spsComputed.push_back (extraPath); spsComputed.push_back (fullLiquidityPath);
lesSandbox.clear (); lesSandbox.clear ();
rc = path::RippleCalc::rippleCalculate ( rc = path::RippleCalc::rippleCalculate (
lesSandbox, lesSandbox,

View File

@@ -17,6 +17,7 @@
*/ */
//============================================================================== //==============================================================================
#include <ripple/app/paths/FindPaths.h>
#include <ripple/basics/StringUtilities.h> #include <ripple/basics/StringUtilities.h>
#include <ripple/rpc/impl/TransactionSign.h> #include <ripple/rpc/impl/TransactionSign.h>
#include <beast/unit_test.h> #include <beast/unit_test.h>
@@ -152,22 +153,20 @@ static Json::Value signPayment(
if (!lpf.isOk ()) if (!lpf.isOk ())
return rpcError (rpcTOO_BUSY); return rpcError (rpcTOO_BUSY);
bool bValid;
auto cache = std::make_shared<RippleLineCache> (lSnapshot); auto cache = std::make_shared<RippleLineCache> (lSnapshot);
Pathfinder pf ( STPath fullLiquidityPath;
auto valid = findPathsForOneIssuer (
cache, cache,
raSrcAddressID, raSrcAddressID.getAccountID(),
dstAccountID, dstAccountID.getAccountID(),
saSendMax.getCurrency (), saSendMax.issue (),
saSendMax.getIssuer (), amount,
amount, bValid); getConfig ().PATH_SEARCH_OLD,
4, // iMaxPaths
spsPaths,
fullLiquidityPath);
STPath extraPath; if (!valid)
if (!bValid ||
!pf.findPaths (getConfig ().PATH_SEARCH_OLD,
4,
spsPaths,
extraPath))
{ {
WriteLog (lsDEBUG, RPCHandler) WriteLog (lsDEBUG, RPCHandler)
<< "transactionSign: build_path: No paths found."; << "transactionSign: build_path: No paths found.";

View File

@@ -117,7 +117,5 @@
#include <ripple/app/main/ParameterTable.h> #include <ripple/app/main/ParameterTable.h>
#include <ripple/app/paths/PathState.h> #include <ripple/app/paths/PathState.h>
#include <ripple/app/paths/RippleCalc.h> #include <ripple/app/paths/RippleCalc.h>
#include <ripple/app/paths/Pathfinder.h>
#endif #endif

View File

@@ -29,5 +29,8 @@
#include <ripple/app/ledger/OrderBookIterator.cpp> #include <ripple/app/ledger/OrderBookIterator.cpp>
#include <ripple/app/consensus/DisputedTx.cpp> #include <ripple/app/consensus/DisputedTx.cpp>
#include <ripple/app/misc/HashRouter.cpp> #include <ripple/app/misc/HashRouter.cpp>
#include <ripple/app/paths/AccountCurrencies.cpp>
#include <ripple/app/paths/Credit.cpp>
#include <ripple/app/paths/FindPaths.cpp>
#include <ripple/app/paths/Pathfinder.cpp> #include <ripple/app/paths/Pathfinder.cpp>
#include <ripple/app/misc/AmendmentTableImpl.cpp> #include <ripple/app/misc/AmendmentTableImpl.cpp>