1#include <xrpl/basics/contract.h>
2#include <xrpl/json/json_writer.h>
3#include <xrpl/ledger/ReadView.h>
4#include <xrpl/protocol/IOUAmount.h>
5#include <xrpl/protocol/XRPAmount.h>
6#include <xrpl/tx/paths/detail/Steps.h>
16 double const ratTol = 0.001;
30 double const diff = std::abs(a - b);
31 auto const r = diff /
std::max(std::abs(a), std::abs(b));
38 return expected == actual;
54 Issue const& curIssue)
76 JLOG(j.error()) <<
"Found offer/account payment step. Aborting payment strand.";
77 UNREACHABLE(
"xrpl::toStep : offer/account payment payment strand");
85 "xrpl::toStep : currency or issuer");
95 JLOG(j.info()) <<
"Found xrp/xrp offer payment step";
99 XRPL_ASSERT(e2->
isOffer(),
"xrpl::toStep : is offer");
101 if (
isXRP(outCurrency))
115 Issue const& deliver,
119 bool ownerPaysTransferFee,
133 for (
auto const& pe : path)
135 auto const t = pe.getNodeType();
144 if (hasAccount && (hasIssuer || hasCurrency))
147 if (hasIssuer &&
isXRP(pe.getIssuerID()))
150 if (hasAccount &&
isXRP(pe.getAccountID()))
153 if (hasCurrency && hasIssuer &&
isXRP(pe.getCurrency()) !=
isXRP(pe.getIssuerID()))
156 if (hasIssuer && (pe.getIssuerID() ==
noAccount()))
159 if (hasAccount && (pe.getAccountID() ==
noAccount()))
163 Issue curIssue = [&] {
164 auto const& currency = sendMaxIssue ? sendMaxIssue->currency : deliver.
currency;
167 return Issue{currency, src};
181 if (sendMaxIssue && sendMaxIssue->account != src &&
182 (path.
empty() || !path[0].isAccount() ||
183 path[0].getAccountID() != sendMaxIssue->account))
188 for (
auto const& i : path)
203 if (!((normPath.
back().isAccount() && normPath.
back().getAccountID() == deliver.
account) ||
209 if (!normPath.
back().isAccount() || normPath.
back().getAccountID() != dst)
215 if (normPath.
size() < 2)
218 auto const strandSrc = normPath.
front().getAccountID();
219 auto const strandDst = normPath.
back().getAccountID();
223 result.reserve(2 * normPath.
size());
232 boost::container::flat_set<Issue> seenBookOuts;
233 seenDirectIssues[0].reserve(normPath.
size());
234 seenDirectIssues[1].reserve(normPath.
size());
235 seenBookOuts.reserve(normPath.
size());
236 auto ctx = [&](
bool isLast =
false) {
245 ownerPaysTransferFee,
265 auto cur = &normPath[i];
266 auto const next = &normPath[i + 1];
268 if (cur->isAccount())
270 curIssue.
account = cur->getAccountID();
272 else if (cur->hasIssuer())
274 curIssue.
account = cur->getIssuerID();
277 if (cur->hasCurrency())
279 curIssue.
currency = cur->getCurrency();
284 if (cur->isAccount() && next->isAccount())
287 curIssue.
account != next->getAccountID())
289 JLOG(j.
trace()) <<
"Inserting implied account";
293 return {msr.first, Strand{}};
294 result.push_back(std::move(msr.second));
300 else if (cur->isAccount() && next->isOffer())
302 if (curIssue.
account != cur->getAccountID())
304 JLOG(j.
trace()) <<
"Inserting implied account before offer";
308 return {msr.first, Strand{}};
309 result.push_back(std::move(msr.second));
315 else if (cur->isOffer() && next->isAccount())
317 if (curIssue.
account != next->getAccountID() && !
isXRP(next->getAccountID()))
321 if (i != normPath.
size() - 2)
327 return {msr.first, Strand{}};
328 result.push_back(std::move(msr.second));
332 JLOG(j.
trace()) <<
"Inserting implied account after offer";
336 return {msr.first, Strand{}};
337 result.push_back(std::move(msr.second));
343 if (!next->isOffer() && next->hasCurrency() && next->getCurrency() != curIssue.
currency)
347 UNREACHABLE(
"xrpl::toStrand : offer currency mismatch");
352 auto s =
toStep(ctx( i == normPath.
size() - 2), cur, next, curIssue);
355 result.emplace_back(std::move(s.second));
359 JLOG(j.
debug()) <<
"toStep failed: " << s.first;
360 return {s.first, Strand{}};
364 auto checkStrand = [&]() ->
bool {
366 if (
auto r = s.directStepAccts())
368 if (
auto const r = s.bookStepBook())
370 Throw<FlowException>(
tefEXCEPTION,
"Step should be either a direct or book step");
376 auto& currency = sendMaxIssue ? sendMaxIssue->currency : deliver.
currency;
379 return Issue{currency, src};
382 for (
auto const& s : result)
384 auto const accts = stepAccts(*s);
385 if (accts.first != curAcc)
388 if (
auto const b = s->bookStepBook())
396 curIss.account = accts.second;
399 curAcc = accts.second;
403 if (curIss.currency != deliver.
currency)
405 if (curIss.account != deliver.
account && curIss.account != dst)
413 JLOG(j.
warn()) <<
"Flow check strand failed";
414 UNREACHABLE(
"xrpl::toStrand : invalid strand");
427 Issue const& deliver,
432 bool ownerPaysTransferFee,
441 auto insert = [&](Strand s) {
458 ownerPaysTransferFee,
463 auto const ter = sp.first;
464 auto& strand = sp.second;
468 JLOG(j.
trace()) <<
"failed to add default path";
474 else if (strand.empty())
476 JLOG(j.
trace()) <<
"toStrand failed";
477 Throw<FlowException>(
tefEXCEPTION,
"toStrand returned tes & empty strand");
481 insert(std::move(strand));
484 else if (paths.
empty())
486 JLOG(j.
debug()) <<
"Flow: Invalid transaction: No paths and direct "
487 "ripple not allowed.";
492 for (
auto const& p : paths)
502 ownerPaysTransferFee,
508 auto& strand = sp.second;
513 JLOG(j.
trace()) <<
"failed to add path: ter: " << ter
518 else if (strand.empty())
520 JLOG(j.
trace()) <<
"toStrand failed";
521 Throw<FlowException>(
tefEXCEPTION,
"toStrand returned tes & empty strand");
525 insert(std::move(strand));
530 return {lastFailTer, std::move(result)};
542 Issue const& strandDeliver_,
545 bool ownerPaysTransferFee_,
548 std::array<boost::container::flat_set<Issue>, 2>& seenDirectIssues_,
549 boost::container::flat_set<Issue>& seenBookOuts_,
554 , strandSrc(strandSrc_)
555 , strandDst(strandDst_)
556 , strandDeliver(strandDeliver_)
557 , limitQuality(limitQuality_)
558 , isFirst(strand_.empty())
560 , ownerPaysTransferFee(ownerPaysTransferFee_)
561 , offerCrossing(offerCrossing_)
563 , strandSize(strand_.size())
564 , prevStep(!strand_.empty() ? strand_.back().
get() : nullptr)
565 , seenDirectIssues(seenDirectIssues_)
566 , seenBookOuts(seenBookOuts_)
567 , ammContext(ammContext_)
568 , domainID(domainID_)
573template <
class InAmt,
class OutAmt>
584 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.
mantissa_type mantissa() const noexcept
exponent_type exponent() const noexcept
A currency issued by an account.
AccountID const & getAccountID() const
Currency const & getCurrency() const
AccountID const & getIssuerID() const
std::vector< STPath >::size_type size() const
std::vector< STPathElement >::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.
bool isConsistent(Book const &book)
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
bool isDirectXrpToXrp(Strand const &strand)
std::pair< TER, std::vector< Strand > > toStrands(ReadView const &sb, 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)
bool isXRP(AccountID const &c)
T get(Section const §ion, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
bool isDirectXrpToXrp< XRPAmount, XRPAmount >(Strand const &strand)
std::pair< TER, Strand > toStrand(ReadView const &sb, 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.
std::pair< TER, std::unique_ptr< Step > > make_XRPEndpointStep(StrandContext const &ctx, AccountID const &acc)
std::pair< TER, std::unique_ptr< Step > > make_BookStepXI(StrandContext const &ctx, Issue const &out)
template bool isDirectXrpToXrp< IOUAmount, IOUAmount >(Strand const &strand)
Currency const & xrpCurrency()
XRP currency.
static bool isDefaultPath(STPath const &path)
static std::pair< TER, std::unique_ptr< Step > > toStep(StrandContext const &ctx, STPathElement const *e1, STPathElement const *e2, Issue const &curIssue)
std::pair< TER, std::unique_ptr< Step > > make_DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
std::pair< TER, std::unique_ptr< Step > > make_BookStepIX(StrandContext const &ctx, Issue const &in)
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
constexpr Number abs(Number x) noexcept
AccountID const & noAccount()
A placeholder for empty accounts.
template bool isDirectXrpToXrp< XRPAmount, IOUAmount >(Strand const &strand)
bool isTesSuccess(TER x) noexcept
std::pair< TER, std::unique_ptr< Step > > make_BookStepII(StrandContext const &ctx, Issue const &in, Issue const &out)
AccountID const & xrpAccount()
Compute AccountID from public key.
static bool isXRPAccount(STPathElement const &pe)
template bool isDirectXrpToXrp< IOUAmount, XRPAmount >(Strand const &strand)
bool isTemMalformed(TER x) noexcept
Context needed to build Strand Steps and for error checking.
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.
bool const isFirst
true if Step is first in Strand
bool const isLast
true if Step is last in Strand