20#include <xrpld/app/paths/detail/Steps.h> 
   22#include <xrpl/basics/contract.h> 
   23#include <xrpl/json/json_writer.h> 
   24#include <xrpl/ledger/ReadView.h> 
   25#include <xrpl/protocol/IOUAmount.h> 
   26#include <xrpl/protocol/XRPAmount.h> 
   36    double const ratTol = 0.001;
 
   52    double const diff = std::abs(a - b);
 
   53    auto const r = diff / 
std::max(std::abs(a), std::abs(b));
 
 
   60    return expected == actual;
 
 
   76    Issue const& curIssue)
 
  101            << 
"Found offer/account payment step. Aborting payment strand.";
 
  102        UNREACHABLE(
"ripple::toStep : offer/account payment payment strand");
 
  110        "ripple::toStep : currency or issuer");
 
  120        JLOG(j.info()) << 
"Found xrp/xrp offer payment step";
 
  124    XRPL_ASSERT(e2->
isOffer(), 
"ripple::toStep : is offer");
 
  126    if (
isXRP(outCurrency))
 
 
  140    Issue const& deliver,
 
  144    bool ownerPaysTransferFee,
 
  154    if ((sendMaxIssue && sendMaxIssue->account == 
noAccount()) ||
 
  159    for (
auto const& pe : path)
 
  161        auto const t = pe.getNodeType();
 
  170        if (hasAccount && (hasIssuer || hasCurrency))
 
  173        if (hasIssuer && 
isXRP(pe.getIssuerID()))
 
  176        if (hasAccount && 
isXRP(pe.getAccountID()))
 
  179        if (hasCurrency && hasIssuer &&
 
  180            isXRP(pe.getCurrency()) != 
isXRP(pe.getIssuerID()))
 
  183        if (hasIssuer && (pe.getIssuerID() == 
noAccount()))
 
  186        if (hasAccount && (pe.getAccountID() == 
noAccount()))
 
  190    Issue curIssue = [&] {
 
  191        auto const& currency =
 
  192            sendMaxIssue ? sendMaxIssue->currency : deliver.
currency;
 
  195        return Issue{currency, src};
 
  205    normPath.
reserve(4 + path.size());
 
  210        if (sendMaxIssue && sendMaxIssue->account != src &&
 
  211            (path.empty() || !path[0].isAccount() ||
 
  212             path[0].getAccountID() != sendMaxIssue->account))
 
  218        for (
auto const& i : path)
 
  235        if (!((normPath.
back().isAccount() &&
 
  236               normPath.
back().getAccountID() == deliver.
account) ||
 
  242        if (!normPath.
back().isAccount() ||
 
  243            normPath.
back().getAccountID() != dst)
 
  249    if (normPath.
size() < 2)
 
  252    auto const strandSrc = normPath.
front().getAccountID();
 
  253    auto const strandDst = normPath.
back().getAccountID();
 
  257    result.reserve(2 * normPath.
size());
 
  266    boost::container::flat_set<Issue> seenBookOuts;
 
  267    seenDirectIssues[0].reserve(normPath.
size());
 
  268    seenDirectIssues[1].reserve(normPath.
size());
 
  269    seenBookOuts.reserve(normPath.
size());
 
  270    auto ctx = [&](
bool isLast = 
false) {
 
  279            ownerPaysTransferFee,
 
  299        auto cur = &normPath[i];
 
  300        auto const next = &normPath[i + 1];
 
  302        if (cur->isAccount())
 
  303            curIssue.
account = cur->getAccountID();
 
  304        else if (cur->hasIssuer())
 
  305            curIssue.
account = cur->getIssuerID();
 
  307        if (cur->hasCurrency())
 
  309            curIssue.
currency = cur->getCurrency();
 
  314        if (cur->isAccount() && next->isAccount())
 
  317                curIssue.
account != cur->getAccountID() &&
 
  318                curIssue.
account != next->getAccountID())
 
  320                JLOG(j.
trace()) << 
"Inserting implied account";
 
  327                    return {msr.first, Strand{}};
 
  328                result.push_back(std::move(msr.second));
 
  337        else if (cur->isAccount() && next->isOffer())
 
  339            if (curIssue.
account != cur->getAccountID())
 
  341                JLOG(j.
trace()) << 
"Inserting implied account before offer";
 
  348                    return {msr.first, Strand{}};
 
  349                result.push_back(std::move(msr.second));
 
  358        else if (cur->isOffer() && next->isAccount())
 
  360            if (curIssue.
account != next->getAccountID() &&
 
  361                !
isXRP(next->getAccountID()))
 
  365                    if (i != normPath.
size() - 2)
 
  373                            return {msr.first, Strand{}};
 
  374                        result.push_back(std::move(msr.second));
 
  379                    JLOG(j.
trace()) << 
"Inserting implied account after offer";
 
  383                        next->getAccountID(),
 
  386                        return {msr.first, Strand{}};
 
  387                    result.push_back(std::move(msr.second));
 
  393        if (!next->isOffer() && next->hasCurrency() &&
 
  394            next->getCurrency() != curIssue.
currency)
 
  398            UNREACHABLE(
"ripple::toStrand : offer currency mismatch");
 
  404            ctx( i == normPath.
size() - 2), cur, next, curIssue);
 
  406            result.emplace_back(std::move(s.second));
 
  409            JLOG(j.
debug()) << 
"toStep failed: " << s.first;
 
  410            return {s.first, Strand{}};
 
  414    auto checkStrand = [&]() -> 
bool {
 
  416            if (
auto r = s.directStepAccts())
 
  418            if (
auto const r = s.bookStepBook())
 
  420            Throw<FlowException>(
 
  421                tefEXCEPTION, 
"Step should be either a direct or book step");
 
  428                sendMaxIssue ? sendMaxIssue->currency : deliver.
currency;
 
  431            return Issue{currency, src};
 
  434        for (
auto const& s : result)
 
  436            auto const accts = stepAccts(*s);
 
  437            if (accts.first != curAcc)
 
  440            if (
auto const b = s->bookStepBook())
 
  448                curIss.account = accts.second;
 
  451            curAcc = accts.second;
 
  455        if (curIss.currency != deliver.
currency)
 
  457        if (curIss.account != deliver.
account && curIss.account != dst)
 
  465        JLOG(j.
warn()) << 
"Flow check strand failed";
 
  466        UNREACHABLE(
"ripple::toStrand : invalid strand");
 
 
  479    Issue const& deliver,
 
  484    bool ownerPaysTransferFee,
 
  493    auto insert = [&](Strand s) {
 
  494        bool const hasStrand =
 
  511            ownerPaysTransferFee,
 
  516        auto const ter = sp.first;
 
  517        auto& strand = sp.second;
 
  521            JLOG(j.
trace()) << 
"failed to add default path";
 
  527        else if (strand.empty())
 
  529            JLOG(j.
trace()) << 
"toStrand failed";
 
  530            Throw<FlowException>(
 
  535            insert(std::move(strand));
 
  538    else if (paths.
empty())
 
  540        JLOG(j.
debug()) << 
"Flow: Invalid transaction: No paths and direct " 
  541                           "ripple not allowed.";
 
  546    for (
auto const& p : paths)
 
  556            ownerPaysTransferFee,
 
  562        auto& strand = sp.second;
 
  567            JLOG(j.
trace()) << 
"failed to add path: ter: " << ter
 
  572        else if (strand.empty())
 
  574            JLOG(j.
trace()) << 
"toStrand failed";
 
  575            Throw<FlowException>(
 
  580            insert(std::move(strand));
 
  585        return {lastFailTer, std::move(result)};
 
 
  597    Issue const& strandDeliver_,
 
  600    bool ownerPaysTransferFee_,
 
  603    std::array<boost::container::flat_set<Issue>, 2>& seenDirectIssues_,
 
  604    boost::container::flat_set<Issue>& seenBookOuts_,
 
  609    , strandSrc(strandSrc_)
 
  610    , strandDst(strandDst_)
 
  611    , strandDeliver(strandDeliver_)
 
  612    , limitQuality(limitQuality_)
 
  613    , isFirst(strand_.empty())
 
  615    , ownerPaysTransferFee(ownerPaysTransferFee_)
 
  616    , offerCrossing(offerCrossing_)
 
  618    , strandSize(strand_.size())
 
  619    , prevStep(!strand_.empty() ? strand_.back().
get() : nullptr)
 
  620    , seenDirectIssues(seenDirectIssues_)
 
  621    , seenBookOuts(seenBookOuts_)
 
  622    , ammContext(ammContext_)
 
  623    , domainID(domainID_)
 
 
  628template <
class InAmt, 
class OutAmt>
 
  639    return (strand.size() == 2);
 
 
A generic endpoint for log messages.
 
Stream trace() const
Severity stream access functions.
 
Maintains AMM info per overall payment engine execution and individual iteration.
 
Floating point representation of amounts with high dynamic range.
 
int exponent() const noexcept
 
std::int64_t mantissa() const noexcept
 
A currency issued by an account.
 
Currency const & getCurrency() const
 
AccountID const & getAccountID() const
 
AccountID const & getIssuerID() const
 
std::vector< STPath >::size_type size() const
 
A step in a payment path.
 
T emplace_back(T... args)
 
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
 
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
 
AccountID const & noAccount()
A placeholder for empty accounts.
 
static std::pair< TER, std::unique_ptr< Step > > toStep(StrandContext const &ctx, STPathElement const *e1, STPathElement const *e2, Issue const &curIssue)
 
bool isConsistent(Book const &book)
 
bool isXRP(AccountID const &c)
 
AccountID const & xrpAccount()
Compute AccountID from public key.
 
std::pair< TER, std::unique_ptr< Step > > make_XRPEndpointStep(StrandContext const &ctx, AccountID const &acc)
 
template bool isDirectXrpToXrp< IOUAmount, IOUAmount >(Strand const &strand)
 
static bool isDefaultPath(STPath const &path)
 
template bool isDirectXrpToXrp< IOUAmount, XRPAmount >(Strand const &strand)
 
std::pair< TER, std::unique_ptr< Step > > make_BookStepXI(StrandContext const &ctx, Issue const &out)
 
std::pair< TER, std::unique_ptr< Step > > make_BookStepIX(StrandContext const &ctx, Issue const &in)
 
template bool isDirectXrpToXrp< XRPAmount, IOUAmount >(Strand const &strand)
 
Currency const & xrpCurrency()
XRP currency.
 
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
 
std::pair< TER, std::unique_ptr< Step > > make_BookStepII(StrandContext const &ctx, Issue const &in, Issue const &out)
 
std::pair< TER, std::unique_ptr< Step > > make_DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
 
bool isDirectXrpToXrp(Strand const &strand)
 
T get(Section const §ion, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
 
bool isTemMalformed(TER x) noexcept
 
static bool isXRPAccount(STPathElement const &pe)
 
std::pair< TER, std::vector< Strand > > toStrands(ReadView const &view, AccountID const &src, AccountID const &dst, Issue const &deliver, std::optional< Quality > const &limitQuality, std::optional< Issue > const &sendMax, STPathSet const &paths, bool addDefaultPath, bool ownerPaysTransferFee, OfferCrossing offerCrossing, AMMContext &ammContext, std::optional< uint256 > const &domainID, beast::Journal j)
Create a Strand for each specified path (including the default path, if indicated)
 
std::pair< TER, Strand > toStrand(ReadView const &view, AccountID const &src, AccountID const &dst, Issue const &deliver, std::optional< Quality > const &limitQuality, std::optional< Issue > const &sendMaxIssue, STPath const &path, bool ownerPaysTransferFee, OfferCrossing offerCrossing, AMMContext &ammContext, std::optional< uint256 > const &domainID, beast::Journal j)
Create a Strand for the specified path.
 
bool isDirectXrpToXrp< XRPAmount, XRPAmount >(Strand const &strand)
 
constexpr Number abs(Number x) noexcept
 
Context needed to build Strand Steps and for error checking.
 
bool const isFirst
true if Step is first in Strand
 
bool const isLast
true if Step is last in Strand
 
StrandContext(ReadView const &view_, std::vector< std::unique_ptr< Step > > const &strand_, AccountID const &strandSrc_, AccountID const &strandDst_, Issue const &strandDeliver_, std::optional< Quality > const &limitQuality_, bool isLast_, bool ownerPaysTransferFee_, OfferCrossing offerCrossing_, bool isDefaultPath_, std::array< boost::container::flat_set< Issue >, 2 > &seenDirectIssues_, boost::container::flat_set< Issue > &seenBookOuts_, AMMContext &ammContext_, std::optional< uint256 > const &domainID, beast::Journal j_)
StrandContext constructor.