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 #include <boost/optional.hpp>
31 
32 #include <numeric>
33 #include <sstream>
34 
35 namespace ripple {
36 
37 template <class TDerived>
38 class DirectStepI : public StepImp<IOUAmount, IOUAmount, DirectStepI<TDerived>>
39 {
40 protected:
44 
45  // Charge transfer fees when the prev step redeems
46  Step const* const prevStep_ = nullptr;
47  bool const isLast_;
49 
50  struct Cache
51  {
56 
58  IOUAmount const& in_,
59  IOUAmount const& srcToDst_,
60  IOUAmount const& out_,
61  DebtDirection srcDebtDir_)
62  : in(in_), srcToDst(srcToDst_), out(out_), srcDebtDir(srcDebtDir_)
63  {
64  }
65  };
66 
67  boost::optional<Cache> cache_;
68 
69  // Compute the maximum value that can flow from src->dst at
70  // the best available quality.
71  // return: first element is max amount that can flow,
72  // second is the debt direction of the source w.r.t. the dst
74  maxPaymentFlow(ReadView const& sb) const;
75 
76  // Compute srcQOut and dstQIn when the source redeems.
78  qualitiesSrcRedeems(ReadView const& sb) const;
79 
80  // Compute srcQOut and dstQIn when the source issues.
82  qualitiesSrcIssues(ReadView const& sb, DebtDirection prevStepDebtDirection)
83  const;
84 
85  // Returns srcQOut, dstQIn
87  qualities(
88  ReadView const& sb,
89  DebtDirection srcDebtDir,
90  StrandDirection strandDir) const;
91 
92 public:
94  StrandContext const& ctx,
95  AccountID const& src,
96  AccountID const& dst,
97  Currency const& c)
98  : src_(src)
99  , dst_(dst)
100  , currency_(c)
101  , prevStep_(ctx.prevStep)
102  , isLast_(ctx.isLast)
103  , j_(ctx.j)
104  {
105  }
106 
107  AccountID const&
108  src() const
109  {
110  return src_;
111  }
112  AccountID const&
113  dst() const
114  {
115  return dst_;
116  }
117  Currency const&
118  currency() const
119  {
120  return currency_;
121  }
122 
123  boost::optional<EitherAmount>
124  cachedIn() const override
125  {
126  if (!cache_)
127  return boost::none;
128  return EitherAmount(cache_->in);
129  }
130 
131  boost::optional<EitherAmount>
132  cachedOut() const override
133  {
134  if (!cache_)
135  return boost::none;
136  return EitherAmount(cache_->out);
137  }
138 
139  boost::optional<AccountID>
140  directStepSrcAcct() const override
141  {
142  return src_;
143  }
144 
145  boost::optional<std::pair<AccountID, AccountID>>
146  directStepAccts() const override
147  {
148  return std::make_pair(src_, dst_);
149  }
150 
152  debtDirection(ReadView const& sb, StrandDirection dir) const override;
153 
155  lineQualityIn(ReadView const& v) const override;
156 
158  qualityUpperBound(ReadView const& v, DebtDirection dir) const override;
159 
161  revImp(
162  PaymentSandbox& sb,
163  ApplyView& afView,
164  boost::container::flat_set<uint256>& ofrsToRm,
165  IOUAmount const& out);
166 
168  fwdImp(
169  PaymentSandbox& sb,
170  ApplyView& afView,
171  boost::container::flat_set<uint256>& ofrsToRm,
172  IOUAmount const& in);
173 
175  validFwd(PaymentSandbox& sb, ApplyView& afView, EitherAmount const& in)
176  override;
177 
178  // Check for error, existing liquidity, and violations of auth/frozen
179  // constraints.
180  TER
181  check(StrandContext const& ctx) const;
182 
183  void
185  IOUAmount const& fwdIn,
186  IOUAmount const& fwdSrcToDst,
187  IOUAmount const& fwdOut,
188  DebtDirection srcDebtDir);
189 
190  friend bool
191  operator==(DirectStepI const& lhs, DirectStepI const& rhs)
192  {
193  return lhs.src_ == rhs.src_ && lhs.dst_ == rhs.dst_ &&
194  lhs.currency_ == rhs.currency_;
195  }
196 
197  friend bool
198  operator!=(DirectStepI const& lhs, DirectStepI const& rhs)
199  {
200  return !(lhs == rhs);
201  }
202 
203 protected:
205  logStringImpl(char const* name) const
206  {
207  std::ostringstream ostr;
208  ostr << name << ": "
209  << "\nSrc: " << src_ << "\nDst: " << dst_;
210  return ostr.str();
211  }
212 
213 private:
214  bool
215  equal(Step const& rhs) const override
216  {
217  if (auto ds = dynamic_cast<DirectStepI const*>(&rhs))
218  {
219  return *this == *ds;
220  }
221  return false;
222  }
223 };
224 
225 //------------------------------------------------------------------------------
226 
227 // Flow is used in two different circumstances for transferring funds:
228 // o Payments, and
229 // o Offer crossing.
230 // The rules for handling funds in these two cases are almost, but not
231 // quite, the same.
232 
233 // Payment DirectStep class (not offer crossing).
234 class DirectIPaymentStep : public DirectStepI<DirectIPaymentStep>
235 {
236 public:
239 
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).
277 class DirectIOfferCrossingStep : public DirectStepI<DirectIOfferCrossingStep>
278 {
279 public:
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, qDir]() -> 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 
404 TER
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 
460 TER
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 
473 template <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 
489 template <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 
503 template <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  assert(static_cast<TDerived const*>(this)->verifyDstQualityIn(dstQIn));
519 
520  Issue const srcToDstIss(currency_, redeems(srcDebtDir) ? dst_ : src_);
521 
522  JLOG(j_.trace()) << "DirectStepI::rev"
523  << " srcRedeems: " << redeems(srcDebtDir)
524  << " outReq: " << to_string(out)
525  << " maxSrcToDst: " << to_string(maxSrcToDst)
526  << " srcQOut: " << srcQOut << " dstQIn: " << dstQIn;
527 
528  if (maxSrcToDst.signum() <= 0)
529  {
530  JLOG(j_.trace()) << "DirectStepI::rev: dry";
531  cache_.emplace(
532  IOUAmount(beast::zero),
533  IOUAmount(beast::zero),
534  IOUAmount(beast::zero),
535  srcDebtDir);
536  return {beast::zero, beast::zero};
537  }
538 
539  IOUAmount const srcToDst =
540  mulRatio(out, QUALITY_ONE, dstQIn, /*roundUp*/ true);
541 
542  if (srcToDst <= maxSrcToDst)
543  {
544  IOUAmount const in =
545  mulRatio(srcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true);
546  cache_.emplace(in, srcToDst, out, srcDebtDir);
547  rippleCredit(
548  sb,
549  src_,
550  dst_,
551  toSTAmount(srcToDst, srcToDstIss),
552  /*checkIssuer*/ true,
553  j_);
554  JLOG(j_.trace()) << "DirectStepI::rev: Non-limiting"
555  << " srcRedeems: " << redeems(srcDebtDir)
556  << " in: " << to_string(in)
557  << " srcToDst: " << to_string(srcToDst)
558  << " out: " << to_string(out);
559  return {in, out};
560  }
561 
562  // limiting node
563  IOUAmount const in =
564  mulRatio(maxSrcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true);
565  IOUAmount const actualOut =
566  mulRatio(maxSrcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false);
567  cache_.emplace(in, maxSrcToDst, actualOut, srcDebtDir);
568  rippleCredit(
569  sb,
570  src_,
571  dst_,
572  toSTAmount(maxSrcToDst, srcToDstIss),
573  /*checkIssuer*/ true,
574  j_);
575  JLOG(j_.trace()) << "DirectStepI::rev: Limiting"
576  << " srcRedeems: " << redeems(srcDebtDir)
577  << " in: " << to_string(in)
578  << " srcToDst: " << to_string(maxSrcToDst)
579  << " out: " << to_string(out);
580  return {in, actualOut};
581 }
582 
583 // The forward pass should never have more liquidity than the reverse
584 // pass. But sometimes rounding differences cause the forward pass to
585 // deliver more liquidity. Use the cached values from the reverse pass
586 // to prevent this.
587 template <class TDerived>
588 void
590  IOUAmount const& fwdIn,
591  IOUAmount const& fwdSrcToDst,
592  IOUAmount const& fwdOut,
593  DebtDirection srcDebtDir)
594 {
595  if (cache_->in < fwdIn)
596  {
597  IOUAmount const smallDiff(1, -9);
598  auto const diff = fwdIn - cache_->in;
599  if (diff > smallDiff)
600  {
601  if (fwdIn.exponent() != cache_->in.exponent() ||
602  !cache_->in.mantissa() ||
603  (double(fwdIn.mantissa()) / double(cache_->in.mantissa())) >
604  1.01)
605  {
606  // Detect large diffs on forward pass so they may be
607  // investigated
608  JLOG(j_.warn())
609  << "DirectStepI::fwd: setCacheLimiting"
610  << " fwdIn: " << to_string(fwdIn)
611  << " cacheIn: " << to_string(cache_->in)
612  << " fwdSrcToDst: " << to_string(fwdSrcToDst)
613  << " cacheSrcToDst: " << to_string(cache_->srcToDst)
614  << " fwdOut: " << to_string(fwdOut)
615  << " cacheOut: " << to_string(cache_->out);
616  cache_.emplace(fwdIn, fwdSrcToDst, fwdOut, srcDebtDir);
617  return;
618  }
619  }
620  }
621  cache_->in = fwdIn;
622  if (fwdSrcToDst < cache_->srcToDst)
623  cache_->srcToDst = fwdSrcToDst;
624  if (fwdOut < cache_->out)
625  cache_->out = fwdOut;
626  cache_->srcDebtDir = srcDebtDir;
627 };
628 
629 template <class TDerived>
632  PaymentSandbox& sb,
633  ApplyView& /*afView*/,
634  boost::container::flat_set<uint256>& /*ofrsToRm*/,
635  IOUAmount const& in)
636 {
637  assert(cache_);
638 
639  auto const [maxSrcToDst, srcDebtDir] =
640  static_cast<TDerived const*>(this)->maxFlow(sb, cache_->srcToDst);
641 
642  auto const [srcQOut, dstQIn] =
643  qualities(sb, srcDebtDir, StrandDirection::forward);
644 
645  Issue const srcToDstIss(currency_, redeems(srcDebtDir) ? dst_ : src_);
646 
647  JLOG(j_.trace()) << "DirectStepI::fwd"
648  << " srcRedeems: " << redeems(srcDebtDir)
649  << " inReq: " << to_string(in)
650  << " maxSrcToDst: " << to_string(maxSrcToDst)
651  << " srcQOut: " << srcQOut << " dstQIn: " << dstQIn;
652 
653  if (maxSrcToDst.signum() <= 0)
654  {
655  JLOG(j_.trace()) << "DirectStepI::fwd: dry";
656  cache_.emplace(
657  IOUAmount(beast::zero),
658  IOUAmount(beast::zero),
659  IOUAmount(beast::zero),
660  srcDebtDir);
661  return {beast::zero, beast::zero};
662  }
663 
664  IOUAmount const srcToDst =
665  mulRatio(in, QUALITY_ONE, srcQOut, /*roundUp*/ false);
666 
667  if (srcToDst <= maxSrcToDst)
668  {
669  IOUAmount const out =
670  mulRatio(srcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false);
671  setCacheLimiting(in, srcToDst, out, srcDebtDir);
672  rippleCredit(
673  sb,
674  src_,
675  dst_,
676  toSTAmount(cache_->srcToDst, srcToDstIss),
677  /*checkIssuer*/ true,
678  j_);
679  JLOG(j_.trace()) << "DirectStepI::fwd: Non-limiting"
680  << " srcRedeems: " << redeems(srcDebtDir)
681  << " in: " << to_string(in)
682  << " srcToDst: " << to_string(srcToDst)
683  << " out: " << to_string(out);
684  }
685  else
686  {
687  // limiting node
688  IOUAmount const actualIn =
689  mulRatio(maxSrcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true);
690  IOUAmount const out =
691  mulRatio(maxSrcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false);
692  setCacheLimiting(actualIn, maxSrcToDst, out, srcDebtDir);
693  rippleCredit(
694  sb,
695  src_,
696  dst_,
697  toSTAmount(cache_->srcToDst, srcToDstIss),
698  /*checkIssuer*/ true,
699  j_);
700  JLOG(j_.trace()) << "DirectStepI::rev: Limiting"
701  << " srcRedeems: " << redeems(srcDebtDir)
702  << " in: " << to_string(actualIn)
703  << " srcToDst: " << to_string(srcToDst)
704  << " out: " << to_string(out);
705  }
706  return {cache_->in, cache_->out};
707 }
708 
709 template <class TDerived>
712  PaymentSandbox& sb,
713  ApplyView& afView,
714  EitherAmount const& in)
715 {
716  if (!cache_)
717  {
718  JLOG(j_.trace()) << "Expected valid cache in validFwd";
719  return {false, EitherAmount(IOUAmount(beast::zero))};
720  }
721 
722  auto const savCache = *cache_;
723 
724  assert(!in.native);
725 
726  auto const [maxSrcToDst, srcDebtDir] =
727  static_cast<TDerived const*>(this)->maxFlow(sb, cache_->srcToDst);
728  (void)srcDebtDir;
729 
730  try
731  {
732  boost::container::flat_set<uint256> dummy;
733  fwdImp(sb, afView, dummy, in.iou); // changes cache
734  }
735  catch (FlowException const&)
736  {
737  return {false, EitherAmount(IOUAmount(beast::zero))};
738  }
739 
740  if (maxSrcToDst < cache_->srcToDst)
741  {
742  JLOG(j_.warn()) << "DirectStepI: Strand re-execute check failed."
743  << " Exceeded max src->dst limit"
744  << " max src->dst: " << to_string(maxSrcToDst)
745  << " actual src->dst: " << to_string(cache_->srcToDst);
746  return {false, EitherAmount(cache_->out)};
747  }
748 
749  if (!(checkNear(savCache.in, cache_->in) &&
750  checkNear(savCache.out, cache_->out)))
751  {
752  JLOG(j_.warn()) << "DirectStepI: Strand re-execute check failed."
753  << " ExpectedIn: " << to_string(savCache.in)
754  << " CachedIn: " << to_string(cache_->in)
755  << " ExpectedOut: " << to_string(savCache.out)
756  << " CachedOut: " << to_string(cache_->out);
757  return {false, EitherAmount(cache_->out)};
758  }
759  return {true, EitherAmount(cache_->out)};
760 }
761 
762 // Returns srcQOut, dstQIn
763 template <class TDerived>
766 {
767  if (!prevStep_)
768  return {QUALITY_ONE, QUALITY_ONE};
769 
770  auto const prevStepQIn = prevStep_->lineQualityIn(sb);
771  auto srcQOut =
772  static_cast<TDerived const*>(this)->quality(sb, QualityDirection::out);
773 
774  if (prevStepQIn > srcQOut)
775  srcQOut = prevStepQIn;
776  return {srcQOut, QUALITY_ONE};
777 }
778 
779 // Returns srcQOut, dstQIn
780 template <class TDerived>
783  ReadView const& sb,
784  DebtDirection prevStepDebtDirection) const
785 {
786  // Charge a transfer rate when issuing and previous step redeems
787 
788  assert(static_cast<TDerived const*>(this)->verifyPrevStepDebtDirection(
789  prevStepDebtDirection));
790 
791  std::uint32_t const srcQOut = redeems(prevStepDebtDirection)
792  ? transferRate(sb, src_).value
793  : QUALITY_ONE;
794  auto dstQIn =
795  static_cast<TDerived const*>(this)->quality(sb, QualityDirection::in);
796 
797  if (isLast_ && dstQIn > QUALITY_ONE)
798  dstQIn = QUALITY_ONE;
799  return {srcQOut, dstQIn};
800 }
801 
802 // Returns srcQOut, dstQIn
803 template <class TDerived>
806  ReadView const& sb,
807  DebtDirection srcDebtDir,
808  StrandDirection strandDir) const
809 {
810  if (redeems(srcDebtDir))
811  {
812  return qualitiesSrcRedeems(sb);
813  }
814  else
815  {
816  auto const prevStepDebtDirection = [&] {
817  if (prevStep_)
818  return prevStep_->debtDirection(sb, strandDir);
819  return DebtDirection::issues;
820  }();
821  return qualitiesSrcIssues(sb, prevStepDebtDirection);
822  }
823 }
824 
825 template <class TDerived>
828 {
829  // dst quality in
830  return static_cast<TDerived const*>(this)->quality(v, QualityDirection::in);
831 }
832 
833 template <class TDerived>
836  ReadView const& v,
837  DebtDirection prevStepDir) const
838 {
839  auto const dir = this->debtDirection(v, StrandDirection::forward);
840 
842  {
843  std::uint32_t const srcQOut = [&]() -> std::uint32_t {
844  if (redeems(prevStepDir) && issues(dir))
845  return transferRate(v, src_).value;
846  return QUALITY_ONE;
847  }();
848  auto dstQIn = static_cast<TDerived const*>(this)->quality(
850 
851  if (isLast_ && dstQIn > QUALITY_ONE)
852  dstQIn = QUALITY_ONE;
853  Issue const iss{currency_, src_};
854  return {
855  Quality(getRate(STAmount(iss, srcQOut), STAmount(iss, dstQIn))),
856  dir};
857  }
858 
859  auto const [srcQOut, dstQIn] = redeems(dir)
860  ? qualitiesSrcRedeems(v)
861  : qualitiesSrcIssues(v, prevStepDir);
862 
863  Issue const iss{currency_, src_};
864  // Be careful not to switch the parameters to `getRate`. The
865  // `getRate(offerOut, offerIn)` function is usually used for offers. It
866  // returns offerIn/offerOut. For a direct step, the rate is srcQOut/dstQIn
867  // (Input*dstQIn/srcQOut = Output; So rate = srcQOut/dstQIn). Although the
868  // first parameter is called `offerOut`, it should take the `dstQIn`
869  // variable.
870  return {
871  Quality(getRate(STAmount(iss, dstQIn), STAmount(iss, srcQOut))), dir};
872 }
873 
874 template <class TDerived>
875 TER
877 {
878  // The following checks apply for both payments and offer crossing.
879  if (!src_ || !dst_)
880  {
881  JLOG(j_.debug()) << "DirectStepI: specified bad account.";
882  return temBAD_PATH;
883  }
884 
885  if (src_ == dst_)
886  {
887  JLOG(j_.debug()) << "DirectStepI: same src and dst.";
888  return temBAD_PATH;
889  }
890 
891  auto const sleSrc = ctx.view.read(keylet::account(src_));
892  if (!sleSrc)
893  {
894  JLOG(j_.warn())
895  << "DirectStepI: can't receive IOUs from non-existent issuer: "
896  << src_;
897  return terNO_ACCOUNT;
898  }
899 
900  // pure issue/redeem can't be frozen
901  if (!(ctx.isLast && ctx.isFirst))
902  {
903  auto const ter = checkFreeze(ctx.view, src_, dst_, currency_);
904  if (ter != tesSUCCESS)
905  return ter;
906  }
907 
908  // If previous step was a direct step then we need to check
909  // no ripple flags.
910  if (ctx.prevStep)
911  {
912  if (auto prevSrc = ctx.prevStep->directStepSrcAcct())
913  {
914  auto const ter =
915  checkNoRipple(ctx.view, *prevSrc, src_, dst_, currency_, j_);
916  if (ter != tesSUCCESS)
917  return ter;
918  }
919  }
920  {
921  Issue const srcIssue{currency_, src_};
922  Issue const dstIssue{currency_, dst_};
923 
924  if (ctx.seenBookOuts.count(srcIssue))
925  {
926  if (!ctx.prevStep)
927  {
928  assert(0); // prev seen book without a prev step!?!
929  return temBAD_PATH_LOOP;
930  }
931 
932  // This is OK if the previous step is a book step that outputs this
933  // issue
934  if (auto book = ctx.prevStep->bookStepBook())
935  {
936  if (book->out != srcIssue)
937  return temBAD_PATH_LOOP;
938  }
939  }
940 
941  if (!ctx.seenDirectIssues[0].insert(srcIssue).second ||
942  !ctx.seenDirectIssues[1].insert(dstIssue).second)
943  {
944  JLOG(j_.debug())
945  << "DirectStepI: loop detected: Index: " << ctx.strandSize
946  << ' ' << *this;
947  return temBAD_PATH_LOOP;
948  }
949  }
950 
951  return static_cast<TDerived const*>(this)->check(ctx, sleSrc);
952 }
953 
954 //------------------------------------------------------------------------------
955 
956 namespace test {
957 // Needed for testing
958 bool
960  Step const& step,
961  AccountID const& src,
962  AccountID const& dst,
963  Currency const& currency)
964 {
965  if (auto ds = dynamic_cast<DirectStepI<DirectIPaymentStep> const*>(&step))
966  {
967  return ds->src() == src && ds->dst() == dst &&
968  ds->currency() == currency;
969  }
970  return false;
971 }
972 } // namespace test
973 
974 //------------------------------------------------------------------------------
975 
978  StrandContext const& ctx,
979  AccountID const& src,
980  AccountID const& dst,
981  Currency const& c)
982 {
983  TER ter = tefINTERNAL;
985  if (ctx.offerCrossing)
986  {
987  auto offerCrossingStep =
988  std::make_unique<DirectIOfferCrossingStep>(ctx, src, dst, c);
989  ter = offerCrossingStep->check(ctx);
990  r = std::move(offerCrossingStep);
991  }
992  else // payment
993  {
994  auto paymentStep =
995  std::make_unique<DirectIPaymentStep>(ctx, src, dst, c);
996  ter = paymentStep->check(ctx);
997  r = std::move(paymentStep);
998  }
999  if (ter != tesSUCCESS)
1000  return {ter, nullptr};
1001 
1002  return {tesSUCCESS, std::move(r)};
1003 }
1004 
1005 } // namespace ripple
ripple::DirectStepI::Cache
Definition: DirectStep.cpp:50
ripple::rippleCredit
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Definition: View.cpp:936
ripple::mulRatio
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
Definition: IOUAmount.cpp:242
ripple::StrandContext
Context needed to build Strand Steps and for error checking.
Definition: Steps.h:472
ripple::sfHighQualityIn
const SF_UINT32 sfHighQualityIn
ripple::StrandContext::strandSize
const size_t strandSize
Length of Strand.
Definition: Steps.h:484
ripple::transferRate
Rate transferRate(ReadView const &view, AccountID const &issuer)
Definition: View.cpp:347
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:330
ripple::DirectStepI::maxPaymentFlow
std::pair< IOUAmount, DebtDirection > maxPaymentFlow(ReadView const &sb) const
Definition: DirectStep.cpp:475
ripple::fixQualityUpperBound
const uint256 fixQualityUpperBound
Definition: Feature.cpp:183
ripple::IOUAmount::exponent
int exponent() const noexcept
Definition: IOUAmount.h:122
ripple::Issue
A currency issued by an account.
Definition: Issue.h:34
ripple::tefINTERNAL
@ tefINTERNAL
Definition: TER.h:150
std::string
STL class.
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:281
ripple::DirectIPaymentStep::verifyDstQualityIn
bool verifyDstQualityIn(std::uint32_t dstQIn) const
Definition: DirectStep.cpp:247
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::terNO_LINE
@ terNO_LINE
Definition: TER.h:194
ripple::DebtDirection
DebtDirection
Definition: Steps.h:37
ripple::PaymentSandbox
A wrapper which makes credits unavailable to balances.
Definition: PaymentSandbox.h:112
std::pair
ripple::DirectIPaymentStep
Definition: DirectStep.cpp:234
ripple::lsfLowAuth
@ lsfLowAuth
Definition: LedgerFormats.h:123
ripple::DirectStepI::qualities
std::pair< std::uint32_t, std::uint32_t > qualities(ReadView const &sb, DebtDirection srcDebtDir, StrandDirection strandDir) const
Definition: DirectStep.cpp:805
ripple::lsfLowNoRipple
@ lsfLowNoRipple
Definition: LedgerFormats.h:125
ripple::DirectIOfferCrossingStep::verifyPrevStepDebtDirection
bool verifyPrevStepDebtDirection(DebtDirection prevStepDir) const
Definition: DirectStep.cpp:284
ripple::accountHolds
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition: View.cpp:92
ripple::DirectStepI::check
TER check(StrandContext const &ctx) const
Definition: DirectStep.cpp:876
ripple::DirectStepI::src
AccountID const & src() const
Definition: DirectStep.cpp:108
ripple::DirectStepI::qualitiesSrcIssues
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcIssues(ReadView const &sb, DebtDirection prevStepDebtDirection) const
Definition: DirectStep.cpp:782
ripple::checkNear
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
Definition: PaySteps.cpp:36
ripple::DirectStepI::directStepAccts
boost::optional< std::pair< AccountID, AccountID > > directStepAccts() const override
Definition: DirectStep.cpp:146
ripple::DirectStepI::Cache::Cache
Cache(IOUAmount const &in_, IOUAmount const &srcToDst_, IOUAmount const &out_, DebtDirection srcDebtDir_)
Definition: DirectStep.cpp:57
ripple::DirectStepI
Definition: DirectStep.cpp:38
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
ripple::DirectStepI::currency
Currency const & currency() const
Definition: DirectStep.cpp:118
ripple::SBoxCmp::diff
@ diff
ripple::DirectStepI::operator==
friend bool operator==(DirectStepI const &lhs, DirectStepI const &rhs)
Definition: DirectStep.cpp:191
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:977
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:93
ripple::IOUAmount
Floating point representation of amounts with high dynamic range.
Definition: IOUAmount.h:41
ripple::DirectIOfferCrossingStep::verifyDstQualityIn
bool verifyDstQualityIn(std::uint32_t dstQIn) const
Definition: DirectStep.cpp:297
ripple::terNO_RIPPLE
@ terNO_RIPPLE
Definition: TER.h:199
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:45
ripple::DirectIOfferCrossingStep::quality
std::uint32_t quality(ReadView const &sb, QualityDirection qDir) const
Definition: DirectStep.cpp:369
ripple::Step::bookStepBook
virtual boost::optional< Book > bookStepBook() const
If this step is a BookStep, return the book.
Definition: Steps.h:195
ripple::temBAD_PATH
@ temBAD_PATH
Definition: TER.h:91
ripple::DirectIOfferCrossingStep
Definition: DirectStep.cpp:277
ripple::toAmount< IOUAmount >
IOUAmount toAmount< IOUAmount >(STAmount const &amt)
Definition: AmountConversions.h:81
ripple::ApplyView
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:140
ripple::lsfHighAuth
@ lsfHighAuth
Definition: LedgerFormats.h:124
ripple::StrandDirection
StrandDirection
Definition: Steps.h:39
ripple::DirectStepI::Cache::srcToDst
IOUAmount srcToDst
Definition: DirectStep.cpp:53
ripple::DirectIPaymentStep::logString
std::string logString() const override
Definition: DirectStep.cpp:270
ripple::DirectIPaymentStep::check
TER check(StrandContext const &ctx, std::shared_ptr< const SLE > const &sleSrc) const
Definition: DirectStep.cpp:405
ripple::getRate
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition: STAmount.cpp:440
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:589
ripple::base_uint< 160, detail::AccountIDTag >
ripple::DirectStepI::operator!=
friend bool operator!=(DirectStepI const &lhs, DirectStepI const &rhs)
Definition: DirectStep.cpp:198
ripple::sfLowQualityOut
const SF_UINT32 sfLowQualityOut
ripple::lsfRequireAuth
@ lsfRequireAuth
Definition: LedgerFormats.h:106
ripple::DirectStepI::currency_
Currency currency_
Definition: DirectStep.cpp:43
ripple::StrandContext::view
ReadView const & view
Current ReadView.
Definition: Steps.h:474
ripple::DirectStepI::cachedOut
boost::optional< EitherAmount > cachedOut() const override
Definition: DirectStep.cpp:132
ripple::DirectStepI::cachedIn
boost::optional< EitherAmount > cachedIn() const override
Definition: DirectStep.cpp:124
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:631
ripple::DirectStepI::logStringImpl
std::string logStringImpl(char const *name) const
Definition: DirectStep.cpp:205
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:134
ripple::DirectStepI::equal
bool equal(Step const &rhs) const override
Definition: DirectStep.cpp:215
ripple::StrandContext::offerCrossing
const bool offerCrossing
true if offer crossing, not payment
Definition: Steps.h:482
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:505
ripple::DirectStepI::prevStep_
Step const *const prevStep_
Definition: DirectStep.cpp:46
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:193
ripple::DebtDirection::redeems
@ redeems
ripple::STAmount
Definition: STAmount.h:42
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:498
ripple::DirectStepI::directStepSrcAcct
boost::optional< AccountID > directStepSrcAcct() const override
Definition: DirectStep.cpp:140
ripple::StrandContext::isLast
const bool isLast
true if Step is last in Strand
Definition: Steps.h:480
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::DirectStepI::dst_
AccountID dst_
Definition: DirectStep.cpp:42
ripple::DirectStepI::lineQualityIn
std::uint32_t lineQualityIn(ReadView const &v) const override
Definition: DirectStep.cpp:827
std::uint32_t
ripple::Rules::enabled
bool enabled(uint256 const &id) const
Returns true if a feature is enabled.
Definition: ReadView.cpp:103
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:194
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:48
ripple::lsfHighNoRipple
@ lsfHighNoRipple
Definition: LedgerFormats.h:126
ripple::terNO_ACCOUNT
@ terNO_ACCOUNT
Definition: TER.h:192
ripple::StrandDirection::reverse
@ reverse
ripple::DebtDirection::issues
@ issues
std::ostringstream
STL class.
ripple::DirectStepI::Cache::srcDebtDir
DebtDirection srcDebtDir
Definition: DirectStep.cpp:55
ripple::test::directStepEqual
bool directStepEqual(Step const &step, AccountID const &src, AccountID const &dst, Currency const &currency)
Definition: DirectStep.cpp:959
ripple::redeems
bool redeems(DebtDirection dir)
Definition: Steps.h:42
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:192
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:128
ripple::DirectStepI::Cache::out
IOUAmount out
Definition: DirectStep.cpp:54
ripple::DirectStepI::dst
AccountID const & dst() const
Definition: DirectStep.cpp:113
ripple::DirectStepI::src_
AccountID src_
Definition: DirectStep.cpp:41
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:377
ripple::sfFlags
const SF_UINT32 sfFlags
ripple::DirectStepI::debtDirection
DebtDirection debtDirection(ReadView const &sb, StrandDirection dir) const override
Definition: DirectStep.cpp:491
ripple::EitherAmount
Definition: AmountSpec.h:57
ripple::DirectStepI::isLast_
const bool isLast_
Definition: DirectStep.cpp:47
ripple::sfBalance
const SF_AMOUNT sfBalance
ripple::DirectStepI::cache_
boost::optional< Cache > cache_
Definition: DirectStep.cpp:67
ripple::DirectIPaymentStep::verifyPrevStepDebtDirection
bool verifyPrevStepDebtDirection(DebtDirection) const
Definition: DirectStep.cpp:240
ripple::StrandContext::isFirst
const bool isFirst
true if Step is first in Strand
Definition: Steps.h:479
ripple::StrandContext::prevStep
Step const *const prevStep
The previous step in the strand.
Definition: Steps.h:488
ripple::tecPATH_DRY
@ tecPATH_DRY
Definition: TER.h:255
ripple::fhIGNORE_FREEZE
@ fhIGNORE_FREEZE
Definition: View.h:53
ripple::DirectStepI::validFwd
std::pair< bool, EitherAmount > validFwd(PaymentSandbox &sb, ApplyView &afView, EitherAmount const &in) override
Definition: DirectStep.cpp:711
ripple::DirectStepI::Cache::in
IOUAmount in
Definition: DirectStep.cpp:52
ripple::sfHighQualityOut
const SF_UINT32 sfHighQualityOut
ripple::QualityDirection
QualityDirection
Definition: Steps.h:38
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:461
ripple::DirectIOfferCrossingStep::maxFlow
std::pair< IOUAmount, DebtDirection > maxFlow(ReadView const &sb, IOUAmount const &desired) const
Definition: DirectStep.cpp:383
std::make_pair
T make_pair(T... args)
ripple::temBAD_PATH_LOOP
@ temBAD_PATH_LOOP
Definition: TER.h:92
ripple::DirectStepI::qualityUpperBound
std::pair< boost::optional< Quality >, DebtDirection > qualityUpperBound(ReadView const &v, DebtDirection dir) const override
Definition: DirectStep.cpp:835
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:494
std::unique_ptr
STL class.
ripple::DirectStepI::qualitiesSrcRedeems
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcRedeems(ReadView const &sb) const
Definition: DirectStep.cpp:765
ripple::Step::directStepSrcAcct
virtual boost::optional< AccountID > directStepSrcAcct() const
If this step is DirectStepI (IOU->IOU direct step), return the src account.
Definition: Steps.h:139
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:216
ripple::DirectIOfferCrossingStep::logString
std::string logString() const override
Definition: DirectStep.cpp:321
ripple::StrandDirection::forward
@ forward