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