20 #ifndef RIPPLE_APP_PATHS_IMPL_STRANDFLOW_H_INCLUDED
21 #define RIPPLE_APP_PATHS_IMPL_STRANDFLOW_H_INCLUDED
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>
33 #include <boost/container/flat_set.hpp>
43 template<
class TInAmt,
class TOutAmt>
47 TInAmt
in = beast::zero;
48 TOutAmt
out = beast::zero;
61 boost::container::flat_set<uint256> ofrsToRm_,
90 template<
class TInAmt,
class TOutAmt>
91 StrandResult <TInAmt, TOutAmt>
95 boost::optional<TInAmt>
const& maxIn,
102 JLOG (j.
warn()) <<
"Empty strand passed to Liquidity";
106 boost::container::flat_set<uint256> ofrsToRm;
108 if (isDirectXrpToXrp<TInAmt, TOutAmt> (strand))
110 return Result{std::move (ofrsToRm)};
118 boost::optional<PaymentSandbox> sb (&baseView);
122 boost::optional<PaymentSandbox> afView (&baseView);
126 for (
auto i = s; i--;)
128 auto r = strand[i]->rev (*sb, *afView, ofrsToRm, stepOut);
129 if (strand[i]->isZero (r.second))
131 JLOG (j.
trace()) <<
"Strand found dry in rev";
132 return Result{std::move (ofrsToRm)};
135 if (i == 0 && maxIn && *maxIn < get<TInAmt> (r.first))
139 sb.emplace (&baseView);
147 if (strand[i]->isZero(r.second))
149 JLOG(j.
trace()) <<
"First step found dry";
150 return Result{std::move(ofrsToRm)};
152 if (get<TInAmt> (r.first) != *maxIn)
158 <<
"Re-executed limiting step failed. r.first: "
162 return Result{std::move (ofrsToRm)};
165 else if (!strand[i]->equalOut (r.second, stepOut))
169 sb.emplace (&baseView);
170 afView.emplace (&baseView);
175 r = strand[i]->rev (*sb, *afView, ofrsToRm, stepOut);
178 if (strand[i]->isZero(r.second))
182 JLOG(j.
trace()) <<
"Limiting step found dry";
183 return Result{std::move(ofrsToRm)};
185 if (!strand[i]->equalOut (r.second, stepOut))
192 <<
"Re-executed limiting step failed. r.second: "
193 << r.second <<
" stepOut: " << stepOut;
195 JLOG (j.
fatal()) <<
"Re-executed limiting step failed";
198 return Result{std::move (ofrsToRm)};
209 for (
auto i = limitingStep + 1; i < s; ++i)
211 auto const r = strand[i]->fwd(*sb, *afView, ofrsToRm, stepIn);
212 if (strand[i]->isZero(r.second))
216 JLOG(j.
trace()) <<
"Non-limiting step found dry";
217 return Result{std::move(ofrsToRm)};
219 if (!strand[i]->equalIn (r.first, stepIn))
225 <<
"Re-executed forward pass failed. r.first: "
226 << r.first <<
" stepIn: " << stepIn;
228 JLOG (j.
fatal()) <<
"Re-executed forward pass failed";
231 return Result{std::move (ofrsToRm)};
237 auto const strandIn = *strand.front ()->cachedIn ();
238 auto const strandOut = *strand.back ()->cachedOut ();
247 for (
auto i = 0; i < s; ++i)
251 strand[i]->validFwd (checkSB, checkAfView, stepIn);
255 <<
"Strand re-execute check failed. Step: " << i;
268 get<TInAmt>(strandIn),
269 get<TOutAmt>(strandOut),
274 catch (FlowException
const&)
276 return Result{std::move (ofrsToRm)};
281 template<
class TInAmt,
class TOutAmt>
284 TInAmt in = beast::zero;
285 TOutAmt out = beast::zero;
286 boost::optional<PaymentSandbox> sandbox;
287 boost::container::flat_set<uint256> removableOffers;
290 FlowResult () =
default;
292 FlowResult (TInAmt
const& in_,
294 PaymentSandbox&& sandbox_,
295 boost::container::flat_set<uint256> ofrsToRm)
298 , sandbox (
std::move (sandbox_))
299 , removableOffers(
std::move (ofrsToRm))
304 FlowResult (
TER ter_, boost::container::flat_set<uint256> ofrsToRm)
305 : removableOffers(
std::move (ofrsToRm))
310 FlowResult (
TER ter_,
313 boost::container::flat_set<uint256> ofrsToRm)
316 , removableOffers (
std::move (ofrsToRm))
324 inline boost::optional<Quality>
325 qualityUpperBound(ReadView
const& v, Strand
const& strand)
328 boost::optional<Quality> stepQ;
330 for (
auto const& step : strand)
332 if (
std::tie(stepQ, dir) = step->qualityUpperBound(v, dir); stepQ)
362 for (
auto& strand : strands)
377 push (Strand
const* s)
384 return cur_.
begin ();
394 return cur_.
begin ();
410 if (i >= next_.
size())
435 template <
class TInAmt,
class TOutAmt>
436 FlowResult<TInAmt, TOutAmt>
439 TOutAmt
const& outReq,
442 boost::optional<Quality>
const& limitQuality,
443 boost::optional<STAmount>
const& sendMaxST,
453 Strand
const& strand;
456 BestStrand (TInAmt
const& in_,
459 Strand
const& strand_,
460 Quality
const& quality_)
463 , sb (std::move (sb_))
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);
485 TOutAmt remainingOut (outReq);
490 ActiveStrands activeStrands (strands);
495 boost::container::flat_multiset<TInAmt> savedIns;
496 savedIns.reserve(maxTries);
497 boost::container::flat_multiset<TOutAmt> savedOuts;
498 savedOuts.reserve(maxTries);
500 auto sum = [](
auto const& col)
504 return TResult{beast::zero};
510 boost::container::flat_set<uint256> ofrsToRmOnFail;
512 while (remainingOut > beast::zero &&
513 (!remainingIn || *remainingIn > beast::zero))
516 if (curTry >= maxTries)
521 activeStrands.activateNext();
523 boost::container::flat_set<uint256> ofrsToRm;
524 boost::optional<BestStrand> best;
525 if (flowDebugInfo) flowDebugInfo->newLiquidityPass();
529 boost::optional<std::size_t> markInactiveOnUse{
false, 0};
530 for (
auto strand : activeStrands)
532 if (offerCrossing && limitQuality)
534 auto const strandQ = qualityUpperBound(sb, *strand);
535 if (!strandQ || *strandQ < *limitQuality)
538 auto f = flow<TInAmt, TOutAmt> (
539 sb, *strand, remainingIn, remainingOut, j);
544 if (!f.success || f.out == beast::zero)
550 assert (f.out <= remainingOut && f.sandbox &&
551 (!remainingIn || f.in <= *remainingIn));
553 Quality
const q (f.out, f.in);
556 <<
"New flow iter (iter, in, out): "
561 if (limitQuality && q < *limitQuality)
564 <<
"Path rejected by limitQuality"
565 <<
" limit: " << *limitQuality
570 activeStrands.push (strand);
572 if (!best || best->quality < q ||
573 (best->quality == q && best->out < f.out))
581 markInactiveOnUse = activeStrands.size() - 1;
583 markInactiveOnUse.reset();
585 best.emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
589 bool const shouldBreak = !best;
593 if (markInactiveOnUse)
595 activeStrands.removeIndex(*markInactiveOnUse);
596 markInactiveOnUse.reset();
598 savedIns.insert (best->in);
599 savedOuts.insert (best->out);
600 remainingOut = outReq -
sum (savedOuts);
602 remainingIn = *sendMax -
sum (savedIns);
609 <<
"Best path: in: " <<
to_string (best->in)
611 <<
" remainingOut: " <<
to_string (remainingOut);
617 JLOG (j.
trace()) <<
"All strands dry.";
622 if (!ofrsToRm.empty ())
625 for (
auto const& o : ofrsToRm)
636 auto const actualOut =
sum (savedOuts);
637 auto const actualIn =
sum (savedIns);
640 <<
"Total flow: in: " <<
to_string (actualIn)
643 if (actualOut != outReq)
645 if (actualOut > outReq)
656 actualIn, actualOut, std::move(ofrsToRmOnFail)};
658 else if (actualOut == beast::zero)
663 if (offerCrossing && !partialPayment)
668 assert (remainingIn);
669 if (remainingIn && *remainingIn != beast::zero)
671 actualIn, actualOut, std::move(ofrsToRmOnFail)};
674 return {actualIn, actualOut, std::move (sb), std::move(ofrsToRmOnFail)};