20 #include <ripple/app/paths/impl/Steps.h>
21 #include <ripple/basics/contract.h>
22 #include <ripple/basics/IOUAmount.h>
23 #include <ripple/basics/XRPAmount.h>
24 #include <ripple/json/json_writer.h>
25 #include <ripple/ledger/ReadView.h>
26 #include <ripple/protocol/Feature.h>
37 double const ratTol = 0.001;
53 double const diff = std::abs (a - b);
54 auto const r =
diff /
std::max (std::abs (a), std::abs (b));
60 return expected == actual;
78 Issue const& curIssue)
102 <<
"Found offer/account payment step. Aborting payment strand.";
118 JLOG (j.info()) <<
"Found xrp/xrp offer payment step";
124 if (
isXRP (outCurrency))
138 Issue const& deliver,
139 boost::optional<Quality>
const& limitQuality,
140 boost::optional<Issue>
const& sendMaxIssue,
142 bool ownerPaysTransferFee,
150 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()))
189 auto const& currency =
191 if (
isXRP (currency))
193 return Issue{currency, src};
204 normPath.
reserve(4 + path.size());
209 if (sendMaxIssue && sendMaxIssue->account != src &&
210 (path.empty() || !path[0].isAccount() ||
211 path[0].getAccountID() != sendMaxIssue->account))
213 normPath.
emplace_back(sendMaxIssue->account, boost::none, boost::none);
216 for (
auto const& i : path)
248 if (normPath.
size() < 2)
256 result.reserve (2 * normPath.
size ());
265 boost::container::flat_set<Issue> seenBookOuts;
266 seenDirectIssues[0].reserve (normPath.
size());
267 seenDirectIssues[1].reserve (normPath.
size());
268 seenBookOuts.reserve (normPath.
size());
269 auto ctx = [&](
bool isLast =
false)
271 return StrandContext{view, result, strandSrc, strandDst, deliver,
272 limitQuality, isLast, ownerPaysTransferFee, offerCrossing,
285 boost::optional<STPathElement> impliedPE;
286 auto cur = &normPath[i];
287 auto const next = &normPath[i + 1];
289 if (cur->isAccount())
290 curIssue.
account = cur->getAccountID ();
291 else if (cur->hasIssuer())
292 curIssue.
account = cur->getIssuerID ();
294 if (cur->hasCurrency())
296 curIssue.
currency = cur->getCurrency ();
301 if (cur->isAccount() && next->isAccount())
304 curIssue.
account != cur->getAccountID () &&
305 curIssue.
account != next->getAccountID ())
307 JLOG (j.
trace()) <<
"Inserting implied account";
311 return {msr.first, Strand{}};
312 result.push_back (std::move (msr.second));
318 else if (cur->isAccount() && next->isOffer())
320 if (curIssue.
account != cur->getAccountID ())
322 JLOG (j.
trace()) <<
"Inserting implied account before offer";
326 return {msr.first, Strand{}};
327 result.push_back (std::move (msr.second));
333 else if (cur->isOffer() && next->isAccount())
335 if (curIssue.
account != next->getAccountID () &&
336 !
isXRP (next->getAccountID ()))
340 if (i != normPath.
size() - 2)
347 return {msr.first, Strand{}};
348 result.push_back(std::move(msr.second));
353 JLOG(j.
trace()) <<
"Inserting implied account after offer";
357 return {msr.first, Strand{}};
358 result.push_back(std::move(msr.second));
364 if (!next->isOffer() &&
365 next->hasCurrency() && next->getCurrency () != curIssue.
currency)
373 toStep (ctx ( i == normPath.
size () - 2), cur, next, curIssue);
375 result.emplace_back (std::move (s.second));
378 JLOG (j.
debug()) <<
"toStep failed: " << s.first;
379 return {s.first, Strand{}};
383 auto checkStrand = [&]() ->
bool {
385 if (
auto r = s.directStepAccts())
387 if (
auto const r = s.bookStepBook())
389 Throw<FlowException>(
390 tefEXCEPTION,
"Step should be either a direct or book step");
397 sendMaxIssue ? sendMaxIssue->currency : deliver.
currency;
400 return Issue{currency, src};
403 for (
auto const& s : result)
405 auto const accts = stepAccts(*s);
406 if (accts.first != curAcc)
409 if (
auto const b = s->bookStepBook())
417 curIss.account = accts.second;
420 curAcc = accts.second;
424 if (curIss.currency != deliver.
currency)
426 if (curIss.account != deliver.
account &&
427 curIss.account != dst)
434 JLOG (j.
warn()) <<
"Flow check strand failed";
447 Issue const& deliver,
448 boost::optional<Quality>
const& limitQuality,
449 boost::optional<Issue>
const& sendMax,
452 bool ownerPaysTransferFee,
459 auto insert = [&](Strand s)
461 bool const hasStrand =
470 auto sp =
toStrand (view, src, dst, deliver, limitQuality,
471 sendMax,
STPath(), ownerPaysTransferFee, offerCrossing, j);
472 auto const ter = sp.first;
473 auto& strand = sp.second;
477 JLOG (j.
trace()) <<
"failed to add default path";
482 else if (strand.empty ())
484 JLOG (j.
trace()) <<
"toStrand failed";
485 Throw<FlowException> (
tefEXCEPTION,
"toStrand returned tes & empty strand");
489 insert(std::move(strand));
492 else if (paths.
empty ())
495 <<
"Flow: Invalid transaction: No paths and direct ripple not allowed.";
500 for (
auto const& p : paths)
502 auto sp =
toStrand (view, src, dst, deliver,
503 limitQuality, sendMax, p, ownerPaysTransferFee, offerCrossing, j);
505 auto& strand = sp.second;
511 <<
"failed to add path: ter: " << ter
516 else if (strand.empty ())
518 JLOG (j.
trace()) <<
"toStrand failed";
519 Throw<FlowException> (
tefEXCEPTION,
"toStrand returned tes & empty strand");
523 insert(std::move(strand));
528 return {lastFailTer, std::move (result)};
540 Issue const& strandDeliver_,
541 boost::optional<Quality>
const& limitQuality_,
543 bool ownerPaysTransferFee_,
546 std::array<boost::container::flat_set<Issue>, 2>& seenDirectIssues_,
547 boost::container::flat_set<Issue>& seenBookOuts_,
550 , strandSrc (strandSrc_)
551 , strandDst (strandDst_)
552 , strandDeliver (strandDeliver_)
553 , limitQuality (limitQuality_)
554 , isFirst (strand_.empty ())
556 , ownerPaysTransferFee (ownerPaysTransferFee_)
557 , offerCrossing (offerCrossing_)
559 , strandSize (strand_.size ())
560 , prevStep (!strand_.empty () ? strand_.back ().
get ()
562 , seenDirectIssues(seenDirectIssues_)
563 , seenBookOuts(seenBookOuts_)
568 template<
class InAmt,
class OutAmt>
579 return (strand.size () == 2);