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