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;
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  std::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  std::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  std::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;
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 
336 qualityUpperBound(ReadView const& v, Strand const& strand)
337 {
338  Quality q{STAmount::uRateOne};
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 std::nullopt;
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(ReadView const& v, std::optional<Quality> const& limitQuality)
381  {
382  // add the strands in `next_` to `cur_`, sorted by theoretical quality.
383  // Best quality first.
384  cur_.clear();
385  if (v.rules().enabled(featureFlowSortStrands) && !next_.empty())
386  {
388  strandQuals.reserve(next_.size());
389  if (next_.size() > 1) // no need to sort one strand
390  {
391  for (Strand const* strand : next_)
392  {
393  if (!strand)
394  {
395  // should not happen
396  continue;
397  }
398  if (auto const qual = qualityUpperBound(v, *strand))
399  {
400  if (limitQuality && *qual < *limitQuality)
401  {
402  // If a strand's quality is ever over limitQuality
403  // it is no longer part of the candidate set. Note
404  // that when transfer fees are charged, and an
405  // account goes from redeeming to issuing then
406  // strand quality _can_ increase; However, this is
407  // an unusual corner case.
408  continue;
409  }
410  strandQuals.push_back({*qual, strand});
411  }
412  }
413  // must stable sort for deterministic order across different c++
414  // standard library implementations
416  strandQuals.begin(),
417  strandQuals.end(),
418  [](auto const& lhs, auto const& rhs) {
419  // higher qualities first
420  return std::get<Quality>(lhs) > std::get<Quality>(rhs);
421  });
422  next_.clear();
423  next_.reserve(strandQuals.size());
424  for (auto const& sq : strandQuals)
425  {
426  next_.push_back(std::get<Strand const*>(sq));
427  }
428  }
429  }
430  std::swap(cur_, next_);
431  }
432 
433  Strand const*
434  get(size_t i) const
435  {
436  if (i >= cur_.size())
437  {
438  assert(0);
439  return nullptr;
440  }
441  return cur_[i];
442  }
443 
444  void
445  push(Strand const* s)
446  {
447  next_.push_back(s);
448  }
449 
450  // Push the strands from index i to the end of cur_ to next_
451  void
452  pushRemainingCurToNext(size_t i)
453  {
454  if (i >= cur_.size())
455  return;
456  next_.insert(next_.end(), std::next(cur_.begin(), i), cur_.end());
457  }
458 
459  auto
460  size() const
461  {
462  return cur_.size();
463  }
464 
465  void
466  removeIndex(std::size_t i)
467  {
468  if (i >= next_.size())
469  return;
470  next_.erase(next_.begin() + i);
471  }
472 };
474 
494 template <class TInAmt, class TOutAmt>
495 FlowResult<TInAmt, TOutAmt>
497  PaymentSandbox const& baseView,
498  std::vector<Strand> const& strands,
499  TOutAmt const& outReq,
500  bool partialPayment,
501  bool offerCrossing,
502  std::optional<Quality> const& limitQuality,
503  std::optional<STAmount> const& sendMaxST,
504  beast::Journal j,
505  path::detail::FlowDebugInfo* flowDebugInfo = nullptr)
506 {
507  // Used to track the strand that offers the best quality (output/input
508  // ratio)
509  struct BestStrand
510  {
511  TInAmt in;
512  TOutAmt out;
513  PaymentSandbox sb;
514  Strand const& strand;
515  Quality quality;
516 
517  BestStrand(
518  TInAmt const& in_,
519  TOutAmt const& out_,
520  PaymentSandbox&& sb_,
521  Strand const& strand_,
522  Quality const& quality_)
523  : in(in_)
524  , out(out_)
525  , sb(std::move(sb_))
526  , strand(strand_)
527  , quality(quality_)
528  {
529  }
530  };
531 
532  std::size_t const maxTries = 1000;
533  std::size_t curTry = 0;
534  std::uint32_t maxOffersToConsider = 1500;
535  std::uint32_t offersConsidered = 0;
536 
537  // There is a bug in gcc that incorrectly warns about using uninitialized
538  // values if `remainingIn` is initialized through a copy constructor. We can
539  // get similar warnings for `sendMax` if it is initialized in the most
540  // natural way. Using `make_optional`, allows us to work around this bug.
541  TInAmt const sendMaxInit =
542  sendMaxST ? toAmount<TInAmt>(*sendMaxST) : TInAmt{beast::zero};
543  std::optional<TInAmt> const sendMax =
544  (sendMaxST && sendMaxInit >= beast::zero)
545  ? std::make_optional(sendMaxInit)
546  : std::nullopt;
547  std::optional<TInAmt> remainingIn =
548  !!sendMax ? std::make_optional(sendMaxInit) : std::nullopt;
549  // std::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;
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  std::optional<std::size_t> markInactiveOnUse;
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
ripple::Rules::enabled
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:94
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:527
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)
std::make_optional
T make_optional(T... args)
iterator
ripple::keylet::offer
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition: Indexes.cpp:222
std::optional::emplace
T emplace(T... args)
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)
std::optional::reset
T reset(T... args)
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::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, std::optional< Quality > const &limitQuality, std::optional< STAmount > const &sendMax, beast::Journal j, path::detail::FlowDebugInfo *flowDebugInfo=nullptr)
Make a payment from the src account to the dst account.
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:893
ripple::TER
TERSubset< CanCvtToTER > TER
Definition: TER.h:568
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::temUNKNOWN
@ temUNKNOWN
Definition: TER.h:122
ripple::tecPATH_PARTIAL
@ tecPATH_PARTIAL
Definition: TER.h:249
ripple::featureFlowSortStrands
const uint256 featureFlowSortStrands
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:59
ripple::tecPATH_DRY
@ tecPATH_DRY
Definition: TER.h:261
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
std::optional
std::size_t
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::detail::ApplyViewBase::peek
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
Definition: ApplyViewBase.cpp:128
ripple::detail::ApplyViewBase::rules
Rules const & rules() const override
Returns the tx processing rules.
Definition: ApplyViewBase.cpp:52
std::vector::end
T end(T... args)
ripple::StrandResult::StrandResult
StrandResult()=default
Strand result constructor.
ripple::tefEXCEPTION
@ tefEXCEPTION
Definition: TER.h:154
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:710
ripple::StrandResult::sandbox
std::optional< PaymentSandbox > sandbox
Resulting Sandbox state.
Definition: StrandFlow.h:50
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:222
ripple::STAmount::uRateOne
static const std::uint64_t uRateOne
Definition: STAmount.h:75
ripple::get
T & get(EitherAmount &amt)
Definition: AmountSpec.h:118
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