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