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/Tuning.h>
26 #include <ripple/app/paths/impl/PathfinderUtils.h>
27 #include <ripple/basics/Log.h>
28 #include <ripple/core/Config.h>
29 #include <ripple/core/JobQueue.h>
30 #include <ripple/json/to_string.h>
31 #include <ripple/ledger/PaymentSandbox.h>
32 
33 #include <tuple>
34 
35 /*
36 
37 Core Pathfinding Engine
38 
39 The pathfinding request is identified by category, XRP to XRP, XRP to
40 non-XRP, non-XRP to XRP, same currency non-XRP to non-XRP, cross-currency
41 non-XRP to non-XRP. For each category, there is a table of paths that the
42 pathfinder searches for. Complete paths are collected.
43 
44 Each complete path is then rated and sorted. Paths with no or trivial
45 liquidity are dropped. Otherwise, paths are sorted based on quality,
46 liquidity, and path length.
47 
48 Path slots are filled in quality (ratio of out to in) order, with the
49 exception that the last path must have enough liquidity to complete the
50 payment (assuming no liquidity overlap). In addition, if no selected path
51 is capable of providing enough liquidity to complete the payment by itself,
52 an extra "covering" path is returned.
53 
54 The selected paths are then tested to determine if they can complete the
55 payment and, if so, at what cost. If they fail and a covering path was
56 found, the test is repeated with the covering path. If this succeeds, the
57 final paths and the estimated cost are returned.
58 
59 The engine permits the search depth to be selected and the paths table
60 includes the depth at which each path type is found. A search depth of zero
61 causes no searching to be done. Extra paths can also be injected, and this
62 should be used to preserve previously-found paths across invokations for the
63 same path request (particularly if the search depth may change).
64 
65 */
66 
67 namespace ripple {
68 
69 namespace {
70 
71 struct AccountCandidate
72 {
73  int priority;
74  AccountID account;
75 
76  static const int highPriority = 10000;
77 };
78 
79 bool
80 compareAccountCandidate(
81  std::uint32_t seq,
82  AccountCandidate const& first,
83  AccountCandidate const& second)
84 {
85  if (first.priority < second.priority)
86  return false;
87 
88  if (first.account > second.account)
89  return true;
90 
91  return (first.priority ^ seq) < (second.priority ^ seq);
92 }
93 
94 using AccountCandidates = std::vector<AccountCandidate>;
95 
96 struct CostedPath
97 {
98  int searchLevel;
100 };
101 
102 using CostedPathList = std::vector<CostedPath>;
103 
105 
106 struct PathCost
107 {
108  int cost;
109  char const* path;
110 };
111 using PathCostList = std::vector<PathCost>;
112 
113 static PathTable mPathTable;
114 
116 pathTypeToString(Pathfinder::PathType const& type)
117 {
118  std::string ret;
119 
120  for (auto const& node : type)
121  {
122  switch (node)
123  {
125  ret.append("s");
126  break;
128  ret.append("a");
129  break;
131  ret.append("b");
132  break;
134  ret.append("x");
135  break;
137  ret.append("f");
138  break;
140  ret.append("d");
141  break;
142  }
143  }
144 
145  return ret;
146 }
147 
148 // Return the smallest amount of useful liquidity for a given amount, and the
149 // total number of paths we have to evaluate.
150 STAmount
151 smallestUsefulAmount(STAmount const& amount, int maxPaths)
152 {
153  return divide(amount, STAmount(maxPaths + 2), amount.issue());
154 }
155 } // namespace
156 
159  AccountID const& uSrcAccount,
160  AccountID const& uDstAccount,
161  Currency const& uSrcCurrency,
162  boost::optional<AccountID> const& uSrcIssuer,
163  STAmount const& saDstAmount,
164  boost::optional<STAmount> const& srcAmount,
165  Application& app)
166  : mSrcAccount(uSrcAccount)
167  , mDstAccount(uDstAccount)
168  , mEffectiveDst(
169  isXRP(saDstAmount.getIssuer()) ? uDstAccount
170  : saDstAmount.getIssuer())
171  , mDstAmount(saDstAmount)
172  , mSrcCurrency(uSrcCurrency)
173  , mSrcIssuer(uSrcIssuer)
174  , mSrcAmount(srcAmount.value_or(STAmount(
175  {uSrcCurrency,
176  uSrcIssuer.value_or(
177  isXRP(uSrcCurrency) ? xrpAccount() : uSrcAccount)},
178  1u,
179  0,
180  true)))
181  , convert_all_(convertAllCheck(mDstAmount))
182  , mLedger(cache->getLedger())
183  , mRLCache(cache)
184  , app_(app)
185  , j_(app.journal("Pathfinder"))
186 {
187  assert(!uSrcIssuer || isXRP(uSrcCurrency) == isXRP(uSrcIssuer.get()));
188 }
189 
190 bool
191 Pathfinder::findPaths(int searchLevel)
192 {
193  if (mDstAmount == beast::zero)
194  {
195  // No need to send zero money.
196  JLOG(j_.debug()) << "Destination amount was zero.";
197  mLedger.reset();
198  return false;
199 
200  // TODO(tom): why do we reset the ledger just in this case and the one
201  // below - why don't we do it each time we return false?
202  }
203 
206  {
207  // No need to send to same account with same currency.
208  JLOG(j_.debug()) << "Tried to send to same issuer";
209  mLedger.reset();
210  return false;
211  }
212 
213  if (mSrcAccount == mEffectiveDst &&
215  {
216  // Default path might work, but any path would loop
217  return true;
218  }
219 
221  auto currencyIsXRP = isXRP(mSrcCurrency);
222 
223  bool useIssuerAccount = mSrcIssuer && !currencyIsXRP && !isXRP(*mSrcIssuer);
224  auto& account = useIssuerAccount ? *mSrcIssuer : mSrcAccount;
225  auto issuer = currencyIsXRP ? AccountID() : account;
226  mSource = STPathElement(account, mSrcCurrency, issuer);
227  auto issuerString =
229  JLOG(j_.trace()) << "findPaths>"
230  << " mSrcAccount=" << mSrcAccount
231  << " mDstAccount=" << mDstAccount
232  << " mDstAmount=" << mDstAmount.getFullText()
233  << " mSrcCurrency=" << mSrcCurrency
234  << " mSrcIssuer=" << issuerString;
235 
236  if (!mLedger)
237  {
238  JLOG(j_.debug()) << "findPaths< no ledger";
239  return false;
240  }
241 
242  bool bSrcXrp = isXRP(mSrcCurrency);
243  bool bDstXrp = isXRP(mDstAmount.getCurrency());
244 
245  if (!mLedger->exists(keylet::account(mSrcAccount)))
246  {
247  // We can't even start without a source account.
248  JLOG(j_.debug()) << "invalid source account";
249  return false;
250  }
251 
252  if ((mEffectiveDst != mDstAccount) &&
254  {
255  JLOG(j_.debug()) << "Non-existent gateway";
256  return false;
257  }
258 
259  if (!mLedger->exists(keylet::account(mDstAccount)))
260  {
261  // Can't find the destination account - we must be funding a new
262  // account.
263  if (!bDstXrp)
264  {
265  JLOG(j_.debug()) << "New account not being funded in XRP ";
266  return false;
267  }
268 
269  auto const reserve = STAmount(mLedger->fees().accountReserve(0));
270  if (mDstAmount < reserve)
271  {
272  JLOG(j_.debug())
273  << "New account not getting enough funding: " << mDstAmount
274  << " < " << reserve;
275  return false;
276  }
277  }
278 
279  // Now compute the payment type from the types of the source and destination
280  // currencies.
281  PaymentType paymentType;
282  if (bSrcXrp && bDstXrp)
283  {
284  // XRP -> XRP
285  JLOG(j_.debug()) << "XRP to XRP payment";
286  paymentType = pt_XRP_to_XRP;
287  }
288  else if (bSrcXrp)
289  {
290  // XRP -> non-XRP
291  JLOG(j_.debug()) << "XRP to non-XRP payment";
292  paymentType = pt_XRP_to_nonXRP;
293  }
294  else if (bDstXrp)
295  {
296  // non-XRP -> XRP
297  JLOG(j_.debug()) << "non-XRP to XRP payment";
298  paymentType = pt_nonXRP_to_XRP;
299  }
300  else if (mSrcCurrency == mDstAmount.getCurrency())
301  {
302  // non-XRP -> non-XRP - Same currency
303  JLOG(j_.debug()) << "non-XRP to non-XRP - same currency";
304  paymentType = pt_nonXRP_to_same;
305  }
306  else
307  {
308  // non-XRP to non-XRP - Different currency
309  JLOG(j_.debug()) << "non-XRP to non-XRP - cross currency";
310  paymentType = pt_nonXRP_to_nonXRP;
311  }
312 
313  // Now iterate over all paths for that paymentType.
314  for (auto const& costedPath : mPathTable[paymentType])
315  {
316  // Only use paths with at most the current search level.
317  if (costedPath.searchLevel <= searchLevel)
318  {
319  addPathsForType(costedPath.type);
320 
321  // TODO(tom): we might be missing other good paths with this
322  // arbitrary cut off.
324  break;
325  }
326  }
327 
328  JLOG(j_.debug()) << mCompletePaths.size() << " complete paths found";
329 
330  // Even if we find no paths, default paths may work, and we don't check them
331  // currently.
332  return true;
333 }
334 
335 TER
337  STPath const& path, // IN: The path to check.
338  STAmount const& minDstAmount, // IN: The minimum output this path must
339  // deliver to be worth keeping.
340  STAmount& amountOut, // OUT: The actual liquidity along the path.
341  uint64_t& qualityOut) const // OUT: The returned initial quality
342 {
343  STPathSet pathSet;
344  pathSet.push_back(path);
345 
346  path::RippleCalc::Input rcInput;
347  rcInput.defaultPathsAllowed = false;
348 
349  PaymentSandbox sandbox(&*mLedger, tapNONE);
350 
351  try
352  {
353  // Compute a path that provides at least the minimum liquidity.
354  if (convert_all_)
355  rcInput.partialPaymentAllowed = true;
356 
358  sandbox,
359  mSrcAmount,
360  minDstAmount,
361  mDstAccount,
362  mSrcAccount,
363  pathSet,
364  app_.logs(),
365  &rcInput);
366  // If we can't get even the minimum liquidity requested, we're done.
367  if (rc.result() != tesSUCCESS)
368  return rc.result();
369 
370  qualityOut = getRate(rc.actualAmountOut, rc.actualAmountIn);
371  amountOut = rc.actualAmountOut;
372 
373  if (!convert_all_)
374  {
375  // Now try to compute the remaining liquidity.
376  rcInput.partialPaymentAllowed = true;
378  sandbox,
379  mSrcAmount,
380  mDstAmount - amountOut,
381  mDstAccount,
382  mSrcAccount,
383  pathSet,
384  app_.logs(),
385  &rcInput);
386 
387  // If we found further liquidity, add it into the result.
388  if (rc.result() == tesSUCCESS)
389  amountOut += rc.actualAmountOut;
390  }
391 
392  return tesSUCCESS;
393  }
394  catch (std::exception const& e)
395  {
396  JLOG(j_.info()) << "checkpath: exception (" << e.what() << ") "
397  << path.getJson(JsonOptions::none);
398  return tefEXCEPTION;
399  }
400 }
401 
402 void
404 {
406 
407  // Must subtract liquidity in default path from remaining amount.
408  try
409  {
410  PaymentSandbox sandbox(&*mLedger, tapNONE);
411 
412  path::RippleCalc::Input rcInput;
413  rcInput.partialPaymentAllowed = true;
415  sandbox,
416  mSrcAmount,
418  mDstAccount,
419  mSrcAccount,
420  STPathSet(),
421  app_.logs(),
422  &rcInput);
423 
424  if (rc.result() == tesSUCCESS)
425  {
426  JLOG(j_.debug())
427  << "Default path contributes: " << rc.actualAmountIn;
428  mRemainingAmount -= rc.actualAmountOut;
429  }
430  else
431  {
432  JLOG(j_.debug())
433  << "Default path fails: " << transToken(rc.result());
434  }
435  }
436  catch (std::exception const&)
437  {
438  JLOG(j_.debug()) << "Default path causes exception";
439  }
440 
441  rankPaths(maxPaths, mCompletePaths, mPathRanks);
442 }
443 
444 static bool
445 isDefaultPath(STPath const& path)
446 {
447  // TODO(tom): default paths can consist of more than just an account:
448  // https://forum.ripple.com/viewtopic.php?f=2&t=8206&start=10#p57713
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:102
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:111
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:73
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:125
std::vector
STL class.
ripple::Pathfinder::afOB_LAST
static const std::uint32_t afOB_LAST
Definition: Pathfinder.h:219
ripple::path::RippleCalc::Input
Definition: RippleCalc.h:46
ripple::isDefaultPath
static bool isDefaultPath(STPath const &path)
Definition: Pathfinder.cpp:445
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:195
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:191
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::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:45
ripple::OrderBookDB::getBooksByTakerPays
OrderBook::List getBooksByTakerPays(Issue const &)
Definition: OrderBookDB.cpp:193
ripple::tapNONE
@ tapNONE
Definition: ApplyView.h:31
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
ripple::Pathfinder::Pathfinder
Pathfinder(std::shared_ptr< RippleLineCache > const &cache, AccountID const &srcAccount, AccountID const &dstAccount, Currency const &uSrcCurrency, boost::optional< AccountID > const &uSrcIssuer, STAmount const &dstAmount, boost::optional< STAmount > const &srcAmount, Application &app)
Construct a pathfinder without an issuer.
Definition: Pathfinder.cpp:157
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:336
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:209
ripple::base_uint< 160, detail::AccountIDTag >
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:106
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:201
ripple::QualityDirection::out
@ out
ripple::STPathElement::getNodeType
auto getNodeType() const
Definition: STPathSet.h:133
ripple::PATHFINDER_MAX_COMPLETE_PATHS
const int PATHFINDER_MAX_COMPLETE_PATHS
Definition: app/paths/Tuning.h:31
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:439
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:134
ripple::Pathfinder::computePathRanks
void computePathRanks(int maxPaths)
Compute the rankings of the paths.
Definition: Pathfinder.cpp:403
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
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:194
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:126
ripple::Pathfinder::mSrcIssuer
boost::optional< AccountID > mSrcIssuer
Definition: Pathfinder.h:188
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:149
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:134
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:176
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:32
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
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
ripple::Pathfinder::mPathsOutCountMap
hash_map< Issue, int > mPathsOutCountMap
Definition: Pathfinder.h:204
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:149
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:183
ripple::Pathfinder::PaymentType
PaymentType
Definition: Pathfinder.h:91
ripple::STPathSet::assembleAdd
bool assembleAdd(STPath const &base, STPathElement const &tail)
Definition: STPathSet.cpp:106
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:216
ripple::Pathfinder::convert_all_
bool convert_all_
Definition: Pathfinder.h:193
ripple::xrpCurrency
Currency const & xrpCurrency()
XRP currency.
Definition: UintTypes.cpp:124
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