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)
 
  172                            << 
"Re-executed limiting step failed. r.first: " 
  176                            "ripple::flow : first step re-executing the " 
  177                            "limiting step failed");
 
  178                        return Result{strand, std::move(ofrsToRm)};
 
  182                else if (!strand[i]->equalOut(r.second, stepOut))
 
  192                    r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
 
  195                    if (strand[i]->isZero(r.second))
 
  199                        JLOG(j.
trace()) << 
"Limiting step found dry";
 
  200                        return Result{strand, std::move(ofrsToRm)};
 
  202                    if (!strand[i]->equalOut(r.second, stepOut))
 
  210                            << 
"Re-executed limiting step failed. r.second: " 
  211                            << r.second << 
" stepOut: " << stepOut;
 
  213                        JLOG(j.
fatal()) << 
"Re-executed limiting step failed";
 
  216                            "ripple::flow : limiting step re-executing the " 
  217                            "limiting step failed");
 
  218                        return Result{strand, std::move(ofrsToRm)};
 
  230            for (
auto i = limitingStep + 1; i < s; ++i)
 
  232                auto const r = strand[i]->fwd(*sb, *afView, ofrsToRm, stepIn);
 
  233                if (strand[i]->isZero(r.second))
 
  237                    JLOG(j.
trace()) << 
"Non-limiting step found dry";
 
  238                    return Result{strand, std::move(ofrsToRm)};
 
  240                if (!strand[i]->equalIn(r.first, stepIn))
 
  248                        << 
"Re-executed forward pass failed. r.first: " 
  249                        << r.first << 
" stepIn: " << stepIn;
 
  251                    JLOG(j.
fatal()) << 
"Re-executed forward pass failed";
 
  254                        "ripple::flow : non-limiting step re-executing the " 
  255                        "forward pass failed");
 
  256                    return Result{strand, std::move(ofrsToRm)};
 
  263        auto const strandIn = *strand.front()->cachedIn();
 
  264        auto const strandOut = *strand.back()->cachedOut();
 
  273            for (
auto i = 0; i < s; ++i)
 
  277                    strand[i]->validFwd(checkSB, checkAfView, stepIn);
 
  281                        << 
"Strand re-execute check failed. Step: " << i;
 
  295            get<TInAmt>(strandIn),
 
  296            get<TOutAmt>(strandOut),
 
  301    catch (FlowException 
const&)
 
  303        return Result{strand, std::move(ofrsToRm)};
 
 
  308template <
class TInAmt, 
class TOutAmt>
 
  311    TInAmt in = beast::zero;
 
  312    TOutAmt out = beast::zero;
 
  314    boost::container::flat_set<uint256> removableOffers;
 
  315    TER ter = temUNKNOWN;
 
  317    FlowResult() = 
default;
 
  322        PaymentSandbox&& sandbox_,
 
  323        boost::container::flat_set<uint256> ofrsToRm)
 
  326        , sandbox(
std::move(sandbox_))
 
  327        , removableOffers(
std::move(ofrsToRm))
 
  332    FlowResult(TER ter_, boost::container::flat_set<uint256> ofrsToRm)
 
  333        : removableOffers(
std::
move(ofrsToRm)), ter(ter_)
 
  341        boost::container::flat_set<uint256> ofrsToRm)
 
  342        : 
in(in_), 
out(out_), removableOffers(
std::
move(ofrsToRm)), ter(ter_)
 
  350qualityUpperBound(ReadView 
const& v, Strand 
const& strand)
 
  355    for (
auto const& step : strand)
 
  357        if (
std::tie(stepQ, dir) = step->qualityUpperBound(v, dir); stepQ)
 
  375template <
typename TOutAmt>
 
  379    Strand 
const& strand,
 
  380    TOutAmt 
const& remainingOut,
 
  381    Quality 
