20#ifndef RIPPLE_APP_PATHS_IMPL_STRANDFLOW_H_INCLUDED
21#define RIPPLE_APP_PATHS_IMPL_STRANDFLOW_H_INCLUDED
23#include <xrpld/app/misc/AMMHelpers.h>
24#include <xrpld/app/paths/AMMContext.h>
25#include <xrpld/app/paths/Credit.h>
26#include <xrpld/app/paths/Flow.h>
27#include <xrpld/app/paths/detail/AmountSpec.h>
28#include <xrpld/app/paths/detail/FlatSets.h>
29#include <xrpld/app/paths/detail/FlowDebugInfo.h>
30#include <xrpld/app/paths/detail/Steps.h>
32#include <xrpl/basics/Log.h>
33#include <xrpl/protocol/Feature.h>
34#include <xrpl/protocol/IOUAmount.h>
35#include <xrpl/protocol/XRPAmount.h>
37#include <boost/container/flat_set.hpp>
46template <
class TInAmt,
class TOutAmt>
50 TInAmt
in = beast::zero;
51 TOutAmt
out = beast::zero;
70 boost::container::flat_set<uint256> ofrsToRm_,
84 boost::container::flat_set<uint256> ofrsToRm_)
103template <
class TInAmt,
class TOutAmt>
104StrandResult<TInAmt, TOutAmt>
107 Strand
const& strand,
115 JLOG(j.
warn()) <<
"Empty strand passed to Liquidity";
119 boost::container::flat_set<uint256> ofrsToRm;
121 if (isDirectXrpToXrp<TInAmt, TOutAmt>(strand))
123 return Result{strand, std::move(ofrsToRm)};
139 for (
auto i = s; i--;)
141 auto r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
142 if (strand[i]->isZero(r.second))
144 JLOG(j.
trace()) <<
"Strand found dry in rev";
145 return Result{strand, std::move(ofrsToRm)};
148 if (i == 0 && maxIn && *maxIn < get<TInAmt>(r.first))
160 if (strand[i]->isZero(r.second))
162 JLOG(j.
trace()) <<
"First step found dry";
163 return Result{strand, std::move(ofrsToRm)};
165 if (get<TInAmt>(r.first) != *maxIn)
171 <<
"Re-executed limiting step failed. r.first: "
175 "ripple::flow : first step re-executing the "
176 "limiting step failed");
177 return Result{strand, std::move(ofrsToRm)};
180 else if (!strand[i]->equalOut(r.second, stepOut))
190 r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
193 if (strand[i]->isZero(r.second))
197 JLOG(j.
trace()) <<
"Limiting step found dry";
198 return Result{strand, std::move(ofrsToRm)};
200 if (!strand[i]->equalOut(r.second, stepOut))
207 <<
"Re-executed limiting step failed. r.second: "
208 << r.second <<
" stepOut: " << stepOut;
210 JLOG(j.
fatal()) <<
"Re-executed limiting step failed";
213 "ripple::flow : limiting step re-executing the "
214 "limiting step failed");
215 return Result{strand, std::move(ofrsToRm)};
226 for (
auto i = limitingStep + 1; i < s; ++i)
228 auto const r = strand[i]->fwd(*sb, *afView, ofrsToRm, stepIn);
229 if (strand[i]->isZero(r.second))
233 JLOG(j.
trace()) <<
"Non-limiting step found dry";
234 return Result{strand, std::move(ofrsToRm)};
236 if (!strand[i]->equalIn(r.first, stepIn))
243 <<
"Re-executed forward pass failed. r.first: "
244 << r.first <<
" stepIn: " << stepIn;
246 JLOG(j.
fatal()) <<
"Re-executed forward pass failed";
249 "ripple::flow : non-limiting step re-executing the "
250 "forward pass failed");
251 return Result{strand, std::move(ofrsToRm)};
257 auto const strandIn = *strand.front()->cachedIn();
258 auto const strandOut = *strand.back()->cachedOut();
267 for (
auto i = 0; i < s; ++i)
271 strand[i]->validFwd(checkSB, checkAfView, stepIn);
275 <<
"Strand re-execute check failed. Step: " << i;
289 get<TInAmt>(strandIn),
290 get<TOutAmt>(strandOut),
295 catch (FlowException
const&)
297 return Result{strand, std::move(ofrsToRm)};
302template <
class TInAmt,
class TOutAmt>
305 TInAmt in = beast::zero;
306 TOutAmt out = beast::zero;
308 boost::container::flat_set<uint256> removableOffers;
309 TER ter = temUNKNOWN;
311 FlowResult() =
default;
316 PaymentSandbox&& sandbox_,
317 boost::container::flat_set<uint256> ofrsToRm)
320 , sandbox(
std::move(sandbox_))
321 , removableOffers(
std::move(ofrsToRm))
326 FlowResult(TER ter_, boost::container::flat_set<uint256> ofrsToRm)
327 : removableOffers(
std::
move(ofrsToRm)), ter(ter_)
335 boost::container::flat_set<uint256> ofrsToRm)
336 :
in(in_),
out(out_), removableOffers(
std::
move(ofrsToRm)), ter(ter_)
344qualityUpperBound(ReadView
const& v, Strand
const& strand)
349 for (
auto const& step : strand)
351 if (
std::tie(stepQ, dir) = step->qualityUpperBound(v, dir); stepQ)
369template <
typename TOutAmt>
373 Strand
const& strand,
374 TOutAmt
const& remainingOut,
375 Quality
const& limitQuality)
380 for (
auto const& step : strand)
382 if (
std::tie(stepQualityFunc, dir) = step->getQualityFunc(v, dir);
386 qf = stepQualityFunc;
388 qf->combine(*stepQualityFunc);
395 if (!qf || qf->isConst())
398 auto const out = [&]() {
399 if (
auto const out = qf->outFromAvgQ(limitQuality); !
out)
402 return XRPAmount{*
out};
404 return IOUAmount{*
out};
407 remainingOut.issue(),
out->mantissa(),
out->exponent()};
437 for (
auto& strand : strands)
438 next_.push_back(&strand);
449 if (v.rules().enabled(featureFlowSortStrands) && !next_.
empty())
453 if (next_.
size() > 1)
455 for (Strand
const* strand : next_)
462 if (
auto const qual = qualityUpperBound(v, *strand))
464 if (limitQuality && *qual < *limitQuality)
482 [](
auto const& lhs,
auto const& rhs) {
484 return std::get<Quality>(lhs) > std::get<Quality>(rhs);
488 for (
auto const& sq : strandQuals)
500 if (i >= cur_.
size())
502 UNREACHABLE(
"ripple::ActiveStrands::get : input out of range");
509 push(Strand
const* s)
516 pushRemainingCurToNext(
size_t i)
518 if (i >= cur_.
size())
532 if (i >= next_.
size())
559template <
class TInAmt,
class TOutAmt>
560FlowResult<TInAmt, TOutAmt>
564 TOutAmt
const& outReq,
580 Strand
const& strand;
587 Strand
const& strand_,
588 Quality
const& quality_)
607 TInAmt
const sendMaxInit =
608 sendMaxST ? toAmount<TInAmt>(*sendMaxST) : TInAmt{beast::zero};
610 (sendMaxST && sendMaxInit >= beast::zero)
617 TOutAmt remainingOut(outReq);
622 ActiveStrands activeStrands(strands);
627 boost::container::flat_multiset<TInAmt> savedIns;
628 savedIns.reserve(maxTries);
629 boost::container::flat_multiset<TOutAmt> savedOuts;
630 savedOuts.reserve(maxTries);
632 auto sum = [](
auto const& col) {
635 return TResult{beast::zero};
641 boost::container::flat_set<uint256> ofrsToRmOnFail;
643 while (remainingOut > beast::zero &&
644 (!remainingIn || *remainingIn > beast::zero))
647 if (curTry >= maxTries)
652 activeStrands.activateNext(sb, limitQuality);
657 auto const limitRemainingOut = [&]() {
658 if (activeStrands.size() == 1 && limitQuality)
659 if (
auto const strand = activeStrands.get(0))
660 return limitOut(sb, *strand, remainingOut, *limitQuality);
663 auto const adjustedRemOut = limitRemainingOut != remainingOut;
665 boost::container::flat_set<uint256> ofrsToRm;
668 flowDebugInfo->newLiquidityPass();
674 for (
size_t strandIndex = 0, sie = activeStrands.size();
678 Strand
const* strand = activeStrands.get(strandIndex);
688 if (offerCrossing && limitQuality)
690 auto const strandQ = qualityUpperBound(sb, *strand);
691 if (!strandQ || *strandQ < *limitQuality)
694 auto f = flow<TInAmt, TOutAmt>(
695 sb, *strand, remainingIn, limitRemainingOut, j);
700 offersConsidered += f.ofrsUsed;
702 if (!f.success || f.out == beast::zero)
706 flowDebugInfo->pushLiquiditySrc(
710 f.out <= remainingOut && f.sandbox &&
711 (!remainingIn || f.in <= *remainingIn),
712 "ripple::flow : remaining constraints");
714 Quality
const q(f.out, f.in);
717 <<
"New flow iter (iter, in, out): " << curTry - 1 <<
" "
723 if (limitQuality && q < *limitQuality &&
728 <<
"Path rejected by limitQuality"
729 <<
" limit: " << *limitQuality <<
" path q: " << q;
735 XRPL_ASSERT(!best,
"ripple::flow : best is unset");
737 activeStrands.push(strand);
738 best.
emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
739 activeStrands.pushRemainingCurToNext(strandIndex + 1);
743 activeStrands.push(strand);
745 if (!best || best->quality < q ||
746 (best->quality == q && best->out < f.out))
757 markInactiveOnUse = activeStrands.size() - 1;
761 markInactiveOnUse.
reset();
764 best.
emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
768 bool const shouldBreak = [&] {
770 return !best || offersConsidered >= maxOffersToConsider;
776 if (markInactiveOnUse)
778 activeStrands.removeIndex(*markInactiveOnUse);
779 markInactiveOnUse.
reset();
781 savedIns.insert(best->in);
782 savedOuts.insert(best->out);
783 remainingOut = outReq -
sum(savedOuts);
785 remainingIn = *sendMax -
sum(savedIns);
788 flowDebugInfo->pushPass(
791 activeStrands.size());
795 <<
" remainingOut: " <<
to_string(remainingOut);
802 JLOG(j.
trace()) <<
"All strands dry.";
807 if (!ofrsToRm.empty())
810 for (
auto const& o : ofrsToRm)
821 auto const actualOut =
sum(savedOuts);
822 auto const actualIn =
sum(savedIns);
838 bool const fillOrKillEnabled = baseView.
rules().
enabled(fixFillOrKill);
840 if (actualOut != outReq)
842 if (actualOut > outReq)
862 if (!offerCrossing ||
868 std::move(ofrsToRmOnFail)};
870 else if (actualOut == beast::zero)
886 XRPL_ASSERT(remainingIn,
"ripple::flow : nonzero remainingIn");
887 if (remainingIn && *remainingIn != beast::zero)
892 std::move(ofrsToRmOnFail)};
895 return {actualIn, actualOut, std::move(sb), std::move(ofrsToRmOnFail)};
A generic endpoint for log messages.
Stream trace() const
Severity stream access functions.
Maintains AMM info per overall payment engine execution and individual iteration.
void setMultiPath(bool fs)
void clear()
Strand execution may fail.
A wrapper which makes credits unavailable to balances.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
static std::uint64_t const uRateOne
Rules const & rules() const override
Returns the tx processing rules.
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
T make_optional(T... args)
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
static auto sum(TCollection const &col)
boost::outcome_v2::result< T, std::error_code > Result
StrandResult< TInAmt, TOutAmt > flow(PaymentSandbox const &baseView, Strand const &strand, std::optional< TInAmt > const &maxIn, TOutAmt const &out, beast::Journal j)
Request out amount from a strand.
Quality composed_quality(Quality const &lhs, Quality const &rhs)
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.
static void limitStepOut(Offer const &offer, TAmounts< TIn, TOut > &ofrAmt, TAmounts< TIn, TOut > &stpAmt, TOut &ownerGives, std::uint32_t transferRateIn, std::uint32_t transferRateOut, TOut const &limit)
std::string to_string(base_uint< Bits, Tag > const &a)
T get(Section const §ion, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
bool withinRelativeDistance(Quality const &calcQuality, Quality const &reqQuality, Number const &dist)
Check if the relative distance between the qualities is within the requested distance.
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Result of flow() execution of a single Strand.
bool inactive
Strand should not considered as a further source of liquidity (dry)
bool success
Strand succeeded.
std::optional< PaymentSandbox > sandbox
Resulting Sandbox state.
TOutAmt out
Currency amount out.
StrandResult(Strand const &strand, TInAmt const &in_, TOutAmt const &out_, PaymentSandbox &&sandbox_, boost::container::flat_set< uint256 > ofrsToRm_, bool inactive_)
boost::container::flat_set< uint256 > ofrsToRm
Offers to remove.
TInAmt in
Currency amount in.
StrandResult(Strand const &strand, boost::container::flat_set< uint256 > ofrsToRm_)
StrandResult()=default
Strand result constructor.