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>
31#include <xrpl/basics/Log.h>
32#include <xrpl/protocol/Feature.h>
33#include <xrpl/protocol/IOUAmount.h>
34#include <xrpl/protocol/XRPAmount.h>
36#include <boost/container/flat_set.hpp>
45template <
class TInAmt,
class TOutAmt>
49 TInAmt
in = beast::zero;
50 TOutAmt
out = beast::zero;
69 boost::container::flat_set<uint256> ofrsToRm_,
83 boost::container::flat_set<uint256> ofrsToRm_)
102template <
class TInAmt,
class TOutAmt>
103StrandResult<TInAmt, TOutAmt>
106 Strand
const& strand,
114 JLOG(j.
warn()) <<
"Empty strand passed to Liquidity";
118 boost::container::flat_set<uint256> ofrsToRm;
120 if (isDirectXrpToXrp<TInAmt, TOutAmt>(strand))
122 return Result{strand, std::move(ofrsToRm)};
138 for (
auto i = s; i--;)
140 auto r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
141 if (strand[i]->isZero(r.second))
143 JLOG(j.
trace()) <<
"Strand found dry in rev";
144 return Result{strand, std::move(ofrsToRm)};
147 if (i == 0 && maxIn && *maxIn < get<TInAmt>(r.first))
159 if (strand[i]->isZero(r.second))
161 JLOG(j.
trace()) <<
"First step found dry";
162 return Result{strand, std::move(ofrsToRm)};
164 if (get<TInAmt>(r.first) != *maxIn)
170 <<
"Re-executed limiting step failed. r.first: "
174 "ripple::flow : first step re-executing the "
175 "limiting step failed");
176 return Result{strand, std::move(ofrsToRm)};
179 else if (!strand[i]->equalOut(r.second, stepOut))
189 r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
192 if (strand[i]->isZero(r.second))
196 JLOG(j.
trace()) <<
"Limiting step found dry";
197 return Result{strand, std::move(ofrsToRm)};
199 if (!strand[i]->equalOut(r.second, stepOut))
206 <<
"Re-executed limiting step failed. r.second: "
207 << r.second <<
" stepOut: " << stepOut;
209 JLOG(j.
fatal()) <<
"Re-executed limiting step failed";
212 "ripple::flow : limiting step re-executing the "
213 "limiting step failed");
214 return Result{strand, std::move(ofrsToRm)};
225 for (
auto i = limitingStep + 1; i < s; ++i)
227 auto const r = strand[i]->fwd(*sb, *afView, ofrsToRm, stepIn);
228 if (strand[i]->isZero(r.second))
232 JLOG(j.
trace()) <<
"Non-limiting step found dry";
233 return Result{strand, std::move(ofrsToRm)};
235 if (!strand[i]->equalIn(r.first, stepIn))
242 <<
"Re-executed forward pass failed. r.first: "
243 << r.first <<
" stepIn: " << stepIn;
245 JLOG(j.
fatal()) <<
"Re-executed forward pass failed";
248 "ripple::flow : non-limiting step re-executing the "
249 "forward pass failed");
250 return Result{strand, std::move(ofrsToRm)};
256 auto const strandIn = *strand.front()->cachedIn();
257 auto const strandOut = *strand.back()->cachedOut();
266 for (
auto i = 0; i < s; ++i)
270 strand[i]->validFwd(checkSB, checkAfView, stepIn);
274 <<
"Strand re-execute check failed. Step: " << i;
288 get<TInAmt>(strandIn),
289 get<TOutAmt>(strandOut),
294 catch (FlowException
const&)
296 return Result{strand, std::move(ofrsToRm)};
301template <
class TInAmt,
class TOutAmt>
304 TInAmt in = beast::zero;
305 TOutAmt out = beast::zero;
307 boost::container::flat_set<uint256> removableOffers;
308 TER ter = temUNKNOWN;
310 FlowResult() =
default;
315 PaymentSandbox&& sandbox_,
316 boost::container::flat_set<uint256> ofrsToRm)
319 , sandbox(
std::move(sandbox_))
320 , removableOffers(
std::move(ofrsToRm))
325 FlowResult(TER ter_, boost::container::flat_set<uint256> ofrsToRm)
326 : removableOffers(
std::move(ofrsToRm)), ter(ter_)
334 boost::container::flat_set<uint256> ofrsToRm)
335 :
in(in_),
out(out_), removableOffers(
std::move(ofrsToRm)), ter(ter_)
343qualityUpperBound(ReadView
const& v, Strand
const& strand)
348 for (
auto const& step : strand)
350 if (
std::tie(stepQ, dir) = step->qualityUpperBound(v, dir); stepQ)
368template <
typename TOutAmt>
372 Strand
const& strand,
373 TOutAmt
const& remainingOut,
374 Quality
const& limitQuality)
379 for (
auto const& step : strand)
381 if (
std::tie(stepQualityFunc, dir) = step->getQualityFunc(v, dir);
385 qf = stepQualityFunc;
387 qf->combine(*stepQualityFunc);
394 if (!qf || qf->isConst())
397 auto const out = [&]() {
398 if (
auto const out = qf->outFromAvgQ(limitQuality); !
out)
400 else if constexpr (std::is_same_v<TOutAmt, XRPAmount>)
401 return XRPAmount{*
out};
402 else if constexpr (std::is_same_v<TOutAmt, IOUAmount>)
403 return IOUAmount{*
out};
406 remainingOut.issue(),
out->mantissa(),
out->exponent()};
436 for (
auto& strand : strands)
448 if (v.rules().enabled(featureFlowSortStrands) && !next_.
empty())
452 if (next_.
size() > 1)
454 for (Strand
const* strand : next_)
461 if (
auto const qual = qualityUpperBound(v, *strand))
463 if (limitQuality && *qual < *limitQuality)
481 [](
auto const& lhs,
auto const& rhs) {
483 return std::get<Quality>(lhs) > std::get<Quality>(rhs);
486 next_.reserve(strandQuals.
size());
487 for (
auto const& sq : strandQuals)
489 next_.push_back(std::get<Strand const*>(sq));
499 if (i >= cur_.
size())
501 UNREACHABLE(
"ripple::ActiveStrands::get : input out of range");
508 push(Strand
const* s)
515 pushRemainingCurToNext(
size_t i)
517 if (i >= cur_.
size())
531 if (i >= next_.size())
533 next_.erase(next_.begin() + i);
558template <
class TInAmt,
class TOutAmt>
559FlowResult<TInAmt, TOutAmt>
563 TOutAmt
const& outReq,
579 Strand
const& strand;
586 Strand
const& strand_,
587 Quality
const& quality_)
606 TInAmt
const sendMaxInit =
607 sendMaxST ? toAmount<TInAmt>(*sendMaxST) : TInAmt{beast::zero};
609 (sendMaxST && sendMaxInit >= beast::zero)
616 TOutAmt remainingOut(outReq);
621 ActiveStrands activeStrands(strands);
626 boost::container::flat_multiset<TInAmt> savedIns;
627 savedIns.reserve(maxTries);
628 boost::container::flat_multiset<TOutAmt> savedOuts;
629 savedOuts.reserve(maxTries);
631 auto sum = [](
auto const& col) {
634 return TResult{beast::zero};
640 boost::container::flat_set<uint256> ofrsToRmOnFail;
642 while (remainingOut > beast::zero &&
643 (!remainingIn || *remainingIn > beast::zero))
646 if (curTry >= maxTries)
651 activeStrands.activateNext(sb, limitQuality);
656 auto const limitRemainingOut = [&]() {
657 if (activeStrands.size() == 1 && limitQuality)
658 if (
auto const strand = activeStrands.get(0))
659 return limitOut(sb, *strand, remainingOut, *limitQuality);
662 auto const adjustedRemOut = limitRemainingOut != remainingOut;
664 boost::container::flat_set<uint256> ofrsToRm;
667 flowDebugInfo->newLiquidityPass();
673 for (
size_t strandIndex = 0, sie = activeStrands.size();
677 Strand
const* strand = activeStrands.get(strandIndex);
687 if (offerCrossing && limitQuality)
689 auto const strandQ = qualityUpperBound(sb, *strand);
690 if (!strandQ || *strandQ < *limitQuality)
693 auto f = flow<TInAmt, TOutAmt>(
694 sb, *strand, remainingIn, limitRemainingOut, j);
699 offersConsidered += f.ofrsUsed;
701 if (!f.success || f.out == beast::zero)
705 flowDebugInfo->pushLiquiditySrc(
709 f.out <= remainingOut && f.sandbox &&
710 (!remainingIn || f.in <= *remainingIn),
711 "ripple::flow : remaining constraints");
713 Quality
const q(f.out, f.in);
716 <<
"New flow iter (iter, in, out): " << curTry - 1 <<
" "
722 if (limitQuality && q < *limitQuality &&
727 <<
"Path rejected by limitQuality"
728 <<
" limit: " << *limitQuality <<
" path q: " << q;
734 XRPL_ASSERT(!best,
"ripple::flow : best is unset");
736 activeStrands.push(strand);
737 best.
emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
738 activeStrands.pushRemainingCurToNext(strandIndex + 1);
742 activeStrands.push(strand);
744 if (!best || best->quality < q ||
745 (best->quality == q && best->out < f.out))
756 markInactiveOnUse = activeStrands.size() - 1;
760 markInactiveOnUse.
reset();
763 best.
emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
767 bool const shouldBreak = [&] {
769 return !best || offersConsidered >= maxOffersToConsider;
775 if (markInactiveOnUse)
777 activeStrands.removeIndex(*markInactiveOnUse);
778 markInactiveOnUse.
reset();
780 savedIns.insert(best->in);
781 savedOuts.insert(best->out);
782 remainingOut = outReq -
sum(savedOuts);
784 remainingIn = *sendMax -
sum(savedIns);
787 flowDebugInfo->pushPass(
790 activeStrands.size());
794 <<
" remainingOut: " <<
to_string(remainingOut);
801 JLOG(j.
trace()) <<
"All strands dry.";
806 if (!ofrsToRm.empty())
809 for (
auto const& o : ofrsToRm)
820 auto const actualOut =
sum(savedOuts);
821 auto const actualIn =
sum(savedIns);
837 bool const fillOrKillEnabled = baseView.
rules().
enabled(fixFillOrKill);
839 if (actualOut != outReq)
841 if (actualOut > outReq)
861 if (!offerCrossing ||
867 std::move(ofrsToRmOnFail)};
869 else if (actualOut == beast::zero)
885 XRPL_ASSERT(remainingIn,
"ripple::flow : nonzero remainingIn");
886 if (remainingIn && *remainingIn != beast::zero)
891 std::move(ofrsToRmOnFail)};
894 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(PreclaimContext const &ctx, AccountID const &src)
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.
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
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.
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.