const& limitQuality)
 
  386    for (
auto const& step : strand)
 
  388        if (
std::tie(stepQualityFunc, dir) = step->getQualityFunc(v, dir);
 
  392                qf = stepQualityFunc;
 
  394                qf->combine(*stepQualityFunc);
 
  401    if (!qf || qf->isConst())
 
  404    auto const out = [&]() {
 
  405        if (
auto const out = qf->outFromAvgQ(limitQuality); !
out)
 
  408            return XRPAmount{*
out};
 
  410            return IOUAmount{*
out};
 
  413                remainingOut.issue(), 
out->mantissa(), 
out->exponent()};
 
  443        for (
auto& strand : strands)
 
  444            next_.push_back(&strand);
 
  455        if (v.rules().enabled(featureFlowSortStrands) && !next_.
empty())
 
  459            if (next_.
size() > 1)  
 
  461                for (Strand 
const* strand : next_)
 
  468                    if (
auto const qual = qualityUpperBound(v, *strand))
 
  470                        if (limitQuality && *qual < *limitQuality)
 
  488                    [](
auto const& lhs, 
auto const& rhs) {
 
  490                        return std::get<Quality>(lhs) > std::get<Quality>(rhs);
 
  494                for (
auto const& sq : strandQuals)
 
  506        if (i >= cur_.
size())
 
  509            UNREACHABLE(
"ripple::ActiveStrands::get : input out of range");
 
  517    push(Strand 
const* s)
 
  524    pushRemainingCurToNext(
size_t i)
 
  526        if (i >= cur_.
size())
 
  540        if (i >= next_.
size())
 
  567template <
class TInAmt, 
class TOutAmt>
 
  568FlowResult<TInAmt, TOutAmt>
 
  572    TOutAmt 
const& outReq,
 
  588        Strand 
const& strand;
 
  595            Strand 
const& strand_,
 
  596            Quality 
const& quality_)
 
  615    TInAmt 
const sendMaxInit =
 
  616        sendMaxST ? toAmount<TInAmt>(*sendMaxST) : TInAmt{beast::zero};
 
  618        (sendMaxST && sendMaxInit >= beast::zero)
 
  625    TOutAmt remainingOut(outReq);
 
  630    ActiveStrands activeStrands(strands);
 
  635    boost::container::flat_multiset<TInAmt> savedIns;
 
  636    savedIns.reserve(maxTries);
 
  637    boost::container::flat_multiset<TOutAmt> savedOuts;
 
  638    savedOuts.reserve(maxTries);
 
  640    auto sum = [](
auto const& col) {
 
  643            return TResult{beast::zero};
 
  649    boost::container::flat_set<uint256> ofrsToRmOnFail;
 
  651    while (remainingOut > beast::zero &&
 
  652           (!remainingIn || *remainingIn > beast::zero))
 
  655        if (curTry >= maxTries)
 
  660        activeStrands.activateNext(sb, limitQuality);
 
  665        auto const limitRemainingOut = [&]() {
 
  666            if (activeStrands.size() == 1 && limitQuality)
 
  667                if (
auto const strand = activeStrands.get(0))
 
  668                    return limitOut(sb, *strand, remainingOut, *limitQuality);
 
  671        auto const adjustedRemOut = limitRemainingOut != remainingOut;
 
  673        boost::container::flat_set<uint256> ofrsToRm;
 
  676            flowDebugInfo->newLiquidityPass();
 
  682        for (
size_t strandIndex = 0, sie = activeStrands.size();
 
  686            Strand 
const* strand = activeStrands.get(strandIndex);
 
  696            if (offerCrossing && limitQuality)
 
  698                auto const strandQ = qualityUpperBound(sb, *strand);
 
  699                if (!strandQ || *strandQ < *limitQuality)
 
  702            auto f = flow<TInAmt, TOutAmt>(
 
  703                sb, *strand, remainingIn, limitRemainingOut, j);
 
  708            offersConsidered += f.ofrsUsed;
 
  710            if (!f.success || f.out == beast::zero)
 
  714                flowDebugInfo->pushLiquiditySrc(
 
  718                f.out <= remainingOut && f.sandbox &&
 
  719                    (!remainingIn || f.in <= *remainingIn),
 
  720                "ripple::flow : remaining constraints");
 
  722            Quality 
const q(f.out, f.in);
 
  725                << 
"New flow iter (iter, in, out): " << curTry - 1 << 
" " 
  731            if (limitQuality && q < *limitQuality &&
 
  736                    << 
"Path rejected by limitQuality" 
  737                    << 
" limit: " << *limitQuality << 
" path q: " << q;
 
  743                XRPL_ASSERT(!best, 
"ripple::flow : best is unset");
 
  745                    activeStrands.push(strand);
 
  746                best.
emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
 
  747                activeStrands.pushRemainingCurToNext(strandIndex + 1);
 
  751            activeStrands.push(strand);
 
  753            if (!best || best->quality < q ||
 
  754                (best->quality == q && best->out < f.out))
 
  765                    markInactiveOnUse = activeStrands.size() - 1;
 
  769                    markInactiveOnUse.
reset();
 
  772                best.
emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
 
  776        bool const shouldBreak = [&] {
 
  778                return !best || offersConsidered >= maxOffersToConsider;
 
  784            if (markInactiveOnUse)
 
  786                activeStrands.removeIndex(*markInactiveOnUse);
 
  787                markInactiveOnUse.
reset();
 
  789            savedIns.insert(best->in);
 
  790            savedOuts.insert(best->out);
 
  791            remainingOut = outReq - 
sum(savedOuts);
 
  793                remainingIn = *sendMax - 
sum(savedIns);
 
  796                flowDebugInfo->pushPass(
 
  799                    activeStrands.size());
 
  803                            << 
" remainingOut: " << 
to_string(remainingOut);
 
  810            JLOG(j.
trace()) << 
"All strands dry.";
 
  815        if (!ofrsToRm.empty())
 
  818            for (
auto const& o : ofrsToRm)
 
  829    auto const actualOut = 
sum(savedOuts);
 
  830    auto const actualIn = 
sum(savedIns);
 
  846    bool const fillOrKillEnabled = baseView.
rules().
enabled(fixFillOrKill);
 
  848    if (actualOut != outReq)
 
  850        if (actualOut > outReq)
 
  870            if (!offerCrossing ||
 
  876                    std::move(ofrsToRmOnFail)};
 
  878        else if (actualOut == beast::zero)
 
  894        XRPL_ASSERT(remainingIn, 
"ripple::flow : nonzero remainingIn");
 
  895        if (remainingIn && *remainingIn != beast::zero)
 
  900                std::move(ofrsToRmOnFail)};
 
  903    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.