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 // should already be taken care of
99 JLOG(j.error())
100 << "Found offer/account payment step. Aborting payment strand.";
101 UNREACHABLE("ripple::toStep : offer/account payment payment strand");
103 }
104
105 XRPL_ASSERT(
108 "ripple::toStep : currency or issuer");
109 auto const outCurrency = e2->getNodeType() & STPathElement::typeCurrency
110 ? e2->getCurrency()
111 : curIssue.currency;
112 auto const outIssuer = e2->getNodeType() & STPathElement::typeIssuer
113 ? e2->getIssuerID()
114 : curIssue.account;
115
116 if (isXRP(curIssue.currency) && isXRP(outCurrency))
117 {
118 JLOG(j.info()) << "Found xrp/xrp offer payment step";
120 }
121
122 XRPL_ASSERT(e2->isOffer(), "ripple::toStep : is offer");
123
124 if (isXRP(outCurrency))
125 return make_BookStepIX(ctx, curIssue);
126
127 if (isXRP(curIssue.currency))
128 return make_BookStepXI(ctx, {outCurrency, outIssuer});
129
130 return make_BookStepII(ctx, curIssue, {outCurrency, outIssuer});
131}
132
135 ReadView const& view,
136 AccountID const& src,
137 AccountID const& dst,
138 Issue const& deliver,
139 std::optional<Quality> const& limitQuality,
140 std::optional<Issue> const& sendMaxIssue,
141 STPath const& path,
142 bool ownerPaysTransferFee,
143 OfferCrossing offerCrossing,
144 AMMContext& ammContext,
145 std::optional<uint256> const& domainID,
147{
148 if (isXRP(src) || isXRP(dst) || !isConsistent(deliver) ||
149 (sendMaxIssue && !isConsistent(*sendMaxIssue)))
150 return {temBAD_PATH, Strand{}};
151
152 if ((sendMaxIssue && sendMaxIssue->account == noAccount()) ||
153 (src == noAccount()) || (dst == noAccount()) ||
154 (deliver.account == noAccount()))
155 return {temBAD_PATH, Strand{}};
156
157 for (auto const& pe : path)
158 {
159 auto const t = pe.getNodeType();
160
161 if ((t & ~STPathElement::typeAll) || !t)
162 return {temBAD_PATH, Strand{}};
163
164 bool const hasAccount = t & STPathElement::typeAccount;
165 bool const hasIssuer = t & STPathElement::typeIssuer;
166 bool const hasCurrency = t & STPathElement::typeCurrency;
167
168 if (hasAccount && (hasIssuer || hasCurrency))
169 return {temBAD_PATH, Strand{}};
170
171 if (hasIssuer && isXRP(pe.getIssuerID()))
172 return {temBAD_PATH, Strand{}};
173
174 if (hasAccount && isXRP(pe.getAccountID()))
175 return {temBAD_PATH, Strand{}};
176
177 if (hasCurrency && hasIssuer &&
178 isXRP(pe.getCurrency()) != isXRP(pe.getIssuerID()))
179 return {temBAD_PATH, Strand{}};
180
181 if (hasIssuer && (pe.getIssuerID() == noAccount()))
182 return {temBAD_PATH, Strand{}};
183
184 if (hasAccount && (pe.getAccountID() == noAccount()))
185 return {temBAD_PATH, Strand{}};
186 }
187
188 Issue curIssue = [&] {
189 auto const& currency =
190 sendMaxIssue ? sendMaxIssue->currency : deliver.currency;
191 if (isXRP(currency))
192 return xrpIssue();
193 return Issue{currency, src};
194 }();
195
196 auto hasCurrency = [](STPathElement const pe) {
197 return pe.getNodeType() & STPathElement::typeCurrency;
198 };
199
201 // reserve enough for the path, the implied source, destination,
202 // sendmax and deliver.
203 normPath.reserve(4 + path.size());
204 {
205 normPath.emplace_back(
206 STPathElement::typeAll, src, curIssue.currency, curIssue.account);
207
208 if (sendMaxIssue && sendMaxIssue->account != src &&
209 (path.empty() || !path[0].isAccount() ||
210 path[0].getAccountID() != sendMaxIssue->account))
211 {
212 normPath.emplace_back(
213 sendMaxIssue->account, std::nullopt, std::nullopt);
214 }
215
216 for (auto const& i : path)
217 normPath.push_back(i);
218
219 {
220 // Note that for offer crossing (only) we do use an offer book
221 // even if all that is changing is the Issue.account.
222 STPathElement const& lastCurrency =
223 *std::find_if(normPath.rbegin(), normPath.rend(), hasCurrency);
224 if ((lastCurrency.getCurrency() != deliver.currency) ||
225 (offerCrossing &&
226 lastCurrency.getIssuerID() != deliver.account))
227 {
228 normPath.emplace_back(
229 std::nullopt, deliver.currency, deliver.account);
230 }
231 }
232
233 if (!((normPath.back().isAccount() &&
234 normPath.back().getAccountID() == deliver.account) ||
235 (dst == deliver.account)))
236 {
237 normPath.emplace_back(deliver.account, std::nullopt, std::nullopt);
238 }
239
240 if (!normPath.back().isAccount() ||
241 normPath.back().getAccountID() != dst)
242 {
243 normPath.emplace_back(dst, std::nullopt, std::nullopt);
244 }
245 }
246
247 if (normPath.size() < 2)
248 return {temBAD_PATH, Strand{}};
249
250 auto const strandSrc = normPath.front().getAccountID();
251 auto const strandDst = normPath.back().getAccountID();
252 bool const isDefaultPath = path.empty();
253
254 Strand result;
255 result.reserve(2 * normPath.size());
256
257 /* A strand may not include the same account node more than once
258 in the same currency. In a direct step, an account will show up
259 at most twice: once as a src and once as a dst (hence the two element
260 array). The strandSrc and strandDst will only show up once each.
261 */
263 // A strand may not include the same offer book more than once
264 boost::container::flat_set<Issue> seenBookOuts;
265 seenDirectIssues[0].reserve(normPath.size());
266 seenDirectIssues[1].reserve(normPath.size());
267 seenBookOuts.reserve(normPath.size());
268 auto ctx = [&](bool isLast = false) {
269 return StrandContext{
270 view,
271 result,
272 strandSrc,
273 strandDst,
274 deliver,
275 limitQuality,
276 isLast,
277 ownerPaysTransferFee,
278 offerCrossing,
280 seenDirectIssues,
281 seenBookOuts,
282 ammContext,
283 domainID,
284 j};
285 };
286
287 for (std::size_t i = 0; i < normPath.size() - 1; ++i)
288 {
289 /* Iterate through the path elements considering them in pairs.
290 The first element of the pair is `cur` and the second element is
291 `next`. When an offer is one of the pairs, the step created will be
292 for `next`. This means when `cur` is an offer and `next` is an
293 account then no step is created, as a step has already been created
294 for that offer.
295 */
297 auto cur = &normPath[i];
298 auto const next = &normPath[i + 1];
299
300 if (cur->isAccount())
301 curIssue.account = cur->getAccountID();
302 else if (cur->hasIssuer())
303 curIssue.account = cur->getIssuerID();
304
305 if (cur->hasCurrency())
306 {
307 curIssue.currency = cur->getCurrency();
308 if (isXRP(curIssue.currency))
309 curIssue.account = xrpAccount();
310 }
311
312 if (cur->isAccount() && next->isAccount())
313 {
314 if (!isXRP(curIssue.currency) &&
315 curIssue.account != cur->getAccountID() &&
316 curIssue.account != next->getAccountID())
317 {
318 JLOG(j.trace()) << "Inserting implied account";
319 auto msr = make_DirectStepI(
320 ctx(),
321 cur->getAccountID(),
322 curIssue.account,
323 curIssue.currency);
324 if (msr.first != tesSUCCESS)
325 return {msr.first, Strand{}};
326 result.push_back(std::move(msr.second));
327 impliedPE.emplace(
329 curIssue.account,
330 xrpCurrency(),
331 xrpAccount());
332 cur = &*impliedPE;
333 }
334 }
335 else if (cur->isAccount() && next->isOffer())
336 {
337 if (curIssue.account != cur->getAccountID())
338 {
339 JLOG(j.trace()) << "Inserting implied account before offer";
340 auto msr = make_DirectStepI(
341 ctx(),
342 cur->getAccountID(),
343 curIssue.account,
344 curIssue.currency);
345 if (msr.first != tesSUCCESS)
346 return {msr.first, Strand{}};
347 result.push_back(std::move(msr.second));
348 impliedPE.emplace(
350 curIssue.account,
351 xrpCurrency(),
352 xrpAccount());
353 cur = &*impliedPE;
354 }
355 }
356 else if (cur->isOffer() && next->isAccount())
357 {
358 if (curIssue.account != next->getAccountID() &&
359 !isXRP(next->getAccountID()))
360 {
361 if (isXRP(curIssue))
362 {
363 if (i != normPath.size() - 2)
364 return {temBAD_PATH, Strand{}};
365 else
366 {
367 // Last step. insert xrp endpoint step
368 auto msr =
369 make_XRPEndpointStep(ctx(), next->getAccountID());
370 if (msr.first != tesSUCCESS)
371 return {msr.first, Strand{}};
372 result.push_back(std::move(msr.second));
373 }
374 }
375 else
376 {
377 JLOG(j.trace()) << "Inserting implied account after offer";
378 auto msr = make_DirectStepI(
379 ctx(),
380 curIssue.account,
381 next->getAccountID(),
382 curIssue.currency);
383 if (msr.first != tesSUCCESS)
384 return {msr.first, Strand{}};
385 result.push_back(std::move(msr.second));
386 }
387 }
388 continue;
389 }
390
391 if (!next->isOffer() && next->hasCurrency() &&
392 next->getCurrency() != curIssue.currency)
393 {
394 // Should never happen
395 UNREACHABLE("ripple::toStrand : offer currency mismatch");
396 return {temBAD_PATH, Strand{}};
397 }
398
399 auto s = toStep(
400 ctx(/*isLast*/ i == normPath.size() - 2), cur, next, curIssue);
401 if (s.first == tesSUCCESS)
402 result.emplace_back(std::move(s.second));
403 else
404 {
405 JLOG(j.debug()) << "toStep failed: " << s.first;
406 return {s.first, Strand{}};
407 }
408 }
409
410 auto checkStrand = [&]() -> bool {
411 auto stepAccts = [](Step const& s) -> std::pair<AccountID, AccountID> {
412 if (auto r = s.directStepAccts())
413 return *r;
414 if (auto const r = s.bookStepBook())
415 return std::make_pair(r->in.account, r->out.account);
416 Throw<FlowException>(
417 tefEXCEPTION, "Step should be either a direct or book step");
419 };
420
421 auto curAcc = src;
422 auto curIss = [&] {
423 auto& currency =
424 sendMaxIssue ? sendMaxIssue->currency : deliver.currency;
425 if (isXRP(currency))
426 return xrpIssue();
427 return Issue{currency, src};
428 }();
429
430 for (auto const& s : result)
431 {
432 auto const accts = stepAccts(*s);
433 if (accts.first != curAcc)
434 return false;
435
436 if (auto const b = s->bookStepBook())
437 {
438 if (curIss != b->in)
439 return false;
440 curIss = b->out;
441 }
442 else
443 {
444 curIss.account = accts.second;
445 }
446
447 curAcc = accts.second;
448 }
449 if (curAcc != dst)
450 return false;
451 if (curIss.currency != deliver.currency)
452 return false;
453 if (curIss.account != deliver.account && curIss.account != dst)
454 return false;
455 return true;
456 };
457
458 if (!checkStrand())
459 {
460 JLOG(j.warn()) << "Flow check strand failed";
461 UNREACHABLE("ripple::toStrand : invalid strand");
462 return {temBAD_PATH, Strand{}};
463 }
464
465 return {tesSUCCESS, std::move(result)};
466}
467
470 ReadView const& view,
471 AccountID const& src,
472 AccountID const& dst,
473 Issue const& deliver,
474 std::optional<Quality> const& limitQuality,
475 std::optional<Issue> const& sendMax,
476 STPathSet const& paths,
477 bool addDefaultPath,
478 bool ownerPaysTransferFee,
479 OfferCrossing offerCrossing,
480 AMMContext& ammContext,
481 std::optional<uint256> const& domainID,
483{
484 std::vector<Strand> result;
485 result.reserve(1 + paths.size());
486 // Insert the strand into result if it is not already part of the vector
487 auto insert = [&](Strand s) {
488 bool const hasStrand =
489 std::find(result.begin(), result.end(), s) != result.end();
490
491 if (!hasStrand)
492 result.emplace_back(std::move(s));
493 };
494
495 if (addDefaultPath)
496 {
497 auto sp = toStrand(
498 view,
499 src,
500 dst,
501 deliver,
502 limitQuality,
503 sendMax,
504 STPath(),
505 ownerPaysTransferFee,
506 offerCrossing,
507 ammContext,
508 domainID,
509 j);
510 auto const ter = sp.first;
511 auto& strand = sp.second;
512
513 if (ter != tesSUCCESS)
514 {
515 JLOG(j.trace()) << "failed to add default path";
516 if (isTemMalformed(ter) || paths.empty())
517 {
518 return {ter, std::vector<Strand>{}};
519 }
520 }
521 else if (strand.empty())
522 {
523 JLOG(j.trace()) << "toStrand failed";
524 Throw<FlowException>(
525 tefEXCEPTION, "toStrand returned tes & empty strand");
526 }
527 else
528 {
529 insert(std::move(strand));
530 }
531 }
532 else if (paths.empty())
533 {
534 JLOG(j.debug()) << "Flow: Invalid transaction: No paths and direct "
535 "ripple not allowed.";
537 }
538
539 TER lastFailTer = tesSUCCESS;
540 for (auto const& p : paths)
541 {
542 auto sp = toStrand(
543 view,
544 src,
545 dst,
546 deliver,
547 limitQuality,
548 sendMax,
549 p,
550 ownerPaysTransferFee,
551 offerCrossing,
552 ammContext,
553 domainID,
554 j);
555 auto ter = sp.first;
556 auto& strand = sp.second;
557
558 if (ter != tesSUCCESS)
559 {
560 lastFailTer = ter;
561 JLOG(j.trace()) << "failed to add path: ter: " << ter
562 << "path: " << p.getJson(JsonOptions::none);
563 if (isTemMalformed(ter))
564 return {ter, std::vector<Strand>{}};
565 }
566 else if (strand.empty())
567 {
568 JLOG(j.trace()) << "toStrand failed";
569 Throw<FlowException>(
570 tefEXCEPTION, "toStrand returned tes & empty strand");
571 }
572 else
573 {
574 insert(std::move(strand));
575 }
576 }
577
578 if (result.empty())
579 return {lastFailTer, std::move(result)};
580
581 return {tesSUCCESS, std::move(result)};
582}
583
585 ReadView const& view_,
586 std::vector<std::unique_ptr<Step>> const& strand_,
587 // A strand may not include an inner node that
588 // replicates the source or destination.
589 AccountID const& strandSrc_,
590 AccountID const& strandDst_,
591 Issue const& strandDeliver_,
592 std::optional<Quality> const& limitQuality_,
593 bool isLast_,
594 bool ownerPaysTransferFee_,
595 OfferCrossing offerCrossing_,
596 bool isDefaultPath_,
597 std::array<boost::container::flat_set<Issue>, 2>& seenDirectIssues_,
598 boost::container::flat_set<Issue>& seenBookOuts_,
599 AMMContext& ammContext_,
600 std::optional<uint256> const& domainID_,
602 : view(view_)
603 , strandSrc(strandSrc_)
604 , strandDst(strandDst_)
605 , strandDeliver(strandDeliver_)
606 , limitQuality(limitQuality_)
607 , isFirst(strand_.empty())
608 , isLast(isLast_)
609 , ownerPaysTransferFee(ownerPaysTransferFee_)
610 , offerCrossing(offerCrossing_)
611 , isDefaultPath(isDefaultPath_)
612 , strandSize(strand_.size())
613 , prevStep(!strand_.empty() ? strand_.back().get() : nullptr)
614 , seenDirectIssues(seenDirectIssues_)
615 , seenBookOuts(seenBookOuts_)
616 , ammContext(ammContext_)
617 , domainID(domainID_)
618 , j(j_)
619{
620}
621
622template <class InAmt, class OutAmt>
623bool
624isDirectXrpToXrp(Strand const& strand)
625{
626 return false;
627}
628
629template <>
630bool
631isDirectXrpToXrp<XRPAmount, XRPAmount>(Strand const& strand)
632{
633 return (strand.size() == 2);
634}
635
636template bool
637isDirectXrpToXrp<XRPAmount, IOUAmount>(Strand const& strand);
638template bool
639isDirectXrpToXrp<IOUAmount, XRPAmount>(Strand const& strand);
640template bool
641isDirectXrpToXrp<IOUAmount, IOUAmount>(Strand const& strand);
642
643} // 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:244
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:624
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:656
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:469
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:134
bool isDirectXrpToXrp< XRPAmount, XRPAmount >(Strand const &strand)
Definition PaySteps.cpp:631
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:584