1#ifndef XRPL_APP_PATHS_IMPL_STRANDFLOW_H_INCLUDED
2#define XRPL_APP_PATHS_IMPL_STRANDFLOW_H_INCLUDED
4#include <xrpld/app/misc/AMMHelpers.h>
5#include <xrpld/app/paths/AMMContext.h>
6#include <xrpld/app/paths/Credit.h>
7#include <xrpld/app/paths/Flow.h>
8#include <xrpld/app/paths/detail/AmountSpec.h>
9#include <xrpld/app/paths/detail/FlatSets.h>
10#include <xrpld/app/paths/detail/FlowDebugInfo.h>
11#include <xrpld/app/paths/detail/Steps.h>
13#include <xrpl/basics/Log.h>
14#include <xrpl/protocol/Feature.h>
15#include <xrpl/protocol/IOUAmount.h>
16#include <xrpl/protocol/XRPAmount.h>
18#include <boost/container/flat_set.hpp>
27template <
class TInAmt,
class TOutAmt>
31 TInAmt
in = beast::zero;
32 TOutAmt
out = beast::zero;
51 boost::container::flat_set<uint256> ofrsToRm_,
65 boost::container::flat_set<uint256> ofrsToRm_)
84template <
class TInAmt,
class TOutAmt>
85StrandResult<TInAmt, TOutAmt>
96 JLOG(j.
warn()) <<
"Empty strand passed to Liquidity";
100 boost::container::flat_set<uint256> ofrsToRm;
102 if (isDirectXrpToXrp<TInAmt, TOutAmt>(strand))
104 return Result{strand, std::move(ofrsToRm)};
120 for (
auto i = s; i--;)
122 auto r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
123 if (strand[i]->isZero(r.second))
125 JLOG(j.
trace()) <<
"Strand found dry in rev";
126 return Result{strand, std::move(ofrsToRm)};
129 if (i == 0 && maxIn && *maxIn < get<TInAmt>(r.first))
141 if (strand[i]->isZero(r.second))
143 JLOG(j.
trace()) <<
"First step found dry";
144 return Result{strand, std::move(ofrsToRm)};
146 if (get<TInAmt>(r.first) != *maxIn)
153 <<
"Re-executed limiting step failed. r.first: "
157 "ripple::flow : first step re-executing the "
158 "limiting step failed");
159 return Result{strand, std::move(ofrsToRm)};
163 else if (!strand[i]->equalOut(r.second, stepOut))
173 r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
176 if (strand[i]->isZero(r.second))
180 JLOG(j.
trace()) <<
"Limiting step found dry";
181 return Result{strand, std::move(ofrsToRm)};
183 if (!strand[i]->equalOut(r.second, stepOut))
191 <<
"Re-executed limiting step failed. r.second: "
192 << r.second <<
" stepOut: " << stepOut;
194 JLOG(j.
fatal()) <<
"Re-executed limiting step failed";
197 "ripple::flow : limiting step re-executing the "
198 "limiting step failed");
199 return Result{strand, std::move(ofrsToRm)};
211 for (
auto i = limitingStep + 1; i < s; ++i)
213 auto const r = strand[i]->fwd(*sb, *afView, ofrsToRm, stepIn);
214 if (strand[i]->isZero(r.second))
218 JLOG(j.
trace()) <<
"Non-limiting step found dry";
219 return Result{strand, std::move(ofrsToRm)};
221 if (!strand[i]->equalIn(r.first, stepIn))
229 <<
"Re-executed forward pass failed. r.first: "
230 << r.first <<
" stepIn: " << stepIn;
232 JLOG(j.
fatal()) <<
"Re-executed forward pass failed";
235 "ripple::flow : non-limiting step re-executing the "
236 "forward pass failed");
237 return Result{strand, std::move(ofrsToRm)};
244 auto const strandIn = *strand.front()->cachedIn();
245 auto const strandOut = *strand.back()->cachedOut();
254 for (
auto i = 0; i < s; ++i)
258 strand[i]->validFwd(checkSB, checkAfView, stepIn);
262 <<
"Strand re-execute check failed. Step: " << i;
276 get<TInAmt>(strandIn),
277 get<TOutAmt>(strandOut),
282 catch (FlowException
const&)
284 return Result{strand, std::move(ofrsToRm)};
289template <
class TInAmt,
class TOutAmt>
292 TInAmt in = beast::zero;
293 TOutAmt out = beast::zero;
295 boost::container::flat_set<uint256> removableOffers;
296 TER ter = temUNKNOWN;
298 FlowResult() =
default;
303 PaymentSandbox&& sandbox_,
304 boost::container::flat_set<uint256> ofrsToRm)
307 , sandbox(
std::move(sandbox_))
308 , removableOffers(
std::move(ofrsToRm))
313 FlowResult(TER ter_, boost::container::flat_set<uint256> ofrsToRm)
314 : removableOffers(
std::
move(ofrsToRm)), ter(ter_)
322 boost::container::flat_set<uint256> ofrsToRm)
323 :
in(in_),
out(out_), removableOffers(
std::
move(ofrsToRm)), ter(ter_)
331qualityUpperBound(ReadView
const& v, Strand
const& strand)
336 for (
auto const& step : strand)
338 if (
std::tie(stepQ, dir) = step->qualityUpperBound(v, dir); stepQ)
356template <
typename TOutAmt>
360 Strand
const& strand,
361 TOutAmt
const& remainingOut,
362 Quality
const& limitQuality)
367 for (
auto const& step : strand)
369 if (
std::tie(stepQualityFunc, dir) = step->getQualityFunc(v, dir);
373 qf = stepQualityFunc;
375 qf->combine(*stepQualityFunc);
382 if (!qf || qf->isConst())
385 auto const out = [&]() {
386 if (
auto const out = qf->outFromAvgQ(limitQuality); !
out)
389 return XRPAmount{*
out};
391 return IOUAmount{*
out};
394 remainingOut.issue(),
out->mantissa(),
out->exponent()};
424 for (
auto& strand : strands)
425 next_.push_back(&strand);
436 if (v.rules().enabled(featureFlowSortStrands) && !next_.
empty())
440 if (next_.
size() > 1)
442 for (Strand
const* strand : next_)
449 if (
auto const qual = qualityUpperBound(v, *strand))
451 if (limitQuality && *qual < *limitQuality)
469 [](
auto const& lhs,
auto const& rhs) {
471 return std::get<Quality>(lhs) > std::get<Quality>(rhs);
475 for (
auto const& sq : strandQuals)
487 if (i >= cur_.
size())
490 UNREACHABLE(
"ripple::ActiveStrands::get : input out of range");
498 push(Strand
const* s)
505 pushRemainingCurToNext(
size_t i)
507 if (i >= cur_.
size())
521 if (i >= next_.
size())
548template <
class TInAmt,
class TOutAmt>
549FlowResult<TInAmt, TOutAmt>
553 TOutAmt
const& outReq,
569 Strand
const& strand;
576 Strand
const& strand_,
577 Quality
const& quality_)
596 TInAmt
const sendMaxInit =
597 sendMaxST ? toAmount<TInAmt>(*sendMaxST) : TInAmt{beast::zero};
599 (sendMaxST && sendMaxInit >= beast::zero)
606 TOutAmt remainingOut(outReq);
611 ActiveStrands activeStrands(strands);
616 boost::container::flat_multiset<TInAmt> savedIns;
617 savedIns.reserve(maxTries);
618 boost::container::flat_multiset<TOutAmt> savedOuts;
619 savedOuts.reserve(maxTries);
621 auto sum = [](
auto const& col) {
624 return TResult{beast::zero};
630 boost::container::flat_set<uint256> ofrsToRmOnFail;
632 while (remainingOut > beast::zero &&
633 (!remainingIn || *remainingIn > beast::zero))
636 if (curTry >= maxTries)
641 activeStrands.activateNext(sb, limitQuality);
646 auto const limitRemainingOut = [&]() {
647 if (activeStrands.size() == 1 && limitQuality)
648 if (
auto const strand = activeStrands.get(0))
649 return limitOut(sb, *strand, remainingOut, *limitQuality);
652 auto const adjustedRemOut = limitRemainingOut != remainingOut;
654 boost::container::flat_set<uint256> ofrsToRm;
657 flowDebugInfo->newLiquidityPass();
663 for (
size_t strandIndex = 0, sie = activeStrands.size();
667 Strand
const* strand = activeStrands.get(strandIndex);
677 if (offerCrossing && limitQuality)
679 auto const strandQ = qualityUpperBound(sb, *strand);
680 if (!strandQ || *strandQ < *limitQuality)
683 auto f = flow<TInAmt, TOutAmt>(
684 sb, *strand, remainingIn, limitRemainingOut, j);
689 offersConsidered += f.ofrsUsed;
691 if (!f.success || f.out == beast::zero)
695 flowDebugInfo->pushLiquiditySrc(
699 f.out <= remainingOut && f.sandbox &&
700 (!remainingIn || f.in <= *remainingIn),
701 "ripple::flow : remaining constraints");
703 Quality
const q(f.out, f.in);
706 <<
"New flow iter (iter, in, out): " << curTry - 1 <<
" "
712 if (limitQuality && q < *limitQuality &&
717 <<
"Path rejected by limitQuality"
718 <<
" limit: " << *limitQuality <<
" path q: " << q;
724 XRPL_ASSERT(!best,
"ripple::flow : best is unset");
726 activeStrands.push(strand);
727 best.
emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
728 activeStrands.pushRemainingCurToNext(strandIndex + 1);
732 activeStrands.push(strand);
734 if (!best || best->quality < q ||
735 (best->quality == q && best->out < f.out))
746 markInactiveOnUse = activeStrands.size() - 1;
750 markInactiveOnUse.
reset();
753 best.
emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
757 bool const shouldBreak = [&] {
759 return !best || offersConsidered >= maxOffersToConsider;
765 if (markInactiveOnUse)
767 activeStrands.removeIndex(*markInactiveOnUse);
768 markInactiveOnUse.
reset();
770 savedIns.insert(best->in);
771 savedOuts.insert(best->out);
772 remainingOut = outReq -
sum(savedOuts);
774 remainingIn = *sendMax -
sum(savedIns);
777 flowDebugInfo->pushPass(
780 activeStrands.size());
784 <<
" remainingOut: " <<
to_string(remainingOut);
791 JLOG(j.
trace()) <<
"All strands dry.";
796 if (!ofrsToRm.empty())
799 for (
auto const& o : ofrsToRm)
810 auto const actualOut =
sum(savedOuts);
811 auto const actualIn =
sum(savedIns);
827 bool const fillOrKillEnabled = baseView.
rules().
enabled(fixFillOrKill);
829 if (actualOut != outReq)
831 if (actualOut > outReq)
851 if (!offerCrossing ||
857 std::move(ofrsToRmOnFail)};
859 else if (actualOut == beast::zero)
875 XRPL_ASSERT(remainingIn,
"ripple::flow : nonzero remainingIn");
876 if (remainingIn && *remainingIn != beast::zero)
881 std::move(ofrsToRmOnFail)};
884 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.