xrpld
Loading...
Searching...
No Matches
PaySteps.cpp
1#include <xrpl/basics/Log.h>
2#include <xrpl/basics/Number.h>
3#include <xrpl/basics/base_uint.h>
4#include <xrpl/basics/contract.h>
5#include <xrpl/beast/utility/Zero.h>
6#include <xrpl/beast/utility/instrumentation.h>
7#include <xrpl/json/json_writer.h>
8#include <xrpl/ledger/ReadView.h>
9#include <xrpl/protocol/AccountID.h>
10#include <xrpl/protocol/Asset.h>
11#include <xrpl/protocol/IOUAmount.h>
12#include <xrpl/protocol/Issue.h>
13#include <xrpl/protocol/MPTIssue.h>
14#include <xrpl/protocol/Quality.h>
15#include <xrpl/protocol/STPathSet.h>
16#include <xrpl/protocol/TER.h>
17#include <xrpl/protocol/UintTypes.h>
18#include <xrpl/tx/paths/detail/Steps.h>
19
20#include <boost/container/flat_set.hpp>
21
22#include <algorithm>
23#include <array>
24#include <cstddef>
25#include <cstdlib>
26#include <memory>
27#include <optional>
28#include <ranges>
29#include <utility>
30#include <vector>
31
32namespace xrpl {
33
34// Check equal with tolerance
35bool
36checkNear(IOUAmount const& expected, IOUAmount const& actual)
37{
38 double const ratTol = 0.001;
39 if (abs(expected.exponent() - actual.exponent()) > 1)
40 return false;
41
42 if (actual.exponent() < -20)
43 return true;
44
45 auto const a =
46 (expected.exponent() < actual.exponent()) ? expected.mantissa() / 10 : expected.mantissa();
47 auto const b =
48 (actual.exponent() < expected.exponent()) ? actual.mantissa() / 10 : actual.mantissa();
49 if (a == b)
50 return true;
51
52 double const diff = std::abs(a - b);
53 auto const r = diff / std::max(std::abs(a), std::abs(b));
54 return r <= ratTol;
55};
56
57static bool
59{
61 return false;
62 return isXRP(pe.getAccountID());
63};
64
67 StrandContext const& ctx,
68 STPathElement const* e1,
69 STPathElement const* e2,
70 Asset const& curAsset)
71{
72 auto& j = ctx.j;
73
74 if (ctx.isFirst && e1->isAccount() &&
75 ((e1->getNodeType() & STPathElement::TypeCurrency) != 0u) && e1->getPathAsset().isXRP())
76 {
77 return makeXrpEndpointStep(ctx, e1->getAccountID());
78 }
79
80 if (ctx.isLast && isXRPAccount(*e1) && e2->isAccount())
81 return makeXrpEndpointStep(ctx, e2->getAccountID());
82
83 // MPTEndpointStep is created in following cases:
84 // 1 Direct payment between an issuer and a holder
85 // e1 is issuer and e2 is holder or vise versa
86 // There is only one step in this case: holder->issuer or
87 // issuer->holder
88 // 2 Direct payment between the holders
89 // e1 is issuer and e2 is holder or vise versa
90 // There are two steps in this case: holder->issuer->holder1
91 // 3 Cross-token payment with Amount or SendMax or both MPT
92 // If destination is an issuer then the last step is BookStep,
93 // otherwise the last step is MPTEndpointStep where e1 is
94 // the issuer and e2 is the holder.
95 // In all cases MPTEndpointStep is always first or last step,
96 // e1/e2 are always account types, and curAsset is always MPT.
97
98 if (e1->isAccount() && e2->isAccount())
99 {
100 return curAsset.visit(
101 [&](MPTIssue const& issue) {
102 return makeMptEndpointStep(
103 ctx, e1->getAccountID(), e2->getAccountID(), issue.getMptID());
104 },
105 [&](Issue const& issue) {
106 return makeDirectStepI(ctx, e1->getAccountID(), e2->getAccountID(), issue.currency);
107 });
108 }
109
110 if (e1->isOffer() && e2->isAccount())
111 {
112 // LCOV_EXCL_START
113 // should already be taken care of
114 JLOG(j.error()) << "Found offer/account payment step. Aborting payment strand.";
115 UNREACHABLE("xrpl::toStep : offer/account payment payment strand");
117 // LCOV_EXCL_STOP
118 }
119
120 XRPL_ASSERT(
123 "xrpl::toStep : currency or issuer");
124 PathAsset const outAsset =
125 ((e2->getNodeType() & STPathElement::TypeAsset) != 0u) ? e2->getPathAsset() : curAsset;
126 auto const outIssuer = ((e2->getNodeType() & STPathElement::TypeIssuer) != 0u)
127 ? e2->getIssuerID()
128 : curAsset.getIssuer();
129
130 if (isXRP(curAsset) && outAsset.isXRP())
131 {
132 JLOG(j.info()) << "Found xrp/xrp offer payment step";
134 }
135
136 XRPL_ASSERT(e2->isOffer(), "xrpl::toStep : is offer");
137
138 if (outAsset.isXRP())
139 {
140 return curAsset.visit(
141 [&](MPTIssue const& issue) { return makeBookStepMx(ctx, issue); },
142 [&](Issue const& issue) { return makeBookStepIx(ctx, issue); });
143 }
144
145 if (isXRP(curAsset))
146 {
147 return outAsset.visit(
148 [&](MPTID const& mpt) { return makeBookStepXm(ctx, mpt); },
149 [&](Currency const& currency) { return makeBookStepXi(ctx, {currency, outIssuer}); });
150 }
151
152 return curAsset.visit(
153 [&](MPTIssue const& issue) {
154 return outAsset.visit(
155 [&](Currency const& currency) {
156 return makeBookStepMi(ctx, issue, {currency, outIssuer});
157 },
158 [&](MPTID const& mpt) { return makeBookStepMm(ctx, issue, mpt); });
159 },
160 [&](Issue const& issue) {
161 return outAsset.visit(
162 [&](MPTID const& mpt) { return makeBookStepIm(ctx, issue, mpt); },
163 [&](Currency const& currency) {
164 return makeBookStepIi(ctx, issue, {currency, outIssuer});
165 });
166 });
167}
168
171 ReadView const& view,
172 AccountID const& src,
173 AccountID const& dst,
174 Asset const& deliver,
175 std::optional<Quality> const& limitQuality,
176 std::optional<Asset> const& sendMaxAsset,
177 STPath const& path,
178 bool ownerPaysTransferFee,
179 OfferCrossing offerCrossing,
180 AMMContext& ammContext,
181 std::optional<uint256> const& domainID,
183{
184 if (isXRP(src) || isXRP(dst) || !isConsistent(deliver) ||
185 (sendMaxAsset && !isConsistent(*sendMaxAsset)))
186 return {temBAD_PATH, Strand{}};
187
188 if ((sendMaxAsset && sendMaxAsset->getIssuer() == noAccount()) || (src == noAccount()) ||
189 (dst == noAccount()) || (deliver.getIssuer() == noAccount()))
190 return {temBAD_PATH, Strand{}};
191
192 if ((deliver.holds<MPTIssue>() && deliver.getIssuer() == beast::kZero) ||
193 (sendMaxAsset && sendMaxAsset->holds<MPTIssue>() &&
194 sendMaxAsset->getIssuer() == beast::kZero))
195 return {temBAD_PATH, Strand{}};
196
197 for (std::size_t i = 0; i < path.size(); ++i)
198 {
199 auto const& pe = path[i];
200 auto const t = pe.getNodeType();
201
202 if (((t & ~STPathElement::TypeAll) != 0u) || (t == 0u))
203 return {temBAD_PATH, Strand{}};
204
205 bool const hasAccount = (t & STPathElement::TypeAccount) != 0u;
206 bool const hasIssuer = (t & STPathElement::TypeIssuer) != 0u;
207 bool const hasCurrency = (t & STPathElement::TypeCurrency) != 0u;
208 bool const hasMPT = (t & STPathElement::TypeMpt) != 0u;
209 bool const hasAsset = (t & STPathElement::TypeAsset) != 0u;
210
211 if (hasAccount && (hasIssuer || hasCurrency))
212 return {temBAD_PATH, Strand{}};
213
214 if (hasIssuer && isXRP(pe.getIssuerID()))
215 return {temBAD_PATH, Strand{}};
216
217 if (hasAccount && isXRP(pe.getAccountID()))
218 return {temBAD_PATH, Strand{}};
219
220 if (hasCurrency && hasIssuer && isXRP(pe.getCurrency()) != isXRP(pe.getIssuerID()))
221 return {temBAD_PATH, Strand{}};
222
223 if (hasIssuer && (pe.getIssuerID() == noAccount()))
224 return {temBAD_PATH, Strand{}};
225
226 if (hasAccount && (pe.getAccountID() == noAccount()))
227 return {temBAD_PATH, Strand{}};
228
229 if (hasMPT && (hasCurrency || hasAccount))
230 return {temBAD_PATH, Strand{}};
231
232 if (hasMPT && hasIssuer && (pe.getIssuerID() != getMPTIssuer(pe.getMPTID())))
233 return {temBAD_PATH, Strand{}};
234
235 // No rippling if MPT
236 if (i > 0 && path[i - 1].hasMPT() && (hasAccount || (hasIssuer && !hasAsset)))
237 return {temBAD_PATH, Strand{}};
238 }
239
240 Asset curAsset = [&]() -> Asset {
241 auto const& asset = sendMaxAsset ? *sendMaxAsset : deliver;
242 return asset.visit(
243 [&](MPTIssue const& issue) -> Asset { return asset; },
244 [&](Issue const& issue) -> Asset {
245 if (isXRP(asset))
246 return xrpIssue();
247 // First step ripples from the source to the issuer.
248 return Issue{issue.currency, src};
249 });
250 }();
251
252 // Currency or MPT
253 auto hasAsset = [](STPathElement const pe) {
254 return pe.getNodeType() & STPathElement::TypeAsset;
255 };
256
258 // reserve enough for the path, the implied source, destination,
259 // sendmax and deliver.
260 normPath.reserve(4 + path.size());
261 {
262 // The first step of a path is always implied to be the sender of the
263 // transaction, as defined by the transaction's Account field. The Asset
264 // is either SendMax or Deliver.
265 auto const t = [&]() {
267 return curAsset.visit(
268 [&](MPTIssue const&) { return t | STPathElement::TypeMpt; },
269 [&](Issue const&) { return t | STPathElement::TypeCurrency; });
270 }();
271 // If MPT then the issuer is the actual issuer, it is never the source
272 // account.
273 normPath.emplace_back(t, src, curAsset, curAsset.getIssuer());
274
275 // If transaction includes SendMax with the issuer, which is not
276 // the sender of the transaction, that issuer is implied to be
277 // the second step of the path. Unless the path starts at an address,
278 // which is the issuer of SendMax.
279 if (sendMaxAsset && sendMaxAsset->getIssuer() != src &&
280 (path.empty() || !path[0].isAccount() ||
281 path[0].getAccountID() != sendMaxAsset->getIssuer()))
282 {
283 normPath.emplace_back(sendMaxAsset->getIssuer(), std::nullopt, std::nullopt);
284 }
285
286 for (auto const& i : path)
287 normPath.push_back(i);
288
289 {
290 // Note that for offer crossing (only) we do use an offer book
291 // even if all that is changing is the Issue.account. Note
292 // that MPTIssue can't change the account.
293 STPathElement const& lastAsset =
294 *std::ranges::find_if(std::ranges::reverse_view(normPath), hasAsset);
295 if (lastAsset.getPathAsset() != deliver ||
296 (offerCrossing != OfferCrossing::No &&
297 lastAsset.getIssuerID() != deliver.getIssuer()))
298 {
299 normPath.emplace_back(std::nullopt, deliver, deliver.getIssuer());
300 }
301 }
302
303 // If the Amount field of the transaction includes an issuer that is not
304 // the same as the Destination of the transaction, that issuer is
305 // implied to be the second-to-last step of the path. If normPath.back
306 // is an offer, which sells MPT then the added path element account is
307 // the MPT's issuer.
308 if (!((normPath.back().isAccount() &&
309 normPath.back().getAccountID() == deliver.getIssuer()) ||
310 (dst == deliver.getIssuer())))
311 {
312 normPath.emplace_back(deliver.getIssuer(), std::nullopt, std::nullopt);
313 }
314
315 // Last step of a path is always implied to be the receiver of a
316 // transaction, as defined by the transaction's Destination field.
317 if (!normPath.back().isAccount() || normPath.back().getAccountID() != dst)
318 {
319 normPath.emplace_back(dst, std::nullopt, std::nullopt);
320 }
321 }
322
323 if (normPath.size() < 2)
324 return {temBAD_PATH, Strand{}};
325
326 auto const strandSrc = normPath.front().getAccountID();
327 auto const strandDst = normPath.back().getAccountID();
328 bool const isDefaultPath = path.empty();
329
330 Strand result;
331 result.reserve(2 * normPath.size());
332
333 /* A strand may not include the same account node more than once
334 in the same currency. In a direct step, an account will show up
335 at most twice: once as a src and once as a dst (hence the two element
336 array). The strandSrc and strandDst will only show up once each.
337 */
339 // A strand may not include the same offer book more than once
340 boost::container::flat_set<Asset> seenBookOuts;
341 seenDirectAssets[0].reserve(normPath.size());
342 seenDirectAssets[1].reserve(normPath.size());
343 seenBookOuts.reserve(normPath.size());
344 auto ctx = [&](bool isLast = false) {
345 return StrandContext{
346 view,
347 result,
348 strandSrc,
349 strandDst,
350 deliver,
351 limitQuality,
352 isLast,
353 ownerPaysTransferFee,
354 offerCrossing,
356 seenDirectAssets,
357 seenBookOuts,
358 ammContext,
359 domainID,
360 j};
361 };
362
363 for (std::size_t i = 0; i < normPath.size() - 1; ++i)
364 {
365 /* Iterate through the path elements considering them in pairs.
366 The first element of the pair is `cur` and the second element is
367 `next`. When an offer is one of the pairs, the step created will be
368 for `next`. This means when `cur` is an offer and `next` is an
369 account then no step is created, as a step has already been created
370 for that offer.
371 */
373 auto cur = &normPath[i];
374 auto const next = &normPath[i + 1];
375
376 // Switch over from MPT to Currency. In this case curAsset account
377 // can be different from the issuer. If cur is MPT then curAsset
378 // is just set to MPTID.
379 if (curAsset.holds<MPTIssue>() && cur->hasCurrency())
380 {
381 curAsset = Issue{};
382 }
383
384 // Can only update the account for Issue since MPTIssue's account
385 // is immutable as it is part of MPTID.
386 curAsset.visit(
387 [&](Issue const&) {
388 if (cur->isAccount())
389 {
390 curAsset.get<Issue>().account = cur->getAccountID();
391 }
392 else if (cur->hasIssuer())
393 {
394 curAsset.get<Issue>().account = cur->getIssuerID();
395 }
396 },
397 [](MPTIssue const&) {});
398
399 if (cur->hasCurrency())
400 {
401 curAsset = Issue{cur->getCurrency(), curAsset.getIssuer()};
402 if (isXRP(curAsset))
403 curAsset.get<Issue>().account = xrpAccount();
404 }
405 else if (cur->hasMPT())
406 {
407 curAsset = cur->getPathAsset().get<MPTID>();
408 }
409
410 using ImpliedStepRet = std::pair<TER, std::unique_ptr<Step>>;
411 auto getImpliedStep =
412 [&](AccountID const& src, AccountID const& dst, Asset const& asset) -> ImpliedStepRet {
413 return asset.visit(
414 [&](MPTIssue const&) -> ImpliedStepRet {
415 JLOG(j.error()) << "MPT is invalid with rippling";
416 return {temBAD_PATH, nullptr};
417 },
418 [&](Issue const& issue) -> ImpliedStepRet {
419 return makeDirectStepI(ctx(), src, dst, issue.currency);
420 });
421 };
422
423 if (cur->isAccount() && next->isAccount())
424 {
425 // This block doesn't execute
426 // since curAsset's account is set to cur's account above.
427 // It should not execute for MPT either because MPT rippling
428 // is invalid. Should this block be removed/amendment excluded?
429 if (!isXRP(curAsset) && curAsset.getIssuer() != cur->getAccountID() &&
430 curAsset.getIssuer() != next->getAccountID())
431 {
432 JLOG(j.trace()) << "Inserting implied account";
433 auto msr = getImpliedStep(cur->getAccountID(), curAsset.getIssuer(), curAsset);
434 if (!isTesSuccess(msr.first))
435 return {msr.first, Strand{}};
436 result.push_back(std::move(msr.second));
437 impliedPE.emplace(
439 cur = &*impliedPE;
440 }
441 }
442 else if (cur->isAccount() && next->isOffer())
443 {
444 // Same as above, this block doesn't execute.
445 if (curAsset.getIssuer() != cur->getAccountID())
446 {
447 JLOG(j.trace()) << "Inserting implied account before offer";
448 auto msr = getImpliedStep(cur->getAccountID(), curAsset.getIssuer(), curAsset);
449 if (!isTesSuccess(msr.first))
450 return {msr.first, Strand{}};
451 result.push_back(std::move(msr.second));
452 impliedPE.emplace(
454 cur = &*impliedPE;
455 }
456 }
457 else if (cur->isOffer() && next->isAccount())
458 {
459 // If the offer sells MPT, then next's account is always the issuer.
460 // See how normPath step is added for second-to-last or last
461 // step. Therefore, this block never executes if MPT.
462 if (curAsset.getIssuer() != next->getAccountID() && !isXRP(next->getAccountID()))
463 {
464 if (isXRP(curAsset))
465 {
466 if (i != normPath.size() - 2)
467 return {temBAD_PATH, Strand{}};
468
469 // Last step. insert xrp endpoint step
470 auto msr = makeXrpEndpointStep(ctx(), next->getAccountID());
471 if (!isTesSuccess(msr.first))
472 return {msr.first, Strand{}};
473 result.push_back(std::move(msr.second));
474 }
475 else
476 {
477 JLOG(j.trace()) << "Inserting implied account after offer";
478 auto msr = getImpliedStep(curAsset.getIssuer(), next->getAccountID(), curAsset);
479 if (!isTesSuccess(msr.first))
480 return {msr.first, Strand{}};
481 result.push_back(std::move(msr.second));
482 }
483 }
484 continue;
485 }
486
487 if (!next->isOffer() && next->hasAsset() && next->getPathAsset() != curAsset)
488 {
489 // Should never happen
490 // LCOV_EXCL_START
491 UNREACHABLE("xrpl::toStrand : offer currency mismatch");
492 return {temBAD_PATH, Strand{}};
493 // LCOV_EXCL_STOP
494 }
495
496 auto s = toStep(ctx(/*isLast*/ i == normPath.size() - 2), cur, next, curAsset);
497 if (isTesSuccess(s.first))
498 {
499 result.emplace_back(std::move(s.second));
500 }
501 else
502 {
503 JLOG(j.debug()) << "toStep failed: " << s.first;
504 return {s.first, Strand{}};
505 }
506 }
507
508 auto checkStrand = [&]() -> bool {
509 auto stepAccts = [](Step const& s) -> std::pair<AccountID, AccountID> {
510 if (auto r = s.directStepAccts())
511 return *r;
512 if (auto const r = s.bookStepBook())
513 return std::make_pair(r->in.getIssuer(), r->out.getIssuer());
514 Throw<FlowException>(tefEXCEPTION, "Step should be either a direct or book step");
516 };
517
518 auto curAcc = src;
519 auto curAsset = [&]() -> Asset {
520 auto const& asset = sendMaxAsset ? *sendMaxAsset : deliver;
521 return asset.visit(
522 [&](MPTIssue const&) -> Asset { return asset; },
523 [&](Issue const& issue) -> Asset {
524 if (isXRP(asset))
525 return xrpIssue();
526 return Issue{issue.currency, src};
527 });
528 }();
529
530 for (auto const& s : result)
531 {
532 auto const accts = stepAccts(*s);
533 if (accts.first != curAcc)
534 return false;
535
536 if (auto const b = s->bookStepBook())
537 {
538 if (curAsset != b->in)
539 return false;
540 curAsset = b->out;
541 }
542 else if (curAsset.holds<Issue>())
543 {
544 curAsset.get<Issue>().account = accts.second;
545 }
546
547 curAcc = accts.second;
548 }
549 if (curAcc != dst)
550 return false;
551 if (curAsset.holds<Issue>() != deliver.holds<Issue>() ||
552 (curAsset.holds<Issue>() &&
553 curAsset.get<Issue>().currency != deliver.get<Issue>().currency) ||
554 (curAsset.holds<MPTIssue>() && curAsset.get<MPTIssue>() != deliver.get<MPTIssue>()))
555 return false;
556 if (curAsset.getIssuer() != deliver.getIssuer() && curAsset.getIssuer() != dst)
557 return false;
558 return true;
559 };
560
561 if (!checkStrand())
562 {
563 // LCOV_EXCL_START
564 JLOG(j.warn()) << "Flow check strand failed";
565 UNREACHABLE("xrpl::toStrand : invalid strand");
566 return {temBAD_PATH, Strand{}};
567 // LCOV_EXCL_STOP
568 }
569
570 return {tesSUCCESS, std::move(result)};
571}
572
575 ReadView const& view,
576 AccountID const& src,
577 AccountID const& dst,
578 Asset const& deliver,
579 std::optional<Quality> const& limitQuality,
580 std::optional<Asset> const& sendMax,
581 STPathSet const& paths,
582 bool addDefaultPath,
583 bool ownerPaysTransferFee,
584 OfferCrossing offerCrossing,
585 AMMContext& ammContext,
586 std::optional<uint256> const& domainID,
588{
589 std::vector<Strand> result;
590 result.reserve(1 + paths.size());
591 // Insert the strand into result if it is not already part of the vector
592 auto insert = [&](Strand s) {
593 bool const hasStrand = std::ranges::find(result, s) != result.end();
594
595 if (!hasStrand)
596 result.emplace_back(std::move(s));
597 };
598
599 if (addDefaultPath)
600 {
601 auto sp = toStrand(
602 view,
603 src,
604 dst,
605 deliver,
606 limitQuality,
607 sendMax,
608 STPath(),
609 ownerPaysTransferFee,
610 offerCrossing,
611 ammContext,
612 domainID,
613 j);
614 auto const ter = sp.first;
615 auto& strand = sp.second;
616
617 if (!isTesSuccess(ter))
618 {
619 JLOG(j.trace()) << "failed to add default path";
620 if (isTemMalformed(ter) || paths.empty())
621 {
622 return {ter, std::vector<Strand>{}};
623 }
624 }
625 else if (strand.empty())
626 {
627 JLOG(j.trace()) << "toStrand failed";
628 Throw<FlowException>(tefEXCEPTION, "toStrand returned tes & empty strand");
629 }
630 else
631 {
632 insert(std::move(strand));
633 }
634 }
635 else if (paths.empty())
636 {
637 JLOG(j.debug()) << "Flow: Invalid transaction: No paths and direct "
638 "ripple not allowed.";
640 }
641
642 TER lastFailTer = tesSUCCESS;
643 for (auto const& p : paths)
644 {
645 auto sp = toStrand(
646 view,
647 src,
648 dst,
649 deliver,
650 limitQuality,
651 sendMax,
652 p,
653 ownerPaysTransferFee,
654 offerCrossing,
655 ammContext,
656 domainID,
657 j);
658 auto ter = sp.first;
659 auto& strand = sp.second;
660
661 if (!isTesSuccess(ter))
662 {
663 lastFailTer = ter;
664 JLOG(j.trace()) << "failed to add path: ter: " << ter
665 << "path: " << p.getJson(JsonOptions::Values::None);
666 if (isTemMalformed(ter))
667 return {ter, std::vector<Strand>{}};
668 }
669 else if (strand.empty())
670 {
671 JLOG(j.trace()) << "toStrand failed";
672 Throw<FlowException>(tefEXCEPTION, "toStrand returned tes & empty strand");
673 }
674 else
675 {
676 insert(std::move(strand));
677 }
678 }
679
680 if (result.empty())
681 return {lastFailTer, std::move(result)};
682
683 return {tesSUCCESS, std::move(result)};
684}
685
687 ReadView const& view,
688 std::vector<std::unique_ptr<Step>> const& strand,
689 // A strand may not include an inner node that
690 // replicates the source or destination.
691 AccountID const& strandSrc,
692 AccountID const& strandDst,
693 Asset const& strandDeliver,
695 bool isLast,
698 bool isDefaultPath,
699 std::array<boost::container::flat_set<Asset>, 2>& seenDirectAssets,
700 boost::container::flat_set<Asset>& seenBookOuts,
702 std::optional<uint256> const& domainId,
704 : view(view)
709 , isFirst(strand.empty())
710 , isLast(isLast)
714 , strandSize(strand.size())
715 , prevStep(!strand.empty() ? strand.back().get() : nullptr)
719 , domainID(domainId)
720 , j(j)
721{
722}
723
724} // namespace xrpl
T back(T... args)
A generic endpoint for log messages.
Definition Journal.h:38
Stream error() const
Definition Journal.h:315
Stream debug() const
Definition Journal.h:297
Stream trace() const
Severity stream access functions.
Definition Journal.h:291
Stream warn() const
Definition Journal.h:309
Maintains AMM info per overall payment engine execution and individual iteration.
Definition AMMContext.h:16
constexpr auto visit(Visitors &&... visitors) const -> decltype(auto)
Definition Asset.h:107
constexpr TIss const & get() const
AccountID const & getIssuer() const
Definition Asset.cpp:21
constexpr bool holds() const
Definition Asset.h:166
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:24
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:165
exponent_type exponent() const noexcept
Definition IOUAmount.h:159
A currency issued by an account.
Definition Issue.h:13
Currency currency
Definition Issue.h:15
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:33
constexpr bool isXRP() const
Definition PathAsset.h:90
constexpr auto visit(Visitors &&... visitors) const -> decltype(auto)
Definition PathAsset.h:43
A view into a ledger.
Definition ReadView.h:31
auto getNodeType() const
Definition STPathSet.h:319
AccountID const & getAccountID() const
Definition STPathSet.h:375
bool isOffer() const
Definition STPathSet.h:325
PathAsset const & getPathAsset() const
Definition STPathSet.h:381
AccountID const & getIssuerID() const
Definition STPathSet.h:399
bool isAccount() const
Definition STPathSet.h:331
std::vector< STPath >::size_type size() const
Definition STPathSet.h:528
bool empty() const
Definition STPathSet.h:534
A step in a payment path.
Definition Steps.h:66
T emplace_back(T... args)
T emplace(T... args)
T empty(T... args)
T end(T... args)
T find_if(T... args)
T front(T... args)
T make_pair(T... args)
T max(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::pair< TER, std::unique_ptr< Step > > makeXrpEndpointStep(StrandContext const &ctx, AccountID const &acc)
std::pair< TER, std::vector< Strand > > toStrands(ReadView const &sb, AccountID const &src, AccountID const &dst, Asset const &deliver, std::optional< Quality > const &limitQuality, std::optional< Asset > 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).
Definition PaySteps.cpp:574
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:97
std::pair< TER, std::unique_ptr< Step > > makeDirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
std::pair< TER, std::unique_ptr< Step > > makeBookStepIi(StrandContext const &ctx, Issue const &in, Issue const &out)
bool isXRP(AccountID const &c)
Definition AccountID.h:70
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
std::pair< TER, Strand > toStrand(ReadView const &sb, AccountID const &src, AccountID const &dst, Asset const &deliver, std::optional< Quality > const &limitQuality, std::optional< Asset > const &sendMaxAsset, STPath const &path, bool ownerPaysTransferFee, OfferCrossing offerCrossing, AMMContext &ammContext, std::optional< uint256 > const &domainID, beast::Journal j)
Create a Strand for the specified path.
Definition PaySteps.cpp:170
std::pair< TER, std::unique_ptr< Step > > makeBookStepMx(StrandContext const &ctx, MPTIssue const &in)
@ tefEXCEPTION
Definition TER.h:162
std::pair< TER, std::unique_ptr< Step > > makeMptEndpointStep(StrandContext const &ctx, AccountID const &src, AccountID const &dst, MPTID const &mpt)
BaseUInt< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Definition UintTypes.h:36
std::pair< TER, std::unique_ptr< Step > > makeBookStepMi(StrandContext const &ctx, MPTIssue const &in, Issue const &out)
AccountID getMPTIssuer(MPTID const &mptid)
Definition MPTIssue.h:84
std::pair< TER, std::unique_ptr< Step > > makeBookStepIx(StrandContext const &ctx, Issue const &in)
Currency const & xrpCurrency()
XRP currency.
Definition UintTypes.cpp:99
static bool isDefaultPath(STPath const &path)
std::pair< TER, std::unique_ptr< Step > > makeBookStepXm(StrandContext const &ctx, MPTIssue const &out)
BaseUInt< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
Definition UintTypes.h:44
std::pair< TER, std::unique_ptr< Step > > makeBookStepXi(StrandContext const &ctx, Issue const &out)
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
Definition PaySteps.cpp:36
constexpr Number abs(Number x) noexcept
Definition Number.h:823
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
AccountID const & noAccount()
A placeholder for empty accounts.
std::pair< TER, std::unique_ptr< Step > > makeBookStepIm(StrandContext const &ctx, Issue const &in, MPTIssue const &out)
@ temBAD_PATH
Definition TER.h:82
@ temRIPPLE_EMPTY
Definition TER.h:99
bool isTesSuccess(TER x) noexcept
Definition TER.h:663
TERSubset< CanCvtToTER > TER
Definition TER.h:634
static std::pair< TER, std::unique_ptr< Step > > toStep(StrandContext const &ctx, STPathElement const *e1, STPathElement const *e2, Asset const &curAsset)
Definition PaySteps.cpp:66
bool isConsistent(Asset const &asset)
Definition Asset.h:312
AccountID const & xrpAccount()
Compute AccountID from public key.
static bool isXRPAccount(STPathElement const &pe)
Definition PaySteps.cpp:58
OfferCrossing
Definition Steps.h:24
bool isTemMalformed(TER x) noexcept
Definition TER.h:645
std::pair< TER, std::unique_ptr< Step > > makeBookStepMm(StrandContext const &ctx, MPTIssue const &in, MPTIssue const &out)
@ tesSUCCESS
Definition TER.h:240
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:49
T push_back(T... args)
T reserve(T... args)
T size(T... args)
Context needed to build Strand Steps and for error checking.
Definition Steps.h:518
beast::Journal const j
Definition Steps.h:546
StrandContext(ReadView const &view, std::vector< std::unique_ptr< Step > > const &strand, AccountID const &strandSrc, AccountID const &strandDst, Asset const &strandDeliver, std::optional< Quality > const &limitQuality, bool isLast, bool ownerPaysTransferFee, OfferCrossing offerCrossing, bool isDefaultPath, std::array< boost::container::flat_set< Asset >, 2 > &seenDirectAssets, boost::container::flat_set< Asset > &seenBookOuts, AMMContext &ammContext, std::optional< uint256 > const &domainID, beast::Journal j)
StrandContext constructor.
Definition PaySteps.cpp:686
Asset const strandDeliver
Asset strand delivers.
Definition Steps.h:522
std::optional< uint256 > domainID
Definition Steps.h:545
AMMContext & ammContext
Definition Steps.h:544
std::optional< Quality > const limitQuality
Worst accepted quality.
Definition Steps.h:523
size_t const strandSize
Length of Strand.
Definition Steps.h:529
ReadView const & view
Current ReadView.
Definition Steps.h:519
bool const isFirst
true if Step is first in Strand
Definition Steps.h:524
std::array< boost::container::flat_set< Asset >, 2 > & seenDirectAssets
A strand may not include the same account node more than once in the same currency.
Definition Steps.h:539
AccountID const strandSrc
Strand source account.
Definition Steps.h:520
bool const ownerPaysTransferFee
true if owner, not sender, pays fee
Definition Steps.h:526
bool const isLast
true if Step is last in Strand
Definition Steps.h:525
bool const isDefaultPath
true if Strand is default path
Definition Steps.h:528
boost::container::flat_set< Asset > & seenBookOuts
A strand may not include an offer that output the same issue more than once.
Definition Steps.h:543
AccountID const strandDst
Strand destination account.
Definition Steps.h:521
Step const *const prevStep
The previous step in the strand.
Definition Steps.h:533
OfferCrossing const offerCrossing
Yes/Sell if offer crossing, not payment.
Definition Steps.h:527