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