rippled
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 <ripple/app/paths/Credit.h>
21 #include <ripple/app/paths/impl/StepChecks.h>
22 #include <ripple/app/paths/impl/Steps.h>
23 #include <ripple/basics/IOUAmount.h>
24 #include <ripple/basics/Log.h>
25 #include <ripple/ledger/PaymentSandbox.h>
26 #include <ripple/protocol/Feature.h>
27 #include <ripple/protocol/Quality.h>
28 
29 #include <boost/container/flat_set.hpp>
30 
31 #include <numeric>
32 #include <sstream>
33 
34 namespace ripple {
35 
36 template <class TDerived>
37 class DirectStepI : public StepImp<IOUAmount, IOUAmount, DirectStepI<TDerived>>
38 {
39 protected:
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
86  qualities(
87  ReadView const& sb,
88  DebtDirection srcDebtDir,
89  StrandDirection strandDir) const;
90 
91 public:
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 
160  revImp(
161  PaymentSandbox& sb,
162  ApplyView& afView,
163  boost::container::flat_set<uint256>& ofrsToRm,
164  IOUAmount const& out);
165 
167  fwdImp(
168  PaymentSandbox& sb,
169  ApplyView& afView,
170  boost::container::flat_set<uint256>& ofrsToRm,
171  IOUAmount const& in);
172 
174  validFwd(PaymentSandbox& sb, ApplyView& afView, EitherAmount const& in)
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 
202 protected:
204  logStringImpl(char const* name) const
205  {
206  std::ostringstream ostr;
207  ostr << name << ": "
208  << "\nSrc: " << src_ << "\nDst: " << dst_;
209  return ostr.str();
210  }
211 
212 private:
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).
233 class DirectIPaymentStep : public DirectStepI<DirectIPaymentStep>
234 {
235 public:
238 
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).
276 class DirectIOfferCrossingStep : public DirectStepI<DirectIOfferCrossingStep>
277 {
278 public:
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, qDir]() -> 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 
403 TER
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 
459 TER
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 
472 template <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 
488 template <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 
502 template <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  assert(static_cast<TDerived const*>(this)->verifyDstQualityIn(dstQIn));
518 
519  Issue const srcToDstIss(currency_, redeems(srcDebtDir) ? dst_ : src_);
520 
521  JLOG(j_.trace()) << "DirectStepI::rev"
522  << " srcRedeems: " << redeems(srcDebtDir)
523  << " outReq: " << to_string(out)
524  << " maxSrcToDst: " << to_string(maxSrcToDst)
525  << " srcQOut: " << srcQOut << " dstQIn: " << dstQIn;
526 
527  if (maxSrcToDst.signum() <= 0)
528  {
529  JLOG(j_.trace()) << "DirectStepI::rev: dry";
530  cache_.emplace(
531  IOUAmount(beast::zero),
532  IOUAmount(beast::zero),
533  IOUAmount(beast::zero),
534  srcDebtDir);
535  return {beast::zero, beast::zero};
536  }
537 
538  IOUAmount const srcToDst =
539  mulRatio(out, QUALITY_ONE, dstQIn, /*roundUp*/ true);
540 
541  if (srcToDst <= maxSrcToDst)
542  {
543  IOUAmount const in =
544  mulRatio(srcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true);
545  cache_.emplace(in, srcToDst, out, srcDebtDir);
546  rippleCredit(
547  sb,
548  src_,
549  dst_,
550  toSTAmount(srcToDst, srcToDstIss),
551  /*checkIssuer*/ true,
552  j_);
553  JLOG(j_.trace()) << "DirectStepI::rev: Non-limiting"
554  << " srcRedeems: " << redeems(srcDebtDir)
555  << " in: " << to_string(in)
556  << " srcToDst: " << to_string(srcToDst)
557  << " out: " << to_string(out);
558  return {in, out};
559  }
560 
561  // limiting node
562  IOUAmount const in =
563  mulRatio(maxSrcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true);
564  IOUAmount const actualOut =
565  mulRatio(maxSrcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false);
566  cache_.emplace(in, maxSrcToDst, actualOut, srcDebtDir);
567  rippleCredit(
568  sb,
569  src_,
570  dst_,
571  toSTAmount(maxSrcToDst, srcToDstIss),
572  /*checkIssuer*/ true,
573  j_);
574  JLOG(j_.trace()) << "DirectStepI::rev: Limiting"
575  << " srcRedeems: " << redeems(srcDebtDir)
576  << " in: " << to_string(in)
577  << " srcToDst: " << to_string(maxSrcToDst)
578  << " out: " << to_string(out);
579  return {in, actualOut};
580 }
581 
582 // The forward pass should never have more liquidity than the reverse
583 // pass. But sometimes rounding differences cause the forward pass to
584 // deliver more liquidity. Use the cached values from the reverse pass
585 // to prevent this.
586 template <class TDerived>
587 void
589  IOUAmount const& fwdIn,
590  IOUAmount const& fwdSrcToDst,
591  IOUAmount const& fwdOut,
592  DebtDirection srcDebtDir)
593 {
594  if (cache_->in < fwdIn)
595  {
596  IOUAmount const smallDiff(1, -9);
597  auto const diff = fwdIn - cache_->in;
598  if (diff > smallDiff)
599  {
600  if (fwdIn.exponent() != cache_->in.exponent() ||
601  !cache_->in.mantissa() ||
602  (double(fwdIn.mantissa()) / double(cache_->in.mantissa())) >
603  1.01)
604  {
605  // Detect large diffs on forward pass so they may be
606  // investigated
607  JLOG(j_.warn())
608  << "DirectStepI::fwd: setCacheLimiting"
609  << " fwdIn: " << to_string(fwdIn)
610  << " cacheIn: " << to_string(cache_->in)
611  << " fwdSrcToDst: " << to_string(fwdSrcToDst)
612  << " cacheSrcToDst: " << to_string(cache_->srcToDst)
613  << " fwdOut: " << to_string(fwdOut)
614  << " cacheOut: " << to_string(cache_->out);
615  cache_.emplace(fwdIn, fwdSrcToDst, fwdOut, srcDebtDir);
616  return;
617  }
618  }
619  }
620  cache_->in = fwdIn;
621  if (fwdSrcToDst < cache_->srcToDst)
622  cache_->srcToDst = fwdSrcToDst;
623  if (fwdOut < cache_->out)
624  cache_->out = fwdOut;
625  cache_->srcDebtDir = srcDebtDir;
626 };
627 
628 template <class TDerived>
631  PaymentSandbox& sb,
632  ApplyView& /*afView*/,
633  boost::container::flat_set<uint256>& /*ofrsToRm*/,
634  IOUAmount const& in)
635 {
636  assert(cache_);
637 
638  auto const [maxSrcToDst, srcDebtDir] =
639  static_cast<TDerived const*>(this)->maxFlow(sb, cache_->srcToDst);
640 
641  auto const [srcQOut, dstQIn] =
642  qualities(sb, srcDebtDir, StrandDirection::forward);
643 
644  Issue const srcToDstIss(currency_, redeems(srcDebtDir) ? dst_ : src_);
645 
646  JLOG(j_.trace()) << "DirectStepI::fwd"
647  << " srcRedeems: " << redeems(srcDebtDir)
648  << " inReq: " << to_string(in)
649  << " maxSrcToDst: " << to_string(maxSrcToDst)
650  << " srcQOut: " << srcQOut << " dstQIn: " << dstQIn;
651 
652  if (maxSrcToDst.signum() <= 0)
653  {
654  JLOG(j_.trace()) << "DirectStepI::fwd: dry";
655  cache_.emplace(
656  IOUAmount(beast::zero),
657  IOUAmount(beast::zero),
658  IOUAmount(beast::zero),
659  srcDebtDir);
660  return {beast::zero, beast::zero};
661  }
662 
663  IOUAmount const srcToDst =
664  mulRatio(in, QUALITY_ONE, srcQOut, /*roundUp*/ false);
665 
666  if (srcToDst <= maxSrcToDst)
667  {
668  IOUAmount const out =
669  mulRatio(srcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false);
670  setCacheLimiting(in, srcToDst, out, srcDebtDir);
671  rippleCredit(
672  sb,
673  src_,
674  dst_,
675  toSTAmount(cache_->srcToDst, srcToDstIss),
676  /*checkIssuer*/ true,
677  j_);
678  JLOG(j_.trace()) << "DirectStepI::fwd: Non-limiting"
679  << " srcRedeems: " << redeems(srcDebtDir)
680  << " in: " << to_string(in)
681  << " srcToDst: " << to_string(srcToDst)
682  << " out: " << to_string(out);
683  }
684  else
685  {
686  // limiting node
687  IOUAmount const actualIn =
688  mulRatio(maxSrcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true);
689  IOUAmount const out =
690  mulRatio(maxSrcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false);
691  setCacheLimiting(actualIn, maxSrcToDst, out, srcDebtDir);
692  rippleCredit(
693  sb,
694  src_,
695  dst_,
696  toSTAmount(cache_->srcToDst, srcToDstIss),
697  /*checkIssuer*/ true,
698  j_);
699  JLOG(j_.trace()) << "DirectStepI::rev: Limiting"
700  << " srcRedeems: " << redeems(srcDebtDir)
701  << " in: " << to_string(actualIn)
702  << " srcToDst: " << to_string(srcToDst)
703  << " out: " << to_string(out);
704  }
705  return {cache_->in, cache_->out};
706 }
707 
708 template <class TDerived>
711  PaymentSandbox& sb,
712  ApplyView& afView,
713  EitherAmount const& in)
714 {
715  if (!cache_)
716  {
717  JLOG(j_.trace()) << "Expected valid cache in validFwd";
718  return {false, EitherAmount(IOUAmount(beast::zero))};
719  }
720 
721  auto const savCache = *cache_;
722 
723  assert(!in.native);
724 
725  auto const [maxSrcToDst, srcDebtDir] =
726  static_cast<TDerived const*>(this)->maxFlow(sb, cache_->srcToDst);
727  (void)srcDebtDir;
728 
729  try
730  {
731  boost::container::flat_set<uint256> dummy;
732  fwdImp(sb, afView, dummy, in.iou); // changes cache
733  }
734  catch (FlowException const&)
735  {
736  return {false, EitherAmount(IOUAmount(beast::zero))};
737  }
738 
739  if (maxSrcToDst < cache_->srcToDst)
740  {
741  JLOG(j_.warn()) << "DirectStepI: Strand re-execute check failed."
742  << " Exceeded max src->dst limit"
743  << " max src->dst: " << to_string(maxSrcToDst)
744  << " actual src->dst: " << to_string(cache_->srcToDst);
745  return {false, EitherAmount(cache_->out)};
746  }
747 
748  if (!(checkNear(savCache.in, cache_->in) &&
749  checkNear(savCache.out, cache_->out)))
750  {
751  JLOG(j_.warn()) << "DirectStepI: Strand re-execute check failed."
752  << " ExpectedIn: " << to_string(savCache.in)
753  << " CachedIn: " << to_string(cache_->in)
754  << " ExpectedOut: " << to_string(savCache.out)
755  << " CachedOut: " << to_string(cache_->out);
756  return {false, EitherAmount(cache_->out)};
757  }
758  return {true, EitherAmount(cache_->out)};
759 }
760 
761 // Returns srcQOut, dstQIn
762 template <class TDerived>
765 {
766  if (!prevStep_)
767  return {QUALITY_ONE, QUALITY_ONE};
768 
769  auto const prevStepQIn = prevStep_->lineQualityIn(sb);
770  auto srcQOut =
771  static_cast<TDerived const*>(this)->quality(sb, QualityDirection::out);
772 
773  if (prevStepQIn > srcQOut)
774  srcQOut = prevStepQIn;
775  return {srcQOut, QUALITY_ONE};
776 }
777 
778 // Returns srcQOut, dstQIn
779 template <class TDerived>
782  ReadView const& sb,
783  DebtDirection prevStepDebtDirection) const
784 {
785  // Charge a transfer rate when issuing and previous step redeems
786 
787  assert(static_cast<TDerived const*>(this)->verifyPrevStepDebtDirection(
788  prevStepDebtDirection));
789 
790  std::uint32_t const srcQOut = redeems(prevStepDebtDirection)
791  ? transferRate(sb, src_).value
792  : QUALITY_ONE;
793  auto dstQIn =
794  static_cast<TDerived const*>(this)->quality(sb, QualityDirection::in);
795 
796  if (isLast_ && dstQIn > QUALITY_ONE)
797  dstQIn = QUALITY_ONE;
798  return {srcQOut, dstQIn};
799 }
800 
801 // Returns srcQOut, dstQIn
802 template <class TDerived>
805  ReadView const& sb,
806  DebtDirection srcDebtDir,
807  StrandDirection strandDir) const
808 {
809  if (redeems(srcDebtDir))
810  {
811  return qualitiesSrcRedeems(sb);
812  }
813  else
814  {
815  auto const prevStepDebtDirection = [&] {
816  if (prevStep_)
817  return prevStep_->debtDirection(sb, strandDir);
818  return DebtDirection::issues;
819  }();
820  return qualitiesSrcIssues(sb, prevStepDebtDirection);
821  }
822 }
823 
824 template <class TDerived>
827 {
828  // dst quality in
829  return static_cast<TDerived const*>(this)->quality(v, QualityDirection::in);
830 }
831 
832 template <class TDerived>
835  ReadView const& v,
836  DebtDirection prevStepDir) const
837 {
838  auto const dir = this->debtDirection(v, StrandDirection::forward);
839 
841  {
842  std::uint32_t const srcQOut = [&]() -> std::uint32_t {
843  if (redeems(prevStepDir) && issues(dir))
844  return transferRate(v, src_).value;
845  return QUALITY_ONE;
846  }();
847  auto dstQIn = static_cast<TDerived const*>(this)->quality(
849 
850  if (isLast_ && dstQIn > QUALITY_ONE)
851  dstQIn = QUALITY_ONE;
852  Issue const iss{currency_, src_};
853  return {
854  Quality(getRate(STAmount(iss, srcQOut), STAmount(iss, dstQIn))),
855  dir};
856  }
857 
858  auto const [srcQOut, dstQIn] = redeems(dir)
859  ? qualitiesSrcRedeems(v)
860  : qualitiesSrcIssues(v, prevStepDir);
861 
862  Issue const iss{currency_, src_};
863  // Be careful not to switch the parameters to `getRate`. The
864  // `getRate(offerOut, offerIn)` function is usually used for offers. It
865  // returns offerIn/offerOut. For a direct step, the rate is srcQOut/dstQIn
866  // (Input*dstQIn/srcQOut = Output; So rate = srcQOut/dstQIn). Although the
867  // first parameter is called `offerOut`, it should take the `dstQIn`
868  // variable.
869  return {
870  Quality(getRate(STAmount(iss, dstQIn), STAmount(iss, srcQOut))), dir};
871 }
872 
873 template <class TDerived>
874 TER
876 {
877  // The following checks apply for both payments and offer crossing.
878  if (!src_ || !dst_)
879  {
880  JLOG(j_.debug()) << "DirectStepI: specified bad account.";
881  return temBAD_PATH;
882  }
883 
884  if (src_ == dst_)
885  {
886  JLOG(j_.debug()) << "DirectStepI: same src and dst.";
887  return temBAD_PATH;
888  }
889 
890  auto const sleSrc = ctx.view.read(keylet::account(src_));
891  if (!sleSrc)
892  {
893  JLOG(j_.warn())
894  << "DirectStepI: can't receive IOUs from non-existent issuer: "
895  << src_;
896  return terNO_ACCOUNT;
897  }
898 
899  // pure issue/redeem can't be frozen
900  if (!(ctx.isLast && ctx.isFirst))
901  {
902  auto const ter = checkFreeze(ctx.view, src_, dst_, currency_);
903  if (ter != tesSUCCESS)
904  return ter;
905  }
906 
907  // If previous step was a direct step then we need to check
908  // no ripple flags.
909  if (ctx.prevStep)
910  {
911  if (auto prevSrc = ctx.prevStep->directStepSrcAcct())
912  {
913  auto const ter =
914  checkNoRipple(ctx.view, *prevSrc, src_, dst_, currency_, j_);
915  if (ter != tesSUCCESS)
916  return ter;
917  }
918  }
919  {
920  Issue const srcIssue{currency_, src_};
921  Issue const dstIssue{currency_, dst_};
922 
923  if (ctx.seenBookOuts.count(srcIssue))
924  {
925  if (!ctx.prevStep)
926  {
927  assert(0); // prev seen book without a prev step!?!
928  return temBAD_PATH_LOOP;
929  }
930 
931  // This is OK if the previous step is a book step that outputs this
932  // issue
933  if (auto book = ctx.prevStep->bookStepBook())
934  {
935  if (book->out != srcIssue)
936  return temBAD_PATH_LOOP;
937  }
938  }
939 
940  if (!ctx.seenDirectIssues[0].insert(srcIssue).second ||
941  !ctx.seenDirectIssues[1].insert(dstIssue).second)
942  {
943  JLOG(j_.debug())
944  << "DirectStepI: loop detected: Index: " << ctx.strandSize
945  << ' ' << *this;
946  return temBAD_PATH_LOOP;
947  }
948  }
949 
950  return static_cast<TDerived const*>(this)->check(ctx, sleSrc);
951 }
952 
953 //------------------------------------------------------------------------------
954 
955 namespace test {
956 // Needed for testing
957 bool
959  Step const& step,
960  AccountID const& src,
961  AccountID const& dst,
962  Currency const& currency)
963 {
964  if (auto ds = dynamic_cast<DirectStepI<DirectIPaymentStep> const*>(&step))
965  {
966  return ds->src() == src && ds->dst() == dst &&
967  ds->currency() == currency;
968  }
969  return false;
970 }
971 } // namespace test
972 
973 //------------------------------------------------------------------------------
974 
977  StrandContext const& ctx,
978  AccountID const& src,
979  AccountID const& dst,
980  Currency const& c)
981 {
982  TER ter = tefINTERNAL;
984  if (ctx.offerCrossing)
985  {
986  auto offerCrossingStep =
987  std::make_unique<DirectIOfferCrossingStep>(ctx, src, dst, c);
988  ter = offerCrossingStep->check(ctx);
989  r = std::move(offerCrossingStep);
990  }
991  else // payment
992  {
993  auto paymentStep =
994  std::make_unique<DirectIPaymentStep>(ctx, src, dst, c);
995  ter = paymentStep->check(ctx);
996  r = std::move(paymentStep);
997  }
998  if (ter != tesSUCCESS)
999  return {ter, nullptr};
1000 
1001  return {tesSUCCESS, std::move(r)};
1002 }
1003 
1004 } // namespace ripple
ripple::DirectStepI::Cache
Definition: DirectStep.cpp:49
ripple::rippleCredit
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Definition: View.cpp:933
ripple::mulRatio
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
Definition: IOUAmount.cpp:182
ripple::StrandContext
Context needed to build Strand Steps and for error checking.
Definition: Steps.h:497
ripple::sfHighQualityIn
const SF_UINT32 sfHighQualityIn
ripple::StrandContext::strandSize
const size_t strandSize
Length of Strand.
Definition: Steps.h:509
ripple::transferRate
Rate transferRate(ReadView const &view, AccountID const &issuer)
Definition: View.cpp:471
sstream
ripple::issues
bool issues(DebtDirection dir)
Definition: Steps.h:48
ripple::DirectIPaymentStep::quality
std::uint32_t quality(ReadView const &sb, QualityDirection qDir) const
Definition: DirectStep.cpp:329
ripple::DirectStepI::maxPaymentFlow
std::pair< IOUAmount, DebtDirection > maxPaymentFlow(ReadView const &sb) const
Definition: DirectStep.cpp:474
ripple::fixQualityUpperBound
const uint256 fixQualityUpperBound
ripple::IOUAmount::exponent
int exponent() const noexcept
Definition: IOUAmount.h:163
ripple::Issue
A currency issued by an account.
Definition: Issue.h:34
ripple::tefINTERNAL
@ tefINTERNAL
Definition: TER.h:155
std::string
STL class.
ripple::Rules::enabled
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:94
std::shared_ptr
STL class.
ripple::creditBalance
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:61
ripple::TypedField
A field with a type known at compile time.
Definition: SField.h:271
ripple::DirectIPaymentStep::verifyDstQualityIn
bool verifyDstQualityIn(std::uint32_t dstQIn) const
Definition: DirectStep.cpp:246
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::terNO_LINE
@ terNO_LINE
Definition: TER.h:200
ripple::DebtDirection
DebtDirection
Definition: Steps.h:37
ripple::PaymentSandbox
A wrapper which makes credits unavailable to balances.
Definition: PaymentSandbox.h:112
ripple::Step::directStepSrcAcct
virtual std::optional< AccountID > directStepSrcAcct() const
If this step is DirectStepI (IOU->IOU direct step), return the src account.
Definition: Steps.h:139
std::pair
ripple::DirectIPaymentStep
Definition: DirectStep.cpp:233
ripple::lsfLowAuth
@ lsfLowAuth
Definition: LedgerFormats.h:254
ripple::DirectStepI::qualities
std::pair< std::uint32_t, std::uint32_t > qualities(ReadView const &sb, DebtDirection srcDebtDir, StrandDirection strandDir) const
Definition: DirectStep.cpp:804
ripple::DirectStepI::cache_
std::optional< Cache > cache_
Definition: DirectStep.cpp:66
ripple::lsfLowNoRipple
@ lsfLowNoRipple
Definition: LedgerFormats.h:256
ripple::DirectIOfferCrossingStep::verifyPrevStepDebtDirection
bool verifyPrevStepDebtDirection(DebtDirection prevStepDir) const
Definition: DirectStep.cpp:283
ripple::accountHolds
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition: View.cpp:223
ripple::DirectStepI::qualityUpperBound
std::pair< std::optional< Quality >, DebtDirection > qualityUpperBound(ReadView const &v, DebtDirection dir) const override
Definition: DirectStep.cpp:834
ripple::DirectStepI::check
TER check(StrandContext const &ctx) const
Definition: DirectStep.cpp:875
ripple::DirectStepI::src
AccountID const & src() const
Definition: DirectStep.cpp:107
ripple::DirectStepI::qualitiesSrcIssues
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcIssues(ReadView const &sb, DebtDirection prevStepDebtDirection) const
Definition: DirectStep.cpp:781
ripple::checkNear
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
Definition: PaySteps.cpp:36
ripple::DirectStepI::Cache::Cache
Cache(IOUAmount const &in_, IOUAmount const &srcToDst_, IOUAmount const &out_, DebtDirection srcDebtDir_)
Definition: DirectStep.cpp:56
ripple::DirectStepI
Definition: DirectStep.cpp:37
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
ripple::DirectStepI::currency
Currency const & currency() const
Definition: DirectStep.cpp:117
ripple::DirectStepI::operator==
friend bool operator==(DirectStepI const &lhs, DirectStepI const &rhs)
Definition: DirectStep.cpp:190
ripple::make_DirectStepI
std::pair< TER, std::unique_ptr< Step > > make_DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
Definition: DirectStep.cpp:976
ripple::QualityDirection::in
@ in
ripple::creditLimit2
IOUAmount creditLimit2(ReadView const &v, AccountID const &acc, AccountID const &iss, Currency const &cur)
Definition: Credit.cpp:51
ripple::DirectStepI::DirectStepI
DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
Definition: DirectStep.cpp:92
ripple::IOUAmount
Floating point representation of amounts with high dynamic range.
Definition: IOUAmount.h:43
ripple::DirectIOfferCrossingStep::verifyDstQualityIn
bool verifyDstQualityIn(std::uint32_t dstQIn) const
Definition: DirectStep.cpp:296
ripple::terNO_RIPPLE
@ terNO_RIPPLE
Definition: TER.h:205
ripple::DirectIOfferCrossingStep::quality
std::uint32_t quality(ReadView const &sb, QualityDirection qDir) const
Definition: DirectStep.cpp:368
ripple::temBAD_PATH
@ temBAD_PATH
Definition: TER.h:94
ripple::DirectIOfferCrossingStep
Definition: DirectStep.cpp:276
ripple::toAmount< IOUAmount >
IOUAmount toAmount< IOUAmount >(STAmount const &amt)
Definition: AmountConversions.h:77
ripple::ApplyView
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:134
ripple::lsfHighAuth
@ lsfHighAuth
Definition: LedgerFormats.h:255
ripple::StrandDirection
StrandDirection
Definition: Steps.h:39
ripple::DirectStepI::Cache::srcToDst
IOUAmount srcToDst
Definition: DirectStep.cpp:52
ripple::DirectIPaymentStep::logString
std::string logString() const override
Definition: DirectStep.cpp:269
ripple::DirectIPaymentStep::check
TER check(StrandContext const &ctx, std::shared_ptr< const SLE > const &sleSrc) const
Definition: DirectStep.cpp:404
ripple::getRate
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition: STAmount.cpp:495
ripple::checkNoRipple
TER checkNoRipple(ReadView const &view, AccountID const &prev, AccountID const &cur, AccountID const &next, Currency const &currency, beast::Journal j)
Definition: StepChecks.h:61
ripple::DirectStepI::setCacheLimiting
void setCacheLimiting(IOUAmount const &fwdIn, IOUAmount const &fwdSrcToDst, IOUAmount const &fwdOut, DebtDirection srcDebtDir)
Definition: DirectStep.cpp:588
ripple::base_uint< 160, detail::AccountIDTag >
ripple::DirectStepI::operator!=
friend bool operator!=(DirectStepI const &lhs, DirectStepI const &rhs)
Definition: DirectStep.cpp:197
ripple::sfLowQualityOut
const SF_UINT32 sfLowQualityOut
ripple::lsfRequireAuth
@ lsfRequireAuth
Definition: LedgerFormats.h:226
ripple::DirectStepI::currency_
Currency currency_
Definition: DirectStep.cpp:42
ripple::Step::bookStepBook
virtual std::optional< Book > bookStepBook() const
If this step is a BookStep, return the book.
Definition: Steps.h:208
ripple::StrandContext::view
ReadView const & view
Current ReadView.
Definition: Steps.h:499
ripple::QualityDirection::out
@ out
ripple::DirectStepI::fwdImp
std::pair< IOUAmount, IOUAmount > fwdImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, IOUAmount const &in)
Definition: DirectStep.cpp:630
ripple::DirectStepI::logStringImpl
std::string logStringImpl(char const *name) const
Definition: DirectStep.cpp:204
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:133
ripple::DirectStepI::equal
bool equal(Step const &rhs) const override
Definition: DirectStep.cpp:214
ripple::StrandContext::offerCrossing
const bool offerCrossing
true if offer crossing, not payment
Definition: Steps.h:507
ripple::toSTAmount
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
Definition: AmountConversions.h:30
ripple::TERSubset< CanCvtToTER >
ripple::DirectStepI::revImp
std::pair< IOUAmount, IOUAmount > revImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, IOUAmount const &out)
Definition: DirectStep.cpp:504
ripple::DirectStepI::prevStep_
Step const *const prevStep_
Definition: DirectStep.cpp:45
ripple::sfLowQualityIn
const SF_UINT32 sfLowQualityIn
ripple::creditLimit
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
ripple::Step
A step in a payment path.
Definition: Steps.h:79
ripple::terNO_AUTH
@ terNO_AUTH
Definition: TER.h:199
ripple::DebtDirection::redeems
@ redeems
ripple::STAmount
Definition: STAmount.h:45
ripple::DirectStepI::cachedOut
std::optional< EitherAmount > cachedOut() const override
Definition: DirectStep.cpp:131
ripple::StrandContext::seenBookOuts
boost::container::flat_set< Issue > & seenBookOuts
A strand may not include an offer that output the same issue more than once.
Definition: Steps.h:523
ripple::StrandContext::isLast
const bool isLast
true if Step is last in Strand
Definition: Steps.h:505
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::DirectStepI::dst_
AccountID dst_
Definition: DirectStep.cpp:41
ripple::DirectStepI::lineQualityIn
std::uint32_t lineQualityIn(ReadView const &v) const override
Definition: DirectStep.cpp:826
std::uint32_t
ripple::keylet::line
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:193
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
ripple::DirectStepI::j_
const beast::Journal j_
Definition: DirectStep.cpp:47
ripple::lsfHighNoRipple
@ lsfHighNoRipple
Definition: LedgerFormats.h:257
ripple::terNO_ACCOUNT
@ terNO_ACCOUNT
Definition: TER.h:198
ripple::StrandDirection::reverse
@ reverse
ripple::DebtDirection::issues
@ issues
std::ostringstream
STL class.
ripple::DirectStepI::Cache::srcDebtDir
DebtDirection srcDebtDir
Definition: DirectStep.cpp:54
ripple::test::directStepEqual
bool directStepEqual(Step const &step, AccountID const &src, AccountID const &dst, Currency const &currency)
Definition: DirectStep.cpp:958
ripple::redeems
bool redeems(DebtDirection dir)
Definition: Steps.h:42
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:125
ripple::Rate::value
std::uint32_t value
Definition: Rate.h:39
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::IOUAmount::mantissa
std::int64_t mantissa() const noexcept
Definition: IOUAmount.h:169
ripple::DirectStepI::Cache::out
IOUAmount out
Definition: DirectStep.cpp:53
ripple::DirectStepI::dst
AccountID const & dst() const
Definition: DirectStep.cpp:112
ripple::DirectStepI::src_
AccountID src_
Definition: DirectStep.cpp:40
ripple::ReadView::rules
virtual Rules const & rules() const =0
Returns the tx processing rules.
ripple::DirectIPaymentStep::maxFlow
std::pair< IOUAmount, DebtDirection > maxFlow(ReadView const &sb, IOUAmount const &desired) const
Definition: DirectStep.cpp:376
ripple::sfFlags
const SF_UINT32 sfFlags
ripple::DirectStepI::cachedIn
std::optional< EitherAmount > cachedIn() const override
Definition: DirectStep.cpp:123
ripple::DirectStepI::debtDirection
DebtDirection debtDirection(ReadView const &sb, StrandDirection dir) const override
Definition: DirectStep.cpp:490
ripple::EitherAmount
Definition: AmountSpec.h:59
ripple::DirectStepI::isLast_
const bool isLast_
Definition: DirectStep.cpp:46
ripple::sfBalance
const SF_AMOUNT sfBalance
ripple::DirectIPaymentStep::verifyPrevStepDebtDirection
bool verifyPrevStepDebtDirection(DebtDirection) const
Definition: DirectStep.cpp:239
ripple::StrandContext::isFirst
const bool isFirst
true if Step is first in Strand
Definition: Steps.h:504
ripple::StrandContext::prevStep
Step const *const prevStep
The previous step in the strand.
Definition: Steps.h:513
ripple::tecPATH_DRY
@ tecPATH_DRY
Definition: TER.h:261
ripple::fhIGNORE_FREEZE
@ fhIGNORE_FREEZE
Definition: View.h:76
ripple::DirectStepI::validFwd
std::pair< bool, EitherAmount > validFwd(PaymentSandbox &sb, ApplyView &afView, EitherAmount const &in) override
Definition: DirectStep.cpp:710
ripple::DirectStepI::Cache::in
IOUAmount in
Definition: DirectStep.cpp:51
ripple::sfHighQualityOut
const SF_UINT32 sfHighQualityOut
std::optional
ripple::QualityDirection
QualityDirection
Definition: Steps.h:38
ripple::DirectStepI::directStepAccts
std::optional< std::pair< AccountID, AccountID > > directStepAccts() const override
Definition: DirectStep.cpp:145
ripple::checkFreeze
TER checkFreeze(ReadView const &view, AccountID const &src, AccountID const &dst, Currency const &currency)
Definition: StepChecks.h:32
std::ostringstream::str
T str(T... args)
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
ripple::DirectIOfferCrossingStep::check
TER check(StrandContext const &ctx, std::shared_ptr< const SLE > const &sleSrc) const
Definition: DirectStep.cpp:460
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::DirectIOfferCrossingStep::maxFlow
std::pair< IOUAmount, DebtDirection > maxFlow(ReadView const &sb, IOUAmount const &desired) const
Definition: DirectStep.cpp:382
std::make_pair
T make_pair(T... args)
ripple::temBAD_PATH_LOOP
@ temBAD_PATH_LOOP
Definition: TER.h:95
numeric
ripple::StrandContext::seenDirectIssues
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:519
std::unique_ptr
STL class.
ripple::DirectStepI::qualitiesSrcRedeems
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcRedeems(ReadView const &sb) const
Definition: DirectStep.cpp:764
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:222
ripple::DirectStepI::directStepSrcAcct
std::optional< AccountID > directStepSrcAcct() const override
Definition: DirectStep.cpp:139
ripple::DirectIOfferCrossingStep::logString
std::string logString() const override
Definition: DirectStep.cpp:320
ripple::StrandDirection::forward
@ forward