rippled
Loading...
Searching...
No Matches
PaySteps.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/paths/detail/Steps.h>
21
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>
27
28#include <algorithm>
29
30namespace ripple {
31
32// Check equal with tolerance
33bool
34checkNear(IOUAmount const& expected, IOUAmount const& actual)
35{
36 double const ratTol = 0.001;
37 if (abs(expected.exponent() - actual.exponent()) > 1)
38 return false;
39
40 if (actual.exponent() < -20)
41 return true;
42
43 auto const a = (expected.exponent() < actual.exponent())
44 ? expected.mantissa() / 10
45 : expected.mantissa();
46 auto const b = (actual.exponent() < expected.exponent())
47 ? actual.mantissa() / 10
48 : 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
57bool
58checkNear(XRPAmount const& expected, XRPAmount const& actual)
59{
60 return expected == actual;
61};
62
63static bool
65{
67 return false;
68 return isXRP(pe.getAccountID());
69};
70
73 StrandContext const& ctx,
74 STPathElement const* e1,
75 STPathElement const* e2,
76 Issue const& curIssue)
77{
78 auto& j = ctx.j;
79
80 if (ctx.isFirst && e1->isAccount() &&
82 isXRP(e1->getCurrency()))
83 {
84 return make_XRPEndpointStep(ctx, e1->getAccountID());
85 }
86
87 if (ctx.isLast && isXRPAccount(*e1) && e2->isAccount())
88 return make_XRPEndpointStep(ctx, e2->getAccountID());
89
90 if (e1->isAccount() && e2->isAccount())
91 {
92 return make_DirectStepI(
93 ctx, e1->getAccountID(), e2->getAccountID(), curIssue.currency);
94 }
95
96 if (e1->isOffer() && e2->isAccount())
97 {
98 // LCOV_EXCL_START
99 // should already be taken care of
100 JLOG(j.error())
101 << "Found offer/account payment step. Aborting payment strand.";
102 UNREACHABLE("ripple::toStep : offer/account payment payment strand");
104 // LCOV_EXCL_STOP
105 }
106
107 XRPL_ASSERT(
110 "ripple::toStep : currency or issuer");
111 auto const outCurrency = e2->getNodeType() & STPathElement::typeCurrency
112 ? e2->getCurrency()
113 : curIssue.currency;
114 auto const outIssuer = e2->getNodeType() & STPathElement::typeIssuer
115 ? e2->getIssuerID()
116 : curIssue.account;
117
118 if (isXRP(curIssue.currency) && isXRP(outCurrency))
119 {
120 JLOG(j.info()) << "Found xrp/xrp offer payment step";
122 }
123
124 XRPL_ASSERT(e2->isOffer(), "ripple::toStep : is offer");
125
126 if (isXRP(outCurrency))
127 return make_BookStepIX(ctx, curIssue);
128
129 if (isXRP(curIssue.currency))
130 return make_BookStepXI(ctx, {outCurrency, outIssuer});
131
132 return make_BookStepII(ctx, curIssue, {outCurrency, outIssuer});
133}
134
137 ReadView const& view,
138 AccountID const& src,
139 AccountID const& dst,
140 Issue const& deliver,
141 std::optional<Quality> const& limitQuality,
142 std::optional<Issue> const& sendMaxIssue,
143 STPath const& path,
144 bool ownerPaysTransferFee,
145 OfferCrossing offerCrossing,
146 AMMContext& ammContext,
147 std::optional<uint256> const& domainID,
149{
150 if (isXRP(src) || isXRP(dst) || !isConsistent(deliver) ||
151 (sendMaxIssue && !isConsistent(*sendMaxIssue)))
152 return {temBAD_PATH, Strand{}};
153
154 if ((sendMaxIssue && sendMaxIssue->account == noAccount()) ||
155 (src == noAccount()) || (dst == noAccount()) ||
156 (deliver.account == noAccount()))
157 return {temBAD_PATH, Strand{}};
158
159 for (auto const& pe : path)
160 {
161 auto const t = pe.getNodeType();
162
163 if ((t & ~STPathElement::typeAll) || !t)
164 return {temBAD_PATH, Strand{}};
165
166 bool const hasAccount = t & STPathElement::typeAccount;
167 bool const hasIssuer = t & STPathElement::typeIssuer;
168 bool const hasCurrency = t & STPathElement::typeCurrency;
169
170 if (hasAccount && (hasIssuer || hasCurrency))
171 return {temBAD_PATH, Strand{}};
172
173 if (hasIssuer && isXRP(pe.getIssuerID()))
174 return {temBAD_PATH, Strand{}};
175
176 if (hasAccount && isXRP(pe.getAccountID()))
177 return {temBAD_PATH, Strand{}};
178
179 if (hasCurrency && hasIssuer &&
180 isXRP(pe.getCurrency()) != isXRP(pe.getIssuerID()))
181 return {temBAD_PATH, Strand{}};
182
183 if (hasIssuer && (pe.getIssuerID() == noAccount()))
184 return {temBAD_PATH, Strand{}};
185
186 if (hasAccount && (pe.getAccountID() == noAccount()))
187 return {temBAD_PATH, Strand{}};
188 }
189
190 Issue curIssue = [&] {
191 auto const& currency =
192 sendMaxIssue ? sendMaxIssue->currency : deliver.currency;
193 if (isXRP(currency))
194 return xrpIssue();
195 return Issue{currency, src};
196 }();
197
198 auto hasCurrency = [](STPathElement const pe) {
199 return pe.getNodeType() & STPathElement::typeCurrency;
200 };
201
203 // reserve enough for the path, the implied source, destination,
204 // sendmax and deliver.
205 normPath.reserve(4 + path.size());
206 {
207 normPath.emplace_back(
208 STPathElement::typeAll, src, curIssue.currency, curIssue.account);
209
210 if (sendMaxIssue && sendMaxIssue->account != src &&
211 (path.empty() || !path[0].isAccount() ||
212 path[0].getAccountID() != sendMaxIssue->account))
213 {
214 normPath.emplace_back(
215 sendMaxIssue->account, std::nullopt, std::nullopt);
216 }
217
218 for (auto const& i : path)
219 normPath.push_back(i);
220
221 {
222 // Note that for offer crossing (only) we do use an offer book
223 // even if all that is changing is the Issue.account.
224 STPathElement const& lastCurrency =
225 *std::find_if(normPath.rbegin(), normPath.rend(), hasCurrency);
226 if ((lastCurrency.getCurrency() != deliver.currency) ||
227 (offerCrossing &&
228 lastCurrency.getIssuerID() != deliver.account))
229 {
230 normPath.emplace_back(
231 std::nullopt, deliver.currency, deliver.account);
232 }
233 }
234
235 if (!((normPath.back().isAccount() &&
236 normPath.back().getAccountID() == deliver.account) ||
237 (dst == deliver.account)))
238 {
239 normPath.emplace_back(deliver.account, std::nullopt, std::nullopt);
240 }
241
242 if (!normPath.back().isAccount() ||
243 normPath.back().getAccountID() != dst)
244 {
245 normPath.emplace_back(dst, std::nullopt, std::nullopt);
246 }
247 }
248
249 if (normPath.size() < 2)
250 return {temBAD_PATH, Strand{}};
251
252 auto const strandSrc = normPath.front().getAccountID();
253 auto const strandDst = normPath.back().getAccountID();
254 bool const isDefaultPath = path.empty();
255
256 Strand result;
257 result.reserve(2 * normPath.size());
258
259 /* A strand may not include the same account node more than once
260 in the same currency. In a direct step, an account will show up
261 at most twice: once as a src and once as a dst (hence the two element
262 array). The strandSrc and strandDst will only show up once each.
263 */
265 // A strand may not include the same offer book more than once
266 boost::container::flat_set<Issue> seenBookOuts;
267 seenDirectIssues[0].reserve(normPath.size());
268 seenDirectIssues[1].reserve(normPath.size());
269 seenBookOuts.reserve(normPath.size());
270 auto ctx = [&](bool isLast = false) {
271 return StrandContext{
272 view,
273 result,
274 strandSrc,
275 strandDst,
276 deliver,
277 limitQuality,
278 isLast,
279 ownerPaysTransferFee,
280 offerCrossing,
282 seenDirectIssues,
283 seenBookOuts,
284 ammContext,
285 domainID,
286 j};
287 };
288
289 for (std::size_t i = 0; i < normPath.size() - 1; ++i)
290 {
291 /* Iterate through the path elements considering them in pairs.
292 The first element of the pair is `cur` and the second element is
293 `next`. When an offer is one of the pairs, the step created will be
294 for `next`. This means when `cur` is an offer and `next` is an
295 account then no step is created, as a step has already been created
296 for that offer.
297 */
299 auto cur = &normPath[i];
300 auto const next = &normPath[i + 1];
301
302 if (cur->isAccount())
303 curIssue.account = cur->getAccountID();
304 else if (cur->hasIssuer())
305 curIssue.account = cur->getIssuerID();
306
307 if (cur->hasCurrency())
308 {
309 curIssue.currency = cur->getCurrency();
310 if (isXRP(curIssue.currency))
311 curIssue.account = xrpAccount();
312 }
313
314 if (cur->isAccount() && next->isAccount())
315 {
316 if (!isXRP(curIssue.currency) &&
317 curIssue.account != cur->getAccountID() &&
318 curIssue.account != next->getAccountID())
319 {
320 JLOG(j.trace()) << "Inserting implied account";
321 auto msr = make_DirectStepI(
322 ctx(),
323 cur->getAccountID(),
324 curIssue.account,
325 curIssue.currency);
326 if (msr.first != tesSUCCESS)
327 return {msr.first, Strand{}};
328 result.push_back(std::move(msr.second));
329 impliedPE.emplace(
331 curIssue.account,
332 xrpCurrency(),
333 xrpAccount());
334 cur = &*impliedPE;
335 }
336 }
337 else if (cur->isAccount() && next->isOffer())
338 {
339 if (curIssue.account != cur->getAccountID())
340 {
341 JLOG(j.trace()) << "Inserting implied account before offer";
342 auto msr = make_DirectStepI(
343 ctx(),
344 cur->getAccountID(),
345 curIssue.account,
346 curIssue.currency);
347 if (msr.first != tesSUCCESS)
348 return {msr.first, Strand{}};
349 result.push_back(std::move(msr.second));
350 impliedPE.emplace(
352 curIssue.account,
353 xrpCurrency(),
354 xrpAccount());
355 cur = &*impliedPE;
356 }
357 }
358 else if (cur->isOffer() && next->isAccount())
359 {
360 if (curIssue.account != next->getAccountID() &&
361 !isXRP(next->getAccountID()))
362 {
363 if (isXRP(curIssue))
364 {
365 if (i != normPath.size() - 2)
366 return {temBAD_PATH, Strand{}};
367 else
368 {
369 // Last step. insert xrp endpoint step
370 auto msr =
371 make_XRPEndpointStep(ctx(), next->getAccountID());
372 if (msr.first != tesSUCCESS)
373 return {msr.first, Strand{}};
374 result.push_back(std::move(msr.second));
375 }
376 }
377 else
378 {
379 JLOG(j.trace()) << "Inserting implied account after offer";
380 auto msr = make_DirectStepI(
381 ctx(),
382 curIssue.account,
383 next->getAccountID(),
384 curIssue.currency);
385 if (msr.first != tesSUCCESS)
386 return {msr.first, Strand{}};
387 result.push_back(std::move(msr.second));
388 }
389 }
390 continue;
391 }
392
393 if (!next->isOffer() && next->hasCurrency() &&
394 next->getCurrency() != curIssue.currency)
395 {
396 // Should never happen
397 // LCOV_EXCL_START
398 UNREACHABLE("ripple::toStrand : offer currency mismatch");
399 return {temBAD_PATH, Strand{}};
400 // LCOV_EXCL_STOP
401 }
402
403 auto s = toStep(
404 ctx(/*isLast*/ i == normPath.size() - 2), cur, next, curIssue);
405 if (s.first == tesSUCCESS)
406 result.emplace_back(std::move(s.second));
407 else
408 {
409 JLOG(j.debug()) << "toStep failed: " << s.first;
410 return {s.first, Strand{}};
411 }
412 }
413
414 auto checkStrand = [&]() -> bool {
415 auto stepAccts = [](Step const& s) -> std::pair<AccountID, AccountID> {
416 if (auto r = s.directStepAccts())
417 return *r;
418 if (auto const r = s.bookStepBook())
419 return std::make_pair(r->in.account, r->out.account);
420 Throw<FlowException>(
421 tefEXCEPTION, "Step should be either a direct or book step");
423 };
424
425 auto curAcc = src;
426 auto curIss = [&] {
427 auto& currency =
428 sendMaxIssue ? sendMaxIssue->currency : deliver.currency;
429 if (isXRP(currency))
430 return xrpIssue();
431 return Issue{currency, src};
432 }();
433
434 for (auto const& s : result)
435 {
436 auto const accts = stepAccts(*s);
437 if (accts.first != curAcc)
438 return false;
439
440 if (auto const b = s->bookStepBook())
441 {
442 if (curIss != b->in)
443 return false;
444 curIss = b->out;
445 }
446 else
447 {
448 curIss.account = accts.second;
449 }
450
451 curAcc = accts.second;
452 }
453 if (curAcc != dst)
454 return false;
455 if (curIss.currency != deliver.currency)
456 return false;
457 if (curIss.account != deliver.account && curIss.account != dst)
458 return false;
459 return true;
460 };
461
462 if (!checkStrand())
463 {
464 // LCOV_EXCL_START
465 JLOG(j.warn()) << "Flow check strand failed";
466 UNREACHABLE("ripple::toStrand : invalid strand");
467 return {temBAD_PATH, Strand{}};
468 // LCOV_EXCL_STOP
469 }
470
471 return {tesSUCCESS, std::move(result)};
472}
473
476 ReadView const& view,
477 AccountID const& src,
478 AccountID const& dst,
479 Issue const& deliver,
480 std::optional<Quality> const& limitQuality,
481 std::optional<Issue> const& sendMax,
482 STPathSet const& paths,
483 bool addDefaultPath,
484 bool ownerPaysTransferFee,
485 OfferCrossing offerCrossing,
486 AMMContext& ammContext,
487 std::optional<uint256> const& domainID,
489{
490 std::vector<Strand> result;
491 result.reserve(1 + paths.size());
492 // Insert the strand into result if it is not already part of the vector
493 auto insert = [&](Strand s) {
494 bool const hasStrand =
495 std::find(result.begin(), result.end(), s) != result.end();
496
497 if (!hasStrand)
498 result.emplace_back(std::move(s));
499 };
500
501 if (addDefaultPath)
502 {
503 auto sp = toStrand(
504 view,
505 src,
506 dst,
507 deliver,
508 limitQuality,
509 sendMax,
510 STPath(),
511 ownerPaysTransferFee,
512 offerCrossing,
513 ammContext,
514 domainID,
515 j);
516 auto const ter = sp.first;
517 auto& strand = sp.second;
518
519 if (ter != tesSUCCESS)
520 {
521 JLOG(j.trace()) << "failed to add default path";
522 if (isTemMalformed(ter) || paths.empty())
523 {
524 return {ter, std::vector<Strand>{}};
525 }
526 }
527 else if (strand.empty())
528 {
529 JLOG(j.trace()) << "toStrand failed";
530 Throw<FlowException>(
531 tefEXCEPTION, "toStrand returned tes & empty strand");
532 }
533 else
534 {
535 insert(std::move(strand));
536 }
537 }
538 else if (paths.empty())
539 {
540 JLOG(j.debug()) << "Flow: Invalid transaction: No paths and direct "
541 "ripple not allowed.";
543 }
544
545 TER lastFailTer = tesSUCCESS;
546 for (auto const& p : paths)
547 {
548 auto sp = toStrand(
549 view,
550 src,
551 dst,
552 deliver,
553 limitQuality,
554 sendMax,
555 p,
556 ownerPaysTransferFee,
557 offerCrossing,
558 ammContext,
559 domainID,
560 j);
561 auto ter = sp.first;
562 auto& strand = sp.second;
563
564 if (ter != tesSUCCESS)
565 {
566 lastFailTer = ter;
567 JLOG(j.trace()) << "failed to add path: ter: " << ter
568 << "path: " << p.getJson(JsonOptions::none);
569 if (isTemMalformed(ter))
570 return {ter, std::vector<Strand>{}};
571 }
572 else if (strand.empty())
573 {
574 JLOG(j.trace()) << "toStrand failed";
575 Throw<FlowException>(
576 tefEXCEPTION, "toStrand returned tes & empty strand");
577 }
578 else
579 {
580 insert(std::move(strand));
581 }
582 }
583
584 if (result.empty())
585 return {lastFailTer, std::move(result)};
586
587 return {tesSUCCESS, std::move(result)};
588}
589
591 ReadView const& view_,
592 std::vector<std::unique_ptr<Step>> const& strand_,
593 // A strand may not include an inner node that
594 // replicates the source or destination.
595 AccountID const& strandSrc_,
596 AccountID const& strandDst_,
597 Issue const& strandDeliver_,
598 std::optional<Quality> const& limitQuality_,
599 bool isLast_,
600 bool ownerPaysTransferFee_,
601 OfferCrossing offerCrossing_,
602 bool isDefaultPath_,
603 std::array<boost::container::flat_set<Issue>, 2>& seenDirectIssues_,
604 boost::container::flat_set<Issue>& seenBookOuts_,
605 AMMContext& ammContext_,
606 std::optional<uint256> const& domainID_,
608 : view(view_)
609 , strandSrc(strandSrc_)
610 , strandDst(strandDst_)
611 , strandDeliver(strandDeliver_)
612 , limitQuality(limitQuality_)
613 , isFirst(strand_.empty())
614 , isLast(isLast_)
615 , ownerPaysTransferFee(ownerPaysTransferFee_)
616 , offerCrossing(offerCrossing_)
617 , isDefaultPath(isDefaultPath_)
618 , strandSize(strand_.size())
619 , prevStep(!strand_.empty() ? strand_.back().get() : nullptr)
620 , seenDirectIssues(seenDirectIssues_)
621 , seenBookOuts(seenBookOuts_)
622 , ammContext(ammContext_)
623 , domainID(domainID_)
624 , j(j_)
625{
626}
627
628template <class InAmt, class OutAmt>
629bool
630isDirectXrpToXrp(Strand const& strand)
631{
632 return false;
633}
634
635template <>
636bool
637isDirectXrpToXrp<XRPAmount, XRPAmount>(Strand const& strand)
638{
639 return (strand.size() == 2);
640}
641
642template bool
643isDirectXrpToXrp<XRPAmount, IOUAmount>(Strand const& strand);
644template bool
645isDirectXrpToXrp<IOUAmount, XRPAmount>(Strand const& strand);
646template bool
647isDirectXrpToXrp<IOUAmount, IOUAmount>(Strand const& strand);
648
649} // namespace ripple
T back(T... args)
T begin(T... args)
A generic endpoint for log messages.
Definition Journal.h:60
Stream debug() const
Definition Journal.h:328
Stream trace() const
Severity stream access functions.
Definition Journal.h:322
Stream warn() const
Definition Journal.h:340
Maintains AMM info per overall payment engine execution and individual iteration.
Definition AMMContext.h:36
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:46
int exponent() const noexcept
Definition IOUAmount.h:172
std::int64_t mantissa() const noexcept
Definition IOUAmount.h:178
A currency issued by an account.
Definition Issue.h:33
AccountID account
Definition Issue.h:36
Currency currency
Definition Issue.h:35
A view into a ledger.
Definition ReadView.h:51
Currency const & getCurrency() const
Definition STPathSet.h:366
AccountID const & getAccountID() const
Definition STPathSet.h:360
bool isOffer() const
Definition STPathSet.h:328
AccountID const & getIssuerID() const
Definition STPathSet.h:372
auto getNodeType() const
Definition STPathSet.h:322
bool isAccount() const
Definition STPathSet.h:334
bool empty() const
Definition STPathSet.h:508
std::vector< STPath >::size_type size() const
Definition STPathSet.h:502
A step in a payment path.
Definition Steps.h:86
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 is_same_v
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:25
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:115
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)
Definition PaySteps.cpp:72
bool isConsistent(Book const &book)
Definition Book.cpp:29
bool isXRP(AccountID const &c)
Definition AccountID.h:90
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)
@ tefEXCEPTION
Definition TER.h:172
template bool isDirectXrpToXrp< IOUAmount, XRPAmount >(Strand const &strand)
OfferCrossing
Definition Steps.h:45
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)
Definition PaySteps.cpp:34
@ tesSUCCESS
Definition TER.h:245
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)
Definition PaySteps.cpp:630
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
bool isTemMalformed(TER x) noexcept
Definition TER.h:660
static bool isXRPAccount(STPathElement const &pe)
Definition PaySteps.cpp:64
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)
Definition PaySteps.cpp:475
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.
Definition PaySteps.cpp:136
bool isDirectXrpToXrp< XRPAmount, XRPAmount >(Strand const &strand)
Definition PaySteps.cpp:637
constexpr Number abs(Number x) noexcept
Definition Number.h:350
@ temBAD_PATH
Definition TER.h:96
@ temRIPPLE_EMPTY
Definition TER.h:113
T push_back(T... args)
T rbegin(T... args)
T rend(T... args)
T reserve(T... args)
T size(T... args)
Context needed to build Strand Steps and for error checking.
Definition Steps.h:533
beast::Journal const j
Definition Steps.h:562
bool const isFirst
true if Step is first in Strand
Definition Steps.h:539
bool const isLast
true if Step is last in Strand
Definition Steps.h:540
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.
Definition PaySteps.cpp:590