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 << ": "
208 << "\nSrc: " << src_ << "\nDst: " << dst_;
209 return ostr.str();
210 }
211
212private:
213 bool
214 equal(Step const& rhs) const override
215 {
216 if (auto ds = dynamic_cast<DirectStepI const*>(&rhs))
217 {
218 return *this == *ds;
219 }
220 return false;
221 }
222};
223
224//------------------------------------------------------------------------------
225
226// Flow is used in two different circumstances for transferring funds:
227// o Payments, and
228// o Offer crossing.
229// The rules for handling funds in these two cases are almost, but not
230// quite, the same.
231
232// Payment DirectStep class (not offer crossing).
233class DirectIPaymentStep : public DirectStepI<DirectIPaymentStep>
234{
235public:
236 using DirectStepI<DirectIPaymentStep>::DirectStepI;
238
239 bool
241 {
242 // A payment doesn't care whether or not prevStepRedeems.
243 return true;
244 }
245
246 bool
248 {
249 // Payments have no particular expectations for what dstQIn will be.
250 return true;
251 }
252
254 quality(ReadView const& sb, QualityDirection qDir) const;
255
256 // Compute the maximum value that can flow from src->dst at
257 // the best available quality.
258 // return: first element is max amount that can flow,
259 // second is the debt direction w.r.t. the source account
261 maxFlow(ReadView const& sb, IOUAmount const& desired) const;
262
263 // Verify the consistency of the step. These checks are specific to
264 // payments and assume that general checks were already performed.
265 TER
266 check(StrandContext const& ctx, std::shared_ptr<const SLE> const& sleSrc)
267 const;
268
270 logString() const override
271 {
272 return logStringImpl("DirectIPaymentStep");
273 }
274};
275
276// Offer crossing DirectStep class (not a payment).
277class DirectIOfferCrossingStep : public DirectStepI<DirectIOfferCrossingStep>
278{
279public:
280 using DirectStepI<DirectIOfferCrossingStep>::DirectStepI;
282
283 bool
285 {
286 // During offer crossing we rely on the fact that prevStepRedeems
287 // will *always* issue. That's because:
288 // o If there's a prevStep_, it will always be a BookStep.
289 // o BookStep::debtDirection() aways returns `issues` when offer
290 // crossing.
291 // An assert based on this return value will tell us if that
292 // behavior changes.
293 return issues(prevStepDir);
294 }
295
296 bool
298 {
299 // Due to a couple of factors dstQIn is always QUALITY_ONE for
300 // offer crossing. If that changes we need to know.
301 return dstQIn == QUALITY_ONE;
302 }
303
305 quality(ReadView const& sb, QualityDirection qDir) const;
306
307 // Compute the maximum value that can flow from src->dst at
308 // the best available quality.
309 // return: first element is max amount that can flow,
310 // second is the debt direction w.r.t the source
312 maxFlow(ReadView const& sb, IOUAmount const& desired) const;
313
314 // Verify the consistency of the step. These checks are specific to
315 // offer crossing and assume that general checks were already performed.
316 TER
317 check(StrandContext const& ctx, std::shared_ptr<const SLE> const& sleSrc)
318 const;
319
321 logString() const override
322 {
323 return logStringImpl("DirectIOfferCrossingStep");
324 }
325};
326
327//------------------------------------------------------------------------------
328
331{
332 if (src_ == dst_)
333 return QUALITY_ONE;
334
335 auto const sle = sb.read(keylet::line(dst_, src_, currency_));
336
337 if (!sle)
338 return QUALITY_ONE;
339
340 auto const& field = [&, this]() -> SF_UINT32 const& {
341 if (qDir == QualityDirection::in)
342 {
343 // compute dst quality in
344 if (this->dst_ < this->src_)
345 return sfLowQualityIn;
346 else
347 return sfHighQualityIn;
348 }
349 else
350 {
351 // compute src quality out
352 if (this->src_ < this->dst_)
353 return sfLowQualityOut;
354 else
355 return sfHighQualityOut;
356 }
357 }();
358
359 if (!sle->isFieldPresent(field))
360 return QUALITY_ONE;
361
362 auto const q = (*sle)[field];
363 if (!q)
364 return QUALITY_ONE;
365 return q;
366}
367
370{
371 // If offer crossing then ignore trust line Quality fields. This
372 // preserves a long-standing tradition.
373 return QUALITY_ONE;
374}
375
378{
379 return maxPaymentFlow(sb);
380}
381
384 const
385{
386 // When isLast and offer crossing then ignore trust line limits. Offer
387 // crossing has the ability to exceed the limit set by a trust line.
388 // We presume that if someone is creating an offer then they intend to
389 // fill as much of that offer as possible, even if the offer exceeds
390 // the limit that a trust line sets.
391 //
392 // A note on using "out" as the desired parameter for maxFlow. In some
393 // circumstances during payments we end up needing a value larger than
394 // "out" for "maxSrcToDst". But as of now (June 2016) that never happens
395 // during offer crossing. That's because, due to a couple of factors,
396 // "dstQIn" is always QUALITY_ONE for offer crossing.
397
398 if (isLast_)
399 return {desired, DebtDirection::issues};
400
401 return maxPaymentFlow(sb);
402}
403
404TER
406 StrandContext const& ctx,
407 std::shared_ptr<const SLE> const& sleSrc) const
408{
409 // Since this is a payment a trust line must be present. Perform all
410 // trust line related checks.
411 {
412 auto const sleLine = ctx.view.read(keylet::line(src_, dst_, currency_));
413 if (!sleLine)
414 {
415 JLOG(j_.trace()) << "DirectStepI: No credit line. " << *this;
416 return terNO_LINE;
417 }
418
419 auto const authField = (src_ > dst_) ? lsfHighAuth : lsfLowAuth;
420
421 if (((*sleSrc)[sfFlags] & lsfRequireAuth) &&
422 !((*sleLine)[sfFlags] & authField) &&
423 (*sleLine)[sfBalance] == beast::zero)
424 {
425 JLOG(j_.warn())
426 << "DirectStepI: can't receive IOUs from issuer without auth."
427 << " src: " << src_;
428 return terNO_AUTH;
429 }
430
431 if (ctx.prevStep)
432 {
433 if (ctx.prevStep->bookStepBook())
434 {
435 auto const noRippleSrcToDst =
436 ((*sleLine)[sfFlags] &
438 if (noRippleSrcToDst)
439 return terNO_RIPPLE;
440 }
441 }
442 }
443
444 {
445 auto const owed = creditBalance(ctx.view, dst_, src_, currency_);
446 if (owed <= beast::zero)
447 {
448 auto const limit = creditLimit(ctx.view, dst_, src_, currency_);
449 if (-owed >= limit)
450 {
451 JLOG(j_.debug()) << "DirectStepI: dry: owed: " << owed
452 << " limit: " << limit;
453 return tecPATH_DRY;
454 }
455 }
456 }
457 return tesSUCCESS;
458}
459
460TER
462 StrandContext const&,
463 std::shared_ptr<const SLE> const&) const
464{
465 // The standard checks are all we can do because any remaining checks
466 // require the existence of a trust line. Offer crossing does not
467 // require a pre-existing trust line.
468 return tesSUCCESS;
469}
470
471//------------------------------------------------------------------------------
472
473template <class TDerived>
476{
477 auto const srcOwed = toAmount<IOUAmount>(
478 accountHolds(sb, src_, currency_, dst_, fhIGNORE_FREEZE, j_));
479
480 if (srcOwed.signum() > 0)
481 return {srcOwed, DebtDirection::redeems};
482
483 // srcOwed is negative or zero
484 return {
485 creditLimit2(sb, dst_, src_, currency_) + srcOwed,
487}
488
489template <class TDerived>
492 const
493{
494 if (dir == StrandDirection::forward && cache_)
495 return cache_->srcDebtDir;
496
497 auto const srcOwed =
498 accountHolds(sb, src_, currency_, dst_, fhIGNORE_FREEZE, j_);
499 return srcOwed.signum() > 0 ? DebtDirection::redeems
501}
502
503template <class TDerived>
506 PaymentSandbox& sb,
507 ApplyView& /*afView*/,
508 boost::container::flat_set<uint256>& /*ofrsToRm*/,
509 IOUAmount const& out)
510{
511 cache_.reset();
512
513 auto const [maxSrcToDst, srcDebtDir] =
514 static_cast<TDerived const*>(this)->maxFlow(sb, out);
515
516 auto const [srcQOut, dstQIn] =
517 qualities(sb, srcDebtDir, StrandDirection::reverse);
518 XRPL_ASSERT(
519 static_cast<TDerived const*>(this)->verifyDstQualityIn(dstQIn),
520 "ripple::DirectStepI : valid destination quality");
521
522 Issue const srcToDstIss(currency_, redeems(srcDebtDir) ? dst_ : src_);
523
524 JLOG(j_.trace()) << "DirectStepI::rev"
525 << " srcRedeems: " << redeems(srcDebtDir)
526 << " outReq: " << to_string(out)
527 << " maxSrcToDst: " << to_string(maxSrcToDst)
528 << " srcQOut: " << srcQOut << " dstQIn: " << dstQIn;
529
530 if (maxSrcToDst.signum() <= 0)
531 {
532 JLOG(j_.trace()) << "DirectStepI::rev: dry";
533 cache_.emplace(
534 IOUAmount(beast::zero),
535 IOUAmount(beast::zero),
536 IOUAmount(beast::zero),
537 srcDebtDir);
538 return {beast::zero, beast::zero};
539 }
540
541 IOUAmount const srcToDst =
542 mulRatio(out, QUALITY_ONE, dstQIn, /*roundUp*/ true);
543
544 if (srcToDst <= maxSrcToDst)
545 {
546 IOUAmount const in =
547 mulRatio(srcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true);
548 cache_.emplace(in, srcToDst, out, srcDebtDir);
550 sb,
551 src_,
552 dst_,
553 toSTAmount(srcToDst, srcToDstIss),
554 /*checkIssuer*/ true,
555 j_);
556 JLOG(j_.trace()) << "DirectStepI::rev: Non-limiting"
557 << " srcRedeems: " << redeems(srcDebtDir)
558 << " in: " << to_string(in)
559 << " srcToDst: " << to_string(srcToDst)
560 << " out: " << to_string(out);
561 return {in, out};
562 }
563
564 // limiting node
565 IOUAmount const in =
566 mulRatio(maxSrcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true);
567 IOUAmount const actualOut =
568 mulRatio(maxSrcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false);
569 cache_.emplace(in, maxSrcToDst, actualOut, srcDebtDir);
571 sb,
572 src_,
573 dst_,
574 toSTAmount(maxSrcToDst, srcToDstIss),
575 /*checkIssuer*/ true,
576 j_);
577 JLOG(j_.trace()) << "DirectStepI::rev: Limiting"
578 << " srcRedeems: " << redeems(srcDebtDir)
579 << " in: " << to_string(in)
580 << " srcToDst: " << to_string(maxSrcToDst)
581 << " out: " << to_string(out);
582 return {in, actualOut};
583}
584
585// The forward pass should never have more liquidity than the reverse
586// pass. But sometimes rounding differences cause the forward pass to
587// deliver more liquidity. Use the cached values from the reverse pass
588// to prevent this.
589template <class TDerived>
590void
592 IOUAmount const& fwdIn,
593 IOUAmount const& fwdSrcToDst,
594 IOUAmount const& fwdOut,
595 DebtDirection srcDebtDir)
596{
597 if (cache_->in < fwdIn)
598 {
599 IOUAmount const smallDiff(1, -9);
600 auto const diff = fwdIn - cache_->in;
601 if (diff > smallDiff)
602 {
603 if (fwdIn.exponent() != cache_->in.exponent() ||
604 !cache_->in.mantissa() ||
605 (double(fwdIn.mantissa()) / double(cache_->in.mantissa())) >
606 1.01)
607 {
608 // Detect large diffs on forward pass so they may be
609 // investigated
610 JLOG(j_.warn())
611 << "DirectStepI::fwd: setCacheLimiting"
612 << " fwdIn: " << to_string(fwdIn)
613 << " cacheIn: " << to_string(cache_->in)
614 << " fwdSrcToDst: " << to_string(fwdSrcToDst)
615 << " cacheSrcToDst: " << to_string(cache_->srcToDst)
616 << " fwdOut: " << to_string(fwdOut)
617 << " cacheOut: " << to_string(cache_->out);
618 cache_.emplace(fwdIn, fwdSrcToDst, fwdOut, srcDebtDir);
619 return;
620 }
621 }
622 }
623 cache_->in = fwdIn;
624 if (fwdSrcToDst < cache_->srcToDst)
625 cache_->srcToDst = fwdSrcToDst;
626 if (fwdOut < cache_->out)
627 cache_->out = fwdOut;
628 cache_->srcDebtDir = srcDebtDir;
629};
630
631template <class TDerived>
634 PaymentSandbox& sb,
635 ApplyView& /*afView*/,
636 boost::container::flat_set<uint256>& /*ofrsToRm*/,
637 IOUAmount const& in)
638{
639 XRPL_ASSERT(cache_, "ripple::DirectStepI::fwdImp : cache is set");
640
641 auto const [maxSrcToDst, srcDebtDir] =
642 static_cast<TDerived const*>(this)->maxFlow(sb, cache_->srcToDst);
643
644 auto const [srcQOut, dstQIn] =
645 qualities(sb, srcDebtDir, StrandDirection::forward);
646
647 Issue const srcToDstIss(currency_, redeems(srcDebtDir) ? dst_ : src_);
648
649 JLOG(j_.trace()) << "DirectStepI::fwd"
650 << " srcRedeems: " << redeems(srcDebtDir)
651 << " inReq: " << to_string(in)
652 << " maxSrcToDst: " << to_string(maxSrcToDst)
653 << " srcQOut: " << srcQOut << " dstQIn: " << dstQIn;
654
655 if (maxSrcToDst.signum() <= 0)
656 {
657 JLOG(j_.trace()) << "DirectStepI::fwd: dry";
658 cache_.emplace(
659 IOUAmount(beast::zero),
660 IOUAmount(beast::zero),
661 IOUAmount(beast::zero),
662 srcDebtDir);
663 return {beast::zero, beast::zero};
664 }
665
666 IOUAmount const srcToDst =
667 mulRatio(in, QUALITY_ONE, srcQOut, /*roundUp*/ false);
668
669 if (srcToDst <= maxSrcToDst)
670 {
671 IOUAmount const out =
672 mulRatio(srcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false);
673 setCacheLimiting(in, srcToDst, out, srcDebtDir);
675 sb,
676 src_,
677 dst_,
678 toSTAmount(cache_->srcToDst, srcToDstIss),
679 /*checkIssuer*/ true,
680 j_);
681 JLOG(j_.trace()) << "DirectStepI::fwd: Non-limiting"
682 << " srcRedeems: " << redeems(srcDebtDir)
683 << " in: " << to_string(in)
684 << " srcToDst: " << to_string(srcToDst)
685 << " out: " << to_string(out);
686 }
687 else
688 {
689 // limiting node
690 IOUAmount const actualIn =
691 mulRatio(maxSrcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true);
692 IOUAmount const out =
693 mulRatio(maxSrcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false);
694 setCacheLimiting(actualIn, maxSrcToDst, out, srcDebtDir);
696 sb,
697 src_,
698 dst_,
699 toSTAmount(cache_->srcToDst, srcToDstIss),
700 /*checkIssuer*/ true,
701 j_);
702 JLOG(j_.trace()) << "DirectStepI::rev: Limiting"
703 << " srcRedeems: " << redeems(srcDebtDir)
704 << " in: " << to_string(actualIn)
705 << " srcToDst: " << to_string(srcToDst)
706 << " out: " << to_string(out);
707 }
708 return {cache_->in, cache_->out};
709}
710
711template <class TDerived>
714 PaymentSandbox& sb,
715 ApplyView& afView,
716 EitherAmount const& in)
717{
718 if (!cache_)
719 {
720 JLOG(j_.trace()) << "Expected valid cache in validFwd";
721 return {false, EitherAmount(IOUAmount(beast::zero))};
722 }
723
724 auto const savCache = *cache_;
725
726 XRPL_ASSERT(!in.native, "ripple::DirectStepI::validFwd : input is not XRP");
727
728 auto const [maxSrcToDst, srcDebtDir] =
729 static_cast<TDerived const*>(this)->maxFlow(sb, cache_->srcToDst);
730 (void)srcDebtDir;
731
732 try
733 {
734 boost::container::flat_set<uint256> dummy;
735 fwdImp(sb, afView, dummy, in.iou); // changes cache
736 }
737 catch (FlowException const&)
738 {
739 return {false, EitherAmount(IOUAmount(beast::zero))};
740 }
741
742 if (maxSrcToDst < cache_->srcToDst)
743 {
744 JLOG(j_.warn()) << "DirectStepI: Strand re-execute check failed."
745 << " Exceeded max src->dst limit"
746 << " max src->dst: " << to_string(maxSrcToDst)
747 << " actual src->dst: " << to_string(cache_->srcToDst);
748 return {false, EitherAmount(cache_->out)};
749 }
750
751 if (!(checkNear(savCache.in, cache_->in) &&
752 checkNear(savCache.out, cache_->out)))
753 {
754 JLOG(j_.warn()) << "DirectStepI: Strand re-execute check failed."
755 << " ExpectedIn: " << to_string(savCache.in)
756 << " CachedIn: " << to_string(cache_->in)
757 << " ExpectedOut: " << to_string(savCache.out)
758 << " CachedOut: " << to_string(cache_->out);
759 return {false, EitherAmount(cache_->out)};
760 }
761 return {true, EitherAmount(cache_->out)};
762}
763
764// Returns srcQOut, dstQIn
765template <class TDerived>
768{
769 if (!prevStep_)
770 return {QUALITY_ONE, QUALITY_ONE};
771
772 auto const prevStepQIn = prevStep_->lineQualityIn(sb);
773 auto srcQOut =
774 static_cast<TDerived const*>(this)->quality(sb, QualityDirection::out);
775
776 if (prevStepQIn > srcQOut)
777 srcQOut = prevStepQIn;
778 return {srcQOut, QUALITY_ONE};
779}
780
781// Returns srcQOut, dstQIn
782template <class TDerived>
785 ReadView const& sb,
786 DebtDirection prevStepDebtDirection) const
787{
788 // Charge a transfer rate when issuing and previous step redeems
789
790 XRPL_ASSERT(
791 static_cast<TDerived const*>(this)->verifyPrevStepDebtDirection(
792 prevStepDebtDirection),
793 "ripple::DirectStepI::qualitiesSrcIssues : will prevStepDebtDirection "
794 "issue");
795
796 std::uint32_t const srcQOut = redeems(prevStepDebtDirection)
797 ? transferRate(sb, src_).value
798 : QUALITY_ONE;
799 auto dstQIn =
800 static_cast<TDerived const*>(this)->quality(sb, QualityDirection::in);
801
802 if (isLast_ && dstQIn > QUALITY_ONE)
803 dstQIn = QUALITY_ONE;
804 return {srcQOut, dstQIn};
805}
806
807// Returns srcQOut, dstQIn
808template <class TDerived>
811 ReadView const& sb,
812 DebtDirection srcDebtDir,
813 StrandDirection strandDir) const
814{
815 if (redeems(srcDebtDir))
816 {
817 return qualitiesSrcRedeems(sb);
818 }
819 else
820 {
821 auto const prevStepDebtDirection = [&] {
822 if (prevStep_)
823 return prevStep_->debtDirection(sb, strandDir);
825 }();
826 return qualitiesSrcIssues(sb, prevStepDebtDirection);
827 }
828}
829
830template <class TDerived>
833{
834 // dst quality in
835 return static_cast<TDerived const*>(this)->quality(v, QualityDirection::in);
836}
837
838template <class TDerived>
841 ReadView const& v,
842 DebtDirection prevStepDir) const
843{
844 auto const dir = this->debtDirection(v, StrandDirection::forward);
845
846 if (!v.rules().enabled(fixQualityUpperBound))
847 {
848 std::uint32_t const srcQOut = [&]() -> std::uint32_t {
849 if (redeems(prevStepDir) && issues(dir))
850 return transferRate(v, src_).value;
851 return QUALITY_ONE;
852 }();
853 auto dstQIn = static_cast<TDerived const*>(this)->quality(
855
856 if (isLast_ && dstQIn > QUALITY_ONE)
857 dstQIn = QUALITY_ONE;
858 Issue const iss{currency_, src_};
859 return {
860 Quality(getRate(STAmount(iss, srcQOut), STAmount(iss, dstQIn))),
861 dir};
862 }
863
864 auto const [srcQOut, dstQIn] = redeems(dir)
865 ? qualitiesSrcRedeems(v)
866 : qualitiesSrcIssues(v, prevStepDir);
867
868 Issue const iss{currency_, src_};
869 // Be careful not to switch the parameters to `getRate`. The
870 // `getRate(offerOut, offerIn)` function is usually used for offers. It
871 // returns offerIn/offerOut. For a direct step, the rate is srcQOut/dstQIn
872 // (Input*dstQIn/srcQOut = Output; So rate = srcQOut/dstQIn). Although the
873 // first parameter is called `offerOut`, it should take the `dstQIn`
874 // variable.
875 return {
876 Quality(getRate(STAmount(iss, dstQIn), STAmount(iss, srcQOut))), dir};
877}
878
879template <class TDerived>
880TER
882{
883 // The following checks apply for both payments and offer crossing.
884 if (!src_ || !dst_)
885 {
886 JLOG(j_.debug()) << "DirectStepI: specified bad account.";
887 return temBAD_PATH;
888 }
889
890 if (src_ == dst_)
891 {
892 JLOG(j_.debug()) << "DirectStepI: same src and dst.";
893 return temBAD_PATH;
894 }
895
896 auto const sleSrc = ctx.view.read(keylet::account(src_));
897 if (!sleSrc)
898 {
899 JLOG(j_.warn())
900 << "DirectStepI: can't receive IOUs from non-existent issuer: "
901 << src_;
902 return terNO_ACCOUNT;
903 }
904
905 // pure issue/redeem can't be frozen
906 if (!(ctx.isLast && ctx.isFirst))
907 {
908 auto const ter = checkFreeze(ctx.view, src_, dst_, currency_);
909 if (ter != tesSUCCESS)
910 return ter;
911 }
912
913 // If previous step was a direct step then we need to check
914 // no ripple flags.
915 if (ctx.prevStep)
916 {
917 if (auto prevSrc = ctx.prevStep->directStepSrcAcct())
918 {
919 auto const ter =
920 checkNoRipple(ctx.view, *prevSrc, src_, dst_, currency_, j_);
921 if (ter != tesSUCCESS)
922 return ter;
923 }
924 }
925 {
926 Issue const srcIssue{currency_, src_};
927 Issue const dstIssue{currency_, dst_};
928
929 if (ctx.seenBookOuts.count(srcIssue))
930 {
931 if (!ctx.prevStep)
932 {
933 UNREACHABLE(
934 "ripple::DirectStepI::check : prev seen book without a "
935 "prev step");
936 return temBAD_PATH_LOOP;
937 }
938
939 // This is OK if the previous step is a book step that outputs this
940 // issue
941 if (auto book = ctx.prevStep->bookStepBook())
942 {
943 if (book->out != srcIssue)
944 return temBAD_PATH_LOOP;
945 }
946 }
947
948 if (!ctx.seenDirectIssues[0].insert(srcIssue).second ||
949 !ctx.seenDirectIssues[1].insert(dstIssue).second)
950 {
951 JLOG(j_.debug())
952 << "DirectStepI: loop detected: Index: " << ctx.strandSize
953 << ' ' << *this;
954 return temBAD_PATH_LOOP;
955 }
956 }
957
958 return static_cast<TDerived const*>(this)->check(ctx, sleSrc);
959}
960
961//------------------------------------------------------------------------------
962
963namespace test {
964// Needed for testing
965bool
967 Step const& step,
968 AccountID const& src,
969 AccountID const& dst,
970 Currency const& currency)
971{
972 if (auto ds = dynamic_cast<DirectStepI<DirectIPaymentStep> const*>(&step))
973 {
974 return ds->src() == src && ds->dst() == dst &&
975 ds->currency() == currency;
976 }
977 return false;
978}
979} // namespace test
980
981//------------------------------------------------------------------------------
982
985 StrandContext const& ctx,
986 AccountID const& src,
987 AccountID const& dst,
988 Currency const& c)
989{
990 TER ter = tefINTERNAL;
992 if (ctx.offerCrossing)
993 {
994 auto offerCrossingStep =
995 std::make_unique<DirectIOfferCrossingStep>(ctx, src, dst, c);
996 ter = offerCrossingStep->check(ctx);
997 r = std::move(offerCrossingStep);
998 }
999 else // payment
1000 {
1001 auto paymentStep =
1002 std::make_unique<DirectIPaymentStep>(ctx, src, dst, c);
1003 ter = paymentStep->check(ctx);
1004 r = std::move(paymentStep);
1005 }
1006 if (ter != tesSUCCESS)
1007 return {ter, nullptr};
1008
1009 return {tesSUCCESS, std::move(r)};
1010}
1011
1012} // 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:140
std::pair< IOUAmount, DebtDirection > maxFlow(ReadView const &sb, IOUAmount const &desired) const
Definition: DirectStep.cpp:383
std::string logString() const override
Definition: DirectStep.cpp:321
std::uint32_t quality(ReadView const &sb, QualityDirection qDir) const
Definition: DirectStep.cpp:369
bool verifyPrevStepDebtDirection(DebtDirection prevStepDir) const
Definition: DirectStep.cpp:284
bool verifyDstQualityIn(std::uint32_t dstQIn) const
Definition: DirectStep.cpp:297
TER check(StrandContext const &ctx, std::shared_ptr< const SLE > const &sleSrc) const
Definition: DirectStep.cpp:461
bool verifyDstQualityIn(std::uint32_t dstQIn) const
Definition: DirectStep.cpp:247
std::uint32_t quality(ReadView const &sb, QualityDirection qDir) const
Definition: DirectStep.cpp:330
std::string logString() const override
Definition: DirectStep.cpp:270
std::pair< IOUAmount, DebtDirection > maxFlow(ReadView const &sb, IOUAmount const &desired) const
Definition: DirectStep.cpp:377
TER check(StrandContext const &ctx, std::shared_ptr< const SLE > const &sleSrc) const
Definition: DirectStep.cpp:405
bool verifyPrevStepDebtDirection(DebtDirection) const
Definition: DirectStep.cpp:240
DebtDirection debtDirection(ReadView const &sb, StrandDirection dir) const override
Definition: DirectStep.cpp:491
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcRedeems(ReadView const &sb) const
Definition: DirectStep.cpp:767
std::pair< IOUAmount, DebtDirection > maxPaymentFlow(ReadView const &sb) const
Definition: DirectStep.cpp:475
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:505
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:633
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcIssues(ReadView const &sb, DebtDirection prevStepDebtDirection) const
Definition: DirectStep.cpp:784
void setCacheLimiting(IOUAmount const &fwdIn, IOUAmount const &fwdSrcToDst, IOUAmount const &fwdOut, DebtDirection srcDebtDir)
Definition: DirectStep.cpp:591
std::uint32_t lineQualityIn(ReadView const &v) const override
Definition: DirectStep.cpp:832
TER check(StrandContext const &ctx) const
Definition: DirectStep.cpp:881
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:214
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:810
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:840
std::pair< bool, EitherAmount > validFwd(PaymentSandbox &sb, ApplyView &afView, EitherAmount const &in) override
Definition: DirectStep.cpp:713
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:966
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:33
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition: View.cpp:650
@ 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:88
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:308
std::pair< TER, std::unique_ptr< Step > > make_DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
Definition: DirectStep.cpp:984
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:2099
@ 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