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.