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/Log.h>
31 #include <ripple/basics/XRPAmount.h>
33 #include <boost/container/flat_set.hpp>
43 template <
class TInAmt,
class TOutAmt>
47 TInAmt
in = beast::zero;
48 TOutAmt
out = beast::zero;
63 boost::container::flat_set<uint256> ofrsToRm_,
74 explicit StrandResult(boost::container::flat_set<uint256> ofrsToRm_)
91 template <
class TInAmt,
class TOutAmt>
92 StrandResult<TInAmt, TOutAmt>
96 boost::optional<TInAmt>
const& maxIn,
103 JLOG(j.
warn()) <<
"Empty strand passed to Liquidity";
107 boost::container::flat_set<uint256> ofrsToRm;
109 if (isDirectXrpToXrp<TInAmt, TOutAmt>(strand))
111 return Result{std::move(ofrsToRm)};
119 boost::optional<PaymentSandbox> sb(&baseView);
123 boost::optional<PaymentSandbox> afView(&baseView);
127 for (
auto i = s; i--;)
129 auto r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
130 if (strand[i]->isZero(r.second))
132 JLOG(j.
trace()) <<
"Strand found dry in rev";
133 return Result{std::move(ofrsToRm)};
136 if (i == 0 && maxIn && *maxIn < get<TInAmt>(r.first))
140 sb.emplace(&baseView);
148 if (strand[i]->isZero(r.second))
150 JLOG(j.
trace()) <<
"First step found dry";
151 return Result{std::move(ofrsToRm)};
153 if (get<TInAmt>(r.first) != *maxIn)
159 <<
"Re-executed limiting step failed. r.first: "
163 return Result{std::move(ofrsToRm)};
166 else if (!strand[i]->equalOut(r.second, stepOut))
170 sb.emplace(&baseView);
171 afView.emplace(&baseView);
176 r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
179 if (strand[i]->isZero(r.second))
183 JLOG(j.
trace()) <<
"Limiting step found dry";
184 return Result{std::move(ofrsToRm)};
186 if (!strand[i]->equalOut(r.second, stepOut))
193 <<
"Re-executed limiting step failed. r.second: "
194 << r.second <<
" stepOut: " << stepOut;
196 JLOG(j.
fatal()) <<
"Re-executed limiting step failed";
199 return Result{std::move(ofrsToRm)};
210 for (
auto i = limitingStep + 1; i < s; ++i)
212 auto const r = strand[i]->fwd(*sb, *afView, ofrsToRm, stepIn);
213 if (strand[i]->isZero(r.second))
217 JLOG(j.
trace()) <<
"Non-limiting step found dry";
218 return Result{std::move(ofrsToRm)};
220 if (!strand[i]->equalIn(r.first, stepIn))
227 <<
"Re-executed forward pass failed. r.first: "
228 << r.first <<
" stepIn: " << stepIn;
230 JLOG(j.
fatal()) <<
"Re-executed forward pass failed";
233 return Result{std::move(ofrsToRm)};
239 auto const strandIn = *strand.front()->cachedIn();
240 auto const strandOut = *strand.back()->cachedOut();
249 for (
auto i = 0; i < s; ++i)
253 strand[i]->validFwd(checkSB, checkAfView, stepIn);
257 <<
"Strand re-execute check failed. Step: " << i;
270 get<TInAmt>(strandIn),
271 get<TOutAmt>(strandOut),
276 catch (FlowException
const&)
278 return Result{std::move(ofrsToRm)};
283 template <
class TInAmt,
class TOutAmt>
286 TInAmt in = beast::zero;
287 TOutAmt out = beast::zero;
288 boost::optional<PaymentSandbox> sandbox;
289 boost::container::flat_set<uint256> removableOffers;
292 FlowResult() =
default;
297 PaymentSandbox&& sandbox_,
298 boost::container::flat_set<uint256> ofrsToRm)
301 , sandbox(
std::move(sandbox_))
302 , removableOffers(
std::move(ofrsToRm))
307 FlowResult(
TER ter_, boost::container::flat_set<uint256> ofrsToRm)
308 : removableOffers(
std::move(ofrsToRm)), ter(ter_)
316 boost::container::flat_set<uint256> ofrsToRm)
317 :
in(in_),
out(out_), removableOffers(
std::move(ofrsToRm)), ter(ter_)
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)
415 if (i >= next_.
size())
441 template <
class TInAmt,
class TOutAmt>
442 FlowResult<TInAmt, TOutAmt>
446 TOutAmt
const& outReq,
449 boost::optional<Quality>
const& limitQuality,
450 boost::optional<STAmount>
const& sendMaxST,
461 Strand
const& strand;
468 Strand
const& strand_,
469 Quality
const& quality_)
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);
494 TOutAmt remainingOut(outReq);
499 ActiveStrands activeStrands(strands);
504 boost::container::flat_multiset<TInAmt> savedIns;
505 savedIns.reserve(maxTries);
506 boost::container::flat_multiset<TOutAmt> savedOuts;
507 savedOuts.reserve(maxTries);
509 auto sum = [](
auto const& col) {
512 return TResult{beast::zero};
518 boost::container::flat_set<uint256> ofrsToRmOnFail;
520 while (remainingOut > beast::zero &&
521 (!remainingIn || *remainingIn > beast::zero))
524 if (curTry >= maxTries)
529 activeStrands.activateNext();
531 boost::container::flat_set<uint256> ofrsToRm;
532 boost::optional<BestStrand> best;
534 flowDebugInfo->newLiquidityPass();
539 boost::optional<std::size_t> markInactiveOnUse{
false, 0};
540 for (
auto strand : activeStrands)
542 if (offerCrossing && limitQuality)
544 auto const strandQ = qualityUpperBound(sb, *strand);
545 if (!strandQ || *strandQ < *limitQuality)
548 auto f = flow<TInAmt, TOutAmt>(
549 sb, *strand, remainingIn, remainingOut, j);
554 if (!f.success || f.out == beast::zero)
558 flowDebugInfo->pushLiquiditySrc(
562 f.out <= remainingOut && f.sandbox &&
563 (!remainingIn || f.in <= *remainingIn));
565 Quality
const q(f.out, f.in);
568 <<
"New flow iter (iter, in, out): " << curTry - 1 <<
" "
571 if (limitQuality && q < *limitQuality)
574 <<
"Path rejected by limitQuality"
575 <<
" limit: " << *limitQuality <<
" path q: " << q;
579 activeStrands.push(strand);
581 if (!best || best->quality < q ||
582 (best->quality == q && best->out < f.out))
590 markInactiveOnUse = activeStrands.size() - 1;
592 markInactiveOnUse.reset();
594 best.emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
598 bool const shouldBreak = !best;
602 if (markInactiveOnUse)
604 activeStrands.removeIndex(*markInactiveOnUse);
605 markInactiveOnUse.reset();
607 savedIns.insert(best->in);
608 savedOuts.insert(best->out);
609 remainingOut = outReq -
sum(savedOuts);
611 remainingIn = *sendMax -
sum(savedIns);
614 flowDebugInfo->pushPass(
617 activeStrands.size());
621 <<
" remainingOut: " <<
to_string(remainingOut);
627 JLOG(j.
trace()) <<
"All strands dry.";
632 if (!ofrsToRm.empty())
635 for (
auto const& o : ofrsToRm)
646 auto const actualOut =
sum(savedOuts);
647 auto const actualIn =
sum(savedIns);
652 if (actualOut != outReq)
654 if (actualOut > outReq)
668 std::move(ofrsToRmOnFail)};
670 else if (actualOut == beast::zero)
675 if (offerCrossing && !partialPayment)
681 if (remainingIn && *remainingIn != beast::zero)
686 std::move(ofrsToRmOnFail)};
689 return {actualIn, actualOut, std::move(sb), std::move(ofrsToRmOnFail)};