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_)
63  , srcToDst(srcToDst_)
64  , out(out_)
65  , srcDebtDir(srcDebtDir_)
66  {}
67  };
68 
69  boost::optional<Cache> cache_;
70 
71  // Compute the maximum value that can flow from src->dst at
72  // the best available quality.
73  // return: first element is max amount that can flow,
74  // second is the debt direction of the source w.r.t. the dst
77  ReadView const& sb) const;
78 
79  // Compute srcQOut and dstQIn when the source redeems.
82  ReadView const& sb) const;
83 
84  // Compute srcQOut and dstQIn when the source issues.
87  ReadView const& sb,
88  DebtDirection prevStepDebtDirection) const;
89 
90  // Returns srcQOut, dstQIn
92  qualities (
93  ReadView const& sb,
94  DebtDirection srcDebtDir,
95  StrandDirection strandDir) const;
96 
97 public:
99  StrandContext const& ctx,
100  AccountID const& src,
101  AccountID const& dst,
102  Currency const& c)
103  : src_(src)
104  , dst_(dst)
105  , currency_ (c)
106  , prevStep_ (ctx.prevStep)
107  , isLast_ (ctx.isLast)
108  , j_ (ctx.j)
109  {}
110 
111  AccountID const& src () const
112  {
113  return src_;
114  }
115  AccountID const& dst () const
116  {
117  return dst_;
118  }
119  Currency const& currency () const
120  {
121  return currency_;
122  }
123 
124  boost::optional<EitherAmount> 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 (
176  PaymentSandbox& sb,
177  ApplyView& afView,
178  EitherAmount const& in) override;
179 
180  // Check for error, existing liquidity, and violations of auth/frozen
181  // constraints.
182  TER check (StrandContext const& ctx) const;
183 
184  void setCacheLimiting (
185  IOUAmount const& fwdIn,
186  IOUAmount const& fwdSrcToDst,
187  IOUAmount const& fwdOut,
188  DebtDirection srcDebtDir);
189 
190  friend bool operator==(DirectStepI const& lhs, DirectStepI const& rhs)
191  {
192  return lhs.src_ == rhs.src_ &&
193  lhs.dst_ == rhs.dst_ &&
194  lhs.currency_ == rhs.currency_;
195  }
196 
197  friend bool operator!=(DirectStepI const& lhs, DirectStepI const& rhs)
198  {
199  return ! (lhs == rhs);
200  }
201 
202 protected:
203  std::string logStringImpl (char const* name) const
204  {
205  std::ostringstream ostr;
206  ostr <<
207  name << ": " <<
208  "\nSrc: " << src_ <<
209  "\nDst: " << dst_;
210  return ostr.str ();
211  }
212 
213 private:
214  bool 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 verifyDstQualityIn (std::uint32_t dstQIn) const
246  {
247  // Payments have no particular expectations for what dstQIn will be.
248  return true;
249  }
250 
252  quality (ReadView const& sb,
253  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,
266  std::shared_ptr<const SLE> const& sleSrc) const;
267 
268  std::string logString () const override
269  {
270  return logStringImpl ("DirectIPaymentStep");
271  }
272 };
273 
274 // Offer crossing DirectStep class (not a payment).
275 class DirectIOfferCrossingStep : public DirectStepI<DirectIOfferCrossingStep>
276 {
277 public:
280 
282  {
283  // During offer crossing we rely on the fact that prevStepRedeems
284  // will *always* issue. That's because:
285  // o If there's a prevStep_, it will always be a BookStep.
286  // o BookStep::debtDirection() aways returns `issues` when offer crossing.
287  // An assert based on this return value will tell us if that
288  // behavior changes.
289  return issues(prevStepDir);
290  }
291 
292  bool verifyDstQualityIn (std::uint32_t dstQIn) const
293  {
294  // Due to a couple of factors dstQIn is always QUALITY_ONE for
295  // offer crossing. If that changes we need to know.
296  return dstQIn == QUALITY_ONE;
297  }
298 
300  quality (ReadView const& sb,
301  QualityDirection qDir) const;
302 
303  // Compute the maximum value that can flow from src->dst at
304  // the best available quality.
305  // return: first element is max amount that can flow,
306  // second is the debt direction w.r.t the source
308  maxFlow (ReadView const& sb, IOUAmount const& desired) const;
309 
310  // Verify the consistency of the step. These checks are specific to
311  // offer crossing and assume that general checks were already performed.
312  TER
313  check (StrandContext const& ctx,
314  std::shared_ptr<const SLE> const& sleSrc) const;
315 
316 
317  std::string logString () const override
318  {
319  return logStringImpl ("DirectIOfferCrossingStep");
320  }
321 };
322 
323 //------------------------------------------------------------------------------
324 
327  QualityDirection qDir) const
328 {
329  if (src_ == dst_)
330  return QUALITY_ONE;
331 
332  auto const sle = sb.read (keylet::line (dst_, src_, currency_));
333 
334  if (!sle)
335  return QUALITY_ONE;
336 
337  auto const& field = [this, qDir]() -> SF_U32 const&
338  {
339  if (qDir == QualityDirection::in)
340  {
341  // compute dst quality in
342  if (this->dst_ < this->src_)
343  return sfLowQualityIn;
344  else
345  return sfHighQualityIn;
346  }
347  else
348  {
349  // compute src quality out
350  if (this->src_ < this->dst_)
351  return sfLowQualityOut;
352  else
353  return sfHighQualityOut;
354  }
355  }();
356 
357  if (! sle->isFieldPresent (field))
358  return QUALITY_ONE;
359 
360  auto const q = (*sle)[field];
361  if (!q)
362  return QUALITY_ONE;
363  return q;
364 }
365 
368  QualityDirection qDir) const
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  ReadView const& sb, IOUAmount const& desired) 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, std::shared_ptr<const SLE> const& sleSrc) const
406 {
407  // Since this is a payment a trust line must be present. Perform all
408  // trust line related checks.
409  {
410  auto const sleLine = ctx.view.read (keylet::line (src_, dst_, currency_));
411  if (!sleLine)
412  {
413  JLOG (j_.trace()) << "DirectStepI: No credit line. " << *this;
414  return terNO_LINE;
415  }
416 
417  auto const authField = (src_ > dst_) ? lsfHighAuth : lsfLowAuth;
418 
419  if (((*sleSrc)[sfFlags] & lsfRequireAuth) &&
420  !((*sleLine)[sfFlags] & authField) &&
421  (*sleLine)[sfBalance] == beast::zero)
422  {
423  JLOG (j_.warn())
424  << "DirectStepI: can't receive IOUs from issuer without auth."
425  << " src: " << src_;
426  return terNO_AUTH;
427  }
428 
429  if (ctx.prevStep)
430  {
431  if (ctx.prevStep->bookStepBook())
432  {
433  auto const noRippleSrcToDst =
434  ((*sleLine)[sfFlags] &
436  if (noRippleSrcToDst)
437  return terNO_RIPPLE;
438  }
439  }
440  }
441 
442  {
443  auto const owed = creditBalance (ctx.view, dst_, src_, currency_);
444  if (owed <= beast::zero)
445  {
446  auto const limit = creditLimit (ctx.view, dst_, src_, currency_);
447  if (-owed >= limit)
448  {
449  JLOG (j_.debug())
450  << "DirectStepI: dry: owed: " << owed << " limit: " << limit;
451  return tecPATH_DRY;
452  }
453  }
454  }
455  return tesSUCCESS;
456 }
457 
458 TER
460  StrandContext const&, std::shared_ptr<const SLE> const&) const
461 {
462  // The standard checks are all we can do because any remaining checks
463  // require the existence of a trust line. Offer crossing does not
464  // require a pre-existing trust line.
465  return tesSUCCESS;
466 }
467 
468 //------------------------------------------------------------------------------
469 
470 template <class TDerived>
473 {
474  auto const srcOwed = toAmount<IOUAmount> (
475  accountHolds (sb, src_, currency_, dst_, fhIGNORE_FREEZE, j_));
476 
477  if (srcOwed.signum () > 0)
478  return {srcOwed, DebtDirection::redeems};
479 
480  // srcOwed is negative or zero
481  return {creditLimit2 (sb, dst_, src_, currency_) + srcOwed, DebtDirection::issues};
482 }
483 
484 template <class TDerived>
487  const
488 {
489  if (dir == StrandDirection::forward && cache_)
490  return cache_->srcDebtDir;
491 
492  auto const srcOwed =
493  accountHolds(sb, src_, currency_, dst_, fhIGNORE_FREEZE, j_);
494  return srcOwed.signum() > 0 ? DebtDirection::redeems
496 }
497 
498 template <class TDerived>
501  PaymentSandbox& sb,
502  ApplyView& /*afView*/,
503  boost::container::flat_set<uint256>& /*ofrsToRm*/,
504  IOUAmount const& out)
505 {
506  cache_.reset ();
507 
508  auto const [maxSrcToDst, srcDebtDir] =
509  static_cast<TDerived const*>(this)->maxFlow(sb, out);
510 
511  auto const [srcQOut, dstQIn] = qualities (sb, srcDebtDir, StrandDirection::reverse);
512  assert (static_cast<TDerived const*>(this)->verifyDstQualityIn (dstQIn));
513 
514  Issue const srcToDstIss(
515  currency_, redeems(srcDebtDir) ? dst_ : src_);
516 
517  JLOG (j_.trace()) <<
518  "DirectStepI::rev" <<
519  " srcRedeems: " << redeems(srcDebtDir) <<
520  " outReq: " << to_string (out) <<
521  " maxSrcToDst: " << to_string (maxSrcToDst) <<
522  " srcQOut: " << srcQOut <<
523  " dstQIn: " << dstQIn;
524 
525  if (maxSrcToDst.signum () <= 0)
526  {
527  JLOG (j_.trace()) << "DirectStepI::rev: dry";
528  cache_.emplace (
529  IOUAmount (beast::zero),
530  IOUAmount (beast::zero),
531  IOUAmount (beast::zero),
532  srcDebtDir);
533  return {beast::zero, beast::zero};
534  }
535 
536  IOUAmount const srcToDst = mulRatio (
537  out, QUALITY_ONE, dstQIn, /*roundUp*/ true);
538 
539  if (srcToDst <= maxSrcToDst)
540  {
541  IOUAmount const in = mulRatio (
542  srcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true);
543  cache_.emplace (in, srcToDst, out, srcDebtDir);
544  rippleCredit (sb,
545  src_, dst_, toSTAmount (srcToDst, srcToDstIss),
546  /*checkIssuer*/ true, j_);
547  JLOG (j_.trace()) <<
548  "DirectStepI::rev: Non-limiting" <<
549  " srcRedeems: " << redeems(srcDebtDir) <<
550  " in: " << to_string (in) <<
551  " srcToDst: " << to_string (srcToDst) <<
552  " out: " << to_string (out);
553  return {in, out};
554  }
555 
556  // limiting node
557  IOUAmount const in = mulRatio (
558  maxSrcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true);
559  IOUAmount const actualOut = mulRatio (
560  maxSrcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false);
561  cache_.emplace (in, maxSrcToDst, actualOut, srcDebtDir);
562  rippleCredit (sb,
563  src_, dst_, toSTAmount (maxSrcToDst, srcToDstIss),
564  /*checkIssuer*/ true, j_);
565  JLOG (j_.trace()) <<
566  "DirectStepI::rev: Limiting" <<
567  " srcRedeems: " << redeems(srcDebtDir) <<
568  " in: " << to_string (in) <<
569  " srcToDst: " << to_string (maxSrcToDst) <<
570  " out: " << to_string (out);
571  return {in, actualOut};
572 }
573 
574 // The forward pass should never have more liquidity than the reverse
575 // pass. But sometimes rounding differences cause the forward pass to
576 // deliver more liquidity. Use the cached values from the reverse pass
577 // to prevent this.
578 template <class TDerived>
579 void
581  IOUAmount const& fwdIn,
582  IOUAmount const& fwdSrcToDst,
583  IOUAmount const& fwdOut,
584  DebtDirection srcDebtDir)
585 {
586  if (cache_->in < fwdIn)
587  {
588  IOUAmount const smallDiff(1, -9);
589  auto const diff = fwdIn - cache_->in;
590  if (diff > smallDiff)
591  {
592  if (fwdIn.exponent () != cache_->in.exponent () ||
593  !cache_->in.mantissa () ||
594  (double(fwdIn.mantissa ()) /
595  double(cache_->in.mantissa ())) > 1.01)
596  {
597  // Detect large diffs on forward pass so they may be investigated
598  JLOG (j_.warn())
599  << "DirectStepI::fwd: setCacheLimiting"
600  << " fwdIn: " << to_string (fwdIn)
601  << " cacheIn: " << to_string (cache_->in)
602  << " fwdSrcToDst: " << to_string (fwdSrcToDst)
603  << " cacheSrcToDst: " << to_string (cache_->srcToDst)
604  << " fwdOut: " << to_string (fwdOut)
605  << " cacheOut: " << to_string (cache_->out);
606  cache_.emplace (fwdIn, fwdSrcToDst, fwdOut, srcDebtDir);
607  return;
608  }
609  }
610  }
611  cache_->in = fwdIn;
612  if (fwdSrcToDst < cache_->srcToDst)
613  cache_->srcToDst = fwdSrcToDst;
614  if (fwdOut < cache_->out)
615  cache_->out = fwdOut;
616  cache_->srcDebtDir = srcDebtDir;
617 };
618 
619 template <class TDerived>
622  PaymentSandbox& sb,
623  ApplyView& /*afView*/,
624  boost::container::flat_set<uint256>& /*ofrsToRm*/,
625  IOUAmount const& in)
626 {
627  assert (cache_);
628 
629  auto const [maxSrcToDst, srcDebtDir] =
630  static_cast<TDerived const*>(this)->maxFlow (sb, cache_->srcToDst);
631 
632  auto const [srcQOut, dstQIn] = qualities (sb, srcDebtDir, StrandDirection::forward);
633 
634  Issue const srcToDstIss (currency_, redeems(srcDebtDir) ? dst_ : src_);
635 
636  JLOG (j_.trace()) <<
637  "DirectStepI::fwd" <<
638  " srcRedeems: " << redeems(srcDebtDir) <<
639  " inReq: " << to_string (in) <<
640  " maxSrcToDst: " << to_string (maxSrcToDst) <<
641  " srcQOut: " << srcQOut <<
642  " dstQIn: " << dstQIn;
643 
644  if (maxSrcToDst.signum () <= 0)
645  {
646  JLOG (j_.trace()) << "DirectStepI::fwd: dry";
647  cache_.emplace (
648  IOUAmount (beast::zero),
649  IOUAmount (beast::zero),
650  IOUAmount (beast::zero),
651  srcDebtDir);
652  return {beast::zero, beast::zero};
653  }
654 
655  IOUAmount const srcToDst = mulRatio (
656  in, QUALITY_ONE, srcQOut, /*roundUp*/ false);
657 
658  if (srcToDst <= maxSrcToDst)
659  {
660  IOUAmount const out = mulRatio (
661  srcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false);
662  setCacheLimiting (in, srcToDst, out, srcDebtDir);
663  rippleCredit (sb,
664  src_, dst_, toSTAmount (cache_->srcToDst, srcToDstIss),
665  /*checkIssuer*/ true, j_);
666  JLOG (j_.trace()) <<
667  "DirectStepI::fwd: Non-limiting" <<
668  " srcRedeems: " << redeems(srcDebtDir) <<
669  " in: " << to_string (in) <<
670  " srcToDst: " << to_string (srcToDst) <<
671  " out: " << to_string (out);
672  }
673  else
674  {
675  // limiting node
676  IOUAmount const actualIn = mulRatio (
677  maxSrcToDst, srcQOut, QUALITY_ONE, /*roundUp*/ true);
678  IOUAmount const out = mulRatio (
679  maxSrcToDst, dstQIn, QUALITY_ONE, /*roundUp*/ false);
680  setCacheLimiting (actualIn, maxSrcToDst, out, srcDebtDir);
681  rippleCredit (sb,
682  src_, dst_, toSTAmount (cache_->srcToDst, srcToDstIss),
683  /*checkIssuer*/ true, j_);
684  JLOG (j_.trace()) <<
685  "DirectStepI::rev: Limiting" <<
686  " srcRedeems: " << redeems(srcDebtDir) <<
687  " in: " << to_string (actualIn) <<
688  " srcToDst: " << to_string (srcToDst) <<
689  " out: " << to_string (out);
690  }
691  return {cache_->in, cache_->out};
692 }
693 
694 template <class TDerived>
697  PaymentSandbox& sb,
698  ApplyView& afView,
699  EitherAmount const& in)
700 {
701  if (!cache_)
702  {
703  JLOG (j_.trace()) << "Expected valid cache in validFwd";
704  return {false, EitherAmount (IOUAmount (beast::zero))};
705  }
706 
707 
708  auto const savCache = *cache_;
709 
710  assert (!in.native);
711 
712  auto const [maxSrcToDst, srcDebtDir] =
713  static_cast<TDerived const*>(this)->maxFlow(sb, cache_->srcToDst);
714  (void)srcDebtDir;
715 
716  try
717  {
718  boost::container::flat_set<uint256> dummy;
719  fwdImp (sb, afView, dummy, in.iou); // changes cache
720  }
721  catch (FlowException const&)
722  {
723  return {false, EitherAmount (IOUAmount (beast::zero))};
724  }
725 
726  if (maxSrcToDst < cache_->srcToDst)
727  {
728  JLOG (j_.warn()) <<
729  "DirectStepI: Strand re-execute check failed." <<
730  " Exceeded max src->dst limit" <<
731  " max src->dst: " << to_string (maxSrcToDst) <<
732  " actual src->dst: " << to_string (cache_->srcToDst);
733  return {false, EitherAmount(cache_->out)};
734  }
735 
736  if (!(checkNear (savCache.in, cache_->in) &&
737  checkNear (savCache.out, cache_->out)))
738  {
739  JLOG (j_.warn()) <<
740  "DirectStepI: Strand re-execute check failed." <<
741  " ExpectedIn: " << to_string (savCache.in) <<
742  " CachedIn: " << to_string (cache_->in) <<
743  " ExpectedOut: " << to_string (savCache.out) <<
744  " CachedOut: " << to_string (cache_->out);
745  return {false, EitherAmount (cache_->out)};
746  }
747  return {true, EitherAmount (cache_->out)};
748 }
749 
750 // Returns srcQOut, dstQIn
751 template <class TDerived>
754  ReadView const& sb) const
755 {
756  if (!prevStep_)
757  return {QUALITY_ONE, QUALITY_ONE};
758 
759  auto const prevStepQIn = prevStep_->lineQualityIn(sb);
760  auto srcQOut =
761  static_cast<TDerived const*>(this)->quality(sb, QualityDirection::out);
762 
763  if (prevStepQIn > srcQOut)
764  srcQOut = prevStepQIn;
765  return {srcQOut, QUALITY_ONE};
766 }
767 
768 // Returns srcQOut, dstQIn
769 template <class TDerived>
772  ReadView const& sb,
773  DebtDirection prevStepDebtDirection) const
774 {
775  // Charge a transfer rate when issuing and previous step redeems
776 
777  assert(static_cast<TDerived const*>(this)->verifyPrevStepDebtDirection(
778  prevStepDebtDirection));
779 
780  std::uint32_t const srcQOut = redeems(prevStepDebtDirection)
781  ? transferRate(sb, src_).value
782  : QUALITY_ONE;
783  auto dstQIn = static_cast<TDerived const*>(this)->quality(
785 
786  if (isLast_ && dstQIn > QUALITY_ONE)
787  dstQIn = QUALITY_ONE;
788  return {srcQOut, dstQIn};
789 }
790 
791 // Returns srcQOut, dstQIn
792 template <class TDerived>
795  ReadView const& sb,
796  DebtDirection srcDebtDir,
797  StrandDirection strandDir) const
798 {
799  if (redeems(srcDebtDir))
800  {
801  return qualitiesSrcRedeems(sb);
802  }
803  else
804  {
805  auto const prevStepDebtDirection = [&] {
806  if (prevStep_)
807  return prevStep_->debtDirection(sb, strandDir);
808  return DebtDirection::issues;
809  }();
810  return qualitiesSrcIssues(sb, prevStepDebtDirection);
811  }
812 }
813 
814 template <class TDerived>
817 {
818  // dst quality in
819  return static_cast<TDerived const*>(this)->quality (
821 }
822 
823 template <class TDerived>
826  const
827 {
828  auto const dir = this->debtDirection(v, StrandDirection::forward);
829 
831  {
832  std::uint32_t const srcQOut = [&]() -> std::uint32_t {
833  if (redeems(prevStepDir) && issues(dir))
834  return transferRate(v, src_).value;
835  return QUALITY_ONE;
836  }();
837  auto dstQIn = static_cast<TDerived const*>(this)->quality(
839 
840  if (isLast_ && dstQIn > QUALITY_ONE)
841  dstQIn = QUALITY_ONE;
842  Issue const iss{currency_, src_};
843  return {Quality(getRate(STAmount(iss, srcQOut), STAmount(iss, dstQIn))),
844  dir};
845  }
846 
847  auto const [srcQOut, dstQIn] = redeems(dir)
848  ? qualitiesSrcRedeems(v)
849  : qualitiesSrcIssues(v, prevStepDir);
850 
851  Issue const iss{currency_, src_};
852  // Be careful not to switch the parameters to `getRate`. The
853  // `getRate(offerOut, offerIn)` function is usually used for offers. It
854  // returns offerIn/offerOut. For a direct step, the rate is srcQOut/dstQIn
855  // (Input*dstQIn/srcQOut = Output; So rate = srcQOut/dstQIn). Although the
856  // first parameter is called `offerOut`, it should take the `dstQIn`
857  // variable.
858  return {Quality(getRate(STAmount(iss, dstQIn), STAmount(iss, srcQOut))),
859  dir};
860 }
861 
862 template <class TDerived>
864 {
865  // The following checks apply for both payments and offer crossing.
866  if (!src_ || !dst_)
867  {
868  JLOG (j_.debug()) << "DirectStepI: specified bad account.";
869  return temBAD_PATH;
870  }
871 
872  if (src_ == dst_)
873  {
874  JLOG (j_.debug()) << "DirectStepI: same src and dst.";
875  return temBAD_PATH;
876  }
877 
878  auto const sleSrc = ctx.view.read (keylet::account (src_));
879  if (!sleSrc)
880  {
881  JLOG (j_.warn())
882  << "DirectStepI: can't receive IOUs from non-existent issuer: "
883  << src_;
884  return terNO_ACCOUNT;
885  }
886 
887  // pure issue/redeem can't be frozen
888  if (!(ctx.isLast && ctx.isFirst))
889  {
890  auto const ter = checkFreeze(ctx.view, src_, dst_, currency_);
891  if (ter != tesSUCCESS)
892  return ter;
893  }
894 
895  // If previous step was a direct step then we need to check
896  // no ripple flags.
897  if (ctx.prevStep)
898  {
899  if (auto prevSrc = ctx.prevStep->directStepSrcAcct())
900  {
901  auto const ter = checkNoRipple(
902  ctx.view, *prevSrc, src_, dst_, currency_, j_);
903  if (ter != tesSUCCESS)
904  return ter;
905  }
906  }
907  {
908  Issue const srcIssue{currency_, src_};
909  Issue const dstIssue{currency_, dst_};
910 
911  if (ctx.seenBookOuts.count (srcIssue))
912  {
913  if (!ctx.prevStep)
914  {
915  assert(0); // prev seen book without a prev step!?!
916  return temBAD_PATH_LOOP;
917  }
918 
919  // This is OK if the previous step is a book step that outputs this issue
920  if (auto book = ctx.prevStep->bookStepBook())
921  {
922  if (book->out != srcIssue)
923  return temBAD_PATH_LOOP;
924  }
925  }
926 
927  if (!ctx.seenDirectIssues[0].insert (srcIssue).second ||
928  !ctx.seenDirectIssues[1].insert (dstIssue).second)
929  {
930  JLOG (j_.debug ())
931  << "DirectStepI: loop detected: Index: " << ctx.strandSize
932  << ' ' << *this;
933  return temBAD_PATH_LOOP;
934  }
935  }
936 
937  return static_cast<TDerived const*>(this)->check (ctx, sleSrc);
938 }
939 
940 //------------------------------------------------------------------------------
941 
942 namespace test
943 {
944 // Needed for testing
945 bool directStepEqual (Step const& step,
946  AccountID const& src,
947  AccountID const& dst,
948  Currency const& currency)
949 {
950  if (auto ds =
951  dynamic_cast<DirectStepI<DirectIPaymentStep> const*> (&step))
952  {
953  return ds->src () == src && ds->dst () == dst &&
954  ds->currency () == currency;
955  }
956  return false;
957 }
958 } // test
959 
960 //------------------------------------------------------------------------------
961 
964  StrandContext const& ctx,
965  AccountID const& src,
966  AccountID const& dst,
967  Currency const& c)
968 {
969  TER ter = tefINTERNAL;
971  if (ctx.offerCrossing)
972  {
973  auto offerCrossingStep =
974  std::make_unique<DirectIOfferCrossingStep> (ctx, src, dst, c);
975  ter = offerCrossingStep->check (ctx);
976  r = std::move (offerCrossingStep);
977  }
978  else // payment
979  {
980  auto paymentStep =
981  std::make_unique<DirectIPaymentStep> (ctx, src, dst, c);
982  ter = paymentStep->check (ctx);
983  r = std::move (paymentStep);
984  }
985  if (ter != tesSUCCESS)
986  return {ter, nullptr};
987 
988  return {tesSUCCESS, std::move (r)};
989 }
990 
991 } // ripple
ripple::DirectStepI::Cache
Definition: DirectStep.cpp:50
ripple::sfHighQualityIn
const SF_U32 sfHighQualityIn(access, STI_UINT32, 16, "HighQualityIn")
Definition: SField.h:353
ripple::rippleCredit
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Definition: View.cpp:914
ripple::mulRatio
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
Definition: IOUAmount.cpp:248
ripple::StrandContext
Context needed to build Strand Steps and for error checking.
Definition: Steps.h:487
ripple::StrandContext::strandSize
const size_t strandSize
Length of Strand.
Definition: Steps.h:499
ripple::transferRate
Rate transferRate(ReadView const &view, AccountID const &issuer)
Definition: View.cpp:338
sstream
ripple::issues
bool issues(DebtDirection dir)
Definition: Steps.h:50
ripple::DirectIPaymentStep::quality
std::uint32_t quality(ReadView const &sb, QualityDirection qDir) const
Definition: DirectStep.cpp:326
ripple::DirectStepI::maxPaymentFlow
std::pair< IOUAmount, DebtDirection > maxPaymentFlow(ReadView const &sb) const
Definition: DirectStep.cpp:472
ripple::fixQualityUpperBound
const uint256 fixQualityUpperBound
Definition: Feature.cpp:178
ripple::IOUAmount::exponent
int exponent() const noexcept
Definition: IOUAmount.h:126
ripple::Issue
A currency issued by an account.
Definition: Issue.h:34
ripple::tefINTERNAL
@ tefINTERNAL
Definition: TER.h:153
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:260
ripple::DirectIPaymentStep::verifyDstQualityIn
bool verifyDstQualityIn(std::uint32_t dstQIn) const
Definition: DirectStep.cpp:245
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:287
ripple::terNO_LINE
@ terNO_LINE
Definition: TER.h:197
ripple::DebtDirection
DebtDirection
Definition: Steps.h:37
ripple::PaymentSandbox
A wrapper which makes credits unavailable to balances.
Definition: PaymentSandbox.h:110
std::pair
ripple::DirectIPaymentStep
Definition: DirectStep.cpp:233
ripple::lsfLowAuth
@ lsfLowAuth
Definition: LedgerFormats.h:149
ripple::DirectStepI::qualities
std::pair< std::uint32_t, std::uint32_t > qualities(ReadView const &sb, DebtDirection srcDebtDir, StrandDirection strandDir) const
Definition: DirectStep.cpp:794
ripple::lsfLowNoRipple
@ lsfLowNoRipple
Definition: LedgerFormats.h:151
ripple::DirectIOfferCrossingStep::verifyPrevStepDebtDirection
bool verifyPrevStepDebtDirection(DebtDirection prevStepDir) const
Definition: DirectStep.cpp:281
ripple::accountHolds
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition: View.cpp:91
ripple::DirectStepI::check
TER check(StrandContext const &ctx) const
Definition: DirectStep.cpp:863
ripple::DirectStepI::src
AccountID const & src() const
Definition: DirectStep.cpp:111
ripple::sfFlags
const SF_U32 sfFlags(access, STI_UINT32, 2, "Flags")
Definition: SField.h:338
ripple::DirectStepI::qualitiesSrcIssues
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcIssues(ReadView const &sb, DebtDirection prevStepDebtDirection) const
Definition: DirectStep.cpp:771
ripple::checkNear
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
Definition: PaySteps.cpp:35
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:302
ripple::DirectStepI::currency
Currency const & currency() const
Definition: DirectStep.cpp:119
ripple::SBoxCmp::diff
@ diff
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:963
ripple::QualityDirection::in
@ in
ripple::creditLimit2
IOUAmount creditLimit2(ReadView const &v, AccountID const &acc, AccountID const &iss, Currency const &cur)
Definition: Credit.cpp:52
ripple::DirectStepI::DirectStepI
DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
Definition: DirectStep.cpp:98
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:292
ripple::terNO_RIPPLE
@ terNO_RIPPLE
Definition: TER.h:202
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:41
ripple::DirectIOfferCrossingStep::quality
std::uint32_t quality(ReadView const &sb, QualityDirection qDir) const
Definition: DirectStep.cpp:367
ripple::Step::bookStepBook
virtual boost::optional< Book > bookStepBook() const
If this step is a BookStep, return the book.
Definition: Steps.h:202
ripple::temBAD_PATH
@ temBAD_PATH
Definition: TER.h:94
ripple::DirectIOfferCrossingStep
Definition: DirectStep.cpp:275
ripple::toAmount< IOUAmount >
IOUAmount toAmount< IOUAmount >(STAmount const &amt)
Definition: AmountConversions.h:82
ripple::ApplyView
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:150
ripple::lsfHighAuth
@ lsfHighAuth
Definition: LedgerFormats.h:150
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:268
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:423
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:63
ripple::DirectStepI::setCacheLimiting
void setCacheLimiting(IOUAmount const &fwdIn, IOUAmount const &fwdSrcToDst, IOUAmount const &fwdOut, DebtDirection srcDebtDir)
Definition: DirectStep.cpp:580
ripple::base_uint< 160, detail::AccountIDTag >
ripple::DirectStepI::operator!=
friend bool operator!=(DirectStepI const &lhs, DirectStepI const &rhs)
Definition: DirectStep.cpp:197
ripple::lsfRequireAuth
@ lsfRequireAuth
Definition: LedgerFormats.h:134
ripple::DirectStepI::currency_
Currency currency_
Definition: DirectStep.cpp:43
ripple::StrandContext::view
ReadView const & view
Current ReadView.
Definition: Steps.h:489
ripple::DirectStepI::cachedOut
boost::optional< EitherAmount > cachedOut() const override
Definition: DirectStep.cpp:132
ripple::keylet::line
static const line_t line
Definition: Indexes.h:176
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:621
ripple::DirectStepI::logStringImpl
std::string logStringImpl(char const *name) const
Definition: DirectStep.cpp:203
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:497
ripple::keylet::account
static const account_t account
Definition: Indexes.h:116
ripple::toSTAmount
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
Definition: AmountConversions.h:31
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:500
ripple::DirectStepI::prevStep_
Step const *const prevStep_
Definition: DirectStep.cpp:46
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::DirectStepI::directStepAccts
boost::optional< std::pair< AccountID, AccountID > > directStepAccts() const override
Definition: DirectStep.cpp:146
ripple::Step
A step in a payment path.
Definition: Steps.h:81
ripple::terNO_AUTH
@ terNO_AUTH
Definition: TER.h:196
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:513
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:495
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:60
ripple::DirectStepI::dst_
AccountID dst_
Definition: DirectStep.cpp:42
ripple::sfLowQualityOut
const SF_U32 sfLowQualityOut(access, STI_UINT32, 19, "LowQualityOut")
Definition: SField.h:356
ripple::DirectStepI::lineQualityIn
std::uint32_t lineQualityIn(ReadView const &v) const override
Definition: DirectStep.cpp:816
std::uint32_t
ripple::Rules::enabled
bool enabled(uint256 const &id) const
Returns true if a feature is enabled.
Definition: ReadView.cpp:107
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:152
ripple::terNO_ACCOUNT
@ terNO_ACCOUNT
Definition: TER.h:195
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:945
ripple::redeems
bool redeems(DebtDirection dir)
Definition: Steps.h:43
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:186
ripple::Rate::value
std::uint32_t value
Definition: Rate.h:40
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:132
ripple::DirectStepI::Cache::out
IOUAmount out
Definition: DirectStep.cpp:54
ripple::sfLowQualityIn
const SF_U32 sfLowQualityIn(access, STI_UINT32, 18, "LowQualityIn")
Definition: SField.h:355
ripple::sfBalance
const SF_Amount sfBalance(access, STI_AMOUNT, 2, "Balance")
Definition: SField.h:424
ripple::DirectStepI::dst
AccountID const & dst() const
Definition: DirectStep.cpp:115
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:376
ripple::DirectStepI::debtDirection
DebtDirection debtDirection(ReadView const &sb, StrandDirection dir) const override
Definition: DirectStep.cpp:486
ripple::EitherAmount
Definition: AmountSpec.h:60
ripple::DirectStepI::isLast_
const bool isLast_
Definition: DirectStep.cpp:47
ripple::DirectStepI::cache_
boost::optional< Cache > cache_
Definition: DirectStep.cpp:69
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:494
ripple::StrandContext::prevStep
Step const *const prevStep
The previous step in the strand.
Definition: Steps.h:503
ripple::tecPATH_DRY
@ tecPATH_DRY
Definition: TER.h:259
ripple::fhIGNORE_FREEZE
@ fhIGNORE_FREEZE
Definition: View.h:55
ripple::DirectStepI::validFwd
std::pair< bool, EitherAmount > validFwd(PaymentSandbox &sb, ApplyView &afView, EitherAmount const &in) override
Definition: DirectStep.cpp:696
ripple::DirectStepI::Cache::in
IOUAmount in
Definition: DirectStep.cpp:52
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:33
std::ostringstream::str
T str(T... args)
beast::Journal::debug
Stream debug() const
Definition: Journal.h:292
ripple::DirectIOfferCrossingStep::check
TER check(StrandContext const &ctx, std::shared_ptr< const SLE > const &sleSrc) const
Definition: DirectStep.cpp:459
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
ripple::DirectStepI::qualityUpperBound
std::pair< boost::optional< Quality >, DebtDirection > qualityUpperBound(ReadView const &v, DebtDirection dir) const override
Definition: DirectStep.cpp:825
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:509
std::unique_ptr
STL class.
ripple::DirectStepI::qualitiesSrcRedeems
std::pair< std::uint32_t, std::uint32_t > qualitiesSrcRedeems(ReadView const &sb) const
Definition: DirectStep.cpp:753
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:145
ripple::sfHighQualityOut
const SF_U32 sfHighQualityOut(access, STI_UINT32, 17, "HighQualityOut")
Definition: SField.h:354
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:219
ripple::DirectIOfferCrossingStep::logString
std::string logString() const override
Definition: DirectStep.cpp:317
ripple::StrandDirection::forward
@ forward