20 #include <ripple/app/paths/impl/Steps.h>
21 #include <ripple/basics/IOUAmount.h>
22 #include <ripple/basics/XRPAmount.h>
23 #include <ripple/basics/contract.h>
24 #include <ripple/json/json_writer.h>
25 #include <ripple/ledger/ReadView.h>
26 #include <ripple/protocol/Feature.h>
38 double const ratTol = 0.001;
54 double const diff = std::abs(a - b);
55 auto const r = diff /
std::max(std::abs(a), std::abs(b));
62 return expected == actual;
78 Issue const& curIssue)
102 <<
"Found offer/account payment step. Aborting payment strand.";
119 JLOG(j.info()) <<
"Found xrp/xrp offer payment step";
125 if (
isXRP(outCurrency))
139 Issue const& deliver,
143 bool ownerPaysTransferFee,
151 if ((sendMaxIssue && sendMaxIssue->account ==
noAccount()) ||
156 for (
auto const& pe : path)
158 auto const t = pe.getNodeType();
167 if (hasAccount && (hasIssuer || hasCurrency))
170 if (hasIssuer &&
isXRP(pe.getIssuerID()))
173 if (hasAccount &&
isXRP(pe.getAccountID()))
176 if (hasCurrency && hasIssuer &&
177 isXRP(pe.getCurrency()) !=
isXRP(pe.getIssuerID()))
180 if (hasIssuer && (pe.getIssuerID() ==
noAccount()))
183 if (hasAccount && (pe.getAccountID() ==
noAccount()))
187 Issue curIssue = [&] {
188 auto const& currency =
189 sendMaxIssue ? sendMaxIssue->currency : deliver.
currency;
192 return Issue{currency, src};
202 normPath.
reserve(4 + path.size());
207 if (sendMaxIssue && sendMaxIssue->account != src &&
208 (path.empty() || !path[0].isAccount() ||
209 path[0].getAccountID() != sendMaxIssue->account))
212 sendMaxIssue->account, std::nullopt, std::nullopt);
215 for (
auto const& i : path)
246 if (normPath.
size() < 2)
254 result.reserve(2 * normPath.
size());
263 boost::container::flat_set<Issue> seenBookOuts;
264 seenDirectIssues[0].reserve(normPath.
size());
265 seenDirectIssues[1].reserve(normPath.
size());
266 seenBookOuts.reserve(normPath.
size());
267 auto ctx = [&](
bool isLast =
false) {
276 ownerPaysTransferFee,
294 auto cur = &normPath[i];
295 auto const next = &normPath[i + 1];
297 if (cur->isAccount())
298 curIssue.
account = cur->getAccountID();
299 else if (cur->hasIssuer())
300 curIssue.
account = cur->getIssuerID();
302 if (cur->hasCurrency())
304 curIssue.
currency = cur->getCurrency();
309 if (cur->isAccount() && next->isAccount())
312 curIssue.
account != cur->getAccountID() &&
313 curIssue.
account != next->getAccountID())
315 JLOG(j.
trace()) <<
"Inserting implied account";
322 return {msr.first, Strand{}};
323 result.push_back(std::move(msr.second));
332 else if (cur->isAccount() && next->isOffer())
334 if (curIssue.
account != cur->getAccountID())
336 JLOG(j.
trace()) <<
"Inserting implied account before offer";
343 return {msr.first, Strand{}};
344 result.push_back(std::move(msr.second));
353 else if (cur->isOffer() && next->isAccount())
355 if (curIssue.
account != next->getAccountID() &&
356 !
isXRP(next->getAccountID()))
360 if (i != normPath.
size() - 2)
368 return {msr.first, Strand{}};
369 result.push_back(std::move(msr.second));
374 JLOG(j.
trace()) <<
"Inserting implied account after offer";
378 next->getAccountID(),
381 return {msr.first, Strand{}};
382 result.push_back(std::move(msr.second));
388 if (!next->isOffer() && next->hasCurrency() &&
389 next->getCurrency() != curIssue.
currency)
397 ctx( i == normPath.
size() - 2), cur, next, curIssue);
399 result.emplace_back(std::move(s.second));
402 JLOG(j.
debug()) <<
"toStep failed: " << s.first;
403 return {s.first, Strand{}};
407 auto checkStrand = [&]() ->
bool {
409 if (
auto r = s.directStepAccts())
411 if (
auto const r = s.bookStepBook())
413 Throw<FlowException>(
414 tefEXCEPTION,
"Step should be either a direct or book step");
421 sendMaxIssue ? sendMaxIssue->currency : deliver.
currency;
424 return Issue{currency, src};
427 for (
auto const& s : result)
429 auto const accts = stepAccts(*s);
430 if (accts.first != curAcc)
433 if (
auto const b = s->bookStepBook())
441 curIss.account = accts.second;
444 curAcc = accts.second;
448 if (curIss.currency != deliver.
currency)
450 if (curIss.account != deliver.
account && curIss.account != dst)
457 JLOG(j.
warn()) <<
"Flow check strand failed";
470 Issue const& deliver,
475 bool ownerPaysTransferFee,
482 auto insert = [&](Strand s) {
483 bool const hasStrand =
500 ownerPaysTransferFee,
503 auto const ter = sp.first;
504 auto& strand = sp.second;
508 JLOG(j.
trace()) <<
"failed to add default path";
514 else if (strand.empty())
516 JLOG(j.
trace()) <<
"toStrand failed";
517 Throw<FlowException>(
522 insert(std::move(strand));
525 else if (paths.
empty())
527 JLOG(j.
debug()) <<
"Flow: Invalid transaction: No paths and direct "
528 "ripple not allowed.";
533 for (
auto const& p : paths)
543 ownerPaysTransferFee,
547 auto& strand = sp.second;
552 JLOG(j.
trace()) <<
"failed to add path: ter: " << ter
557 else if (strand.empty())
559 JLOG(j.
trace()) <<
"toStrand failed";
560 Throw<FlowException>(
565 insert(std::move(strand));
570 return {lastFailTer, std::move(result)};
582 Issue const& strandDeliver_,
585 bool ownerPaysTransferFee_,
588 std::array<boost::container::flat_set<Issue>, 2>& seenDirectIssues_,
589 boost::container::flat_set<Issue>& seenBookOuts_,
592 , strandSrc(strandSrc_)
593 , strandDst(strandDst_)
594 , strandDeliver(strandDeliver_)
595 , limitQuality(limitQuality_)
596 , isFirst(strand_.empty())
598 , ownerPaysTransferFee(ownerPaysTransferFee_)
599 , offerCrossing(offerCrossing_)
601 , strandSize(strand_.size())
602 , prevStep(!strand_.empty() ? strand_.back().
get() : nullptr)
603 , seenDirectIssues(seenDirectIssues_)
604 , seenBookOuts(seenBookOuts_)
609 template <
class InAmt,
class OutAmt>
620 return (strand.size() == 2);
Context needed to build Strand Steps and for error checking.
int exponent() const noexcept
A currency issued by an account.
bool isConsistent(Book const &book)
Stream trace() const
Severity stream access functions.
std::pair< TER, std::unique_ptr< Step > > make_BookStepXI(StrandContext const &ctx, Issue const &out)
std::pair< TER, std::unique_ptr< Step > > make_BookStepII(StrandContext const &ctx, Issue const &in, Issue const &out)
Currency const & getCurrency() const
static bool isDefaultPath(STPath const &path)
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
template bool isDirectXrpToXrp< IOUAmount, IOUAmount >(Strand const &strand)
std::pair< TER, std::unique_ptr< Step > > make_DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
Floating point representation of amounts with high dynamic range.
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, bool offerCrossing, beast::Journal j)
Create a Strand for the specified path.
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, bool offerCrossing, beast::Journal j)
Create a Strand for each specified path (including the default path, if indicated)
Integers of any length that is a multiple of 32-bits.
static std::pair< TER, std::unique_ptr< Step > > toStep(StrandContext const &ctx, STPathElement const *e1, STPathElement const *e2, Issue const &curIssue)
std::vector< STPath >::size_type size() const
std::pair< TER, std::unique_ptr< Step > > make_XRPEndpointStep(StrandContext const &ctx, AccountID const &acc)
A step in a payment path.
AccountID const & xrpAccount()
Compute AccountID from public key.
bool isXRP(AccountID const &c)
const bool isLast
true if Step is last in Strand
A generic endpoint for log messages.
bool isDirectXrpToXrp(Strand const &strand)
AccountID const & getIssuerID() const
bool isDirectXrpToXrp< XRPAmount, XRPAmount >(Strand const &strand)
T emplace_back(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::int64_t mantissa() const noexcept
constexpr Number abs(Number x) noexcept
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_, bool offerCrossing_, bool isDefaultPath_, std::array< boost::container::flat_set< Issue >, 2 > &seenDirectIssues_, boost::container::flat_set< Issue > &seenBookOuts_, beast::Journal j_)
StrandContext constructor.
static bool isXRPAccount(STPathElement const &pe)
const bool isFirst
true if Step is first in Strand
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
template bool isDirectXrpToXrp< IOUAmount, XRPAmount >(Strand const &strand)
std::pair< TER, std::unique_ptr< Step > > make_BookStepIX(StrandContext const &ctx, Issue const &in)
template bool isDirectXrpToXrp< XRPAmount, IOUAmount >(Strand const &strand)
AccountID const & noAccount()
A placeholder for empty accounts.
Currency const & xrpCurrency()
XRP currency.
bool isTemMalformed(TER x)
T & get(EitherAmount &amt)
AccountID const & getAccountID() const