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