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