3#include <xrpld/app/misc/AMMHelpers.h>
4#include <xrpld/app/paths/AMMContext.h>
5#include <xrpld/app/paths/Flow.h>
6#include <xrpld/app/paths/detail/AmountSpec.h>
7#include <xrpld/app/paths/detail/FlatSets.h>
8#include <xrpld/app/paths/detail/FlowDebugInfo.h>
9#include <xrpld/app/paths/detail/Steps.h>
11#include <xrpl/basics/Log.h>
12#include <xrpl/ledger/Credit.h>
13#include <xrpl/protocol/Feature.h>
14#include <xrpl/protocol/IOUAmount.h>
15#include <xrpl/protocol/XRPAmount.h>
17#include <boost/container/flat_set.hpp>
26template <
class TInAmt,
class TOutAmt>
30 TInAmt
in = beast::zero;
31 TOutAmt
out = beast::zero;
50 boost::container::flat_set<uint256> ofrsToRm_,
62 StrandResult(Strand
const& strand, boost::container::flat_set<uint256> ofrsToRm_)
79template <
class TInAmt,
class TOutAmt>
80StrandResult<TInAmt, TOutAmt>
91 JLOG(j.
warn()) <<
"Empty strand passed to Liquidity";
95 boost::container::flat_set<uint256> ofrsToRm;
97 if (isDirectXrpToXrp<TInAmt, TOutAmt>(strand))
99 return Result{strand, std::move(ofrsToRm)};
115 for (
auto i = s; i--;)
117 auto r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
118 if (strand[i]->isZero(r.second))
120 JLOG(j.
trace()) <<
"Strand found dry in rev";
121 return Result{strand, std::move(ofrsToRm)};
124 if (i == 0 && maxIn && *maxIn < get<TInAmt>(r.first))
132 r = strand[i]->fwd(*sb, *afView, ofrsToRm,
EitherAmount(*maxIn));
135 if (strand[i]->isZero(r.second))
137 JLOG(j.
trace()) <<
"First step found dry";
138 return Result{strand, std::move(ofrsToRm)};
140 if (get<TInAmt>(r.first) != *maxIn)
146 JLOG(j.
fatal()) <<
"Re-executed limiting step failed. r.first: "
149 "xrpl::flow : first step re-executing the "
150 "limiting step failed");
151 return Result{strand, std::move(ofrsToRm)};
155 else if (!strand[i]->equalOut(r.second, stepOut))
165 r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
168 if (strand[i]->isZero(r.second))
172 JLOG(j.
trace()) <<
"Limiting step found dry";
173 return Result{strand, std::move(ofrsToRm)};
175 if (!strand[i]->equalOut(r.second, stepOut))
182 JLOG(j.
fatal()) <<
"Re-executed limiting step failed. r.second: " << r.second
183 <<
" stepOut: " << stepOut;
185 JLOG(j.
fatal()) <<
"Re-executed limiting step failed";
188 "xrpl::flow : limiting step re-executing the "
189 "limiting step failed");
190 return Result{strand, std::move(ofrsToRm)};
202 for (
auto i = limitingStep + 1; i < s; ++i)
204 auto const r = strand[i]->fwd(*sb, *afView, ofrsToRm, stepIn);
205 if (strand[i]->isZero(r.second))
209 JLOG(j.
trace()) <<
"Non-limiting step found dry";
210 return Result{strand, std::move(ofrsToRm)};
212 if (!strand[i]->equalIn(r.first, stepIn))
219 JLOG(j.
fatal()) <<
"Re-executed forward pass failed. r.first: " << r.first <<
" stepIn: " << stepIn;
221 JLOG(j.
fatal()) <<
"Re-executed forward pass failed";
224 "xrpl::flow : non-limiting step re-executing the "
225 "forward pass failed");
226 return Result{strand, std::move(ofrsToRm)};
233 auto const strandIn = *strand.front()->cachedIn();
234 auto const strandOut = *strand.back()->cachedOut();
243 for (
auto i = 0; i < s; ++i)
246 std::tie(
valid, stepIn) = strand[i]->validFwd(checkSB, checkAfView, stepIn);
249 JLOG(j.
warn()) <<
"Strand re-execute check failed. Step: " << i;
260 strand, get<TInAmt>(strandIn), get<TOutAmt>(strandOut), std::move(*sb), std::move(ofrsToRm), inactive);
262 catch (FlowException
const&)
264 return Result{strand, std::move(ofrsToRm)};
269template <
class TInAmt,
class TOutAmt>
272 TInAmt in = beast::zero;
273 TOutAmt out = beast::zero;
275 boost::container::flat_set<uint256> removableOffers;
276 TER ter = temUNKNOWN;
278 FlowResult() =
default;
283 PaymentSandbox&& sandbox_,
284 boost::container::flat_set<uint256> ofrsToRm)
285 : in(in_), out(out_), sandbox(
std::move(sandbox_)), removableOffers(
std::move(ofrsToRm)), ter(tesSUCCESS)
289 FlowResult(TER ter_, boost::container::flat_set<uint256> ofrsToRm) : removableOffers(
std::
move(ofrsToRm)), ter(ter_)
293 FlowResult(TER ter_, TInAmt
const& in_, TOutAmt
const& out_, boost::container::flat_set<uint256> ofrsToRm)
294 :
in(in_),
out(out_), removableOffers(
std::
move(ofrsToRm)), ter(ter_)
302qualityUpperBound(ReadView
const& v, Strand
const& strand)
307 for (
auto const& step : strand)
309 if (
std::tie(stepQ, dir) = step->qualityUpperBound(v, dir); stepQ)
327template <
typename TOutAmt>
329limitOut(ReadView
const& v, Strand
const& strand, TOutAmt
const& remainingOut, Quality
const& limitQuality)
334 for (
auto const& step : strand)
336 if (
std::tie(stepQualityFunc, dir) = step->getQualityFunc(v, dir); stepQualityFunc)
339 qf = stepQualityFunc;
341 qf->combine(*stepQualityFunc);
348 if (!qf || qf->isConst())
351 auto const out = [&]() {
352 if (
auto const out = qf->outFromAvgQ(limitQuality); !
out)
355 return XRPAmount{*
out};
357 return IOUAmount{*
out};
359 return STAmount{remainingOut.issue(),
out->mantissa(),
out->exponent()};
389 for (
auto& strand : strands)
390 next_.push_back(&strand);
405 if (next_.
size() > 1)
407 for (Strand
const* strand : next_)
414 if (
auto const qual = qualityUpperBound(v, *strand))
416 if (limitQuality && *qual < *limitQuality)
426 strandQualities.
push_back({*qual, strand});
433 return std::get<Quality>(lhs) > std::get<Quality>(rhs);
437 for (
auto const& sq : strandQualities)
449 if (i >= cur_.
size())
452 UNREACHABLE(
"xrpl::ActiveStrands::get : input out of range");
460 push(Strand
const* s)
467 pushRemainingCurToNext(
size_t i)
469 if (i >= cur_.
size())
483 if (i >= next_.
size())
510template <
class TInAmt,
class TOutAmt>
511FlowResult<TInAmt, TOutAmt>
515 TOutAmt
const& outReq,
531 Strand
const& strand;
538 Strand
const& strand_,
539 Quality
const& quality_)
540 :
in(in_),
out(out_), sb(std::move(sb_)), strand(strand_), quality(quality_)
554 TInAmt
const sendMaxInit = sendMaxST ? toAmount<TInAmt>(*sendMaxST) : TInAmt{beast::zero};
560 TOutAmt remainingOut(outReq);
565 ActiveStrands activeStrands(strands);
570 boost::container::flat_multiset<TInAmt> savedIns;
571 savedIns.reserve(maxTries);
572 boost::container::flat_multiset<TOutAmt> savedOuts;
573 savedOuts.reserve(maxTries);
575 auto sum = [](
auto const& col) {
578 return TResult{beast::zero};
584 boost::container::flat_set<uint256> ofrsToRmOnFail;
586 while (remainingOut > beast::zero && (!remainingIn || *remainingIn > beast::zero))
589 if (curTry >= maxTries)
594 activeStrands.activateNext(sb, limitQuality);
599 auto const limitRemainingOut = [&]() {
600 if (activeStrands.size() == 1 && limitQuality)
601 if (
auto const strand = activeStrands.get(0))
602 return limitOut(sb, *strand, remainingOut, *limitQuality);
605 auto const adjustedRemOut = limitRemainingOut != remainingOut;
607 boost::container::flat_set<uint256> ofrsToRm;
610 flowDebugInfo->newLiquidityPass();
616 for (
size_t strandIndex = 0, sie = activeStrands.size(); strandIndex != sie; ++strandIndex)
618 Strand
const* strand = activeStrands.get(strandIndex);
628 if (offerCrossing && limitQuality)
630 auto const strandQ = qualityUpperBound(sb, *strand);
631 if (!strandQ || *strandQ < *limitQuality)
634 auto f = flow<TInAmt, TOutAmt>(sb, *strand, remainingIn, limitRemainingOut, j);
639 offersConsidered += f.ofrsUsed;
641 if (!f.success || f.out == beast::zero)
648 f.out <= remainingOut && f.sandbox && (!remainingIn || f.in <= *remainingIn),
649 "xrpl::flow : remaining constraints");
651 Quality
const q(f.out, f.in);
653 JLOG(j.
trace()) <<
"New flow iter (iter, in, out): " << curTry - 1 <<
" " <<
to_string(f.in) <<
" "
659 if (limitQuality && q < *limitQuality &&
662 JLOG(j.
trace()) <<
"Path rejected by limitQuality"
663 <<
" limit: " << *limitQuality <<
" path q: " << q;
667 XRPL_ASSERT(!best,
"xrpl::flow : best is unset");
669 activeStrands.push(strand);
670 best.
emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
671 activeStrands.pushRemainingCurToNext(strandIndex + 1);
675 bool const shouldBreak = !best || offersConsidered >= maxOffersToConsider;
679 if (markInactiveOnUse)
681 activeStrands.removeIndex(*markInactiveOnUse);
682 markInactiveOnUse.
reset();
684 savedIns.insert(best->in);
685 savedOuts.insert(best->out);
686 remainingOut = outReq -
sum(savedOuts);
688 remainingIn = *sendMax -
sum(savedIns);
694 <<
" remainingOut: " <<
to_string(remainingOut);
701 JLOG(j.
trace()) <<
"All strands dry.";
706 if (!ofrsToRm.empty())
709 for (
auto const& o : ofrsToRm)
720 auto const actualOut =
sum(savedOuts);
721 auto const actualIn =
sum(savedIns);
736 bool const fillOrKillEnabled = baseView.
rules().
enabled(fixFillOrKill);
738 if (actualOut != outReq)
740 if (actualOut > outReq)
761 return {
tecPATH_PARTIAL, actualIn, actualOut, std::move(ofrsToRmOnFail)};
763 else if (actualOut == beast::zero)
768 if (offerCrossing && (!partialPayment && (!fillOrKillEnabled || offerCrossing ==
OfferCrossing::sell)))
777 XRPL_ASSERT(remainingIn,
"xrpl::flow : nonzero remainingIn");
778 if (remainingIn && *remainingIn != beast::zero)
779 return {
tecPATH_PARTIAL, actualIn, actualOut, std::move(ofrsToRmOnFail)};
782 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.
Number is a floating point type that can represent a wide range of values.
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
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
Rules const & rules() const override
Returns the tx processing rules.
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)
Quality composed_quality(Quality const &lhs, Quality const &rhs)
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.
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.
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.
boost::outcome_v2::result< T, std::error_code > Result
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.
TOutAmt out
Currency amount out.
boost::container::flat_set< uint256 > ofrsToRm
Offers to remove.
std::optional< PaymentSandbox > sandbox
Resulting Sandbox state.
bool success
Strand succeeded.
StrandResult()=default
Strand result constructor.
StrandResult(Strand const &strand, TInAmt const &in_, TOutAmt const &out_, PaymentSandbox &&sandbox_, boost::container::flat_set< uint256 > ofrsToRm_, bool inactive_)
StrandResult(Strand const &strand, boost::container::flat_set< uint256 > ofrsToRm_)
TInAmt in
Currency amount in.
bool inactive
Strand should not considered as a further source of liquidity (dry)