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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -33,112 +33,126 @@ class Pathfinder
public:
Pathfinder (
RippleLineCache::ref cache,
RippleAddress const& srcAccountID,
RippleAddress const& dstAccountID,
Currency const& srcCurrencyID,
Account const& srcIssuerID,
STAmount const& dstAmount,
bool& bValid);
Account const& srcAccount,
Account const& dstAccount,
Currency const& uSrcCurrency,
Account const& uSrcIssuer,
STAmount const& dstAmount);
static void initPathTable();
static void initPathTable ();
bool findPaths (
int iLevel,
unsigned int const iMaxPaths,
STPathSet& spsDst,
STPath& spExtraPath);
bool findPaths (int searchLevel);
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
{
pt_XRP_to_XRP,
pt_XRP_to_nonXRP,
pt_nonXRP_to_XRP,
pt_nonXRP_to_same,
pt_nonXRP_to_nonXRP
pt_nonXRP_to_same, // Destination currency is the same as source.
pt_nonXRP_to_nonXRP // Destination currency is NOT the same as source.
};
enum NodeType
{
nt_SOURCE, // The source account with an issuer account, if required
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
};
private:
/*
Call graph of methoids
typedef std::vector<NodeType> PathType_t;
typedef std::pair<int, PathType_t> CostedPath_t;
typedef std::vector<CostedPath_t> CostedPathList_t;
findPaths:
addPathsForType:
addLinks:
addLink:
getPathsOut
issueMatchesOrigin
isNoRippleOut:
isNoRipple
typedef std::pair<int, const char*> PathCost;
typedef std::vector<PathCost> PathCostList;
ensurePathsAreComplete
typedef std::map<PaymentType, CostedPathList_t> PathTable;
getBestPaths:
rippleCalculate
getPathLiquidity:
rippleCalculate
*/
/** Fill a CostedPathList_t from its description. */
static void fillPaths(PaymentType type,
PathCostList const& costs);
// Add all paths of one type to mCompletePaths.
STPathSet& addPathsForType (PathType const& type);
/** @return true if any building paths are now complete. */
bool checkComplete (STPathSet& retPathSet);
static std::string pathTypeToString(PathType_t const&);
bool matchesOrigin (Issue const&);
bool issueMatchesOrigin (Issue const&);
int getPathsOut (
Currency const& currency,
Account const& accountID,
Account const& account,
bool isDestCurrency,
Account const& dest);
void addLink(
void addLink (
STPath const& currentPath,
STPathSet& incompletePaths,
int addFlags);
void addLink(
// Call addLink() for each path in currentPaths.
void addLinks (
STPathSet const& currentPaths,
STPathSet& incompletePaths,
int addFlags);
STPathSet& getPaths(PathType_t const& type,
bool addComplete = true);
STPathSet filterPaths(int iMaxPaths,
STPath& extraPath);
TER checkPath (STPath const& path, STAmount const& minDestAmount,
STAmount& amount, uint64_t& quality) const;
// Compute the liquidity for a path. Return tesSUCCESS if it has has enough
// liquidity to be worth keeping, otherwise an error.
TER getPathLiquidity (
STPath const& path, // IN: The path to check.
STAmount const& minDstAmount, // IN: The minimum output this path must
// deliver to be worth keeping.
STAmount& amountOut, // OUT: The actual liquidity on the path.
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);
// Is the "no ripple" flag set from one account to another?
bool isNoRipple (
Account const& setByID,
Account const& setOnID,
Currency const& currencyID);
Account const& fromAccount,
Account const& toAccount,
Currency const& currency);
// Our main table of paths
Account mSrcAccount;
Account mDstAccount;
STAmount mDstAmount;
Currency mSrcCurrency;
Account mSrcIssuer;
STAmount mSrcAmount;
static PathTable mPathTable;
static PathType_t makePath(char const*);
Account mSrcAccountID;
Account mDstAccountID;
STAmount mDstAmount;
Currency mSrcCurrencyID;
Account mSrcIssuerID;
STAmount mSrcAmount;
Ledger::pointer mLedger;
LoadEvent::pointer m_loadEvent;
RippleLineCache::pointer mRLCache;
Ledger::pointer mLedger;
LoadEvent::pointer m_loadEvent;
RippleLineCache::pointer mRLCache;
STPathElement mSource;
STPathSet mCompletePaths;
std::map<PathType_t, STPathSet> mPaths;
std::map<PathType, STPathSet> mPaths;
hash_map<Issue, int> mPathsOutCountMap;
@@ -146,51 +160,18 @@ private:
static std::uint32_t const afADD_ACCOUNTS = 0x001;
// 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
static std::uint32_t const afOB_XRP = 0x010;
static std::uint32_t const afOB_XRP = 0x010;
// Must link to destination currency
static std::uint32_t const afOB_LAST = 0x040;
static std::uint32_t const afOB_LAST = 0x040;
// 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
#endif

View File

@@ -18,6 +18,7 @@
//==============================================================================
#include <ripple/app/book/Quality.h>
#include <ripple/app/paths/Credit.h>
#include <ripple/app/paths/cursor/RippleLiquidity.h>
namespace ripple {
@@ -46,8 +47,8 @@ TER PathCursor::reverseLiquidityForAccount () const
auto const isFinalNode = (nodeIndex_ == lastNodeIndex);
// 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.
const bool previousNodeIsAccount = !nodeIndex_ ||
previousNode().isAccount();
@@ -80,7 +81,7 @@ TER PathCursor::reverseLiquidityForAccount () const
// For previousNodeIsAccount:
// Previous account is already owed.
const STAmount saPrvOwed = (previousNodeIsAccount && nodeIndex_ != 0)
? credit_balance (ledger(),
? creditBalance (ledger(),
node().account_,
previousAccountID,
node().issue_.currency)
@@ -88,7 +89,7 @@ TER PathCursor::reverseLiquidityForAccount () const
// The limit amount that the previous account may owe.
const STAmount saPrvLimit = (previousNodeIsAccount && nodeIndex_ != 0)
? credit_limit (ledger(),
? creditLimit (ledger(),
node().account_,
previousAccountID,
node().issue_.currency)
@@ -96,7 +97,7 @@ TER PathCursor::reverseLiquidityForAccount () const
// Next account is owed.
const STAmount saNxtOwed = (nextNodeIsAccount && nodeIndex_ != lastNodeIndex)
? credit_balance (ledger(),
? creditBalance (ledger(),
node().account_,
nextAccountID,
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>
namespace ripple {
@@ -119,11 +121,10 @@ Json::Value doRipplePathFind (RPC::Context& context)
}
else
{
auto usCurrencies = usAccountSourceCurrencies (raSrc, cache, true);
auto currencies = accountSourceCurrencies (raSrc, cache, true);
jvSrcCurrencies = Json::Value (Json::arrayValue);
for (auto const& uCurrency: usCurrencies)
for (auto const& uCurrency: currencies)
{
Json::Value jvCurrency (Json::objectValue);
jvCurrency["currency"] = to_string(uCurrency);
@@ -134,7 +135,7 @@ Json::Value doRipplePathFind (RPC::Context& context)
// Fill in currencies destination will accept
Json::Value jvDestCur (Json::arrayValue);
auto usDestCurrID = usAccountDestCurrencies (raDst, cache, true);
auto usDestCurrID = accountDestCurrencies (raDst, cache, true);
for (auto const& uCurrency: usDestCurrID)
jvDestCur.append (to_string (uCurrency));
@@ -177,11 +178,6 @@ Json::Value doRipplePathFind (RPC::Context& context)
return rpcError (rpcSRC_ISR_MALFORMED);
}
STPathSet spsComputed;
bool bValid;
Pathfinder pf (cache, raSrc, raDst, uSrcCurrencyID,
uSrcIssuerID, saDstAmount, bValid);
int level = getConfig().PATH_SEARCH_OLD;
if ((getConfig().PATH_SEARCH_MAX > level)
&& !getApp().getFeeTrack().isLoadedLocal())
@@ -196,6 +192,7 @@ Json::Value doRipplePathFind (RPC::Context& context)
level = rLev;
}
STPathSet spsComputed;
if (context.params_.isMember("paths"))
{
STParsedJSONObject paths ("paths", context.params_["paths"]);
@@ -205,8 +202,18 @@ Json::Value doRipplePathFind (RPC::Context& context)
spsComputed = paths.object.get()->downcast<STPathSet> ();
}
STPath extraPath;
if (!bValid || !pf.findPaths (level, 4, spsComputed, extraPath))
STPath fullLiquidityPath;
auto valid = findPathsForOneIssuer(
cache,
raSrc.getAccountID(),
raDst.getAccountID(),
{uSrcCurrencyID, uSrcIssuerID},
saDstAmount,
level,
4, // iMaxPaths
spsComputed,
fullLiquidityPath);
if (!valid)
{
WriteLog (lsWARNING, RPCHandler)
<< "ripple_path_find: No paths found.";
@@ -241,13 +248,13 @@ Json::Value doRipplePathFind (RPC::Context& context)
<< " saMaxAmountAct=" << rc.actualAmountIn
<< " saDstAmountAct=" << rc.actualAmountOut;
if (extraPath.size() > 0 &&
if (fullLiquidityPath.size() > 0 &&
(rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL))
{
WriteLog (lsDEBUG, PathRequest)
<< "Trying with an extra path element";
spsComputed.push_back (extraPath);
spsComputed.push_back (fullLiquidityPath);
lesSandbox.clear ();
rc = path::RippleCalc::rippleCalculate (
lesSandbox,

View File

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

View File

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

View File

@@ -29,5 +29,8 @@
#include <ripple/app/ledger/OrderBookIterator.cpp>
#include <ripple/app/consensus/DisputedTx.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/misc/AmendmentTableImpl.cpp>