20 #include <ripple/app/main/Application.h>
21 #include <ripple/app/paths/Tuning.h>
22 #include <ripple/app/paths/Pathfinder.h>
23 #include <ripple/app/paths/RippleCalc.h>
24 #include <ripple/app/paths/RippleLineCache.h>
25 #include <ripple/ledger/PaymentSandbox.h>
26 #include <ripple/app/ledger/OrderBookDB.h>
27 #include <ripple/basics/Log.h>
28 #include <ripple/json/to_string.h>
29 #include <ripple/core/JobQueue.h>
30 #include <ripple/core/Config.h>
69 struct AccountCandidate
74 static const int highPriority = 10000;
77 bool compareAccountCandidate (
79 AccountCandidate
const& first, AccountCandidate
const& second)
81 if (first.priority < second.priority)
84 if (first.account > second.account)
87 return (first.priority ^ seq) < (second.priority ^ seq);
108 static PathTable mPathTable;
114 for (
auto const& node : type)
149 boost::optional<AccountID>
const& uSrcIssuer,
151 boost::optional<STAmount>
const& srcAmount,
153 : mSrcAccount (uSrcAccount),
154 mDstAccount (uDstAccount),
155 mEffectiveDst (
isXRP(saDstAmount.getIssuer ()) ?
156 uDstAccount : saDstAmount.getIssuer ()),
157 mDstAmount (saDstAmount),
158 mSrcCurrency (uSrcCurrency),
159 mSrcIssuer (uSrcIssuer),
160 mSrcAmount (srcAmount.value_or(
STAmount({uSrcCurrency,
161 uSrcIssuer.value_or(
isXRP(uSrcCurrency) ?
163 convert_all_ (mDstAmount ==
165 mLedger (cache->getLedger ()),
168 j_ (app.journal (
"Pathfinder"))
170 assert (! uSrcIssuer ||
isXRP(uSrcCurrency) ==
isXRP(uSrcIssuer.get()));
178 JLOG (
j_.
debug()) <<
"Destination amount was zero.";
191 JLOG (
j_.
debug()) <<
"Tried to send to same issuer";
207 bool useIssuerAccount
210 auto issuer = currencyIsXRP ?
AccountID() : account;
220 <<
" mSrcIssuer=" << issuerString;
224 JLOG (
j_.
debug()) <<
"findPaths< no ledger";
234 JLOG (
j_.
debug()) <<
"invalid source account";
242 <<
"Non-existent gateway";
253 <<
"New account not being funded in XRP ";
261 <<
"New account not getting enough funding: "
270 if (bSrcXrp && bDstXrp)
273 JLOG (
j_.
debug()) <<
"XRP to XRP payment";
279 JLOG (
j_.
debug()) <<
"XRP to non-XRP payment";
285 JLOG (
j_.
debug()) <<
"non-XRP to XRP payment";
291 JLOG (
j_.
debug()) <<
"non-XRP to non-XRP - same currency";
297 JLOG (
j_.
debug()) <<
"non-XRP to non-XRP - cross currency";
302 for (
auto const& costedPath : mPathTable[paymentType])
305 if (costedPath.searchLevel <= searchLevel)
329 uint64_t& qualityOut)
const
358 qualityOut =
getRate (rc.actualAmountOut, rc.actualAmountIn);
359 amountOut = rc.actualAmountOut;
368 mDstAmount - amountOut,
377 amountOut += rc.actualAmountOut;
385 "checkpath: exception (" << e.
what() <<
") " <<
429 <<
"Default path contributes: " << rc.actualAmountIn;
435 <<
"Default path fails: " <<
transToken (rc.result ());
440 JLOG (
j_.
debug()) <<
"Default path causes exception";
462 return path.size() == 1;
471 for (
auto it = path.begin() + 1; it != path.end(); ++it)
484 rankedPaths.
clear ();
498 saMinDstAmount = smallestUsefulAmount(
mDstAmount, maxPaths);
501 for (
int i = 0; i < paths.
size (); ++i)
503 auto const& currentPath = paths[i];
504 if (! currentPath.empty())
509 currentPath, saMinDstAmount, liquidity, uQuality);
513 "findPaths: dropping : " <<
520 "findPaths: quality: " << uQuality <<
524 currentPath.size (), liquidity, i});
538 if (! convert_all_ && a.quality != b.quality)
539 return a.quality < b.quality;
542 if (a.liquidity != b.liquidity)
543 return a.liquidity > b.liquidity;
546 if (a.length != b.length)
547 return a.length < b.length;
550 return a.index > b.index;
557 STPath& fullLiquidityPath,
561 JLOG (
j_.
debug()) <<
"findPaths: " <<
563 extraPaths.
size () <<
" extras";
568 assert (fullLiquidityPath.
empty ());
572 rankPaths (maxPaths, extraPaths, extraPathRanks);
582 auto extraPathsIterator = extraPathRanks.
begin();
585 extraPathsIterator != extraPathRanks.
end())
587 bool usePath =
false;
588 bool useExtraPath =
false;
592 else if (extraPathsIterator == extraPathRanks.
end())
594 else if (extraPathsIterator->quality < pathsIterator->quality)
596 else if (extraPathsIterator->quality > pathsIterator->quality)
598 else if (extraPathsIterator->liquidity > pathsIterator->liquidity)
600 else if (extraPathsIterator->liquidity < pathsIterator->liquidity)
609 auto& pathRank = usePath ? *pathsIterator : *extraPathsIterator;
612 extraPaths[pathRank.index];
615 ++extraPathsIterator;
620 auto iPathsLeft = maxPaths - bestPaths.
size ();
621 if (!(iPathsLeft > 0 || fullLiquidityPath.
empty ()))
630 bool startsWithIssuer =
false;
632 if (! issuerIsSender && usePath)
636 path.front().getAccountID() != srcIssuer)
641 startsWithIssuer =
true;
644 if (iPathsLeft > 1 ||
645 (iPathsLeft > 0 && pathRank.liquidity >= remaining))
649 remaining -= pathRank.liquidity;
652 else if (iPathsLeft == 0 &&
654 fullLiquidityPath.
empty ())
657 fullLiquidityPath = (startsWithIssuer ?
removeIssuer (path) : path);
659 "Found extra full path: " <<
665 "Skipping a non-filling path: " <<
670 if (remaining > beast::zero)
672 assert (fullLiquidityPath.
empty ());
674 "Paths could not send " << remaining <<
" of " <<
mDstAmount;
679 "findPaths: RESULTS: " <<
688 bool matchingAccount =
693 return matchingCurrency && matchingAccount;
702 Issue const issue (currency, account);
715 int aFlags = sleAccount->getFieldU32 (
sfFlags);
725 for (
auto const& item :
mRLCache->getRippleLines (account))
732 else if (rspEntry->
getBalance () <= beast::zero &&
735 || (bAuthRequired && !rspEntry->
getAuth ())))
738 else if (isDstCurrency &&
767 <<
"addLink< on " << currentPaths.
size ()
768 <<
" source(s), flags=" << addFlags;
769 for (
auto const& path: currentPaths)
770 addLink (path, incompletePaths, addFlags);
776 auto it =
mPaths.find (pathType);
781 if (pathType.
empty ())
793 <<
"getPaths< adding onto '"
794 << pathTypeToString (parentPathType) <<
"' to get '"
795 << pathTypeToString (pathType) <<
"'";
800 auto nodeType = pathType.
back ();
805 assert (pathsOut.
empty ());
837 <<
" complete paths added";
841 <<
"getPaths> " << pathsOut.
size () <<
" partial paths found";
851 toAccount, fromAccount, currency));
853 auto const flag ((toAccount > fromAccount)
856 return sleRipple && (sleRipple->getFieldU32 (
sfFlags) & flag);
864 if (currentPath.
empty ())
875 auto const& fromAccount = (currentPath.
size () == 1)
877 : (currentPath.
end () - 2)->getAccountID ();
886 for (
auto const& p : pathSet)
891 pathSet.push_back (path);
895 const STPath& currentPath,
900 auto const& uEndCurrency = pathEnd.getCurrency ();
901 auto const& uEndIssuer = pathEnd.getIssuerID ();
902 auto const& uEndAccount = pathEnd.getAccountID ();
903 bool const bOnXRP = uEndCurrency.isZero ();
910 JLOG (
j_.
trace()) <<
"addLink< flags="
911 << addFlags <<
" onXRP=" << bOnXRP;
922 <<
"complete path found ax: "
934 bool const bRequireAuth (
936 bool const bIsEndCurrency (
938 bool const bIsNoRippleOut (
940 bool const bDestOnly (
943 auto& rippleLines (
mRLCache->getRippleLines (uEndAccount));
945 AccountCandidates candidates;
946 candidates.reserve (rippleLines.size ());
948 for (
auto const& item : rippleLines)
950 auto* rs =
dynamic_cast<RippleState const *
> (item.get ());
954 <<
"Couldn't decipher RippleState";
957 auto const& acct = rs->getAccountIDPeer ();
959 if (hasEffectiveDestination && (acct ==
mDstAccount))
967 if (bDestOnly && !bToDestination)
972 if ((uEndCurrency == rs->getLimit ().getCurrency ()) &&
973 !currentPath.
hasSeen (acct, uEndCurrency, acct))
976 if (rs->getBalance () <= beast::zero
977 && (!rs->getLimitPeer ()
978 || -rs->getBalance () >= rs->getLimitPeer ()
979 || (bRequireAuth && !rs->getAuth ())))
983 else if (bIsNoRippleOut && rs->getNoRipple ())
987 else if (bToDestination)
993 if (!currentPath.
empty ())
996 <<
"complete path found ae: "
1002 else if (!bDestOnly)
1005 candidates.push_back (
1006 {AccountCandidate::highPriority, acct});
1022 candidates.push_back ({
out, acct});
1027 if (!candidates.empty())
1029 std::sort (candidates.begin (), candidates.end (),
1032 std::placeholders::_1,
1033 std::placeholders::_2));
1035 int count = candidates.size ();
1039 else if (count > 50)
1042 auto it = candidates.begin();
1043 while (count-- != 0)
1051 incompletePaths.
assembleAdd (currentPath, pathElement);
1060 <<
"Path ends on non-existent issuer";
1071 {uEndCurrency, uEndIssuer}))
1078 incompletePaths.
assembleAdd (currentPath, pathElement);
1083 bool bDestOnly = (addFlags &
afOB_LAST) != 0;
1085 {uEndCurrency, uEndIssuer});
1087 << books.size () <<
" books found from this currency/issuer";
1089 for (
auto const& book : books)
1093 book->getCurrencyOut (),
1094 book->getIssuerOut ()) &&
1099 STPath newPath (currentPath);
1101 if (book->getCurrencyOut().isZero())
1116 <<
"complete path found bx: "
1123 else if (!currentPath.
hasSeen(
1124 book->getIssuerOut (),
1125 book->getCurrencyOut (),
1126 book->getIssuerOut ()))
1130 if ((newPath.
size() >= 2) &&
1131 (newPath.
back().isAccount ()) &&
1132 (newPath[newPath.
size() - 2].isOffer ()))
1138 book->getIssuerOut());
1146 book->getIssuerOut());
1149 if (hasEffectiveDestination &&
1159 <<
"complete path found ba: "
1168 book->getIssuerOut (),
1169 book->getCurrencyOut (),
1170 book->getIssuerOut ()));
1224 auto& list = mPathTable[type];
1225 assert (list.empty());
1226 for (
auto& cost: costs)
1227 list.push_back ({cost.cost, makePath (cost.path)});