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)
100 <<
"Found offer/account payment step. Aborting payment strand.";
101 UNREACHABLE(
"ripple::toStep : offer/account payment payment strand");
108 "ripple::toStep : currency or issuer");
118 JLOG(j.info()) <<
"Found xrp/xrp offer payment step";
122 XRPL_ASSERT(e2->
isOffer(),
"ripple::toStep : is offer");
124 if (
isXRP(outCurrency))
138 Issue const& deliver,
142 bool ownerPaysTransferFee,
152 if ((sendMaxIssue && sendMaxIssue->account ==
noAccount()) ||
157 for (
auto const& pe : path)
159 auto const t = pe.getNodeType();
168 if (hasAccount && (hasIssuer || hasCurrency))
171 if (hasIssuer &&
isXRP(pe.getIssuerID()))
174 if (hasAccount &&
isXRP(pe.getAccountID()))
177 if (hasCurrency && hasIssuer &&
178 isXRP(pe.getCurrency()) !=
isXRP(pe.getIssuerID()))
181 if (hasIssuer && (pe.getIssuerID() ==
noAccount()))
184 if (hasAccount && (pe.getAccountID() ==
noAccount()))
188 Issue curIssue = [&] {
189 auto const& currency =
190 sendMaxIssue ? sendMaxIssue->currency : deliver.
currency;
193 return Issue{currency, src};
203 normPath.
reserve(4 + path.size());
208 if (sendMaxIssue && sendMaxIssue->account != src &&
209 (path.empty() || !path[0].isAccount() ||
210 path[0].getAccountID() != sendMaxIssue->account))
216 for (
auto const& i : path)
233 if (!((normPath.
back().isAccount() &&
234 normPath.
back().getAccountID() == deliver.
account) ||
240 if (!normPath.
back().isAccount() ||
241 normPath.
back().getAccountID() != dst)
247 if (normPath.
size() < 2)
250 auto const strandSrc = normPath.
front().getAccountID();
251 auto const strandDst = normPath.
back().getAccountID();
255 result.reserve(2 * normPath.
size());
264 boost::container::flat_set<Issue> seenBookOuts;
265 seenDirectIssues[0].reserve(normPath.
size());
266 seenDirectIssues[1].reserve(normPath.
size());
267 seenBookOuts.reserve(normPath.
size());
268 auto ctx = [&](
bool isLast =
false) {
277 ownerPaysTransferFee,
297 auto cur = &normPath[i];
298 auto const next = &normPath[i + 1];
300 if (cur->isAccount())
301 curIssue.
account = cur->getAccountID();
302 else if (cur->hasIssuer())
303 curIssue.
account = cur->getIssuerID();
305 if (cur->hasCurrency())
307 curIssue.
currency = cur->getCurrency();
312 if (cur->isAccount() && next->isAccount())
315 curIssue.
account != cur->getAccountID() &&
316 curIssue.
account != next->getAccountID())
318 JLOG(j.
trace()) <<
"Inserting implied account";
325 return {msr.first, Strand{}};
326 result.push_back(std::move(msr.second));
335 else if (cur->isAccount() && next->isOffer())
337 if (curIssue.
account != cur->getAccountID())
339 JLOG(j.
trace()) <<
"Inserting implied account before offer";
346 return {msr.first, Strand{}};
347 result.push_back(std::move(msr.second));
356 else if (cur->isOffer() && next->isAccount())
358 if (curIssue.
account != next->getAccountID() &&
359 !
isXRP(next->getAccountID()))
363 if (i != normPath.
size() - 2)
371 return {msr.first, Strand{}};
372 result.push_back(std::move(msr.second));
377 JLOG(j.
trace()) <<
"Inserting implied account after offer";
381 next->getAccountID(),
384 return {msr.first, Strand{}};
385 result.push_back(std::move(msr.second));
391 if (!next->isOffer() && next->hasCurrency() &&
392 next->getCurrency() != curIssue.
currency)
395 UNREACHABLE(
"ripple::toStrand : offer currency mismatch");
400 ctx( i == normPath.
size() - 2), cur, next, curIssue);
402 result.emplace_back(std::move(s.second));
405 JLOG(j.
debug()) <<
"toStep failed: " << s.first;
406 return {s.first, Strand{}};
410 auto checkStrand = [&]() ->
bool {
412 if (
auto r = s.directStepAccts())
414 if (
auto const r = s.bookStepBook())
416 Throw<FlowException>(
417 tefEXCEPTION,
"Step should be either a direct or book step");
424 sendMaxIssue ? sendMaxIssue->currency : deliver.
currency;
427 return Issue{currency, src};
430 for (
auto const& s : result)
432 auto const accts = stepAccts(*s);
433 if (accts.first != curAcc)
436 if (
auto const b = s->bookStepBook())
444 curIss.account = accts.second;
447 curAcc = accts.second;
451 if (curIss.currency != deliver.
currency)
453 if (curIss.account != deliver.
account && curIss.account != dst)
460 JLOG(j.
warn()) <<
"Flow check strand failed";
461 UNREACHABLE(
"ripple::toStrand : invalid strand");
473 Issue const& deliver,
478 bool ownerPaysTransferFee,
487 auto insert = [&](Strand s) {
488 bool const hasStrand =
505 ownerPaysTransferFee,
510 auto const ter = sp.first;
511 auto& strand = sp.second;
515 JLOG(j.
trace()) <<
"failed to add default path";
521 else if (strand.empty())
523 JLOG(j.
trace()) <<
"toStrand failed";
524 Throw<FlowException>(
529 insert(std::move(strand));
532 else if (paths.
empty())
534 JLOG(j.
debug()) <<
"Flow: Invalid transaction: No paths and direct "
535 "ripple not allowed.";
540 for (
auto const& p : paths)
550 ownerPaysTransferFee,
556 auto& strand = sp.second;
561 JLOG(j.
trace()) <<
"failed to add path: ter: " << ter
566 else if (strand.empty())
568 JLOG(j.
trace()) <<
"toStrand failed";
569 Throw<FlowException>(
574 insert(std::move(strand));
579 return {lastFailTer, std::move(result)};
591 Issue const& strandDeliver_,
594 bool ownerPaysTransferFee_,
597 std::array<boost::container::flat_set<Issue>, 2>& seenDirectIssues_,
598 boost::container::flat_set<Issue>& seenBookOuts_,
603 , strandSrc(strandSrc_)
604 , strandDst(strandDst_)
605 , strandDeliver(strandDeliver_)
606 , limitQuality(limitQuality_)
607 , isFirst(strand_.empty())
609 , ownerPaysTransferFee(ownerPaysTransferFee_)
610 , offerCrossing(offerCrossing_)
612 , strandSize(strand_.size())
613 , prevStep(!strand_.empty() ? strand_.back().
get() : nullptr)
614 , seenDirectIssues(seenDirectIssues_)
615 , seenBookOuts(seenBookOuts_)
616 , ammContext(ammContext_)
617 , domainID(domainID_)
622template <
class InAmt,
class OutAmt>
633 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.