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/XRPAmount.h>
31 #include <ripple/basics/Log.h>
32 
33 #include <boost/container/flat_set.hpp>
34 
35 #include <algorithm>
36 #include <iterator>
37 #include <numeric>
38 #include <sstream>
39 
40 namespace ripple {
41 
43 template<class TInAmt, class TOutAmt>
45 {
46  bool success;
47  TInAmt in = beast::zero;
48  TOutAmt out = beast::zero;
49  boost::optional<PaymentSandbox> sandbox;
50  boost::container::flat_set<uint256> ofrsToRm;
51  // strand can be inactive if there is no more liquidity or too many offers have been consumed
52  bool inactive = false;
53 
55  StrandResult () = default;
56 
58  TInAmt const& in_,
59  TOutAmt const& out_,
60  PaymentSandbox&& sandbox_,
61  boost::container::flat_set<uint256> ofrsToRm_,
62  bool inactive_)
63  : success(true)
64  , in(in_)
65  , out(out_)
66  , sandbox(std::move(sandbox_))
67  , ofrsToRm(std::move(ofrsToRm_))
68  , inactive(inactive_)
69  {
70  }
71 
72  explicit
73  StrandResult(boost::container::flat_set<uint256> ofrsToRm_)
74  : success(false), ofrsToRm(std::move(ofrsToRm_))
75  {
76  }
77 };
78 
90 template<class TInAmt, class TOutAmt>
91 StrandResult <TInAmt, TOutAmt>
93  PaymentSandbox const& baseView,
94  Strand const& strand,
95  boost::optional<TInAmt> const& maxIn,
96  TOutAmt const& out,
98 {
99  using Result = StrandResult<TInAmt, TOutAmt>;
100  if (strand.empty ())
101  {
102  JLOG (j.warn()) << "Empty strand passed to Liquidity";
103  return {};
104  }
105 
106  boost::container::flat_set<uint256> ofrsToRm;
107 
108  if (isDirectXrpToXrp<TInAmt, TOutAmt> (strand))
109  {
110  return Result{std::move (ofrsToRm)};
111  }
112 
113  try
114  {
115  std::size_t const s = strand.size ();
116 
117  std::size_t limitingStep = strand.size ();
118  boost::optional<PaymentSandbox> sb (&baseView);
119  // The "all funds" view determines if an offer becomes unfunded or is
120  // found unfunded
121  // These are the account balances before the strand executes
122  boost::optional<PaymentSandbox> afView (&baseView);
124  {
125  EitherAmount stepOut (out);
126  for (auto i = s; i--;)
127  {
128  auto r = strand[i]->rev (*sb, *afView, ofrsToRm, stepOut);
129  if (strand[i]->isZero (r.second))
130  {
131  JLOG (j.trace()) << "Strand found dry in rev";
132  return Result{std::move (ofrsToRm)};
133  }
134 
135  if (i == 0 && maxIn && *maxIn < get<TInAmt> (r.first))
136  {
137  // limiting - exceeded maxIn
138  // Throw out previous results
139  sb.emplace (&baseView);
140  limitingStep = i;
141 
142  // re-execute the limiting step
143  r = strand[i]->fwd (
144  *sb, *afView, ofrsToRm, EitherAmount (*maxIn));
145  limitStepOut = r.second;
146 
147  if (strand[i]->isZero(r.second))
148  {
149  JLOG(j.trace()) << "First step found dry";
150  return Result{std::move(ofrsToRm)};
151  }
152  if (get<TInAmt> (r.first) != *maxIn)
153  {
154  // Something is very wrong
155  // throwing out the sandbox can only increase liquidity
156  // yet the limiting is still limiting
157  JLOG(j.fatal())
158  << "Re-executed limiting step failed. r.first: "
159  << to_string(get<TInAmt>(r.first))
160  << " maxIn: " << to_string(*maxIn);
161  assert(0);
162  return Result{std::move (ofrsToRm)};
163  }
164  }
165  else if (!strand[i]->equalOut (r.second, stepOut))
166  {
167  // limiting
168  // Throw out previous results
169  sb.emplace (&baseView);
170  afView.emplace (&baseView);
171  limitingStep = i;
172 
173  // re-execute the limiting step
174  stepOut = r.second;
175  r = strand[i]->rev (*sb, *afView, ofrsToRm, stepOut);
176  limitStepOut = r.second;
177 
178  if (strand[i]->isZero(r.second))
179  {
180  // A tiny input amount can cause this step to output zero.
181  // I.e. 10^-80 IOU into an IOU -> XRP offer.
182  JLOG(j.trace()) << "Limiting step found dry";
183  return Result{std::move(ofrsToRm)};
184  }
185  if (!strand[i]->equalOut (r.second, stepOut))
186  {
187  // Something is very wrong
188  // throwing out the sandbox can only increase liquidity
189  // yet the limiting is still limiting
190 #ifndef NDEBUG
191  JLOG(j.fatal())
192  << "Re-executed limiting step failed. r.second: "
193  << r.second << " stepOut: " << stepOut;
194 #else
195  JLOG (j.fatal()) << "Re-executed limiting step failed";
196 #endif
197  assert (0);
198  return Result{std::move (ofrsToRm)};
199  }
200  }
201 
202  // prev node needs to produce what this node wants to consume
203  stepOut = r.first;
204  }
205  }
206 
207  {
208  EitherAmount stepIn (limitStepOut);
209  for (auto i = limitingStep + 1; i < s; ++i)
210  {
211  auto const r = strand[i]->fwd(*sb, *afView, ofrsToRm, stepIn);
212  if (strand[i]->isZero(r.second))
213  {
214  // A tiny input amount can cause this step to output zero.
215  // I.e. 10^-80 IOU into an IOU -> XRP offer.
216  JLOG(j.trace()) << "Non-limiting step found dry";
217  return Result{std::move(ofrsToRm)};
218  }
219  if (!strand[i]->equalIn (r.first, stepIn))
220  {
221  // The limits should already have been found, so executing a strand forward
222  // from the limiting step should not find a new limit
223 #ifndef NDEBUG
224  JLOG(j.fatal())
225  << "Re-executed forward pass failed. r.first: "
226  << r.first << " stepIn: " << stepIn;
227 #else
228  JLOG (j.fatal()) << "Re-executed forward pass failed";
229 #endif
230  assert (0);
231  return Result{std::move (ofrsToRm)};
232  }
233  stepIn = r.second;
234  }
235  }
236 
237  auto const strandIn = *strand.front ()->cachedIn ();
238  auto const strandOut = *strand.back ()->cachedOut ();
239 
240 #ifndef NDEBUG
241  {
242  // Check that the strand will execute as intended
243  // Re-executing the strand will change the cached values
244  PaymentSandbox checkSB (&baseView);
245  PaymentSandbox checkAfView (&baseView);
246  EitherAmount stepIn (*strand[0]->cachedIn ());
247  for (auto i = 0; i < s; ++i)
248  {
249  bool valid;
250  std::tie (valid, stepIn) =
251  strand[i]->validFwd (checkSB, checkAfView, stepIn);
252  if (!valid)
253  {
254  JLOG (j.warn())
255  << "Strand re-execute check failed. Step: " << i;
256  break;
257  }
258  }
259  }
260 #endif
261 
262  bool const inactive = std::any_of(
263  strand.begin(),
264  strand.end(),
265  [](std::unique_ptr<Step> const& step) { return step->inactive(); });
266 
267  return Result(
268  get<TInAmt>(strandIn),
269  get<TOutAmt>(strandOut),
270  std::move(*sb),
271  std::move(ofrsToRm),
272  inactive);
273  }
274  catch (FlowException const&)
275  {
276  return Result{std::move (ofrsToRm)};
277  }
278 }
279 
281 template<class TInAmt, class TOutAmt>
282 struct FlowResult
283 {
284  TInAmt in = beast::zero;
285  TOutAmt out = beast::zero;
286  boost::optional<PaymentSandbox> sandbox;
287  boost::container::flat_set<uint256> removableOffers;
288  TER ter = temUNKNOWN;
289 
290  FlowResult () = default;
291 
292  FlowResult (TInAmt const& in_,
293  TOutAmt const& out_,
294  PaymentSandbox&& sandbox_,
295  boost::container::flat_set<uint256> ofrsToRm)
296  : in (in_)
297  , out (out_)
298  , sandbox (std::move (sandbox_))
299  , removableOffers(std::move (ofrsToRm))
300  , ter (tesSUCCESS)
301  {
302  }
303 
304  FlowResult (TER ter_, boost::container::flat_set<uint256> ofrsToRm)
305  : removableOffers(std::move (ofrsToRm))
306  , ter (ter_)
307  {
308  }
309 
310  FlowResult (TER ter_,
311  TInAmt const& in_,
312  TOutAmt const& out_,
313  boost::container::flat_set<uint256> ofrsToRm)
314  : in (in_)
315  , out (out_)
316  , removableOffers (std::move (ofrsToRm))
317  , ter (ter_)
318  {
319  }
320 };
322 
324 inline boost::optional<Quality>
325 qualityUpperBound(ReadView const& v, Strand const& strand)
326 {
327  Quality q{STAmount::uRateOne};
328  boost::optional<Quality> stepQ;
330  for (auto const& step : strand)
331  {
332  if (std::tie(stepQ, dir) = step->qualityUpperBound(v, dir); stepQ)
333  q = composed_quality(q, *stepQ);
334  else
335  return boost::none;
336  }
337  return q;
338 };
340 
342 /* Track the non-dry strands
343 
344  flow will search the non-dry strands (stored in `cur_`) for the best
345  available liquidity If flow doesn't use all the liquidity of a strand, that
346  strand is added to `next_`. The strands in `next_` are searched after the
347  current best liquidity is used.
348  */
349 class ActiveStrands
350 {
351 private:
352  // Strands to be explored for liquidity
354  // Strands that may be explored for liquidity on the next iteration
356 
357 public:
358  ActiveStrands (std::vector<Strand> const& strands)
359  {
360  cur_.reserve (strands.size ());
361  next_.reserve (strands.size ());
362  for (auto& strand : strands)
363  next_.push_back (&strand);
364  }
365 
366  // Start a new iteration in the search for liquidity
367  // Set the current strands to the strands in `next_`
368  void
369  activateNext ()
370  {
371  // Swap, don't move, so we keep the reserve in next_
372  cur_.clear ();
373  std::swap (cur_, next_);
374  }
375 
376  void
377  push (Strand const* s)
378  {
379  next_.push_back (s);
380  }
381 
382  auto begin ()
383  {
384  return cur_.begin ();
385  }
386 
387  auto end ()
388  {
389  return cur_.end ();
390  }
391 
392  auto begin () const
393  {
394  return cur_.begin ();
395  }
396 
397  auto end () const
398  {
399  return cur_.end ();
400  }
401 
402  auto size () const
403  {
404  return cur_.size ();
405  }
406 
407  void
408  removeIndex(std::size_t i)
409  {
410  if (i >= next_.size())
411  return;
412  next_.erase(next_.begin() + i);
413  }
414 };
416 
435 template <class TInAmt, class TOutAmt>
436 FlowResult<TInAmt, TOutAmt>
437 flow (PaymentSandbox const& baseView,
438  std::vector<Strand> const& strands,
439  TOutAmt const& outReq,
440  bool partialPayment,
441  bool offerCrossing,
442  boost::optional<Quality> const& limitQuality,
443  boost::optional<STAmount> const& sendMaxST,
444  beast::Journal j,
445  path::detail::FlowDebugInfo* flowDebugInfo=nullptr)
446 {
447  // Used to track the strand that offers the best quality (output/input ratio)
448  struct BestStrand
449  {
450  TInAmt in;
451  TOutAmt out;
452  PaymentSandbox sb;
453  Strand const& strand;
454  Quality quality;
455 
456  BestStrand (TInAmt const& in_,
457  TOutAmt const& out_,
458  PaymentSandbox&& sb_,
459  Strand const& strand_,
460  Quality const& quality_)
461  : in (in_)
462  , out (out_)
463  , sb (std::move (sb_))
464  , strand (strand_)
465  , quality (quality_)
466  {
467  }
468  };
469 
470  std::size_t const maxTries = 1000;
471  std::size_t curTry = 0;
472 
473  // There is a bug in gcc that incorrectly warns about using uninitialized
474  // values if `remainingIn` is initialized through a copy constructor. We can
475  // get similar warnings for `sendMax` if it is initialized in the most
476  // natural way. Using `make_optional`, allows us to work around this bug.
477  TInAmt const sendMaxInit =
478  sendMaxST ? toAmount<TInAmt>(*sendMaxST) : TInAmt{beast::zero};
479  boost::optional<TInAmt> const sendMax =
480  boost::make_optional(sendMaxST && sendMaxInit >= beast::zero, sendMaxInit);
481  boost::optional<TInAmt> remainingIn =
482  boost::make_optional(!!sendMax, sendMaxInit);
483  // boost::optional<TInAmt> remainingIn{sendMax};
484 
485  TOutAmt remainingOut (outReq);
486 
487  PaymentSandbox sb (&baseView);
488 
489  // non-dry strands
490  ActiveStrands activeStrands (strands);
491 
492  // Keeping a running sum of the amount in the order they are processed
493  // will not give the best precision. Keep a collection so they may be summed
494  // from smallest to largest
495  boost::container::flat_multiset<TInAmt> savedIns;
496  savedIns.reserve(maxTries);
497  boost::container::flat_multiset<TOutAmt> savedOuts;
498  savedOuts.reserve(maxTries);
499 
500  auto sum = [](auto const& col)
501  {
502  using TResult = std::decay_t<decltype (*col.begin ())>;
503  if (col.empty ())
504  return TResult{beast::zero};
505  return std::accumulate (col.begin () + 1, col.end (), *col.begin ());
506  };
507 
508  // These offers only need to be removed if the payment is not
509  // successful
510  boost::container::flat_set<uint256> ofrsToRmOnFail;
511 
512  while (remainingOut > beast::zero &&
513  (!remainingIn || *remainingIn > beast::zero))
514  {
515  ++curTry;
516  if (curTry >= maxTries)
517  {
518  return {telFAILED_PROCESSING, std::move(ofrsToRmOnFail)};
519  }
520 
521  activeStrands.activateNext();
522 
523  boost::container::flat_set<uint256> ofrsToRm;
524  boost::optional<BestStrand> best;
525  if (flowDebugInfo) flowDebugInfo->newLiquidityPass();
526  // Index of strand to mark as inactive (remove from the active list) if the
527  // liquidity is used. This is used for strands that consume too many offers
528  // Constructed as `false,0` to workaround a gcc warning about uninitialized variables
529  boost::optional<std::size_t> markInactiveOnUse{false, 0};
530  for (auto strand : activeStrands)
531  {
532  if (offerCrossing && limitQuality)
533  {
534  auto const strandQ = qualityUpperBound(sb, *strand);
535  if (!strandQ || *strandQ < *limitQuality)
536  continue;
537  }
538  auto f = flow<TInAmt, TOutAmt> (
539  sb, *strand, remainingIn, remainingOut, j);
540 
541  // rm bad offers even if the strand fails
542  SetUnion(ofrsToRm, f.ofrsToRm);
543 
544  if (!f.success || f.out == beast::zero)
545  continue;
546 
547  if (flowDebugInfo)
548  flowDebugInfo->pushLiquiditySrc(EitherAmount(f.in), EitherAmount(f.out));
549 
550  assert (f.out <= remainingOut && f.sandbox &&
551  (!remainingIn || f.in <= *remainingIn));
552 
553  Quality const q (f.out, f.in);
554 
555  JLOG (j.trace())
556  << "New flow iter (iter, in, out): "
557  << curTry-1 << " "
558  << to_string(f.in) << " "
559  << to_string(f.out);
560 
561  if (limitQuality && q < *limitQuality)
562  {
563  JLOG (j.trace())
564  << "Path rejected by limitQuality"
565  << " limit: " << *limitQuality
566  << " path q: " << q;
567  continue;
568  }
569 
570  activeStrands.push (strand);
571 
572  if (!best || best->quality < q ||
573  (best->quality == q && best->out < f.out))
574  {
575  // If this strand is inactive (because it consumed too many
576  // offers) and ends up having the best quality, remove it from
577  // the activeStrands. If it doesn't end up having the best
578  // quality, keep it active.
579 
580  if (f.inactive)
581  markInactiveOnUse = activeStrands.size() - 1;
582  else
583  markInactiveOnUse.reset();
584 
585  best.emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
586  }
587  }
588 
589  bool const shouldBreak = !best;
590 
591  if (best)
592  {
593  if (markInactiveOnUse)
594  {
595  activeStrands.removeIndex(*markInactiveOnUse);
596  markInactiveOnUse.reset();
597  }
598  savedIns.insert (best->in);
599  savedOuts.insert (best->out);
600  remainingOut = outReq - sum (savedOuts);
601  if (sendMax)
602  remainingIn = *sendMax - sum (savedIns);
603 
604  if (flowDebugInfo)
605  flowDebugInfo->pushPass (EitherAmount (best->in),
606  EitherAmount (best->out), activeStrands.size ());
607 
608  JLOG (j.trace())
609  << "Best path: in: " << to_string (best->in)
610  << " out: " << to_string (best->out)
611  << " remainingOut: " << to_string (remainingOut);
612 
613  best->sb.apply (sb);
614  }
615  else
616  {
617  JLOG (j.trace()) << "All strands dry.";
618  }
619 
620  best.reset (); // view in best must be destroyed before modifying base
621  // view
622  if (!ofrsToRm.empty ())
623  {
624  SetUnion(ofrsToRmOnFail, ofrsToRm);
625  for (auto const& o : ofrsToRm)
626  {
627  if (auto ok = sb.peek (keylet::offer (o)))
628  offerDelete (sb, ok, j);
629  }
630  }
631 
632  if (shouldBreak)
633  break;
634  }
635 
636  auto const actualOut = sum (savedOuts);
637  auto const actualIn = sum (savedIns);
638 
639  JLOG (j.trace())
640  << "Total flow: in: " << to_string (actualIn)
641  << " out: " << to_string (actualOut);
642 
643  if (actualOut != outReq)
644  {
645  if (actualOut > outReq)
646  {
647  assert (0);
648  return {tefEXCEPTION, std::move(ofrsToRmOnFail)};
649  }
650  if (!partialPayment)
651  {
652  // If we're offerCrossing a !partialPayment, then we're
653  // handling tfFillOrKill. That case is handled below; not here.
654  if (!offerCrossing)
655  return {tecPATH_PARTIAL,
656  actualIn, actualOut, std::move(ofrsToRmOnFail)};
657  }
658  else if (actualOut == beast::zero)
659  {
660  return {tecPATH_DRY, std::move(ofrsToRmOnFail)};
661  }
662  }
663  if (offerCrossing && !partialPayment)
664  {
665  // If we're offer crossing and partialPayment is *not* true, then
666  // we're handling a FillOrKill offer. In this case remainingIn must
667  // be zero (all funds must be consumed) or else we kill the offer.
668  assert (remainingIn);
669  if (remainingIn && *remainingIn != beast::zero)
670  return {tecPATH_PARTIAL,
671  actualIn, actualOut, std::move(ofrsToRmOnFail)};
672  }
673 
674  return {actualIn, actualOut, std::move (sb), std::move(ofrsToRmOnFail)};
675 }
676 
677 } // ripple
678 
679 #endif
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:312
sstream
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:287
ripple::composed_quality
Quality composed_quality(Quality const &lhs, Quality const &rhs)
Definition: Quality.cpp:103
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:473
ripple::StrandResult
Result of flow() execution of a single Strand.
Definition: StrandFlow.h:44
ripple::DebtDirection
DebtDirection
Definition: Steps.h:37
ripple::PaymentSandbox
A wrapper which makes credits unavailable to balances.
Definition: PaymentSandbox.h:110
ripple::path::detail::FlowDebugInfo
Definition: FlowDebugInfo.h:41
std::vector::reserve
T reserve(T... args)
std::vector
STL class.
std::vector::size
T size(T... args)
ripple::StrandResult::StrandResult
StrandResult(boost::container::flat_set< uint256 > ofrsToRm_)
Definition: StrandFlow.h:73
iterator
beast::Journal::warn
Stream warn() const
Definition: Journal.h:302
ripple::StrandResult::out
TOutAmt out
Currency amount out.
Definition: StrandFlow.h:48
ripple::QualityDirection::in
@ in
std::any_of
T any_of(T... args)
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:41
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:50
ripple::StrandResult::in
TInAmt in
Currency amount in.
Definition: StrandFlow.h:47
ripple::telFAILED_PROCESSING
@ telFAILED_PROCESSING
Definition: TER.h:57
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:871
ripple::TER
TERSubset< CanCvtToTER > TER
Definition: TER.h:477
std::vector::erase
T erase(T... args)
std::accumulate
T accumulate(T... args)
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:60
ripple::temUNKNOWN
@ temUNKNOWN
Definition: TER.h:122
ripple::tecPATH_PARTIAL
@ tecPATH_PARTIAL
Definition: TER.h:247
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:46
std::begin
T begin(T... args)
std
STL namespace.
ripple::EitherAmount
Definition: AmountSpec.h:60
ripple::tecPATH_DRY
@ tecPATH_DRY
Definition: TER.h:259
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:34
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:138
std::end
T end(T... args)
ripple::StrandResult::StrandResult
StrandResult()=default
Strand result constructor.
ripple::tefEXCEPTION
@ tefEXCEPTION
Definition: TER.h:152
numeric
ripple::keylet::offer
static const offer_t offer
Definition: Indexes.h:191
std::unique_ptr
STL class.
ripple::sum
static auto sum(TCollection const &col)
Definition: BookStep.cpp:651
ripple::StrandResult::inactive
bool inactive
Strand should not considered as a further source of liquidity (dry)
Definition: StrandFlow.h:52
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:219
ripple::StrandResult::sandbox
boost::optional< PaymentSandbox > sandbox
Resulting Sandbox state.
Definition: StrandFlow.h:49
ripple::STAmount::uRateOne
static const std::uint64_t uRateOne
Definition: STAmount.h:73
ripple::StrandResult::StrandResult
StrandResult(TInAmt const &in_, TOutAmt const &out_, PaymentSandbox &&sandbox_, boost::container::flat_set< uint256 > ofrsToRm_, bool inactive_)
Definition: StrandFlow.h:57