rippled
Pathfinder.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/app/ledger/OrderBookDB.h>
21 #include <ripple/app/main/Application.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/app/paths/impl/PathfinderUtils.h>
26 #include <ripple/basics/Log.h>
27 #include <ripple/core/Config.h>
28 #include <ripple/core/JobQueue.h>
29 #include <ripple/json/to_string.h>
30 #include <ripple/ledger/PaymentSandbox.h>
31 
32 #include <tuple>
33 
34 /*
35 
36 Core Pathfinding Engine
37 
38 The pathfinding request is identified by category, XRP to XRP, XRP to
39 non-XRP, non-XRP to XRP, same currency non-XRP to non-XRP, cross-currency
40 non-XRP to non-XRP. For each category, there is a table of paths that the
41 pathfinder searches for. Complete paths are collected.
42 
43 Each complete path is then rated and sorted. Paths with no or trivial
44 liquidity are dropped. Otherwise, paths are sorted based on quality,
45 liquidity, and path length.
46 
47 Path slots are filled in quality (ratio of out to in) order, with the
48 exception that the last path must have enough liquidity to complete the
49 payment (assuming no liquidity overlap). In addition, if no selected path
50 is capable of providing enough liquidity to complete the payment by itself,
51 an extra "covering" path is returned.
52 
53 The selected paths are then tested to determine if they can complete the
54 payment and, if so, at what cost. If they fail and a covering path was
55 found, the test is repeated with the covering path. If this succeeds, the
56 final paths and the estimated cost are returned.
57 
58 The engine permits the search depth to be selected and the paths table
59 includes the depth at which each path type is found. A search depth of zero
60 causes no searching to be done. Extra paths can also be injected, and this
61 should be used to preserve previously-found paths across invokations for the
62 same path request (particularly if the search depth may change).
63 
64 */
65 
66 namespace ripple {
67 
68 namespace {
69 
70 // This is an arbitrary cutoff, and it might cause us to miss other
71 // good paths with this arbitrary cut off.
72 constexpr std::size_t PATHFINDER_MAX_COMPLETE_PATHS = 1000;
73 
74 struct AccountCandidate
75 {
76  int priority;
77  AccountID account;
78 
79  static const int highPriority = 10000;
80 };
81 
82 bool
83 compareAccountCandidate(
84  std::uint32_t seq,
85  AccountCandidate const& first,
86  AccountCandidate const& second)
87 {
88  if (first.priority < second.priority)
89  return false;
90 
91  if (first.account > second.account)
92  return true;
93 
94  return (first.priority ^ seq) < (second.priority ^ seq);
95 }
96 
97 using AccountCandidates = std::vector<AccountCandidate>;
98 
99 struct CostedPath
100 {
101  int searchLevel;
103 };
104 
105 using CostedPathList = std::vector<CostedPath>;
106 
108 
109 struct PathCost
110 {
111  int cost;
112  char const* path;
113 };
114 using PathCostList = std::vector<PathCost>;
115 
116 static PathTable mPathTable;
117 
119 pathTypeToString(Pathfinder::PathType const& type)
120 {
121  std::string ret;
122 
123  for (auto const& node : type)
124  {
125  switch (node)
126  {
128  ret.append("s");
129  break;
131  ret.append("a");
132  break;
134  ret.append("b");
135  break;
137  ret.append("x");
138  break;
140  ret.append("f");
141  break;
143  ret.append("d");
144  break;
145  }
146  }
147 
148  return ret;
149 }
150 
151 // Return the smallest amount of useful liquidity for a given amount, and the
152 // total number of paths we have to evaluate.
153 STAmount
154 smallestUsefulAmount(STAmount const& amount, int maxPaths)
155 {
156  return divide(amount, STAmount(maxPaths + 2), amount.issue());
157 }
158 } // namespace
159 
162  AccountID const& uSrcAccount,
163  AccountID const& uDstAccount,
164  Currency const& uSrcCurrency,
165  std::optional<AccountID> const& uSrcIssuer,
166  STAmount const& saDstAmount,
167  std::optional<STAmount> const& srcAmount,
168  Application& app)
169  : mSrcAccount(uSrcAccount)
170  , mDstAccount(uDstAccount)
171  , mEffectiveDst(
172  isXRP(saDstAmount.getIssuer()) ? uDstAccount
173  : saDstAmount.getIssuer())
174  , mDstAmount(saDstAmount)
175  , mSrcCurrency(uSrcCurrency)
176  , mSrcIssuer(uSrcIssuer)
177  , mSrcAmount(srcAmount.value_or(STAmount(
178  {uSrcCurrency,
179  uSrcIssuer.value_or(
180  isXRP(uSrcCurrency) ? xrpAccount() : uSrcAccount)},
181  1u,
182  0,
183  true)))
184  , convert_all_(convertAllCheck(mDstAmount))
185  , mLedger(cache->getLedger())
186  , mRLCache(cache)
187  , app_(app)
188  , j_(app.journal("Pathfinder"))
189 {
190  assert(!uSrcIssuer || isXRP(uSrcCurrency) == isXRP(uSrcIssuer.value()));
191 }
192 
193 bool
194 Pathfinder::findPaths(int searchLevel)
195 {
196  if (mDstAmount == beast::zero)
197  {
198  // No need to send zero money.
199  JLOG(j_.debug()) << "Destination amount was zero.";
200  mLedger.reset();
201  return false;
202 
203  // TODO(tom): why do we reset the ledger just in this case and the one
204  // below - why don't we do it each time we return false?
205  }
206 
209  {
210  // No need to send to same account with same currency.
211  JLOG(j_.debug()) << "Tried to send to same issuer";
212  mLedger.reset();
213  return false;
214  }
215 
216  if (mSrcAccount == mEffectiveDst &&
218  {
219  // Default path might work, but any path would loop
220  return true;
221  }
222 
224  auto currencyIsXRP = isXRP(mSrcCurrency);
225 
226  bool useIssuerAccount = mSrcIssuer && !currencyIsXRP && !isXRP(*mSrcIssuer);
227  auto& account = useIssuerAccount ? *mSrcIssuer : mSrcAccount;
228  auto issuer = currencyIsXRP ? AccountID() : account;
229  mSource = STPathElement(account, mSrcCurrency, issuer);
230  auto issuerString =
232  JLOG(j_.trace()) << "findPaths>"
233  << " mSrcAccount=" << mSrcAccount
234  << " mDstAccount=" << mDstAccount
235  << " mDstAmount=" << mDstAmount.getFullText()
236  << " mSrcCurrency=" << mSrcCurrency
237  << " mSrcIssuer=" << issuerString;
238 
239  if (!mLedger)
240  {
241  JLOG(j_.debug()) << "findPaths< no ledger";
242  return false;
243  }
244 
245  bool bSrcXrp = isXRP(mSrcCurrency);
246  bool bDstXrp = isXRP(mDstAmount.getCurrency());
247 
248  if (!mLedger->exists(keylet::account(mSrcAccount)))
249  {
250  // We can't even start without a source account.
251  JLOG(j_.debug()) << "invalid source account";
252  return false;
253  }
254 
255  if ((mEffectiveDst != mDstAccount) &&
257  {
258  JLOG(j_.debug()) << "Non-existent gateway";
259  return false;
260  }
261 
262  if (!mLedger->exists(keylet::account(mDstAccount)))
263  {
264  // Can't find the destination account - we must be funding a new
265  // account.
266  if (!bDstXrp)
267  {
268  JLOG(j_.debug()) << "New account not being funded in XRP ";
269  return false;
270  }
271 
272  auto const reserve = STAmount(mLedger->fees().accountReserve(0));
273  if (mDstAmount < reserve)
274  {
275  JLOG(j_.debug())
276  << "New account not getting enough funding: " << mDstAmount
277  << " < " << reserve;
278  return false;
279  }
280  }
281 
282  // Now compute the payment type from the types of the source and destination
283  // currencies.
284  PaymentType paymentType;
285  if (bSrcXrp && bDstXrp)
286  {
287  // XRP -> XRP
288  JLOG(j_.debug()) << "XRP to XRP payment";
289  paymentType = pt_XRP_to_XRP;
290  }
291  else if (bSrcXrp)
292  {
293  // XRP -> non-XRP
294  JLOG(j_.debug()) << "XRP to non-XRP payment";
295  paymentType = pt_XRP_to_nonXRP;
296  }
297  else if (bDstXrp)
298  {
299  // non-XRP -> XRP
300  JLOG(j_.debug()) << "non-XRP to XRP payment";
301  paymentType = pt_nonXRP_to_XRP;
302  }
303  else if (mSrcCurrency == mDstAmount.getCurrency())
304  {
305  // non-XRP -> non-XRP - Same currency
306  JLOG(j_.debug()) << "non-XRP to non-XRP - same currency";
307  paymentType = pt_nonXRP_to_same;
308  }
309  else
310  {
311  // non-XRP to non-XRP - Different currency
312  JLOG(j_.debug()) << "non-XRP to non-XRP - cross currency";
313  paymentType = pt_nonXRP_to_nonXRP;
314  }
315 
316  // Now iterate over all paths for that paymentType.
317  for (auto const& costedPath : mPathTable[paymentType])
318  {
319  // Only use paths with at most the current search level.
320  if (costedPath.searchLevel <= searchLevel)
321  {
322  addPathsForType(costedPath.type);
323 
324  if (mCompletePaths.size() > PATHFINDER_MAX_COMPLETE_PATHS)
325  break;
326  }
327  }
328 
329  JLOG(j_.debug()) << mCompletePaths.size() << " complete paths found";
330 
331  // Even if we find no paths, default paths may work, and we don't check them
332  // currently.
333  return true;
334 }
335 
336 TER
338  STPath const& path, // IN: The path to check.
339  STAmount const& minDstAmount, // IN: The minimum output this path must
340  // deliver to be worth keeping.
341  STAmount& amountOut, // OUT: The actual liquidity along the path.
342  uint64_t& qualityOut) const // OUT: The returned initial quality
343 {
344  STPathSet pathSet;
345  pathSet.push_back(path);
346 
347  path::RippleCalc::Input rcInput;
348  rcInput.defaultPathsAllowed = false;
349 
350  PaymentSandbox sandbox(&*mLedger, tapNONE);
351 
352  try
353  {
354  // Compute a path that provides at least the minimum liquidity.
355  if (convert_all_)
356  rcInput.partialPaymentAllowed = true;
357 
359  sandbox,
360  mSrcAmount,
361  minDstAmount,
362  mDstAccount,
363  mSrcAccount,
364  pathSet,
365  app_.logs(),
366  &rcInput);
367  // If we can't get even the minimum liquidity requested, we're done.
368  if (rc.result() != tesSUCCESS)
369  return rc.result();
370 
371  qualityOut = getRate(rc.actualAmountOut, rc.actualAmountIn);
372  amountOut = rc.actualAmountOut;
373 
374  if (!convert_all_)
375  {
376  // Now try to compute the remaining liquidity.
377  rcInput.partialPaymentAllowed = true;
379  sandbox,
380  mSrcAmount,
381  mDstAmount - amountOut,
382  mDstAccount,
383  mSrcAccount,
384  pathSet,
385  app_.logs(),
386  &rcInput);
387 
388  // If we found further liquidity, add it into the result.
389  if (rc.result() == tesSUCCESS)
390  amountOut += rc.actualAmountOut;
391  }
392 
393  return tesSUCCESS;
394  }
395  catch (std::exception const& e)
396  {
397  JLOG(j_.info()) << "checkpath: exception (" << e.what() << ") "
398  << path.getJson(JsonOptions::none);
399  return tefEXCEPTION;
400  }
401 }
402 
403 void
405 {
407 
408  // Must subtract liquidity in default path from remaining amount.
409  try
410  {
411  PaymentSandbox sandbox(&*mLedger, tapNONE);
412 
413  path::RippleCalc::Input rcInput;
414  rcInput.partialPaymentAllowed = true;
416  sandbox,
417  mSrcAmount,
419  mDstAccount,
420  mSrcAccount,
421  STPathSet(),
422  app_.logs(),
423  &rcInput);
424 
425  if (rc.result() == tesSUCCESS)
426  {
427  JLOG(j_.debug())
428  << "Default path contributes: " << rc.actualAmountIn;
429  mRemainingAmount -= rc.actualAmountOut;
430  }
431  else
432  {
433  JLOG(j_.debug())
434  << "Default path fails: " << transToken(rc.result());
435  }
436  }
437  catch (std::exception const&)
438  {
439  JLOG(j_.debug()) << "Default path causes exception";
440  }
441 
442  rankPaths(maxPaths, mCompletePaths, mPathRanks);
443 }
444 
445 static bool
446 isDefaultPath(STPath const& path)
447 {
448  // FIXME: default paths can consist of more than just an account:
449  //
450  // JoelKatz writes:
451  // So the test for whether a path is a default path is incorrect. I'm not
452  // sure it's worth the complexity of fixing though. If we are going to fix
453  // it, I'd suggest doing it this way:
454  //
455  // 1) Compute the default path, probably by using 'expandPath' to expand an
456  // empty path. 2) Chop off the source and destination nodes.
457  //
458  // 3) In the pathfinding loop, if the source issuer is not the sender,
459  // reject all paths that don't begin with the issuer's account node or match
460  // the path we built at step 2.
461  return path.size() == 1;
462 }
463 
464 static STPath
465 removeIssuer(STPath const& path)
466 {
467  // This path starts with the issuer, which is already implied
468  // so remove the head node
469  STPath ret;
470 
471  for (auto it = path.begin() + 1; it != path.end(); ++it)
472  ret.push_back(*it);
473 
474  return ret;
475 }
476 
477 // For each useful path in the input path set,
478 // create a ranking entry in the output vector of path ranks
479 void
481  int maxPaths,
482  STPathSet const& paths,
483  std::vector<PathRank>& rankedPaths)
484 {
485  rankedPaths.clear();
486  rankedPaths.reserve(paths.size());
487 
488  auto const saMinDstAmount = [&]() -> STAmount {
489  if (!convert_all_)
490  {
491  // Ignore paths that move only very small amounts.
492  return smallestUsefulAmount(mDstAmount, maxPaths);
493  }
494 
495  // On convert_all_ partialPaymentAllowed will be set to true
496  // and requiring a huge amount will find the highest liquidity.
497  return largestAmount(mDstAmount);
498  }();
499 
500  for (int i = 0; i < paths.size(); ++i)
501  {
502  auto const& currentPath = paths[i];
503  if (!currentPath.empty())
504  {
505  STAmount liquidity;
506  uint64_t uQuality;
507  auto const resultCode = getPathLiquidity(
508  currentPath, saMinDstAmount, liquidity, uQuality);
509  if (resultCode != tesSUCCESS)
510  {
511  JLOG(j_.debug())
512  << "findPaths: dropping : " << transToken(resultCode)
513  << ": " << currentPath.getJson(JsonOptions::none);
514  }
515  else
516  {
517  JLOG(j_.debug()) << "findPaths: quality: " << uQuality << ": "
518  << currentPath.getJson(JsonOptions::none);
519 
520  rankedPaths.push_back(
521  {uQuality, currentPath.size(), liquidity, i});
522  }
523  }
524  }
525 
526  // Sort paths by:
527  // cost of path (when considering quality)
528  // width of path
529  // length of path
530  // A better PathRank is lower, best are sorted to the beginning.
531  std::sort(
532  rankedPaths.begin(),
533  rankedPaths.end(),
534  [&](Pathfinder::PathRank const& a, Pathfinder::PathRank const& b) {
535  // 1) Higher quality (lower cost) is better
536  if (!convert_all_ && a.quality != b.quality)
537  return a.quality < b.quality;
538 
539  // 2) More liquidity (higher volume) is better
540  if (a.liquidity != b.liquidity)
541  return a.liquidity > b.liquidity;
542 
543  // 3) Shorter paths are better
544  if (a.length != b.length)
545  return a.length < b.length;
546 
547  // 4) Tie breaker
548  return a.index > b.index;
549  });
550 }
551 
552 STPathSet
554  int maxPaths,
555  STPath& fullLiquidityPath,
556  STPathSet const& extraPaths,
557  AccountID const& srcIssuer)
558 {
559  JLOG(j_.debug()) << "findPaths: " << mCompletePaths.size() << " paths and "
560  << extraPaths.size() << " extras";
561 
562  if (mCompletePaths.empty() && extraPaths.empty())
563  return mCompletePaths;
564 
565  assert(fullLiquidityPath.empty());
566  const bool issuerIsSender =
567  isXRP(mSrcCurrency) || (srcIssuer == mSrcAccount);
568 
569  std::vector<PathRank> extraPathRanks;
570  rankPaths(maxPaths, extraPaths, extraPathRanks);
571 
572  STPathSet bestPaths;
573 
574  // The best PathRanks are now at the start. Pull off enough of them to
575  // fill bestPaths, then look through the rest for the best individual
576  // path that can satisfy the entire liquidity - if one exists.
577  STAmount remaining = mRemainingAmount;
578 
579  auto pathsIterator = mPathRanks.begin();
580  auto extraPathsIterator = extraPathRanks.begin();
581 
582  while (pathsIterator != mPathRanks.end() ||
583  extraPathsIterator != extraPathRanks.end())
584  {
585  bool usePath = false;
586  bool useExtraPath = false;
587 
588  if (pathsIterator == mPathRanks.end())
589  useExtraPath = true;
590  else if (extraPathsIterator == extraPathRanks.end())
591  usePath = true;
592  else if (extraPathsIterator->quality < pathsIterator->quality)
593  useExtraPath = true;
594  else if (extraPathsIterator->quality > pathsIterator->quality)
595  usePath = true;
596  else if (extraPathsIterator->liquidity > pathsIterator->liquidity)
597  useExtraPath = true;
598  else if (extraPathsIterator->liquidity < pathsIterator->liquidity)
599  usePath = true;
600  else
601  {
602  // Risk is high they have identical liquidity
603  useExtraPath = true;
604  usePath = true;
605  }
606 
607  auto& pathRank = usePath ? *pathsIterator : *extraPathsIterator;
608 
609  auto const& path = usePath ? mCompletePaths[pathRank.index]
610  : extraPaths[pathRank.index];
611 
612  if (useExtraPath)
613  ++extraPathsIterator;
614 
615  if (usePath)
616  ++pathsIterator;
617 
618  auto iPathsLeft = maxPaths - bestPaths.size();
619  if (!(iPathsLeft > 0 || fullLiquidityPath.empty()))
620  break;
621 
622  if (path.empty())
623  {
624  assert(false);
625  continue;
626  }
627 
628  bool startsWithIssuer = false;
629 
630  if (!issuerIsSender && usePath)
631  {
632  // Need to make sure path matches issuer constraints
633  if (isDefaultPath(path) || path.front().getAccountID() != srcIssuer)
634  {
635  continue;
636  }
637 
638  startsWithIssuer = true;
639  }
640 
641  if (iPathsLeft > 1 ||
642  (iPathsLeft > 0 && pathRank.liquidity >= remaining))
643  // last path must fill
644  {
645  --iPathsLeft;
646  remaining -= pathRank.liquidity;
647  bestPaths.push_back(startsWithIssuer ? removeIssuer(path) : path);
648  }
649  else if (
650  iPathsLeft == 0 && pathRank.liquidity >= mDstAmount &&
651  fullLiquidityPath.empty())
652  {
653  // We found an extra path that can move the whole amount.
654  fullLiquidityPath = (startsWithIssuer ? removeIssuer(path) : path);
655  JLOG(j_.debug()) << "Found extra full path: "
656  << fullLiquidityPath.getJson(JsonOptions::none);
657  }
658  else
659  {
660  JLOG(j_.debug()) << "Skipping a non-filling path: "
661  << path.getJson(JsonOptions::none);
662  }
663  }
664 
665  if (remaining > beast::zero)
666  {
667  assert(fullLiquidityPath.empty());
668  JLOG(j_.info()) << "Paths could not send " << remaining << " of "
669  << mDstAmount;
670  }
671  else
672  {
673  JLOG(j_.debug()) << "findPaths: RESULTS: "
674  << bestPaths.getJson(JsonOptions::none);
675  }
676  return bestPaths;
677 }
678 
679 bool
681 {
682  bool matchingCurrency = (issue.currency == mSrcCurrency);
683  bool matchingAccount = isXRP(issue.currency) ||
684  (mSrcIssuer && issue.account == mSrcIssuer) ||
685  issue.account == mSrcAccount;
686 
687  return matchingCurrency && matchingAccount;
688 }
689 
690 int
692  Currency const& currency,
693  AccountID const& account,
694  bool isDstCurrency,
695  AccountID const& dstAccount)
696 {
697  Issue const issue(currency, account);
698 
699  auto [it, inserted] = mPathsOutCountMap.emplace(issue, 0);
700 
701  // If it was already present, return the stored number of paths
702  if (!inserted)
703  return it->second;
704 
705  auto sleAccount = mLedger->read(keylet::account(account));
706 
707  if (!sleAccount)
708  return 0;
709 
710  int aFlags = sleAccount->getFieldU32(sfFlags);
711  bool const bAuthRequired = (aFlags & lsfRequireAuth) != 0;
712  bool const bFrozen = ((aFlags & lsfGlobalFreeze) != 0);
713 
714  int count = 0;
715 
716  if (!bFrozen)
717  {
718  count = app_.getOrderBookDB().getBookSize(issue);
719 
720  for (auto const& item : mRLCache->getRippleLines(account))
721  {
722  RippleState* rspEntry = (RippleState*)item.get();
723 
724  if (currency != rspEntry->getLimit().getCurrency())
725  {
726  }
727  else if (
728  rspEntry->getBalance() <= beast::zero &&
729  (!rspEntry->getLimitPeer() ||
730  -rspEntry->getBalance() >= rspEntry->getLimitPeer() ||
731  (bAuthRequired && !rspEntry->getAuth())))
732  {
733  }
734  else if (
735  isDstCurrency && dstAccount == rspEntry->getAccountIDPeer())
736  {
737  count += 10000; // count a path to the destination extra
738  }
739  else if (rspEntry->getNoRipplePeer())
740  {
741  // This probably isn't a useful path out
742  }
743  else if (rspEntry->getFreezePeer())
744  {
745  // Not a useful path out
746  }
747  else
748  {
749  ++count;
750  }
751  }
752  }
753  it->second = count;
754  return count;
755 }
756 
757 void
759  STPathSet const& currentPaths, // The paths to build from
760  STPathSet& incompletePaths, // The set of partial paths we add to
761  int addFlags)
762 {
763  JLOG(j_.debug()) << "addLink< on " << currentPaths.size()
764  << " source(s), flags=" << addFlags;
765  for (auto const& path : currentPaths)
766  addLink(path, incompletePaths, addFlags);
767 }
768 
769 STPathSet&
771 {
772  // See if the set of paths for this type already exists.
773  auto it = mPaths.find(pathType);
774  if (it != mPaths.end())
775  return it->second;
776 
777  // Otherwise, if the type has no nodes, return the empty path.
778  if (pathType.empty())
779  return mPaths[pathType];
780 
781  // Otherwise, get the paths for the parent PathType by calling
782  // addPathsForType recursively.
783  PathType parentPathType = pathType;
784  parentPathType.pop_back();
785 
786  STPathSet const& parentPaths = addPathsForType(parentPathType);
787  STPathSet& pathsOut = mPaths[pathType];
788 
789  JLOG(j_.debug()) << "getPaths< adding onto '"
790  << pathTypeToString(parentPathType) << "' to get '"
791  << pathTypeToString(pathType) << "'";
792 
793  int initialSize = mCompletePaths.size();
794 
795  // Add the last NodeType to the lists.
796  auto nodeType = pathType.back();
797  switch (nodeType)
798  {
799  case nt_SOURCE:
800  // Source must always be at the start, so pathsOut has to be empty.
801  assert(pathsOut.empty());
802  pathsOut.push_back(STPath());
803  break;
804 
805  case nt_ACCOUNTS:
806  addLinks(parentPaths, pathsOut, afADD_ACCOUNTS);
807  break;
808 
809  case nt_BOOKS:
810  addLinks(parentPaths, pathsOut, afADD_BOOKS);
811  break;
812 
813  case nt_XRP_BOOK:
814  addLinks(parentPaths, pathsOut, afADD_BOOKS | afOB_XRP);
815  break;
816 
817  case nt_DEST_BOOK:
818  addLinks(parentPaths, pathsOut, afADD_BOOKS | afOB_LAST);
819  break;
820 
821  case nt_DESTINATION:
822  // FIXME: What if a different issuer was specified on the
823  // destination amount?
824  // TODO(tom): what does this even mean? Should it be a JIRA?
825  addLinks(parentPaths, pathsOut, afADD_ACCOUNTS | afAC_LAST);
826  break;
827  }
828 
829  if (mCompletePaths.size() != initialSize)
830  {
831  JLOG(j_.debug()) << (mCompletePaths.size() - initialSize)
832  << " complete paths added";
833  }
834 
835  JLOG(j_.debug()) << "getPaths> " << pathsOut.size()
836  << " partial paths found";
837  return pathsOut;
838 }
839 
840 bool
842  AccountID const& fromAccount,
843  AccountID const& toAccount,
844  Currency const& currency)
845 {
846  auto sleRipple =
847  mLedger->read(keylet::line(toAccount, fromAccount, currency));
848 
849  auto const flag(
850  (toAccount > fromAccount) ? lsfHighNoRipple : lsfLowNoRipple);
851 
852  return sleRipple && (sleRipple->getFieldU32(sfFlags) & flag);
853 }
854 
855 // Does this path end on an account-to-account link whose last account has
856 // set "no ripple" on the link?
857 bool
859 {
860  // Must have at least one link.
861  if (currentPath.empty())
862  return false;
863 
864  // Last link must be an account.
865  STPathElement const& endElement = currentPath.back();
866  if (!(endElement.getNodeType() & STPathElement::typeAccount))
867  return false;
868 
869  // If there's only one item in the path, return true if that item specifies
870  // no ripple on the output. A path with no ripple on its output can't be
871  // followed by a link with no ripple on its input.
872  auto const& fromAccount = (currentPath.size() == 1)
873  ? mSrcAccount
874  : (currentPath.end() - 2)->getAccountID();
875  auto const& toAccount = endElement.getAccountID();
876  return isNoRipple(fromAccount, toAccount, endElement.getCurrency());
877 }
878 
879 void
880 addUniquePath(STPathSet& pathSet, STPath const& path)
881 {
882  // TODO(tom): building an STPathSet this way is quadratic in the size
883  // of the STPathSet!
884  for (auto const& p : pathSet)
885  {
886  if (p == path)
887  return;
888  }
889  pathSet.push_back(path);
890 }
891 
892 void
894  const STPath& currentPath, // The path to build from
895  STPathSet& incompletePaths, // The set of partial paths we add to
896  int addFlags)
897 {
898  auto const& pathEnd = currentPath.empty() ? mSource : currentPath.back();
899  auto const& uEndCurrency = pathEnd.getCurrency();
900  auto const& uEndIssuer = pathEnd.getIssuerID();
901  auto const& uEndAccount = pathEnd.getAccountID();
902  bool const bOnXRP = uEndCurrency.isZero();
903 
904  // Does pathfinding really need to get this to
905  // a gateway (the issuer of the destination amount)
906  // rather than the ultimate destination?
907  bool const hasEffectiveDestination = mEffectiveDst != mDstAccount;
908 
909  JLOG(j_.trace()) << "addLink< flags=" << addFlags << " onXRP=" << bOnXRP;
910  JLOG(j_.trace()) << currentPath.getJson(JsonOptions::none);
911 
912  if (addFlags & afADD_ACCOUNTS)
913  {
914  // add accounts
915  if (bOnXRP)
916  {
917  if (mDstAmount.native() && !currentPath.empty())
918  { // non-default path to XRP destination
919  JLOG(j_.trace()) << "complete path found ax: "
920  << currentPath.getJson(JsonOptions::none);
921  addUniquePath(mCompletePaths, currentPath);
922  }
923  }
924  else
925  {
926  // search for accounts to add
927  auto const sleEnd = mLedger->read(keylet::account(uEndAccount));
928 
929  if (sleEnd)
930  {
931  bool const bRequireAuth(
932  sleEnd->getFieldU32(sfFlags) & lsfRequireAuth);
933  bool const bIsEndCurrency(
934  uEndCurrency == mDstAmount.getCurrency());
935  bool const bIsNoRippleOut(isNoRippleOut(currentPath));
936  bool const bDestOnly(addFlags & afAC_LAST);
937 
938  auto& rippleLines(mRLCache->getRippleLines(uEndAccount));
939 
940  AccountCandidates candidates;
941  candidates.reserve(rippleLines.size());
942 
943  for (auto const& item : rippleLines)
944  {
945  auto* rs = dynamic_cast<RippleState const*>(item.get());
946  if (!rs)
947  {
948  JLOG(j_.error()) << "Couldn't decipher RippleState";
949  continue;
950  }
951  auto const& acct = rs->getAccountIDPeer();
952 
953  if (hasEffectiveDestination && (acct == mDstAccount))
954  {
955  // We skipped the gateway
956  continue;
957  }
958 
959  bool bToDestination = acct == mEffectiveDst;
960 
961  if (bDestOnly && !bToDestination)
962  {
963  continue;
964  }
965 
966  if ((uEndCurrency == rs->getLimit().getCurrency()) &&
967  !currentPath.hasSeen(acct, uEndCurrency, acct))
968  {
969  // path is for correct currency and has not been seen
970  if (rs->getBalance() <= beast::zero &&
971  (!rs->getLimitPeer() ||
972  -rs->getBalance() >= rs->getLimitPeer() ||
973  (bRequireAuth && !rs->getAuth())))
974  {
975  // path has no credit
976  }
977  else if (bIsNoRippleOut && rs->getNoRipple())
978  {
979  // Can't leave on this path
980  }
981  else if (bToDestination)
982  {
983  // destination is always worth trying
984  if (uEndCurrency == mDstAmount.getCurrency())
985  {
986  // this is a complete path
987  if (!currentPath.empty())
988  {
989  JLOG(j_.trace())
990  << "complete path found ae: "
991  << currentPath.getJson(
993  addUniquePath(mCompletePaths, currentPath);
994  }
995  }
996  else if (!bDestOnly)
997  {
998  // this is a high-priority candidate
999  candidates.push_back(
1000  {AccountCandidate::highPriority, acct});
1001  }
1002  }
1003  else if (acct == mSrcAccount)
1004  {
1005  // going back to the source is bad
1006  }
1007  else
1008  {
1009  // save this candidate
1010  int out = getPathsOut(
1011  uEndCurrency,
1012  acct,
1013  bIsEndCurrency,
1014  mEffectiveDst);
1015  if (out)
1016  candidates.push_back({out, acct});
1017  }
1018  }
1019  }
1020 
1021  if (!candidates.empty())
1022  {
1023  std::sort(
1024  candidates.begin(),
1025  candidates.end(),
1026  std::bind(
1027  compareAccountCandidate,
1028  mLedger->seq(),
1029  std::placeholders::_1,
1030  std::placeholders::_2));
1031 
1032  int count = candidates.size();
1033  // allow more paths from source
1034  if ((count > 10) && (uEndAccount != mSrcAccount))
1035  count = 10;
1036  else if (count > 50)
1037  count = 50;
1038 
1039  auto it = candidates.begin();
1040  while (count-- != 0)
1041  {
1042  // Add accounts to incompletePaths
1043  STPathElement pathElement(
1045  it->account,
1046  uEndCurrency,
1047  it->account);
1048  incompletePaths.assembleAdd(currentPath, pathElement);
1049  ++it;
1050  }
1051  }
1052  }
1053  else
1054  {
1055  JLOG(j_.warn()) << "Path ends on non-existent issuer";
1056  }
1057  }
1058  }
1059  if (addFlags & afADD_BOOKS)
1060  {
1061  // add order books
1062  if (addFlags & afOB_XRP)
1063  {
1064  // to XRP only
1065  if (!bOnXRP &&
1066  app_.getOrderBookDB().isBookToXRP({uEndCurrency, uEndIssuer}))
1067  {
1068  STPathElement pathElement(
1070  xrpAccount(),
1071  xrpCurrency(),
1072  xrpAccount());
1073  incompletePaths.assembleAdd(currentPath, pathElement);
1074  }
1075  }
1076  else
1077  {
1078  bool bDestOnly = (addFlags & afOB_LAST) != 0;
1079  auto books = app_.getOrderBookDB().getBooksByTakerPays(
1080  {uEndCurrency, uEndIssuer});
1081  JLOG(j_.trace())
1082  << books.size() << " books found from this currency/issuer";
1083 
1084  for (auto const& book : books)
1085  {
1086  if (!currentPath.hasSeen(
1087  xrpAccount(),
1088  book->getCurrencyOut(),
1089  book->getIssuerOut()) &&
1090  !issueMatchesOrigin(book->book().out) &&
1091  (!bDestOnly ||
1092  (book->getCurrencyOut() == mDstAmount.getCurrency())))
1093  {
1094  STPath newPath(currentPath);
1095 
1096  if (book->getCurrencyOut().isZero())
1097  { // to XRP
1098 
1099  // add the order book itself
1100  newPath.emplace_back(
1102  xrpAccount(),
1103  xrpCurrency(),
1104  xrpAccount());
1105 
1106  if (mDstAmount.getCurrency().isZero())
1107  {
1108  // destination is XRP, add account and path is
1109  // complete
1110  JLOG(j_.trace())
1111  << "complete path found bx: "
1112  << currentPath.getJson(JsonOptions::none);
1113  addUniquePath(mCompletePaths, newPath);
1114  }
1115  else
1116  incompletePaths.push_back(newPath);
1117  }
1118  else if (!currentPath.hasSeen(
1119  book->getIssuerOut(),
1120  book->getCurrencyOut(),
1121  book->getIssuerOut()))
1122  {
1123  // Don't want the book if we've already seen the issuer
1124  // book -> account -> book
1125  if ((newPath.size() >= 2) &&
1126  (newPath.back().isAccount()) &&
1127  (newPath[newPath.size() - 2].isOffer()))
1128  {
1129  // replace the redundant account with the order book
1130  newPath[newPath.size() - 1] = STPathElement(
1133  xrpAccount(),
1134  book->getCurrencyOut(),
1135  book->getIssuerOut());
1136  }
1137  else
1138  {
1139  // add the order book
1140  newPath.emplace_back(
1143  xrpAccount(),
1144  book->getCurrencyOut(),
1145  book->getIssuerOut());
1146  }
1147 
1148  if (hasEffectiveDestination &&
1149  book->getIssuerOut() == mDstAccount &&
1150  book->getCurrencyOut() == mDstAmount.getCurrency())
1151  {
1152  // We skipped a required issuer
1153  }
1154  else if (
1155  book->getIssuerOut() == mEffectiveDst &&
1156  book->getCurrencyOut() == mDstAmount.getCurrency())
1157  { // with the destination account, this path is
1158  // complete
1159  JLOG(j_.trace())
1160  << "complete path found ba: "
1161  << currentPath.getJson(JsonOptions::none);
1162  addUniquePath(mCompletePaths, newPath);
1163  }
1164  else
1165  {
1166  // add issuer's account, path still incomplete
1167  incompletePaths.assembleAdd(
1168  newPath,
1169  STPathElement(
1171  book->getIssuerOut(),
1172  book->getCurrencyOut(),
1173  book->getIssuerOut()));
1174  }
1175  }
1176  }
1177  }
1178  }
1179  }
1180 }
1181 
1182 namespace {
1183 
1185 makePath(char const* string)
1186 {
1188 
1189  while (true)
1190  {
1191  switch (*string++)
1192  {
1193  case 's': // source
1195  break;
1196 
1197  case 'a': // accounts
1199  break;
1200 
1201  case 'b': // books
1203  break;
1204 
1205  case 'x': // xrp book
1207  break;
1208 
1209  case 'f': // book to final currency
1211  break;
1212 
1213  case 'd':
1214  // Destination (with account, if required and not already
1215  // present).
1217  break;
1218 
1219  case 0:
1220  return ret;
1221  }
1222  }
1223 }
1224 
1225 void
1226 fillPaths(Pathfinder::PaymentType type, PathCostList const& costs)
1227 {
1228  auto& list = mPathTable[type];
1229  assert(list.empty());
1230  for (auto& cost : costs)
1231  list.push_back({cost.cost, makePath(cost.path)});
1232 }
1233 
1234 } // namespace
1235 
1236 // Costs:
1237 // 0 = minimum to make some payments possible
1238 // 1 = include trivial paths to make common cases work
1239 // 4 = normal fast search level
1240 // 7 = normal slow search level
1241 // 10 = most agressive
1242 
1243 void
1245 {
1246  // CAUTION: Do not include rules that build default paths
1247 
1248  mPathTable.clear();
1249  fillPaths(pt_XRP_to_XRP, {});
1250 
1251  fillPaths(
1253  {{1, "sfd"}, // source -> book -> gateway
1254  {3, "sfad"}, // source -> book -> account -> destination
1255  {5, "sfaad"}, // source -> book -> account -> account -> destination
1256  {6, "sbfd"}, // source -> book -> book -> destination
1257  {8, "sbafd"}, // source -> book -> account -> book -> destination
1258  {9, "sbfad"}, // source -> book -> book -> account -> destination
1259  {10, "sbafad"}});
1260 
1261  fillPaths(
1263  {{1, "sxd"}, // gateway buys XRP
1264  {2, "saxd"}, // source -> gateway -> book(XRP) -> dest
1265  {6, "saaxd"},
1266  {7, "sbxd"},
1267  {8, "sabxd"},
1268  {9, "sabaxd"}});
1269 
1270  // non-XRP to non-XRP (same currency)
1271  fillPaths(
1273  {
1274  {1, "sad"}, // source -> gateway -> destination
1275  {1, "sfd"}, // source -> book -> destination
1276  {4, "safd"}, // source -> gateway -> book -> destination
1277  {4, "sfad"},
1278  {5, "saad"},
1279  {5, "sbfd"},
1280  {6, "sxfad"},
1281  {6, "safad"},
1282  {6, "saxfd"}, // source -> gateway -> book to XRP -> book ->
1283  // destination
1284  {6, "saxfad"},
1285  {6, "sabfd"}, // source -> gateway -> book -> book -> destination
1286  {7, "saaad"},
1287  });
1288 
1289  // non-XRP to non-XRP (different currency)
1290  fillPaths(
1292  {
1293  {1, "sfad"},
1294  {1, "safd"},
1295  {3, "safad"},
1296  {4, "sxfd"},
1297  {5, "saxfd"},
1298  {5, "sxfad"},
1299  {5, "sbfd"},
1300  {6, "saxfad"},
1301  {6, "sabfd"},
1302  {7, "saafd"},
1303  {8, "saafad"},
1304  {9, "safaad"},
1305  });
1306 }
1307 
1308 } // namespace ripple
ripple::Pathfinder::pt_nonXRP_to_nonXRP
@ pt_nonXRP_to_nonXRP
Definition: Pathfinder.h:96
ripple::RippleState
Wraps a trust line SLE for convenience.
Definition: RippleState.h:39
ripple::Pathfinder::mSrcCurrency
Currency mSrcCurrency
Definition: Pathfinder.h:187
ripple::Application
Definition: Application.h:115
ripple::Pathfinder::mSrcAccount
AccountID mSrcAccount
Definition: Pathfinder.h:183
ripple::Application::getOrderBookDB
virtual OrderBookDB & getOrderBookDB()=0
ripple::Pathfinder::PathType
std::vector< NodeType > PathType
Definition: Pathfinder.h:87
ripple::STPath::push_back
void push_back(STPathElement const &e)
Definition: STPathSet.h:234
std::bind
T bind(T... args)
ripple::Issue
A currency issued by an account.
Definition: Issue.h:34
ripple::lsfGlobalFreeze
@ lsfGlobalFreeze
Definition: LedgerFormats.h:219
std::string
STL class.
std::shared_ptr
STL class.
ripple::Pathfinder::afADD_ACCOUNTS
static const std::uint32_t afADD_ACCOUNTS
Definition: Pathfinder.h:210
std::exception
STL class.
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::jtPATH_FIND
@ jtPATH_FIND
Definition: Job.h:76
ripple::PaymentSandbox
A wrapper which makes credits unavailable to balances.
Definition: PaymentSandbox.h:112
ripple::removeIssuer
static STPath removeIssuer(STPath const &path)
Definition: Pathfinder.cpp:465
ripple::RippleState::getFreezePeer
bool getFreezePeer() const
Has the peer set the freeze flag on us.
Definition: RippleState.h:117
std::vector::reserve
T reserve(T... args)
ripple::lsfLowNoRipple
@ lsfLowNoRipple
Definition: LedgerFormats.h:233
std::vector
STL class.
ripple::Pathfinder::afOB_LAST
static const std::uint32_t afOB_LAST
Definition: Pathfinder.h:219
std::optional::value_or
T value_or(T... args)
ripple::path::RippleCalc::Input
Definition: RippleCalc.h:46
ripple::isDefaultPath
static bool isDefaultPath(STPath const &path)
Definition: Pathfinder.cpp:446
ripple::Pathfinder::nt_XRP_BOOK
@ nt_XRP_BOOK
Definition: Pathfinder.h:81
ripple::Pathfinder::mPaths
std::map< PathType, STPathSet > mPaths
Definition: Pathfinder.h:202
ripple::Issue::currency
Currency currency
Definition: Issue.h:37
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
ripple::transToken
std::string transToken(TER code)
Definition: TER.cpp:196
ripple::Pathfinder::mDstAmount
STAmount mDstAmount
Definition: Pathfinder.h:186
ripple::Pathfinder::app_
Application & app_
Definition: Pathfinder.h:206
ripple::Pathfinder::findPaths
bool findPaths(int searchLevel)
Definition: Pathfinder.cpp:194
tuple
ripple::Pathfinder::initPathTable
static void initPathTable()
Definition: Pathfinder.cpp:1244
ripple::STPathElement::getCurrency
Currency const & getCurrency() const
Definition: STPathSet.h:177
std::vector::back
T back(T... args)
ripple::OrderBookDB::getBooksByTakerPays
OrderBook::List getBooksByTakerPays(Issue const &)
Definition: OrderBookDB.cpp:189
ripple::tapNONE
@ tapNONE
Definition: ApplyView.h:30
ripple::STPath::size
std::vector< STPathElement >::size_type size() const
Definition: STPathSet.h:222
ripple::STPath::end
std::vector< STPathElement >::const_iterator end() const
Definition: STPathSet.h:261
ripple::RippleState::getAuth
bool getAuth() const
Definition: RippleState.h:79
std::sort
T sort(T... args)
ripple::STPathElement::typeCurrency
@ typeCurrency
Definition: STPathSet.h:40
ripple::STPathSet
Definition: STPathSet.h:309
ripple::Pathfinder::pt_XRP_to_XRP
@ pt_XRP_to_XRP
Definition: Pathfinder.h:92
ripple::Pathfinder::isNoRipple
bool isNoRipple(AccountID const &fromAccount, AccountID const &toAccount, Currency const &currency)
Definition: Pathfinder.cpp:841
ripple::Pathfinder::getPathLiquidity
TER getPathLiquidity(STPath const &path, STAmount const &minDstAmount, STAmount &amountOut, uint64_t &qualityOut) const
Definition: Pathfinder.cpp:337
ripple::STPathSet::empty
bool empty() const
Definition: STPathSet.h:387
ripple::STPathElement::typeIssuer
@ typeIssuer
Definition: STPathSet.h:41
std::vector::clear
T clear(T... args)
ripple::Pathfinder::afAC_LAST
static const std::uint32_t afAC_LAST
Definition: Pathfinder.h:222
ripple::Pathfinder::getBestPaths
STPathSet getBestPaths(int maxPaths, STPath &fullLiquidityPath, STPathSet const &extraPaths, AccountID const &srcIssuer)
Definition: Pathfinder.cpp:553
std::vector::push_back
T push_back(T... args)
ripple::getRate
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition: STAmount.cpp:442
ripple::divide
STAmount divide(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:77
ripple::OrderBookDB::isBookToXRP
bool isBookToXRP(Issue const &)
Definition: OrderBookDB.cpp:205
ripple::base_uint
Integers of any length that is a multiple of 32-bits.
Definition: base_uint.h:75
ripple::convertAllCheck
bool convertAllCheck(STAmount const &a)
Definition: PathfinderUtils.h:46
ripple::STAmount::getFullText
std::string getFullText() const override
Definition: STAmount.cpp:489
ripple::RippleState::getLimit
STAmount const & getLimit() const
Definition: RippleState.h:129
ripple::lsfRequireAuth
@ lsfRequireAuth
Definition: LedgerFormats.h:214
ripple::RippleState::getAccountIDPeer
AccountID const & getAccountIDPeer() const
Definition: RippleState.h:72
ripple::Pathfinder::mSrcAmount
STAmount mSrcAmount
Definition: Pathfinder.h:189
ripple::OrderBookDB::getBookSize
int getBookSize(Issue const &)
Definition: OrderBookDB.cpp:197
ripple::QualityDirection::out
@ out
ripple::STPathElement::getNodeType
auto getNodeType() const
Definition: STPathSet.h:133
ripple::path::RippleCalc::Input::defaultPathsAllowed
bool defaultPathsAllowed
Definition: RippleCalc.h:51
ripple::Pathfinder::nt_BOOKS
@ nt_BOOKS
Definition: Pathfinder.h:80
ripple::base_uint::isZero
bool isZero() const
Definition: base_uint.h:511
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:130
ripple::Pathfinder::mSrcIssuer
std::optional< AccountID > mSrcIssuer
Definition: Pathfinder.h:188
ripple::Pathfinder::computePathRanks
void computePathRanks(int maxPaths)
Compute the rankings of the paths.
Definition: Pathfinder.cpp:404
ripple::Pathfinder::addPathsForType
STPathSet & addPathsForType(PathType const &type)
Definition: Pathfinder.cpp:770
ripple::JsonOptions::none
@ none
ripple::TERSubset< CanCvtToTER >
ripple::Pathfinder::addLinks
void addLinks(STPathSet const &currentPaths, STPathSet &incompletePaths, int addFlags)
Definition: Pathfinder.cpp:758
ripple::Application::getJobQueue
virtual JobQueue & getJobQueue()=0
ripple::Pathfinder::mLedger
std::shared_ptr< ReadView const > mLedger
Definition: Pathfinder.h:195
ripple::STPathElement::getAccountID
AccountID const & getAccountID() const
Definition: STPathSet.h:171
ripple::Pathfinder::rankPaths
void rankPaths(int maxPaths, STPathSet const &paths, std::vector< PathRank > &rankedPaths)
Definition: Pathfinder.cpp:480
ripple::STAmount
Definition: STAmount.h:43
ripple::RippleState::getNoRipplePeer
bool getNoRipplePeer() const
Definition: RippleState.h:103
ripple::STPath::emplace_back
void emplace_back(Args &&... args)
Definition: STPathSet.h:241
ripple::xrpAccount
AccountID const & xrpAccount()
Compute AccountID from public key.
Definition: AccountID.cpp:90
beast::Journal::error
Stream error() const
Definition: Journal.h:333
beast::Journal::info
Stream info() const
Definition: Journal.h:321
ripple::Pathfinder::mRLCache
std::shared_ptr< RippleLineCache > mRLCache
Definition: Pathfinder.h:197
ripple::Application::logs
virtual Logs & logs()=0
ripple::STPath::back
std::vector< STPathElement >::const_reference back() const
Definition: STPathSet.h:273
ripple::Pathfinder::addLink
void addLink(STPath const &currentPath, STPathSet &incompletePaths, int addFlags)
Definition: Pathfinder.cpp:893
ripple::isXRP
bool isXRP(AccountID const &c)
Definition: AccountID.h:89
ripple::Pathfinder::Pathfinder
Pathfinder(std::shared_ptr< RippleLineCache > const &cache, AccountID const &srcAccount, AccountID const &dstAccount, Currency const &uSrcCurrency, std::optional< AccountID > const &uSrcIssuer, STAmount const &dstAmount, std::optional< STAmount > const &srcAmount, Application &app)
Construct a pathfinder without an issuer.
Definition: Pathfinder.cpp:160
std::uint32_t
ripple::Pathfinder::m_loadEvent
std::unique_ptr< LoadEvent > m_loadEvent
Definition: Pathfinder.h:196
ripple::Pathfinder::mRemainingAmount
STAmount mRemainingAmount
The amount remaining from mSrcAccount after the default liquidity has been removed.
Definition: Pathfinder.h:192
ripple::Pathfinder::getPathsOut
int getPathsOut(Currency const &currency, AccountID const &account, bool isDestCurrency, AccountID const &dest)
Definition: Pathfinder.cpp:691
ripple::keylet::line
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition: Indexes.cpp:190
std::map
STL class.
std::vector::pop_back
T pop_back(T... args)
ripple::Pathfinder::nt_DESTINATION
@ nt_DESTINATION
Definition: Pathfinder.h:83
ripple::Pathfinder::mEffectiveDst
AccountID mEffectiveDst
Definition: Pathfinder.h:185
std::string::append
T append(T... args)
ripple::STAmount::getCurrency
Currency const & getCurrency() const
Definition: STAmount.h:205
ripple::lsfHighNoRipple
@ lsfHighNoRipple
Definition: LedgerFormats.h:234
ripple::Pathfinder::mDstAccount
AccountID mDstAccount
Definition: Pathfinder.h:184
ripple::Pathfinder::mSource
STPathElement mSource
Definition: Pathfinder.h:199
ripple::Pathfinder::mCompletePaths
STPathSet mCompletePaths
Definition: Pathfinder.h:200
ripple::STPath::getJson
Json::Value getJson(JsonOptions) const
Definition: STPathSet.cpp:148
ripple::Pathfinder::pt_XRP_to_nonXRP
@ pt_XRP_to_nonXRP
Definition: Pathfinder.h:93
ripple::STPath::hasSeen
bool hasSeen(AccountID const &account, Currency const &currency, AccountID const &issuer) const
Definition: STPathSet.cpp:133
ripple::STAmount::native
bool native() const noexcept
Definition: STAmount.h:183
ripple::STPath::empty
bool empty() const
Definition: STPathSet.h:228
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::addUniquePath
void addUniquePath(STPathSet &pathSet, STPath const &path)
Definition: Pathfinder.cpp:880
ripple::Pathfinder::afOB_XRP
static const std::uint32_t afOB_XRP
Definition: Pathfinder.h:216
ripple::Pathfinder::issueMatchesOrigin
bool issueMatchesOrigin(Issue const &)
Definition: Pathfinder.cpp:680
ripple::STPathElement
Definition: STPathSet.h:33
ripple::RippleState::getBalance
STAmount const & getBalance() const
Definition: RippleState.h:123
std::vector::begin
T begin(T... args)
ripple::sfFlags
const SF_UINT32 sfFlags
ripple::Pathfinder::j_
const beast::Journal j_
Definition: Pathfinder.h:207
ripple::STPathSet::getJson
Json::Value getJson(JsonOptions) const override
Definition: STPathSet.cpp:175
ripple::Pathfinder::pt_nonXRP_to_same
@ pt_nonXRP_to_same
Definition: Pathfinder.h:95
ripple::path::RippleCalc::rippleCalculate
static Output rippleCalculate(PaymentSandbox &view, STAmount const &saMaxAmountReq, STAmount const &saDstAmountReq, AccountID const &uDstAccountID, AccountID const &uSrcAccountID, STPathSet const &spsPaths, Logs &l, Input const *const pInputs=nullptr)
Definition: RippleCalc.cpp:31
ripple::STPathElement::typeAccount
@ typeAccount
Definition: STPathSet.h:38
ripple::Pathfinder::afADD_BOOKS
static const std::uint32_t afADD_BOOKS
Definition: Pathfinder.h:213
ripple::path::RippleCalc::Input::partialPaymentAllowed
bool partialPaymentAllowed
Definition: RippleCalc.h:50
ripple::STPathSet::size
std::vector< STPath >::size_type size() const
Definition: STPathSet.h:381
ripple::Pathfinder::pt_nonXRP_to_XRP
@ pt_nonXRP_to_XRP
Definition: Pathfinder.h:94
std::vector::empty
T empty(T... args)
ripple::Pathfinder::isNoRippleOut
bool isNoRippleOut(STPath const &currentPath)
Definition: Pathfinder.cpp:858
std::optional
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
ripple::Pathfinder::mPathsOutCountMap
hash_map< Issue, int > mPathsOutCountMap
Definition: Pathfinder.h:204
std::size_t
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:38
ripple::Pathfinder::nt_ACCOUNTS
@ nt_ACCOUNTS
Definition: Pathfinder.h:79
std::vector::end
T end(T... args)
ripple::Pathfinder::nt_DEST_BOOK
@ nt_DEST_BOOK
Definition: Pathfinder.h:82
ripple::tefEXCEPTION
@ tefEXCEPTION
Definition: TER.h:150
ripple::Pathfinder::PathRank
Definition: Pathfinder.h:99
ripple::RippleState::getLimitPeer
STAmount const & getLimitPeer() const
Definition: RippleState.h:135
ripple::Pathfinder::mPathRanks
std::vector< PathRank > mPathRanks
Definition: Pathfinder.h:201
ripple::JobQueue::makeLoadEvent
std::unique_ptr< LoadEvent > makeLoadEvent(JobType t, std::string const &name)
Return a scoped LoadEvent.
Definition: JobQueue.cpp:146
ripple::Pathfinder::PaymentType
PaymentType
Definition: Pathfinder.h:91
ripple::STPathSet::assembleAdd
bool assembleAdd(STPath const &base, STPathElement const &tail)
Definition: STPathSet.cpp:105
ripple::STPath
Definition: STPathSet.h:212
ripple::largestAmount
STAmount largestAmount(STAmount const &amt)
Definition: PathfinderUtils.h:28
ripple::Pathfinder::nt_SOURCE
@ nt_SOURCE
Definition: Pathfinder.h:78
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:217
ripple::Pathfinder::convert_all_
bool convert_all_
Definition: Pathfinder.h:193
ripple::xrpCurrency
Currency const & xrpCurrency()
XRP currency.
Definition: UintTypes.cpp:121
ripple::AccountID
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition: AccountID.h:47
ripple::Issue::account
AccountID account
Definition: Issue.h:38
std::exception::what
T what(T... args)
ripple::convertAmount
STAmount convertAmount(STAmount const &amt, bool all)
Definition: PathfinderUtils.h:37
ripple::STPathSet::push_back
void push_back(STPath const &e)
Definition: STPathSet.h:393