rippled
StrandFlow.h
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 #ifndef RIPPLE_APP_PATHS_IMPL_STRANDFLOW_H_INCLUDED
21 #define RIPPLE_APP_PATHS_IMPL_STRANDFLOW_H_INCLUDED
22 
23 #include <ripple/app/paths/Credit.h>
24 #include <ripple/app/paths/Flow.h>
25 #include <ripple/app/paths/impl/AmountSpec.h>
26 #include <ripple/app/paths/impl/FlatSets.h>
27 #include <ripple/app/paths/impl/FlowDebugInfo.h>
28 #include <ripple/app/paths/impl/Steps.h>
29 #include <ripple/basics/IOUAmount.h>
30 #include <ripple/basics/Log.h>
31 #include <ripple/basics/XRPAmount.h>
32 #include <ripple/protocol/Feature.h>
33 
34 #include <boost/container/flat_set.hpp>
35 
36 #include <algorithm>
37 #include <iterator>
38 #include <numeric>
39 #include <sstream>
40 
41 namespace ripple {
42 
44 template <class TInAmt, class TOutAmt>
46 {
47  bool success;
48  TInAmt in = beast::zero;
49  TOutAmt out = beast::zero;
50  boost::optional<PaymentSandbox> sandbox;
51  boost::container::flat_set<uint256> ofrsToRm;
52  // Num offers consumed or partially consumed (includes expired and unfunded
53  // offers)
55  // strand can be inactive if there is no more liquidity or too many offers
56  // have been consumed
57  bool inactive = false;
58 
61  StrandResult() = default;
62 
64  Strand const& strand,
65  TInAmt const& in_,
66  TOutAmt const& out_,
67  PaymentSandbox&& sandbox_,
68  boost::container::flat_set<uint256> ofrsToRm_,
69  bool inactive_)
70  : success(true)
71  , in(in_)
72  , out(out_)
73  , sandbox(std::move(sandbox_))
74  , ofrsToRm(std::move(ofrsToRm_))
75  , ofrsUsed(offersUsed(strand))
76  , inactive(inactive_)
77  {
78  }
79 
81  Strand const& strand,
82  boost::container::flat_set<uint256> ofrsToRm_)
83  : success(false)
84  , ofrsToRm(std::move(ofrsToRm_))
85  , ofrsUsed(offersUsed(strand))
86  {
87  }
88 };
89 
101 template <class TInAmt, class TOutAmt>
102 StrandResult<TInAmt, TOutAmt>
104  PaymentSandbox const& baseView,
105  Strand const& strand,
106  boost::optional<TInAmt> const& maxIn,
107  TOutAmt const& out,
108  beast::Journal j)
109 {
110  using Result = StrandResult<TInAmt, TOutAmt>;
111  if (strand.empty())
112  {
113  JLOG(j.warn()) << "Empty strand passed to Liquidity";
114  return {};
115  }
116 
117  boost::container::flat_set<uint256> ofrsToRm;
118 
119  if (isDirectXrpToXrp<TInAmt, TOutAmt>(strand))
120  {
121  return Result{strand, std::move(ofrsToRm)};
122  }
123 
124  try
125  {
126  std::size_t const s = strand.size();
127 
128  std::size_t limitingStep = strand.size();
129  boost::optional<PaymentSandbox> sb(&baseView);
130  // The "all funds" view determines if an offer becomes unfunded or is
131  // found unfunded
132  // These are the account balances before the strand executes
133  boost::optional<PaymentSandbox> afView(&baseView);
135  {
136  EitherAmount stepOut(out);
137  for (auto i = s; i--;)
138  {
139  auto r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
140  if (strand[i]->isZero(r.second))
141  {
142  JLOG(j.trace()) << "Strand found dry in rev";
143  return Result{strand, std::move(ofrsToRm)};
144  }
145 
146  if (i == 0 && maxIn && *maxIn < get<TInAmt>(r.first))
147  {
148  // limiting - exceeded maxIn
149  // Throw out previous results
150  sb.emplace(&baseView);
151  limitingStep = i;
152 
153  // re-execute the limiting step
154  r = strand[i]->fwd(
155  *sb, *afView, ofrsToRm, EitherAmount(*maxIn));
156  limitStepOut = r.second;
157 
158  if (strand[i]->isZero(r.second))
159  {
160  JLOG(j.trace()) << "First step found dry";
161  return Result{strand, std::move(ofrsToRm)};
162  }
163  if (get<TInAmt>(r.first) != *maxIn)
164  {
165  // Something is very wrong
166  // throwing out the sandbox can only increase liquidity
167  // yet the limiting is still limiting
168  JLOG(j.fatal())
169  << "Re-executed limiting step failed. r.first: "
170  << to_string(get<TInAmt>(r.first))
171  << " maxIn: " << to_string(*maxIn);
172  assert(0);
173  return Result{strand, std::move(ofrsToRm)};
174  }
175  }
176  else if (!strand[i]->equalOut(r.second, stepOut))
177  {
178  // limiting
179  // Throw out previous results
180  sb.emplace(&baseView);
181  afView.emplace(&baseView);
182  limitingStep = i;
183 
184  // re-execute the limiting step
185  stepOut = r.second;
186  r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
187  limitStepOut = r.second;
188 
189  if (strand[i]->isZero(r.second))
190  {
191  // A tiny input amount can cause this step to output
192  // zero. I.e. 10^-80 IOU into an IOU -> XRP offer.
193  JLOG(j.trace()) << "Limiting step found dry";
194  return Result{strand, std::move(ofrsToRm)};
195  }
196  if (!strand[i]->equalOut(r.second, stepOut))
197  {
198  // Something is very wrong
199  // throwing out the sandbox can only increase liquidity
200  // yet the limiting is still limiting
201 #ifndef NDEBUG
202  JLOG(j.fatal())
203  << "Re-executed limiting step failed. r.second: "
204  << r.second << " stepOut: " << stepOut;
205 #else
206  JLOG(j.fatal()) << "Re-executed limiting step failed";
207 #endif
208  assert(0);
209  return Result{strand, std::move(ofrsToRm)};
210  }
211  }
212 
213  // prev node needs to produce what this node wants to consume
214  stepOut = r.first;
215  }
216  }
217 
218  {
219  EitherAmount stepIn(limitStepOut);
220  for (auto i = limitingStep + 1; i < s; ++i)
221  {
222  auto const r = strand[i]->fwd(*sb, *afView, ofrsToRm, stepIn);
223  if (strand[i]->isZero(r.second))
224  {
225  // A tiny input amount can cause this step to output zero.
226  // I.e. 10^-80 IOU into an IOU -> XRP offer.
227  JLOG(j.trace()) << "Non-limiting step found dry";
228  return Result{strand, std::move(ofrsToRm)};
229  }
230  if (!strand[i]->equalIn(r.first, stepIn))
231  {
232  // The limits should already have been found, so executing a
233  // strand forward from the limiting step should not find a
234  // new limit
235 #ifndef NDEBUG
236  JLOG(j.fatal())
237  << "Re-executed forward pass failed. r.first: "
238  << r.first << " stepIn: " << stepIn;
239 #else
240  JLOG(j.fatal()) << "Re-executed forward pass failed";
241 #endif
242  assert(0);
243  return Result{strand, std::move(ofrsToRm)};
244  }
245  stepIn = r.second;
246  }
247  }
248 
249  auto const strandIn = *strand.front()->cachedIn();
250  auto const strandOut = *strand.back()->cachedOut();
251 
252 #ifndef NDEBUG
253  {
254  // Check that the strand will execute as intended
255  // Re-executing the strand will change the cached values
256  PaymentSandbox checkSB(&baseView);
257  PaymentSandbox checkAfView(&baseView);
258  EitherAmount stepIn(*strand[0]->cachedIn());
259  for (auto i = 0; i < s; ++i)
260  {
261  bool valid;
262  std::tie(valid, stepIn) =
263  strand[i]->validFwd(checkSB, checkAfView, stepIn);
264  if (!valid)
265  {
266  JLOG(j.warn())
267  << "Strand re-execute check failed. Step: " << i;
268  break;
269  }
270  }
271  }
272 #endif
273 
274  bool const inactive = std::any_of(
275  strand.begin(),
276  strand.end(),
277  [](std::unique_ptr<Step> const& step) { return step->inactive(); });
278 
279  return Result(
280  strand,
281  get<TInAmt>(strandIn),
282  get<TOutAmt>(strandOut),
283  std::move(*sb),
284  std::move(ofrsToRm),
285  inactive);
286  }
287  catch (FlowException const&)
288  {
289  return Result{strand, std::move(ofrsToRm)};
290  }
291 }
292 
294 template <class TInAmt, class TOutAmt>
295 struct FlowResult
296 {
297  TInAmt in = beast::zero;
298  TOutAmt out = beast::zero;
299  boost::optional<PaymentSandbox> sandbox;
300  boost::container::flat_set<uint256> removableOffers;
301  TER ter = temUNKNOWN;
302 
303  FlowResult() = default;
304 
305  FlowResult(
306  TInAmt const& in_,
307  TOutAmt const& out_,
308  PaymentSandbox&& sandbox_,
309  boost::container::flat_set<uint256> ofrsToRm)
310  : in(in_)
311  , out(out_)
312  , sandbox(std::move(sandbox_))
313  , removableOffers(std::move(ofrsToRm))
314  , ter(tesSUCCESS)
315  {
316  }
317 
318  FlowResult(TER ter_, boost::container::flat_set<uint256> ofrsToRm)
319  : removableOffers(std::move(ofrsToRm)), ter(ter_)
320  {
321  }
322 
323  FlowResult(
324  TER ter_,
325  TInAmt const& in_,
326  TOutAmt const& out_,
327  boost::container::flat_set<uint256> ofrsToRm)
328  : in(in_), out(out_), removableOffers(std::move(ofrsToRm)), ter(ter_)
329  {
330  }
331 };
333 
335 inline boost::optional<Quality>
336 qualityUpperBound(ReadView const& v, Strand const& strand)
337 {
338  Quality q{STAmount::uRateOne};
339  boost::optional<Quality> stepQ;
341  for (auto const& step : strand)
342  {
343  if (std::tie(stepQ, dir) = step->qualityUpperBound(v, dir); stepQ)
344  q = composed_quality(q, *stepQ);
345  else
346  return boost::none;
347  }
348  return q;
349 };
351 
353 /* Track the non-dry strands
354 
355  flow will search the non-dry strands (stored in `cur_`) for the best
356  available liquidity If flow doesn't use all the liquidity of a strand, that
357  strand is added to `next_`. The strands in `next_` are searched after the
358  current best liquidity is used.
359  */
360 class ActiveStrands
361 {
362 private:
363  // Strands to be explored for liquidity
365  // Strands that may be explored for liquidity on the next iteration
367 
368 public:
369  ActiveStrands(std::vector<Strand> const& strands)
370  {
371  cur_.reserve(strands.size());
372  next_.reserve(strands.size());
373  for (auto& strand : strands)
374  next_.push_back(&strand);
375  }
376 
377  // Start a new iteration in the search for liquidity
378  // Set the current strands to the strands in `next_`
379  void
380  activateNext(
381  ReadView const& v,
382  boost::optional<Quality> const& limitQuality)
383  {
384  // add the strands in `next_` to `cur_`, sorted by theoretical quality.
385  // Best quality first.
386  cur_.clear();
387  if (v.rules().enabled(featureFlowSortStrands) && !next_.empty())
388  {
390  strandQuals.reserve(next_.size());
391  if (next_.size() > 1) // no need to sort one strand
392  {
393  for (Strand const* strand : next_)
394  {
395  if (!strand)
396  {
397  // should not happen
398  continue;
399  }
400  if (auto const qual = qualityUpperBound(v, *strand))
401  {
402  if (limitQuality && *qual < *limitQuality)
403  {
404  // If a strand's quality is ever over limitQuality
405  // it is no longer part of the candidate set. Note
406  // that when transfer fees are charged, and an
407  // account goes from redeeming to issuing then
408  // strand quality _can_ increase; However, this is
409  // an unusual corner case.
410  continue;
411  }
412  strandQuals.push_back({*qual, strand});
413  }
414  }
415  // must stable sort for deterministic order across different c++
416  // standard library implementations
418  strandQuals.begin(),
419  strandQuals.end(),
420  [](auto const& lhs, auto const& rhs) {
421  // higher qualities first
422  return std::get<Quality>(lhs) > std::get<Quality>(rhs);
423  });
424  next_.clear();
425  next_.reserve(strandQuals.size());
426  for (auto const& sq : strandQuals)
427  {
428  next_.push_back(std::get<Strand const*>(sq));
429  }
430  }
431  }
432  std::swap(cur_, next_);
433  }
434 
435  Strand const*
436  get(size_t i) const
437  {
438  if (i >= cur_.size())
439  {
440  assert(0);
441  return nullptr;
442  }
443  return cur_[i];
444  }
445 
446  void
447  push(Strand const* s)
448  {
449  next_.push_back(s);
450  }
451 
452  // Push the strands from index i to the end of cur_ to next_
453  void
454  pushRemainingCurToNext(size_t i)
455  {
456  if (i >= cur_.size())
457  return;
458  next_.insert(next_.end(), std::next(cur_.begin(), i), cur_.end());
459  }
460 
461  auto
462  size() const
463  {
464  return cur_.size();
465  }
466 
467  void
468  removeIndex(std::size_t i)
469  {
470  if (i >= next_.size())
471  return;
472  next_.erase(next_.begin() + i);
473  }
474 };
476 
496 template <class TInAmt, class TOutAmt>
497 FlowResult<TInAmt, TOutAmt>
499  PaymentSandbox const& baseView,
500  std::vector<Strand> const& strands,
501  TOutAmt const& outReq,
502  bool partialPayment,
503  bool offerCrossing,
504  boost::optional<Quality> const& limitQuality,
505  boost::optional<STAmount> const& sendMaxST,
506  beast::Journal j,
507  path::detail::FlowDebugInfo* flowDebugInfo = nullptr)
508 {
509  // Used to track the strand that offers the best quality (output/input
510  // ratio)
511  struct BestStrand
512  {
513  TInAmt in;
514  TOutAmt out;
515  PaymentSandbox sb;
516  Strand const& strand;
517  Quality quality;
518 
519  BestStrand(
520  TInAmt const& in_,
521  TOutAmt const& out_,
522  PaymentSandbox&& sb_,
523  Strand const& strand_,
524  Quality const& quality_)
525  : in(in_)
526  , out(out_)
527  , sb(std::move(sb_))
528  , strand(strand_)
529  , quality(quality_)
530  {
531  }
532  };
533 
534  std::size_t const maxTries = 1000;
535  std::size_t curTry = 0;
536  std::uint32_t maxOffersToConsider = 1500;
537  std::uint32_t offersConsidered = 0;
538 
539  // There is a bug in gcc that incorrectly warns about using uninitialized
540  // values if `remainingIn` is initialized through a copy constructor. We can
541  // get similar warnings for `sendMax` if it is initialized in the most
542  // natural way. Using `make_optional`, allows us to work around this bug.
543  TInAmt const sendMaxInit =
544  sendMaxST ? toAmount<TInAmt>(*sendMaxST) : TInAmt{beast::zero};
545  boost::optional<TInAmt> const sendMax = boost::make_optional(
546  sendMaxST && sendMaxInit >= beast::zero, sendMaxInit);
547  boost::optional<TInAmt> remainingIn =
548  boost::make_optional(!!sendMax, sendMaxInit);
549  // boost::optional<TInAmt> remainingIn{sendMax};
550 
551  TOutAmt remainingOut(outReq);
552 
553  PaymentSandbox sb(&baseView);
554 
555  // non-dry strands
556  ActiveStrands activeStrands(strands);
557 
558  // Keeping a running sum of the amount in the order they are processed
559  // will not give the best precision. Keep a collection so they may be summed
560  // from smallest to largest
561  boost::container::flat_multiset<TInAmt> savedIns;
562  savedIns.reserve(maxTries);
563  boost::container::flat_multiset<TOutAmt> savedOuts;
564  savedOuts.reserve(maxTries);
565 
566  auto sum = [](auto const& col) {
567  using TResult = std::decay_t<decltype(*col.begin())>;
568  if (col.empty())
569  return TResult{beast::zero};
570  return std::accumulate(col.begin() + 1, col.end(), *col.begin());
571  };
572 
573  // These offers only need to be removed if the payment is not
574  // successful
575  boost::container::flat_set<uint256> ofrsToRmOnFail;
576 
577  while (remainingOut > beast::zero &&
578  (!remainingIn || *remainingIn > beast::zero))
579  {
580  ++curTry;
581  if (curTry >= maxTries)
582  {
583  return {telFAILED_PROCESSING, std::move(ofrsToRmOnFail)};
584  }
585 
586  activeStrands.activateNext(sb, limitQuality);
587 
588  boost::container::flat_set<uint256> ofrsToRm;
589  boost::optional<BestStrand> best;
590  if (flowDebugInfo)
591  flowDebugInfo->newLiquidityPass();
592  // Index of strand to mark as inactive (remove from the active list) if
593  // the liquidity is used. This is used for strands that consume too many
594  // offers Constructed as `false,0` to workaround a gcc warning about
595  // uninitialized variables
596  boost::optional<std::size_t> markInactiveOnUse{false, 0};
597  for (size_t strandIndex = 0, sie = activeStrands.size();
598  strandIndex != sie;
599  ++strandIndex)
600  {
601  Strand const* strand = activeStrands.get(strandIndex);
602  if (!strand)
603  {
604  // should not happen
605  continue;
606  }
607  if (offerCrossing && limitQuality)
608  {
609  auto const strandQ = qualityUpperBound(sb, *strand);
610  if (!strandQ || *strandQ < *limitQuality)
611  continue;
612  }
613  auto f = flow<TInAmt, TOutAmt>(
614  sb, *strand, remainingIn, remainingOut, j);
615 
616  // rm bad offers even if the strand fails
617  SetUnion(ofrsToRm, f.ofrsToRm);
618 
619  offersConsidered += f.ofrsUsed;
620 
621  if (!f.success || f.out == beast::zero)
622  continue;
623 
624  if (flowDebugInfo)
625  flowDebugInfo->pushLiquiditySrc(
626  EitherAmount(f.in), EitherAmount(f.out));
627 
628  assert(
629  f.out <= remainingOut && f.sandbox &&
630  (!remainingIn || f.in <= *remainingIn));
631 
632  Quality const q(f.out, f.in);
633 
634  JLOG(j.trace())
635  << "New flow iter (iter, in, out): " << curTry - 1 << " "
636  << to_string(f.in) << " " << to_string(f.out);
637 
638  if (limitQuality && q < *limitQuality)
639  {
640  JLOG(j.trace())
641  << "Path rejected by limitQuality"
642  << " limit: " << *limitQuality << " path q: " << q;
643  continue;
644  }
645 
646  if (baseView.rules().enabled(featureFlowSortStrands))
647  {
648  assert(!best);
649  if (!f.inactive)
650  activeStrands.push(strand);
651  best.emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
652  activeStrands.pushRemainingCurToNext(strandIndex + 1);
653  break;
654  }
655 
656  activeStrands.push(strand);
657 
658  if (!best || best->quality < q ||
659  (best->quality == q && best->out < f.out))
660  {
661  // If this strand is inactive (because it consumed too many
662  // offers) and ends up having the best quality, remove it
663  // from the activeStrands. If it doesn't end up having the
664  // best quality, keep it active.
665 
666  if (f.inactive)
667  {
668  // This should be `nextSize`, not `size`. This issue is
669  // fixed in featureFlowSortStrands.
670  markInactiveOnUse = activeStrands.size() - 1;
671  }
672  else
673  {
674  markInactiveOnUse.reset();
675  }
676 
677  best.emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
678  }
679  }
680 
681  bool const shouldBreak = [&] {
682  if (baseView.rules().enabled(featureFlowSortStrands))
683  return !best || offersConsidered >= maxOffersToConsider;
684  return !best;
685  }();
686 
687  if (best)
688  {
689  if (markInactiveOnUse)
690  {
691  activeStrands.removeIndex(*markInactiveOnUse);
692  markInactiveOnUse.reset();
693  }
694  savedIns.insert(best->in);
695  savedOuts.insert(best->out);
696  remainingOut = outReq - sum(savedOuts);
697  if (sendMax)
698  remainingIn = *sendMax - sum(savedIns);
699 
700  if (flowDebugInfo)
701  flowDebugInfo->pushPass(
702  EitherAmount(best->in),
703  EitherAmount(best->out),
704  activeStrands.size());
705 
706  JLOG(j.trace()) << "Best path: in: " << to_string(best->in)
707  << " out: " << to_string(best->out)
708  << " remainingOut: " << to_string(remainingOut);
709 
710  best->sb.apply(sb);
711  }
712  else
713  {
714  JLOG(j.trace()) << "All strands dry.";
715  }
716 
717  best.reset(); // view in best must be destroyed before modifying base
718  // view
719  if (!ofrsToRm.empty())
720  {
721  SetUnion(ofrsToRmOnFail, ofrsToRm);
722  for (auto const& o : ofrsToRm)
723  {
724  if (auto ok = sb.peek(keylet::offer(o)))
725  offerDelete(sb, ok, j);
726  }
727  }
728 
729  if (shouldBreak)
730  break;
731  }
732 
733  auto const actualOut = sum(savedOuts);
734  auto const actualIn = sum(savedIns);
735 
736  JLOG(j.trace()) << "Total flow: in: " << to_string(actualIn)
737  << " out: " << to_string(actualOut);
738 
739  if (actualOut != outReq)
740  {
741  if (actualOut > outReq)
742  {
743  assert(0);
744  return {tefEXCEPTION, std::move(ofrsToRmOnFail)};
745  }
746  if (!partialPayment)
747  {
748  // If we're offerCrossing a !partialPayment, then we're
749  // handling tfFillOrKill. That case is handled below; not here.
750  if (!offerCrossing)
751  return {
753  actualIn,
754  actualOut,
755  std::move(ofrsToRmOnFail)};
756  }
757  else if (actualOut == beast::zero)
758  {
759  return {tecPATH_DRY, std::move(ofrsToRmOnFail)};
760  }
761  }
762  if (offerCrossing && !partialPayment)
763  {
764  // If we're offer crossing and partialPayment is *not* true, then
765  // we're handling a FillOrKill offer. In this case remainingIn must
766  // be zero (all funds must be consumed) or else we kill the offer.
767  assert(remainingIn);
768  if (remainingIn && *remainingIn != beast::zero)
769  return {
771  actualIn,
772  actualOut,
773  std::move(ofrsToRmOnFail)};
774  }
775 
776  return {actualIn, actualOut, std::move(sb), std::move(ofrsToRmOnFail)};
777 }
778 
779 } // namespace ripple
780 
781 #endif
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:339
sstream
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::composed_quality
Quality composed_quality(Quality const &lhs, Quality const &rhs)
Definition: Quality.cpp:101
ripple::limitStepOut
static void limitStepOut(Quality const &ofrQ, TAmounts< TIn, TOut > &ofrAmt, TAmounts< TIn, TOut > &stpAmt, TOut &ownerGives, std::uint32_t transferRateIn, std::uint32_t transferRateOut, TOut const &limit)
Definition: BookStep.cpp:528
ripple::StrandResult
Result of flow() execution of a single Strand.
Definition: StrandFlow.h:45
ripple::DebtDirection
DebtDirection
Definition: Steps.h:37
ripple::PaymentSandbox
A wrapper which makes credits unavailable to balances.
Definition: PaymentSandbox.h:112
ripple::path::detail::FlowDebugInfo
Definition: FlowDebugInfo.h:38
ripple::StrandResult::ofrsUsed
std::uint32_t ofrsUsed
Definition: StrandFlow.h:54
std::vector::reserve
T reserve(T... args)
std::vector
STL class.
std::vector::size
T size(T... args)
iterator
ripple::keylet::offer
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition: Indexes.cpp:223
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
ripple::StrandResult::out
TOutAmt out
Currency amount out.
Definition: StrandFlow.h:49
ripple::QualityDirection::in
@ in
std::any_of
T any_of(T... args)
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:45
algorithm
std::vector::clear
T clear(T... args)
std::tie
T tie(T... args)
std::vector::push_back
T push_back(T... args)
ripple::StrandResult::ofrsToRm
boost::container::flat_set< uint256 > ofrsToRm
Offers to remove.
Definition: StrandFlow.h:51
ripple::StrandResult::in
TInAmt in
Currency amount in.
Definition: StrandFlow.h:48
ripple::telFAILED_PROCESSING
@ telFAILED_PROCESSING
Definition: TER.h:55
ripple::QualityDirection::out
@ out
ripple::offerDelete
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition: View.cpp:899
ripple::TER
TERSubset< CanCvtToTER > TER
Definition: TER.h:551
ripple::StrandResult::StrandResult
StrandResult(Strand const &strand, boost::container::flat_set< uint256 > ofrsToRm_)
Definition: StrandFlow.h:80
std::accumulate
T accumulate(T... args)
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
std::uint32_t
ripple::Rules::enabled
bool enabled(uint256 const &id) const
Returns true if a feature is enabled.
Definition: ReadView.cpp:103
ripple::temUNKNOWN
@ temUNKNOWN
Definition: TER.h:120
ripple::tecPATH_PARTIAL
@ tecPATH_PARTIAL
Definition: TER.h:243
ripple::featureFlowSortStrands
const uint256 featureFlowSortStrands
Definition: Feature.cpp:192
std::swap
T swap(T... args)
std::decay_t
ripple::DebtDirection::issues
@ issues
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::StrandResult::success
bool success
Strand succeeded.
Definition: StrandFlow.h:47
std::vector::begin
T begin(T... args)
std
STL namespace.
ripple::EitherAmount
Definition: AmountSpec.h:57
ripple::tecPATH_DRY
@ tecPATH_DRY
Definition: TER.h:255
std::vector::empty
T empty(T... args)
ripple::SetUnion
void SetUnion(boost::container::flat_set< T > &dst, boost::container::flat_set< T > const &src)
Given two flat sets dst and src, compute dst = dst union src.
Definition: FlatSets.h:35
ripple::flow
path::RippleCalc::Output flow(PaymentSandbox &view, STAmount const &deliver, AccountID const &src, AccountID const &dst, STPathSet const &paths, bool defaultPaths, bool partialPayment, bool ownerPaysTransferFee, bool offerCrossing, boost::optional< Quality > const &limitQuality, boost::optional< STAmount > const &sendMax, beast::Journal j, path::detail::FlowDebugInfo *flowDebugInfo=nullptr)
Make a payment from the src account to the dst account.
std::size_t
ripple::detail::ApplyViewBase::peek
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
Definition: ApplyViewBase.cpp:129
ripple::detail::ApplyViewBase::rules
Rules const & rules() const override
Returns the tx processing rules.
Definition: ApplyViewBase.cpp:53
std::vector::end
T end(T... args)
ripple::StrandResult::StrandResult
StrandResult()=default
Strand result constructor.
ripple::tefEXCEPTION
@ tefEXCEPTION
Definition: TER.h:149
numeric
std::stable_sort
T stable_sort(T... args)
std::unique_ptr
STL class.
ripple::sum
static auto sum(TCollection const &col)
Definition: BookStep.cpp:711
ripple::StrandResult::inactive
bool inactive
Strand should not considered as a further source of liquidity (dry)
Definition: StrandFlow.h:57
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:216
ripple::StrandResult::sandbox
boost::optional< PaymentSandbox > sandbox
Resulting Sandbox state.
Definition: StrandFlow.h:50
ripple::STAmount::uRateOne
static const std::uint64_t uRateOne
Definition: STAmount.h:73
ripple::get
T & get(EitherAmount &amt)
Definition: AmountSpec.h:116
std::next
T next(T... args)
ripple::StrandResult::StrandResult
StrandResult(Strand const &strand, TInAmt const &in_, TOutAmt const &out_, PaymentSandbox &&sandbox_, boost::container::flat_set< uint256 > ofrsToRm_, bool inactive_)
Definition: StrandFlow.h:63