rippled
Loading...
Searching...
No Matches
DirectStep.cpp
1#include <xrpld/app/paths/detail/StepChecks.h>
2#include <xrpld/app/paths/detail/Steps.h>
3
4#include <xrpl/basics/Log.h>
5#include <xrpl/ledger/Credit.h>
6#include <xrpl/ledger/PaymentSandbox.h>
7#include <xrpl/protocol/Feature.h>
8#include <xrpl/protocol/IOUAmount.h>
9#include <xrpl/protocol/Quality.h>
10
11#include <boost/container/flat_set.hpp>
12
13#include <numeric>
14#include <sstream>
15
16namespace xrpl {
17
18template <class TDerived>
19class DirectStepI : public StepImp<IOUAmount, IOUAmount, DirectStepI<TDerived>>
20{
21protected:
25
26 // Charge transfer fees when the prev step redeems
27 Step const* const prevStep_ = nullptr;
28 bool const isLast_;
30
31 struct Cache
32 {
37
38 Cache(IOUAmount const& in_, IOUAmount const& srcToDst_, IOUAmount const& out_, DebtDirection srcDebtDir_)
39 : in(in_), srcToDst(srcToDst_), out(out_), srcDebtDir(srcDebtDir_)
40 {
41 }
42 };
43
45
46 // Compute the maximum value that can flow from src->dst at
47 // the best available quality.
48 // return: first element is max amount that can flow,
49 // second is the debt direction of the source w.r.t. the dst
51 maxPaymentFlow(ReadView const& sb) const;
52
53 // Compute srcQOut and dstQIn when the source redeems.
55 qualitiesSrcRedeems(ReadView const& sb) const;
56
57 // Compute srcQOut and dstQIn when the source issues.
59 qualitiesSrcIssues(ReadView const& sb, DebtDirection prevStepDebtDirection) const;
60
61 // Returns srcQOut, dstQIn
63 qualities(ReadView const& sb, DebtDirection srcDebtDir, StrandDirection strandDir) const;
64
65public:
66 DirectStepI(StrandContext const& ctx, AccountID const& src, AccountID const& dst, Currency const& c)
67 : src_(src), dst_(dst), currency_(c), prevStep_(ctx.prevStep), isLast_(ctx.isLast), j_(ctx.j)
68 {
69 }
70
71 AccountID const&
72 src() const
73 {
74 return src_;
75 }
76 AccountID const&
77 dst() const
78 {
79 return dst_;
80 }
81 Currency const&
82 currency() const
83 {
84 return currency_;
85 }
86
88 cachedIn() const override
89 {
90 if (!cache_)
91 return std::nullopt;
92 return EitherAmount(cache_->in);
93 }
94
96 cachedOut() const override
97 {
98 if (!cache_)
99 return std::nullopt;
100 return EitherAmount(cache_->out);
101 }
102
104 directStepSrcAcct() const override
105 {
106 return src_;
107 }
108
110 directStepAccts() const override
111 {
112 return std::make_pair(src_, dst_);
113 }
114
116 debtDirection(ReadView const& sb, StrandDirection dir) const override;
117
119 lineQualityIn(ReadView const& v) const override;
120
122 qualityUpperBound(ReadView const& v, DebtDirection dir) const override;
123
125 revImp(PaymentSandbox& sb, ApplyView& afView, boost::container::flat_set<uint256>& ofrsToRm, IOUAmount const& out);
126
128 fwdImp(PaymentSandbox& sb, ApplyView& afView, boost::container::flat_set<uint256>& ofrsToRm, IOUAmount const& in);
129
131 validFwd(PaymentSandbox& sb, ApplyView& afView, EitherAmount const& in) override;
132
133 // Check for error, existing liquidity, and violations of auth/frozen
134 // constraints.
135 TER
136 check(StrandContext const& ctx) const;
137
138 void
140 IOUAmount const& fwdIn,
141 IOUAmount const& fwdSrcToDst,
142 IOUAmount const& fwdOut,
143 DebtDirection srcDebtDir);
144
145 friend bool
146 operator==(DirectStepI const& lhs, DirectStepI const& rhs)
147 {
148 return lhs.src_ == rhs.src_ && lhs.dst_ == rhs.dst_ && lhs.currency_ == rhs.currency_;
149 }
150
151 friend bool
152 operator!=(DirectStepI const& lhs, DirectStepI const& rhs)
153 {
154 return !(lhs == rhs);
155 }
156
157protected:
159 logStringImpl(char const* name) const
160 {
162 ostr << name << ": "
163 << "\nSrc: " << src_ << "\nDst: " << dst_;
164 return ostr.str();
165 }
166
167private:
168 bool
169 equal(Step const& rhs) const override
170 {
171 if (auto ds = dynamic_cast<DirectStepI const*>(&rhs))
172 {
173 return *this == *ds;
174 }
175 return false;
176 }
177};
178
179//------------------------------------------------------------------------------
180
181// Flow is used in two different circumstances for transferring funds:
182// o Payments, and
183// o Offer crossing.
184// The rules for handling funds in these two cases are almost, but not
185// quite, the same.
186
187// Payment DirectStep class (not offer crossing).
188class DirectIPaymentStep : public DirectStepI<DirectIPaymentStep>
189{
190public:
191 using DirectStepI<DirectIPaymentStep>::DirectStepI;
193
194 bool
196 {
197 // A payment doesn't care whether or not prevStepRedeems.
198 return true;
199 }
200
201 bool
203 {
204 // Payments have no particular expectations for what dstQIn will be.
205 return true;
206 }
207
209 quality(ReadView const& sb, QualityDirection qDir) const;
210
211 // Compute the maximum value that can flow from src->dst at
212 // the best available quality.
213 // return: first element is max amount that can flow,
214 // second is the debt direction w.r.t. the source account
216 maxFlow(ReadView const& sb, IOUAmount const& desired) const;
217
218 // Verify the consistency of the step. These checks are specific to
219 // payments and assume that general checks were already performed.
220 TER
221 check(StrandContext const& ctx, std::shared_ptr<const SLE> const& sleSrc) const;
222
224 logString() const override
225 {
226 return logStringImpl("DirectIPaymentStep");
227 }
228};
229
230// Offer crossing DirectStep class (not a payment).
231class DirectIOfferCrossingStep : public DirectStepI<DirectIOfferCrossingStep>
232{
233public:
234 using DirectStepI<DirectIOfferCrossingStep>::DirectStepI;
236
237 bool
239 {
240 // During offer crossing we rely on the fact that prevStepRedeems
241 // will *always* issue. That's because:
242 // o If there's a prevStep_, it will always be a BookStep.
243 // o BookStep::debtDirection() always returns `issues` when offer
244 // crossing.
245 // An assert based on this return value will tell us if that
246 // behavior changes.
247 return issues(prevStepDir);
248 }
249
250 bool
252 {
253 // Due to a couple of factors dstQIn is always QUALITY_ONE for
254 // offer crossing. If that changes we need to know.
255 return dstQIn == QUALITY_ONE;
256 }
257
259 quality(ReadView const& sb, QualityDirection qDir) const;
260
261 // Compute the maximum value that can flow from src->dst at
262 // the best available quality.
263 // return: first element is max amount that can flow,
264 // second is the debt direction w.r.t the source
266 maxFlow(ReadView const& sb, IOUAmount const& desired) const;
267
268 // Verify the consistency of the step. These checks are specific to
269 // offer crossing and assume that general checks were already performed.
270 TER
271 check(StrandContext const& ctx, std::shared_ptr<const SLE> const& sleSrc) const;
272
274 logString() const override
275 {
276 return logStringImpl("DirectIOfferCrossingStep");
277 }
278};
279
280//------------------------------------------------------------------------------
281
284{
285 if (src_ == dst_)
286 return QUALITY_ONE;
287
288 auto const sle = sb.read(keylet::line(dst_, src_, currency_));
289
290 if (!sle)
291 return QUALITY_ONE;
292
293 auto const& field = [&, this]() -> SF_UINT32 const& {
294 if (qDir == QualityDirection::in)
295 {
296 // compute dst quality in
297 if (this->dst_ < this->src_)
298 return sfLowQualityIn;
299 else
300 return sfHighQualityIn;
301 }
302 else
303 {
304 // compute src quality out
305 if (this->src_ < this->dst_)
306 return sfLowQualityOut;
307 else
308 return sfHighQualityOut;
309 }
310 }();
311
312 if (!sle->isFieldPresent(field))
313 return QUALITY_ONE;
314
315 auto const q = (*sle)[field];
316 if (!q)
317 return QUALITY_ONE;
318 return q;
319}
320
323{
324 // If offer crossing then ignore trust line Quality fields. This
325 // preserves a long-standing tradition.
326 return QUALITY_ONE;
327}
328
331{
332 return maxPaymentFlow(sb);
333}
334
337{
338 // When isLast and offer crossing then ignore trust line limits. Offer
339 // crossing has the ability to exceed the limit set by a trust line.
340 // We presume that if someone is creating an offer then they intend to
341 // fill as much of that offer as possible, even if the offer exceeds
342 // the limit that a trust line sets.
343 //
344 // A note on using "out" as the desired parameter for maxFlow. In some
345 // circumstances during payments we end up needing a value larger than
346 // "out" for "maxSrcToDst". But as of now (June 2016) that never happens
347 // during offer crossing. That's because, due to a couple of factors,
348 // "dstQIn" is always QUALITY_ONE for offer crossing.
349
350 if (isLast_)
351 return {desired, DebtDirection::issues};
352
353 return maxPaymentFlow(sb);
354}
355
356TER
358{
359 // Since this is a payment a trust line must be present. Perform all
360 // trust line related checks.
361 {
362 auto const sleLine = ctx.view.read(keylet::line(src_, dst_, currency_));
363 if (!sleLine)
364 {
365 JLOG(j_.trace()) << "DirectStepI: No credit line. " << *this;
366 return terNO_LINE;
367 }
368
369 auto const authField = (src_ > dst_) ? lsfHighAuth : lsfLowAuth;
370
371 if (((*sleSrc)[sfFlags] & lsfRequireAuth) && !((*sleLine)[sfFlags] & authField) &&
372 (*sleLine)[sfBalance] == beast::zero)
373 {
374 JLOG(j_.debug()) << "DirectStepI: can't receive IOUs from issuer without auth."
375 << " src: " << src_;
376 return terNO_AUTH;
377 }
378
379 if (ctx.prevStep)
380 {
381 if (ctx.prevStep->bookStepBook())
382 {
383 auto const noRippleSrcToDst =
384 ((*sleLine)[sfFlags] & ((src_ > dst_) ? lsfHighNoRipple : lsfLowNoRipple));
385 if (noRippleSrcToDst)
386 return terNO_RIPPLE;
387 }
388 }
389 }
390
391 {
392 auto const owed = creditBalance(ctx.view, dst_, src_, currency_);
393 if (owed <= beast::zero)
394 {
395 auto const limit = creditLimit(ctx.view, dst_, src_, currency_);
396 if (-owed >= limit)
397 {
398 JLOG(j_.debug()) << "DirectStepI: dry: owed: " << owed << " limit: " << limit;
399 return tecPATH_DRY;
400 }
401 }
402 }
403 return tesSUCCESS;
404}
405
406TER
408{
409 // The standard checks are all we can do because any remaining checks
410 // require the existence of a trust line. Offer crossing does not
411 // require a pre-existing trust line.
412 return tesSUCCESS;
413}
414
415//------------------------------------------------------------------------------
416
417template <class TDerived>
420{
421 auto const srcOwed = toAmount<IOUAmount>(accountHolds(sb, src_, currency_, dst_, fhIGNORE_FREEZE, j_));
422
423 if (srcOwed.signum() > 0)
424 return {srcOwed, DebtDirection::redeems};
425
426 // srcOwed is negative or zero
427 return {creditLimit2(sb, dst_, src_, currency_) + srcOwed, DebtDirection::issues};
428}
429
430template <class TDerived>
433{
434 if (dir == StrandDirection::forward && cache_)
435 return cache_->srcDebtDir;
436
437 auto const srcOwed = accountHolds(sb, src_, currency_, dst_, fhIGNORE_FREEZE, j_);
438 return srcOwed.signum() > 0 ? DebtDirection::redeems : DebtDirection::issues;
439}
440
441template <class TDerived>
444 PaymentSandbox& sb,
445 ApplyView& /*afView*/,
446 boost::container::flat_set<uint256>& /*ofrsToRm*/,
447 IOUAmount const& out)
448{
449 cache_.reset();
450
451 auto const [maxSrcToDst, srcDebtDir] = static_cast<TDerived const*>(this)->maxFlow(sb, out);
452
453 auto const [srcQOut, dstQIn] = qualities(sb, srcDebtDir, StrandDirection::reverse);
454 XRPL_ASSERT(
455 static_cast<TDerived const*>(this)->verifyDstQualityIn(dstQIn),
456 "xrpl::DirectStepI : valid destination quality");
457
458 Issue const srcToDstIss(currency_, redeems(srcDebtDir) ? dst_ : src_);
459
460 JLOG(j_.trace()) << "DirectStepI::rev"
461 << " srcRedeems: " << redeems(srcDebtDir) << " outReq: " << to_string(out)
462 << " maxSrcToDst: " << to_string(maxSrcToDst) << " srcQOut: " << srcQOut << " dstQIn: " << dstQIn;
463
464 if (maxSrcToDst.signum() <= 0)
465 {
466 JLOG(j_.trace()) << "DirectStepI::rev: dry";
467 cache_.emplace(IOUAmount(beast::zero), IOUAmount(beast::zero), IOUAmount(beast::zero), srcDebtDir);
468 return {beast::zero, beast::zero};
469 }
470
471 IOUAmount const srcToDst = mulRatio(out, QUALITY_ONE, dstQIn, /*roundUp*/ true);
472
473 if (srcToDst <= maxSrcToDst)
474 {
475 IOUAmount const in = mulRatio(srcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true);
476 cache_.emplace(in, srcToDst, out, srcDebtDir);
478 sb,
479 src_,
480 dst_,
481 toSTAmount(srcToDst, srcToDstIss),
482 /*checkIssuer*/ true,
483 j_);
484 JLOG(j_.trace()) << "DirectStepI::rev: Non-limiting"
485 << " srcRedeems: " << redeems(srcDebtDir) << " in: " << to_string(in)
486 << " srcToDst: " << to_string(srcToDst) << " out: " << to_string(out);
487 return {in, out};
488 }
489
490 // limiting node
491 IOUAmount const in = mulRatio(maxSrcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true);
492 IOUAmount const actualOut = mulRatio(maxSrcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false);
493 cache_.emplace(in, maxSrcToDst, actualOut, srcDebtDir);
495 sb,
496 src_,
497 dst_,
498 toSTAmount(maxSrcToDst, srcToDstIss),
499 /*checkIssuer*/ true,
500 j_);
501 JLOG(j_.trace()) << "DirectStepI::rev: Limiting"
502 << " srcRedeems: " << redeems(srcDebtDir) << " in: " << to_string(in)
503 << " srcToDst: " << to_string(maxSrcToDst) << " out: " << to_string(out);
504 return {in, actualOut};
505}
506
507// The forward pass should never have more liquidity than the reverse
508// pass. But sometimes rounding differences cause the forward pass to
509// deliver more liquidity. Use the cached values from the reverse pass
510// to prevent this.
511template <class TDerived>
512void
514 IOUAmount const& fwdIn,
515 IOUAmount const& fwdSrcToDst,
516 IOUAmount const& fwdOut,
517 DebtDirection srcDebtDir)
518{
519 if (cache_->in < fwdIn)
520 {
521 IOUAmount const smallDiff(1, -9);
522 auto const diff = fwdIn - cache_->in;
523 if (diff > smallDiff)
524 {
525 if (fwdIn.exponent() != cache_->in.exponent() || !cache_->in.mantissa() ||
526 (double(fwdIn.mantissa()) / double(cache_->in.mantissa())) > 1.01)
527 {
528 // Detect large diffs on forward pass so they may be
529 // investigated
530 JLOG(j_.warn()) << "DirectStepI::fwd: setCacheLimiting"
531 << " fwdIn: " << to_string(fwdIn) << " cacheIn: " << to_string(cache_->in)
532 << " fwdSrcToDst: " << to_string(fwdSrcToDst)
533 << " cacheSrcToDst: " << to_string(cache_->srcToDst) << " fwdOut: " << to_string(fwdOut)
534 << " cacheOut: " << to_string(cache_->out);
535 cache_.emplace(fwdIn, fwdSrcToDst, fwdOut, srcDebtDir);
536 return;
537 }
538 }
539 }
540 cache_->in = fwdIn;
541 if (fwdSrcToDst < cache_->srcToDst)
542 cache_->srcToDst = fwdSrcToDst;
543 if (fwdOut < cache_->out)
544 cache_->out = fwdOut;
545 cache_->srcDebtDir = srcDebtDir;
546};
547
548template <class TDerived>
551 PaymentSandbox& sb,
552 ApplyView& /*afView*/,
553 boost::container::flat_set<uint256>& /*ofrsToRm*/,
554 IOUAmount const& in)
555{
556 XRPL_ASSERT(cache_, "xrpl::DirectStepI::fwdImp : cache is set");
557
558 auto const [maxSrcToDst, srcDebtDir] = static_cast<TDerived const*>(this)->maxFlow(sb, cache_->srcToDst);
559
560 auto const [srcQOut, dstQIn] = qualities(sb, srcDebtDir, StrandDirection::forward);
561
562 Issue const srcToDstIss(currency_, redeems(srcDebtDir) ? dst_ : src_);
563
564 JLOG(j_.trace()) << "DirectStepI::fwd"
565 << " srcRedeems: " << redeems(srcDebtDir) << " inReq: " << to_string(in)
566 << " maxSrcToDst: " << to_string(maxSrcToDst) << " srcQOut: " << srcQOut << " dstQIn: " << dstQIn;
567
568 if (maxSrcToDst.signum() <= 0)
569 {
570 JLOG(j_.trace()) << "DirectStepI::fwd: dry";
571 cache_.emplace(IOUAmount(beast::zero), IOUAmount(beast::zero), IOUAmount(beast::zero), srcDebtDir);
572 return {beast::zero, beast::zero};
573 }
574
575 IOUAmount const srcToDst = mulRatio(in, QUALITY_ONE, srcQOut, /*roundUp*/ false);
576
577 if (srcToDst <= maxSrcToDst)
578 {
579 IOUAmount const out = mulRatio(srcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false);
580 setCacheLimiting(in, srcToDst, out, srcDebtDir);
582 sb,
583 src_,
584 dst_,
585 toSTAmount(cache_->srcToDst, srcToDstIss),
586 /*checkIssuer*/ true,
587 j_);
588 JLOG(j_.trace()) << "DirectStepI::fwd: Non-limiting"
589 << " srcRedeems: " << redeems(srcDebtDir) << " in: " << to_string(in)
590 << " srcToDst: " << to_string(srcToDst) << " out: " << to_string(out);
591 }
592 else
593 {
594 // limiting node
595 IOUAmount const actualIn = mulRatio(maxSrcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true);
596 IOUAmount const out = mulRatio(maxSrcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false);
597 setCacheLimiting(actualIn, maxSrcToDst, out, srcDebtDir);
599 sb,
600 src_,
601 dst_,
602 toSTAmount(cache_->srcToDst, srcToDstIss),
603 /*checkIssuer*/ true,
604 j_);
605 JLOG(j_.trace()) << "DirectStepI::rev: Limiting"
606 << " srcRedeems: " << redeems(srcDebtDir) << " in: " << to_string(actualIn)
607 << " srcToDst: " << to_string(srcToDst) << " out: " << to_string(out);
608 }
609 return {cache_->in, cache_->out};
610}
611
612template <class TDerived>
615{
616 if (!cache_)
617 {
618 JLOG(j_.trace()) << "Expected valid cache in validFwd";
619 return {false, EitherAmount(IOUAmount(beast::zero))};
620 }
621
622 auto const savCache = *cache_;
623
624 XRPL_ASSERT(!in.native, "xrpl::DirectStepI::validFwd : input is not XRP");
625
626 auto const [maxSrcToDst, srcDebtDir] = static_cast<TDerived const*>(this)->maxFlow(sb, cache_->srcToDst);
627 (void)srcDebtDir;
628
629 try
630 {
631 boost::container::flat_set<uint256> dummy;
632 fwdImp(sb, afView, dummy, in.iou); // changes cache
633 }
634 catch (FlowException const&)
635 {
636 return {false, EitherAmount(IOUAmount(beast::zero))};
637 }
638
639 if (maxSrcToDst < cache_->srcToDst)
640 {
641 JLOG(j_.warn()) << "DirectStepI: Strand re-execute check failed."
642 << " Exceeded max src->dst limit"
643 << " max src->dst: " << to_string(maxSrcToDst)
644 << " actual src->dst: " << to_string(cache_->srcToDst);
645 return {false, EitherAmount(cache_->out)};
646 }
647
648 if (!(checkNear(savCache.in, cache_->in) && checkNear(savCache.out, cache_->out)))
649 {
650 JLOG(j_.warn()) << "DirectStepI: Strand re-execute check failed."
651 << " ExpectedIn: " << to_string(savCache.in) << " CachedIn: " << to_string(cache_->in)
652 << " ExpectedOut: " << to_string(savCache.out) << " CachedOut: " << to_string(cache_->out);
653 return {false, EitherAmount(cache_->out)};
654 }
655 return {true, EitherAmount(cache_->out)};
656}
657
658// Returns srcQOut, dstQIn
659template <class TDerived>
662{
663 if (!prevStep_)
664 return {QUALITY_ONE, QUALITY_ONE};
665
666 auto const prevStepQIn = prevStep_->lineQualityIn(sb);
667 auto srcQOut = static_cast<TDerived const*>(this)->quality(sb, QualityDirection::out);
668
669 if (prevStepQIn > srcQOut)
670 srcQOut = prevStepQIn;
671 return {srcQOut, QUALITY_ONE};
672}
673
674// Returns srcQOut, dstQIn
675template <class TDerived>
678{
679 // Charge a transfer rate when issuing and previous step redeems
680
681 XRPL_ASSERT(
682 static_cast<TDerived const*>(this)->verifyPrevStepDebtDirection(prevStepDebtDirection),
683 "xrpl::DirectStepI::qualitiesSrcIssues : will prevStepDebtDirection "
684 "issue");
685
686 std::uint32_t const srcQOut = redeems(prevStepDebtDirection) ? transferRate(sb, src_).value : QUALITY_ONE;
687 auto dstQIn = static_cast<TDerived const*>(this)->quality(sb, QualityDirection::in);
688
689 if (isLast_ && dstQIn > QUALITY_ONE)
690 dstQIn = QUALITY_ONE;
691 return {srcQOut, dstQIn};
692}
693
694// Returns srcQOut, dstQIn
695template <class TDerived>
698{
699 if (redeems(srcDebtDir))
700 {
701 return qualitiesSrcRedeems(sb);
702 }
703 else
704 {
705 auto const prevStepDebtDirection = [&] {
706 if (prevStep_)
707 return prevStep_->debtDirection(sb, strandDir);
709 }();
710 return qualitiesSrcIssues(sb, prevStepDebtDirection);
711 }
712}
713
714template <class TDerived>
717{
718 // dst quality in
719 return static_cast<TDerived const*>(this)->quality(v, QualityDirection::in);
720}
721
722template <class TDerived>
725{
726 auto const dir = this->debtDirection(v, StrandDirection::forward);
727
728 auto const [srcQOut, dstQIn] = redeems(dir) ? qualitiesSrcRedeems(v) : qualitiesSrcIssues(v, prevStepDir);
729
730 Issue const iss{currency_, src_};
731 // Be careful not to switch the parameters to `getRate`. The
732 // `getRate(offerOut, offerIn)` function is usually used for offers. It
733 // returns offerIn/offerOut. For a direct step, the rate is srcQOut/dstQIn
734 // (Input*dstQIn/srcQOut = Output; So rate = srcQOut/dstQIn). Although the
735 // first parameter is called `offerOut`, it should take the `dstQIn`
736 // variable.
737 return {Quality(getRate(STAmount(iss, dstQIn), STAmount(iss, srcQOut))), dir};
738}
739
740template <class TDerived>
741TER
743{
744 // The following checks apply for both payments and offer crossing.
745 if (!src_ || !dst_)
746 {
747 JLOG(j_.debug()) << "DirectStepI: specified bad account.";
748 return temBAD_PATH;
749 }
750
751 if (src_ == dst_)
752 {
753 JLOG(j_.debug()) << "DirectStepI: same src and dst.";
754 return temBAD_PATH;
755 }
756
757 auto const sleSrc = ctx.view.read(keylet::account(src_));
758 if (!sleSrc)
759 {
760 JLOG(j_.warn()) << "DirectStepI: can't receive IOUs from non-existent issuer: " << src_;
761 return terNO_ACCOUNT;
762 }
763
764 // pure issue/redeem can't be frozen
765 if (!(ctx.isLast && ctx.isFirst))
766 {
767 auto const ter = checkFreeze(ctx.view, src_, dst_, currency_);
768 if (ter != tesSUCCESS)
769 return ter;
770 }
771
772 // If previous step was a direct step then we need to check
773 // no ripple flags.
774 if (ctx.prevStep)
775 {
776 if (auto prevSrc = ctx.prevStep->directStepSrcAcct())
777 {
778 auto const ter = checkNoRipple(ctx.view, *prevSrc, src_, dst_, currency_, j_);
779 if (ter != tesSUCCESS)
780 return ter;
781 }
782 }
783 {
784 Issue const srcIssue{currency_, src_};
785 Issue const dstIssue{currency_, dst_};
786
787 if (ctx.seenBookOuts.count(srcIssue))
788 {
789 if (!ctx.prevStep)
790 {
791 // LCOV_EXCL_START
792 UNREACHABLE(
793 "xrpl::DirectStepI::check : prev seen book without a "
794 "prev step");
795 return temBAD_PATH_LOOP;
796 // LCOV_EXCL_STOP
797 }
798
799 // This is OK if the previous step is a book step that outputs this
800 // issue
801 if (auto book = ctx.prevStep->bookStepBook())
802 {
803 if (book->out != srcIssue)
804 return temBAD_PATH_LOOP;
805 }
806 }
807
808 if (!ctx.seenDirectIssues[0].insert(srcIssue).second || !ctx.seenDirectIssues[1].insert(dstIssue).second)
809 {
810 JLOG(j_.debug()) << "DirectStepI: loop detected: Index: " << ctx.strandSize << ' ' << *this;
811 return temBAD_PATH_LOOP;
812 }
813 }
814
815 return static_cast<TDerived const*>(this)->check(ctx, sleSrc);
816}
817
818//------------------------------------------------------------------------------
819
820namespace test {
821// Needed for testing
822bool
823directStepEqual(Step const& step, AccountID const& src, AccountID const& dst, Currency const& currency)
824{
825 if (auto ds = dynamic_cast<DirectStepI<DirectIPaymentStep> const*>(&step))
826 {
827 return ds->src() == src && ds->dst() == dst && ds->currency() == currency;
828 }
829 return false;
830}
831} // namespace test
832
833//------------------------------------------------------------------------------
834
836make_DirectStepI(StrandContext const& ctx, AccountID const& src, AccountID const& dst, Currency const& c)
837{
838 TER ter = tefINTERNAL;
840 if (ctx.offerCrossing)
841 {
842 auto offerCrossingStep = std::make_unique<DirectIOfferCrossingStep>(ctx, src, dst, c);
843 ter = offerCrossingStep->check(ctx);
844 r = std::move(offerCrossingStep);
845 }
846 else // payment
847 {
848 auto paymentStep = std::make_unique<DirectIPaymentStep>(ctx, src, dst, c);
849 ter = paymentStep->check(ctx);
850 r = std::move(paymentStep);
851 }
852 if (ter != tesSUCCESS)
853 return {ter, nullptr};
854
855 return {tesSUCCESS, std::move(r)};
856}
857
858} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:41
Stream debug() const
Definition Journal.h:301
Stream trace() const
Severity stream access functions.
Definition Journal.h:295
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:115
std::uint32_t quality(ReadView const &sb, QualityDirection qDir) const
bool verifyDstQualityIn(std::uint32_t dstQIn) const
bool verifyPrevStepDebtDirection(DebtDirection prevStepDir) const
std::pair< IOUAmount, DebtDirection > maxFlow(ReadView const &sb, IOUAmount const &desired) const
std::string logString() const override
TER check(StrandContext const &ctx, std::shared_ptr< const SLE > const &sleSrc) const
bool verifyDstQualityIn(std::uint32_t dstQIn) const
std::uint32_t quality(ReadView const &sb, QualityDirection qDir) const
std::pair< IOUAmount, DebtDirection > maxFlow(ReadView const &sb, IOUAmount const &desired) const
bool verifyPrevStepDebtDirection(DebtDirection) const
TER check(StrandContext const &ctx, std::shared_ptr< const SLE > const &sleSrc) const
std::string logString() const override
Step const *const prevStep_
std::pair< IOUAmount, IOUAmount > revImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, IOUAmount const &out)
Currency const & currency() const
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcIssues(ReadView const &sb, DebtDirection prevStepDebtDirection) const
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcRedeems(ReadView const &sb) const
bool equal(Step const &rhs) const override
bool const isLast_
DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
std::pair< IOUAmount, IOUAmount > fwdImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, IOUAmount const &in)
void setCacheLimiting(IOUAmount const &fwdIn, IOUAmount const &fwdSrcToDst, IOUAmount const &fwdOut, DebtDirection srcDebtDir)
AccountID const & src() const
std::optional< EitherAmount > cachedOut() const override
std::optional< AccountID > directStepSrcAcct() const override
AccountID const & dst() const
friend bool operator==(DirectStepI const &lhs, DirectStepI const &rhs)
std::pair< std::optional< Quality >, DebtDirection > qualityUpperBound(ReadView const &v, DebtDirection dir) const override
beast::Journal const j_
std::string logStringImpl(char const *name) const
std::uint32_t lineQualityIn(ReadView const &v) const override
std::optional< Cache > cache_
std::optional< std::pair< AccountID, AccountID > > directStepAccts() const override
std::pair< bool, EitherAmount > validFwd(PaymentSandbox &sb, ApplyView &afView, EitherAmount const &in) override
std::pair< std::uint32_t, std::uint32_t > qualities(ReadView const &sb, DebtDirection srcDebtDir, StrandDirection strandDir) const
std::pair< IOUAmount, DebtDirection > maxPaymentFlow(ReadView const &sb) const
DebtDirection debtDirection(ReadView const &sb, StrandDirection dir) const override
TER check(StrandContext const &ctx) const
friend bool operator!=(DirectStepI const &lhs, DirectStepI const &rhs)
std::optional< EitherAmount > cachedIn() const override
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:26
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:162
exponent_type exponent() const noexcept
Definition IOUAmount.h:156
A currency issued by an account.
Definition Issue.h:14
A wrapper which makes credits unavailable to balances.
A view into a ledger.
Definition ReadView.h:32
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
A step in a payment path.
Definition Steps.h:67
virtual std::optional< AccountID > directStepSrcAcct() const
If this step is DirectStepI (IOU->IOU direct step), return the src account.
Definition Steps.h:126
virtual std::optional< Book > bookStepBook() const
If this step is a BookStep, return the book.
Definition Steps.h:205
T is_same_v
T make_pair(T... args)
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:214
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:160
bool directStepEqual(Step const &step, AccountID const &src, AccountID const &dst, Currency const &currency)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
@ terNO_LINE
Definition TER.h:200
@ terNO_AUTH
Definition TER.h:199
@ terNO_RIPPLE
Definition TER.h:205
@ terNO_ACCOUNT
Definition TER.h:198
@ fhIGNORE_FREEZE
Definition View.h:59
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:598
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
@ tefINTERNAL
Definition TER.h:154
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
Definition View.cpp:392
STAmount creditLimit(ReadView const &view, AccountID const &account, AccountID const &issuer, Currency const &currency)
Calculate the maximum amount of IOUs that an account can hold.
Definition Credit.cpp:9
DebtDirection
Definition Steps.h:23
StrandDirection
Definition Steps.h:25
IOUAmount creditLimit2(ReadView const &v, AccountID const &acc, AccountID const &iss, Currency const &cur)
Definition Credit.cpp:27
QualityDirection
Definition Steps.h:24
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:699
TER checkFreeze(ReadView const &view, AccountID const &src, AccountID const &dst, Currency const &currency)
Definition StepChecks.h:14
STAmount creditBalance(ReadView const &view, AccountID const &account, AccountID const &issuer, Currency const &currency)
Returns the amount of IOUs issued by issuer that are held by an account.
Definition Credit.cpp:33
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition STAmount.cpp:434
std::pair< TER, std::unique_ptr< Step > > make_DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
Definition PaySteps.cpp:15
@ temBAD_PATH
Definition TER.h:77
@ temBAD_PATH_LOOP
Definition TER.h:78
IOUAmount toAmount< IOUAmount >(STAmount const &amt)
@ tecPATH_DRY
Definition TER.h:276
TER checkNoRipple(ReadView const &view, AccountID const &prev, AccountID const &cur, AccountID const &next, Currency const &currency, beast::Journal j)
Definition StepChecks.h:60
@ lsfRequireAuth
@ lsfLowNoRipple
@ lsfHighNoRipple
@ lsfHighAuth
@ tesSUCCESS
Definition TER.h:226
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:3083
T str(T... args)
Cache(IOUAmount const &in_, IOUAmount const &srcToDst_, IOUAmount const &out_, DebtDirection srcDebtDir_)
std::uint32_t value
Definition Rate.h:22
Context needed to build Strand Steps and for error checking.
Definition Steps.h:509
boost::container::flat_set< Issue > & seenBookOuts
A strand may not include an offer that output the same issue more than once.
Definition Steps.h:534
size_t const strandSize
Length of Strand.
Definition Steps.h:520
ReadView const & view
Current ReadView.
Definition Steps.h:510
bool const isFirst
true if Step is first in Strand
Definition Steps.h:515
bool const isLast
true if Step is last in Strand
Definition Steps.h:516
std::array< boost::container::flat_set< Issue >, 2 > & seenDirectIssues
A strand may not include the same account node more than once in the same currency.
Definition Steps.h:530
Step const *const prevStep
The previous step in the strand.
Definition Steps.h:524
OfferCrossing const offerCrossing
Yes/Sell if offer crossing, not payment.
Definition Steps.h:518
A field with a type known at compile time.
Definition SField.h:302